@aotter/mantle-cloudflare 0.0.11-alpha.11
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/LICENSE +202 -0
- package/README.md +10 -0
- package/dist/assets/aotterFavicon.d.ts +2 -0
- package/dist/assets/aotterFavicon.d.ts.map +1 -0
- package/dist/assets/aotterFavicon.js +5 -0
- package/dist/assets/aotterFavicon.js.map +1 -0
- package/dist/auth/ConsoleEmailSender.d.ts +15 -0
- package/dist/auth/ConsoleEmailSender.d.ts.map +1 -0
- package/dist/auth/ConsoleEmailSender.js +19 -0
- package/dist/auth/ConsoleEmailSender.js.map +1 -0
- package/dist/auth/appleClientSecret.d.ts +70 -0
- package/dist/auth/appleClientSecret.d.ts.map +1 -0
- package/dist/auth/appleClientSecret.js +111 -0
- package/dist/auth/appleClientSecret.js.map +1 -0
- package/dist/auth/createAuth.d.ts +176 -0
- package/dist/auth/createAuth.d.ts.map +1 -0
- package/dist/auth/createAuth.js +360 -0
- package/dist/auth/createAuth.js.map +1 -0
- package/dist/bindings/AssetsAssetServer.d.ts +14 -0
- package/dist/bindings/AssetsAssetServer.d.ts.map +1 -0
- package/dist/bindings/AssetsAssetServer.js +20 -0
- package/dist/bindings/AssetsAssetServer.js.map +1 -0
- package/dist/bindings/D1DatabaseDriver.d.ts +21 -0
- package/dist/bindings/D1DatabaseDriver.d.ts.map +1 -0
- package/dist/bindings/D1DatabaseDriver.js +126 -0
- package/dist/bindings/D1DatabaseDriver.js.map +1 -0
- package/dist/bindings/KvCacheBinding.d.ts +14 -0
- package/dist/bindings/KvCacheBinding.d.ts.map +1 -0
- package/dist/bindings/KvCacheBinding.js +27 -0
- package/dist/bindings/KvCacheBinding.js.map +1 -0
- package/dist/bindings/R2MediaStorage.d.ts +80 -0
- package/dist/bindings/R2MediaStorage.d.ts.map +1 -0
- package/dist/bindings/R2MediaStorage.js +189 -0
- package/dist/bindings/R2MediaStorage.js.map +1 -0
- package/dist/bindings/WorkersQueueHookDispatcher.d.ts +38 -0
- package/dist/bindings/WorkersQueueHookDispatcher.d.ts.map +1 -0
- package/dist/bindings/WorkersQueueHookDispatcher.js +68 -0
- package/dist/bindings/WorkersQueueHookDispatcher.js.map +1 -0
- package/dist/bindings/index.d.ts +6 -0
- package/dist/bindings/index.d.ts.map +1 -0
- package/dist/bindings/index.js +6 -0
- package/dist/bindings/index.js.map +1 -0
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +2 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/turnstile.d.ts +43 -0
- package/dist/handlers/turnstile.d.ts.map +1 -0
- package/dist/handlers/turnstile.js +71 -0
- package/dist/handlers/turnstile.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/mount/bootRuntimeOnce.d.ts +17 -0
- package/dist/mount/bootRuntimeOnce.d.ts.map +1 -0
- package/dist/mount/bootRuntimeOnce.js +35 -0
- package/dist/mount/bootRuntimeOnce.js.map +1 -0
- package/dist/mount/cmsConfig.d.ts +33 -0
- package/dist/mount/cmsConfig.d.ts.map +1 -0
- package/dist/mount/cmsConfig.js +2 -0
- package/dist/mount/cmsConfig.js.map +1 -0
- package/dist/mount/index.d.ts +6 -0
- package/dist/mount/index.d.ts.map +1 -0
- package/dist/mount/index.js +5 -0
- package/dist/mount/index.js.map +1 -0
- package/dist/mount/mountMcp.d.ts +32 -0
- package/dist/mount/mountMcp.d.ts.map +1 -0
- package/dist/mount/mountMcp.js +107 -0
- package/dist/mount/mountMcp.js.map +1 -0
- package/dist/mount/mountPublicRoutes.d.ts +82 -0
- package/dist/mount/mountPublicRoutes.d.ts.map +1 -0
- package/dist/mount/mountPublicRoutes.js +243 -0
- package/dist/mount/mountPublicRoutes.js.map +1 -0
- package/dist/mount/mountServerEndpoints.d.ts +10 -0
- package/dist/mount/mountServerEndpoints.d.ts.map +1 -0
- package/dist/mount/mountServerEndpoints.js +503 -0
- package/dist/mount/mountServerEndpoints.js.map +1 -0
- package/dist/oauth/consentHtml.d.ts +15 -0
- package/dist/oauth/consentHtml.d.ts.map +1 -0
- package/dist/oauth/consentHtml.js +89 -0
- package/dist/oauth/consentHtml.js.map +1 -0
- package/dist/oauth/index.d.ts +4 -0
- package/dist/oauth/index.d.ts.map +1 -0
- package/dist/oauth/index.js +4 -0
- package/dist/oauth/index.js.map +1 -0
- package/dist/oauth/mountOAuth.d.ts +29 -0
- package/dist/oauth/mountOAuth.d.ts.map +1 -0
- package/dist/oauth/mountOAuth.js +105 -0
- package/dist/oauth/mountOAuth.js.map +1 -0
- package/dist/oauth/oauthConstants.d.ts +15 -0
- package/dist/oauth/oauthConstants.d.ts.map +1 -0
- package/dist/oauth/oauthConstants.js +15 -0
- package/dist/oauth/oauthConstants.js.map +1 -0
- package/dist/oauth/oauthSingleton.d.ts +34 -0
- package/dist/oauth/oauthSingleton.d.ts.map +1 -0
- package/dist/oauth/oauthSingleton.js +22 -0
- package/dist/oauth/oauthSingleton.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import { DiagnosticError, HTTP_STATUS_BY_CODE, MCP_HINT_KEYWORD, VIEW_PARAMS_RESERVED, isMediaMcpHint, redactForWire, runtimeDiagnostic, } from "@aotter/mantle-spec";
|
|
2
|
+
import { ViewParamCoercionError, coerceViewParams, evaluateAuthAll, matchPath, } from "@aotter/mantle-runtime";
|
|
3
|
+
import { indexHtml } from "@aotter/mantle-admin-ui";
|
|
4
|
+
import { ADMIN_ROLE_SET } from "../auth/createAuth.js";
|
|
5
|
+
import { AOTTER_FAVICON_SVG } from "../assets/aotterFavicon.js";
|
|
6
|
+
const [PAGE_PARAM, SHOW_PARAM] = VIEW_PARAMS_RESERVED;
|
|
7
|
+
/** Mount HTTP Triggers + Views + the Better Auth admin surface.
|
|
8
|
+
* HTTP Trigger bearer-token authentication is delegated to the OAuth
|
|
9
|
+
* provider lib (via `createMcpApiHandler`) — if a Trigger needs
|
|
10
|
+
* identity, route it under an MCP `apiHandler` instead of a Hono
|
|
11
|
+
* catch-all. */
|
|
12
|
+
export function mountServerEndpoints(app, ref) {
|
|
13
|
+
for (const t of ref.manifests) {
|
|
14
|
+
if (t.kind !== "Trigger")
|
|
15
|
+
continue;
|
|
16
|
+
const source = t.spec.source;
|
|
17
|
+
if (source.kind !== "http")
|
|
18
|
+
continue;
|
|
19
|
+
const { method, path } = source;
|
|
20
|
+
const honoPath = openApiToHono(path);
|
|
21
|
+
const triggerName = t.metadata.name;
|
|
22
|
+
app.on(method, honoPath, async (c) => {
|
|
23
|
+
const runtime = await ref.get();
|
|
24
|
+
const waitUntil = readWaitUntil(c);
|
|
25
|
+
return handleHttpTrigger(c.req.raw, runtime, ref.auth, triggerName, path, waitUntil);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
for (const v of ref.manifests) {
|
|
29
|
+
if (v.kind !== "View")
|
|
30
|
+
continue;
|
|
31
|
+
const viewName = v.metadata.name;
|
|
32
|
+
app.get(`/api/views/${viewName}`, async (c) => {
|
|
33
|
+
const runtime = await ref.get();
|
|
34
|
+
const waitUntil = readWaitUntil(c);
|
|
35
|
+
return handleViewRequest(c.req.raw, runtime, viewName, ref.auth, waitUntil);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
mountAdminBetterAuth(app, ref, ref.auth);
|
|
39
|
+
}
|
|
40
|
+
function mountAdminBetterAuth(app, ref, auth) {
|
|
41
|
+
const spa = () => new Response(indexHtml, {
|
|
42
|
+
headers: { "content-type": "text/html; charset=utf-8" },
|
|
43
|
+
});
|
|
44
|
+
app.get("/favicon.svg", () => new Response(AOTTER_FAVICON_SVG, {
|
|
45
|
+
headers: {
|
|
46
|
+
"content-type": "image/svg+xml; charset=utf-8",
|
|
47
|
+
"cache-control": "public, max-age=86400",
|
|
48
|
+
},
|
|
49
|
+
}));
|
|
50
|
+
// Public read-only manifest of registered sign-in methods. The admin
|
|
51
|
+
// SPA hits this on sign-in-page mount so it can render per-method
|
|
52
|
+
// sections without baking the method list into its build. No secrets
|
|
53
|
+
// or sender refs — only the `kind` strings.
|
|
54
|
+
//
|
|
55
|
+
// `Cache-Control: no-store` because the list reflects deploy-time
|
|
56
|
+
// config; if the operator rolls a new method, a CDN-cached response
|
|
57
|
+
// would silently misroute the sign-in UI until the cache expires.
|
|
58
|
+
app.get("/api/auth/methods", () => Response.json({ methods: auth.methods }, {
|
|
59
|
+
headers: { "cache-control": "no-store" },
|
|
60
|
+
}));
|
|
61
|
+
// Better Auth handles the rest of `/api/auth/*` (sign-in, callback,
|
|
62
|
+
// session, magic-link, OTP, OAuth provider routes). Owned by the SDK
|
|
63
|
+
// so consumers don't have to wire — and can't accidentally register a
|
|
64
|
+
// catch-all BEFORE the specific routes above and silently swallow
|
|
65
|
+
// them. Hono matches in registration order; this catch-all sits last.
|
|
66
|
+
app.all("/api/auth/*", (c) => auth.handler(c.req.raw));
|
|
67
|
+
// Well-known OAuth endpoints (RFC 8414 + RFC 9728) used to be
|
|
68
|
+
// forwarded to Better Auth's mcp() plugin here. With the carve-out
|
|
69
|
+
// to @cloudflare/workers-oauth-provider (PR #193), the top-level
|
|
70
|
+
// `OAuthProvider` serves AS metadata and per-resource PRM
|
|
71
|
+
// automatically — adopters wire it via `createOAuthProvider` +
|
|
72
|
+
// `createMcpApiHandler` from the package index. No SDK-level
|
|
73
|
+
// forwarder needed in `mountServerEndpoints`.
|
|
74
|
+
for (const path of [
|
|
75
|
+
"/admin",
|
|
76
|
+
"/admin/",
|
|
77
|
+
"/admin/sign-in",
|
|
78
|
+
"/admin/c/:collection",
|
|
79
|
+
"/admin/approvals",
|
|
80
|
+
"/admin/preferences",
|
|
81
|
+
"/admin/settings",
|
|
82
|
+
]) {
|
|
83
|
+
app.get(path, spa);
|
|
84
|
+
}
|
|
85
|
+
// Pre-derive the collections projection — `ref.manifests` is
|
|
86
|
+
// immutable post-boot, so the filter / Set / mediaFields work doesn't
|
|
87
|
+
// need to repeat per request.
|
|
88
|
+
const schemas = ref.manifests.filter((m) => m.kind === "Schema");
|
|
89
|
+
const translatedParents = new Set();
|
|
90
|
+
for (const s of schemas) {
|
|
91
|
+
if (s.spec.translates)
|
|
92
|
+
translatedParents.add(s.spec.translates.parent);
|
|
93
|
+
}
|
|
94
|
+
const collections = schemas
|
|
95
|
+
.filter((s) => !s.spec.translates)
|
|
96
|
+
.map((s) => ({
|
|
97
|
+
name: s.metadata.name,
|
|
98
|
+
title: s.spec.title,
|
|
99
|
+
description: s.spec.description ?? null,
|
|
100
|
+
lifecycle: s.spec.lifecycle ?? "simple",
|
|
101
|
+
hasTranslations: translatedParents.has(s.metadata.name),
|
|
102
|
+
mediaFields: mediaFieldsForCollection(s, schemas),
|
|
103
|
+
}));
|
|
104
|
+
const guarded = (method, path, body) => {
|
|
105
|
+
app.on(method.toUpperCase(), path, async (c) => {
|
|
106
|
+
const gate = await readAdminGate(c, auth);
|
|
107
|
+
if (gate.kind === "unauth")
|
|
108
|
+
return adminUnauthenticated(c, path);
|
|
109
|
+
if (gate.kind === "forbidden")
|
|
110
|
+
return adminNotStaff(c, path, gate.login);
|
|
111
|
+
return body(c, gate);
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
guarded("get", "/admin/api/me", (_c, gate) => jsonResponse(200, { login: gate.login, role: gate.role, userId: gate.userId }));
|
|
115
|
+
guarded("get", "/admin/api/collections", () => jsonResponse(200, { collections }));
|
|
116
|
+
guarded("get", "/admin/api/site", async (c) => {
|
|
117
|
+
const runtime = await ref.get();
|
|
118
|
+
const site = await runtime.siteConfig.load();
|
|
119
|
+
const url = new URL(c.req.url);
|
|
120
|
+
return jsonResponse(200, {
|
|
121
|
+
...site,
|
|
122
|
+
publicUrl: site.origin || url.origin,
|
|
123
|
+
mcpUrl: `${url.origin}/mcp/staff`,
|
|
124
|
+
staffMcpUrl: `${url.origin}/mcp/staff`,
|
|
125
|
+
userMcpUrl: `${url.origin}/mcp`,
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
guarded("get", "/admin/api/entries", async (c) => {
|
|
129
|
+
const collection = c.req.query("collection");
|
|
130
|
+
if (!collection) {
|
|
131
|
+
return jsonResponse(400, {
|
|
132
|
+
ok: false,
|
|
133
|
+
diagnostic: runtimeDiagnostic({
|
|
134
|
+
code: "INPUT_VALIDATION_FAILED",
|
|
135
|
+
severity: "error",
|
|
136
|
+
path: "GET /admin/api/entries",
|
|
137
|
+
expected: "?collection=<name> query parameter",
|
|
138
|
+
message: "Missing `collection` query parameter.",
|
|
139
|
+
}),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
const runtime = await ref.get();
|
|
143
|
+
const rawLimit = c.req.query("limit");
|
|
144
|
+
const parsedLimit = rawLimit ? Number.parseInt(rawLimit, 10) : NaN;
|
|
145
|
+
// Admin pagination needs the cursored shape — `executePage` returns
|
|
146
|
+
// `{ rows, nextCursor? }`. `execute()` is the flat-array variant
|
|
147
|
+
// for app code.
|
|
148
|
+
const result = await runtime.listEntries.executePage({
|
|
149
|
+
collection,
|
|
150
|
+
status: c.req.query("status"),
|
|
151
|
+
limit: Number.isFinite(parsedLimit) ? parsedLimit : undefined,
|
|
152
|
+
cursor: c.req.query("cursor") ?? undefined,
|
|
153
|
+
});
|
|
154
|
+
const items = result.rows.map((row) => ({
|
|
155
|
+
id: row.id,
|
|
156
|
+
collection: row.collection,
|
|
157
|
+
locale: row.locale ?? null,
|
|
158
|
+
status: row.status,
|
|
159
|
+
version: row.version,
|
|
160
|
+
title: row.data.title,
|
|
161
|
+
updated_at: row.updatedAt,
|
|
162
|
+
}));
|
|
163
|
+
return jsonResponse(200, { items, next_cursor: result.nextCursor ?? null });
|
|
164
|
+
});
|
|
165
|
+
// Three-step direct-upload flow: POST /uploads (capability) → caller
|
|
166
|
+
// PUTs directly to R2 S3 (Worker bypassed) → POST /uploads/:id/commit.
|
|
167
|
+
const MEDIA_UPLOADS_PATH = "/admin/api/media/uploads";
|
|
168
|
+
const MEDIA_COMMIT_PATH = "/admin/api/media/uploads/:uploadId/commit";
|
|
169
|
+
guarded("post", MEDIA_UPLOADS_PATH, async (c) => {
|
|
170
|
+
const runtime = await ref.get();
|
|
171
|
+
const media = runtime.media;
|
|
172
|
+
if (!media)
|
|
173
|
+
return mediaNotConfiguredResponse(`POST ${MEDIA_UPLOADS_PATH}`);
|
|
174
|
+
const body = (await c.req.raw.json().catch(() => ({})));
|
|
175
|
+
if (typeof body.filename !== "string" ||
|
|
176
|
+
typeof body.mimeType !== "string" ||
|
|
177
|
+
typeof body.byteSize !== "number") {
|
|
178
|
+
return jsonResponse(400, {
|
|
179
|
+
ok: false,
|
|
180
|
+
diagnostic: runtimeDiagnostic({
|
|
181
|
+
code: "INPUT_VALIDATION_FAILED",
|
|
182
|
+
severity: "error",
|
|
183
|
+
path: `POST ${MEDIA_UPLOADS_PATH}`,
|
|
184
|
+
expected: "{ filename: string, mimeType: string, byteSize: number }",
|
|
185
|
+
}),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
const { filename, mimeType, byteSize } = body;
|
|
189
|
+
return runUseCase(`POST ${MEDIA_UPLOADS_PATH}`, () => media.createUpload.execute({
|
|
190
|
+
filename,
|
|
191
|
+
mimeType,
|
|
192
|
+
byteSize,
|
|
193
|
+
alt: typeof body.alt === "string" ? body.alt : undefined,
|
|
194
|
+
caption: typeof body.caption === "string" ? body.caption : undefined,
|
|
195
|
+
purpose: typeof body.purpose === "string" ? body.purpose : undefined,
|
|
196
|
+
}));
|
|
197
|
+
});
|
|
198
|
+
guarded("post", MEDIA_COMMIT_PATH, async (c) => {
|
|
199
|
+
const runtime = await ref.get();
|
|
200
|
+
const media = runtime.media;
|
|
201
|
+
if (!media)
|
|
202
|
+
return mediaNotConfiguredResponse(`POST ${MEDIA_COMMIT_PATH}`);
|
|
203
|
+
// Hono only invokes this handler when the route matched, so the
|
|
204
|
+
// path param is always present at runtime.
|
|
205
|
+
const uploadId = c.req.param("uploadId");
|
|
206
|
+
const body = (await c.req.raw.json().catch(() => ({})));
|
|
207
|
+
return runUseCase(`POST ${MEDIA_COMMIT_PATH}`, () => media.commitUpload.execute({
|
|
208
|
+
uploadId,
|
|
209
|
+
alt: typeof body.alt === "string" ? body.alt : undefined,
|
|
210
|
+
caption: typeof body.caption === "string" ? body.caption : undefined,
|
|
211
|
+
checksum: typeof body.checksum === "string" ? body.checksum : undefined,
|
|
212
|
+
}));
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
async function readAdminGate(c, auth) {
|
|
216
|
+
const session = await auth.getSession(c.req.raw);
|
|
217
|
+
if (!session)
|
|
218
|
+
return { kind: "unauth" };
|
|
219
|
+
const role = session.user.role ?? null;
|
|
220
|
+
const login = session.user.githubLogin ?? null;
|
|
221
|
+
if (!role || !ADMIN_ROLE_SET.has(role)) {
|
|
222
|
+
return { kind: "forbidden", login };
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
kind: "ok",
|
|
226
|
+
userId: session.user.id,
|
|
227
|
+
login,
|
|
228
|
+
role: role,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
async function handleHttpTrigger(req, runtime, auth, triggerName, triggerPath, waitUntil) {
|
|
232
|
+
const trigger = runtime.triggersByName.get(triggerName);
|
|
233
|
+
if (!trigger) {
|
|
234
|
+
return jsonError({ status: 500, code: "INTERNAL_ERROR", message: `Trigger '${triggerName}' missing post-boot.` });
|
|
235
|
+
}
|
|
236
|
+
const procName = trigger.spec.target.procedure;
|
|
237
|
+
const procedure = runtime.proceduresByName.get(procName);
|
|
238
|
+
if (!procedure) {
|
|
239
|
+
return jsonError({ status: 500, code: "INTERNAL_ERROR", message: `Procedure '${procName}' missing post-boot.` });
|
|
240
|
+
}
|
|
241
|
+
const url = new URL(req.url);
|
|
242
|
+
const params = matchPath(triggerPath, url.pathname) ?? {};
|
|
243
|
+
const body = await readBody(req);
|
|
244
|
+
// Spread order matters: URL path params are authoritative for the
|
|
245
|
+
// resource identifier (a `DELETE /entries/{id}` body MUST NOT spoof
|
|
246
|
+
// `id`). Body fields fill in non-path inputs only.
|
|
247
|
+
const input = { ...body, ...params };
|
|
248
|
+
const ctx = await buildHandlerContext(req, auth, waitUntil);
|
|
249
|
+
const result = await runtime.invokeProcedure.execute({
|
|
250
|
+
procedure,
|
|
251
|
+
input,
|
|
252
|
+
ctx,
|
|
253
|
+
pathPrefix: `${req.method} ${triggerPath}`,
|
|
254
|
+
});
|
|
255
|
+
if (result.ok) {
|
|
256
|
+
return jsonResponse(200, { ok: true, data: result.data });
|
|
257
|
+
}
|
|
258
|
+
const status = HTTP_STATUS_BY_CODE[result.diagnostic.code] ?? 500;
|
|
259
|
+
return jsonResponse(status, { ok: false, diagnostic: result.diagnostic });
|
|
260
|
+
}
|
|
261
|
+
async function handleViewRequest(req, runtime, viewName, auth, waitUntil) {
|
|
262
|
+
const view = runtime.viewsByName.get(viewName);
|
|
263
|
+
if (!view) {
|
|
264
|
+
return jsonError({ status: 500, code: "INTERNAL_ERROR", message: `View '${viewName}' missing post-boot.` });
|
|
265
|
+
}
|
|
266
|
+
const viewPath = `GET /api/views/${viewName}`;
|
|
267
|
+
// Resolve caller identity FIRST and evaluate `requires.auth.all`
|
|
268
|
+
// BEFORE param coercion. Two reasons:
|
|
269
|
+
// (1) a param-coercion 400 against an auth-gated View would leak
|
|
270
|
+
// the View's parameter contract to an unauthorized caller —
|
|
271
|
+
// this matters for both anonymous probes AND authenticated
|
|
272
|
+
// users without the required role.
|
|
273
|
+
// (2) the UNAUTHENTICATED branch in ExecuteViewUseCase only fires
|
|
274
|
+
// when ctx is undefined, so passing a guest ctx for no-session
|
|
275
|
+
// would collapse 401 and 403 into the same AUTH_DENIED.
|
|
276
|
+
const ctx = await buildViewCtx(req, auth, waitUntil);
|
|
277
|
+
if (view.spec.requires?.auth) {
|
|
278
|
+
if (!ctx) {
|
|
279
|
+
return jsonResponse(401, {
|
|
280
|
+
ok: false,
|
|
281
|
+
diagnostic: runtimeDiagnostic({
|
|
282
|
+
code: "UNAUTHENTICATED",
|
|
283
|
+
severity: "error",
|
|
284
|
+
path: viewPath,
|
|
285
|
+
expected: "authenticated session",
|
|
286
|
+
message: `View '${viewName}' requires authentication.`,
|
|
287
|
+
}),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
const denial = evaluateAuthAll(view.spec.requires, ctx, viewPath, "runtime");
|
|
291
|
+
if (denial) {
|
|
292
|
+
return jsonResponse(HTTP_STATUS_BY_CODE[denial.code] ?? 403, {
|
|
293
|
+
ok: false,
|
|
294
|
+
diagnostic: denial,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const url = new URL(req.url);
|
|
299
|
+
const page = parsePositiveInt(url.searchParams.get(PAGE_PARAM));
|
|
300
|
+
const show = parsePositiveInt(url.searchParams.get(SHOW_PARAM));
|
|
301
|
+
let params;
|
|
302
|
+
try {
|
|
303
|
+
params = coerceViewParams(view, url.searchParams);
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
if (err instanceof ViewParamCoercionError) {
|
|
307
|
+
return jsonResponse(400, {
|
|
308
|
+
ok: false,
|
|
309
|
+
diagnostic: runtimeDiagnostic({
|
|
310
|
+
code: "INPUT_VALIDATION_FAILED",
|
|
311
|
+
severity: "error",
|
|
312
|
+
path: viewPath,
|
|
313
|
+
expected: "query string conforms to View.spec.params",
|
|
314
|
+
message: err.message,
|
|
315
|
+
}),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
throw err;
|
|
319
|
+
}
|
|
320
|
+
const result = await runtime.executeView.execute({
|
|
321
|
+
view,
|
|
322
|
+
pathPrefix: viewPath,
|
|
323
|
+
options: { params, page, show },
|
|
324
|
+
ctx,
|
|
325
|
+
});
|
|
326
|
+
if (result.ok) {
|
|
327
|
+
return jsonResponse(200, { ok: true, data: result.result });
|
|
328
|
+
}
|
|
329
|
+
const status = HTTP_STATUS_BY_CODE[result.diagnostic.code] ?? 500;
|
|
330
|
+
return jsonResponse(status, { ok: false, diagnostic: result.diagnostic });
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Resolve caller identity for a View request from the Better Auth
|
|
334
|
+
* cookie session. Returns `undefined` when there is no session at all
|
|
335
|
+
* so callers can distinguish 401 (no session) from 403 (session but
|
|
336
|
+
* wrong role) — `ExecuteViewUseCase` produces `UNAUTHENTICATED` for
|
|
337
|
+
* `ctx: undefined` and `AUTH_DENIED` for a ctx whose predicates fail.
|
|
338
|
+
*/
|
|
339
|
+
async function buildViewCtx(req, auth, waitUntil) {
|
|
340
|
+
const session = await auth.getSession(req);
|
|
341
|
+
if (!session)
|
|
342
|
+
return undefined;
|
|
343
|
+
const wu = waitUntil ? { waitUntil } : {};
|
|
344
|
+
const role = await auth.getUserRole(session.user.id);
|
|
345
|
+
const staff = role && ADMIN_ROLE_SET.has(role)
|
|
346
|
+
? { id: session.user.id, role: role }
|
|
347
|
+
: null;
|
|
348
|
+
return { user: { id: session.user.id }, staff, env: {}, ...wu };
|
|
349
|
+
}
|
|
350
|
+
function parsePositiveInt(raw) {
|
|
351
|
+
if (raw == null || raw === "")
|
|
352
|
+
return undefined;
|
|
353
|
+
const n = Number.parseInt(raw, 10);
|
|
354
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
355
|
+
}
|
|
356
|
+
async function readBody(req) {
|
|
357
|
+
if (req.method === "GET" || req.method === "DELETE" || req.method === "HEAD") {
|
|
358
|
+
return {};
|
|
359
|
+
}
|
|
360
|
+
const ct = req.headers.get("content-type") ?? "";
|
|
361
|
+
if (!ct.includes("json"))
|
|
362
|
+
return {};
|
|
363
|
+
try {
|
|
364
|
+
const parsed = await req.json();
|
|
365
|
+
return parsed ?? {};
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
return {};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async function buildHandlerContext(_req, _auth, waitUntil) {
|
|
372
|
+
const wu = waitUntil ? { waitUntil } : {};
|
|
373
|
+
// HTTP Triggers run on `defaultHandler` (Hono), which the OAuth lib
|
|
374
|
+
// does NOT route through token verification. Bearer-authenticated
|
|
375
|
+
// identity is only available on apiHandlers registered with
|
|
376
|
+
// `createOAuthProvider`. Triggers that need identity should migrate
|
|
377
|
+
// to an MCP tool, or read it from the caller frontend layer.
|
|
378
|
+
return { user: null, staff: null, env: {}, ...wu };
|
|
379
|
+
}
|
|
380
|
+
function openApiToHono(path) {
|
|
381
|
+
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Hono's `c.executionCtx` getter THROWS when the underlying request
|
|
385
|
+
* was dispatched without an `ExecutionContext` (test harness via
|
|
386
|
+
* `app.request(...)`, non-Workers runtimes). Treat the throw as
|
|
387
|
+
* "no waitUntil available" and fall back to inline-await downstream.
|
|
388
|
+
*/
|
|
389
|
+
function readWaitUntil(c) {
|
|
390
|
+
try {
|
|
391
|
+
const ctx = c.executionCtx;
|
|
392
|
+
return ctx.waitUntil.bind(ctx);
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
return undefined;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function jsonResponse(status, body) {
|
|
399
|
+
return new Response(JSON.stringify(body), {
|
|
400
|
+
status,
|
|
401
|
+
headers: { "content-type": "application/json" },
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
function mediaFieldsForCollection(schema, schemas) {
|
|
405
|
+
const related = [
|
|
406
|
+
schema,
|
|
407
|
+
...schemas.filter((s) => s.spec.translates?.parent === schema.metadata.name),
|
|
408
|
+
];
|
|
409
|
+
const out = [];
|
|
410
|
+
for (const s of related) {
|
|
411
|
+
out.push(...mediaFieldsForSchema(s));
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
function mediaFieldsForSchema(schema) {
|
|
416
|
+
const props = schema.spec.schema.properties ?? {};
|
|
417
|
+
const out = [];
|
|
418
|
+
for (const [name, prop] of Object.entries(props)) {
|
|
419
|
+
if (typeof prop !== "object" || prop === null)
|
|
420
|
+
continue;
|
|
421
|
+
const hint = prop[MCP_HINT_KEYWORD];
|
|
422
|
+
if (!isMediaMcpHint(hint))
|
|
423
|
+
continue;
|
|
424
|
+
out.push({ name, hint });
|
|
425
|
+
}
|
|
426
|
+
return out;
|
|
427
|
+
}
|
|
428
|
+
function adminUnauthenticated(c, path) {
|
|
429
|
+
return jsonResponse(401, {
|
|
430
|
+
ok: false,
|
|
431
|
+
diagnostic: runtimeDiagnostic({
|
|
432
|
+
code: "UNAUTHENTICATED",
|
|
433
|
+
severity: "error",
|
|
434
|
+
path: `${c.req.method} ${path}`,
|
|
435
|
+
expected: "active session cookie",
|
|
436
|
+
message: "Not signed in. Sign in via /admin/sign-in first.",
|
|
437
|
+
}),
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
// Distinct from UNAUTHENTICATED so the SPA can render an "access
|
|
441
|
+
// denied" view for users who DID sign in but lack a staff row,
|
|
442
|
+
// instead of bouncing them back to /admin/sign-in (which the OAuth
|
|
443
|
+
// re-auth then silently fast-forwards through, producing a visible
|
|
444
|
+
// 5-step redirect chain that looks like an infinite loop).
|
|
445
|
+
function adminNotStaff(c, path, login) {
|
|
446
|
+
return jsonResponse(403, {
|
|
447
|
+
ok: false,
|
|
448
|
+
login,
|
|
449
|
+
diagnostic: runtimeDiagnostic({
|
|
450
|
+
code: "AUTH_DENIED",
|
|
451
|
+
severity: "error",
|
|
452
|
+
path: `${c.req.method} ${path}`,
|
|
453
|
+
expected: "staff role for the signed-in user",
|
|
454
|
+
message: "Signed in, but this account isn't on the admin staff list. Contact a site owner to be added.",
|
|
455
|
+
}),
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
function jsonError(args) {
|
|
459
|
+
const diagnostic = {
|
|
460
|
+
code: args.code,
|
|
461
|
+
severity: "error",
|
|
462
|
+
phase: "runtime",
|
|
463
|
+
path: "mount/http",
|
|
464
|
+
message: args.message,
|
|
465
|
+
};
|
|
466
|
+
return jsonResponse(args.status, { ok: false, diagnostic });
|
|
467
|
+
}
|
|
468
|
+
async function runUseCase(opPath, fn) {
|
|
469
|
+
try {
|
|
470
|
+
const result = await fn();
|
|
471
|
+
return jsonResponse(200, result);
|
|
472
|
+
}
|
|
473
|
+
catch (e) {
|
|
474
|
+
if (e instanceof DiagnosticError) {
|
|
475
|
+
const status = HTTP_STATUS_BY_CODE[e.diagnostic.code] ?? 500;
|
|
476
|
+
return jsonResponse(status, { ok: false, diagnostic: redactForWire(e.diagnostic) });
|
|
477
|
+
}
|
|
478
|
+
// Don't leak raw exception strings on the wire — R2 / D1 / aws4fetch
|
|
479
|
+
// errors can carry bucket names, account IDs, or query fragments.
|
|
480
|
+
console.error(`[runUseCase ${opPath}] unhandled error`, e);
|
|
481
|
+
return jsonResponse(500, {
|
|
482
|
+
ok: false,
|
|
483
|
+
diagnostic: runtimeDiagnostic({
|
|
484
|
+
code: "INTERNAL_ERROR",
|
|
485
|
+
severity: "error",
|
|
486
|
+
path: opPath,
|
|
487
|
+
message: "An internal error occurred.",
|
|
488
|
+
}),
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function mediaNotConfiguredResponse(path) {
|
|
493
|
+
return jsonResponse(501, {
|
|
494
|
+
ok: false,
|
|
495
|
+
diagnostic: runtimeDiagnostic({
|
|
496
|
+
code: "MEDIA_NOT_CONFIGURED",
|
|
497
|
+
severity: "error",
|
|
498
|
+
path,
|
|
499
|
+
message: "Media uploads are not enabled on this deployment. Bind a `mediaStorage` adapter in `createCmsRuntime` to enable.",
|
|
500
|
+
}),
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
//# sourceMappingURL=mountServerEndpoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mountServerEndpoints.js","sourceRoot":"","sources":["../../src/mount/mountServerEndpoints.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth consent UI renderer. Self-contained HTML — no external assets,
|
|
3
|
+
* no framework. Navy-on-cream palette mirrors the admin SPA.
|
|
4
|
+
* Supports zh-TW and en locales.
|
|
5
|
+
*/
|
|
6
|
+
export interface ConsentModel {
|
|
7
|
+
readonly clientName: string;
|
|
8
|
+
readonly redirectUri: string;
|
|
9
|
+
readonly scopes: readonly string[];
|
|
10
|
+
readonly oauthRequestJson: string;
|
|
11
|
+
}
|
|
12
|
+
/** Detect consent UI locale from Accept-Language header. */
|
|
13
|
+
export declare function detectConsentLocale(acceptLanguage: string | null): "zh-TW" | "en";
|
|
14
|
+
export declare function renderConsentHtml(locale: "zh-TW" | "en", model: ConsentModel | null): string;
|
|
15
|
+
//# sourceMappingURL=consentHtml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consentHtml.d.ts","sourceRoot":"","sources":["../../src/oauth/consentHtml.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED,4DAA4D;AAC5D,wBAAgB,mBAAmB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,GAAG,IAAI,CAKjF;AAwDD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,GAAG,MAAM,CA4B5F"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth consent UI renderer. Self-contained HTML — no external assets,
|
|
3
|
+
* no framework. Navy-on-cream palette mirrors the admin SPA.
|
|
4
|
+
* Supports zh-TW and en locales.
|
|
5
|
+
*/
|
|
6
|
+
/** Detect consent UI locale from Accept-Language header. */
|
|
7
|
+
export function detectConsentLocale(acceptLanguage) {
|
|
8
|
+
if (!acceptLanguage)
|
|
9
|
+
return "en";
|
|
10
|
+
const lower = acceptLanguage.toLowerCase();
|
|
11
|
+
if (lower.includes("zh-tw") || lower.includes("zh_tw"))
|
|
12
|
+
return "zh-TW";
|
|
13
|
+
return "en";
|
|
14
|
+
}
|
|
15
|
+
const STRINGS = {
|
|
16
|
+
en: {
|
|
17
|
+
title: "Authorize · mantle",
|
|
18
|
+
eyebrow: "Authorize MCP access",
|
|
19
|
+
heading: (client) => `Allow ${client} to access your CMS?`,
|
|
20
|
+
redirectLabel: "Will redirect to",
|
|
21
|
+
scopesLabel: "Requested scopes",
|
|
22
|
+
approve: "Approve",
|
|
23
|
+
deny: "Deny",
|
|
24
|
+
invalidTitle: "Invalid authorization request",
|
|
25
|
+
invalidBody: "Missing or malformed consent payload. Return to your MCP client and try again.",
|
|
26
|
+
},
|
|
27
|
+
"zh-TW": {
|
|
28
|
+
title: "授權 · mantle",
|
|
29
|
+
eyebrow: "授權 MCP 存取",
|
|
30
|
+
heading: (client) => `允許 ${client} 存取您的 CMS?`,
|
|
31
|
+
redirectLabel: "將重新導向至",
|
|
32
|
+
scopesLabel: "請求的授權範圍",
|
|
33
|
+
approve: "同意",
|
|
34
|
+
deny: "拒絕",
|
|
35
|
+
invalidTitle: "無效的授權請求",
|
|
36
|
+
invalidBody: "缺少或格式錯誤的授權資訊,請返回 MCP 客戶端重試。",
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
function escapeHtml(s) {
|
|
40
|
+
return s
|
|
41
|
+
.replace(/&/g, "&")
|
|
42
|
+
.replace(/</g, "<")
|
|
43
|
+
.replace(/>/g, ">")
|
|
44
|
+
.replace(/"/g, """)
|
|
45
|
+
.replace(/'/g, "'");
|
|
46
|
+
}
|
|
47
|
+
const CSS = `
|
|
48
|
+
:root{--navy:#1a3062;--navy-light:#4d6aac;--bg:#eef1f8;--surface:rgba(255,255,255,.92);--border:rgba(151,158,175,.30);--muted:#6b7280;--fg:#111827}
|
|
49
|
+
*{box-sizing:border-box}
|
|
50
|
+
body{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:1rem;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;color:var(--fg);background:radial-gradient(ellipse at 20% 20%,rgba(77,106,172,.10) 0%,transparent 55%),radial-gradient(ellipse at 80% 80%,rgba(0,183,199,.07) 0%,transparent 55%),var(--bg)}
|
|
51
|
+
.card{max-width:32rem;width:100%;padding:2rem;border-radius:.75rem;background:var(--surface);border:1px solid var(--border);box-shadow:0 2px 20px rgba(26,48,98,.08),0 1px 4px rgba(26,48,98,.04);backdrop-filter:blur(14px)}
|
|
52
|
+
.eyebrow{font-size:.7rem;text-transform:uppercase;letter-spacing:.18em;font-weight:500;color:var(--muted);margin:0 0 .5rem}
|
|
53
|
+
h1{font-size:1.5rem;line-height:1.3;font-weight:500;margin:0 0 .75rem;letter-spacing:-.02em}
|
|
54
|
+
p{margin:0 0 1rem;font-size:.95rem;line-height:1.55}
|
|
55
|
+
.muted{color:var(--muted);font-size:.875rem}
|
|
56
|
+
code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8rem;padding:.125rem .4rem;border-radius:.25rem;background:rgba(26,48,98,.06)}
|
|
57
|
+
.scopes{margin:0 0 1.5rem;display:flex;flex-wrap:wrap;gap:.375rem}
|
|
58
|
+
.scope{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.75rem;padding:.25rem .5rem;border-radius:.25rem;background:rgba(26,48,98,.06)}
|
|
59
|
+
form{display:flex;gap:.75rem}
|
|
60
|
+
button{flex:1;padding:.625rem 1rem;border:0;border-radius:.5rem;font:inherit;font-weight:500;cursor:pointer;transition:opacity .15s,background .15s}
|
|
61
|
+
button[value="approve"]{background:var(--navy);color:#fff}
|
|
62
|
+
button[value="approve"]:hover{opacity:.9}
|
|
63
|
+
button[value="deny"]{background:rgba(26,48,98,.08);color:var(--fg)}
|
|
64
|
+
button[value="deny"]:hover{background:rgba(26,48,98,.14)}
|
|
65
|
+
`.trim();
|
|
66
|
+
export function renderConsentHtml(locale, model) {
|
|
67
|
+
const t = STRINGS[locale];
|
|
68
|
+
const lang = locale === "zh-TW" ? "zh-Hant-TW" : "en";
|
|
69
|
+
const head = `<!doctype html><html lang="${lang}"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>${t.title}</title><style>${CSS}</style></head><body><div class="card">`;
|
|
70
|
+
const tail = `</div></body></html>`;
|
|
71
|
+
if (!model) {
|
|
72
|
+
return `${head}<p class="eyebrow">${t.eyebrow}</p><h1>${t.invalidTitle}</h1><p class="muted">${t.invalidBody}</p>${tail}`;
|
|
73
|
+
}
|
|
74
|
+
const scopesBlock = model.scopes.length > 0
|
|
75
|
+
? `<p class="eyebrow">${t.scopesLabel}</p><div class="scopes">${model.scopes.map((s) => `<span class="scope">${escapeHtml(s)}</span>`).join("")}</div>`
|
|
76
|
+
: "";
|
|
77
|
+
return (`${head}` +
|
|
78
|
+
`<p class="eyebrow">${t.eyebrow}</p>` +
|
|
79
|
+
`<h1>${t.heading(escapeHtml(model.clientName))}</h1>` +
|
|
80
|
+
`<p class="muted">${t.redirectLabel} <code>${escapeHtml(model.redirectUri)}</code></p>` +
|
|
81
|
+
`${scopesBlock}` +
|
|
82
|
+
`<form method="post" action="/oauth/authorize">` +
|
|
83
|
+
`<input type="hidden" name="oauth_request" value="${escapeHtml(model.oauthRequestJson)}"/>` +
|
|
84
|
+
`<button type="submit" name="decision" value="approve">${t.approve}</button>` +
|
|
85
|
+
`<button type="submit" name="decision" value="deny">${t.deny}</button>` +
|
|
86
|
+
`</form>` +
|
|
87
|
+
`${tail}`);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=consentHtml.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consentHtml.js","sourceRoot":"","sources":["../../src/oauth/consentHtml.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { OAUTH_AUTHORIZE_PATH, OAUTH_TOKEN_PATH, OAUTH_REGISTER_PATH, } from "./oauthConstants.js";
|
|
2
|
+
export { createOAuthProvider, type CreateOAuthProviderArgs, } from "./oauthSingleton.js";
|
|
3
|
+
export { mountAuthorize, type MountAuthorizeOptions, type OAuthApiProps, } from "./mountOAuth.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,KAAK,uBAAuB,GAC7B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,aAAa,GACnB,MAAM,iBAAiB,CAAC"}
|