sh3-server 0.8.1 → 0.8.2
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/app/assets/index-Cb-zoqb1.js +17 -0
- package/app/assets/index-Cb-zoqb1.js.map +1 -0
- package/app/assets/index-DPcN5Lor.css +1 -0
- package/app/index.html +2 -2
- package/dist/auth.d.ts +10 -3
- package/dist/auth.js +14 -22
- package/dist/caller.d.ts +16 -0
- package/dist/caller.js +54 -0
- package/dist/fs-backend.d.ts +10 -0
- package/dist/fs-backend.js +105 -0
- package/dist/index.js +18 -10
- package/dist/keys.d.ts +33 -19
- package/dist/keys.js +172 -49
- package/dist/packages.d.ts +4 -2
- package/dist/packages.js +4 -4
- package/dist/routes/admin.js +7 -3
- package/dist/routes/keys.d.ts +21 -0
- package/dist/routes/keys.js +164 -0
- package/dist/scope.d.ts +9 -0
- package/dist/scope.js +25 -0
- package/dist/shard-router.d.ts +9 -2
- package/dist/shard-router.js +58 -29
- package/dist/shell-shard/index.d.ts +4 -1
- package/package.json +1 -1
- package/app/assets/index-C3rCTpjL.js +0 -17
- package/app/assets/index-C3rCTpjL.js.map +0 -1
- package/app/assets/index-GfhVhkjD.css +0 -1
package/dist/shard-router.js
CHANGED
|
@@ -1,30 +1,51 @@
|
|
|
1
1
|
// packages/sh3-server/src/shard-router.ts
|
|
2
2
|
import { Hono } from 'hono';
|
|
3
|
-
import { mkdirSync } from 'node:fs';
|
|
3
|
+
import { mkdirSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
+
import { scopeRequired, tenantRequired } from './scope.js';
|
|
5
6
|
import { pathToFileURL } from 'node:url';
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
import { getSyncBundle, createSyncHandle, createSyncRegistry } from 'sh3-core/server-sync';
|
|
8
|
+
/** Middleware requiring the caller's scope set to include admin:*. */
|
|
9
|
+
export function adminOnly(_keys, settings) {
|
|
8
10
|
return async (c, next) => {
|
|
9
|
-
|
|
10
|
-
if (!settings.get().auth.required) {
|
|
11
|
+
if (!settings.get().auth.required)
|
|
11
12
|
return next();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const session = c.get('session') ?? c.env?.session;
|
|
15
|
-
if (session?.role === 'admin') {
|
|
13
|
+
const caller = c.get('caller');
|
|
14
|
+
if (caller?.scopes.includes('admin:*'))
|
|
16
15
|
return next();
|
|
16
|
+
return c.json({ error: 'Admin privileges required' }, 403);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const PERMISSION_DOCUMENTS_SYNC = 'documents:sync';
|
|
20
|
+
/**
|
|
21
|
+
* Build a ServerShardContext object for the given shard, wiring
|
|
22
|
+
* sync/syncRegistry based on the declared permissions.
|
|
23
|
+
*/
|
|
24
|
+
export function buildShardCtx(shardId, dataDir, permissions, keys, settings, wsRegister, documentBackend) {
|
|
25
|
+
const hasSync = permissions.includes(PERMISSION_DOCUMENTS_SYNC);
|
|
26
|
+
const ctx = {
|
|
27
|
+
shardId,
|
|
28
|
+
dataDir,
|
|
29
|
+
permissions,
|
|
30
|
+
adminOnly: adminOnly(keys, settings),
|
|
31
|
+
scopeRequired,
|
|
32
|
+
tenantRequired,
|
|
33
|
+
wsRegister,
|
|
34
|
+
};
|
|
35
|
+
ctx.sync = async (tenantId, connectorId) => {
|
|
36
|
+
if (!hasSync) {
|
|
37
|
+
throw new Error(`Shard "${shardId}" cannot call ctx.sync — missing '${PERMISSION_DOCUMENTS_SYNC}' permission in manifest.`);
|
|
17
38
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
39
|
+
const { engine, registry } = await getSyncBundle(documentBackend, tenantId);
|
|
40
|
+
return createSyncHandle({ tenantId, connectorId, engine, registry });
|
|
41
|
+
};
|
|
42
|
+
ctx.syncRegistry = (tenantId) => {
|
|
43
|
+
if (!hasSync) {
|
|
44
|
+
throw new Error(`Shard "${shardId}" cannot call ctx.syncRegistry — missing '${PERMISSION_DOCUMENTS_SYNC}' permission in manifest.`);
|
|
25
45
|
}
|
|
26
|
-
return
|
|
46
|
+
return createSyncRegistry(documentBackend, tenantId);
|
|
27
47
|
};
|
|
48
|
+
return ctx;
|
|
28
49
|
}
|
|
29
50
|
/**
|
|
30
51
|
* Dynamic shard route manager. Holds a Map of shard Hono sub-apps
|
|
@@ -44,13 +65,17 @@ export class ShardRouter {
|
|
|
44
65
|
throw new Error(`${shardId}/server.js invalid export shape — expected { id, routes }`);
|
|
45
66
|
}
|
|
46
67
|
const shardDataDir = join(ctx.pkgDir, 'data');
|
|
68
|
+
const manifestPath = join(ctx.pkgDir, 'manifest.json');
|
|
69
|
+
let permissions = [];
|
|
70
|
+
try {
|
|
71
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
72
|
+
permissions = Array.isArray(manifest.permissions) ? manifest.permissions : [];
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Framework built-ins (mountStatic) may have no sibling manifest; default to empty.
|
|
76
|
+
}
|
|
47
77
|
const router = new Hono();
|
|
48
|
-
const shardCtx =
|
|
49
|
-
shardId: shard.id,
|
|
50
|
-
dataDir: shardDataDir,
|
|
51
|
-
adminOnly: adminOnly(ctx.keys, ctx.settings),
|
|
52
|
-
wsRegister: ctx.wsRegister,
|
|
53
|
-
};
|
|
78
|
+
const shardCtx = buildShardCtx(shard.id, shardDataDir, permissions, ctx.keys, ctx.settings, ctx.wsRegister, ctx.documentBackend);
|
|
54
79
|
await shard.routes(router, shardCtx);
|
|
55
80
|
// Create data dir only after routes() succeeds — so a failure
|
|
56
81
|
// doesn't leave behind a dir that prevents install rollback cleanup.
|
|
@@ -68,13 +93,17 @@ export class ShardRouter {
|
|
|
68
93
|
throw new Error(`${shardId} static mount — expected { id, routes }, got ${typeof mod}`);
|
|
69
94
|
}
|
|
70
95
|
const shardDataDir = join(ctx.pkgDir, 'data');
|
|
96
|
+
const manifestPath = join(ctx.pkgDir, 'manifest.json');
|
|
97
|
+
let permissions = [];
|
|
98
|
+
try {
|
|
99
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
100
|
+
permissions = Array.isArray(manifest.permissions) ? manifest.permissions : [];
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Framework built-ins (mountStatic) may have no sibling manifest; default to empty.
|
|
104
|
+
}
|
|
71
105
|
const router = new Hono();
|
|
72
|
-
const shardCtx =
|
|
73
|
-
shardId: mod.id,
|
|
74
|
-
dataDir: shardDataDir,
|
|
75
|
-
adminOnly: adminOnly(ctx.keys, ctx.settings),
|
|
76
|
-
wsRegister: ctx.wsRegister,
|
|
77
|
-
};
|
|
106
|
+
const shardCtx = buildShardCtx(mod.id, shardDataDir, permissions, ctx.keys, ctx.settings, ctx.wsRegister, ctx.documentBackend);
|
|
78
107
|
await mod.routes(router, shardCtx);
|
|
79
108
|
mkdirSync(shardDataDir, { recursive: true });
|
|
80
109
|
this.shards.set(shardId, router);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
import type { Context } from 'hono';
|
|
2
|
+
import type { Context, MiddlewareHandler } from 'hono';
|
|
3
3
|
import type { WsLike } from './session-manager.js';
|
|
4
4
|
export interface ShellServerContext {
|
|
5
5
|
shardId: string;
|
|
@@ -8,6 +8,9 @@ export interface ShellServerContext {
|
|
|
8
8
|
tenantRootBase?: string;
|
|
9
9
|
adminOnly: any;
|
|
10
10
|
wsRegister: (onConnect: (ws: WsLike, c: Context) => void) => any;
|
|
11
|
+
permissions: string[];
|
|
12
|
+
scopeRequired: (scope: string) => MiddlewareHandler;
|
|
13
|
+
tenantRequired: MiddlewareHandler;
|
|
11
14
|
}
|
|
12
15
|
declare const _default: {
|
|
13
16
|
id: string;
|