@edge-base/server 0.3.1 → 0.3.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/admin-build/_app/immutable/chunks/{D3hdqsfp.js → BENT96gS.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BSIzoJ5I.js → BR-X1vqg.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BkrC_6Ss.js → BYTXdmFz.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DOOhHO-v.js → C-_7bS5w.js} +1 -1
- package/admin-build/_app/immutable/chunks/{aPulNooa.js → CCB6XzFZ.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DF40xjpj.js → CWTeyOdB.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Dvlxm2Iq.js → CX5CmbTG.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Cek51lkE.js → CdgSuiqt.js} +3 -3
- package/admin-build/_app/immutable/chunks/{Xm1dHMTE.js → Cuny2nPU.js} +1 -1
- package/admin-build/_app/immutable/chunks/{C1Mycuvd.js → D15DP4DQ.js} +1 -1
- package/admin-build/_app/immutable/chunks/{dZsKGnWZ.js → DAzRCtch.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DdjFeYwG.js → DYF4byEY.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BunyH6GE.js → Dcey5WNo.js} +1 -1
- package/admin-build/_app/immutable/chunks/{C-zXt-cq.js → HK54RDdv.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BeYswbvA.js → gUtOgqgM.js} +1 -1
- package/admin-build/_app/immutable/chunks/{D0mNYcxN.js → pnqPwnFK.js} +1 -1
- package/admin-build/_app/immutable/entry/{app.j1WHBlZX.js → app.DwslmBQo.js} +2 -2
- package/admin-build/_app/immutable/entry/start.Bi51W3Uj.js +1 -0
- package/admin-build/_app/immutable/nodes/{0.Oo7ZpbR-.js → 0.SSaZIC3t.js} +1 -1
- package/admin-build/_app/immutable/nodes/{1.zgMnKHns.js → 1.CqSv9lq5.js} +1 -1
- package/admin-build/_app/immutable/nodes/{10.u5mkRiUe.js → 10.BoQz_717.js} +1 -1
- package/admin-build/_app/immutable/nodes/{11.9ynAgaxV.js → 11.B-yS_gft.js} +1 -1
- package/admin-build/_app/immutable/nodes/{12.M_EqzJ6s.js → 12.WsVtLoVx.js} +1 -1
- package/admin-build/_app/immutable/nodes/{13.D_NQ9q60.js → 13.TbqRQJKh.js} +1 -1
- package/admin-build/_app/immutable/nodes/{14.-vENIJ2w.js → 14.cldePwj3.js} +1 -1
- package/admin-build/_app/immutable/nodes/{15.DoMCZhGv.js → 15.Bec037-f.js} +1 -1
- package/admin-build/_app/immutable/nodes/{16.6BmnzScq.js → 16.Dd0yTYhN.js} +1 -1
- package/admin-build/_app/immutable/nodes/{17.q6QQ9N_J.js → 17.CoiqH0lF.js} +1 -1
- package/admin-build/_app/immutable/nodes/{18.DUhJ6dKj.js → 18.BDXxM1Qi.js} +1 -1
- package/admin-build/_app/immutable/nodes/{19.CvkvPlGP.js → 19.lo9mTPiR.js} +1 -1
- package/admin-build/_app/immutable/nodes/{20.cACSbRIl.js → 20.B7kbGqh_.js} +1 -1
- package/admin-build/_app/immutable/nodes/21.BGm1TcfO.js +1 -0
- package/admin-build/_app/immutable/nodes/{22.BsQ6xeKr.js → 22.VOtCuTkf.js} +1 -1
- package/admin-build/_app/immutable/nodes/{23.CNW3mLz5.js → 23.C2Uxx-kO.js} +1 -1
- package/admin-build/_app/immutable/nodes/{24.CReqtEOy.js → 24.BxdEwsS3.js} +1 -1
- package/admin-build/_app/immutable/nodes/{25.BuxVhqlb.js → 25.CVu2qoCv.js} +1 -1
- package/admin-build/_app/immutable/nodes/{26.C-LzPf62.js → 26.DqYlpX7Z.js} +1 -1
- package/admin-build/_app/immutable/nodes/{27.Dl5RL_tB.js → 27.emgbpP8y.js} +1 -1
- package/admin-build/_app/immutable/nodes/{28.CWfSQgjH.js → 28.BVPSPK68.js} +1 -1
- package/admin-build/_app/immutable/nodes/{29.wUSfh2eQ.js → 29.BsDQQ16z.js} +1 -1
- package/admin-build/_app/immutable/nodes/{3.D_laBXeK.js → 3.uc8joqGb.js} +1 -1
- package/admin-build/_app/immutable/nodes/{30.DcMhZyQ0.js → 30.D4CYqQrE.js} +1 -1
- package/admin-build/_app/immutable/nodes/{31.CSwhNsNi.js → 31.DvUuXrVU.js} +1 -1
- package/admin-build/_app/immutable/nodes/{4.Cq28vPI2.js → 4.BqTZ0Uoq.js} +1 -1
- package/admin-build/_app/immutable/nodes/{5.BTT9B3oI.js → 5.DAO3--6W.js} +1 -1
- package/admin-build/_app/immutable/nodes/{6.iCmmzXZp.js → 6.CBbGl_Wo.js} +1 -1
- package/admin-build/_app/immutable/nodes/{7.qY5v7qqJ.js → 7.s-I8cKAh.js} +1 -1
- package/admin-build/_app/immutable/nodes/{8.C6p4RvgK.js → 8.Ct6B-y3f.js} +1 -1
- package/admin-build/_app/immutable/nodes/{9.f548N7zh.js → 9.Dok_ZoeZ.js} +1 -1
- package/admin-build/_app/version.json +1 -1
- package/admin-build/index.html +7 -7
- package/openapi.json +193 -0
- package/package.json +3 -3
- package/src/__tests__/error-format.test.ts +21 -0
- package/src/__tests__/postgres-api-consistency.test.ts +240 -0
- package/src/__tests__/postgres-batch-compat.test.ts +33 -27
- package/src/__tests__/postgres-batch.test.ts +394 -0
- package/src/__tests__/postgres-field-ops-compat.test.ts +2 -1
- package/src/__tests__/postgres-transact.test.ts +148 -0
- package/src/__tests__/smoke-skip-report.test.ts +1 -1
- package/src/durable-objects/database-do.ts +455 -15
- package/src/lib/d1-handler.ts +416 -50
- package/src/lib/errors.ts +8 -4
- package/src/lib/internal-transport.ts +10 -0
- package/src/lib/postgres-handler.ts +562 -84
- package/src/routes/auth.ts +58 -46
- package/src/routes/tables.ts +140 -0
- package/admin-build/_app/immutable/entry/start.BgMeGq-q.js +0 -1
- package/admin-build/_app/immutable/nodes/21.DZ7G8rMF.js +0 -1
package/src/routes/auth.ts
CHANGED
|
@@ -999,15 +999,8 @@ authRoute.openapi(signup, async (c) => {
|
|
|
999
999
|
|
|
1000
1000
|
const userId = generateId();
|
|
1001
1001
|
const db = getAuthDb(c);
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
try {
|
|
1005
|
-
await registerEmailPending(db, body.email, userId);
|
|
1006
|
-
} catch (err) {
|
|
1007
|
-
if ((err as Error).message === 'EMAIL_ALREADY_REGISTERED') {
|
|
1008
|
-
throw new EdgeBaseError(409, 'Email already registered.', undefined, 'email-already-exists');
|
|
1009
|
-
}
|
|
1010
|
-
throw new EdgeBaseError(500, 'Signup failed. Please try again.', undefined, 'internal-error');
|
|
1002
|
+
if (await lookupEmail(db, body.email)) {
|
|
1003
|
+
throw new EdgeBaseError(409, 'Email already registered.', undefined, 'email-already-exists');
|
|
1011
1004
|
}
|
|
1012
1005
|
|
|
1013
1006
|
// Create user directly in D1
|
|
@@ -1015,10 +1008,27 @@ authRoute.openapi(signup, async (c) => {
|
|
|
1015
1008
|
// Password policy validation
|
|
1016
1009
|
const policyResult = await validatePassword(body.password, getPasswordPolicyConfig(c.env));
|
|
1017
1010
|
if (!policyResult.valid) {
|
|
1018
|
-
await deleteEmailPending(db, body.email).catch(() => {});
|
|
1019
1011
|
throw new EdgeBaseError(400, policyResult.errors[0], { password: { code: 'password_policy', message: policyResult.errors.join('; ') } }, 'password-policy');
|
|
1020
1012
|
}
|
|
1021
1013
|
|
|
1014
|
+
// beforeSignUp hook — blocking, can cancel signup
|
|
1015
|
+
await executeAuthHook(c.env, c.executionCtx, 'beforeSignUp', {
|
|
1016
|
+
id: userId,
|
|
1017
|
+
email: body.email,
|
|
1018
|
+
displayName,
|
|
1019
|
+
avatarUrl,
|
|
1020
|
+
}, { blocking: true, workerUrl: getWorkerUrl(c.req.url, c.env) });
|
|
1021
|
+
|
|
1022
|
+
// Register pending in D1 email index
|
|
1023
|
+
try {
|
|
1024
|
+
await registerEmailPending(db, body.email, userId);
|
|
1025
|
+
} catch (err) {
|
|
1026
|
+
if ((err as Error).message === 'EMAIL_ALREADY_REGISTERED') {
|
|
1027
|
+
throw new EdgeBaseError(409, 'Email already registered.', undefined, 'email-already-exists');
|
|
1028
|
+
}
|
|
1029
|
+
throw new EdgeBaseError(500, 'Signup failed. Please try again.', undefined, 'internal-error');
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1022
1032
|
const passwordHash = await hashPassword(body.password);
|
|
1023
1033
|
|
|
1024
1034
|
await authService.createUser(db, {
|
|
@@ -1033,14 +1043,6 @@ authRoute.openapi(signup, async (c) => {
|
|
|
1033
1043
|
locale,
|
|
1034
1044
|
});
|
|
1035
1045
|
|
|
1036
|
-
// beforeSignUp hook — blocking, can cancel signup
|
|
1037
|
-
await executeAuthHook(c.env, c.executionCtx, 'beforeSignUp', {
|
|
1038
|
-
id: userId,
|
|
1039
|
-
email: body.email,
|
|
1040
|
-
displayName,
|
|
1041
|
-
avatarUrl,
|
|
1042
|
-
}, { blocking: true, workerUrl: getWorkerUrl(c.req.url, c.env) });
|
|
1043
|
-
|
|
1044
1046
|
// Create session + tokens
|
|
1045
1047
|
const session = await createSessionAndTokens(c.env, userId, ip, c.req.header('user-agent') || '');
|
|
1046
1048
|
|
|
@@ -1382,19 +1384,24 @@ authRoute.openapi(signinMagicLink, async (c) => {
|
|
|
1382
1384
|
} else if (autoCreate) {
|
|
1383
1385
|
// Auto-create user + send magic link
|
|
1384
1386
|
const userId = generateId();
|
|
1387
|
+
const reqLocale = parseAcceptLanguage(c.req.header('accept-language'));
|
|
1385
1388
|
|
|
1386
1389
|
try {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1390
|
+
// beforeSignUp hook
|
|
1391
|
+
await executeAuthHook(c.env, c.executionCtx, 'beforeSignUp', {
|
|
1392
|
+
id: userId, email: body.email, displayName: null, avatarUrl: null,
|
|
1393
|
+
}, { blocking: true, workerUrl: getWorkerUrl(c.req.url, c.env) });
|
|
1394
|
+
|
|
1395
|
+
try {
|
|
1396
|
+
await registerEmailPending(db, body.email, userId);
|
|
1397
|
+
} catch (err) {
|
|
1398
|
+
if ((err as Error).message === 'EMAIL_ALREADY_REGISTERED') {
|
|
1399
|
+
return c.json({ ok: true });
|
|
1400
|
+
}
|
|
1401
|
+
throw new EdgeBaseError(500, 'Magic link request failed.', undefined, 'internal-error');
|
|
1391
1402
|
}
|
|
1392
|
-
throw new EdgeBaseError(500, 'Magic link request failed.', undefined, 'internal-error');
|
|
1393
|
-
}
|
|
1394
1403
|
|
|
1395
|
-
try {
|
|
1396
1404
|
// Create user with no password, verified = 1
|
|
1397
|
-
const reqLocale = parseAcceptLanguage(c.req.header('accept-language'));
|
|
1398
1405
|
await authService.createUser(db, {
|
|
1399
1406
|
userId,
|
|
1400
1407
|
email: body.email,
|
|
@@ -1405,11 +1412,6 @@ authRoute.openapi(signinMagicLink, async (c) => {
|
|
|
1405
1412
|
locale: reqLocale ?? 'en',
|
|
1406
1413
|
});
|
|
1407
1414
|
|
|
1408
|
-
// beforeSignUp hook
|
|
1409
|
-
await executeAuthHook(c.env, c.executionCtx, 'beforeSignUp', {
|
|
1410
|
-
id: userId, email: body.email, displayName: null, avatarUrl: null,
|
|
1411
|
-
}, { blocking: true, workerUrl: getWorkerUrl(c.req.url, c.env) });
|
|
1412
|
-
|
|
1413
1415
|
// Sync to _users_public
|
|
1414
1416
|
const user = await authService.getUserById(db, userId);
|
|
1415
1417
|
if (user) {
|
|
@@ -1671,15 +1673,20 @@ authRoute.openapi(signinPhone, async (c) => {
|
|
|
1671
1673
|
const userId = generateId();
|
|
1672
1674
|
|
|
1673
1675
|
try {
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1676
|
+
// beforeSignUp hook
|
|
1677
|
+
await executeAuthHook(c.env, c.executionCtx, 'beforeSignUp', {
|
|
1678
|
+
id: userId, email: null, phone, displayName: null, avatarUrl: null,
|
|
1679
|
+
}, { blocking: true, workerUrl: getWorkerUrl(c.req.url, c.env) });
|
|
1680
|
+
|
|
1681
|
+
try {
|
|
1682
|
+
await registerPhonePending(db, phone, userId);
|
|
1683
|
+
} catch (err) {
|
|
1684
|
+
if ((err as Error).message === 'PHONE_ALREADY_REGISTERED') {
|
|
1685
|
+
return c.json({ ok: true });
|
|
1686
|
+
}
|
|
1687
|
+
throw new EdgeBaseError(500, 'Phone OTP request failed.', undefined, 'internal-error');
|
|
1678
1688
|
}
|
|
1679
|
-
throw new EdgeBaseError(500, 'Phone OTP request failed.', undefined, 'internal-error');
|
|
1680
|
-
}
|
|
1681
1689
|
|
|
1682
|
-
try {
|
|
1683
1690
|
// Create user with phone in D1
|
|
1684
1691
|
await authService.createUser(db, {
|
|
1685
1692
|
userId,
|
|
@@ -2143,19 +2150,24 @@ authRoute.openapi(signinEmailOtp, async (c) => {
|
|
|
2143
2150
|
}
|
|
2144
2151
|
|
|
2145
2152
|
const userId = generateId();
|
|
2153
|
+
const otpReqLocale = parseAcceptLanguage(c.req.header('accept-language'));
|
|
2146
2154
|
|
|
2147
2155
|
try {
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2156
|
+
// beforeSignUp hook
|
|
2157
|
+
await executeAuthHook(c.env, c.executionCtx, 'beforeSignUp', {
|
|
2158
|
+
id: userId, email, displayName: null, avatarUrl: null,
|
|
2159
|
+
}, { blocking: true, workerUrl: getWorkerUrl(c.req.url, c.env) });
|
|
2160
|
+
|
|
2161
|
+
try {
|
|
2162
|
+
await registerEmailPending(db, email, userId);
|
|
2163
|
+
} catch (err) {
|
|
2164
|
+
if ((err as Error).message === 'EMAIL_ALREADY_REGISTERED') {
|
|
2165
|
+
return c.json({ ok: true });
|
|
2166
|
+
}
|
|
2167
|
+
throw new EdgeBaseError(500, 'Email OTP request failed.', undefined, 'internal-error');
|
|
2152
2168
|
}
|
|
2153
|
-
throw new EdgeBaseError(500, 'Email OTP request failed.', undefined, 'internal-error');
|
|
2154
|
-
}
|
|
2155
2169
|
|
|
2156
|
-
try {
|
|
2157
2170
|
// Create user with email, verified = 1
|
|
2158
|
-
const otpReqLocale = parseAcceptLanguage(c.req.header('accept-language'));
|
|
2159
2171
|
await authService.createUser(db, {
|
|
2160
2172
|
userId,
|
|
2161
2173
|
email,
|
package/src/routes/tables.ts
CHANGED
|
@@ -35,8 +35,16 @@ import {
|
|
|
35
35
|
jsonResponseSchema, errorResponseSchema, zodDefaultHook,
|
|
36
36
|
} from '../lib/schemas.js';
|
|
37
37
|
import type { AuthContext } from '@edge-base/shared';
|
|
38
|
+
import { EdgeBaseError } from '@edge-base/shared';
|
|
38
39
|
import { handlePgRequest } from '../lib/postgres-handler.js';
|
|
39
40
|
import { handleD1Request } from '../lib/d1-handler.js';
|
|
41
|
+
import {
|
|
42
|
+
buildKeymap,
|
|
43
|
+
resolveServiceKeyCandidate,
|
|
44
|
+
validateKey,
|
|
45
|
+
type ConstraintContext,
|
|
46
|
+
} from '../lib/service-key.js';
|
|
47
|
+
import { getTrustedClientIp } from '../lib/client-ip.js';
|
|
40
48
|
|
|
41
49
|
|
|
42
50
|
export const tablesRoute = new OpenAPIHono<HonoEnv>({ defaultHook: zodDefaultHook });
|
|
@@ -559,6 +567,138 @@ tablesRoute.openapi(dbDeleteRecord, async (c) => {
|
|
|
559
567
|
return routeToDO(c as unknown as Context<HonoEnv>, namespace, instanceId, table, `/tables/${table}/${id}`);
|
|
560
568
|
});
|
|
561
569
|
|
|
570
|
+
// ─── POST /{namespace}/transact and /{namespace}/{instanceId}/transact ─
|
|
571
|
+
// Multi-table atomic writes. The rules middleware skips non-/tables/ paths,
|
|
572
|
+
// so Service Keys are validated here (per write-op table scope) and access
|
|
573
|
+
// rules are enforced per operation inside the DO. DO/SQLite provider only.
|
|
574
|
+
|
|
575
|
+
const transactBodySchema = z.object({
|
|
576
|
+
operations: z.array(z.record(z.string(), z.unknown())).openapi({
|
|
577
|
+
description: 'Ordered insert | update | delete | expect operations applied atomically.',
|
|
578
|
+
}),
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
const dbSingleTransact = createRoute({
|
|
582
|
+
operationId: 'dbSingleTransact',
|
|
583
|
+
method: 'post',
|
|
584
|
+
path: '/{namespace}/transact',
|
|
585
|
+
tags: ['client'],
|
|
586
|
+
summary: 'Apply multi-table write operations atomically in a single-instance database',
|
|
587
|
+
request: {
|
|
588
|
+
params: z.object({
|
|
589
|
+
namespace: z.string().openapi({ description: 'Database namespace', example: 'app' }),
|
|
590
|
+
}),
|
|
591
|
+
body: { content: { 'application/json': { schema: transactBodySchema } }, required: true },
|
|
592
|
+
},
|
|
593
|
+
responses: {
|
|
594
|
+
200: { description: 'Ordered per-operation results', content: { 'application/json': { schema: jsonResponseSchema } } },
|
|
595
|
+
400: { description: 'Bad request', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
596
|
+
403: { description: 'Forbidden', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
597
|
+
409: { description: 'Expectation failed', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
598
|
+
501: { description: 'Provider unsupported', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
599
|
+
},
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
const dbTransact = createRoute({
|
|
603
|
+
operationId: 'dbTransact',
|
|
604
|
+
method: 'post',
|
|
605
|
+
path: '/{namespace}/{instanceId}/transact',
|
|
606
|
+
tags: ['client'],
|
|
607
|
+
summary: 'Apply multi-table write operations atomically in a dynamic database instance',
|
|
608
|
+
request: {
|
|
609
|
+
params: z.object({
|
|
610
|
+
namespace: z.string().openapi({ description: 'Database namespace', example: 'workspace' }),
|
|
611
|
+
instanceId: z.string().openapi({ description: 'Instance ID', example: 'ws-456' }),
|
|
612
|
+
}),
|
|
613
|
+
body: { content: { 'application/json': { schema: transactBodySchema } }, required: true },
|
|
614
|
+
},
|
|
615
|
+
responses: {
|
|
616
|
+
200: { description: 'Ordered per-operation results', content: { 'application/json': { schema: jsonResponseSchema } } },
|
|
617
|
+
400: { description: 'Bad request', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
618
|
+
403: { description: 'Forbidden', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
619
|
+
409: { description: 'Expectation failed', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
620
|
+
501: { description: 'Provider unsupported', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
621
|
+
},
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
async function handleTransactRoute(
|
|
625
|
+
c: Context<HonoEnv>,
|
|
626
|
+
namespace: string,
|
|
627
|
+
instanceId: string | undefined,
|
|
628
|
+
): Promise<Response> {
|
|
629
|
+
const config = parseConfig(c.env);
|
|
630
|
+
const target = resolveDbTarget(config, namespace, instanceId);
|
|
631
|
+
if (!target.ok) {
|
|
632
|
+
return c.json({
|
|
633
|
+
code: target.status,
|
|
634
|
+
message: formatDbTargetValidationIssue(target.issue, namespace),
|
|
635
|
+
}, target.status);
|
|
636
|
+
}
|
|
637
|
+
// All providers accept /transact: DO via transactionSync, D1 via a single
|
|
638
|
+
// db.batch() transaction, PostgreSQL via BEGIN/COMMIT on a session-scoped
|
|
639
|
+
// connection (the PG handler itself rejects the local dev sidecar).
|
|
640
|
+
|
|
641
|
+
// Service Key validation per write-op table (rules middleware skipped this path).
|
|
642
|
+
const provided = resolveServiceKeyCandidate(
|
|
643
|
+
c.req,
|
|
644
|
+
c.get('serviceKeyToken') as string | null | undefined,
|
|
645
|
+
);
|
|
646
|
+
if (provided) {
|
|
647
|
+
let body: { operations?: Array<Record<string, unknown>> } = {};
|
|
648
|
+
try {
|
|
649
|
+
body = await c.req.json();
|
|
650
|
+
} catch {
|
|
651
|
+
body = {};
|
|
652
|
+
}
|
|
653
|
+
const writeTables = new Set<string>();
|
|
654
|
+
for (const op of body.operations ?? []) {
|
|
655
|
+
if (typeof op.table === 'string' && op.table) writeTables.add(op.table);
|
|
656
|
+
}
|
|
657
|
+
const constraintCtx: ConstraintContext = {
|
|
658
|
+
env: c.env.ENVIRONMENT,
|
|
659
|
+
ip: getTrustedClientIp(c.env, c.req),
|
|
660
|
+
};
|
|
661
|
+
if (target.value.instanceId) constraintCtx.tenantId = target.value.instanceId;
|
|
662
|
+
const keymap = buildKeymap(config, c.env);
|
|
663
|
+
let validCount = 0;
|
|
664
|
+
for (const tableName of writeTables) {
|
|
665
|
+
const { result } = validateKey(
|
|
666
|
+
provided,
|
|
667
|
+
`db:table:${tableName}:write`,
|
|
668
|
+
config,
|
|
669
|
+
c.env,
|
|
670
|
+
keymap,
|
|
671
|
+
constraintCtx,
|
|
672
|
+
);
|
|
673
|
+
if (result === 'invalid') {
|
|
674
|
+
throw new EdgeBaseError(
|
|
675
|
+
401,
|
|
676
|
+
`Invalid X-EdgeBase-Service-Key for scope 'db:table:${tableName}:write'.`,
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
if (result === 'missing') break; // not a recognized key — fall through to user auth
|
|
680
|
+
validCount += 1;
|
|
681
|
+
}
|
|
682
|
+
// Every named table's write scope validated → trusted bypass, like rules middleware.
|
|
683
|
+
if (writeTables.size > 0 && validCount === writeTables.size) {
|
|
684
|
+
c.set('isServiceKey' as never, true);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return routeToDO(c, namespace, instanceId, '', '/transact');
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
tablesRoute.openapi(dbSingleTransact, async (c) => {
|
|
692
|
+
const namespace = c.req.param('namespace')!;
|
|
693
|
+
return handleTransactRoute(c as unknown as Context<HonoEnv>, namespace, undefined);
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
tablesRoute.openapi(dbTransact, async (c) => {
|
|
697
|
+
const namespace = c.req.param('namespace')!;
|
|
698
|
+
const instanceId = c.req.param('instanceId')!;
|
|
699
|
+
return handleTransactRoute(c as unknown as Context<HonoEnv>, namespace, instanceId);
|
|
700
|
+
});
|
|
701
|
+
|
|
562
702
|
// ======================================================================
|
|
563
703
|
// Shared DO proxy logic
|
|
564
704
|
// ======================================================================
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as r}from"../chunks/DdjFeYwG.js";import{w as t}from"../chunks/D3hdqsfp.js";export{t as load_css,r as start};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as m}from"../chunks/Cek51lkE.js";export{m as component};
|