|
1 | 1 | import { mkdir } from "node:fs/promises"; |
2 | 2 | import { join, resolve } from "node:path"; |
| 3 | +import { parseArgs } from "node:util"; |
3 | 4 | import { createSecretCodec } from "../src/server/secrets/secret-codec.ts"; |
4 | 5 | import { SqliteRuntimeDatabase } from "../src/server/storage/sqlite-runtime-store.ts"; |
5 | 6 |
|
6 | | -const command = process.argv[2]; |
7 | | -const options = parseOptions(process.argv.slice(3)); |
8 | | -const dataDir = resolve(options.dataDir ?? process.env.OOMOL_CONNECT_DATA_DIR ?? join(process.cwd(), "data")); |
9 | | -const databasePath = join(dataDir, "connect.sqlite"); |
10 | | -const secretCodec = createSecretCodec(process.env.OOMOL_CONNECT_ENCRYPTION_KEY); |
| 7 | +const { positionals, values: options } = parseArgs({ |
| 8 | + args: process.argv.slice(2), |
| 9 | + allowPositionals: true, |
| 10 | + options: { |
| 11 | + "data-dir": { type: "string" }, |
| 12 | + plain: { type: "boolean" }, |
| 13 | + yes: { type: "boolean" }, |
| 14 | + }, |
| 15 | + strict: true, |
| 16 | +}); |
| 17 | +const [command] = positionals; |
11 | 18 |
|
12 | | -if (!command || !["reset", "rotate-key"].includes(command)) { |
| 19 | +if (positionals.length !== 1 || (command !== "reset" && command !== "rotate-key")) { |
13 | 20 | printUsageAndExit(); |
14 | 21 | } |
15 | 22 |
|
16 | | -await mkdir(dataDir, { recursive: true }); |
17 | | - |
| 23 | +const nextEncryptionKey = process.env.OOMOL_CONNECT_NEW_ENCRYPTION_KEY; |
18 | 24 | if (command === "rotate-key") { |
19 | | - const nextEncryptionKey = process.env.OOMOL_CONNECT_NEW_ENCRYPTION_KEY; |
20 | | - if (!nextEncryptionKey && options.plain !== "true") { |
21 | | - throw new Error("rotate-key requires OOMOL_CONNECT_NEW_ENCRYPTION_KEY unless --plain is set."); |
| 25 | + if (options.yes) { |
| 26 | + throw new Error("--yes is only valid with reset."); |
22 | 27 | } |
23 | | - const database = new SqliteRuntimeDatabase(databasePath, { secretCodec }); |
24 | | - try { |
25 | | - await database.rotateSecretCodec(createSecretCodec(options.plain === "true" ? undefined : nextEncryptionKey)); |
26 | | - console.log(`Rotated runtime secret encryption in ${databasePath}.`); |
27 | | - } finally { |
28 | | - database.close(); |
| 28 | + if (!nextEncryptionKey && !options.plain) { |
| 29 | + throw new Error("rotate-key requires OOMOL_CONNECT_NEW_ENCRYPTION_KEY unless --plain is set."); |
29 | 30 | } |
30 | 31 | } else { |
31 | | - const database = new SqliteRuntimeDatabase(databasePath, { secretCodec }); |
32 | | - try { |
33 | | - if (options.yes !== "true") { |
34 | | - throw new Error("reset requires --yes."); |
35 | | - } |
36 | | - database.resetRuntimeData(); |
37 | | - console.log(`Reset runtime data in ${databasePath}.`); |
38 | | - } finally { |
39 | | - database.close(); |
| 32 | + if (options.plain) { |
| 33 | + throw new Error("--plain is only valid with rotate-key."); |
| 34 | + } |
| 35 | + if (!options.yes) { |
| 36 | + throw new Error("reset requires --yes."); |
40 | 37 | } |
41 | 38 | } |
42 | 39 |
|
43 | | -type RuntimeDataCommandOptions = { |
44 | | - dataDir?: string; |
45 | | - plain?: string; |
46 | | - yes?: string; |
47 | | -}; |
48 | | - |
49 | | -function parseOptions(args: string[]): RuntimeDataCommandOptions { |
50 | | - const options: RuntimeDataCommandOptions = {}; |
51 | | - for (let index = 0; index < args.length; index += 1) { |
52 | | - const arg = args[index]; |
53 | | - if (arg === "--yes") { |
54 | | - options.yes = "true"; |
55 | | - continue; |
56 | | - } |
57 | | - if (arg === "--plain") { |
58 | | - options.plain = "true"; |
59 | | - continue; |
60 | | - } |
61 | | - |
62 | | - const value = args[index + 1]; |
63 | | - if (!value) { |
64 | | - throw new Error(`${arg} requires a value.`); |
65 | | - } |
| 40 | +const dataDir = resolve(options["data-dir"] ?? process.env.OOMOL_CONNECT_DATA_DIR ?? join(process.cwd(), "data")); |
| 41 | +const databasePath = join(dataDir, "connect.sqlite"); |
| 42 | +const secretCodec = createSecretCodec(process.env.OOMOL_CONNECT_ENCRYPTION_KEY); |
| 43 | +await mkdir(dataDir, { recursive: true }); |
66 | 44 |
|
67 | | - if (arg === "--data-dir") { |
68 | | - options.dataDir = value; |
69 | | - } else { |
70 | | - throw new Error(`Unknown option: ${arg}.`); |
71 | | - } |
72 | | - index += 1; |
| 45 | +const database = new SqliteRuntimeDatabase(databasePath, { secretCodec }); |
| 46 | +try { |
| 47 | + if (command === "rotate-key") { |
| 48 | + await database.rotateSecretCodec(createSecretCodec(options.plain ? undefined : nextEncryptionKey)); |
| 49 | + console.log(`Rotated runtime secret encryption in ${databasePath}.`); |
| 50 | + } else { |
| 51 | + database.resetRuntimeData(); |
| 52 | + console.log(`Reset runtime data in ${databasePath}.`); |
73 | 53 | } |
74 | | - |
75 | | - return options; |
| 54 | +} finally { |
| 55 | + database.close(); |
76 | 56 | } |
77 | 57 |
|
78 | 58 | function printUsageAndExit(): never { |
|
0 commit comments