@cybermem/dashboard 0.5.8 → 0.5.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,12 @@
1
- import { exec } from 'child_process'
2
- import { createReadStream, statSync } from 'fs'
1
+ import { execSync } from 'child_process'
2
+ import { createReadStream, rmSync, statSync } from 'fs'
3
3
  import { NextResponse } from 'next/server'
4
4
  import { tmpdir } from 'os'
5
5
  import { join } from 'path'
6
- import { promisify } from 'util'
7
6
 
8
7
  export const dynamic = 'force-dynamic'
9
8
 
10
- const execAsync = promisify(exec)
9
+ const DATA_DIR = process.env.DATA_DIR || '/data'
11
10
 
12
11
  /**
13
12
  * GET /api/backup
@@ -19,14 +18,9 @@ export async function GET() {
19
18
  const backupName = `cybermem-backup-${timestamp}.tar.gz`
20
19
  const tmpPath = join(tmpdir(), backupName)
21
20
 
22
- // Create backup via docker
23
- const containerName = process.env.OPENMEMORY_CONTAINER || 'cybermem-openmemory'
24
-
25
21
  try {
26
- // Copy data from container to temp location
27
- await execAsync(
28
- `docker cp ${containerName}:/data - | gzip > "${tmpPath}"`
29
- )
22
+ // Create backup via tar command (available in Node alpine image)
23
+ execSync(`tar -czf "${tmpPath}" -C "${DATA_DIR}" .`, { stdio: 'pipe' })
30
24
 
31
25
  // Get file stats
32
26
  const stats = statSync(tmpPath)
@@ -38,7 +32,11 @@ export async function GET() {
38
32
  const webStream = new ReadableStream({
39
33
  start(controller) {
40
34
  stream.on('data', (chunk) => controller.enqueue(chunk))
41
- stream.on('end', () => controller.close())
35
+ stream.on('end', () => {
36
+ controller.close()
37
+ // Clean up temp file after streaming
38
+ try { rmSync(tmpPath) } catch {}
39
+ })
42
40
  stream.on('error', (err) => controller.error(err))
43
41
  }
44
42
  })
@@ -51,9 +49,9 @@ export async function GET() {
51
49
  }
52
50
  })
53
51
 
54
- } catch (dockerError: any) {
52
+ } catch (tarError: any) {
55
53
  return NextResponse.json(
56
- { error: `Backup failed: ${dockerError.message}` },
54
+ { error: `Backup failed: ${tarError.message}` },
57
55
  { status: 500 }
58
56
  )
59
57
  }
@@ -1,10 +1,10 @@
1
- import { exec } from 'child_process'
1
+ import { readdirSync, statSync, unlinkSync } from 'fs'
2
2
  import { NextRequest, NextResponse } from 'next/server'
3
- import { promisify } from 'util'
3
+ import { join } from 'path'
4
4
 
5
5
  export const dynamic = 'force-dynamic'
6
6
 
7
- const execAsync = promisify(exec)
7
+ const DATA_DIR = process.env.DATA_DIR || '/data'
8
8
 
9
9
  /**
10
10
  * POST /api/reset
@@ -24,52 +24,38 @@ export async function POST(request: NextRequest) {
24
24
  )
25
25
  }
26
26
 
27
- // Wipe database via docker exec
28
- const containerName = process.env.OPENMEMORY_CONTAINER || 'cybermem-openmemory'
29
-
27
+ // Remove SQLite files directly via volume mount
30
28
  try {
31
- // Remove SQLite files
32
- await execAsync(
33
- `docker exec ${containerName} sh -c 'rm -f /data/openmemory.sqlite*'`
34
- )
35
-
36
- // Restart container to reinitialize
37
- await execAsync(`docker restart ${containerName}`)
29
+ const files = readdirSync(DATA_DIR)
30
+ let deletedCount = 0
38
31
 
39
- // Wait for health
40
- let healthy = false
41
- for (let i = 0; i < 30; i++) {
42
- await new Promise(r => setTimeout(r, 2000))
43
- try {
44
- const healthUrl = process.env.CYBERMEM_URL || 'http://localhost:8626'
45
- const res = await fetch(`${healthUrl}/health`, { cache: 'no-store' })
46
- if (res.ok) {
47
- healthy = true
48
- break
32
+ for (const file of files) {
33
+ if (file.startsWith('openmemory.sqlite')) {
34
+ const filePath = join(DATA_DIR, file)
35
+ try {
36
+ const stat = statSync(filePath)
37
+ if (stat.isFile()) {
38
+ unlinkSync(filePath)
39
+ deletedCount++
40
+ }
41
+ } catch {
42
+ // File may already be deleted
49
43
  }
50
- } catch {
51
- // Still starting up
52
44
  }
53
45
  }
54
46
 
55
- if (!healthy) {
56
- return NextResponse.json(
57
- { error: 'Database reset but container failed to become healthy' },
58
- { status: 500 }
59
- )
60
- }
61
-
62
- // Restart log-exporter and db-exporter
63
- await execAsync('docker restart cybermem-log-exporter cybermem-db-exporter').catch(() => {})
64
-
47
+ // Notify user that container restart is needed
65
48
  return NextResponse.json({
66
49
  success: true,
67
- message: 'Database reset successfully'
50
+ message: `Deleted ${deletedCount} database files. Restart openmemory container to reinitialize.`,
51
+ deletedCount,
52
+ restartRequired: true,
53
+ restartCommand: 'docker restart cybermem-openmemory'
68
54
  })
69
55
 
70
- } catch (dockerError: any) {
56
+ } catch (fsError: any) {
71
57
  return NextResponse.json(
72
- { error: `Docker command failed: ${dockerError.message}` },
58
+ { error: `File operation failed: ${fsError.message}` },
73
59
  { status: 500 }
74
60
  )
75
61
  }
@@ -1,13 +1,12 @@
1
- import { exec } from 'child_process'
2
- import { unlinkSync, writeFileSync } from 'fs'
1
+ import { execSync } from 'child_process'
2
+ import { readdirSync, rmSync, unlinkSync, writeFileSync } from 'fs'
3
3
  import { NextRequest, NextResponse } from 'next/server'
4
4
  import { tmpdir } from 'os'
5
5
  import { join } from 'path'
6
- import { promisify } from 'util'
7
6
 
8
7
  export const dynamic = 'force-dynamic'
9
8
 
10
- const execAsync = promisify(exec)
9
+ const DATA_DIR = process.env.DATA_DIR || '/data'
11
10
 
12
11
  /**
13
12
  * POST /api/restore
@@ -35,7 +34,6 @@ export async function POST(request: NextRequest) {
35
34
  )
36
35
  }
37
36
 
38
- const containerName = process.env.OPENMEMORY_CONTAINER || 'cybermem-openmemory'
39
37
  const tmpPath = join(tmpdir(), `restore-${Date.now()}.tar.gz`)
40
38
 
41
39
  try {
@@ -43,51 +41,33 @@ export async function POST(request: NextRequest) {
43
41
  const buffer = Buffer.from(await file.arrayBuffer())
44
42
  writeFileSync(tmpPath, buffer)
45
43
 
46
- // Stop container
47
- await execAsync(`docker stop ${containerName}`)
44
+ // Remove existing database files
45
+ const existingFiles = readdirSync(DATA_DIR)
46
+ for (const f of existingFiles) {
47
+ if (f.startsWith('openmemory.sqlite')) {
48
+ try { rmSync(join(DATA_DIR, f)) } catch {}
49
+ }
50
+ }
48
51
 
49
- // Extract backup to container volume
50
- await execAsync(
51
- `gunzip -c "${tmpPath}" | docker cp - ${containerName}:/`
52
- )
52
+ // Extract backup to data directory
53
+ execSync(`tar -xzf "${tmpPath}" -C "${DATA_DIR}"`, { stdio: 'pipe' })
53
54
 
54
55
  // Clean up temp file
55
56
  unlinkSync(tmpPath)
56
57
 
57
- // Start container
58
- await execAsync(`docker start ${containerName}`)
59
-
60
- // Wait for health
61
- let healthy = false
62
- for (let i = 0; i < 30; i++) {
63
- await new Promise(r => setTimeout(r, 2000))
64
- try {
65
- const healthUrl = process.env.CYBERMEM_URL || 'http://localhost:8626'
66
- const res = await fetch(`${healthUrl}/health`, { cache: 'no-store' })
67
- if (res.ok) {
68
- healthy = true
69
- break
70
- }
71
- } catch {
72
- // Still starting up
73
- }
74
- }
75
-
76
- // Restart exporters
77
- await execAsync('docker restart cybermem-log-exporter cybermem-db-exporter').catch(() => {})
78
-
79
58
  return NextResponse.json({
80
59
  success: true,
81
- message: 'Database restored successfully',
82
- healthy
60
+ message: 'Database restored successfully. Restart openmemory container to apply.',
61
+ restartRequired: true,
62
+ restartCommand: 'docker restart cybermem-openmemory'
83
63
  })
84
64
 
85
- } catch (dockerError: any) {
65
+ } catch (restoreError: any) {
86
66
  // Clean up temp file on error
87
67
  try { unlinkSync(tmpPath) } catch {}
88
68
 
89
69
  return NextResponse.json(
90
- { error: `Restore failed: ${dockerError.message}` },
70
+ { error: `Restore failed: ${restoreError.message}` },
91
71
  { status: 500 }
92
72
  )
93
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cybermem/dashboard",
3
- "version": "0.5.8",
3
+ "version": "0.5.12",
4
4
  "description": "CyberMem Monitoring Dashboard",
5
5
  "homepage": "https://cybermem.dev",
6
6
  "repository": {