@edge-base/server 0.2.3 → 0.2.5
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/admin-build/_app/immutable/chunks/{DpVAayDG.js → 6oMK_164.js} +1 -1
- package/admin-build/_app/immutable/chunks/{B5Nwfelm.js → B2TnDKF7.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DCvwWZrm.js → B6MschND.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Du5vWVa2.js → B94PilAN.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Dc1-6Po6.js → BEW7Ez_g.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Dlty5069.js → BoOooyH6.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CzSAxmuj.js → BqTb6Mxk.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DCKcAiQH.js → BvHnF5tV.js} +1 -1
- package/admin-build/_app/immutable/chunks/{B-_-hJ9o.js → CaVKAiCe.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DRqPU3wD.js → Cdm5zBRA.js} +1 -1
- package/admin-build/_app/immutable/chunks/{byv2rTy8.js → CrOZMmdF.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DiyBpamp.js → Cw6OYcq-.js} +1 -1
- package/admin-build/_app/immutable/chunks/{A_3UuvCe.js → D2j3I1VQ.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BxoNtYHK.js → DPdQ7z0T.js} +3 -3
- package/admin-build/_app/immutable/chunks/{nZvorU8i.js → J2Gw0SMu.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CZ0TVkCa.js → pUxw8jfq.js} +1 -1
- package/admin-build/_app/immutable/entry/{app.CfrmEXPD.js → app.D3flihMw.js} +2 -2
- package/admin-build/_app/immutable/entry/start.Cl6sLxnz.js +1 -0
- package/admin-build/_app/immutable/nodes/{0.Cn2BZ4da.js → 0.CdczqZLK.js} +1 -1
- package/admin-build/_app/immutable/nodes/{1.Dv4LX_Co.js → 1.DxcSsEqS.js} +1 -1
- package/admin-build/_app/immutable/nodes/{10.DPVv3kat.js → 10.DuAd4aIm.js} +1 -1
- package/admin-build/_app/immutable/nodes/{11.CiCb6Ayu.js → 11.0jgHQL92.js} +1 -1
- package/admin-build/_app/immutable/nodes/{12.CIPyeekF.js → 12.CKNPqmyy.js} +1 -1
- package/admin-build/_app/immutable/nodes/{13.Z15Lt36e.js → 13.B1p2POXS.js} +1 -1
- package/admin-build/_app/immutable/nodes/{14.s0l5bAq3.js → 14.Bb-REBND.js} +1 -1
- package/admin-build/_app/immutable/nodes/{15.UwSSNO76.js → 15.1uBFCX0X.js} +1 -1
- package/admin-build/_app/immutable/nodes/{16.qiD8i883.js → 16.BR7WwQrS.js} +1 -1
- package/admin-build/_app/immutable/nodes/{17.Dy3dcSvu.js → 17.Cm57KKXV.js} +1 -1
- package/admin-build/_app/immutable/nodes/{18.DeXyPYsO.js → 18.CoiwfAuQ.js} +1 -1
- package/admin-build/_app/immutable/nodes/{19.CAbuyS6w.js → 19.B8ZdLlXj.js} +1 -1
- package/admin-build/_app/immutable/nodes/{20.Bec0T7un.js → 20.DnHeFlTv.js} +1 -1
- package/admin-build/_app/immutable/nodes/21.CJFaf0Ia.js +1 -0
- package/admin-build/_app/immutable/nodes/{22.CdVprrv2.js → 22.CItETFzy.js} +1 -1
- package/admin-build/_app/immutable/nodes/{23.Y8RzVLoF.js → 23.CWSGMcKJ.js} +1 -1
- package/admin-build/_app/immutable/nodes/{24.CWhHYFBx.js → 24.CWbEqNMB.js} +1 -1
- package/admin-build/_app/immutable/nodes/{25.wCBplOVt.js → 25.DRkLEhKi.js} +1 -1
- package/admin-build/_app/immutable/nodes/{26.Cod_JRFK.js → 26.BRxO8AYH.js} +1 -1
- package/admin-build/_app/immutable/nodes/{27.BO2HVMu9.js → 27.BLs-nVHz.js} +1 -1
- package/admin-build/_app/immutable/nodes/{28.DxG-FBVQ.js → 28.G79qkdBK.js} +1 -1
- package/admin-build/_app/immutable/nodes/{29.CjGqWGvE.js → 29.BOcI6g0N.js} +1 -1
- package/admin-build/_app/immutable/nodes/{3.By3_OmdZ.js → 3.B6q-7qr8.js} +1 -1
- package/admin-build/_app/immutable/nodes/{30.M_H7Htpq.js → 30.DAIC7dKd.js} +1 -1
- package/admin-build/_app/immutable/nodes/{31.DEU18izM.js → 31.pl0XXjXF.js} +1 -1
- package/admin-build/_app/immutable/nodes/{4.DeYhKtzJ.js → 4.DOdvVlZj.js} +1 -1
- package/admin-build/_app/immutable/nodes/{5.9WLgxhrD.js → 5.BW_zlgye.js} +1 -1
- package/admin-build/_app/immutable/nodes/{6.BdT2i_dd.js → 6.Dxy1CAI2.js} +1 -1
- package/admin-build/_app/immutable/nodes/{7.CHq0s4K6.js → 7.BG98w_o7.js} +1 -1
- package/admin-build/_app/immutable/nodes/{8.DuvRw-XZ.js → 8.DoG5R2rG.js} +1 -1
- package/admin-build/_app/immutable/nodes/{9.C2Ub82wn.js → 9.Dmxf6zAC.js} +1 -1
- package/admin-build/_app/version.json +1 -1
- package/admin-build/index.html +7 -7
- package/package.json +3 -3
- package/src/__tests__/admin-data-routes.test.ts +29 -0
- package/src/__tests__/database-do-route-validation.test.ts +108 -0
- package/src/__tests__/database-live-route.test.ts +82 -0
- package/src/__tests__/do-router.test.ts +116 -0
- package/src/__tests__/functions-context.test.ts +84 -0
- package/src/__tests__/functions-d1-proxy.test.ts +54 -0
- package/src/__tests__/meta-route-registration.test.ts +20 -15
- package/src/__tests__/plugin-migration-routing.test.ts +32 -0
- package/src/__tests__/provider-aware-sql.test.ts +9 -3
- package/src/__tests__/room-auth-state-loss.test.ts +122 -0
- package/src/__tests__/room-handler-context.test.ts +4 -4
- package/src/__tests__/room-rate-limit-scopes.test.ts +38 -0
- package/src/__tests__/runtime-startup.test.ts +49 -0
- package/src/__tests__/scheduled.test.ts +55 -0
- package/src/__tests__/service-key-db-proxy.test.ts +122 -1
- package/src/__tests__/sql-route.test.ts +66 -0
- package/src/__tests__/table-hook-runtime.test.ts +137 -0
- package/src/durable-objects/database-do.ts +50 -45
- package/src/durable-objects/database-live-do.ts +15 -0
- package/src/durable-objects/room-runtime-base.ts +387 -129
- package/src/durable-objects/rooms-do.ts +31 -24
- package/src/index.ts +334 -282
- package/src/lib/d1-handler.ts +10 -21
- package/src/lib/do-router.ts +135 -3
- package/src/lib/functions.ts +4 -3
- package/src/lib/internal-transport.ts +28 -12
- package/src/lib/plugin-migration-routing.ts +28 -0
- package/src/lib/postgres-handler.ts +12 -20
- package/src/lib/provider-aware-sql.ts +19 -15
- package/src/lib/runtime-startup.ts +53 -0
- package/src/lib/table-hook-runtime.ts +62 -0
- package/src/routes/admin.ts +41 -41
- package/src/routes/database-live.ts +110 -12
- package/src/routes/sql.ts +22 -17
- package/src/routes/tables.ts +42 -29
- package/admin-build/_app/immutable/entry/start.l1WvHznQ.js +0 -1
- package/admin-build/_app/immutable/nodes/21.DuDYelMY.js +0 -1
package/src/index.ts
CHANGED
|
@@ -1,54 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { initFunctionRegistry } from './_functions-registry.js';
|
|
4
|
-
import { corsMiddleware } from './middleware/cors.js';
|
|
5
|
-
import { rateLimitMiddleware } from './middleware/rate-limit.js';
|
|
6
|
-
import { errorHandlerMiddleware } from './middleware/error-handler.js';
|
|
7
|
-
import { internalGuardMiddleware } from './middleware/internal-guard.js';
|
|
8
|
-
import { authMiddleware } from './middleware/auth.js';
|
|
9
|
-
import { rulesMiddleware } from './middleware/rules.js';
|
|
10
|
-
|
|
11
|
-
import { loggerMiddleware } from './middleware/logger.js';
|
|
12
|
-
import { EdgeBaseError } from '@edge-base/shared';
|
|
13
|
-
import { SERVER_VERSION } from './lib/version.js';
|
|
14
|
-
import { healthRoute } from './routes/health.js';
|
|
15
|
-
import { tablesRoute } from './routes/tables.js';
|
|
16
|
-
import { schemaRoute } from './routes/schema-endpoint.js';
|
|
17
|
-
import { authRoute } from './routes/auth.js';
|
|
18
|
-
import { adminAuthRoute } from './routes/admin-auth.js';
|
|
19
|
-
import { oauthRoute } from './routes/oauth.js';
|
|
20
|
-
import { databaseLiveRoute } from './routes/database-live.js';
|
|
21
|
-
import { storageRoute } from './routes/storage.js';
|
|
22
|
-
import { functionsRoute } from './routes/functions.js';
|
|
23
|
-
import { adminRoute } from './routes/admin.js';
|
|
24
|
-
import { backupRoute } from './routes/backup.js';
|
|
25
|
-
import { sqlRoute } from './routes/sql.js';
|
|
26
|
-
import { kvRoute } from './routes/kv.js';
|
|
27
|
-
import { d1Route } from './routes/d1.js';
|
|
28
|
-
import { vectorizeRoute } from './routes/vectorize.js';
|
|
29
|
-
import { configRoute } from './routes/config.js';
|
|
30
|
-
import { pushRoute } from './routes/push.js';
|
|
31
|
-
import { roomRoute } from './routes/room.js';
|
|
32
|
-
import { analyticsApi } from './routes/analytics-api.js';
|
|
33
|
-
import { parseConfig, setConfig } from './lib/do-router.js';
|
|
34
|
-
import { createAdminAssetRequest } from './lib/admin-assets.js';
|
|
35
|
-
import { resolveAdminFaviconTarget, resolveAdminRedirectTarget } from './lib/admin-routing.js';
|
|
36
|
-
import { zodDefaultHook } from './lib/schemas.js';
|
|
37
|
-
import { executePluginMigrations } from './lib/plugin-migrations.js';
|
|
38
|
-
import { getFunctionsByTrigger, buildFunctionContext, getWorkerUrl } from './lib/functions.js';
|
|
39
|
-
import { parseCron, matchesCron } from './lib/cron.js';
|
|
40
|
-
import { parseDuration } from './lib/jwt.js';
|
|
41
|
-
import { resolveStartupConfig } from './lib/startup-config.js';
|
|
42
|
-
import * as authService from './lib/auth-d1-service.js';
|
|
43
|
-
import { ensureAuthSchema, deleteAnon } from './lib/auth-d1.js';
|
|
44
|
-
import { resolveAuthDb } from './lib/auth-db-adapter.js';
|
|
45
|
-
import { resolveRootServiceKey } from './lib/service-key.js';
|
|
46
|
-
import { normalizeOpenApiDocument, type OpenApiSpec } from './lib/openapi.js';
|
|
47
|
-
import generatedConfig from './generated-config.js';
|
|
1
|
+
import type { HonoEnv } from './lib/hono.js';
|
|
2
|
+
import type { OpenApiSpec } from './lib/openapi.js';
|
|
48
3
|
import type { Env } from './types.js';
|
|
49
|
-
|
|
50
|
-
// Compile-time constant — injected by wrangler [define] in wrangler.test.toml
|
|
51
|
-
declare const EDGEBASE_TEST_BUILD: boolean | undefined;
|
|
4
|
+
import { ensureServerStartup } from './lib/runtime-startup.js';
|
|
52
5
|
|
|
53
6
|
// ─── DO Re-exports (wrangler needs exports from main entry) ───
|
|
54
7
|
export { DatabaseDO } from './durable-objects/database-do.js';
|
|
@@ -57,275 +10,374 @@ export { AuthDO } from './durable-objects/auth-do.js';
|
|
|
57
10
|
export { RoomsDO } from './durable-objects/rooms-do.js';
|
|
58
11
|
export { LogsDO } from './durable-objects/logs-do.js';
|
|
59
12
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
13
|
+
let appPromise: Promise<Awaited<ReturnType<typeof buildApp>>> | null = null;
|
|
14
|
+
|
|
15
|
+
async function buildApp() {
|
|
16
|
+
await ensureServerStartup();
|
|
17
|
+
|
|
18
|
+
const [
|
|
19
|
+
honoModule,
|
|
20
|
+
httpExceptionModule,
|
|
21
|
+
corsModule,
|
|
22
|
+
rateLimitModule,
|
|
23
|
+
errorHandlerModule,
|
|
24
|
+
internalGuardModule,
|
|
25
|
+
authMiddlewareModule,
|
|
26
|
+
rulesMiddlewareModule,
|
|
27
|
+
loggerModule,
|
|
28
|
+
sharedModule,
|
|
29
|
+
versionModule,
|
|
30
|
+
healthRouteModule,
|
|
31
|
+
tablesRouteModule,
|
|
32
|
+
schemaRouteModule,
|
|
33
|
+
authRouteModule,
|
|
34
|
+
adminAuthRouteModule,
|
|
35
|
+
oauthRouteModule,
|
|
36
|
+
databaseLiveRouteModule,
|
|
37
|
+
storageRouteModule,
|
|
38
|
+
functionsRouteModule,
|
|
39
|
+
adminRouteModule,
|
|
40
|
+
backupRouteModule,
|
|
41
|
+
sqlRouteModule,
|
|
42
|
+
kvRouteModule,
|
|
43
|
+
d1RouteModule,
|
|
44
|
+
vectorizeRouteModule,
|
|
45
|
+
configRouteModule,
|
|
46
|
+
pushRouteModule,
|
|
47
|
+
roomRouteModule,
|
|
48
|
+
analyticsRouteModule,
|
|
49
|
+
adminAssetsModule,
|
|
50
|
+
adminRoutingModule,
|
|
51
|
+
schemasModule,
|
|
52
|
+
pluginMigrationsModule,
|
|
53
|
+
pluginMigrationRoutingModule,
|
|
54
|
+
functionsModule,
|
|
55
|
+
openApiModule,
|
|
56
|
+
doRouterModule,
|
|
57
|
+
] = await Promise.all([
|
|
58
|
+
import('./lib/hono.js'),
|
|
59
|
+
import('hono/http-exception'),
|
|
60
|
+
import('./middleware/cors.js'),
|
|
61
|
+
import('./middleware/rate-limit.js'),
|
|
62
|
+
import('./middleware/error-handler.js'),
|
|
63
|
+
import('./middleware/internal-guard.js'),
|
|
64
|
+
import('./middleware/auth.js'),
|
|
65
|
+
import('./middleware/rules.js'),
|
|
66
|
+
import('./middleware/logger.js'),
|
|
67
|
+
import('@edge-base/shared'),
|
|
68
|
+
import('./lib/version.js'),
|
|
69
|
+
import('./routes/health.js'),
|
|
70
|
+
import('./routes/tables.js'),
|
|
71
|
+
import('./routes/schema-endpoint.js'),
|
|
72
|
+
import('./routes/auth.js'),
|
|
73
|
+
import('./routes/admin-auth.js'),
|
|
74
|
+
import('./routes/oauth.js'),
|
|
75
|
+
import('./routes/database-live.js'),
|
|
76
|
+
import('./routes/storage.js'),
|
|
77
|
+
import('./routes/functions.js'),
|
|
78
|
+
import('./routes/admin.js'),
|
|
79
|
+
import('./routes/backup.js'),
|
|
80
|
+
import('./routes/sql.js'),
|
|
81
|
+
import('./routes/kv.js'),
|
|
82
|
+
import('./routes/d1.js'),
|
|
83
|
+
import('./routes/vectorize.js'),
|
|
84
|
+
import('./routes/config.js'),
|
|
85
|
+
import('./routes/push.js'),
|
|
86
|
+
import('./routes/room.js'),
|
|
87
|
+
import('./routes/analytics-api.js'),
|
|
88
|
+
import('./lib/admin-assets.js'),
|
|
89
|
+
import('./lib/admin-routing.js'),
|
|
90
|
+
import('./lib/schemas.js'),
|
|
91
|
+
import('./lib/plugin-migrations.js'),
|
|
92
|
+
import('./lib/plugin-migration-routing.js'),
|
|
93
|
+
import('./lib/functions.js'),
|
|
94
|
+
import('./lib/openapi.js'),
|
|
95
|
+
import('./lib/do-router.js'),
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
const { OpenAPIHono } = honoModule;
|
|
99
|
+
const { HTTPException } = httpExceptionModule;
|
|
100
|
+
const { corsMiddleware } = corsModule;
|
|
101
|
+
const { rateLimitMiddleware } = rateLimitModule;
|
|
102
|
+
const { errorHandlerMiddleware } = errorHandlerModule;
|
|
103
|
+
const { internalGuardMiddleware } = internalGuardModule;
|
|
104
|
+
const { authMiddleware } = authMiddlewareModule;
|
|
105
|
+
const { rulesMiddleware } = rulesMiddlewareModule;
|
|
106
|
+
const { loggerMiddleware } = loggerModule;
|
|
107
|
+
const { EdgeBaseError } = sharedModule;
|
|
108
|
+
const { SERVER_VERSION } = versionModule;
|
|
109
|
+
const { createAdminAssetRequest } = adminAssetsModule;
|
|
110
|
+
const { resolveAdminFaviconTarget, resolveAdminRedirectTarget } = adminRoutingModule;
|
|
111
|
+
const { zodDefaultHook } = schemasModule;
|
|
112
|
+
const { executePluginMigrations } = pluginMigrationsModule;
|
|
113
|
+
const { shouldRunPluginMigrationsForRequestPath } = pluginMigrationRoutingModule;
|
|
114
|
+
const { getWorkerUrl } = functionsModule;
|
|
115
|
+
const { normalizeOpenApiDocument } = openApiModule;
|
|
116
|
+
|
|
117
|
+
const app = new OpenAPIHono<HonoEnv>({ defaultHook: zodDefaultHook });
|
|
118
|
+
|
|
119
|
+
app.use('*', errorHandlerMiddleware);
|
|
120
|
+
app.use('*', loggerMiddleware);
|
|
121
|
+
app.use('*', corsMiddleware);
|
|
122
|
+
|
|
123
|
+
app.use('*', async (c, next) => {
|
|
124
|
+
const env = c.env as Env;
|
|
125
|
+
const config = doRouterModule.parseConfig(env);
|
|
126
|
+
const requestPath = new URL(c.req.url).pathname;
|
|
127
|
+
if (config?.plugins?.length && shouldRunPluginMigrationsForRequestPath(requestPath)) {
|
|
128
|
+
await executePluginMigrations(config.plugins, env, config, getWorkerUrl(c.req.url, env));
|
|
129
|
+
}
|
|
130
|
+
return next();
|
|
131
|
+
});
|
|
81
132
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
133
|
+
app.use('*', rateLimitMiddleware);
|
|
134
|
+
app.use('/api/*', authMiddleware);
|
|
135
|
+
app.use('/api/db/*', rulesMiddleware);
|
|
136
|
+
app.use('/internal/*', internalGuardMiddleware);
|
|
137
|
+
|
|
138
|
+
app.route('/api', healthRouteModule.healthRoute);
|
|
139
|
+
app.route('/api/auth', authRouteModule.authRoute);
|
|
140
|
+
app.route('/api/auth/admin', adminAuthRouteModule.adminAuthRoute);
|
|
141
|
+
app.route('/api/auth/oauth', oauthRouteModule.oauthRoute);
|
|
142
|
+
app.route('/api/db', tablesRouteModule.tablesRoute);
|
|
143
|
+
app.route('/api/db', databaseLiveRouteModule.databaseLiveRoute);
|
|
144
|
+
app.route('/api/schema', schemaRouteModule.schemaRoute);
|
|
145
|
+
app.route('/api/storage', storageRouteModule.storageRoute);
|
|
146
|
+
app.route('/api/functions', functionsRouteModule.functionsRoute);
|
|
147
|
+
app.route('/api/sql', sqlRouteModule.sqlRoute);
|
|
148
|
+
app.route('/api/kv', kvRouteModule.kvRoute);
|
|
149
|
+
app.route('/api/d1', d1RouteModule.d1Route);
|
|
150
|
+
app.route('/api/vectorize', vectorizeRouteModule.vectorizeRoute);
|
|
151
|
+
app.route('/api/config', configRouteModule.configRoute);
|
|
152
|
+
app.route('/api/push', pushRouteModule.pushRoute);
|
|
153
|
+
app.route('/api/room', roomRouteModule.roomRoute);
|
|
154
|
+
app.route('/api/analytics', analyticsRouteModule.analyticsApi);
|
|
155
|
+
app.route('/admin/api', adminRouteModule.adminRoute);
|
|
156
|
+
app.route('/admin/api/backup', backupRouteModule.backupRoute);
|
|
157
|
+
|
|
158
|
+
app.get('/', (c) => {
|
|
159
|
+
const env = c.env as Env;
|
|
160
|
+
const externalAdminUrl = resolveAdminRedirectTarget(c.req.url, env.ADMIN_ORIGIN);
|
|
161
|
+
if (externalAdminUrl) {
|
|
162
|
+
return c.redirect(externalAdminUrl, 302);
|
|
163
|
+
}
|
|
164
|
+
if (env.ASSETS) {
|
|
165
|
+
return c.redirect('/admin', 302);
|
|
166
|
+
}
|
|
167
|
+
return c.json({
|
|
168
|
+
name: 'EdgeBase API',
|
|
169
|
+
docs: '/openapi.json',
|
|
170
|
+
admin: null,
|
|
171
|
+
});
|
|
172
|
+
});
|
|
90
173
|
|
|
91
|
-
|
|
174
|
+
app.get('/favicon.ico', async (c) => {
|
|
175
|
+
const env = c.env as Env;
|
|
176
|
+
const externalFaviconUrl = resolveAdminFaviconTarget(env.ADMIN_ORIGIN);
|
|
177
|
+
if (externalFaviconUrl) {
|
|
178
|
+
return c.redirect(externalFaviconUrl, 302);
|
|
179
|
+
}
|
|
92
180
|
|
|
93
|
-
|
|
181
|
+
if (!env.ASSETS) {
|
|
182
|
+
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
183
|
+
}
|
|
94
184
|
|
|
95
|
-
|
|
96
|
-
|
|
185
|
+
const url = new URL(c.req.url);
|
|
186
|
+
url.pathname = '/admin/favicon.svg';
|
|
187
|
+
return env.ASSETS.fetch(createAdminAssetRequest(new Request(url.toString(), c.req.raw)));
|
|
188
|
+
});
|
|
97
189
|
|
|
98
|
-
app.
|
|
99
|
-
|
|
100
|
-
|
|
190
|
+
app.get('/favicon.svg', async (c) => {
|
|
191
|
+
const env = c.env as Env;
|
|
192
|
+
const externalFaviconUrl = resolveAdminFaviconTarget(env.ADMIN_ORIGIN);
|
|
193
|
+
if (externalFaviconUrl) {
|
|
194
|
+
return c.redirect(externalFaviconUrl, 302);
|
|
195
|
+
}
|
|
101
196
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// stub.fetch (which is allowed by the x-internal whitelist). No stripping needed.
|
|
197
|
+
if (env.ASSETS) {
|
|
198
|
+
return env.ASSETS.fetch(c.req.raw);
|
|
199
|
+
}
|
|
106
200
|
|
|
107
|
-
|
|
108
|
-
app.use('*', async (c, next) => {
|
|
109
|
-
const config = parseConfig(c.env);
|
|
110
|
-
const requestPath = new URL(c.req.url).pathname;
|
|
111
|
-
const shouldSkipPluginMigrations =
|
|
112
|
-
requestPath.startsWith('/admin/api/backup/') ||
|
|
113
|
-
requestPath.startsWith('/admin/api/data/backup/') ||
|
|
114
|
-
requestPath.startsWith('/internal/backup/');
|
|
115
|
-
if (!shouldSkipPluginMigrations && config?.plugins?.length) {
|
|
116
|
-
await executePluginMigrations(config.plugins, c.env, config, getWorkerUrl(c.req.url, c.env));
|
|
117
|
-
}
|
|
118
|
-
return next();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
app.use('*', rateLimitMiddleware);
|
|
122
|
-
|
|
123
|
-
// Auth middleware — JWT verification + auth context injection (M3)
|
|
124
|
-
app.use('/api/*', authMiddleware);
|
|
125
|
-
|
|
126
|
-
// Context middleware removed — DB-level access rules (§4,) handle multi-tenancy.
|
|
127
|
-
|
|
128
|
-
// Rules middleware — access rules evaluation (M4,)
|
|
129
|
-
app.use('/api/db/*', rulesMiddleware);
|
|
130
|
-
|
|
131
|
-
// ─── Internal Guard ───
|
|
132
|
-
app.use('/internal/*', internalGuardMiddleware);
|
|
133
|
-
|
|
134
|
-
// ─── Routes ───
|
|
135
|
-
app.route('/api', healthRoute);
|
|
136
|
-
app.route('/api/auth', authRoute);
|
|
137
|
-
app.route('/api/auth/admin', adminAuthRoute);
|
|
138
|
-
app.route('/api/auth/oauth', oauthRoute);
|
|
139
|
-
app.route('/api/db', tablesRoute);
|
|
140
|
-
app.route('/api/db', databaseLiveRoute);
|
|
141
|
-
app.route('/api/schema', schemaRoute);
|
|
142
|
-
app.route('/api/storage', storageRoute);
|
|
143
|
-
app.route('/api/functions', functionsRoute);
|
|
144
|
-
app.route('/api/sql', sqlRoute);
|
|
145
|
-
app.route('/api/kv', kvRoute);
|
|
146
|
-
app.route('/api/d1', d1Route);
|
|
147
|
-
app.route('/api/vectorize', vectorizeRoute);
|
|
148
|
-
app.route('/api/config', configRoute);
|
|
149
|
-
app.route('/api/push', pushRoute);
|
|
150
|
-
app.route('/api/room', roomRoute);
|
|
151
|
-
app.route('/api/analytics', analyticsApi);
|
|
152
|
-
// ─── Admin Dashboard (M12,) ───
|
|
153
|
-
app.route('/admin/api', adminRoute);
|
|
154
|
-
app.route('/admin/api/backup', backupRoute);
|
|
155
|
-
|
|
156
|
-
app.get('/', (c) => {
|
|
157
|
-
const externalAdminUrl = resolveAdminRedirectTarget(c.req.url, c.env.ADMIN_ORIGIN);
|
|
158
|
-
if (externalAdminUrl) {
|
|
159
|
-
return c.redirect(externalAdminUrl, 302);
|
|
160
|
-
}
|
|
161
|
-
if (c.env.ASSETS) {
|
|
162
|
-
return c.redirect('/admin', 302);
|
|
163
|
-
}
|
|
164
|
-
return c.json({
|
|
165
|
-
name: 'EdgeBase API',
|
|
166
|
-
docs: '/openapi.json',
|
|
167
|
-
admin: null,
|
|
201
|
+
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
168
202
|
});
|
|
169
|
-
});
|
|
170
203
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
204
|
+
app.get('/_app/*', async (c) => {
|
|
205
|
+
const env = c.env as Env;
|
|
206
|
+
if (env.ASSETS) {
|
|
207
|
+
return env.ASSETS.fetch(c.req.raw);
|
|
208
|
+
}
|
|
177
209
|
|
|
178
|
-
if (!c.env.ASSETS) {
|
|
179
210
|
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
180
|
-
}
|
|
211
|
+
});
|
|
181
212
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
213
|
+
app.get('/admin/*', async (c) => {
|
|
214
|
+
const env = c.env as Env;
|
|
215
|
+
const externalAdminUrl = resolveAdminRedirectTarget(c.req.url, env.ADMIN_ORIGIN);
|
|
216
|
+
if (externalAdminUrl) {
|
|
217
|
+
return c.redirect(externalAdminUrl, 302);
|
|
218
|
+
}
|
|
219
|
+
if (env.ASSETS) {
|
|
220
|
+
return env.ASSETS.fetch(createAdminAssetRequest(c.req.raw));
|
|
221
|
+
}
|
|
222
|
+
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
223
|
+
});
|
|
186
224
|
|
|
187
|
-
app.get('/
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
225
|
+
app.get('/admin', async (c) => {
|
|
226
|
+
const env = c.env as Env;
|
|
227
|
+
const externalAdminUrl = resolveAdminRedirectTarget(c.req.url, env.ADMIN_ORIGIN);
|
|
228
|
+
if (externalAdminUrl) {
|
|
229
|
+
return c.redirect(externalAdminUrl, 302);
|
|
230
|
+
}
|
|
231
|
+
if (env.ASSETS) {
|
|
232
|
+
return env.ASSETS.fetch(createAdminAssetRequest(c.req.raw));
|
|
233
|
+
}
|
|
234
|
+
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
235
|
+
});
|
|
192
236
|
|
|
193
|
-
|
|
194
|
-
return c.
|
|
195
|
-
}
|
|
237
|
+
app.get('/harness', (c) => {
|
|
238
|
+
return c.redirect('/harness/', 302);
|
|
239
|
+
});
|
|
196
240
|
|
|
197
|
-
|
|
198
|
-
|
|
241
|
+
app.get('/harness/', async (c) => {
|
|
242
|
+
const env = c.env as Env;
|
|
243
|
+
if (env.ASSETS) {
|
|
244
|
+
return env.ASSETS.fetch(c.req.raw);
|
|
245
|
+
}
|
|
246
|
+
return c.json({ code: 404, message: 'Harness assets not deployed.' }, 404);
|
|
247
|
+
});
|
|
199
248
|
|
|
200
|
-
app.get('/
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
249
|
+
app.get('/harness/assets/*', async (c) => {
|
|
250
|
+
const env = c.env as Env;
|
|
251
|
+
if (env.ASSETS) {
|
|
252
|
+
return env.ASSETS.fetch(c.req.raw);
|
|
253
|
+
}
|
|
254
|
+
return c.json({ code: 404, message: 'Harness assets not deployed.' }, 404);
|
|
255
|
+
});
|
|
204
256
|
|
|
205
|
-
|
|
206
|
-
|
|
257
|
+
app.get('/harness/*', (c) => {
|
|
258
|
+
return c.redirect('/harness/', 302);
|
|
259
|
+
});
|
|
207
260
|
|
|
208
|
-
app.get('/
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return c.
|
|
215
|
-
}
|
|
216
|
-
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
217
|
-
});
|
|
218
|
-
app.get('/admin', async (c) => {
|
|
219
|
-
const externalAdminUrl = resolveAdminRedirectTarget(c.req.url, c.env.ADMIN_ORIGIN);
|
|
220
|
-
if (externalAdminUrl) {
|
|
221
|
-
return c.redirect(externalAdminUrl, 302);
|
|
222
|
-
}
|
|
223
|
-
if (c.env.ASSETS) {
|
|
224
|
-
return c.env.ASSETS.fetch(createAdminAssetRequest(c.req.raw));
|
|
225
|
-
}
|
|
226
|
-
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
app.get('/harness', (c) => {
|
|
230
|
-
return c.redirect('/harness/', 302);
|
|
231
|
-
});
|
|
232
|
-
app.get('/harness/', async (c) => {
|
|
233
|
-
if (c.env.ASSETS) {
|
|
234
|
-
return c.env.ASSETS.fetch(c.req.raw);
|
|
235
|
-
}
|
|
236
|
-
return c.json({ code: 404, message: 'Harness assets not deployed.' }, 404);
|
|
237
|
-
});
|
|
238
|
-
app.get('/harness/assets/*', async (c) => {
|
|
239
|
-
if (c.env.ASSETS) {
|
|
240
|
-
return c.env.ASSETS.fetch(c.req.raw);
|
|
241
|
-
}
|
|
242
|
-
return c.json({ code: 404, message: 'Harness assets not deployed.' }, 404);
|
|
243
|
-
});
|
|
244
|
-
app.get('/harness/*', (c) => {
|
|
245
|
-
return c.redirect('/harness/', 302);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// ─── OpenAPI Spec ───
|
|
249
|
-
app.get('/openapi.json', (c) => {
|
|
250
|
-
const spec = app.getOpenAPI31Document({
|
|
251
|
-
openapi: '3.1.0',
|
|
252
|
-
info: { title: 'EdgeBase API', version: SERVER_VERSION },
|
|
261
|
+
app.get('/openapi.json', (c) => {
|
|
262
|
+
const spec = app.getOpenAPI31Document({
|
|
263
|
+
openapi: '3.1.0',
|
|
264
|
+
info: { title: 'EdgeBase API', version: SERVER_VERSION },
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
return c.json(normalizeOpenApiDocument(spec as OpenApiSpec, new URL(c.req.url).origin));
|
|
253
268
|
});
|
|
254
269
|
|
|
255
|
-
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
app.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
270
|
+
app.notFound((c) => {
|
|
271
|
+
return c.json({ code: 404, message: 'Not found.' }, 404);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
app.onError((err, c) => {
|
|
275
|
+
if (err instanceof SyntaxError) {
|
|
276
|
+
return c.json(
|
|
277
|
+
{
|
|
278
|
+
code: 400,
|
|
279
|
+
message: 'Invalid JSON payload. Please ensure your request body is valid JSON.',
|
|
280
|
+
},
|
|
281
|
+
400,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
if (err instanceof EdgeBaseError) {
|
|
285
|
+
return c.json(err.toJSON(), err.code as 400);
|
|
286
|
+
}
|
|
287
|
+
if (err instanceof HTTPException) {
|
|
288
|
+
return c.json({ code: err.status, message: err.message }, err.status as 400);
|
|
289
|
+
}
|
|
290
|
+
const e = err as unknown as Record<string, unknown>;
|
|
291
|
+
if (
|
|
292
|
+
typeof e.code === 'number' &&
|
|
293
|
+
e.code >= 400 &&
|
|
294
|
+
e.code < 600 &&
|
|
295
|
+
typeof e.message === 'string'
|
|
296
|
+
) {
|
|
297
|
+
const body: { code: number; message: string; data?: unknown } = {
|
|
298
|
+
code: e.code as number,
|
|
299
|
+
message: e.message as string,
|
|
300
|
+
};
|
|
301
|
+
if (e.data) body.data = e.data;
|
|
302
|
+
return c.json(body, e.code as number as 400);
|
|
303
|
+
}
|
|
304
|
+
console.error('Unhandled error:', err);
|
|
305
|
+
return c.json({ code: 500, message: 'Internal server error.' }, 500);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
return app;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function getApp() {
|
|
312
|
+
if (!appPromise) {
|
|
313
|
+
appPromise = buildApp();
|
|
295
314
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
});
|
|
315
|
+
return appPromise;
|
|
316
|
+
}
|
|
299
317
|
|
|
300
318
|
export default {
|
|
301
|
-
fetch:
|
|
319
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
|
320
|
+
const app = await getApp();
|
|
321
|
+
return app.fetch(request, env, ctx);
|
|
322
|
+
},
|
|
302
323
|
|
|
303
|
-
/**
|
|
304
|
-
* Cloudflare Cron Triggers — replaces db:_system alarm-based scheduling.
|
|
305
|
-
* CLI generates [triggers] section in wrangler.toml from config schedule functions.
|
|
306
|
-
*/
|
|
307
324
|
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
|
|
308
|
-
|
|
325
|
+
await ensureServerStartup();
|
|
326
|
+
|
|
327
|
+
const [
|
|
328
|
+
pluginMigrationsModule,
|
|
329
|
+
functionsModule,
|
|
330
|
+
cronModule,
|
|
331
|
+
jwtModule,
|
|
332
|
+
authServiceModule,
|
|
333
|
+
authD1Module,
|
|
334
|
+
authDbAdapterModule,
|
|
335
|
+
serviceKeyModule,
|
|
336
|
+
doRouterModule,
|
|
337
|
+
] = await Promise.all([
|
|
338
|
+
import('./lib/plugin-migrations.js'),
|
|
339
|
+
import('./lib/functions.js'),
|
|
340
|
+
import('./lib/cron.js'),
|
|
341
|
+
import('./lib/jwt.js'),
|
|
342
|
+
import('./lib/auth-d1-service.js'),
|
|
343
|
+
import('./lib/auth-d1.js'),
|
|
344
|
+
import('./lib/auth-db-adapter.js'),
|
|
345
|
+
import('./lib/service-key.js'),
|
|
346
|
+
import('./lib/do-router.js'),
|
|
347
|
+
]);
|
|
348
|
+
|
|
349
|
+
const { executePluginMigrations } = pluginMigrationsModule;
|
|
350
|
+
const { getFunctionsByTrigger, buildFunctionContext, getWorkerUrl } = functionsModule;
|
|
351
|
+
const { parseCron, matchesCron } = cronModule;
|
|
352
|
+
const { parseDuration } = jwtModule;
|
|
353
|
+
const { ensureAuthSchema, deleteAnon } = authD1Module;
|
|
354
|
+
const { resolveAuthDb } = authDbAdapterModule;
|
|
355
|
+
const { resolveRootServiceKey } = serviceKeyModule;
|
|
356
|
+
|
|
357
|
+
const config = doRouterModule.parseConfig(env);
|
|
358
|
+
if (config.plugins?.length) {
|
|
359
|
+
await executePluginMigrations(
|
|
360
|
+
config.plugins,
|
|
361
|
+
env,
|
|
362
|
+
config,
|
|
363
|
+
getWorkerUrl('http://internal/scheduled', env),
|
|
364
|
+
);
|
|
365
|
+
}
|
|
309
366
|
const scheduleFns = getFunctionsByTrigger('schedule');
|
|
310
367
|
|
|
311
368
|
const now = new Date(event.scheduledTime);
|
|
312
|
-
|
|
313
|
-
// Schedule function timeout (default: 10s)
|
|
314
369
|
const timeoutStr = config.functions?.scheduleFunctionTimeout ?? '10s';
|
|
315
370
|
const timeoutMs = parseDuration(timeoutStr) * 1000;
|
|
316
371
|
|
|
317
|
-
// ── System cron: session cleanup + anonymous account cleanup ──
|
|
318
372
|
ctx.waitUntil(
|
|
319
373
|
(async () => {
|
|
320
374
|
try {
|
|
321
375
|
const authDb = resolveAuthDb(env as unknown as Record<string, unknown>);
|
|
322
376
|
await ensureAuthSchema(authDb);
|
|
323
|
-
|
|
324
|
-
await authService.cleanExpiredSessions(authDb);
|
|
325
|
-
// Clean stale anonymous accounts
|
|
377
|
+
await authServiceModule.cleanExpiredSessions(authDb);
|
|
326
378
|
if (config?.auth?.anonymousAuth) {
|
|
327
379
|
const retentionDays = config.auth.anonymousRetentionDays ?? 30;
|
|
328
|
-
const deletedIds = await
|
|
380
|
+
const deletedIds = await authServiceModule.cleanStaleAnonymousAccounts(authDb, retentionDays);
|
|
329
381
|
for (const id of deletedIds) {
|
|
330
382
|
await deleteAnon(authDb, id).catch(() => {});
|
|
331
383
|
}
|
|
@@ -345,7 +397,7 @@ export default {
|
|
|
345
397
|
if (!matchesCron(now, schedule)) continue;
|
|
346
398
|
|
|
347
399
|
const fnCtx = buildFunctionContext({
|
|
348
|
-
request: new Request(
|
|
400
|
+
request: new Request(`http://internal/schedule/${name}`),
|
|
349
401
|
auth: null,
|
|
350
402
|
databaseNamespace: env.DATABASE,
|
|
351
403
|
authNamespace: env.AUTH,
|