@edge-base/server 0.1.5 → 0.2.1
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/assets/19.4Si2ZFC_.css +1 -0
- package/admin-build/_app/immutable/assets/{3.Dg81Pgmd.css → 3.BtHYobTg.css} +1 -1
- package/admin-build/_app/immutable/assets/SqlEditor.Bbp1RIk0.css +1 -0
- package/admin-build/_app/immutable/assets/TableSqlTab.yeNZfhgG.css +1 -0
- package/admin-build/_app/immutable/chunks/B0QyxC2M.js +128 -0
- package/admin-build/_app/immutable/chunks/{Bsp3uE8m.js → BCKr7yKd.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CWHVkYdi.js → BFs_qStz.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CKIubXVC.js → BTJcQFEp.js} +1 -1
- package/admin-build/_app/immutable/chunks/BY07qVPA.js +1 -0
- package/admin-build/_app/immutable/chunks/{Cn5ZQY9O.js → BcIUK2sk.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CKFIU4S8.js → BsFiK_FJ.js} +1 -1
- package/admin-build/_app/immutable/chunks/{COYZ6F_d.js → CSGrwS7E.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DLc6L4xD.js → CqUxCvs_.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DLTo2HQr.js → D-x55wdW.js} +1 -1
- package/admin-build/_app/immutable/chunks/D755Tqat.js +1 -0
- package/admin-build/_app/immutable/chunks/{Jxx0jGlP.js → DjOEv9M9.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DLRcaFHo.js → DnLqc9L1.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BnlxEqP4.js → Dqk2TGNU.js} +1 -1
- package/admin-build/_app/immutable/chunks/{7FYfV8UU.js → k0CIJkw4.js} +1 -1
- package/admin-build/_app/immutable/chunks/lSpxLU5p.js +2 -0
- package/admin-build/_app/immutable/chunks/m9QZTyVV.js +1 -0
- package/admin-build/_app/immutable/entry/{app.C4kLStKR.js → app.BTsq3_xq.js} +2 -2
- package/admin-build/_app/immutable/entry/start.zXCirpgY.js +1 -0
- package/admin-build/_app/immutable/nodes/0.BZ00WDYH.js +1 -0
- package/admin-build/_app/immutable/nodes/{1.s1kW8gyv.js → 1.RzSJ3yyr.js} +1 -1
- package/admin-build/_app/immutable/nodes/{10.Cr9ml-GD.js → 10.D-rsiquF.js} +1 -1
- package/admin-build/_app/immutable/nodes/{11.DXTOyMEn.js → 11.l7-bgtFD.js} +1 -1
- package/admin-build/_app/immutable/nodes/{12.DzhitECA.js → 12.Dkq0H7B5.js} +1 -1
- package/admin-build/_app/immutable/nodes/{13.U_1VNA5x.js → 13.DtK_4oRz.js} +1 -1
- package/admin-build/_app/immutable/nodes/{14.x0SzmV0P.js → 14.BKo7-AMx.js} +1 -1
- package/admin-build/_app/immutable/nodes/{15.BJI1Z1hk.js → 15.CQAj_6lq.js} +1 -1
- package/admin-build/_app/immutable/nodes/{16.CUvRFqLW.js → 16.XVIG-Ffr.js} +1 -1
- package/admin-build/_app/immutable/nodes/{17.sDFD-Vbm.js → 17.g6raZLCM.js} +1 -1
- package/admin-build/_app/immutable/nodes/{18.DdoTnAXc.js → 18.IQz6a3T6.js} +1 -1
- package/admin-build/_app/immutable/nodes/19.CAAZ8i8h.js +2 -0
- package/admin-build/_app/immutable/nodes/{20.fxudyPKp.js → 20.BPcX3KPj.js} +1 -1
- package/admin-build/_app/immutable/nodes/21.DoPabrY_.js +1 -0
- package/admin-build/_app/immutable/nodes/{22.CY6ICoyn.js → 22.Br5AG_5Z.js} +1 -1
- package/admin-build/_app/immutable/nodes/{23.DmEaHsZw.js → 23.KjbrdXoE.js} +1 -1
- package/admin-build/_app/immutable/nodes/{24.Cqerdln2.js → 24.C3n2-hgw.js} +1 -1
- package/admin-build/_app/immutable/nodes/25.SFDSBzHd.js +2 -0
- package/admin-build/_app/immutable/nodes/26.D95vui6E.js +1 -0
- package/admin-build/_app/immutable/nodes/{27.DAeKhnG9.js → 27.FgLgdjwB.js} +1 -1
- package/admin-build/_app/immutable/nodes/{28.D_lJZpNR.js → 28.B9sYYm1F.js} +1 -1
- package/admin-build/_app/immutable/nodes/{29.Dx7iQCpT.js → 29.DyqZ_wbN.js} +1 -1
- package/admin-build/_app/immutable/nodes/3.Bzo2yVIO.js +2 -0
- package/admin-build/_app/immutable/nodes/{30.DcO-bjQZ.js → 30.c1CiNwiS.js} +1 -1
- package/admin-build/_app/immutable/nodes/{31.DesWAzIF.js → 31.CXty66Vh.js} +1 -1
- package/admin-build/_app/immutable/nodes/{4.B2rm4_4a.js → 4.BgQaXZ27.js} +1 -1
- package/admin-build/_app/immutable/nodes/{5.4Os1aYeW.js → 5.BuJrHvxH.js} +1 -1
- package/admin-build/_app/immutable/nodes/{6.BzR--5Dj.js → 6.CkBBC94k.js} +1 -1
- package/admin-build/_app/immutable/nodes/{7.QDC-47H1.js → 7.D2YBvNFM.js} +1 -1
- package/admin-build/_app/immutable/nodes/{8.DDPWEc96.js → 8.D8qQWo_z.js} +1 -1
- package/admin-build/_app/immutable/nodes/{9.fyfVIJPa.js → 9.BLDLX5hV.js} +1 -1
- package/admin-build/_app/version.json +1 -1
- package/admin-build/index.html +7 -7
- package/openapi.json +6710 -5866
- package/package.json +2 -2
- package/src/__tests__/functions-route.test.ts +153 -0
- package/src/__tests__/internal-request.test.ts +5 -5
- package/src/__tests__/meta-export-coverage.test.ts +3 -2
- package/src/__tests__/meta-route-registration.test.ts +3 -2
- package/src/__tests__/openapi-coverage.test.ts +5 -1
- package/src/__tests__/pagination.test.ts +12 -8
- package/src/__tests__/postgres-dialect.test.ts +2 -2
- package/src/__tests__/query.test.ts +7 -7
- package/src/__tests__/rate-limit.test.ts +0 -1
- package/src/__tests__/room-handler-context.test.ts +31 -0
- package/src/__tests__/room-runtime-routing.test.ts +48 -0
- package/src/__tests__/runtime-surface-accounting.test.ts +5 -4
- package/src/__tests__/smoke-skip-report.test.ts +3 -2
- package/src/durable-objects/database-do.ts +9 -4
- package/src/durable-objects/database-live-do.ts +22 -10
- package/src/durable-objects/logs-do.ts +2 -2
- package/src/durable-objects/rooms-do.ts +202 -0
- package/src/lib/auth-d1-service.ts +1 -1
- package/src/lib/auth-d1.ts +10 -0
- package/src/lib/d1-handler.ts +23 -4
- package/src/lib/internal-request.ts +5 -8
- package/src/lib/openapi.ts +1 -0
- package/src/lib/pagination.ts +3 -3
- package/src/lib/postgres-handler.ts +2 -2
- package/src/lib/query-engine.ts +2 -2
- package/src/middleware/rate-limit.ts +11 -11
- package/src/routes/admin.ts +30 -3
- package/src/routes/auth.ts +74 -33
- package/src/routes/room.ts +42 -0
- package/src/types.ts +6 -0
- package/admin-build/_app/immutable/assets/TableSqlTab.BHquaMBM.css +0 -1
- package/admin-build/_app/immutable/chunks/BPXFNSAT.js +0 -128
- package/admin-build/_app/immutable/chunks/DKjA1S1a.js +0 -1
- package/admin-build/_app/immutable/chunks/fBE8lw-R.js +0 -1
- package/admin-build/_app/immutable/chunks/gE9fQ_Ff.js +0 -2
- package/admin-build/_app/immutable/entry/start.CAAH6ztW.js +0 -1
- package/admin-build/_app/immutable/nodes/0.BywaJfpH.js +0 -1
- package/admin-build/_app/immutable/nodes/19.CUvRFqLW.js +0 -1
- package/admin-build/_app/immutable/nodes/21.N2QN0lbw.js +0 -1
- package/admin-build/_app/immutable/nodes/25.x3hL7Y-O.js +0 -2
- package/admin-build/_app/immutable/nodes/26.CEzfjTSO.js +0 -1
- package/admin-build/_app/immutable/nodes/3.CKC_yDnF.js +0 -2
package/src/routes/auth.ts
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
} from '../lib/auth-redirect.js';
|
|
30
30
|
import {
|
|
31
31
|
signAccessToken, signRefreshToken, verifyRefreshTokenWithFallback,
|
|
32
|
-
parseDuration,
|
|
32
|
+
parseDuration, TokenExpiredError,
|
|
33
33
|
} from '../lib/jwt.js';
|
|
34
34
|
import { generateId } from '../lib/uuid.js';
|
|
35
35
|
import { captchaMiddleware } from '../middleware/captcha-verify.js';
|
|
@@ -108,6 +108,15 @@ authRoute.onError((err, c) => {
|
|
|
108
108
|
|
|
109
109
|
// ─── Helpers ───
|
|
110
110
|
|
|
111
|
+
function isReleaseMode(env: Env): boolean {
|
|
112
|
+
return parseConfig(env)?.release ?? false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function shouldExposeAuthTestSecrets(env: Env): boolean {
|
|
116
|
+
return env.EDGEBASE_TEST === '1'
|
|
117
|
+
|| env.EDGEBASE_TEST === 'true';
|
|
118
|
+
}
|
|
119
|
+
|
|
111
120
|
function requireAuth(auth: AuthContext | null): string {
|
|
112
121
|
if (!auth) {
|
|
113
122
|
throw new EdgeBaseError(401, 'Authentication required.', undefined, 'unauthenticated');
|
|
@@ -500,6 +509,13 @@ async function createSessionAndTokens(
|
|
|
500
509
|
metadata,
|
|
501
510
|
});
|
|
502
511
|
|
|
512
|
+
// Update lastSignedInAt on the user record
|
|
513
|
+
try {
|
|
514
|
+
await db.run('UPDATE _users SET lastSignedInAt = ? WHERE id = ?', [now, userId]);
|
|
515
|
+
} catch {
|
|
516
|
+
// Non-critical: don't block sign-in if this column doesn't exist yet
|
|
517
|
+
}
|
|
518
|
+
|
|
503
519
|
return { accessToken, refreshToken, sessionId };
|
|
504
520
|
}
|
|
505
521
|
|
|
@@ -1288,6 +1304,7 @@ authRoute.openapi(signinMagicLink, async (c) => {
|
|
|
1288
1304
|
const record = await lookupEmail(getAuthDb(c), body.email);
|
|
1289
1305
|
|
|
1290
1306
|
const db = getAuthDb(c);
|
|
1307
|
+
const exposeTestSecrets = shouldExposeAuthTestSecrets(c.env);
|
|
1291
1308
|
let debugToken: string | undefined;
|
|
1292
1309
|
let debugActionUrl: string | undefined;
|
|
1293
1310
|
|
|
@@ -1327,9 +1344,12 @@ authRoute.openapi(signinMagicLink, async (c) => {
|
|
|
1327
1344
|
type: 'magic-link',
|
|
1328
1345
|
state: redirect.state,
|
|
1329
1346
|
});
|
|
1347
|
+
if (exposeTestSecrets) {
|
|
1348
|
+
debugToken = token;
|
|
1349
|
+
debugActionUrl = magicLinkUrl;
|
|
1350
|
+
}
|
|
1330
1351
|
if (!provider) {
|
|
1331
|
-
|
|
1332
|
-
if (!release) {
|
|
1352
|
+
if (!isReleaseMode(c.env)) {
|
|
1333
1353
|
console.warn('[MagicLink] Email provider not configured. Token:', token);
|
|
1334
1354
|
debugToken = token;
|
|
1335
1355
|
debugActionUrl = magicLinkUrl;
|
|
@@ -1417,6 +1437,10 @@ authRoute.openapi(signinMagicLink, async (c) => {
|
|
|
1417
1437
|
type: 'magic-link',
|
|
1418
1438
|
state: redirect.state,
|
|
1419
1439
|
});
|
|
1440
|
+
if (exposeTestSecrets) {
|
|
1441
|
+
debugToken = token;
|
|
1442
|
+
debugActionUrl = magicLinkUrl;
|
|
1443
|
+
}
|
|
1420
1444
|
if (provider) {
|
|
1421
1445
|
const locale = resolveEmailLocale(c.env, reqLocale);
|
|
1422
1446
|
const html = renderMagicLink({
|
|
@@ -1431,8 +1455,7 @@ authRoute.openapi(signinMagicLink, async (c) => {
|
|
|
1431
1455
|
resolveSubject(c.env, 'magicLink', defaultSubject, locale), html, locale,
|
|
1432
1456
|
).catch(() => {});
|
|
1433
1457
|
} else {
|
|
1434
|
-
|
|
1435
|
-
if (!release) {
|
|
1458
|
+
if (!isReleaseMode(c.env)) {
|
|
1436
1459
|
debugToken = token;
|
|
1437
1460
|
debugActionUrl = magicLinkUrl;
|
|
1438
1461
|
}
|
|
@@ -1591,6 +1614,7 @@ authRoute.openapi(signinPhone, async (c) => {
|
|
|
1591
1614
|
// Look up phone in D1
|
|
1592
1615
|
const record = await lookupPhone(getAuthDb(c), phone);
|
|
1593
1616
|
|
|
1617
|
+
const exposeTestSecrets = shouldExposeAuthTestSecrets(c.env);
|
|
1594
1618
|
let devCode: string | undefined;
|
|
1595
1619
|
|
|
1596
1620
|
const db = getAuthDb(c);
|
|
@@ -1602,6 +1626,7 @@ authRoute.openapi(signinPhone, async (c) => {
|
|
|
1602
1626
|
if (!user) return c.json({ ok: true });
|
|
1603
1627
|
|
|
1604
1628
|
const code = generateOTP();
|
|
1629
|
+
if (exposeTestSecrets) devCode = code;
|
|
1605
1630
|
|
|
1606
1631
|
// Store OTP in KV with 5 min TTL
|
|
1607
1632
|
await c.env.KV.put(
|
|
@@ -1623,8 +1648,7 @@ authRoute.openapi(signinPhone, async (c) => {
|
|
|
1623
1648
|
`Your ${appName} verification code is: ${code}. Valid for 5 minutes.`,
|
|
1624
1649
|
);
|
|
1625
1650
|
} else {
|
|
1626
|
-
|
|
1627
|
-
if (!release) {
|
|
1651
|
+
if (!isReleaseMode(c.env)) {
|
|
1628
1652
|
console.warn('[Phone] SMS provider not configured. OTP:', code);
|
|
1629
1653
|
devCode = code;
|
|
1630
1654
|
}
|
|
@@ -1654,6 +1678,7 @@ authRoute.openapi(signinPhone, async (c) => {
|
|
|
1654
1678
|
await authService.updateUser(db, userId, { phone, phoneVerified: false });
|
|
1655
1679
|
|
|
1656
1680
|
const code = generateOTP();
|
|
1681
|
+
if (exposeTestSecrets) devCode = code;
|
|
1657
1682
|
|
|
1658
1683
|
// Store OTP in KV
|
|
1659
1684
|
await c.env.KV.put(
|
|
@@ -1675,8 +1700,7 @@ authRoute.openapi(signinPhone, async (c) => {
|
|
|
1675
1700
|
`Your ${appName} verification code is: ${code}. Valid for 5 minutes.`,
|
|
1676
1701
|
);
|
|
1677
1702
|
} else {
|
|
1678
|
-
|
|
1679
|
-
if (!release) {
|
|
1703
|
+
if (!isReleaseMode(c.env)) {
|
|
1680
1704
|
console.warn('[Phone] SMS provider not configured. OTP:', code);
|
|
1681
1705
|
devCode = code;
|
|
1682
1706
|
}
|
|
@@ -1690,8 +1714,7 @@ authRoute.openapi(signinPhone, async (c) => {
|
|
|
1690
1714
|
}
|
|
1691
1715
|
|
|
1692
1716
|
// Return OTP code only in dev mode (SMS provider not configured) for testing
|
|
1693
|
-
|
|
1694
|
-
return c.json(devCode && !release ? { ok: true, code: devCode } : { ok: true });
|
|
1717
|
+
return c.json(devCode ? { ok: true, code: devCode } : { ok: true });
|
|
1695
1718
|
});
|
|
1696
1719
|
|
|
1697
1720
|
// POST /verify-phone — verify OTP → create session
|
|
@@ -1871,6 +1894,7 @@ authRoute.openapi(linkPhone, async (c) => {
|
|
|
1871
1894
|
}
|
|
1872
1895
|
|
|
1873
1896
|
const code = generateOTP();
|
|
1897
|
+
const exposeTestSecrets = shouldExposeAuthTestSecrets(c.env);
|
|
1874
1898
|
|
|
1875
1899
|
// Store link OTP in KV (separate key pattern)
|
|
1876
1900
|
await c.env.KV.put(
|
|
@@ -1891,14 +1915,13 @@ authRoute.openapi(linkPhone, async (c) => {
|
|
|
1891
1915
|
phone,
|
|
1892
1916
|
`Your ${appName} phone linking code is: ${code}. Valid for 5 minutes.`,
|
|
1893
1917
|
);
|
|
1894
|
-
return c.json({ ok: true });
|
|
1918
|
+
return c.json(exposeTestSecrets ? { ok: true, code } : { ok: true });
|
|
1895
1919
|
} else {
|
|
1896
|
-
|
|
1897
|
-
if (!release) {
|
|
1920
|
+
if (!isReleaseMode(c.env)) {
|
|
1898
1921
|
console.warn('[Phone] SMS provider not configured. Link OTP:', code);
|
|
1899
1922
|
return c.json({ ok: true, code });
|
|
1900
1923
|
}
|
|
1901
|
-
return c.json({ ok: true });
|
|
1924
|
+
return c.json(exposeTestSecrets ? { ok: true, code } : { ok: true });
|
|
1902
1925
|
}
|
|
1903
1926
|
});
|
|
1904
1927
|
|
|
@@ -2048,6 +2071,7 @@ authRoute.openapi(signinEmailOtp, async (c) => {
|
|
|
2048
2071
|
// Look up email in D1
|
|
2049
2072
|
const record = await lookupEmail(getAuthDb(c), email);
|
|
2050
2073
|
|
|
2074
|
+
const exposeTestSecrets = shouldExposeAuthTestSecrets(c.env);
|
|
2051
2075
|
let devCode: string | undefined;
|
|
2052
2076
|
|
|
2053
2077
|
const db = getAuthDb(c);
|
|
@@ -2059,6 +2083,7 @@ authRoute.openapi(signinEmailOtp, async (c) => {
|
|
|
2059
2083
|
if (!user) return c.json({ ok: true });
|
|
2060
2084
|
|
|
2061
2085
|
const code = generateOTP();
|
|
2086
|
+
if (exposeTestSecrets) devCode = code;
|
|
2062
2087
|
|
|
2063
2088
|
// Store OTP in KV with 5 min TTL
|
|
2064
2089
|
await c.env.KV.put(
|
|
@@ -2079,8 +2104,7 @@ authRoute.openapi(signinEmailOtp, async (c) => {
|
|
|
2079
2104
|
resolveSubject(c.env, 'emailOtp', defaultSubject, locale), html, locale,
|
|
2080
2105
|
);
|
|
2081
2106
|
} else {
|
|
2082
|
-
|
|
2083
|
-
if (!release) {
|
|
2107
|
+
if (!isReleaseMode(c.env)) {
|
|
2084
2108
|
console.warn('[EmailOTP] Email provider not configured. OTP:', code);
|
|
2085
2109
|
devCode = code;
|
|
2086
2110
|
}
|
|
@@ -2117,6 +2141,7 @@ authRoute.openapi(signinEmailOtp, async (c) => {
|
|
|
2117
2141
|
});
|
|
2118
2142
|
|
|
2119
2143
|
const code = generateOTP();
|
|
2144
|
+
if (exposeTestSecrets) devCode = code;
|
|
2120
2145
|
|
|
2121
2146
|
// Store OTP in KV
|
|
2122
2147
|
await c.env.KV.put(
|
|
@@ -2137,8 +2162,7 @@ authRoute.openapi(signinEmailOtp, async (c) => {
|
|
|
2137
2162
|
resolveSubject(c.env, 'emailOtp', defaultSubject, locale), html, locale,
|
|
2138
2163
|
);
|
|
2139
2164
|
} else {
|
|
2140
|
-
|
|
2141
|
-
if (!release) {
|
|
2165
|
+
if (!isReleaseMode(c.env)) {
|
|
2142
2166
|
console.warn('[EmailOTP] Email provider not configured. OTP:', code);
|
|
2143
2167
|
devCode = code;
|
|
2144
2168
|
}
|
|
@@ -2152,8 +2176,7 @@ authRoute.openapi(signinEmailOtp, async (c) => {
|
|
|
2152
2176
|
}
|
|
2153
2177
|
|
|
2154
2178
|
// Return OTP code only in dev mode (email provider not configured) for testing
|
|
2155
|
-
|
|
2156
|
-
return c.json(devCode && !release ? { ok: true, code: devCode } : { ok: true });
|
|
2179
|
+
return c.json(devCode ? { ok: true, code: devCode } : { ok: true });
|
|
2157
2180
|
});
|
|
2158
2181
|
|
|
2159
2182
|
// POST /verify-email-otp — verify OTP → create session
|
|
@@ -2754,11 +2777,27 @@ authRoute.openapi(signout, async (c) => {
|
|
|
2754
2777
|
refreshToken: body.refreshToken,
|
|
2755
2778
|
});
|
|
2756
2779
|
|
|
2757
|
-
const payload = decodeTokenUnsafe(body.refreshToken);
|
|
2758
|
-
if (!payload?.sub) throw new EdgeBaseError(401, 'Invalid refresh token.', undefined, 'invalid-refresh-token');
|
|
2759
|
-
|
|
2760
2780
|
const db = getAuthDb(c);
|
|
2761
|
-
|
|
2781
|
+
let tokenPayload;
|
|
2782
|
+
try {
|
|
2783
|
+
tokenPayload = await verifyRefreshTokenWithFallback(
|
|
2784
|
+
body.refreshToken,
|
|
2785
|
+
getUserSecret(c.env),
|
|
2786
|
+
c.env.JWT_USER_SECRET_OLD,
|
|
2787
|
+
c.env.JWT_USER_SECRET_OLD_AT,
|
|
2788
|
+
);
|
|
2789
|
+
} catch (err) {
|
|
2790
|
+
if (err instanceof TokenExpiredError) {
|
|
2791
|
+
throw new EdgeBaseError(401, 'Refresh token expired.', undefined, 'refresh-token-expired');
|
|
2792
|
+
}
|
|
2793
|
+
throw new EdgeBaseError(401, 'Invalid refresh token.', undefined, 'invalid-refresh-token');
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
const userId = tokenPayload.sub;
|
|
2797
|
+
const sessionResult = await authService.getSessionByRefreshToken(db, body.refreshToken, userId);
|
|
2798
|
+
if (!sessionResult) {
|
|
2799
|
+
throw new EdgeBaseError(401, 'Invalid refresh token.', undefined, 'invalid-refresh-token');
|
|
2800
|
+
}
|
|
2762
2801
|
|
|
2763
2802
|
// beforeSignOut hook — blocking
|
|
2764
2803
|
await executeAuthHook(c.env, c.executionCtx, 'beforeSignOut', { userId }, { blocking: true, workerUrl: getWorkerUrl(c.req.url, c.env) });
|
|
@@ -2991,8 +3030,8 @@ authRoute.openapi(changeEmail, async (c) => {
|
|
|
2991
3030
|
}
|
|
2992
3031
|
}
|
|
2993
3032
|
|
|
2994
|
-
const
|
|
2995
|
-
if (!
|
|
3033
|
+
const exposeTestSecrets = shouldExposeAuthTestSecrets(c.env);
|
|
3034
|
+
if (exposeTestSecrets || !isReleaseMode(c.env)) {
|
|
2996
3035
|
const emailCfg = getEmailConfig(c.env);
|
|
2997
3036
|
const fallbackVerifyUrl = emailCfg?.emailChangeUrl
|
|
2998
3037
|
? emailCfg.emailChangeUrl.replace('{token}', token)
|
|
@@ -3970,8 +4009,7 @@ authRoute.openapi(requestEmailVerification, async (c) => {
|
|
|
3970
4009
|
|
|
3971
4010
|
const provider = createEmailProvider(getEmailConfig(c.env), c.env);
|
|
3972
4011
|
if (!provider) {
|
|
3973
|
-
|
|
3974
|
-
if (!release) {
|
|
4012
|
+
if (shouldExposeAuthTestSecrets(c.env) || !isReleaseMode(c.env)) {
|
|
3975
4013
|
console.warn('[VerifyEmail] Email provider not configured. Verification email not sent. Token:', token);
|
|
3976
4014
|
return c.json({ ok: true, message: 'Email provider not configured.', token, actionUrl: verifyUrl });
|
|
3977
4015
|
}
|
|
@@ -3992,7 +4030,9 @@ authRoute.openapi(requestEmailVerification, async (c) => {
|
|
|
3992
4030
|
resolveSubject(c.env, 'verification', defaultSubject, locale), html, locale,
|
|
3993
4031
|
);
|
|
3994
4032
|
|
|
3995
|
-
return c.json(
|
|
4033
|
+
return c.json(shouldExposeAuthTestSecrets(c.env)
|
|
4034
|
+
? { ok: result.success, messageId: result.messageId, token, actionUrl: verifyUrl }
|
|
4035
|
+
: { ok: result.success, messageId: result.messageId });
|
|
3996
4036
|
});
|
|
3997
4037
|
|
|
3998
4038
|
// POST /verify-email — KV token→shardId lookup → direct Shard call
|
|
@@ -4123,8 +4163,7 @@ authRoute.openapi(requestPasswordReset, async (c) => {
|
|
|
4123
4163
|
|
|
4124
4164
|
const provider = createEmailProvider(getEmailConfig(c.env), c.env);
|
|
4125
4165
|
if (!provider) {
|
|
4126
|
-
|
|
4127
|
-
if (!release) {
|
|
4166
|
+
if (shouldExposeAuthTestSecrets(c.env) || !isReleaseMode(c.env)) {
|
|
4128
4167
|
console.warn('[Auth] Email provider not configured. Reset email not sent. Token:', token);
|
|
4129
4168
|
return c.json({ ok: true, message: 'Email provider not configured.', token, actionUrl: resetUrl });
|
|
4130
4169
|
}
|
|
@@ -4145,7 +4184,9 @@ authRoute.openapi(requestPasswordReset, async (c) => {
|
|
|
4145
4184
|
resolveSubject(c.env, 'passwordReset', defaultSubject, locale), html, locale,
|
|
4146
4185
|
);
|
|
4147
4186
|
|
|
4148
|
-
return c.json(
|
|
4187
|
+
return c.json(shouldExposeAuthTestSecrets(c.env)
|
|
4188
|
+
? { ok: result.success, messageId: result.messageId, token, actionUrl: resetUrl }
|
|
4189
|
+
: { ok: result.success, messageId: result.messageId });
|
|
4149
4190
|
});
|
|
4150
4191
|
|
|
4151
4192
|
// POST /reset-password — KV token→shardId lookup → direct Shard call
|
package/src/routes/room.ts
CHANGED
|
@@ -70,6 +70,12 @@ const roomRealtimeCreateSessionBodySchema = z.object({
|
|
|
70
70
|
thirdparty: z.boolean().optional().openapi({ description: 'Forward Cloudflare Realtime thirdparty mode' }),
|
|
71
71
|
sessionDescription: roomRealtimeSessionDescriptionSchema.optional(),
|
|
72
72
|
});
|
|
73
|
+
const roomCloudflareRealtimeKitCreateSessionBodySchema = z.object({
|
|
74
|
+
connectionId: z.string().optional().openapi({ description: 'Specific room connection ID to bind the Cloudflare RealtimeKit participant to' }),
|
|
75
|
+
customParticipantId: z.string().optional().openapi({ description: 'Optional custom participant identifier for the provisioned RealtimeKit participant' }),
|
|
76
|
+
name: z.string().optional().openapi({ description: 'Optional display name for the provisioned RealtimeKit participant' }),
|
|
77
|
+
picture: z.string().optional().openapi({ description: 'Optional avatar URL for the provisioned RealtimeKit participant' }),
|
|
78
|
+
});
|
|
73
79
|
const roomRealtimeCreateSessionResponseSchema = z.object({
|
|
74
80
|
sessionId: z.string().openapi({ description: 'Realtime provider session ID' }),
|
|
75
81
|
sessionDescription: roomRealtimeSessionDescriptionSchema.optional(),
|
|
@@ -78,6 +84,15 @@ const roomRealtimeCreateSessionResponseSchema = z.object({
|
|
|
78
84
|
connectionId: z.string().optional().openapi({ description: 'Room connection ID associated with the session' }),
|
|
79
85
|
reused: z.boolean().optional().openapi({ description: 'Whether an existing provider session was reused' }),
|
|
80
86
|
});
|
|
87
|
+
const roomCloudflareRealtimeKitCreateSessionResponseSchema = z.object({
|
|
88
|
+
sessionId: z.string().openapi({ description: 'Cloudflare RealtimeKit participant ID' }),
|
|
89
|
+
meetingId: z.string().openapi({ description: 'Cloudflare RealtimeKit meeting ID backing the room session' }),
|
|
90
|
+
participantId: z.string().openapi({ description: 'Cloudflare RealtimeKit participant ID' }),
|
|
91
|
+
authToken: z.string().openapi({ description: 'RealtimeKit auth token for the provisioned participant' }),
|
|
92
|
+
presetName: z.string().optional().openapi({ description: 'RealtimeKit preset used for the provisioned participant' }),
|
|
93
|
+
connectionId: z.string().optional().openapi({ description: 'Room connection ID associated with the session' }),
|
|
94
|
+
reused: z.boolean().optional().openapi({ description: 'Whether an existing provider participant was reused' }),
|
|
95
|
+
});
|
|
81
96
|
const roomRealtimeSessionStateSchema = z.object({
|
|
82
97
|
sessionId: z.string().openapi({ description: 'Realtime provider session ID' }),
|
|
83
98
|
connectionId: z.string().optional().openapi({ description: 'Room connection ID associated with the session' }),
|
|
@@ -525,6 +540,27 @@ const createRoomRealtimeSession = createRoute({
|
|
|
525
540
|
},
|
|
526
541
|
});
|
|
527
542
|
|
|
543
|
+
const createRoomCloudflareRealtimeKitSession = createRoute({
|
|
544
|
+
operationId: 'createRoomCloudflareRealtimeKitSession',
|
|
545
|
+
method: 'post',
|
|
546
|
+
path: '/media/cloudflare_realtimekit/session',
|
|
547
|
+
tags: ['client'],
|
|
548
|
+
summary: 'Create a room Cloudflare RealtimeKit session',
|
|
549
|
+
description: 'Creates a Cloudflare RealtimeKit session for the authenticated room member.',
|
|
550
|
+
request: {
|
|
551
|
+
query: roomQuerySchema,
|
|
552
|
+
body: { content: { 'application/json': { schema: roomCloudflareRealtimeKitCreateSessionBodySchema } }, required: false },
|
|
553
|
+
},
|
|
554
|
+
responses: {
|
|
555
|
+
200: { description: 'Cloudflare RealtimeKit session created', content: { 'application/json': { schema: roomCloudflareRealtimeKitCreateSessionResponseSchema } } },
|
|
556
|
+
400: { description: 'Bad request', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
557
|
+
401: { description: 'Authentication required', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
558
|
+
403: { description: 'Forbidden', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
559
|
+
404: { description: 'Room runtime not found', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
560
|
+
409: { description: 'Conflicting existing published media', content: { 'application/json': { schema: errorResponseSchema } } },
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
|
|
528
564
|
const createRoomRealtimeIceServers = createRoute({
|
|
529
565
|
operationId: 'createRoomRealtimeIceServers',
|
|
530
566
|
method: 'post',
|
|
@@ -637,3 +673,9 @@ roomRoute.openapi(closeRoomRealtimeTracks, async (c) =>
|
|
|
637
673
|
requireAuth: true,
|
|
638
674
|
validatedJson: c.req.valid('json'),
|
|
639
675
|
}));
|
|
676
|
+
|
|
677
|
+
roomRoute.openapi(createRoomCloudflareRealtimeKitSession, async (c) =>
|
|
678
|
+
proxyRoomDoRequest(c, '/media/cloudflare_realtimekit/session', 'POST', {
|
|
679
|
+
requireAuth: true,
|
|
680
|
+
validatedJson: c.req.valid('json'),
|
|
681
|
+
}));
|
package/src/types.ts
CHANGED
|
@@ -68,6 +68,8 @@ export interface Env {
|
|
|
68
68
|
CAPTCHA_SITE_KEY?: string;
|
|
69
69
|
/** Cloudflare Realtime app ID for SFU session control. */
|
|
70
70
|
CF_REALTIME_APP_ID?: string;
|
|
71
|
+
/** Cloudflare RealtimeKit preset name used when creating participant tokens. */
|
|
72
|
+
CF_REALTIME_PRESET_NAME?: string;
|
|
71
73
|
/** Cloudflare Realtime app secret for SFU session control. */
|
|
72
74
|
CF_REALTIME_APP_SECRET?: string;
|
|
73
75
|
/** Optional override for the Cloudflare Realtime API base URL. */
|
|
@@ -92,6 +94,10 @@ export interface Env {
|
|
|
92
94
|
EDGEBASE_EMAIL_API_URL?: string;
|
|
93
95
|
/** Optional override for SMS delivery in deployed/local mock environments. */
|
|
94
96
|
EDGEBASE_SMS_API_URL?: string;
|
|
97
|
+
/** Test-only flag set by wrangler.test.toml for SDK E2E flows. */
|
|
98
|
+
EDGEBASE_TEST?: string;
|
|
99
|
+
/** Test-only flag that prefers the bundled test config at startup. */
|
|
100
|
+
EDGEBASE_USE_TEST_CONFIG?: string;
|
|
95
101
|
|
|
96
102
|
// ─── Dev Mode ───
|
|
97
103
|
/** Schema Editor sidecar port — set by CLI dev command via --var */
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.sql-editor-wrap.svelte-392xt8{border:1px solid var(--color-border);border-radius:var(--radius-md);overflow:hidden}.sql-editor-wrap.svelte-392xt8 .cm-editor{min-height:120px}.sql-editor-wrap.svelte-392xt8 .cm-editor.cm-focused{border-color:var(--color-primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 20%,transparent)}.table-sql.svelte-1syrthj{display:flex;flex-direction:column;gap:var(--space-4)}.table-sql__toolbar.svelte-1syrthj{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap}.table-sql__target.svelte-1syrthj{display:inline-flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-bg-secondary);font-size:12px}.table-sql__target-label.svelte-1syrthj{color:var(--color-text-tertiary);text-transform:uppercase;letter-spacing:.04em}.table-sql__target.svelte-1syrthj code:where(.svelte-1syrthj){font-family:var(--font-mono);color:var(--color-text)}.table-sql__shortcut.svelte-1syrthj{font-size:12px;color:var(--color-text-tertiary);margin-left:auto}.table-sql__result-tabs.svelte-1syrthj{display:flex;flex-wrap:wrap;gap:var(--space-2)}.table-sql__result-tab.svelte-1syrthj{display:inline-flex;align-items:center;gap:var(--space-2);padding:var(--space-2) var(--space-3);border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-bg-secondary);color:var(--color-text-secondary);cursor:pointer}.table-sql__result-tab--active.svelte-1syrthj{border-color:var(--color-primary);color:var(--color-primary)}.table-sql__result-open.svelte-1syrthj{border:none;background:transparent;color:inherit;cursor:pointer;padding:0;font:inherit}.table-sql__result-close.svelte-1syrthj{border:none;background:transparent;color:inherit;cursor:pointer;padding:0;line-height:1}.table-sql__result-panel.svelte-1syrthj{border:1px solid var(--color-border);border-radius:var(--radius-md);padding:var(--space-4);background:var(--color-bg)}.table-sql__result-meta.svelte-1syrthj{margin-bottom:var(--space-3);font-size:12px;color:var(--color-text-secondary)}.table-sql__result-error.svelte-1syrthj{color:var(--color-danger, #ef4444)}.table-sql__error-block.svelte-1syrthj,.table-sql__empty.svelte-1syrthj{margin:0;padding:var(--space-4);border-radius:var(--radius-md);background:var(--color-bg-secondary);color:var(--color-text-secondary);font-size:13px}
|