This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

stigmergic / scripts / sync-to-r2.mjs
3.7 kB 124 lines
1#!/usr/bin/env node 2// ─── Sync vault + carry to R2 ─────────────────────────────────────── 3// Walks the local vault and carry directories, uploads each file to R2 4// under {did}/{relativePath}. 5// 6// Usage: 7// node scripts/sync-to-r2.mjs --did did:plc:xxx --vault /home/nandi/vault --carry /home/nandi/.local/share/carry-vault 8// 9// Requires R2 credentials as env vars: 10// R2_ACCOUNT_ID, R2_ACCESS_KEY, R2_SECRET_KEY 11 12import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; 13import { readdir, readFile, stat } from "fs/promises"; 14import { join, relative } from "path"; 15 16const args = process.argv.slice(2); 17function getArg(name: string): string | undefined { 18 const idx = args.indexOf(`--${name}`); 19 return idx >= 0 ? args[idx + 1] : undefined; 20} 21 22const DID = getArg("did") || process.env.STRATA_DID; 23const VAULT_DIR = getArg("vault") || process.env.VAULT_DIR || "/home/nandi/vault"; 24const CARRY_DIR = getArg("carry") || process.env.CARRY_DIR || "/home/nandi/.local/share/carry-vault"; 25const R2_ACCOUNT_ID = process.env.R2_ACCOUNT_ID; 26const R2_ACCESS_KEY = process.env.R2_ACCESS_KEY; 27const R2_SECRET_KEY = process.env.R2_SECRET_KEY; 28 29if (!DID) { 30 console.error("Usage: node sync-to-r2.mjs --did did:plc:xxx [--vault path] [--carry path]"); 31 console.error(" or set STRATA_DID, VAULT_DIR, CARRY_DIR env vars"); 32 process.exit(1); 33} 34 35if (!R2_ACCOUNT_ID || !R2_ACCESS_KEY || !R2_SECRET_KEY) { 36 console.error("Set R2_ACCOUNT_ID, R2_ACCESS_KEY, R2_SECRET_KEY env vars"); 37 process.exit(1); 38} 39 40const s3 = new S3Client({ 41 region: "auto", 42 endpoint: `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, 43 credentials: { 44 accessKeyId: R2_ACCESS_KEY, 45 secretAccessKey: R2_SECRET_KEY, 46 }, 47}); 48 49const VAULT_BUCKET = "stigmergic-vault"; 50const CARRY_BUCKET = "stigmergic-carry"; 51 52async function walkDir(dir: string): Promise<string[]> { 53 const files: string[] = []; 54 try { 55 const entries = await readdir(dir, { withFileTypes: true }); 56 for (const entry of entries) { 57 const fullPath = join(dir, entry.name); 58 if (entry.isDirectory()) { 59 files.push(...await walkDir(fullPath)); 60 } else { 61 files.push(fullPath); 62 } 63 } 64 } catch { 65 // Directory doesn't exist, skip 66 } 67 return files; 68} 69 70async function syncDir( 71 baseDir: string, 72 bucket: string, 73 prefix: string, 74 extensions: Set<string>, 75): Promise<number> { 76 const files = await walkDir(baseDir); 77 let uploaded = 0; 78 79 for (const filePath of files) { 80 const relPath = relative(baseDir, filePath); 81 const ext = relPath.split(".").pop() || ""; 82 83 if (!extensions.has(ext)) continue; 84 85 const key = `${prefix}/${relPath}`; 86 const body = await readFile(filePath); 87 88 try { 89 await s3.send( 90 new PutObjectCommand({ 91 Bucket: bucket, 92 Key: key, 93 Body: body, 94 ContentType: ext === "md" ? "text/markdown" : "application/json", 95 }), 96 ); 97 uploaded++; 98 console.log(`${key}`); 99 } catch (e: any) { 100 console.error(`${key}: ${e.message}`); 101 } 102 } 103 104 return uploaded; 105} 106 107async function main() { 108 console.log(`Syncing data for ${DID}`); 109 console.log(); 110 111 console.log("Syncing vault..."); 112 const vaultCount = await syncDir(VAULT_DIR, VAULT_BUCKET, DID, new Set(["md", "canvas"])); 113 console.log(` ${vaultCount} vault files uploaded`); 114 115 console.log(); 116 console.log("Syncing carry..."); 117 const carryCount = await syncDir(CARRY_DIR, CARRY_BUCKET, DID, new Set(["json"])); 118 console.log(` ${carryCount} carry files uploaded`); 119 120 console.log(); 121 console.log("Done."); 122} 123 124main().catch(console.error);