station-kit 1.0.8 → 1.1.0
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.
- package/.next/standalone/package.json +3 -1
- package/.next/standalone/packages/station-kit/.next/BUILD_ID +1 -1
- package/.next/standalone/packages/station-kit/.next/app-build-manifest.json +75 -16
- package/.next/standalone/packages/station-kit/.next/app-path-routes-manifest.json +10 -3
- package/.next/standalone/packages/station-kit/.next/build-manifest.json +3 -3
- package/.next/standalone/packages/station-kit/.next/prerender-manifest.json +108 -12
- package/.next/standalone/packages/station-kit/.next/routes-manifest.json +49 -0
- package/.next/standalone/packages/station-kit/.next/server/app/_not-found/page.js +2 -2
- package/.next/standalone/packages/station-kit/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/_not-found.rsc +7 -7
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/[id]/page.js +2 -2
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/dyn/[name]/page.js +2 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/dyn/[name]/page.js.nft.json +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/dyn/[name]/page_client-reference-manifest.js +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/dyn/[name]/v/[n]/page.js +2 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/dyn/[name]/v/[n]/page.js.nft.json +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/dyn/[name]/v/[n]/page_client-reference-manifest.js +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/new/page.js +2 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/new/page.js.nft.json +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/new/page_client-reference-manifest.js +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/new.html +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/new.meta +7 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/new.rsc +25 -0
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/page.js +2 -2
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts.html +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/broadcasts.rsc +8 -8
- package/.next/standalone/packages/station-kit/.next/server/app/index.html +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/index.rsc +8 -8
- package/.next/standalone/packages/station-kit/.next/server/app/page.js +2 -2
- package/.next/standalone/packages/station-kit/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/playground/expression/page.js +2 -0
- package/.next/standalone/packages/station-kit/.next/server/app/playground/expression/page.js.nft.json +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/playground/expression/page_client-reference-manifest.js +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/playground/expression.html +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/playground/expression.meta +7 -0
- package/.next/standalone/packages/station-kit/.next/server/app/playground/expression.rsc +25 -0
- package/.next/standalone/packages/station-kit/.next/server/app/runs/[id]/page.js +2 -2
- package/.next/standalone/packages/station-kit/.next/server/app/runs/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/[id]/page.js +2 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/[id]/page.js.nft.json +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/[id]/page_client-reference-manifest.js +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/new/page.js +2 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/new/page.js.nft.json +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/new/page_client-reference-manifest.js +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/new.html +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/new.meta +7 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/new.rsc +25 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/page.js +2 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/page.js.nft.json +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules/page_client-reference-manifest.js +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules.html +1 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules.meta +7 -0
- package/.next/standalone/packages/station-kit/.next/server/app/schedules.rsc +25 -0
- package/.next/standalone/packages/station-kit/.next/server/app/settings/page.js +2 -2
- package/.next/standalone/packages/station-kit/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/settings.html +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/settings.rsc +8 -8
- package/.next/standalone/packages/station-kit/.next/server/app/signals/[name]/page.js +2 -2
- package/.next/standalone/packages/station-kit/.next/server/app/signals/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/signals/page.js +2 -2
- package/.next/standalone/packages/station-kit/.next/server/app/signals/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/signals.html +1 -1
- package/.next/standalone/packages/station-kit/.next/server/app/signals.rsc +8 -8
- package/.next/standalone/packages/station-kit/.next/server/app-paths-manifest.json +10 -3
- package/.next/standalone/packages/station-kit/.next/server/chunks/102.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/chunks/535.js +2 -0
- package/.next/standalone/packages/station-kit/.next/server/chunks/606.js +14 -14
- package/.next/standalone/packages/station-kit/.next/server/chunks/783.js +3 -3
- package/.next/standalone/packages/station-kit/.next/server/middleware-build-manifest.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/pages/404.html +1 -1
- package/.next/standalone/packages/station-kit/.next/server/pages/500.html +1 -1
- package/.next/standalone/packages/station-kit/.next/server/pages/_app.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/pages/_document.js +1 -1
- package/.next/standalone/packages/station-kit/.next/server/pages/_error.js +9 -9
- package/.next/standalone/packages/station-kit/.next/server/pages-manifest.json +1 -1
- package/.next/standalone/packages/station-kit/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/packages/station-kit/.next/static/THKSkCipW_pj0F6DRXYEG/_buildManifest.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/145-9e370afd2e5aba39.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/285-ff198f0a909c4fdd.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/561-33d912169940283e.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/935-dff12960528de017.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/_not-found/{page-ce21b4ba9038a5a7.js → page-67ef312aee40cfeb.js} +1 -1
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/broadcasts/[id]/page-fe2f5467a0c68fef.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/broadcasts/dyn/[name]/page-0d2505242014f51e.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/broadcasts/dyn/[name]/v/[n]/page-5eac0507f49a00ec.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/broadcasts/new/page-3d02707043d24dc7.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/broadcasts/page-dee500ccc01f0821.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/layout-e14e14f3e5b0b8a9.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/page-aac41ef7a470daab.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/playground/expression/page-dc9d91f3f50f4716.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/runs/[id]/page-9e4c4f751a4bea72.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/schedules/[id]/page-435f67be180b8e4f.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/schedules/new/page-f697c289c813496a.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/schedules/page-738d98dc0b63166e.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/settings/page-fc5654b31f57ac21.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/signals/[name]/page-4b1c09a539a1ebcd.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/signals/page-d2f2403dfede87cc.js +1 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/pages/_app-a3774a320f58a018.js +1 -0
- package/.next/standalone/packages/station-kit/package.json +7 -4
- package/dist/config/schema.d.ts +23 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +2 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/server/auth/keys.d.ts +91 -8
- package/dist/server/auth/keys.d.ts.map +1 -1
- package/dist/server/auth/keys.js +289 -54
- package/dist/server/auth/keys.js.map +1 -1
- package/dist/server/index.d.ts +5 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +84 -9
- package/dist/server/index.js.map +1 -1
- package/dist/server/log-store.d.ts +102 -6
- package/dist/server/log-store.d.ts.map +1 -1
- package/dist/server/log-store.js +140 -32
- package/dist/server/log-store.js.map +1 -1
- package/dist/server/middleware/auth.js +1 -1
- package/dist/server/middleware/auth.js.map +1 -1
- package/dist/server/routes/broadcasts.d.ts.map +1 -1
- package/dist/server/routes/broadcasts.js +3 -1
- package/dist/server/routes/broadcasts.js.map +1 -1
- package/dist/server/routes/runs.js +1 -1
- package/dist/server/routes/runs.js.map +1 -1
- package/dist/server/routes/v1/definitions.d.ts +21 -0
- package/dist/server/routes/v1/definitions.d.ts.map +1 -0
- package/dist/server/routes/v1/definitions.js +139 -0
- package/dist/server/routes/v1/definitions.js.map +1 -0
- package/dist/server/routes/v1/expressions.d.ts +3 -0
- package/dist/server/routes/v1/expressions.d.ts.map +1 -0
- package/dist/server/routes/v1/expressions.js +56 -0
- package/dist/server/routes/v1/expressions.js.map +1 -0
- package/dist/server/routes/v1/keys.js +3 -3
- package/dist/server/routes/v1/keys.js.map +1 -1
- package/dist/server/routes/v1/runs.js +1 -1
- package/dist/server/routes/v1/runs.js.map +1 -1
- package/dist/server/routes/v1/schedules.d.ts +10 -0
- package/dist/server/routes/v1/schedules.d.ts.map +1 -0
- package/dist/server/routes/v1/schedules.js +169 -0
- package/dist/server/routes/v1/schedules.js.map +1 -0
- package/dist/server/routes/v1/trigger.d.ts.map +1 -1
- package/dist/server/routes/v1/trigger.js +21 -0
- package/dist/server/routes/v1/trigger.js.map +1 -1
- package/package.json +12 -9
- package/src/app/broadcasts/components/broadcast-builder.tsx +535 -0
- package/src/app/broadcasts/components/dag-editor.tsx +510 -0
- package/src/app/broadcasts/dyn/[name]/dynamic-detail.tsx +243 -0
- package/src/app/broadcasts/dyn/[name]/page.tsx +10 -0
- package/src/app/broadcasts/dyn/[name]/v/[n]/page.tsx +10 -0
- package/src/app/broadcasts/dyn/[name]/v/[n]/version-view.tsx +285 -0
- package/src/app/broadcasts/new/page.tsx +102 -0
- package/src/app/broadcasts/page.tsx +176 -91
- package/src/app/components/api-panel.tsx +151 -0
- package/src/app/components/shell.tsx +23 -0
- package/src/app/hooks/use-api.ts +117 -0
- package/src/app/playground/expression/page.tsx +245 -0
- package/src/app/schedules/[id]/page.tsx +10 -0
- package/src/app/schedules/[id]/schedule-editor.tsx +195 -0
- package/src/app/schedules/components/schedule-form.tsx +140 -0
- package/src/app/schedules/new/page.tsx +166 -0
- package/src/app/schedules/page.tsx +126 -0
- package/src/config/schema.ts +25 -0
- package/src/server/auth/keys.ts +348 -58
- package/src/server/index.ts +118 -11
- package/src/server/log-store.ts +196 -45
- package/src/server/middleware/auth.ts +1 -1
- package/src/server/routes/broadcasts.ts +3 -1
- package/src/server/routes/runs.ts +1 -1
- package/src/server/routes/v1/definitions.ts +164 -0
- package/src/server/routes/v1/expressions.ts +76 -0
- package/src/server/routes/v1/keys.ts +3 -3
- package/src/server/routes/v1/runs.ts +1 -1
- package/src/server/routes/v1/schedules.ts +176 -0
- package/src/server/routes/v1/trigger.ts +27 -0
- package/.next/standalone/packages/station-kit/.next/static/chunks/580-f007f4d4c050db4e.js +0 -1
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/broadcasts/[id]/page-a0a20cccda13a0e9.js +0 -1
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/broadcasts/page-937eb876f9087bc9.js +0 -1
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/layout-68cd71116ba65cd8.js +0 -1
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/page-70b0c0958c03459a.js +0 -1
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/runs/[id]/page-01f8040619fe56c5.js +0 -1
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/settings/page-beac11049f90da31.js +0 -1
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/signals/[name]/page-931e6a38a4a53d25.js +0 -1
- package/.next/standalone/packages/station-kit/.next/static/chunks/app/signals/page-6a123a355d93fec5.js +0 -1
- package/.next/standalone/packages/station-kit/.next/static/chunks/pages/_app-0a7b2e66ecbe3f0a.js +0 -1
- package/.next/standalone/packages/station-kit/.next/static/xYd6dn0Ox68DaamIrH_pB/_buildManifest.js +0 -1
- /package/.next/standalone/packages/station-kit/.next/static/{xYd6dn0Ox68DaamIrH_pB → THKSkCipW_pj0F6DRXYEG}/_ssgManifest.js +0 -0
package/dist/server/auth/keys.js
CHANGED
|
@@ -1,12 +1,152 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, writeSync, } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { dirname } from "node:path";
|
|
5
|
+
/**
|
|
6
|
+
* Default ApiKeyStorageAdapter backed by a JSON file. Used by the Station
|
|
7
|
+
* server when no `keyStorage` is configured. Has no native dependencies —
|
|
8
|
+
* works on any Node 18+ install without compiling bindings.
|
|
9
|
+
*
|
|
10
|
+
* Crash-safety: writes go through a fsync'd tmp-file + rename, with a
|
|
11
|
+
* second fsync on the parent directory so the rename itself survives
|
|
12
|
+
* power loss. The keys file is created with `0o600` and the parent dir
|
|
13
|
+
* with `0o700` so a default umask doesn't expose key metadata.
|
|
14
|
+
*
|
|
15
|
+
* Single-process only: do not point two `createStation` instances at
|
|
16
|
+
* the same file or last-rename-wins will silently clobber writes. For
|
|
17
|
+
* multi-process or high-volume deployments, implement
|
|
18
|
+
* `ApiKeyStorageAdapter` against Postgres / MySQL / Redis.
|
|
19
|
+
*/
|
|
20
|
+
export class FileKeyStorage {
|
|
21
|
+
filePath;
|
|
22
|
+
records = new Map();
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.filePath = options.filePath;
|
|
25
|
+
mkdirSync(dirname(this.filePath), { recursive: true, mode: 0o700 });
|
|
26
|
+
this.load();
|
|
27
|
+
}
|
|
28
|
+
load() {
|
|
29
|
+
if (!existsSync(this.filePath))
|
|
30
|
+
return;
|
|
31
|
+
try {
|
|
32
|
+
const raw = readFileSync(this.filePath, "utf8");
|
|
33
|
+
const data = JSON.parse(raw);
|
|
34
|
+
if (Array.isArray(data)) {
|
|
35
|
+
for (const r of data)
|
|
36
|
+
this.records.set(r.id, r);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Corrupt or unreadable file — start fresh rather than throwing.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
flush() {
|
|
44
|
+
const tmp = `${this.filePath}.tmp`;
|
|
45
|
+
const body = JSON.stringify(Array.from(this.records.values()), null, 2);
|
|
46
|
+
// Write tmp file with fsync so its bytes are durable before rename.
|
|
47
|
+
const fd = openSync(tmp, "w", 0o600);
|
|
48
|
+
try {
|
|
49
|
+
writeSync(fd, body);
|
|
50
|
+
fsyncSync(fd);
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
closeSync(fd);
|
|
54
|
+
}
|
|
55
|
+
renameSync(tmp, this.filePath);
|
|
56
|
+
// fsync the parent directory so the rename's directory entry survives
|
|
57
|
+
// a crash. Best-effort: opening a directory for fsync isn't supported
|
|
58
|
+
// on every platform (notably Windows), so swallow errors.
|
|
59
|
+
try {
|
|
60
|
+
const dirFd = openSync(dirname(this.filePath), "r");
|
|
61
|
+
try {
|
|
62
|
+
fsyncSync(dirFd);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
closeSync(dirFd);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Platform doesn't support directory fsync; rename + tmp fsync
|
|
70
|
+
// already give us most of the durability we can offer.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
insert(record) {
|
|
74
|
+
this.records.set(record.id, { ...record });
|
|
75
|
+
this.flush();
|
|
76
|
+
}
|
|
77
|
+
findByHash(keyHash) {
|
|
78
|
+
for (const r of this.records.values()) {
|
|
79
|
+
if (r.keyHash === keyHash)
|
|
80
|
+
return { ...r };
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
list() {
|
|
85
|
+
return Array.from(this.records.values())
|
|
86
|
+
.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
|
87
|
+
.map((r) => {
|
|
88
|
+
const { keyHash: _h, ...rest } = r;
|
|
89
|
+
return rest;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
touch(id, lastUsedIso) {
|
|
93
|
+
const r = this.records.get(id);
|
|
94
|
+
if (!r)
|
|
95
|
+
return;
|
|
96
|
+
r.lastUsed = lastUsedIso;
|
|
97
|
+
this.flush();
|
|
98
|
+
}
|
|
99
|
+
revoke(id) {
|
|
100
|
+
const r = this.records.get(id);
|
|
101
|
+
if (!r)
|
|
102
|
+
return false;
|
|
103
|
+
r.revoked = true;
|
|
104
|
+
this.flush();
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
let cachedBetterSqlite3 = null;
|
|
109
|
+
function loadBetterSqlite3() {
|
|
110
|
+
if (cachedBetterSqlite3)
|
|
111
|
+
return cachedBetterSqlite3;
|
|
112
|
+
try {
|
|
113
|
+
const requireFn = createRequire(import.meta.url);
|
|
114
|
+
cachedBetterSqlite3 = requireFn("better-sqlite3");
|
|
115
|
+
return cachedBetterSqlite3;
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
119
|
+
throw new Error(`SqliteKeyStorage requires the optional 'better-sqlite3' package, ` +
|
|
120
|
+
`which isn't installed. Install it with:\n` +
|
|
121
|
+
` npm install better-sqlite3\n` +
|
|
122
|
+
`Or use FileKeyStorage (default) / MemoryKeyStorage instead.\n` +
|
|
123
|
+
`Underlying error: ${reason}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Optional ApiKeyStorageAdapter backed by better-sqlite3. Requires the
|
|
128
|
+
* `better-sqlite3` package to be installed separately — Station Kit no
|
|
129
|
+
* longer ships it as a hard dependency.
|
|
130
|
+
*
|
|
131
|
+
* Prefer `FileKeyStorage` (the default) unless you specifically need
|
|
132
|
+
* sqlite features (concurrent reads from multiple processes, large
|
|
133
|
+
* key catalogs, etc.).
|
|
134
|
+
*/
|
|
135
|
+
export class SqliteKeyStorage {
|
|
136
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4
137
|
db;
|
|
5
|
-
|
|
6
|
-
|
|
138
|
+
table;
|
|
139
|
+
constructor(options) {
|
|
140
|
+
const tableName = options.tableName ?? "api_keys";
|
|
141
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {
|
|
142
|
+
throw new Error(`Invalid table name "${tableName}"`);
|
|
143
|
+
}
|
|
144
|
+
this.table = tableName;
|
|
145
|
+
const Database = loadBetterSqlite3();
|
|
146
|
+
this.db = new Database(options.dbPath);
|
|
7
147
|
this.db.pragma("journal_mode = WAL");
|
|
8
148
|
this.db.exec(`
|
|
9
|
-
CREATE TABLE IF NOT EXISTS
|
|
149
|
+
CREATE TABLE IF NOT EXISTS ${this.table} (
|
|
10
150
|
id TEXT PRIMARY KEY,
|
|
11
151
|
name TEXT NOT NULL,
|
|
12
152
|
key_hash TEXT NOT NULL UNIQUE,
|
|
@@ -19,73 +159,168 @@ export class KeyStore {
|
|
|
19
159
|
)
|
|
20
160
|
`);
|
|
21
161
|
}
|
|
22
|
-
|
|
23
|
-
create(name, scopes = ["trigger", "read"]) {
|
|
24
|
-
const id = crypto.randomUUID();
|
|
25
|
-
const rawKey = `sk_live_${crypto.randomBytes(16).toString("hex")}`;
|
|
26
|
-
const keyHash = crypto.createHash("sha256").update(rawKey).digest("hex");
|
|
27
|
-
const keyPrefix = rawKey.slice(0, 12);
|
|
28
|
-
const createdAt = new Date().toISOString();
|
|
162
|
+
insert(record) {
|
|
29
163
|
this.db.prepare(`
|
|
30
|
-
INSERT INTO
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
164
|
+
INSERT INTO ${this.table}
|
|
165
|
+
(id, name, key_hash, key_prefix, scopes, created_at, last_used, expires_at, revoked)
|
|
166
|
+
VALUES
|
|
167
|
+
(@id, @name, @key_hash, @key_prefix, @scopes, @created_at, @last_used, @expires_at, @revoked)
|
|
168
|
+
`).run({
|
|
169
|
+
id: record.id,
|
|
170
|
+
name: record.name,
|
|
171
|
+
key_hash: record.keyHash,
|
|
172
|
+
key_prefix: record.keyPrefix,
|
|
173
|
+
scopes: JSON.stringify(record.scopes),
|
|
174
|
+
created_at: record.createdAt,
|
|
175
|
+
last_used: record.lastUsed,
|
|
176
|
+
expires_at: record.expiresAt,
|
|
177
|
+
revoked: record.revoked ? 1 : 0,
|
|
178
|
+
});
|
|
37
179
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`).get(keyHash);
|
|
45
|
-
if (!row)
|
|
46
|
-
return null;
|
|
47
|
-
if (row.revoked)
|
|
48
|
-
return null;
|
|
49
|
-
if (row.expires_at && new Date(row.expires_at) < new Date())
|
|
50
|
-
return null;
|
|
51
|
-
// Update last_used
|
|
52
|
-
this.db.prepare("UPDATE api_keys SET last_used = ? WHERE id = ?").run(new Date().toISOString(), row.id);
|
|
53
|
-
return {
|
|
54
|
-
id: row.id,
|
|
55
|
-
name: row.name,
|
|
56
|
-
keyHash: row.key_hash,
|
|
57
|
-
keyPrefix: row.key_prefix,
|
|
58
|
-
scopes: JSON.parse(row.scopes),
|
|
59
|
-
createdAt: row.created_at,
|
|
60
|
-
lastUsed: row.last_used,
|
|
61
|
-
expiresAt: row.expires_at,
|
|
62
|
-
revoked: Boolean(row.revoked),
|
|
63
|
-
};
|
|
180
|
+
findByHash(keyHash) {
|
|
181
|
+
const row = this.db
|
|
182
|
+
.prepare(`SELECT id, name, key_hash, key_prefix, scopes, created_at, last_used, expires_at, revoked
|
|
183
|
+
FROM ${this.table} WHERE key_hash = ?`)
|
|
184
|
+
.get(keyHash);
|
|
185
|
+
return row ? rowToApiKey(row) : null;
|
|
64
186
|
}
|
|
65
|
-
/** List all keys (without hashes). */
|
|
66
187
|
list() {
|
|
67
|
-
const rows = this.db
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
188
|
+
const rows = this.db
|
|
189
|
+
.prepare(`SELECT id, name, key_prefix, scopes, created_at, last_used, expires_at, revoked
|
|
190
|
+
FROM ${this.table} ORDER BY created_at DESC`)
|
|
191
|
+
.all();
|
|
71
192
|
return rows.map((row) => ({
|
|
72
193
|
id: row.id,
|
|
73
194
|
name: row.name,
|
|
74
195
|
keyPrefix: row.key_prefix,
|
|
75
196
|
scopes: JSON.parse(row.scopes),
|
|
76
197
|
createdAt: row.created_at,
|
|
77
|
-
lastUsed: row.last_used,
|
|
78
|
-
expiresAt: row.expires_at,
|
|
198
|
+
lastUsed: row.last_used ?? null,
|
|
199
|
+
expiresAt: row.expires_at ?? null,
|
|
79
200
|
revoked: Boolean(row.revoked),
|
|
80
201
|
}));
|
|
81
202
|
}
|
|
82
|
-
|
|
203
|
+
touch(id, lastUsedIso) {
|
|
204
|
+
this.db.prepare(`UPDATE ${this.table} SET last_used = ? WHERE id = ?`).run(lastUsedIso, id);
|
|
205
|
+
}
|
|
83
206
|
revoke(id) {
|
|
84
|
-
const result = this.db.prepare(
|
|
207
|
+
const result = this.db.prepare(`UPDATE ${this.table} SET revoked = 1 WHERE id = ?`).run(id);
|
|
85
208
|
return result.changes > 0;
|
|
86
209
|
}
|
|
87
210
|
close() {
|
|
88
211
|
this.db.close();
|
|
89
212
|
}
|
|
90
213
|
}
|
|
214
|
+
function rowToApiKey(row) {
|
|
215
|
+
return {
|
|
216
|
+
id: row.id,
|
|
217
|
+
name: row.name,
|
|
218
|
+
keyHash: row.key_hash,
|
|
219
|
+
keyPrefix: row.key_prefix,
|
|
220
|
+
scopes: JSON.parse(row.scopes),
|
|
221
|
+
createdAt: row.created_at,
|
|
222
|
+
lastUsed: row.last_used ?? null,
|
|
223
|
+
expiresAt: row.expires_at ?? null,
|
|
224
|
+
revoked: Boolean(row.revoked),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
// ─── In-memory storage for tests / ephemeral deployments ────────────
|
|
228
|
+
export class MemoryKeyStorage {
|
|
229
|
+
records = new Map();
|
|
230
|
+
insert(record) {
|
|
231
|
+
this.records.set(record.id, { ...record });
|
|
232
|
+
}
|
|
233
|
+
findByHash(keyHash) {
|
|
234
|
+
for (const r of this.records.values()) {
|
|
235
|
+
if (r.keyHash === keyHash)
|
|
236
|
+
return { ...r };
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
list() {
|
|
241
|
+
return Array.from(this.records.values())
|
|
242
|
+
.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
|
243
|
+
.map((r) => {
|
|
244
|
+
const { keyHash: _h, ...rest } = r;
|
|
245
|
+
return rest;
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
touch(id, lastUsedIso) {
|
|
249
|
+
const r = this.records.get(id);
|
|
250
|
+
if (r)
|
|
251
|
+
r.lastUsed = lastUsedIso;
|
|
252
|
+
}
|
|
253
|
+
revoke(id) {
|
|
254
|
+
const r = this.records.get(id);
|
|
255
|
+
if (!r)
|
|
256
|
+
return false;
|
|
257
|
+
r.revoked = true;
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// ─── KeyStore — owns crypto, delegates persistence ──────────────────
|
|
262
|
+
export class KeyStore {
|
|
263
|
+
storage;
|
|
264
|
+
/**
|
|
265
|
+
* Pass an `ApiKeyStorageAdapter` for any backend. The string overload is
|
|
266
|
+
* retained for backwards compatibility — it constructs a FileKeyStorage
|
|
267
|
+
* at the given path. (Previously this returned a SqliteKeyStorage; SQLite
|
|
268
|
+
* is now opt-in to avoid native build dependencies.)
|
|
269
|
+
*/
|
|
270
|
+
constructor(storageOrPath) {
|
|
271
|
+
if (typeof storageOrPath === "string") {
|
|
272
|
+
const filePath = storageOrPath.endsWith(".db")
|
|
273
|
+
? storageOrPath.replace(/\.db$/, ".json")
|
|
274
|
+
: storageOrPath;
|
|
275
|
+
this.storage = new FileKeyStorage({ filePath });
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
this.storage = storageOrPath;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/** Generate a new API key. Returns the full key (only shown once) and the stored record. */
|
|
282
|
+
async create(name, scopes = ["trigger", "read"]) {
|
|
283
|
+
const id = crypto.randomUUID();
|
|
284
|
+
const rawKey = `sk_live_${crypto.randomBytes(16).toString("hex")}`;
|
|
285
|
+
const keyHash = crypto.createHash("sha256").update(rawKey).digest("hex");
|
|
286
|
+
const keyPrefix = rawKey.slice(0, 12);
|
|
287
|
+
const createdAt = new Date().toISOString();
|
|
288
|
+
const record = {
|
|
289
|
+
id, name, keyHash, keyPrefix, scopes, createdAt,
|
|
290
|
+
lastUsed: null, expiresAt: null, revoked: false,
|
|
291
|
+
};
|
|
292
|
+
await this.storage.insert(record);
|
|
293
|
+
return { key: rawKey, record };
|
|
294
|
+
}
|
|
295
|
+
/** Verify an API key. Returns the key record if valid, null otherwise. */
|
|
296
|
+
async verify(rawKey) {
|
|
297
|
+
const keyHash = crypto.createHash("sha256").update(rawKey).digest("hex");
|
|
298
|
+
const record = await this.storage.findByHash(keyHash);
|
|
299
|
+
if (!record)
|
|
300
|
+
return null;
|
|
301
|
+
if (record.revoked)
|
|
302
|
+
return null;
|
|
303
|
+
if (record.expiresAt && new Date(record.expiresAt) < new Date())
|
|
304
|
+
return null;
|
|
305
|
+
// Touch is best-effort — don't block verification on the write. Wrap in
|
|
306
|
+
// an explicit deferred so a synchronous throw from a sync `touch()` is
|
|
307
|
+
// also swallowed, matching the async case.
|
|
308
|
+
Promise.resolve()
|
|
309
|
+
.then(() => this.storage.touch(record.id, new Date().toISOString()))
|
|
310
|
+
.catch(() => { });
|
|
311
|
+
return record;
|
|
312
|
+
}
|
|
313
|
+
/** List all keys (without hashes). */
|
|
314
|
+
async list() {
|
|
315
|
+
return this.storage.list();
|
|
316
|
+
}
|
|
317
|
+
/** Revoke a key by ID. */
|
|
318
|
+
async revoke(id) {
|
|
319
|
+
return this.storage.revoke(id);
|
|
320
|
+
}
|
|
321
|
+
async close() {
|
|
322
|
+
if (this.storage.close)
|
|
323
|
+
await this.storage.close();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
91
326
|
//# sourceMappingURL=keys.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/server/auth/keys.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,QAAQ,MAAM,
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/server/auth/keys.ts"],"names":[],"mappings":"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"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { StationConfig } from "../config/schema.js";
|
|
2
2
|
import { KeyStore } from "./auth/keys.js";
|
|
3
|
-
export { KeyStore } from "./auth/keys.js";
|
|
4
|
-
export type { ApiKey } from "./auth/keys.js";
|
|
3
|
+
export { KeyStore, FileKeyStorage, SqliteKeyStorage, MemoryKeyStorage, } from "./auth/keys.js";
|
|
4
|
+
export type { ApiKey, ApiKeyPublic, ApiKeyStorageAdapter, FileKeyStorageOptions, SqliteKeyStorageOptions, } from "./auth/keys.js";
|
|
5
|
+
export { LogStore, FileLogStorage, MemoryLogStorage, } from "./log-store.js";
|
|
6
|
+
export type { LogStorageAdapter, FileLogStorageOptions, } from "./log-store.js";
|
|
7
|
+
export type { LogEntry } from "./log-buffer.js";
|
|
5
8
|
export interface StationInstance {
|
|
6
9
|
start(): Promise<void>;
|
|
7
10
|
stop(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAWzD,OAAO,EAAE,QAAQ,EAAkB,MAAM,gBAAgB,CAAC;AAiB1D,OAAO,EACL,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,MAAM,EACN,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAiWnH"}
|
package/dist/server/index.js
CHANGED
|
@@ -3,19 +3,20 @@ import { createMiddleware } from "hono/factory";
|
|
|
3
3
|
import { serve } from "@hono/node-server";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
|
-
import { SignalRunner, MemoryAdapter } from "station-signal";
|
|
6
|
+
import { SignalRunner, MemoryAdapter, parseInterval } from "station-signal";
|
|
7
7
|
import { BroadcastRunner, BroadcastMemoryAdapter } from "station-broadcast";
|
|
8
|
+
import { ScheduleReconciler, } from "station-schedules";
|
|
8
9
|
import { ensureStationDir } from "../station-dir.js";
|
|
9
10
|
import { WebSocketHub } from "./ws.js";
|
|
10
11
|
import { SSEHub } from "./sse.js";
|
|
11
12
|
import { LogBuffer } from "./log-buffer.js";
|
|
12
|
-
import { LogStore } from "./log-store.js";
|
|
13
|
+
import { LogStore, FileLogStorage } from "./log-store.js";
|
|
13
14
|
import { StationSignalSubscriber, StationBroadcastSubscriber } from "./subscriber.js";
|
|
14
15
|
import { healthRoutes } from "./routes/health.js";
|
|
15
16
|
import { signalRoutes } from "./routes/signals.js";
|
|
16
17
|
import { runRoutes } from "./routes/runs.js";
|
|
17
18
|
import { broadcastRoutes } from "./routes/broadcasts.js";
|
|
18
|
-
import { KeyStore } from "./auth/keys.js";
|
|
19
|
+
import { KeyStore, FileKeyStorage } from "./auth/keys.js";
|
|
19
20
|
import { verifySessionToken, verifyCredentials, createSessionToken } from "./auth/session.js";
|
|
20
21
|
import { authResolver } from "./middleware/auth.js";
|
|
21
22
|
import { requireScope } from "./middleware/scope-guard.js";
|
|
@@ -28,20 +29,30 @@ import { v1TriggerRoutes } from "./routes/v1/trigger.js";
|
|
|
28
29
|
import { v1KeyRoutes } from "./routes/v1/keys.js";
|
|
29
30
|
import { v1AuthRoutes } from "./routes/v1/auth.js";
|
|
30
31
|
import { v1EventRoutes } from "./routes/v1/events.js";
|
|
31
|
-
|
|
32
|
+
import { v1DefinitionRoutes, v1DefinitionReadRoutes } from "./routes/v1/definitions.js";
|
|
33
|
+
import { v1ScheduleRoutes, v1ScheduleReadRoutes } from "./routes/v1/schedules.js";
|
|
34
|
+
import { v1ExpressionRoutes } from "./routes/v1/expressions.js";
|
|
35
|
+
export { KeyStore, FileKeyStorage, SqliteKeyStorage, MemoryKeyStorage, } from "./auth/keys.js";
|
|
36
|
+
export { LogStore, FileLogStorage, MemoryLogStorage, } from "./log-store.js";
|
|
32
37
|
export async function createStation(config, cwd, nextPort) {
|
|
33
38
|
const signalAdapter = config.adapter ?? new MemoryAdapter();
|
|
34
39
|
const broadcastAdapter = config.broadcastAdapter ?? (config.broadcastsDir ? new BroadcastMemoryAdapter() : undefined);
|
|
35
40
|
const { dataDir } = ensureStationDir(cwd, config.stationDir);
|
|
41
|
+
warnIfLegacySqliteFiles(dataDir);
|
|
36
42
|
const wsHub = new WebSocketHub();
|
|
37
43
|
const sseHub = new SSEHub();
|
|
38
44
|
const logBuffer = new LogBuffer();
|
|
39
|
-
const logStore = new LogStore(
|
|
45
|
+
const logStore = new LogStore(config.logStorage ?? new FileLogStorage({
|
|
46
|
+
filePath: resolve(dataDir, "station-logs.jsonl"),
|
|
47
|
+
onError: (err) => console.error("[station] log write failed:", err),
|
|
48
|
+
}));
|
|
40
49
|
// Auth: create KeyStore and SessionConfig if auth is configured
|
|
41
50
|
let keyStore;
|
|
42
51
|
let sessionConfig;
|
|
43
52
|
if (config.auth) {
|
|
44
|
-
|
|
53
|
+
const storage = config.auth.keyStorage
|
|
54
|
+
?? new FileKeyStorage({ filePath: resolve(dataDir, "station-keys.json") });
|
|
55
|
+
keyStore = new KeyStore(storage);
|
|
45
56
|
sessionConfig = {
|
|
46
57
|
username: config.auth.username,
|
|
47
58
|
password: config.auth.password,
|
|
@@ -68,7 +79,20 @@ export async function createStation(config, cwd, nextPort) {
|
|
|
68
79
|
// Create runners if enabled
|
|
69
80
|
let signalRunner;
|
|
70
81
|
let broadcastRunner;
|
|
82
|
+
const scheduleAdapter = config.scheduleAdapter;
|
|
71
83
|
if (config.runRunners) {
|
|
84
|
+
// Build schedule reconcilers up front. Each reconciler handles only the
|
|
85
|
+
// kinds it's responsible for; the runner ticks it once per loop.
|
|
86
|
+
const signalScheduleReconciler = scheduleAdapter
|
|
87
|
+
? new ScheduleReconciler({
|
|
88
|
+
adapter: scheduleAdapter,
|
|
89
|
+
kinds: ["signal"],
|
|
90
|
+
parseInterval,
|
|
91
|
+
triggerFn: (s) => signalRunner.triggerSignal(s.target, s.input ?? {}),
|
|
92
|
+
hasPendingOrRunning: (s) => signalRunner.hasPendingOrRunningForSignal(s.target),
|
|
93
|
+
onError: (err) => console.error("[station] Signal schedule reconciler:", err),
|
|
94
|
+
})
|
|
95
|
+
: undefined;
|
|
72
96
|
signalRunner = new SignalRunner({
|
|
73
97
|
signalsDir,
|
|
74
98
|
adapter: signalAdapter,
|
|
@@ -77,14 +101,26 @@ export async function createStation(config, cwd, nextPort) {
|
|
|
77
101
|
maxAttempts: config.runner.maxAttempts,
|
|
78
102
|
retryBackoffMs: config.runner.retryBackoffMs,
|
|
79
103
|
subscribers: [stationSignalSub],
|
|
104
|
+
scheduleReconciler: signalScheduleReconciler,
|
|
80
105
|
});
|
|
81
106
|
if (broadcastsDir || broadcastAdapter) {
|
|
107
|
+
const broadcastScheduleReconciler = scheduleAdapter
|
|
108
|
+
? new ScheduleReconciler({
|
|
109
|
+
adapter: scheduleAdapter,
|
|
110
|
+
kinds: ["broadcast-static", "broadcast-dynamic"],
|
|
111
|
+
parseInterval,
|
|
112
|
+
triggerFn: (s) => broadcastRunner.trigger(s.target, s.input ?? {}),
|
|
113
|
+
hasPendingOrRunning: (s) => broadcastRunner.hasPendingOrRunningForBroadcast(s.target),
|
|
114
|
+
onError: (err) => console.error("[station] Broadcast schedule reconciler:", err),
|
|
115
|
+
})
|
|
116
|
+
: undefined;
|
|
82
117
|
broadcastRunner = new BroadcastRunner({
|
|
83
118
|
signalRunner,
|
|
84
119
|
broadcastsDir,
|
|
85
120
|
adapter: broadcastAdapter ?? new BroadcastMemoryAdapter(),
|
|
86
121
|
pollIntervalMs: config.broadcastRunner.pollIntervalMs,
|
|
87
122
|
subscribers: [stationBroadcastSub],
|
|
123
|
+
scheduleReconciler: broadcastScheduleReconciler,
|
|
88
124
|
});
|
|
89
125
|
}
|
|
90
126
|
}
|
|
@@ -166,6 +202,15 @@ export async function createStation(config, cwd, nextPort) {
|
|
|
166
202
|
readRoutes.route("/", v1RunRoutes({ signalRunner, signalAdapter, logBuffer, logStore }));
|
|
167
203
|
readRoutes.route("/", v1BroadcastRoutes({ broadcastRunner, broadcastAdapter, broadcastSubscriber: stationBroadcastSub }));
|
|
168
204
|
readRoutes.route("/", v1EventRoutes({ sseHub }));
|
|
205
|
+
readRoutes.route("/", v1ExpressionRoutes());
|
|
206
|
+
// Schedule GET + preview are read-scoped; mutating routes are mounted under admin below.
|
|
207
|
+
readRoutes.route("/", v1ScheduleReadRoutes({ scheduleAdapter }));
|
|
208
|
+
readRoutes.route("/", v1DefinitionReadRoutes({
|
|
209
|
+
broadcastRunner,
|
|
210
|
+
broadcastAdapter,
|
|
211
|
+
signalRunner,
|
|
212
|
+
signalSubscriber: stationSignalSub,
|
|
213
|
+
}));
|
|
169
214
|
v1.route("/", readRoutes);
|
|
170
215
|
// Trigger-scope routes
|
|
171
216
|
const triggerRoutes = new Hono();
|
|
@@ -198,10 +243,17 @@ export async function createStation(config, cwd, nextPort) {
|
|
|
198
243
|
return c.json({ data: { cancelled: true } });
|
|
199
244
|
});
|
|
200
245
|
v1.route("/", cancelRoutes);
|
|
201
|
-
// Admin-scope routes
|
|
246
|
+
// Admin-scope routes — destructive / mutating endpoints
|
|
202
247
|
const adminRoutes = new Hono();
|
|
203
248
|
adminRoutes.use("/*", requireScope("admin"));
|
|
204
249
|
adminRoutes.route("/", v1KeyRoutes({ keyStore }));
|
|
250
|
+
adminRoutes.route("/", v1DefinitionRoutes({
|
|
251
|
+
broadcastRunner,
|
|
252
|
+
broadcastAdapter,
|
|
253
|
+
signalRunner,
|
|
254
|
+
signalSubscriber: stationSignalSub,
|
|
255
|
+
}));
|
|
256
|
+
adminRoutes.route("/", v1ScheduleRoutes({ scheduleAdapter }));
|
|
205
257
|
v1.route("/", adminRoutes);
|
|
206
258
|
app.route("/api/v1", v1);
|
|
207
259
|
// ── Proxy to Next.js standalone server ──────────────────────────
|
|
@@ -283,12 +335,35 @@ export async function createStation(config, cwd, nextPort) {
|
|
|
283
335
|
}
|
|
284
336
|
wsHub.close();
|
|
285
337
|
sseHub.close();
|
|
286
|
-
logStore.close();
|
|
287
|
-
keyStore?.close();
|
|
338
|
+
await logStore.close();
|
|
339
|
+
await keyStore?.close();
|
|
288
340
|
if (httpServer) {
|
|
289
341
|
httpServer.close();
|
|
290
342
|
}
|
|
291
343
|
},
|
|
292
344
|
};
|
|
293
345
|
}
|
|
346
|
+
// Existing deployments that ran older Station versions persisted keys
|
|
347
|
+
// to `station-keys.db` (SQLite) and run logs to `station-logs.db`. The
|
|
348
|
+
// new defaults are `station-keys.json` and `station-logs.jsonl`; the
|
|
349
|
+
// legacy files are NOT auto-migrated. Emit a one-time warning so an
|
|
350
|
+
// upgrade doesn't silently appear to wipe a user's API keys.
|
|
351
|
+
function warnIfLegacySqliteFiles(dataDir) {
|
|
352
|
+
const legacy = [
|
|
353
|
+
{ file: "station-keys.db", replacement: "station-keys.json" },
|
|
354
|
+
{ file: "station-logs.db", replacement: "station-logs.jsonl" },
|
|
355
|
+
];
|
|
356
|
+
for (const { file, replacement } of legacy) {
|
|
357
|
+
const legacyPath = resolve(dataDir, file);
|
|
358
|
+
if (!existsSync(legacyPath))
|
|
359
|
+
continue;
|
|
360
|
+
const replacementPath = resolve(dataDir, replacement);
|
|
361
|
+
if (existsSync(replacementPath))
|
|
362
|
+
continue;
|
|
363
|
+
console.warn(`[station] Legacy ${file} detected at ${legacyPath} but no ${replacement} found. ` +
|
|
364
|
+
`Station no longer reads SQLite-backed defaults; data in ${file} will not be loaded. ` +
|
|
365
|
+
`If you need the contents, export them with the better-sqlite3 CLI before upgrading. ` +
|
|
366
|
+
`To suppress this warning, delete or rename ${file}.`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
294
369
|
//# sourceMappingURL=index.js.map
|