@edge-base/server 0.2.4 → 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/{Dj-E9-FO.js → 6oMK_164.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BME_U9TJ.js → B2TnDKF7.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Dj0QUuOf.js → B6MschND.js} +1 -1
- package/admin-build/_app/immutable/chunks/{fYEKMQ-Z.js → B94PilAN.js} +1 -1
- package/admin-build/_app/immutable/chunks/{C6lpZLE2.js → BEW7Ez_g.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BYI6CUvd.js → BoOooyH6.js} +1 -1
- package/admin-build/_app/immutable/chunks/{5RQRbp5q.js → BqTb6Mxk.js} +1 -1
- package/admin-build/_app/immutable/chunks/{wCNueVYy.js → BvHnF5tV.js} +1 -1
- package/admin-build/_app/immutable/chunks/{XQM1k9PM.js → CaVKAiCe.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BjWZuf8W.js → Cdm5zBRA.js} +1 -1
- package/admin-build/_app/immutable/chunks/CrOZMmdF.js +1 -0
- package/admin-build/_app/immutable/chunks/Cw6OYcq-.js +1 -0
- package/admin-build/_app/immutable/chunks/{BgDzp0i0.js → D2j3I1VQ.js} +1 -1
- package/admin-build/_app/immutable/chunks/{D5GswVnI.js → DPdQ7z0T.js} +3 -3
- package/admin-build/_app/immutable/chunks/{DYaCRWMA.js → J2Gw0SMu.js} +1 -1
- package/admin-build/_app/immutable/chunks/{g_-Kpxu3.js → pUxw8jfq.js} +1 -1
- package/admin-build/_app/immutable/entry/{app.C8ylfBe6.js → app.D3flihMw.js} +2 -2
- package/admin-build/_app/immutable/entry/start.Cl6sLxnz.js +1 -0
- package/admin-build/_app/immutable/nodes/{0.CJJ6HZbp.js → 0.CdczqZLK.js} +1 -1
- package/admin-build/_app/immutable/nodes/{1.B4sI5cB4.js → 1.DxcSsEqS.js} +1 -1
- package/admin-build/_app/immutable/nodes/{10.D6hvCer6.js → 10.DuAd4aIm.js} +1 -1
- package/admin-build/_app/immutable/nodes/{11.Dx7b8aQ5.js → 11.0jgHQL92.js} +1 -1
- package/admin-build/_app/immutable/nodes/{12.Bqmy5KIF.js → 12.CKNPqmyy.js} +1 -1
- package/admin-build/_app/immutable/nodes/{13.CC6KpXgS.js → 13.B1p2POXS.js} +1 -1
- package/admin-build/_app/immutable/nodes/{14.yCo1Ix8E.js → 14.Bb-REBND.js} +1 -1
- package/admin-build/_app/immutable/nodes/{15.co0UfPlh.js → 15.1uBFCX0X.js} +1 -1
- package/admin-build/_app/immutable/nodes/{16.D0xkPUBW.js → 16.BR7WwQrS.js} +1 -1
- package/admin-build/_app/immutable/nodes/{17.CebNqPeh.js → 17.Cm57KKXV.js} +1 -1
- package/admin-build/_app/immutable/nodes/{18.JUoLOZxh.js → 18.CoiwfAuQ.js} +1 -1
- package/admin-build/_app/immutable/nodes/{19.ND8kmQJe.js → 19.B8ZdLlXj.js} +1 -1
- package/admin-build/_app/immutable/nodes/{20.DYb-q3W8.js → 20.DnHeFlTv.js} +1 -1
- package/admin-build/_app/immutable/nodes/21.CJFaf0Ia.js +1 -0
- package/admin-build/_app/immutable/nodes/{22.UOzm8WYV.js → 22.CItETFzy.js} +1 -1
- package/admin-build/_app/immutable/nodes/{23.BLgq21om.js → 23.CWSGMcKJ.js} +1 -1
- package/admin-build/_app/immutable/nodes/{24.DN9usmUs.js → 24.CWbEqNMB.js} +1 -1
- package/admin-build/_app/immutable/nodes/{25.BddRfAyE.js → 25.DRkLEhKi.js} +1 -1
- package/admin-build/_app/immutable/nodes/{26.Dl6XHIeT.js → 26.BRxO8AYH.js} +1 -1
- package/admin-build/_app/immutable/nodes/{27.D0iNwALG.js → 27.BLs-nVHz.js} +1 -1
- package/admin-build/_app/immutable/nodes/{28.9dKQmdGi.js → 28.G79qkdBK.js} +1 -1
- package/admin-build/_app/immutable/nodes/{29.wXzfJUXp.js → 29.BOcI6g0N.js} +1 -1
- package/admin-build/_app/immutable/nodes/{3.z8ut3jS-.js → 3.B6q-7qr8.js} +1 -1
- package/admin-build/_app/immutable/nodes/{30.BtZETNsL.js → 30.DAIC7dKd.js} +1 -1
- package/admin-build/_app/immutable/nodes/{31.CYonj2Jh.js → 31.pl0XXjXF.js} +1 -1
- package/admin-build/_app/immutable/nodes/{4.COtDPQ9b.js → 4.DOdvVlZj.js} +1 -1
- package/admin-build/_app/immutable/nodes/{5.CTRCeIhp.js → 5.BW_zlgye.js} +1 -1
- package/admin-build/_app/immutable/nodes/{6.ChHi3QkR.js → 6.Dxy1CAI2.js} +1 -1
- package/admin-build/_app/immutable/nodes/{7.CCMtr6Ac.js → 7.BG98w_o7.js} +1 -1
- package/admin-build/_app/immutable/nodes/{8.DpWJ-X_-.js → 8.DoG5R2rG.js} +1 -1
- package/admin-build/_app/immutable/nodes/{9.DOkvfmir.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__/database-do-route-validation.test.ts +10 -7
- package/src/__tests__/meta-route-registration.test.ts +20 -15
- 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/durable-objects/database-do.ts +14 -0
- 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 +326 -280
- package/src/lib/runtime-startup.ts +53 -0
- package/admin-build/_app/immutable/chunks/DBsVqhuh.js +0 -1
- package/admin-build/_app/immutable/chunks/D__dwMuW.js +0 -1
- package/admin-build/_app/immutable/entry/start.CtsqDyfj.js +0 -1
- package/admin-build/_app/immutable/nodes/21.cz3IN9Cc.js +0 -1
package/src/index.ts
CHANGED
|
@@ -1,55 +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 { shouldRunPluginMigrationsForRequestPath } from './lib/plugin-migration-routing.js';
|
|
39
|
-
import { getFunctionsByTrigger, buildFunctionContext, getWorkerUrl } from './lib/functions.js';
|
|
40
|
-
import { parseCron, matchesCron } from './lib/cron.js';
|
|
41
|
-
import { parseDuration } from './lib/jwt.js';
|
|
42
|
-
import { resolveStartupConfig } from './lib/startup-config.js';
|
|
43
|
-
import * as authService from './lib/auth-d1-service.js';
|
|
44
|
-
import { ensureAuthSchema, deleteAnon } from './lib/auth-d1.js';
|
|
45
|
-
import { resolveAuthDb } from './lib/auth-db-adapter.js';
|
|
46
|
-
import { resolveRootServiceKey } from './lib/service-key.js';
|
|
47
|
-
import { normalizeOpenApiDocument, type OpenApiSpec } from './lib/openapi.js';
|
|
48
|
-
import generatedConfig from './generated-config.js';
|
|
1
|
+
import type { HonoEnv } from './lib/hono.js';
|
|
2
|
+
import type { OpenApiSpec } from './lib/openapi.js';
|
|
49
3
|
import type { Env } from './types.js';
|
|
50
|
-
|
|
51
|
-
// Compile-time constant — injected by wrangler [define] in wrangler.test.toml
|
|
52
|
-
declare const EDGEBASE_TEST_BUILD: boolean | undefined;
|
|
4
|
+
import { ensureServerStartup } from './lib/runtime-startup.js';
|
|
53
5
|
|
|
54
6
|
// ─── DO Re-exports (wrangler needs exports from main entry) ───
|
|
55
7
|
export { DatabaseDO } from './durable-objects/database-do.js';
|
|
@@ -58,252 +10,351 @@ export { AuthDO } from './durable-objects/auth-do.js';
|
|
|
58
10
|
export { RoomsDO } from './durable-objects/rooms-do.js';
|
|
59
11
|
export { LogsDO } from './durable-objects/logs-do.js';
|
|
60
12
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
});
|
|
82
132
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
});
|
|
91
173
|
|
|
92
|
-
|
|
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
|
+
}
|
|
93
180
|
|
|
94
|
-
|
|
181
|
+
if (!env.ASSETS) {
|
|
182
|
+
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
183
|
+
}
|
|
95
184
|
|
|
96
|
-
|
|
97
|
-
|
|
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
|
+
});
|
|
98
189
|
|
|
99
|
-
app.
|
|
100
|
-
|
|
101
|
-
|
|
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
|
+
}
|
|
102
196
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// 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
|
+
}
|
|
107
200
|
|
|
108
|
-
|
|
109
|
-
// routes that can execute plugin code or touch plugin-managed tables.
|
|
110
|
-
app.use('*', async (c, next) => {
|
|
111
|
-
const config = parseConfig(c.env);
|
|
112
|
-
const requestPath = new URL(c.req.url).pathname;
|
|
113
|
-
if (config?.plugins?.length && shouldRunPluginMigrationsForRequestPath(requestPath)) {
|
|
114
|
-
await executePluginMigrations(config.plugins, c.env, config, getWorkerUrl(c.req.url, c.env));
|
|
115
|
-
}
|
|
116
|
-
return next();
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
app.use('*', rateLimitMiddleware);
|
|
120
|
-
|
|
121
|
-
// Auth middleware — JWT verification + auth context injection (M3)
|
|
122
|
-
app.use('/api/*', authMiddleware);
|
|
123
|
-
|
|
124
|
-
// Context middleware removed — DB-level access rules (§4,) handle multi-tenancy.
|
|
125
|
-
|
|
126
|
-
// Rules middleware — access rules evaluation (M4,)
|
|
127
|
-
app.use('/api/db/*', rulesMiddleware);
|
|
128
|
-
|
|
129
|
-
// ─── Internal Guard ───
|
|
130
|
-
app.use('/internal/*', internalGuardMiddleware);
|
|
131
|
-
|
|
132
|
-
// ─── Routes ───
|
|
133
|
-
app.route('/api', healthRoute);
|
|
134
|
-
app.route('/api/auth', authRoute);
|
|
135
|
-
app.route('/api/auth/admin', adminAuthRoute);
|
|
136
|
-
app.route('/api/auth/oauth', oauthRoute);
|
|
137
|
-
app.route('/api/db', tablesRoute);
|
|
138
|
-
app.route('/api/db', databaseLiveRoute);
|
|
139
|
-
app.route('/api/schema', schemaRoute);
|
|
140
|
-
app.route('/api/storage', storageRoute);
|
|
141
|
-
app.route('/api/functions', functionsRoute);
|
|
142
|
-
app.route('/api/sql', sqlRoute);
|
|
143
|
-
app.route('/api/kv', kvRoute);
|
|
144
|
-
app.route('/api/d1', d1Route);
|
|
145
|
-
app.route('/api/vectorize', vectorizeRoute);
|
|
146
|
-
app.route('/api/config', configRoute);
|
|
147
|
-
app.route('/api/push', pushRoute);
|
|
148
|
-
app.route('/api/room', roomRoute);
|
|
149
|
-
app.route('/api/analytics', analyticsApi);
|
|
150
|
-
// ─── Admin Dashboard (M12,) ───
|
|
151
|
-
app.route('/admin/api', adminRoute);
|
|
152
|
-
app.route('/admin/api/backup', backupRoute);
|
|
153
|
-
|
|
154
|
-
app.get('/', (c) => {
|
|
155
|
-
const externalAdminUrl = resolveAdminRedirectTarget(c.req.url, c.env.ADMIN_ORIGIN);
|
|
156
|
-
if (externalAdminUrl) {
|
|
157
|
-
return c.redirect(externalAdminUrl, 302);
|
|
158
|
-
}
|
|
159
|
-
if (c.env.ASSETS) {
|
|
160
|
-
return c.redirect('/admin', 302);
|
|
161
|
-
}
|
|
162
|
-
return c.json({
|
|
163
|
-
name: 'EdgeBase API',
|
|
164
|
-
docs: '/openapi.json',
|
|
165
|
-
admin: null,
|
|
201
|
+
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
166
202
|
});
|
|
167
|
-
});
|
|
168
203
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
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
|
+
}
|
|
175
209
|
|
|
176
|
-
if (!c.env.ASSETS) {
|
|
177
210
|
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
178
|
-
}
|
|
211
|
+
});
|
|
179
212
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
+
});
|
|
184
224
|
|
|
185
|
-
app.get('/
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
+
});
|
|
190
236
|
|
|
191
|
-
|
|
192
|
-
return c.
|
|
193
|
-
}
|
|
237
|
+
app.get('/harness', (c) => {
|
|
238
|
+
return c.redirect('/harness/', 302);
|
|
239
|
+
});
|
|
194
240
|
|
|
195
|
-
|
|
196
|
-
|
|
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
|
+
});
|
|
197
248
|
|
|
198
|
-
app.get('/
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
+
});
|
|
202
256
|
|
|
203
|
-
|
|
204
|
-
|
|
257
|
+
app.get('/harness/*', (c) => {
|
|
258
|
+
return c.redirect('/harness/', 302);
|
|
259
|
+
});
|
|
205
260
|
|
|
206
|
-
app.get('/
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return c.
|
|
213
|
-
}
|
|
214
|
-
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
215
|
-
});
|
|
216
|
-
app.get('/admin', async (c) => {
|
|
217
|
-
const externalAdminUrl = resolveAdminRedirectTarget(c.req.url, c.env.ADMIN_ORIGIN);
|
|
218
|
-
if (externalAdminUrl) {
|
|
219
|
-
return c.redirect(externalAdminUrl, 302);
|
|
220
|
-
}
|
|
221
|
-
if (c.env.ASSETS) {
|
|
222
|
-
return c.env.ASSETS.fetch(createAdminAssetRequest(c.req.raw));
|
|
223
|
-
}
|
|
224
|
-
return c.json({ code: 404, message: 'Admin dashboard not deployed.' }, 404);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
app.get('/harness', (c) => {
|
|
228
|
-
return c.redirect('/harness/', 302);
|
|
229
|
-
});
|
|
230
|
-
app.get('/harness/', async (c) => {
|
|
231
|
-
if (c.env.ASSETS) {
|
|
232
|
-
return c.env.ASSETS.fetch(c.req.raw);
|
|
233
|
-
}
|
|
234
|
-
return c.json({ code: 404, message: 'Harness assets not deployed.' }, 404);
|
|
235
|
-
});
|
|
236
|
-
app.get('/harness/assets/*', async (c) => {
|
|
237
|
-
if (c.env.ASSETS) {
|
|
238
|
-
return c.env.ASSETS.fetch(c.req.raw);
|
|
239
|
-
}
|
|
240
|
-
return c.json({ code: 404, message: 'Harness assets not deployed.' }, 404);
|
|
241
|
-
});
|
|
242
|
-
app.get('/harness/*', (c) => {
|
|
243
|
-
return c.redirect('/harness/', 302);
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// ─── OpenAPI Spec ───
|
|
247
|
-
app.get('/openapi.json', (c) => {
|
|
248
|
-
const spec = app.getOpenAPI31Document({
|
|
249
|
-
openapi: '3.1.0',
|
|
250
|
-
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));
|
|
251
268
|
});
|
|
252
269
|
|
|
253
|
-
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
app.
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
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();
|
|
293
314
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
});
|
|
315
|
+
return appPromise;
|
|
316
|
+
}
|
|
297
317
|
|
|
298
318
|
export default {
|
|
299
|
-
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
|
+
},
|
|
300
323
|
|
|
301
|
-
/**
|
|
302
|
-
* Cloudflare Cron Triggers — replaces db:_system alarm-based scheduling.
|
|
303
|
-
* CLI generates [triggers] section in wrangler.toml from config schedule functions.
|
|
304
|
-
*/
|
|
305
324
|
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
|
|
306
|
-
|
|
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);
|
|
307
358
|
if (config.plugins?.length) {
|
|
308
359
|
await executePluginMigrations(
|
|
309
360
|
config.plugins,
|
|
@@ -315,23 +366,18 @@ export default {
|
|
|
315
366
|
const scheduleFns = getFunctionsByTrigger('schedule');
|
|
316
367
|
|
|
317
368
|
const now = new Date(event.scheduledTime);
|
|
318
|
-
|
|
319
|
-
// Schedule function timeout (default: 10s)
|
|
320
369
|
const timeoutStr = config.functions?.scheduleFunctionTimeout ?? '10s';
|
|
321
370
|
const timeoutMs = parseDuration(timeoutStr) * 1000;
|
|
322
371
|
|
|
323
|
-
// ── System cron: session cleanup + anonymous account cleanup ──
|
|
324
372
|
ctx.waitUntil(
|
|
325
373
|
(async () => {
|
|
326
374
|
try {
|
|
327
375
|
const authDb = resolveAuthDb(env as unknown as Record<string, unknown>);
|
|
328
376
|
await ensureAuthSchema(authDb);
|
|
329
|
-
|
|
330
|
-
await authService.cleanExpiredSessions(authDb);
|
|
331
|
-
// Clean stale anonymous accounts
|
|
377
|
+
await authServiceModule.cleanExpiredSessions(authDb);
|
|
332
378
|
if (config?.auth?.anonymousAuth) {
|
|
333
379
|
const retentionDays = config.auth.anonymousRetentionDays ?? 30;
|
|
334
|
-
const deletedIds = await
|
|
380
|
+
const deletedIds = await authServiceModule.cleanStaleAnonymousAccounts(authDb, retentionDays);
|
|
335
381
|
for (const id of deletedIds) {
|
|
336
382
|
await deleteAnon(authDb, id).catch(() => {});
|
|
337
383
|
}
|
|
@@ -351,7 +397,7 @@ export default {
|
|
|
351
397
|
if (!matchesCron(now, schedule)) continue;
|
|
352
398
|
|
|
353
399
|
const fnCtx = buildFunctionContext({
|
|
354
|
-
request: new Request(
|
|
400
|
+
request: new Request(`http://internal/schedule/${name}`),
|
|
355
401
|
auth: null,
|
|
356
402
|
databaseNamespace: env.DATABASE,
|
|
357
403
|
authNamespace: env.AUTH,
|