@edge-base/server 0.2.5 → 0.2.7
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/{DILS_-VJ.js → B3CvhH3c.js} +1 -1
- package/admin-build/_app/immutable/chunks/BDYewzou.js +1 -0
- package/admin-build/_app/immutable/chunks/{Cdm5zBRA.js → BEM1BeVF.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Dt4vL4Df.js → BYL_uBga.js} +1 -1
- package/admin-build/_app/immutable/chunks/{B94PilAN.js → BYyykAbh.js} +1 -1
- package/admin-build/_app/immutable/chunks/BaUG2TJ-.js +1 -0
- package/admin-build/_app/immutable/chunks/{C72lTcG0.js → Bcs4KYNp.js} +1 -1
- package/admin-build/_app/immutable/chunks/{D2j3I1VQ.js → BfpUQYr3.js} +1 -1
- package/admin-build/_app/immutable/chunks/BhCO1Fpt.js +1 -0
- package/admin-build/_app/immutable/chunks/{B8s_s9QY.js → BkZCgsc3.js} +1 -1
- package/admin-build/_app/immutable/chunks/CIOC1v_q.js +128 -0
- package/admin-build/_app/immutable/chunks/CjcrXziO.js +2 -0
- package/admin-build/_app/immutable/chunks/CvczjTXx.js +1 -0
- package/admin-build/_app/immutable/chunks/D1u3u7xu.js +1 -0
- package/admin-build/_app/immutable/chunks/{B0HRJ657.js → DOOPbWwG.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BqTb6Mxk.js → DaXO-sFP.js} +1 -1
- package/admin-build/_app/immutable/chunks/DnpbvAPi.js +1 -0
- package/admin-build/_app/immutable/chunks/{B6MschND.js → Dz9cUCuv.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CaVKAiCe.js → Tea2dBJ8.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Z41NK6i6.js → bguI1TeA.js} +1 -1
- package/admin-build/_app/immutable/chunks/{J2Gw0SMu.js → ejoEf2I5.js} +1 -1
- package/admin-build/_app/immutable/chunks/{B2TnDKF7.js → iEyeblJR.js} +1 -1
- package/admin-build/_app/immutable/chunks/{_teD5ji5.js → nlAMTi52.js} +1 -1
- package/admin-build/_app/immutable/chunks/qKdzaeX3.js +1 -0
- package/admin-build/_app/immutable/entry/{app.D3flihMw.js → app.DoUaxnew.js} +2 -2
- package/admin-build/_app/immutable/entry/start.MmZh8oBH.js +1 -0
- package/admin-build/_app/immutable/nodes/{0.CdczqZLK.js → 0.Dsxi8s7i.js} +1 -1
- package/admin-build/_app/immutable/nodes/1.Cp2l-hol.js +1 -0
- package/admin-build/_app/immutable/nodes/10.4oY6m8Nz.js +1 -0
- package/admin-build/_app/immutable/nodes/11.DfcozD4J.js +1 -0
- package/admin-build/_app/immutable/nodes/12.uJgZdCIA.js +1 -0
- package/admin-build/_app/immutable/nodes/13.CaN1kRev.js +110 -0
- package/admin-build/_app/immutable/nodes/14.DQ5xIi3s.js +3 -0
- package/admin-build/_app/immutable/nodes/15.B_EkebTJ.js +1 -0
- package/admin-build/_app/immutable/nodes/{16.BR7WwQrS.js → 16.Tko1ZX8-.js} +1 -1
- package/admin-build/_app/immutable/nodes/{17.Cm57KKXV.js → 17.BCmWMJX9.js} +1 -1
- package/admin-build/_app/immutable/nodes/18.hmGhl1O2.js +1 -0
- package/admin-build/_app/immutable/nodes/19.D-1infOo.js +2 -0
- package/admin-build/_app/immutable/nodes/{20.DnHeFlTv.js → 20.CY4KKcBL.js} +1 -1
- package/admin-build/_app/immutable/nodes/21.B9lbNUQr.js +1 -0
- package/admin-build/_app/immutable/nodes/22.14Vd7bnt.js +1 -0
- package/admin-build/_app/immutable/nodes/{23.CWSGMcKJ.js → 23.Be6jK77o.js} +2 -2
- package/admin-build/_app/immutable/nodes/24.CSTFkr6R.js +2 -0
- package/admin-build/_app/immutable/nodes/25.DRTg8fHc.js +2 -0
- package/admin-build/_app/immutable/nodes/26.DKt-9lwQ.js +1 -0
- package/admin-build/_app/immutable/nodes/27.D5caPu0F.js +1 -0
- package/admin-build/_app/immutable/nodes/28.hJhlnlyY.js +1 -0
- package/admin-build/_app/immutable/nodes/29.CDYBzFyT.js +1 -0
- package/admin-build/_app/immutable/nodes/{3.B6q-7qr8.js → 3.DMyKwkGn.js} +1 -1
- package/admin-build/_app/immutable/nodes/30.BaHNeEmc.js +1 -0
- package/admin-build/_app/immutable/nodes/31.C6PV5L-2.js +1 -0
- package/admin-build/_app/immutable/nodes/4.9E118Ftm.js +1 -0
- package/admin-build/_app/immutable/nodes/5.D8guAl3v.js +1 -0
- package/admin-build/_app/immutable/nodes/6.D1u__DtT.js +1 -0
- package/admin-build/_app/immutable/nodes/7.DWXHnRFf.js +1 -0
- package/admin-build/_app/immutable/nodes/8.Dojd8krc.js +1 -0
- package/admin-build/_app/immutable/nodes/9.CLtrr0K_.js +1 -0
- package/admin-build/_app/version.json +1 -1
- package/admin-build/index.html +7 -7
- package/openapi.json +6 -1941
- package/package.json +3 -3
- package/src/__tests__/openapi-coverage.test.ts +0 -6
- package/src/__tests__/push-handlers.test.ts +1 -1
- package/src/__tests__/room-auth-state-loss.test.ts +6 -0
- package/src/__tests__/room-handler-context.test.ts +0 -31
- package/src/__tests__/room-rate-limit-scopes.test.ts +1 -5
- package/src/__tests__/room-runtime-routing.test.ts +24 -111
- package/src/__tests__/route-parser.test.ts +6 -0
- package/src/__tests__/schema.test.ts +15 -6
- package/src/__tests__/smoke-skip-report.test.ts +1 -1
- package/src/durable-objects/database-do.ts +7 -1
- package/src/durable-objects/room-runtime-base.ts +290 -57
- package/src/durable-objects/rooms-do.ts +212 -1336
- package/src/index.ts +23 -9
- package/src/lib/d1-handler.ts +32 -17
- package/src/lib/openapi.ts +1 -4
- package/src/lib/postgres-handler.ts +24 -12
- package/src/lib/route-parser.ts +3 -0
- package/src/lib/schemas.ts +12 -2
- package/src/middleware/captcha-verify.ts +16 -3
- package/src/middleware/error-handler.ts +1 -1
- package/src/middleware/rules.ts +28 -9
- package/src/routes/admin-auth.ts +3 -3
- package/src/routes/admin.ts +13 -8
- package/src/routes/analytics-api.ts +3 -3
- package/src/routes/auth.ts +1 -1
- package/src/routes/backup.ts +1 -1
- package/src/routes/d1.ts +14 -7
- package/src/routes/database-live.ts +13 -6
- package/src/routes/kv.ts +21 -10
- package/src/routes/oauth.ts +1 -1
- package/src/routes/push.ts +119 -77
- package/src/routes/room.ts +203 -280
- package/src/routes/schema-endpoint.ts +2 -2
- package/src/routes/sql.ts +10 -6
- package/src/routes/storage.ts +4 -2
- package/src/routes/vectorize.ts +16 -4
- package/src/types.ts +1 -14
- package/admin-build/_app/immutable/chunks/6oMK_164.js +0 -1
- package/admin-build/_app/immutable/chunks/BEW7Ez_g.js +0 -1
- package/admin-build/_app/immutable/chunks/BoOooyH6.js +0 -1
- package/admin-build/_app/immutable/chunks/BvHnF5tV.js +0 -1
- package/admin-build/_app/immutable/chunks/CoI6jjbg.js +0 -2
- package/admin-build/_app/immutable/chunks/CrOZMmdF.js +0 -1
- package/admin-build/_app/immutable/chunks/Cw6OYcq-.js +0 -1
- package/admin-build/_app/immutable/chunks/DPdQ7z0T.js +0 -128
- package/admin-build/_app/immutable/chunks/pUxw8jfq.js +0 -1
- package/admin-build/_app/immutable/entry/start.Cl6sLxnz.js +0 -1
- package/admin-build/_app/immutable/nodes/1.DxcSsEqS.js +0 -1
- package/admin-build/_app/immutable/nodes/10.DuAd4aIm.js +0 -1
- package/admin-build/_app/immutable/nodes/11.0jgHQL92.js +0 -1
- package/admin-build/_app/immutable/nodes/12.CKNPqmyy.js +0 -1
- package/admin-build/_app/immutable/nodes/13.B1p2POXS.js +0 -110
- package/admin-build/_app/immutable/nodes/14.Bb-REBND.js +0 -3
- package/admin-build/_app/immutable/nodes/15.1uBFCX0X.js +0 -1
- package/admin-build/_app/immutable/nodes/18.CoiwfAuQ.js +0 -1
- package/admin-build/_app/immutable/nodes/19.B8ZdLlXj.js +0 -2
- package/admin-build/_app/immutable/nodes/21.CJFaf0Ia.js +0 -1
- package/admin-build/_app/immutable/nodes/22.CItETFzy.js +0 -1
- package/admin-build/_app/immutable/nodes/24.CWbEqNMB.js +0 -2
- package/admin-build/_app/immutable/nodes/25.DRkLEhKi.js +0 -2
- package/admin-build/_app/immutable/nodes/26.BRxO8AYH.js +0 -1
- package/admin-build/_app/immutable/nodes/27.BLs-nVHz.js +0 -1
- package/admin-build/_app/immutable/nodes/28.G79qkdBK.js +0 -1
- package/admin-build/_app/immutable/nodes/29.BOcI6g0N.js +0 -1
- package/admin-build/_app/immutable/nodes/30.DAIC7dKd.js +0 -1
- package/admin-build/_app/immutable/nodes/31.pl0XXjXF.js +0 -1
- package/admin-build/_app/immutable/nodes/4.DOdvVlZj.js +0 -1
- package/admin-build/_app/immutable/nodes/5.BW_zlgye.js +0 -1
- package/admin-build/_app/immutable/nodes/6.Dxy1CAI2.js +0 -1
- package/admin-build/_app/immutable/nodes/7.BG98w_o7.js +0 -1
- package/admin-build/_app/immutable/nodes/8.DoG5R2rG.js +0 -1
- package/admin-build/_app/immutable/nodes/9.Dmxf6zAC.js +0 -1
- package/src/__tests__/cloudflare-realtime.test.ts +0 -113
- package/src/lib/cloudflare-realtime.ts +0 -251
package/src/routes/admin.ts
CHANGED
|
@@ -362,7 +362,7 @@ adminRoute.onError((err, c) => {
|
|
|
362
362
|
return c.json(err.toJSON(), err.code as 400);
|
|
363
363
|
}
|
|
364
364
|
console.error('Admin Dashboard unhandled error:', err);
|
|
365
|
-
return c.json({ code: 500, message: '
|
|
365
|
+
return c.json({ code: 500, message: 'Admin dashboard request failed unexpectedly. Check the worker logs for the original exception.' }, 500);
|
|
366
366
|
});
|
|
367
367
|
|
|
368
368
|
// ─────────────────────────────────────────────
|
|
@@ -2133,7 +2133,12 @@ api.openapi(adminImportTable, async (c) => {
|
|
|
2133
2133
|
}
|
|
2134
2134
|
} catch (err) {
|
|
2135
2135
|
for (let j = 0; j < chunk.length; j++) {
|
|
2136
|
-
errors.push({
|
|
2136
|
+
errors.push({
|
|
2137
|
+
row: i + j,
|
|
2138
|
+
message: err instanceof Error
|
|
2139
|
+
? err.message
|
|
2140
|
+
: 'Import failed with an unexpected admin API error. Check worker logs for details.',
|
|
2141
|
+
});
|
|
2137
2142
|
}
|
|
2138
2143
|
}
|
|
2139
2144
|
}
|
|
@@ -3436,7 +3441,7 @@ api.openapi(adminDestroyApp, async (c) => {
|
|
|
3436
3441
|
const label = `D1 ${r.name}`;
|
|
3437
3442
|
const result = await cfApi(accountId, apiToken, 'DELETE', `/d1/database/${r.id}`);
|
|
3438
3443
|
if (result.ok) deleted.push(label);
|
|
3439
|
-
else failed.push({ resource: label, error: result.error ?? 'Unknown error' });
|
|
3444
|
+
else failed.push({ resource: label, error: result.error ?? 'Unknown Cloudflare API error while deleting this resource.' });
|
|
3440
3445
|
}
|
|
3441
3446
|
}
|
|
3442
3447
|
|
|
@@ -3446,7 +3451,7 @@ api.openapi(adminDestroyApp, async (c) => {
|
|
|
3446
3451
|
const label = `Vectorize ${indexName}`;
|
|
3447
3452
|
const result = await cfApi(accountId, apiToken, 'DELETE', `/vectorize/v2/indexes/${indexName}`);
|
|
3448
3453
|
if (result.ok) deleted.push(label);
|
|
3449
|
-
else failed.push({ resource: label, error: result.error ?? 'Unknown error' });
|
|
3454
|
+
else failed.push({ resource: label, error: result.error ?? 'Unknown Cloudflare API error while deleting this resource.' });
|
|
3450
3455
|
}
|
|
3451
3456
|
}
|
|
3452
3457
|
|
|
@@ -3455,7 +3460,7 @@ api.openapi(adminDestroyApp, async (c) => {
|
|
|
3455
3460
|
const label = `Hyperdrive ${r.name}`;
|
|
3456
3461
|
const result = await cfApi(accountId, apiToken, 'DELETE', `/hyperdrive/configs/${r.id}`);
|
|
3457
3462
|
if (result.ok) deleted.push(label);
|
|
3458
|
-
else failed.push({ resource: label, error: result.error ?? 'Unknown error' });
|
|
3463
|
+
else failed.push({ resource: label, error: result.error ?? 'Unknown Cloudflare API error while deleting this resource.' });
|
|
3459
3464
|
}
|
|
3460
3465
|
}
|
|
3461
3466
|
|
|
@@ -3492,7 +3497,7 @@ api.openapi(adminDestroyApp, async (c) => {
|
|
|
3492
3497
|
// Turnstile uses zone-level API, not account
|
|
3493
3498
|
const result = await cfApi(accountId, apiToken, 'DELETE', `/challenges/widgets/${r.id}`);
|
|
3494
3499
|
if (result.ok) deleted.push(label);
|
|
3495
|
-
else failed.push({ resource: label, error: result.error ?? 'Unknown error' });
|
|
3500
|
+
else failed.push({ resource: label, error: result.error ?? 'Unknown Cloudflare API error while deleting this resource.' });
|
|
3496
3501
|
}
|
|
3497
3502
|
}
|
|
3498
3503
|
|
|
@@ -3503,7 +3508,7 @@ api.openapi(adminDestroyApp, async (c) => {
|
|
|
3503
3508
|
if (result.ok) {
|
|
3504
3509
|
deleted.push(label);
|
|
3505
3510
|
} else {
|
|
3506
|
-
failed.push({ resource: label, error: result.error ?? 'Unknown error' });
|
|
3511
|
+
failed.push({ resource: label, error: result.error ?? 'Unknown Cloudflare API error while deleting this resource.' });
|
|
3507
3512
|
}
|
|
3508
3513
|
}
|
|
3509
3514
|
|
|
@@ -3513,7 +3518,7 @@ api.openapi(adminDestroyApp, async (c) => {
|
|
|
3513
3518
|
const label = `KV ${r.name}`;
|
|
3514
3519
|
const result = await cfApi(accountId, apiToken, 'DELETE', `/storage/kv/namespaces/${r.id}`);
|
|
3515
3520
|
if (result.ok) deleted.push(label);
|
|
3516
|
-
else failed.push({ resource: label, error: result.error ?? 'Unknown error' });
|
|
3521
|
+
else failed.push({ resource: label, error: result.error ?? 'Unknown Cloudflare API error while deleting this resource.' });
|
|
3517
3522
|
}
|
|
3518
3523
|
}
|
|
3519
3524
|
|
|
@@ -29,10 +29,10 @@ function requireServiceKey(c: { env: Env; req: { header: (name: string) => strin
|
|
|
29
29
|
const constraintCtx = buildConstraintCtx(c.env as never, c.req);
|
|
30
30
|
const { result } = validateKey(provided, 'analytics:*:*:*', config, c.env as never, undefined, constraintCtx);
|
|
31
31
|
if (result === 'missing') {
|
|
32
|
-
throw new EdgeBaseError(403, 'Service
|
|
32
|
+
throw new EdgeBaseError(403, 'X-EdgeBase-Service-Key is required for analytics queries.');
|
|
33
33
|
}
|
|
34
34
|
if (result === 'invalid') {
|
|
35
|
-
throw new EdgeBaseError(401, 'Invalid Service
|
|
35
|
+
throw new EdgeBaseError(401, 'Invalid X-EdgeBase-Service-Key for analytics queries.');
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -107,7 +107,7 @@ analyticsApi.openapi(trackEvents, async (c) => {
|
|
|
107
107
|
try {
|
|
108
108
|
body = await c.req.json();
|
|
109
109
|
} catch {
|
|
110
|
-
throw new EdgeBaseError(400, 'Invalid JSON body.');
|
|
110
|
+
throw new EdgeBaseError(400, 'Invalid JSON body for analytics tracking. Send application/json with { events: [...] }.');
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
const events = body.events;
|
package/src/routes/auth.ts
CHANGED
|
@@ -1238,7 +1238,7 @@ authRoute.openapi(signinAnonymous, async (c) => {
|
|
|
1238
1238
|
|
|
1239
1239
|
const user = await authService.getUserById(db, userId);
|
|
1240
1240
|
if (user) {
|
|
1241
|
-
syncUserPublic(c.env, c.executionCtx, userId, authService.buildPublicUserData(user));
|
|
1241
|
+
await syncUserPublic(c.env, c.executionCtx, userId, authService.buildPublicUserData(user), true);
|
|
1242
1242
|
|
|
1243
1243
|
return c.json({
|
|
1244
1244
|
user: authService.sanitizeUser(user),
|
package/src/routes/backup.ts
CHANGED
|
@@ -86,7 +86,7 @@ backupRoute.onError((err, c) => {
|
|
|
86
86
|
return c.json(err.toJSON(), err.code as 400);
|
|
87
87
|
}
|
|
88
88
|
console.error('Backup API error:', err);
|
|
89
|
-
return c.json({ code: 500, message: '
|
|
89
|
+
return c.json({ code: 500, message: 'Backup operation failed unexpectedly. Check the worker logs for the original exception.' }, 500);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
92
|
// ─── DO Name Helpers ───
|
package/src/routes/d1.ts
CHANGED
|
@@ -22,6 +22,10 @@ import { zodDefaultHook, d1BodySchema, jsonResponseSchema, errorResponseSchema }
|
|
|
22
22
|
|
|
23
23
|
export const d1Route = new OpenAPIHono<HonoEnv>({ defaultHook: zodDefaultHook });
|
|
24
24
|
|
|
25
|
+
function invalidD1JsonMessage(): string {
|
|
26
|
+
return 'Invalid JSON body. Send application/json with { query, params? }.';
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
/**
|
|
26
30
|
* POST /api/d1/:database
|
|
27
31
|
* Body: { query: string, params?: unknown[] }
|
|
@@ -51,12 +55,12 @@ d1Route.openapi(executeD1Query, async (c) => {
|
|
|
51
55
|
try {
|
|
52
56
|
body = await c.req.json();
|
|
53
57
|
} catch {
|
|
54
|
-
return c.json({ code: 400, message:
|
|
58
|
+
return c.json({ code: 400, message: invalidD1JsonMessage() }, 400);
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
const { query, params } = body;
|
|
58
62
|
if (!query || typeof query !== 'string') {
|
|
59
|
-
return c.json({ code: 400, message: 'query
|
|
63
|
+
return c.json({ code: 400, message: "Missing required field 'query'. Send the SQL string in the request body." }, 400);
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
// §2 Allowlist: validate database is declared in config
|
|
@@ -76,16 +80,19 @@ d1Route.openapi(executeD1Query, async (c) => {
|
|
|
76
80
|
buildConstraintCtx(c.env, c.req),
|
|
77
81
|
);
|
|
78
82
|
if (skResult === 'missing') {
|
|
79
|
-
return c.json({ code: 403, message:
|
|
83
|
+
return c.json({ code: 403, message: `X-EdgeBase-Service-Key is required to execute raw SQL on D1 database '${nameParam}'.` }, 403);
|
|
80
84
|
}
|
|
81
85
|
if (skResult === 'invalid') {
|
|
82
|
-
return c.json({ code: 401, message:
|
|
86
|
+
return c.json({ code: 401, message: `Invalid X-EdgeBase-Service-Key for D1 database '${nameParam}'.` }, 401);
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
// §1 Env type — dynamic binding access via type assertion
|
|
86
90
|
const binding = (c.env as unknown as Record<string, unknown>)[d1Config.binding] as D1Database | undefined;
|
|
87
91
|
if (!binding) {
|
|
88
|
-
return c.json({
|
|
92
|
+
return c.json({
|
|
93
|
+
code: 500,
|
|
94
|
+
message: `D1 binding '${d1Config.binding}' is unavailable. Check the binding name in edgebase.config.ts and wrangler.toml.`,
|
|
95
|
+
}, 500);
|
|
89
96
|
}
|
|
90
97
|
|
|
91
98
|
// Execute D1 query — all SQL allowed (DDL included), ? bind variables enforced
|
|
@@ -103,7 +110,7 @@ d1Route.openapi(executeD1Query, async (c) => {
|
|
|
103
110
|
},
|
|
104
111
|
});
|
|
105
112
|
} catch (err) {
|
|
106
|
-
const message = err instanceof Error ? err.message : 'D1 query
|
|
107
|
-
throw new EdgeBaseError(400, message);
|
|
113
|
+
const message = err instanceof Error ? err.message : 'Unknown D1 query error';
|
|
114
|
+
throw new EdgeBaseError(400, `D1 query failed for '${nameParam}': ${message}`);
|
|
108
115
|
}
|
|
109
116
|
});
|
|
@@ -26,6 +26,10 @@ import {
|
|
|
26
26
|
|
|
27
27
|
export const databaseLiveRoute = new OpenAPIHono<HonoEnv>({ defaultHook: zodDefaultHook });
|
|
28
28
|
|
|
29
|
+
function invalidDatabaseLiveJsonMessage(): string {
|
|
30
|
+
return 'Invalid JSON body for database-live broadcast. Send application/json with { channel, event, payload? }.';
|
|
31
|
+
}
|
|
32
|
+
|
|
29
33
|
const MAX_PENDING_PER_IP = 5;
|
|
30
34
|
const PENDING_TTL_SECONDS = 60;
|
|
31
35
|
const dbLiveQuerySchema = z.object({
|
|
@@ -269,15 +273,15 @@ databaseLiveRoute.openapi(databaseLiveBroadcast, async (c) => {
|
|
|
269
273
|
try {
|
|
270
274
|
body = await c.req.json();
|
|
271
275
|
} catch {
|
|
272
|
-
return c.json({ code: 400, message:
|
|
276
|
+
return c.json({ code: 400, message: invalidDatabaseLiveJsonMessage() }, 400);
|
|
273
277
|
}
|
|
274
278
|
|
|
275
279
|
const { channel, event, payload } = body;
|
|
276
280
|
if (!channel || typeof channel !== 'string') {
|
|
277
|
-
return c.json({ code: 400, message: 'channel
|
|
281
|
+
return c.json({ code: 400, message: "Missing required field 'channel' for database-live broadcast." }, 400);
|
|
278
282
|
}
|
|
279
283
|
if (!event || typeof event !== 'string') {
|
|
280
|
-
return c.json({ code: 400, message: 'event
|
|
284
|
+
return c.json({ code: 400, message: "Missing required field 'event' for database-live broadcast." }, 400);
|
|
281
285
|
}
|
|
282
286
|
|
|
283
287
|
// Service Key required AND validated
|
|
@@ -291,10 +295,10 @@ databaseLiveRoute.openapi(databaseLiveBroadcast, async (c) => {
|
|
|
291
295
|
buildConstraintCtx(c.env, c.req),
|
|
292
296
|
);
|
|
293
297
|
if (skResult === 'missing') {
|
|
294
|
-
return c.json({ code: 403, message:
|
|
298
|
+
return c.json({ code: 403, message: `X-EdgeBase-Service-Key is required to broadcast to database-live channel '${channel}'.` }, 403);
|
|
295
299
|
}
|
|
296
300
|
if (skResult === 'invalid') {
|
|
297
|
-
return c.json({ code: 401, message:
|
|
301
|
+
return c.json({ code: 401, message: `Invalid X-EdgeBase-Service-Key for database-live channel '${channel}'.` }, 401);
|
|
298
302
|
}
|
|
299
303
|
|
|
300
304
|
// Route broadcast through the DatabaseLiveDO hub
|
|
@@ -308,7 +312,10 @@ databaseLiveRoute.openapi(databaseLiveBroadcast, async (c) => {
|
|
|
308
312
|
}));
|
|
309
313
|
|
|
310
314
|
if (!doResponse.ok) {
|
|
311
|
-
return c.json({
|
|
315
|
+
return c.json({
|
|
316
|
+
code: doResponse.status,
|
|
317
|
+
message: `Broadcast to database-live channel '${channel}' failed for event '${event}'.`,
|
|
318
|
+
}, doResponse.status as 400 | 500);
|
|
312
319
|
}
|
|
313
320
|
|
|
314
321
|
return c.json({ ok: true });
|
package/src/routes/kv.ts
CHANGED
|
@@ -20,6 +20,14 @@ import { zodDefaultHook, kvBodySchema, jsonResponseSchema, errorResponseSchema }
|
|
|
20
20
|
|
|
21
21
|
export const kvRoute = new OpenAPIHono<HonoEnv>({ defaultHook: zodDefaultHook });
|
|
22
22
|
|
|
23
|
+
function invalidKvJsonMessage(): string {
|
|
24
|
+
return 'Invalid JSON body. Send application/json with a KV operation payload like { action, key, value }.';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function missingKvFieldMessage(field: string, action: string): string {
|
|
28
|
+
return `Missing required field '${field}' for KV action '${action}'.`;
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
function normalizeKvBindingError(action: string, error: unknown): EdgeBaseError {
|
|
24
32
|
const message = error instanceof Error ? error.message : String(error);
|
|
25
33
|
const lowered = message.toLowerCase();
|
|
@@ -75,12 +83,12 @@ kvRoute.openapi(kvOperation, async (c) => {
|
|
|
75
83
|
try {
|
|
76
84
|
body = await c.req.json();
|
|
77
85
|
} catch {
|
|
78
|
-
return c.json({ code: 400, message:
|
|
86
|
+
return c.json({ code: 400, message: invalidKvJsonMessage() }, 400);
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
const { action } = body;
|
|
82
90
|
if (!action || !['get', 'set', 'delete', 'list'].includes(action)) {
|
|
83
|
-
return c.json({ code: 400, message: "action
|
|
91
|
+
return c.json({ code: 400, message: "Invalid KV action. Expected one of: 'get', 'set', 'delete', 'list'." }, 400);
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
// §2 Allowlist: validate namespace is declared in config
|
|
@@ -105,28 +113,31 @@ kvRoute.openapi(kvOperation, async (c) => {
|
|
|
105
113
|
buildConstraintCtx(c.env, c.req),
|
|
106
114
|
);
|
|
107
115
|
if (skResult === 'missing') {
|
|
108
|
-
return c.json({ code: 403, message:
|
|
116
|
+
return c.json({ code: 403, message: `X-EdgeBase-Service-Key is required to access KV namespace '${nameParam}'.` }, 403);
|
|
109
117
|
}
|
|
110
118
|
if (skResult === 'invalid') {
|
|
111
|
-
return c.json({ code: 401, message:
|
|
119
|
+
return c.json({ code: 401, message: `Invalid X-EdgeBase-Service-Key for KV namespace '${nameParam}'.` }, 401);
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
// §1 Env type — dynamic binding access via type assertion
|
|
115
123
|
const binding = (c.env as unknown as Record<string, unknown>)[kvConfig.binding] as KVNamespace | undefined;
|
|
116
124
|
if (!binding) {
|
|
117
|
-
return c.json({
|
|
125
|
+
return c.json({
|
|
126
|
+
code: 500,
|
|
127
|
+
message: `KV binding '${kvConfig.binding}' is unavailable. Check the binding name in edgebase.config.ts and wrangler.toml.`,
|
|
128
|
+
}, 500);
|
|
118
129
|
}
|
|
119
130
|
|
|
120
131
|
// Execute KV operation
|
|
121
132
|
switch (action) {
|
|
122
133
|
case 'get': {
|
|
123
|
-
if (!body.key) return c.json({ code: 400, message: 'key
|
|
134
|
+
if (!body.key) return c.json({ code: 400, message: missingKvFieldMessage('key', 'get') }, 400);
|
|
124
135
|
const value = await binding.get(body.key);
|
|
125
136
|
return c.json({ value });
|
|
126
137
|
}
|
|
127
138
|
case 'set': {
|
|
128
|
-
if (!body.key) return c.json({ code: 400, message: 'key
|
|
129
|
-
if (body.value === undefined) return c.json({ code: 400, message: 'value
|
|
139
|
+
if (!body.key) return c.json({ code: 400, message: missingKvFieldMessage('key', 'set') }, 400);
|
|
140
|
+
if (body.value === undefined) return c.json({ code: 400, message: missingKvFieldMessage('value', 'set') }, 400);
|
|
130
141
|
const putOptions: KVNamespacePutOptions = {};
|
|
131
142
|
if (body.ttl) putOptions.expirationTtl = body.ttl;
|
|
132
143
|
try {
|
|
@@ -137,7 +148,7 @@ kvRoute.openapi(kvOperation, async (c) => {
|
|
|
137
148
|
return c.json({ ok: true });
|
|
138
149
|
}
|
|
139
150
|
case 'delete': {
|
|
140
|
-
if (!body.key) return c.json({ code: 400, message: 'key
|
|
151
|
+
if (!body.key) return c.json({ code: 400, message: missingKvFieldMessage('key', 'delete') }, 400);
|
|
141
152
|
try {
|
|
142
153
|
await binding.delete(body.key);
|
|
143
154
|
} catch (error) {
|
|
@@ -162,6 +173,6 @@ kvRoute.openapi(kvOperation, async (c) => {
|
|
|
162
173
|
});
|
|
163
174
|
}
|
|
164
175
|
default:
|
|
165
|
-
return c.json({ code: 400, message:
|
|
176
|
+
return c.json({ code: 400, message: `Unsupported KV action '${action}'.` }, 400);
|
|
166
177
|
}
|
|
167
178
|
});
|
package/src/routes/oauth.ts
CHANGED
|
@@ -86,7 +86,7 @@ oauthRoute.onError((err, c) => {
|
|
|
86
86
|
return c.json(err.toJSON(), err.code as 400);
|
|
87
87
|
}
|
|
88
88
|
console.error('OAuth unhandled error:', err);
|
|
89
|
-
return c.json({ code: 500, message: 'OAuth
|
|
89
|
+
return c.json({ code: 500, message: 'OAuth flow failed unexpectedly. Check the worker logs for the original exception.' }, 500);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
92
|
// ─── Helpers ───
|