canister-export-backup.mjs
109 lines 3.7 KB
Raw
sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2 feat(auth): persistent login system + C7 session introspection Human minor ⚠ breaking 16 days ago
1 #!/usr/bin/env node
2 /**
3 * Operator backup: canister notes + full proposals → JSON (optional AES-256-GCM, optional S3).
4 * Env matches operator notes in .env.example and canister HTTP export expectations.
5 *
6 * @see scripts/canister-export-backup.sh (invokes this file)
7 */
8 import fs from 'node:fs';
9 import path from 'node:path';
10 import { fileURLToPath } from 'node:url';
11 import dotenv from 'dotenv';
12 import {
13 parseBackupVaultIds,
14 resolveBackupS3Prefix,
15 resolveCanisterBackupBaseUrl,
16 } from '../lib/canister-export-env.mjs';
17 import {
18 buildOperatorVaultPayload,
19 encryptOperatorBackupUtf8,
20 fetchFullProposalsForOperatorExport,
21 fetchNotesFromExport,
22 putS3Object,
23 safeVaultFileToken,
24 utcBackupStamp,
25 } from '../lib/operator-canister-backup.mjs';
26
27 const __dirname = path.dirname(fileURLToPath(import.meta.url));
28 const repoRoot = path.resolve(__dirname, '..');
29 const envPath = path.join(repoRoot, '.env');
30 if (fs.existsSync(envPath)) {
31 dotenv.config({ path: envPath });
32 }
33
34 const userId = (process.env.KNOWTATION_CANISTER_BACKUP_USER_ID ?? '').trim();
35 if (!userId) {
36 console.error('ERROR: KNOWTATION_CANISTER_BACKUP_USER_ID is required.');
37 process.exit(1);
38 }
39
40 const baseUrl = resolveCanisterBackupBaseUrl(process.env, repoRoot);
41 if (!baseUrl) {
42 console.error('ERROR: Set KNOWTATION_CANISTER_URL or KNOWTATION_CANISTER_BACKUP_URL, or rely on canister_ids.json with BACKUP_USER_ID set.');
43 process.exit(1);
44 }
45 const hadExplicitUrl =
46 Boolean((process.env.KNOWTATION_CANISTER_URL ?? '').trim()) ||
47 Boolean((process.env.KNOWTATION_CANISTER_BACKUP_URL ?? '').trim());
48 if (!hadExplicitUrl) {
49 console.log('==> Defaulting KNOWTATION_CANISTER_URL from hub/icp/canister_ids.json');
50 console.log(` ${baseUrl}`);
51 }
52
53 const rawBackupDir = (process.env.KNOWTATION_CANISTER_BACKUP_DIR ?? 'backups').trim() || 'backups';
54 const backupDir = path.isAbsolute(rawBackupDir)
55 ? rawBackupDir
56 : path.resolve(repoRoot, rawBackupDir);
57 fs.mkdirSync(backupDir, { recursive: true });
58
59 const stamp = utcBackupStamp();
60 const vaultIds = parseBackupVaultIds(process.env);
61 if (vaultIds.length === 0) {
62 console.error('ERROR: No vault ids to export.');
63 process.exit(1);
64 }
65
66 const keyHex = (process.env.KNOWTATION_CANISTER_BACKUP_ENCRYPT_KEY_HEX ?? '').trim();
67 const s3Bucket = (process.env.KNOWTATION_CANISTER_BACKUP_S3_BUCKET ?? '').trim();
68 const s3Prefix = resolveBackupS3Prefix(process.env);
69 const skipS3 = (process.env.KNOWTATION_CANISTER_BACKUP_SKIP_S3 ?? '').trim() === '1';
70
71 for (const vaultId of vaultIds) {
72 if (!vaultId) continue;
73 const safe = safeVaultFileToken(vaultId);
74 const baseName = `canister-export-${safe}-${stamp}`;
75 console.log(`==> Export vault ${vaultId} (${baseUrl})`);
76
77 const notes = await fetchNotesFromExport(baseUrl, userId, vaultId);
78 const proposals = await fetchFullProposalsForOperatorExport(baseUrl, userId, vaultId);
79 const payload = buildOperatorVaultPayload(vaultId, notes, proposals);
80 const json = JSON.stringify(payload);
81
82 let outBuf;
83 let outFile;
84 if (keyHex) {
85 outBuf = encryptOperatorBackupUtf8(json, keyHex);
86 outFile = `${baseName}.json.enc`;
87 console.log(` Encrypted ${json.length} bytes JSON → ${outBuf.length} bytes (${outFile})`);
88 } else {
89 outBuf = Buffer.from(json, 'utf8');
90 outFile = `${baseName}.json`;
91 console.log(` Wrote ${outBuf.length} bytes (${outFile})`);
92 }
93
94 const outPath = path.join(backupDir, outFile);
95 fs.writeFileSync(outPath, outBuf);
96
97 if (s3Bucket && !skipS3) {
98 const key = `${s3Prefix}${outFile}`;
99 console.log(` S3: s3://${s3Bucket}/${key}`);
100 await putS3Object({
101 bucket: s3Bucket,
102 key,
103 body: outBuf,
104 region: process.env.AWS_REGION,
105 });
106 }
107 }
108
109 console.log(`canister-export-backup: OK (${stamp})`);
File History 2 commits
sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2 feat(auth): persistent login system + C7 session introspection Human minor 16 days ago