@bobbykim/manguito-cms-api 0.1.0
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/dist/codegen/index.d.mts +7 -0
- package/dist/codegen/index.d.ts +7 -0
- package/dist/codegen/index.js +701 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/codegen/index.mjs +672 -0
- package/dist/codegen/index.mjs.map +1 -0
- package/dist/index.d.mts +41 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +3228 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3189 -0
- package/dist/index.mjs.map +1 -0
- package/dist/runtime/index.d.mts +29 -0
- package/dist/runtime/index.d.ts +29 -0
- package/dist/runtime/index.js +118 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/index.mjs +86 -0
- package/dist/runtime/index.mjs.map +1 -0
- package/dist/storage/index.d.mts +25 -0
- package/dist/storage/index.d.ts +25 -0
- package/dist/storage/index.js +287 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/index.mjs +253 -0
- package/dist/storage/index.mjs.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/app.ts","../src/middleware/cors.ts","../src/middleware/security-headers.ts","../src/middleware/error.ts","../src/middleware/auth.ts","../src/auth/jwt.ts","../src/middleware/must-change-password.ts","../src/middleware/permission.ts","../src/middleware/hierarchy.ts","../src/middleware/rate-limit.ts","../src/auth/registry.ts","../src/routes/query-params.ts","../src/routes/content.ts","../src/routes/media.ts","../src/routes/admin/content.ts","../src/media-references.ts","../src/relations.ts","../src/routes/admin/media.ts","../src/routes/admin/auth.ts","../src/auth/password.ts","../src/routes/admin/users.ts","../src/routes/admin/config.ts","../src/routes/admin/schema.ts","../src/repositories/content.ts","../src/repositories/media.ts","../src/server/node.ts","../src/index.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport { sql } from 'drizzle-orm'\nimport type { StorageAdapter, SchemaRegistry, ParsedContentType, ParsedTaxonomyType, ResolvedRateLimitConfig, CorsConfig } from '@bobbykim/manguito-cms-core'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport { createCorsMiddleware } from './middleware/cors.js'\nimport { createSecurityHeadersMiddleware } from './middleware/security-headers.js'\nimport { errorHandler } from './middleware/error.js'\nimport { createAuthMiddleware } from './middleware/auth.js'\nimport { mustChangePasswordCheck } from './middleware/must-change-password.js'\nimport { createPermissionMiddleware } from './middleware/permission.js'\nimport { createHierarchyMiddleware } from './middleware/hierarchy.js'\nimport { resolveListRateLimit } from './middleware/rate-limit.js'\nimport { buildRolesRegistry } from './auth/registry.js'\nimport { registerPublicContentRoutes } from './routes/content.js'\nimport { registerPublicMediaRoutes } from './routes/media.js'\nimport { registerAdminContentRoutes } from './routes/admin/content.js'\nimport { registerAdminMediaRoutes } from './routes/admin/media.js'\nimport { registerAuthRoutes } from './routes/admin/auth.js'\nimport { registerUserRoutes } from './routes/admin/users.js'\nimport { registerConfigRoute } from './routes/admin/config.js'\nimport { registerSchemaRoute } from './routes/admin/schema.js'\nimport { createDrizzleContentRepository } from './repositories/content.js'\nimport { buildRelationsMap } from './relations.js'\nimport { createMediaRepository } from './repositories/media.js'\n\nexport type CreateCmsAppOptions = {\n /** CMS display name shown in GET /admin/api/config. Defaults to 'Manguito CMS'. */\n name?: string\n prefix?: string\n storage: StorageAdapter\n registry: SchemaRegistry\n db: DrizzlePostgresInstance\n media?: {\n /** Optional upload size cap in bytes. Returned by GET /admin/api/config for client-side UX validation. */\n max_file_size?: number\n }\n rateLimit?: ResolvedRateLimitConfig\n cors?: CorsConfig\n}\n\nexport interface ManguitoCmsAPIAdapter {\n readonly prefix: string\n readonly app: Hono\n}\n\nconst MISSING_STORAGE_ERROR = `✗ api.storage is required but not configured.\n Add a storage adapter to your manguito.config.ts:\n\n api: createAPIAdapter({\n storage: createLocalAdapter(), // dev\n // storage: createS3Adapter({ bucket: '...', region: '...' }) // production\n })\n\nExiting.`\n\nexport function createCmsApp(options: CreateCmsAppOptions): ManguitoCmsAPIAdapter {\n if (!options.storage) {\n throw new Error(MISSING_STORAGE_ERROR)\n }\n\n const prefix = options.prefix ?? '/api'\n const { storage, registry, db, rateLimit, media, cors } = options\n const cmsName = options.name ?? 'Manguito CMS'\n const maxFileSize = media?.max_file_size\n\n // Build roles registry — throws immediately if roles are missing or invalid.\n // The server must not start with a broken registry.\n const rolesRegistry = buildRolesRegistry(registry.roles.roles)\n\n const app = new Hono()\n\n // Security headers for all routes — registered before CORS so normal and\n // CORS-handled responses carry the conservative defaults. Responses produced\n // by app.onError (thrown errors) bypass this middleware's post-next step;\n // those are JSON error envelopes with no clickjacking/XSS surface. CSP\n // connect-src is derived from the storage adapter: presigned uploads go\n // browser→storage directly, so that host must be allowlisted.\n const uploadOrigins = storage.getUploadOrigins?.() ?? []\n app.use('*', createSecurityHeadersMiddleware({ connectSrc: uploadOrigins }))\n\n // CORS for all routes\n app.use('*', createCorsMiddleware(cors ?? { origin: '*', enabled: true }))\n app.onError(errorHandler)\n\n // Rate limiter for public list endpoints — threaded into route registrators,\n // applied only to paginated collection routes (not single-item lookups).\n // `undefined` when disabled via rateLimit.findAll === '*'.\n const listRateLimit = resolveListRateLimit(rateLimit)\n\n // ── Middleware factories — all close over rolesRegistry built once at startup ──\n\n const authMiddleware = createAuthMiddleware(db)\n const requirePermission = createPermissionMiddleware(rolesRegistry)\n\n const getUserRole = async (userId: string): Promise<string | null> => {\n const result = await db.execute(\n sql`SELECT r.name AS role\n FROM users u\n JOIN roles r ON r.id = u.role_id\n WHERE u.id = ${userId}\n LIMIT 1`\n )\n return (result.rows[0] as { role: string } | undefined)?.role ?? null\n }\n\n const requireHierarchy = createHierarchyMiddleware(rolesRegistry, getUserRole)\n\n // ── Repositories ──────────────────────────────────────────────────────────────\n\n const contentRepos = Object.fromEntries(\n Object.entries(registry.content_types).map(([typeName, ct]) => [\n typeName,\n createDrizzleContentRepository(db, (ct as ParsedContentType).db.table_name),\n ])\n )\n const taxonomyRepos = Object.fromEntries(\n Object.entries(registry.taxonomy_types).map(([typeName, tt]) => [\n typeName,\n createDrizzleContentRepository(db, (tt as ParsedTaxonomyType).db.table_name),\n ])\n )\n const repos = { ...contentRepos, ...taxonomyRepos }\n const mediaRepo = createMediaRepository(db)\n\n // ── Public-only repos ───────────────────────────────────────────────────────\n //\n // Relation resolution (?include=, always-resolved media) is wired only for\n // the public read API. Admin routes reuse `repos` above and must keep\n // receiving raw foreign-key IDs back from findOne/update — the edit forms\n // (e.g. MediaUpload.vue) expect a plain ID string, not a resolved object.\n const publicContentRepos = Object.fromEntries(\n Object.entries(registry.content_types).map(([typeName, ct]) => [\n typeName,\n createDrizzleContentRepository(db, (ct as ParsedContentType).db.table_name, {\n relations: buildRelationsMap((ct as ParsedContentType).fields, registry),\n }),\n ])\n )\n const publicTaxonomyRepos = Object.fromEntries(\n Object.entries(registry.taxonomy_types).map(([typeName, tt]) => [\n typeName,\n createDrizzleContentRepository(db, (tt as ParsedTaxonomyType).db.table_name, {\n relations: buildRelationsMap((tt as ParsedTaxonomyType).fields, registry),\n }),\n ])\n )\n const publicRepos = { ...publicContentRepos, ...publicTaxonomyRepos }\n\n // ── Auth routes registered directly on app BEFORE the blanket use() calls ───────\n //\n // registerAuthRoutes uses full paths (/admin/api/auth/login etc.). Mounting via\n // app.route('/admin/api/auth', subRouter) would strip that prefix and make the\n // routes unreachable, so we register directly on app.\n //\n // In Hono, handlers registered before a use() call are reached first. A route\n // handler that returns a Response does not call next(), so the blanket\n // authMiddleware registered below never runs for matched auth paths.\n registerAuthRoutes(app, db)\n\n // ── Blanket middleware for all /admin/api/* (registered after auth routes) ─────\n app.use('/admin/api/*', authMiddleware)\n app.use('/admin/api/*', mustChangePasswordCheck)\n\n // ── OpenAPI spec endpoints ────────────────────────────────────────────────────\n\n app.get('/api/openapi.json', (c) => {\n const paths: Record<string, unknown> = {}\n\n for (const ct of Object.values(registry.content_types) as ParsedContentType[]) {\n const base = ct.default_base_path\n if (ct.only_one) {\n paths[`/api/${base}`] = { get: { summary: `Get ${ct.label}`, tags: [ct.label] } }\n } else {\n paths[`/api/${base}`] = { get: { summary: `List published ${ct.label}`, tags: [ct.label] } }\n paths[`/api/${base}/{slug}`] = { get: { summary: `Get ${ct.label} by slug`, tags: [ct.label] } }\n }\n }\n\n for (const tt of Object.values(registry.taxonomy_types) as ParsedTaxonomyType[]) {\n paths[`/api/taxonomy/${tt.name}`] = { get: { summary: `List published ${tt.label}`, tags: [tt.label] } }\n paths[`/api/taxonomy/${tt.name}/{id}`] = { get: { summary: `Get ${tt.label} by id`, tags: [tt.label] } }\n }\n\n paths['/api/media'] = { get: { summary: 'List media items', tags: ['Media'] } }\n paths['/api/media/{id}'] = { get: { summary: 'Get media item by id', tags: ['Media'] } }\n\n return c.json({\n openapi: '3.0.3',\n info: { title: 'Manguito CMS Public API', version: '1.0.0' },\n paths,\n })\n })\n // Admin spec — auth covered by the blanket use() above\n app.get('/admin/api/openapi.json', (c) =>\n c.json({\n openapi: '3.0.3',\n info: { title: 'Manguito CMS Admin API', version: '1.0.0' },\n paths: {},\n })\n )\n\n // ── Public routes ─────────────────────────────────────────────────────────────\n\n registerPublicContentRoutes(app, registry, publicRepos, listRateLimit)\n registerPublicMediaRoutes(app, mediaRepo, listRateLimit)\n\n // ── Admin routes — all registered AFTER the blanket use() so auth middleware\n // runs before every handler. registerConfigRoute is called first so its\n // GET /admin/api/config handler wins over the stale stub in content.ts.\n registerConfigRoute(\n app,\n {\n name: cmsName,\n ...(maxFileSize !== undefined && { maxFileSize }),\n // Cloud storage (s3/cloudinary) issues real presigned URLs, so the admin\n // uploads straight to it — never routing the file through the server\n // (which breaks on serverless). Local storage has no presigned receiver,\n // so it uses the direct upload endpoint.\n presignedUploads: storage.type !== 'local',\n },\n rolesRegistry,\n db,\n )\n registerSchemaRoute(app, registry, db)\n registerUserRoutes(app, db, requirePermission, requireHierarchy)\n registerAdminContentRoutes(app, registry, repos, mediaRepo, requirePermission, db)\n registerAdminMediaRoutes(app, mediaRepo, storage, requirePermission, maxFileSize)\n\n return { prefix, app }\n}\n","import type { MiddlewareHandler } from 'hono'\nimport type { CorsConfig } from '@bobbykim/manguito-cms-core'\n\nexport function createCorsMiddleware(corsConfig: CorsConfig): MiddlewareHandler {\n const allowList = Array.isArray(corsConfig.origin)\n ? corsConfig.origin\n : [corsConfig.origin]\n const wildcard = allowList.includes('*')\n const methods = corsConfig.methods?.join(',') ?? 'GET,POST,PATCH,DELETE,OPTIONS'\n\n return async function corsMiddleware(c, next) {\n if (corsConfig.enabled === false) {\n return next()\n }\n\n const requestOrigin = c.req.header('origin')\n\n if (wildcard) {\n // Wildcard cannot be combined with credentials per the CORS spec.\n c.res.headers.set('Access-Control-Allow-Origin', '*')\n } else if (requestOrigin && allowList.includes(requestOrigin)) {\n c.res.headers.set('Access-Control-Allow-Origin', requestOrigin)\n c.res.headers.set('Vary', 'Origin')\n if (corsConfig.credentials === true) {\n c.res.headers.set('Access-Control-Allow-Credentials', 'true')\n }\n }\n // Non-matching origins: emit no Allow-Origin (browser blocks the read).\n\n c.res.headers.set('Access-Control-Allow-Methods', methods)\n c.res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization')\n\n if (c.req.method === 'OPTIONS') {\n return c.newResponse(null, 204)\n }\n return next()\n }\n}\n","import type { MiddlewareHandler } from 'hono'\n\nexport type SecurityHeadersOptions = {\n /** Extra origins allowed for connect-src (e.g. the storage upload host). */\n connectSrc?: string[]\n}\n\n/**\n * Conservative security headers. CSP allows same-origin scripts/styles/fonts\n * for the admin SPA (served same-origin) and blocks framing. connect-src is\n * 'self' plus any storage upload origins passed in — presigned uploads go\n * browser→storage directly (ADR api/0004), so that host must be allowlisted.\n */\nexport function createSecurityHeadersMiddleware(\n options: SecurityHeadersOptions = {},\n): MiddlewareHandler {\n const connectSrc = [\"'self'\", ...(options.connectSrc ?? [])].join(' ')\n const csp = [\n \"default-src 'self'\",\n \"img-src 'self' data: https:\",\n \"media-src 'self' https:\",\n \"style-src 'self' 'unsafe-inline'\",\n \"script-src 'self'\",\n \"font-src 'self' data:\",\n `connect-src ${connectSrc}`,\n \"frame-ancestors 'none'\",\n \"base-uri 'self'\",\n \"form-action 'self'\",\n ].join('; ')\n\n return async function securityHeaders(c, next) {\n await next()\n c.res.headers.set('X-Content-Type-Options', 'nosniff')\n c.res.headers.set('X-Frame-Options', 'DENY')\n c.res.headers.set('Referrer-Policy', 'no-referrer')\n c.res.headers.set('Content-Security-Policy', csp)\n }\n}\n","import type { ErrorHandler } from 'hono'\nimport type { ErrorCode } from '@bobbykim/manguito-cms-core'\n\nconst ERROR_STATUS_MAP: Partial<Record<ErrorCode, number>> = {\n NOT_FOUND: 404,\n SLUG_NOT_FOUND: 404,\n METHOD_NOT_ALLOWED: 405,\n VALIDATION_ERROR: 422,\n INVALID_SLUG_FORMAT: 422,\n SLUG_CONFLICT: 409,\n PUBLISH_VALIDATION_ERROR: 422,\n SINGLETON_ALREADY_EXISTS: 409,\n UNAUTHORIZED: 401,\n TOKEN_EXPIRED: 401,\n TOKEN_INVALID: 401,\n INSUFFICIENT_PERMISSION: 403,\n INSUFFICIENT_PRIVILEGE: 403,\n INVALID_FILTER_FIELD: 400,\n INVALID_FILTER_OPERATOR: 400,\n INVALID_SORT_FIELD: 400,\n INVALID_PAGINATION: 400,\n INVALID_INCLUDE_FIELD: 400,\n UNSUPPORTED_MIME_TYPE: 415,\n STORAGE_ERROR: 502,\n MEDIA_IN_USE: 409,\n PRESIGNED_URL_EXPIRED: 410,\n RATE_LIMITED: 429,\n INTERNAL_ERROR: 500,\n}\n\ntype ApiError = Error & { code?: ErrorCode }\n\nexport const errorHandler: ErrorHandler = (err, c) => {\n console.error(err.stack ?? err.message)\n\n const apiErr = err as ApiError\n const code: ErrorCode = apiErr.code ?? 'INTERNAL_ERROR'\n const status = ERROR_STATUS_MAP[code] ?? 500\n\n return c.json({ ok: false, error: { code, message: err.message } }, status as Parameters<typeof c.json>[1])\n}\n","import type { MiddlewareHandler } from 'hono'\nimport { getCookie } from 'hono/cookie'\nimport { sql } from 'drizzle-orm'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport {\n verifyToken,\n signToken,\n setAuthCookie,\n AUTH_TOKEN_TTL,\n PROACTIVE_REFRESH_THRESHOLD,\n} from '../auth/jwt.js'\n\ntype UserRow = {\n token_version: number\n must_change_password: boolean\n}\n\nexport function createAuthMiddleware(db: DrizzlePostgresInstance): MiddlewareHandler {\n return async (c, next) => {\n const token = getCookie(c, 'auth_token')\n if (!token) {\n return c.json(\n { ok: false, error: { code: 'UNAUTHORIZED', message: 'Authentication required' } },\n 401,\n )\n }\n\n let payload\n try {\n payload = await verifyToken(token)\n } catch (err) {\n const code = (err as { code?: string }).code\n if (code === 'TOKEN_EXPIRED') {\n return c.json(\n { ok: false, error: { code: 'TOKEN_EXPIRED', message: 'Token has expired' } },\n 401,\n )\n }\n return c.json(\n { ok: false, error: { code: 'TOKEN_INVALID', message: 'Token signature is invalid' } },\n 401,\n )\n }\n\n const result = await db.execute(\n sql`SELECT token_version, must_change_password FROM users WHERE id = ${payload.user_id} LIMIT 1`,\n )\n const row = result.rows[0] as UserRow | undefined\n\n if (!row) {\n return c.json(\n { ok: false, error: { code: 'TOKEN_INVALID', message: 'Token signature is invalid' } },\n 401,\n )\n }\n\n if (payload.token_version !== row.token_version) {\n return c.json(\n { ok: false, error: { code: 'TOKEN_INVALID', message: 'Token has been invalidated' } },\n 401,\n )\n }\n\n c.set('user', {\n id: payload.user_id,\n role: payload.role,\n must_change_password: row.must_change_password,\n })\n\n const now = Math.floor(Date.now() / 1000)\n if (payload.expires_at < now + PROACTIVE_REFRESH_THRESHOLD) {\n const newToken = await signToken(\n {\n user_id: payload.user_id,\n role: payload.role,\n token_version: payload.token_version,\n },\n AUTH_TOKEN_TTL,\n )\n setAuthCookie(c, 'auth_token', newToken, {})\n }\n\n await next()\n }\n}\n","import type { Context } from 'hono'\nimport { sign, verify as jwtVerify } from 'hono/jwt'\nimport { setCookie, deleteCookie } from 'hono/cookie'\nimport type { JWTPayload, ErrorCode } from '@bobbykim/manguito-cms-core'\n\ntype CookieOptions = NonNullable<Parameters<typeof setCookie>[3]>\n\n// ─── Token lifetimes ──────────────────────────────────────────────────────────\n\nexport const AUTH_TOKEN_TTL = 2 * 60 * 60\nexport const REFRESH_TOKEN_TTL = 7 * 24 * 60 * 60\nexport const PROACTIVE_REFRESH_THRESHOLD = 30 * 60\n\n// ─── JWT helpers ──────────────────────────────────────────────────────────────\n\nexport async function signToken(\n payload: Omit<JWTPayload, 'expires_at'>,\n expiresInSeconds: number,\n): Promise<string> {\n const secret = process.env['AUTH_SECRET']\n if (!secret) throw new Error('AUTH_SECRET environment variable is not set')\n const fullPayload: JWTPayload = {\n ...payload,\n expires_at: Math.floor(Date.now() / 1000) + expiresInSeconds,\n }\n return sign(fullPayload as Record<string, unknown>, secret)\n}\n\nexport async function verifyToken(token: string): Promise<JWTPayload> {\n const secret = process.env['AUTH_SECRET']\n if (!secret) throw new Error('AUTH_SECRET environment variable is not set')\n let raw: Record<string, unknown>\n try {\n raw = (await jwtVerify(token, secret, 'HS256')) as Record<string, unknown>\n } catch {\n throw Object.assign(new Error('Token signature is invalid'), {\n code: 'TOKEN_INVALID' as ErrorCode,\n })\n }\n const payload = raw as JWTPayload\n if (payload.expires_at < Math.floor(Date.now() / 1000)) {\n throw Object.assign(new Error('Token has expired'), {\n code: 'TOKEN_EXPIRED' as ErrorCode,\n })\n }\n return payload\n}\n\n// ─── Cookie helpers ───────────────────────────────────────────────────────────\n\nexport function setAuthCookie(\n c: Context,\n name: string,\n token: string,\n options: CookieOptions,\n): void {\n setCookie(c, name, token, {\n ...options,\n httpOnly: true,\n secure: true,\n sameSite: 'Strict',\n })\n}\n\nexport function clearAuthCookie(c: Context, name: string, path?: string): void {\n deleteCookie(c, name, path !== undefined ? { path } : undefined)\n}\n","import type { MiddlewareHandler } from 'hono'\n\nexport const mustChangePasswordCheck: MiddlewareHandler = async (c, next) => {\n const user = c.get('user') as { must_change_password: boolean } | undefined\n\n if (\n user?.must_change_password === true &&\n c.req.path !== '/admin/api/users/change-password'\n ) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'PASSWORD_CHANGE_REQUIRED',\n message: 'You must change your password before continuing.',\n },\n },\n 403,\n )\n }\n\n await next()\n}\n","import type { MiddlewareHandler } from 'hono'\nimport type { Permission } from '@bobbykim/manguito-cms-core'\nimport type { RolesRegistry } from '../auth/registry.js'\n\ntype AuthUser = { role: string }\n\nexport function createPermissionMiddleware(registry: RolesRegistry) {\n return function requirePermission(permission: Permission): MiddlewareHandler {\n return async (c, next) => {\n const user = c.get('user') as AuthUser | undefined\n const role = user ? registry[user.role] : undefined\n\n if (!role || !role.permissions.includes(permission)) {\n return c.json(\n {\n ok: false,\n error: { code: 'INSUFFICIENT_PERMISSION', message: 'Insufficient permission' },\n },\n 403,\n )\n }\n\n await next()\n }\n }\n}\n","import type { MiddlewareHandler } from 'hono'\nimport type { RolesRegistry } from '../auth/registry.js'\n\ntype AuthUser = { role: string }\n\nexport function createHierarchyMiddleware(\n registry: RolesRegistry,\n getUserRole: (id: string) => Promise<string | null>,\n) {\n return function requireHierarchy(): MiddlewareHandler {\n return async (c, next) => {\n const user = c.get('user') as AuthUser | undefined\n const actingRole = user ? registry[user.role] : undefined\n\n if (!actingRole) {\n return c.json(\n {\n ok: false,\n error: { code: 'INSUFFICIENT_PRIVILEGE', message: 'Insufficient privilege' },\n },\n 403,\n )\n }\n\n const method = c.req.method\n const targetId = c.req.param('id') as string | undefined\n\n let targetRoleName: string | null = null\n\n if (method === 'POST' && !targetId) {\n // POST /admin/api/users — create; role must be in request body\n let body: { role?: unknown } = {}\n try {\n body = await c.req.json<{ role?: unknown }>()\n } catch {\n // malformed body — role is absent, treated as INVALID_ROLE below\n }\n targetRoleName = typeof body.role === 'string' ? body.role : null\n if (!targetRoleName) {\n return c.json(\n {\n ok: false,\n error: { code: 'INVALID_ROLE', message: 'Request body must include a valid role' },\n },\n 400,\n )\n }\n } else if (method === 'PATCH' && targetId) {\n // PATCH /admin/api/users/:id — update; role from body if present, else from DB\n let bodyRole: string | undefined\n try {\n const body = await c.req.json<{ role?: unknown }>()\n if (typeof body.role === 'string') bodyRole = body.role\n } catch {\n // no JSON body — fall through to DB lookup\n }\n targetRoleName = bodyRole ?? (await getUserRole(targetId))\n } else if (targetId) {\n // DELETE /admin/api/users/:id or POST /admin/api/users/:id/reset-password\n targetRoleName = await getUserRole(targetId)\n }\n\n // Target user not found — let the route handler return 404\n if (targetRoleName === null) {\n return next()\n }\n\n const targetRole = registry[targetRoleName]\n if (!targetRole) {\n return c.json(\n {\n ok: false,\n error: { code: 'INVALID_ROLE', message: `Role \"${targetRoleName}\" is not valid` },\n },\n 400,\n )\n }\n\n if (actingRole.hierarchy_level >= targetRole.hierarchy_level) {\n return c.json(\n {\n ok: false,\n error: { code: 'INSUFFICIENT_PRIVILEGE', message: 'Insufficient privilege' },\n },\n 403,\n )\n }\n\n await next()\n }\n }\n}\n","import type { MiddlewareHandler } from 'hono'\nimport type { ResolvedRateLimitConfig } from '@bobbykim/manguito-cms-core'\n\n// Defaults for the public list-endpoint limiter. Single source of truth —\n// createCmsApp resolves its limiter through resolveListRateLimit below.\nconst DEFAULT_WINDOW_MS = 60_000\nconst DEFAULT_MAX_PER_IP = 30\nconst DEFAULT_MAX_GLOBAL = 500\n\nexport type RateLimitOptions = {\n windowMs: number\n maxPerIp: number\n maxGlobal: number\n}\n\nexport function createRateLimitMiddleware(options: RateLimitOptions): MiddlewareHandler {\n const { windowMs, maxPerIp, maxGlobal } = options\n const ipRequests = new Map<string, number[]>()\n const globalRequests: number[] = []\n\n return async function rateLimitMiddleware(c, next) {\n const authToken = c.req.raw.headers.get('cookie')\n ?.split(';')\n .map((s) => s.trim())\n .find((s) => s.startsWith('auth_token='))\n\n if (authToken) {\n return next()\n }\n\n const now = Date.now()\n const cutoff = now - windowMs\n\n // purge stale global entries\n let gi = 0\n while (gi < globalRequests.length && globalRequests[gi]! < cutoff) gi++\n globalRequests.splice(0, gi)\n\n // get client IP\n const forwarded = c.req.header('x-forwarded-for')\n const ip = forwarded ? forwarded.split(',')[0]!.trim() : (c.env?.incoming?.socket?.remoteAddress ?? 'unknown')\n\n // purge stale IP entries\n const ipTimes = ipRequests.get(ip) ?? []\n let ii = 0\n while (ii < ipTimes.length && ipTimes[ii]! < cutoff) ii++\n ipTimes.splice(0, ii)\n ipRequests.set(ip, ipTimes)\n\n const retryAfterFromGlobal =\n globalRequests.length > 0\n ? Math.ceil((globalRequests[0]! + windowMs - now) / 1000)\n : 0\n const retryAfterFromIp =\n ipTimes.length > 0 ? Math.ceil((ipTimes[0]! + windowMs - now) / 1000) : 0\n\n if (globalRequests.length >= maxGlobal) {\n const retryAfter = Math.max(retryAfterFromGlobal, 1)\n const reset = Math.floor((now + retryAfter * 1000) / 1000)\n return c.json(\n { ok: false, error: { code: 'RATE_LIMITED', message: `Too many requests. Please retry after ${retryAfter} seconds.` } },\n 429,\n {\n 'Retry-After': String(retryAfter),\n 'X-RateLimit-Limit': String(maxGlobal),\n 'X-RateLimit-Remaining': '0',\n 'X-RateLimit-Reset': String(reset),\n }\n )\n }\n\n if (ipTimes.length >= maxPerIp) {\n const retryAfter = Math.max(retryAfterFromIp, 1)\n const reset = Math.floor((now + retryAfter * 1000) / 1000)\n return c.json(\n { ok: false, error: { code: 'RATE_LIMITED', message: `Too many requests. Please retry after ${retryAfter} seconds.` } },\n 429,\n {\n 'Retry-After': String(retryAfter),\n 'X-RateLimit-Limit': String(maxPerIp),\n 'X-RateLimit-Remaining': '0',\n 'X-RateLimit-Reset': String(reset),\n }\n )\n }\n\n globalRequests.push(now)\n ipTimes.push(now)\n\n return next()\n }\n}\n\n/**\n * Resolves the public list-endpoint config into a middleware, or `undefined`\n * when the limiter is disabled via the `findAll: '*'` wildcard. Route\n * registrators skip registration when this is `undefined`, so a disabled\n * limiter has zero request-path overhead.\n */\nexport function resolveListRateLimit(\n rateLimit?: ResolvedRateLimitConfig,\n): MiddlewareHandler | undefined {\n const findAll = rateLimit?.findAll\n if (findAll === '*') {\n return undefined\n }\n return createRateLimitMiddleware({\n windowMs: findAll?.windowMs ?? DEFAULT_WINDOW_MS,\n maxPerIp: findAll?.maxPerIp ?? DEFAULT_MAX_PER_IP,\n maxGlobal: findAll?.maxGlobal ?? DEFAULT_MAX_GLOBAL,\n })\n}\n","import type { ParsedRole } from '@bobbykim/manguito-cms-core'\n\nexport type RolesRegistry = Record<string, ParsedRole>\n\nconst SYSTEM_ROLES = ['admin', 'manager', 'editor', 'writer', 'viewer'] as const\n\nfunction validateRoles(roles: ParsedRole[]): void {\n if (roles.length === 0) {\n throw new Error(\n 'Fatal: roles registry failed to build — roles array is empty. Run `manguito validate` to check your roles schema.',\n )\n }\n\n const nameSet = new Set(roles.map((r) => r.name))\n for (const systemRole of SYSTEM_ROLES) {\n if (!nameSet.has(systemRole)) {\n throw new Error(\n `Fatal: roles registry failed to build — missing system role \"${systemRole}\". Run \\`manguito validate\\` to check your roles schema.`,\n )\n }\n }\n\n const levelsSeen = new Map<number, string>()\n for (const role of roles) {\n const existing = levelsSeen.get(role.hierarchy_level)\n if (existing !== undefined) {\n throw new Error(\n `Fatal: roles registry failed to build — duplicate hierarchy_level ${role.hierarchy_level} on roles \"${existing}\" and \"${role.name}\". Run \\`manguito validate\\` to check your roles schema.`,\n )\n }\n levelsSeen.set(role.hierarchy_level, role.name)\n }\n\n const namesSeen = new Set<string>()\n for (const role of roles) {\n if (namesSeen.has(role.name)) {\n throw new Error(\n `Fatal: roles registry failed to build — duplicate role name \"${role.name}\". Run \\`manguito validate\\` to check your roles schema.`,\n )\n }\n namesSeen.add(role.name)\n }\n}\n\nexport function buildRolesRegistry(roles: ParsedRole[]): RolesRegistry {\n validateRoles(roles)\n return Object.fromEntries(roles.map((r) => [r.name, r]))\n}\n","import type { FilterValue, FilterOperator } from '@bobbykim/manguito-cms-core'\n\n// Query-param parsing shared by the public and admin content/taxonomy list routes.\n\n// Only indexed system fields are sortable.\nexport const SORTABLE_FIELDS = new Set<string>(['title', 'created_at', 'updated_at'])\n\n// Field types whose values are relations rather than plain columns.\nexport const RELATION_FIELD_TYPES = new Set([\n 'paragraph',\n 'reference',\n 'image',\n 'video',\n 'file',\n])\n\nexport function parsePagination(\n pageStr: string | undefined,\n perPageStr: string | undefined\n): { ok: true; page: number; per_page: number } | { ok: false } {\n const page = pageStr !== undefined ? Number(pageStr) : 1\n const per_page = perPageStr !== undefined ? Number(perPageStr) : 10\n\n if (!Number.isInteger(page) || page < 1) return { ok: false }\n if (!Number.isInteger(per_page) || per_page < 1 || per_page > 100) return { ok: false }\n return { ok: true, page, per_page }\n}\n\nexport function parseInclude(includeParam: string | undefined): string[] {\n if (!includeParam) return []\n return includeParam\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n}\n\nexport function parseFilters(\n url: string,\n validFields: Set<string>\n): { ok: true; filters: Record<string, FilterValue> } | { ok: false; invalidField: string } {\n const { searchParams } = new URL(url)\n const filters: Record<string, FilterValue> = {}\n\n for (const [key, value] of searchParams.entries()) {\n const simpleMatch = /^filter\\[([^\\]]+)\\]$/.exec(key)\n const opMatch = /^filter\\[([^\\]]+)\\]\\[([^\\]]+)\\]$/.exec(key)\n\n if (simpleMatch) {\n const field = simpleMatch[1]!\n if (!validFields.has(field)) return { ok: false, invalidField: field }\n const existing = filters[field]\n if (existing !== undefined) {\n filters[field] = Array.isArray(existing)\n ? [...existing, value]\n : [existing as string | number | boolean, value]\n } else {\n filters[field] = value\n }\n } else if (opMatch) {\n const field = opMatch[1]!\n const operator = opMatch[2]!\n if (!validFields.has(field)) return { ok: false, invalidField: field }\n if (!['gt', 'gte', 'lt', 'lte'].includes(operator)) continue\n const existing = (filters[field] as FilterOperator | undefined) ?? {}\n filters[field] = { ...(existing as FilterOperator), [operator]: value }\n }\n }\n\n return { ok: true, filters }\n}\n","import type { Hono, Handler, MiddlewareHandler } from 'hono'\nimport type {\n SchemaRegistry,\n ContentRepository,\n} from '@bobbykim/manguito-cms-core'\nimport {\n SORTABLE_FIELDS,\n RELATION_FIELD_TYPES,\n parsePagination,\n parseInclude,\n parseFilters,\n} from './query-params.js'\n\nexport type ContentRepos = Record<string, ContentRepository<unknown>>\n\nfunction isPublished(item: unknown): boolean {\n return (item as Record<string, unknown>)['published'] === true\n}\n\nexport function registerPublicContentRoutes(\n app: Hono,\n registry: SchemaRegistry,\n repos: ContentRepos,\n listRateLimit?: MiddlewareHandler\n): void {\n // ── Meta-endpoints: list available schema types ───────────────────────────\n // Registered before the dynamic per-type routes to avoid path conflicts.\n\n function registerListRoute(path: string, handler: Handler): void {\n if (listRateLimit) {\n app.get(path, listRateLimit, handler)\n } else {\n app.get(path, handler)\n }\n }\n\n registerListRoute('/api/content', (c) => {\n const data = Object.values(registry.content_types).map((ct) => ({\n name: ct.name,\n label: ct.label,\n only_one: ct.only_one,\n }))\n return c.json({ ok: true, data })\n })\n\n registerListRoute('/api/taxonomy', (c) => {\n const data = Object.values(registry.taxonomy_types).map((tt) => ({\n name: tt.name,\n label: tt.label,\n }))\n return c.json({ ok: true, data })\n })\n\n // ── Per-type content routes ───────────────────────────────────────────────\n\n for (const [typeName, contentType] of Object.entries(registry.content_types)) {\n const basePath = contentType.default_base_path\n const repo = repos[typeName]\n if (!repo) continue\n\n const schemaFieldNames = new Set<string>([\n ...contentType.fields.map((f) => f.name),\n ...contentType.system_fields.map((f) => f.name),\n ])\n\n const relationFieldNames = new Set<string>(\n contentType.fields\n .filter((f) => RELATION_FIELD_TYPES.has(f.field_type))\n .map((f) => f.name)\n )\n\n if (contentType.only_one) {\n app.get(`/api/${basePath}`, async (c) => {\n const result = await repo.findMany({ published_only: true, page: 1, per_page: 1 })\n if (result.data.length === 0) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Not found' } },\n 404\n )\n }\n return c.json({ ok: true, data: result.data[0] })\n })\n } else {\n registerListRoute(`/api/${basePath}`, async (c) => {\n const pagination = parsePagination(c.req.query('page'), c.req.query('per_page'))\n if (!pagination.ok) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_PAGINATION',\n message: 'page must be ≥ 1 and per_page must be between 1 and 100',\n },\n },\n 400\n )\n }\n\n const sortBy = c.req.query('sort_by') ?? 'created_at'\n if (!SORTABLE_FIELDS.has(sortBy)) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_SORT_FIELD',\n message: `'${sortBy}' is not sortable. Allowed: title, created_at, updated_at`,\n },\n },\n 400\n )\n }\n\n const sortOrder = c.req.query('sort_order') ?? 'asc'\n if (sortOrder !== 'asc' && sortOrder !== 'desc') {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_SORT_FIELD',\n message: `sort_order must be 'asc' or 'desc'`,\n },\n },\n 400\n )\n }\n\n const filtersResult = parseFilters(c.req.url, schemaFieldNames)\n if (!filtersResult.ok) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_FILTER_FIELD',\n message: `Filter field '${filtersResult.invalidField}' does not exist on this content type`,\n },\n },\n 400\n )\n }\n\n const include = parseInclude(c.req.query('include'))\n for (const field of include) {\n if (!relationFieldNames.has(field)) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_INCLUDE_FIELD',\n message: `'${field}' is not a valid relation field`,\n },\n },\n 400\n )\n }\n }\n\n const result = await repo.findMany({\n published_only: true,\n page: pagination.page,\n per_page: pagination.per_page,\n sort_by: sortBy as 'title' | 'created_at' | 'updated_at',\n sort_order: sortOrder as 'asc' | 'desc',\n filters: filtersResult.filters,\n include,\n })\n\n return c.json(result)\n })\n\n app.get(`/api/${basePath}/:slug`, async (c) => {\n const slug = c.req.param('slug')\n\n const include = parseInclude(c.req.query('include'))\n for (const field of include) {\n if (!relationFieldNames.has(field)) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_INCLUDE_FIELD',\n message: `'${field}' is not a valid relation field`,\n },\n },\n 400\n )\n }\n }\n\n const item = await repo.findBySlug(slug, include)\n\n if (!item || !isPublished(item)) {\n return c.json(\n {\n ok: false,\n error: { code: 'SLUG_NOT_FOUND', message: `No item found with slug '${slug}'` },\n },\n 404\n )\n }\n\n return c.json({ ok: true, data: item })\n })\n }\n }\n\n for (const [typeName] of Object.entries(registry.taxonomy_types)) {\n const repo = repos[typeName]\n if (!repo) continue\n\n registerListRoute(`/api/taxonomy/${typeName}`, async (c) => {\n const pagination = parsePagination(c.req.query('page'), c.req.query('per_page'))\n if (!pagination.ok) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_PAGINATION',\n message: 'page must be ≥ 1 and per_page must be between 1 and 100',\n },\n },\n 400\n )\n }\n\n const result = await repo.findMany({\n published_only: true,\n page: pagination.page,\n per_page: pagination.per_page,\n })\n\n return c.json(result)\n })\n\n app.get(`/api/taxonomy/${typeName}/:id`, async (c) => {\n const id = c.req.param('id')\n const item = await repo.findOne(id)\n\n if (!item || !isPublished(item)) {\n return c.json(\n {\n ok: false,\n error: { code: 'NOT_FOUND', message: 'Taxonomy term not found' },\n },\n 404\n )\n }\n\n return c.json({ ok: true, data: item })\n })\n }\n}\n","import type { Hono, Handler, MiddlewareHandler } from 'hono'\nimport type { MediaRepository } from '@bobbykim/manguito-cms-core'\n\nconst VALID_MEDIA_TYPES = new Set(['image', 'video', 'file'])\n\nfunction parsePagination(\n pageStr: string | undefined,\n perPageStr: string | undefined\n): { ok: true; page: number; per_page: number } | { ok: false } {\n const page = pageStr !== undefined ? Number(pageStr) : 1\n const per_page = perPageStr !== undefined ? Number(perPageStr) : 10\n\n if (!Number.isInteger(page) || page < 1) return { ok: false }\n if (!Number.isInteger(per_page) || per_page < 1 || per_page > 100) return { ok: false }\n return { ok: true, page, per_page }\n}\n\nexport function registerPublicMediaRoutes(\n app: Hono,\n mediaRepo: MediaRepository,\n listRateLimit?: MiddlewareHandler\n): void {\n const mediaListHandler: Handler = async (c) => {\n const pagination = parsePagination(c.req.query('page'), c.req.query('per_page'))\n if (!pagination.ok) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_PAGINATION',\n message: 'page must be ≥ 1 and per_page must be between 1 and 100',\n },\n },\n 400\n )\n }\n\n const typeParam = c.req.query('type')\n if (typeParam !== undefined && !VALID_MEDIA_TYPES.has(typeParam)) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: `type must be one of: image, video, file`,\n },\n },\n 422\n )\n }\n\n const opts: Parameters<typeof mediaRepo.findMany>[0] = {\n page: pagination.page,\n per_page: pagination.per_page,\n }\n if (typeParam !== undefined) {\n opts.type = typeParam as 'image' | 'video' | 'file'\n }\n\n const result = await mediaRepo.findMany(opts)\n return c.json(result)\n }\n\n if (listRateLimit) {\n app.get('/api/media', listRateLimit, mediaListHandler)\n } else {\n app.get('/api/media', mediaListHandler)\n }\n\n app.get('/api/media/:id', async (c) => {\n const id = c.req.param('id')\n const item = await mediaRepo.findOne(id)\n\n if (!item) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Media item not found' } },\n 404\n )\n }\n\n return c.json({ ok: true, data: item })\n })\n}\n","import { sql } from 'drizzle-orm'\nimport type { Hono } from 'hono'\nimport type {\n SchemaRegistry,\n MediaRepository,\n FilterValue,\n ParsedField,\n} from '@bobbykim/manguito-cms-core'\nimport {\n SORTABLE_FIELDS,\n RELATION_FIELD_TYPES,\n parsePagination,\n parseInclude,\n parseFilters,\n} from '../query-params.js'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport {\n topLevelMediaDelta,\n mergeMediaDeltas,\n applyMediaReferenceDelta,\n type MediaDelta,\n} from '../../media-references.js'\nimport {\n persistParagraphField,\n deleteParagraphField,\n persistJunctionField,\n} from '../../relations.js'\nimport type { createPermissionMiddleware } from '../../middleware/permission.js'\nimport type { ContentRepos } from '../content.js'\n\n// ─── SQL helpers ─────────────────────────────────────────────────────────────\n\nfunction quoteIdent(name: string): string {\n if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`)\n return `\"${name}\"`\n}\n\nasync function lookupBasePathId(db: DrizzlePostgresInstance, pathOrName: string): Promise<string | null> {\n const r = await db.execute(\n sql`SELECT id FROM base_paths WHERE path = ${pathOrName} OR name = ${pathOrName} LIMIT 1`\n )\n return (r.rows[0] as { id: string } | undefined)?.id ?? null\n}\n\n// ─── Shared query-param helpers ───────────────────────────────────────────────\n\nconst SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/\n\n// ─── Validation helpers ───────────────────────────────────────────────────────\n\nfunction isEmpty(value: unknown): boolean {\n if (value === null || value === undefined) return true\n if (typeof value === 'string' && value.trim() === '') return true\n if (Array.isArray(value) && value.length === 0) return true\n return false\n}\n\nfunction checkRequiredFields(\n fields: ParsedField[],\n data: Record<string, unknown>\n): { field: string; message: string }[] {\n return fields\n .filter((f) => f.required && isEmpty(data[f.name]))\n .map((f) => ({ field: f.name, message: `${f.label} is required` }))\n}\n\n// Extract IDs from already-resolved media field values ({ id: string } objects).\n// Admin repos are created without relation resolution (see app.ts) so edit forms get\n// plain foreign-key id strings back — media fields here are raw ids, not resolved\n// { id, url, ... } objects.\n// ─── List query helpers (shared between content and taxonomy list routes) ─────\n\ntype ListQueryResult =\n | {\n ok: true\n pagination: { page: number; per_page: number }\n sortBy: string\n sortOrder: string\n filters: Record<string, FilterValue>\n include: string[]\n search: string\n }\n | { ok: false; response: { code: string; message: string }; status: 400 }\n\nfunction parseListQuery(\n url: string,\n schemaFieldNames: Set<string>,\n relationFieldNames: Set<string>\n): ListQueryResult {\n const searchParams = new URL(url).searchParams\n\n const pagination = parsePagination(\n searchParams.get('page') ?? undefined,\n searchParams.get('per_page') ?? undefined\n )\n if (!pagination.ok) {\n return {\n ok: false,\n response: {\n code: 'INVALID_PAGINATION',\n message: 'page must be ≥ 1 and per_page must be between 1 and 100',\n },\n status: 400,\n }\n }\n\n const sortBy = searchParams.get('sort_by') ?? 'created_at'\n if (!SORTABLE_FIELDS.has(sortBy)) {\n return {\n ok: false,\n response: {\n code: 'INVALID_SORT_FIELD',\n message: `'${sortBy}' is not sortable. Allowed: title, created_at, updated_at`,\n },\n status: 400,\n }\n }\n\n const sortOrder = searchParams.get('sort_order') ?? 'asc'\n if (sortOrder !== 'asc' && sortOrder !== 'desc') {\n return {\n ok: false,\n response: { code: 'INVALID_SORT_FIELD', message: `sort_order must be 'asc' or 'desc'` },\n status: 400,\n }\n }\n\n const filtersResult = parseFilters(url, schemaFieldNames)\n if (!filtersResult.ok) {\n return {\n ok: false,\n response: {\n code: 'INVALID_FILTER_FIELD',\n message: `Filter field '${filtersResult.invalidField}' does not exist on this schema`,\n },\n status: 400,\n }\n }\n\n const include = parseInclude(searchParams.get('include') ?? undefined)\n for (const field of include) {\n if (!relationFieldNames.has(field)) {\n return {\n ok: false,\n response: { code: 'INVALID_INCLUDE_FIELD', message: `'${field}' is not a valid relation field` },\n status: 400,\n }\n }\n }\n\n const search = (searchParams.get('search') ?? '').trim()\n\n return {\n ok: true,\n pagination,\n sortBy,\n sortOrder,\n filters: filtersResult.filters,\n include,\n search,\n }\n}\n\n// ─── Register ─────────────────────────────────────────────────────────────────\n\nexport function registerAdminContentRoutes(\n app: Hono,\n registry: SchemaRegistry,\n repos: ContentRepos,\n mediaRepo: MediaRepository,\n requirePermission: ReturnType<typeof createPermissionMiddleware>,\n db?: DrizzlePostgresInstance,\n): void {\n // ── Content type routes ───────────────────────────────────────────────────\n\n for (const [typeName, contentType] of Object.entries(registry.content_types)) {\n const basePath = `content/${typeName}`\n const repo = repos[typeName]\n if (!repo) continue\n\n const schemaFieldNames = new Set<string>([\n ...contentType.fields.map((f) => f.name),\n ...contentType.system_fields.map((f) => f.name),\n ])\n\n const relationFieldNames = new Set<string>(\n contentType.fields\n .filter((f) => RELATION_FIELD_TYPES.has(f.field_type))\n .map((f) => f.name)\n )\n\n const requiredFields = contentType.fields.filter((f) => f.required)\n\n const mediaFields = contentType.fields.filter(\n (f) => f.field_type === 'image' || f.field_type === 'video' || f.field_type === 'file'\n )\n\n const paragraphFieldDefs = contentType.fields.filter((f) => f.db_column === null)\n\n // Free-text search target — text/plain fields (mirrors the \"first text field is\n // the title\" convention the admin frontend already uses) plus slug, when present.\n // Singleton (only_one) content types have no slug column at all.\n const searchableColumns = [\n ...contentType.fields\n .filter((f) => f.field_type === 'text/plain' && f.db_column !== null)\n .map((f) => f.db_column!.column_name),\n ...(contentType.system_fields.some((f) => f.name === 'slug') ? ['slug'] : []),\n ]\n\n // GET /admin/api/{base_path}\n app.get(\n `/admin/api/${basePath}`,\n requirePermission('content:read'),\n async (c) => {\n const parsed = parseListQuery(c.req.url, schemaFieldNames, relationFieldNames)\n if (!parsed.ok) {\n return c.json({ ok: false, error: parsed.response }, parsed.status)\n }\n\n const publishedParam = c.req.query('published')\n const extraFilters: Record<string, FilterValue> = {}\n if (publishedParam === 'false') extraFilters['published'] = false\n\n const findOpts: Parameters<typeof repo.findMany>[0] = {\n page: parsed.pagination.page,\n per_page: parsed.pagination.per_page,\n sort_by: parsed.sortBy as 'title' | 'created_at' | 'updated_at',\n sort_order: parsed.sortOrder as 'asc' | 'desc',\n filters: { ...parsed.filters, ...extraFilters },\n include: parsed.include,\n }\n if (publishedParam === 'true') findOpts.published_only = true\n if (parsed.search !== '' && searchableColumns.length > 0) {\n findOpts.search = { term: parsed.search, columns: searchableColumns }\n }\n\n const result = await repo.findMany(findOpts)\n\n return c.json(result)\n }\n )\n\n // GET /admin/api/{base_path}/:id\n app.get(\n `/admin/api/${basePath}/:id`,\n requirePermission('content:read'),\n async (c) => {\n const id = c.req.param('id')\n const item = await repo.findOne(id)\n\n if (!item) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Not found' } },\n 404\n )\n }\n\n // Resolve paragraph and junction fields so the edit form loads real data.\n // This deliberately does NOT reuse the relation module's resolvers: the\n // edit read scopes paragraph rows by parent_id AND parent_field, whereas\n // resolveRelationField scopes by parent_id only. Two paragraph fields of\n // the same paragraph type would be mixed by the resolver — so the more\n // precise per-field read stays here on purpose.\n if (db) {\n const row = item as Record<string, unknown>\n for (const f of contentType.fields) {\n if (f.db_column === null) {\n const comp = f.ui_component as { component: string; ref?: string }\n if (comp.component !== 'paragraph-embed' || !comp.ref) continue\n const pType = registry.paragraph_types[comp.ref]\n if (!pType) continue\n const r = await db.execute(\n sql`SELECT * FROM ${sql.raw(quoteIdent(pType.db.table_name))} WHERE parent_id = ${id} AND parent_field = ${f.name} ORDER BY \"order\" ASC`\n )\n row[f.name] = r.rows\n } else if (f.db_column.junction) {\n const j = f.db_column.junction\n const r = await db.execute(\n sql`SELECT ${sql.raw(quoteIdent(j.right_column))} FROM ${sql.raw(quoteIdent(j.table_name))} WHERE ${sql.raw(quoteIdent(j.left_column))} = ${id}`\n )\n row[f.name] = r.rows.map(\n (row) => (row as Record<string, unknown>)[j.right_column] as string\n )\n }\n }\n }\n\n return c.json({ ok: true, data: item })\n }\n )\n\n // POST /admin/api/{base_path}\n app.post(\n `/admin/api/${basePath}`,\n requirePermission('content:create'),\n async (c) => {\n const body = (await c.req.json()) as Record<string, unknown>\n\n if (contentType.only_one) {\n const existing = await repo.findMany({ page: 1, per_page: 1 })\n if (existing.meta.total > 0) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'SINGLETON_ALREADY_EXISTS',\n message: `Only one instance of '${contentType.label}' is allowed`,\n },\n },\n 409\n )\n }\n } else {\n const slug = body['slug']\n if (typeof slug !== 'string' || slug.trim() === '') {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: 'Required fields are missing',\n details: [{ field: 'slug', message: 'Slug is required' }],\n },\n },\n 422\n )\n }\n\n if (!SLUG_PATTERN.test(slug)) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_SLUG_FORMAT',\n message:\n 'Slug must be lowercase alphanumeric with hyphens only — no leading or trailing hyphens',\n },\n },\n 422\n )\n }\n\n const conflict = await repo.findBySlug(slug)\n if (conflict) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'SLUG_CONFLICT',\n message: `Slug '${slug}' already exists for this content type`,\n },\n },\n 409\n )\n }\n }\n\n const fieldErrors = checkRequiredFields(requiredFields, body)\n if (fieldErrors.length > 0) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: 'Required fields are missing',\n details: fieldErrors,\n },\n },\n 422\n )\n }\n\n if (body['published'] === true) {\n const publishDeny = await requirePermission('content:edit')(c, async () => {})\n if (publishDeny) return publishDeny\n }\n\n // Classify fields\n const paragraphFields = contentType.fields.filter((f) => f.db_column === null)\n const junctionFields = contentType.fields.filter(\n (f) => f.db_column !== null && f.db_column.junction !== undefined\n )\n const columnFields = contentType.fields.filter(\n (f) => f.db_column !== null && f.db_column.junction === undefined\n )\n\n // Build column-only data\n const columnData: Record<string, unknown> = {\n published: body['published'] ?? false,\n }\n if (contentType.only_one) {\n columnData['slug'] = typeName\n } else {\n columnData['slug'] = body['slug']\n }\n for (const f of columnFields) {\n const val = body[f.name]\n columnData[f.db_column!.column_name] = val !== undefined ? val : null\n }\n\n // Resolve base_path_id (seeded from routes.json at startup)\n if (db) {\n const basePathId = await lookupBasePathId(db, contentType.default_base_path)\n if (!basePathId) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'BASE_PATH_NOT_FOUND',\n message: `Base path '${contentType.default_base_path}' not found — run manguito migrate`,\n },\n },\n 500\n )\n }\n columnData['base_path_id'] = basePathId\n }\n\n const item = await repo.create(columnData as Parameters<typeof repo.create>[0])\n const itemId = (item as Record<string, unknown>)['id'] as string\n\n // Reconcile media reference counts across top-level fields and paragraphs.\n const mediaDeltas: MediaDelta[] = [topLevelMediaDelta(mediaFields, null, body)]\n\n // Save paragraph and junction rows\n if (db) {\n for (const f of paragraphFields) {\n const comp = f.ui_component as { component: string; ref?: string }\n if (comp.component !== 'paragraph-embed' || !comp.ref) continue\n const pType = registry.paragraph_types[comp.ref]\n if (!pType) continue\n const items = Array.isArray(body[f.name]) ? (body[f.name] as unknown[]) : []\n mediaDeltas.push(await persistParagraphField(db, itemId, contentType.db.table_name, f.name, pType, items))\n }\n for (const f of junctionFields) {\n const junction = f.db_column!.junction!\n const relatedIds = Array.isArray(body[f.name])\n ? (body[f.name] as unknown[]).filter((v): v is string => typeof v === 'string')\n : []\n await persistJunctionField(db, itemId, junction, relatedIds)\n }\n }\n\n await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo)\n\n return c.json({ ok: true, data: item }, 201)\n }\n )\n\n // PATCH /admin/api/{base_path}/:id\n app.patch(\n `/admin/api/${basePath}/:id`,\n requirePermission('content:edit'),\n async (c) => {\n const id = c.req.param('id')\n const body = (await c.req.json()) as Record<string, unknown>\n\n const existing = await repo.findOne(id)\n if (!existing) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Not found' } },\n 404\n )\n }\n\n if (!contentType.only_one && 'slug' in body) {\n const slug = body['slug']\n if (typeof slug !== 'string' || slug.trim() === '') {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: 'Required fields are missing',\n details: [{ field: 'slug', message: 'Slug cannot be empty' }],\n },\n },\n 422\n )\n }\n\n if (!SLUG_PATTERN.test(slug)) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_SLUG_FORMAT',\n message:\n 'Slug must be lowercase alphanumeric with hyphens only — no leading or trailing hyphens',\n },\n },\n 422\n )\n }\n\n const conflict = await repo.findBySlug(slug)\n if (conflict && (conflict as Record<string, unknown>)['id'] !== id) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'SLUG_CONFLICT',\n message: `Slug '${slug}' already exists for this content type`,\n },\n },\n 409\n )\n }\n }\n\n if (body['published'] === true) {\n const publishDeny = await requirePermission('content:edit')(c, async () => {})\n if (publishDeny) return publishDeny\n\n const merged = { ...(existing as Record<string, unknown>), ...body }\n const fieldErrors = checkRequiredFields(requiredFields, merged)\n if (fieldErrors.length > 0) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'PUBLISH_VALIDATION_ERROR',\n message: 'Cannot publish — required fields are missing',\n details: fieldErrors,\n },\n },\n 422\n )\n }\n }\n\n // Classify fields\n const patchParagraphFields = contentType.fields.filter((f) => f.db_column === null)\n const patchJunctionFields = contentType.fields.filter(\n (f) => f.db_column !== null && f.db_column.junction !== undefined\n )\n const patchColumnFields = contentType.fields.filter(\n (f) => f.db_column !== null && f.db_column.junction === undefined\n )\n\n // Build column-only patch data\n const patchData: Record<string, unknown> = {}\n if (!contentType.only_one && 'slug' in body) patchData['slug'] = body['slug']\n if ('published' in body) patchData['published'] = body['published']\n for (const f of patchColumnFields) {\n if (f.name in body) {\n const val = body[f.name]\n patchData[f.db_column!.column_name] = val !== undefined ? val : null\n }\n }\n\n const updated = await repo.update(id, patchData as Parameters<typeof repo.update>[1])\n if (!updated) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Not found' } },\n 404\n )\n }\n\n // Reconcile media reference counts across top-level fields and paragraphs.\n const mediaDeltas: MediaDelta[] = [\n topLevelMediaDelta(mediaFields, existing as Record<string, unknown>, body),\n ]\n\n // Delete+reinsert paragraph and junction rows\n if (db) {\n for (const f of patchParagraphFields) {\n const comp = f.ui_component as { component: string; ref?: string }\n if (comp.component !== 'paragraph-embed' || !comp.ref) continue\n const pType = registry.paragraph_types[comp.ref]\n if (!pType) continue\n const items = Array.isArray(body[f.name]) ? (body[f.name] as unknown[]) : []\n mediaDeltas.push(await persistParagraphField(db, id, contentType.db.table_name, f.name, pType, items))\n }\n for (const f of patchJunctionFields) {\n const junction = f.db_column!.junction!\n const relatedIds = Array.isArray(body[f.name])\n ? (body[f.name] as unknown[]).filter((v): v is string => typeof v === 'string')\n : []\n await persistJunctionField(db, id, junction, relatedIds)\n }\n }\n\n await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo)\n\n return c.json({ ok: true, data: updated })\n }\n )\n\n // DELETE /admin/api/{base_path}/:id\n app.delete(\n `/admin/api/${basePath}/:id`,\n requirePermission('content:delete'),\n async (c) => {\n const id = c.req.param('id')\n const item = await repo.findOne(id)\n\n if (!item) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Not found' } },\n 404\n )\n }\n\n // A delete removes every media reference the item held (top-level + paragraphs).\n const mediaDeltas: MediaDelta[] = [\n topLevelMediaDelta(mediaFields, item as Record<string, unknown>, null),\n ]\n\n // Paragraph rows have no FK/cascade back to their parent — clean them up\n // explicitly so they don't leak, and collect their media for decrementing.\n if (db) {\n for (const f of paragraphFieldDefs) {\n const comp = f.ui_component as { component: string; ref?: string }\n if (comp.component !== 'paragraph-embed' || !comp.ref) continue\n const pType = registry.paragraph_types[comp.ref]\n if (!pType) continue\n mediaDeltas.push(await deleteParagraphField(db, id, f.name, pType))\n }\n }\n\n await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo)\n\n await repo.delete(id)\n return c.json({ ok: true })\n }\n )\n }\n\n // ── Taxonomy type routes ──────────────────────────────────────────────────\n\n for (const [typeName, taxonomyType] of Object.entries(registry.taxonomy_types)) {\n const repo = repos[typeName]\n if (!repo) continue\n\n const schemaFieldNames = new Set<string>([\n ...taxonomyType.fields.map((f) => f.name),\n ...taxonomyType.system_fields.map((f) => f.name),\n ])\n\n const relationFieldNames = new Set<string>(\n taxonomyType.fields\n .filter((f) => RELATION_FIELD_TYPES.has(f.field_type))\n .map((f) => f.name)\n )\n\n const requiredFields = taxonomyType.fields.filter((f) => f.required)\n\n const mediaFields = taxonomyType.fields.filter(\n (f) => f.field_type === 'image' || f.field_type === 'video' || f.field_type === 'file'\n )\n\n const paragraphFieldDefs = taxonomyType.fields.filter((f) => f.db_column === null)\n\n const searchableColumns = [\n ...taxonomyType.fields\n .filter((f) => f.field_type === 'text/plain' && f.db_column !== null)\n .map((f) => f.db_column!.column_name),\n ...(taxonomyType.system_fields.some((f) => f.name === 'slug') ? ['slug'] : []),\n ]\n\n // GET /admin/api/taxonomy/{type}\n app.get(\n `/admin/api/taxonomy/${typeName}`,\n requirePermission('content:read'),\n async (c) => {\n const parsed = parseListQuery(c.req.url, schemaFieldNames, relationFieldNames)\n if (!parsed.ok) {\n return c.json({ ok: false, error: parsed.response }, parsed.status)\n }\n\n const publishedParam = c.req.query('published')\n const extraFilters: Record<string, FilterValue> = {}\n if (publishedParam === 'false') extraFilters['published'] = false\n\n const findOpts: Parameters<typeof repo.findMany>[0] = {\n page: parsed.pagination.page,\n per_page: parsed.pagination.per_page,\n sort_by: parsed.sortBy as 'title' | 'created_at' | 'updated_at',\n sort_order: parsed.sortOrder as 'asc' | 'desc',\n filters: { ...parsed.filters, ...extraFilters },\n include: parsed.include,\n }\n if (publishedParam === 'true') findOpts.published_only = true\n if (parsed.search !== '' && searchableColumns.length > 0) {\n findOpts.search = { term: parsed.search, columns: searchableColumns }\n }\n\n const result = await repo.findMany(findOpts)\n\n return c.json(result)\n }\n )\n\n // GET /admin/api/taxonomy/{type}/:id\n app.get(\n `/admin/api/taxonomy/${typeName}/:id`,\n requirePermission('content:read'),\n async (c) => {\n const id = c.req.param('id')\n const item = await repo.findOne(id)\n\n if (!item) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Taxonomy term not found' } },\n 404\n )\n }\n\n return c.json({ ok: true, data: item })\n }\n )\n\n // POST /admin/api/taxonomy/{type}\n app.post(\n `/admin/api/taxonomy/${typeName}`,\n requirePermission('content:create'),\n async (c) => {\n const body = (await c.req.json()) as Record<string, unknown>\n\n const fieldErrors = checkRequiredFields(requiredFields, body)\n if (fieldErrors.length > 0) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: 'Required fields are missing',\n details: fieldErrors,\n },\n },\n 422\n )\n }\n\n if (body['published'] === true) {\n const publishDeny = await requirePermission('content:edit')(c, async () => {})\n if (publishDeny) return publishDeny\n }\n\n // Filter to column-only fields\n const taxColumnFields = taxonomyType.fields.filter(\n (f) => f.db_column !== null && f.db_column.junction === undefined\n )\n const taxParagraphFields = taxonomyType.fields.filter((f) => f.db_column === null)\n const taxColumnData: Record<string, unknown> = {\n published: body['published'] ?? false,\n }\n for (const f of taxColumnFields) {\n const val = body[f.name]\n taxColumnData[f.db_column!.column_name] = val !== undefined ? val : null\n }\n\n const item = await repo.create(taxColumnData as Parameters<typeof repo.create>[0])\n const taxItemId = (item as Record<string, unknown>)['id'] as string\n\n // Reconcile media reference counts across top-level fields and paragraphs.\n const mediaDeltas: MediaDelta[] = [topLevelMediaDelta(mediaFields, null, body)]\n\n if (db) {\n for (const f of taxParagraphFields) {\n const comp = f.ui_component as { component: string; ref?: string }\n if (comp.component !== 'paragraph-embed' || !comp.ref) continue\n const pType = registry.paragraph_types[comp.ref]\n if (!pType) continue\n const items = Array.isArray(body[f.name]) ? (body[f.name] as unknown[]) : []\n mediaDeltas.push(await persistParagraphField(db, taxItemId, taxonomyType.db.table_name, f.name, pType, items))\n }\n }\n\n await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo)\n\n return c.json({ ok: true, data: item }, 201)\n }\n )\n\n // PATCH /admin/api/taxonomy/{type}/:id\n app.patch(\n `/admin/api/taxonomy/${typeName}/:id`,\n requirePermission('content:edit'),\n async (c) => {\n const id = c.req.param('id')\n const body = (await c.req.json()) as Record<string, unknown>\n\n const existing = await repo.findOne(id)\n if (!existing) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Taxonomy term not found' } },\n 404\n )\n }\n\n if (body['published'] === true) {\n const publishDeny = await requirePermission('content:edit')(c, async () => {})\n if (publishDeny) return publishDeny\n\n const merged = { ...(existing as Record<string, unknown>), ...body }\n const fieldErrors = checkRequiredFields(requiredFields, merged)\n if (fieldErrors.length > 0) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'PUBLISH_VALIDATION_ERROR',\n message: 'Cannot publish — required fields are missing',\n details: fieldErrors,\n },\n },\n 422\n )\n }\n }\n\n // Filter to column-only fields\n const taxPatchColumnFields = taxonomyType.fields.filter(\n (f) => f.db_column !== null && f.db_column.junction === undefined\n )\n const taxPatchParagraphFields = taxonomyType.fields.filter((f) => f.db_column === null)\n const taxPatchData: Record<string, unknown> = {}\n if ('published' in body) taxPatchData['published'] = body['published']\n for (const f of taxPatchColumnFields) {\n if (f.name in body) {\n const val = body[f.name]\n taxPatchData[f.db_column!.column_name] = val !== undefined ? val : null\n }\n }\n\n const updated = await repo.update(id, taxPatchData as Parameters<typeof repo.update>[1])\n if (!updated) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Taxonomy term not found' } },\n 404\n )\n }\n\n // Reconcile media reference counts across top-level fields and paragraphs.\n const mediaDeltas: MediaDelta[] = [\n topLevelMediaDelta(mediaFields, existing as Record<string, unknown>, body),\n ]\n\n if (db) {\n for (const f of taxPatchParagraphFields) {\n const comp = f.ui_component as { component: string; ref?: string }\n if (comp.component !== 'paragraph-embed' || !comp.ref) continue\n const pType = registry.paragraph_types[comp.ref]\n if (!pType) continue\n const items = Array.isArray(body[f.name]) ? (body[f.name] as unknown[]) : []\n mediaDeltas.push(await persistParagraphField(db, id, taxonomyType.db.table_name, f.name, pType, items))\n }\n }\n\n await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo)\n\n return c.json({ ok: true, data: updated })\n }\n )\n\n // DELETE /admin/api/taxonomy/{type}/:id\n app.delete(\n `/admin/api/taxonomy/${typeName}/:id`,\n requirePermission('content:delete'),\n async (c) => {\n const id = c.req.param('id')\n const item = await repo.findOne(id)\n\n if (!item) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Taxonomy term not found' } },\n 404\n )\n }\n\n // A delete removes every media reference the item held (top-level + paragraphs).\n const mediaDeltas: MediaDelta[] = [\n topLevelMediaDelta(mediaFields, item as Record<string, unknown>, null),\n ]\n\n if (db) {\n for (const f of paragraphFieldDefs) {\n const comp = f.ui_component as { component: string; ref?: string }\n if (comp.component !== 'paragraph-embed' || !comp.ref) continue\n const pType = registry.paragraph_types[comp.ref]\n if (!pType) continue\n mediaDeltas.push(await deleteParagraphField(db, id, f.name, pType))\n }\n }\n\n await applyMediaReferenceDelta(mergeMediaDeltas(...mediaDeltas), mediaRepo)\n\n await repo.delete(id)\n return c.json({ ok: true })\n }\n )\n }\n\n // ── Config endpoint ───────────────────────────────────────────────────────\n\n app.get('/admin/api/config', (c) => {\n return c.json({\n ok: true,\n data: {\n content_types: Object.values(registry.content_types).map((ct) => ({\n name: ct.name,\n label: ct.label,\n only_one: ct.only_one,\n default_base_path: ct.default_base_path,\n fields: ct.fields,\n system_fields: ct.system_fields,\n })),\n taxonomy_types: Object.values(registry.taxonomy_types).map((tt) => ({\n name: tt.name,\n label: tt.label,\n fields: tt.fields,\n system_fields: tt.system_fields,\n })),\n enum_types: Object.values(registry.enum_types).map((et) => ({\n name: et.name,\n label: et.label,\n values: et.values,\n })),\n },\n })\n })\n}\n","import type { MediaRepository, ParsedField } from '@bobbykim/manguito-cms-core'\n\n// ─── Media reference tracking ─────────────────────────────────────────────────\n//\n// Reconciles media.reference_count on a content write. reference_count counts\n// *content items* that reference a media row (see CONTEXT.md \"Orphaned media\"\n// and docs/adr/api/0007), not reference slots — so a media id used by several\n// fields/paragraphs of one item counts once, and an id moved between slots in\n// one write is a no-op.\n//\n// A write produces a MediaDelta of the ids it gains and loses. Top-level field\n// deltas are computed here (pure); paragraph deltas are produced by the paragraph\n// persistence path and merged in. The merged delta is applied once.\n\nexport type MediaDelta = {\n added: string[]\n removed: string[]\n}\n\n// A media field holds a raw id string (not a resolved { id, url } object) on the\n// content/paragraph row. Empty string and non-strings mean \"no media\".\nfunction extractMediaId(value: unknown): string | null {\n return typeof value === 'string' && value !== '' ? value : null\n}\n\n// Media ids gained and lost between two content states for the top-level media\n// fields. Unifies all three writes:\n// create — before = null (no prior ids; set fields are added)\n// update — before/after are rows (a field absent from `after` is untouched)\n// delete — after = null (every current id is removed)\nexport function topLevelMediaDelta(\n mediaFields: ParsedField[],\n before: Record<string, unknown> | null,\n after: Record<string, unknown> | null\n): MediaDelta {\n const added: string[] = []\n const removed: string[] = []\n\n for (const f of mediaFields) {\n // On a partial update, a media field not present in the patch body is untouched.\n // (after === null is a delete — every field is in scope.)\n if (after !== null && !(f.name in after)) continue\n\n const oldId = before ? extractMediaId(before[f.name]) : null\n const newId = after ? extractMediaId(after[f.name]) : null\n if (newId === oldId) continue\n if (oldId) removed.push(oldId)\n if (newId) added.push(newId)\n }\n\n return { added, removed }\n}\n\n// Combine the top-level delta with each paragraph field's delta into one delta.\nexport function mergeMediaDeltas(...deltas: MediaDelta[]): MediaDelta {\n return {\n added: deltas.flatMap((d) => d.added),\n removed: deltas.flatMap((d) => d.removed),\n }\n}\n\n// Apply a delta to reference counts, per content item:\n// - dedup each side (an id referenced by many slots counts once)\n// - cancel ids that are both added and removed (moved between slots → no-op)\n// - skip the repo call when a side is empty\nexport async function applyMediaReferenceDelta(\n delta: MediaDelta,\n mediaRepo: MediaRepository\n): Promise<void> {\n const added = new Set(delta.added)\n const removed = new Set(delta.removed)\n\n const netAdd = [...added].filter((id) => !removed.has(id))\n const netRemove = [...removed].filter((id) => !added.has(id))\n\n if (netAdd.length > 0) await mediaRepo.incrementReferenceCount(netAdd)\n if (netRemove.length > 0) await mediaRepo.decrementReferenceCount(netRemove)\n}\n","import crypto from 'node:crypto'\nimport { sql } from 'drizzle-orm'\nimport type { ParsedField, ParsedParagraphType, SchemaRegistry } from '@bobbykim/manguito-cms-core'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport type { MediaDelta } from './media-references.js'\n\n// ─── Relation persistence ─────────────────────────────────────────────────────\n//\n// Owns paragraph/junction/reference/media relation table SQL — both the write\n// side (persist/delete below) and the read side (descriptors + resolvers further\n// down). Each persist/delete returns the MediaDelta of media ids it gained and\n// lost (see media-references.ts), which the caller merges and reconciles once.\n// The repository's resolveRows() drives the read resolvers.\n\nfunction quoteIdent(name: string): string {\n if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`)\n return `\"${name}\"`\n}\n\n// Paragraph rows live on a child table with their own image/video/file columns\n// (e.g. a \"photo card\" block embeds an image) — these need the same reference-count\n// bookkeeping as top-level content fields, one level down.\nfunction paragraphMediaFieldNames(pType: ParsedParagraphType): Set<string> {\n return new Set(\n pType.fields\n .filter((f) => f.field_type === 'image' || f.field_type === 'video' || f.field_type === 'file')\n .map((f) => f.name)\n )\n}\n\nasync function fetchParagraphMediaIds(\n db: DrizzlePostgresInstance,\n pType: ParsedParagraphType,\n itemId: string,\n fieldName: string\n): Promise<string[]> {\n const mediaColumns = pType.fields\n .filter((f) => f.field_type === 'image' || f.field_type === 'video' || f.field_type === 'file')\n .map((f) => f.db_column!.column_name)\n if (mediaColumns.length === 0) return []\n\n const tbl = sql.raw(quoteIdent(pType.db.table_name))\n const cols = sql.join(mediaColumns.map((c) => sql.raw(quoteIdent(c))), sql`, `)\n const result = await db.execute(\n sql`SELECT ${cols} FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`\n )\n\n const ids: string[] = []\n for (const row of result.rows as Record<string, unknown>[]) {\n for (const col of mediaColumns) {\n const v = row[col]\n if (typeof v === 'string' && v !== '') ids.push(v)\n }\n }\n return ids\n}\n\n// Replaces every paragraph row for one field of one parent (delete + reinsert),\n// returning the media ids removed (old rows) and added (new rows).\nexport async function persistParagraphField(\n db: DrizzlePostgresInstance,\n itemId: string,\n parentType: string,\n fieldName: string,\n pType: ParsedParagraphType,\n items: unknown[]\n): Promise<MediaDelta> {\n const removed = await fetchParagraphMediaIds(db, pType, itemId, fieldName)\n\n const tbl = sql.raw(quoteIdent(pType.db.table_name))\n await db.execute(sql`DELETE FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`)\n\n const mediaFieldNames = paragraphMediaFieldNames(pType)\n const added: string[] = []\n\n for (let i = 0; i < items.length; i++) {\n const pItem = items[i] as Record<string, unknown>\n const data: Record<string, unknown> = {\n id: crypto.randomUUID(),\n parent_id: itemId,\n parent_type: parentType,\n parent_field: fieldName,\n order: i,\n created_at: new Date(),\n updated_at: new Date(),\n }\n for (const pf of pType.fields) {\n if (!pf.db_column || pf.db_column.junction) continue\n data[pf.db_column.column_name] = pItem[pf.name] ?? null\n }\n const cols = sql.join(Object.keys(data).map((k) => sql.raw(quoteIdent(k))), sql`, `)\n const vals = sql.join(Object.values(data).map((v) => sql`${v}`), sql`, `)\n await db.execute(sql`INSERT INTO ${tbl} (${cols}) VALUES (${vals})`)\n\n for (const name of mediaFieldNames) {\n const val = pItem[name]\n if (typeof val === 'string' && val !== '') added.push(val)\n }\n }\n\n return { added, removed }\n}\n\n// Deletes all paragraph rows for one field of one parent (used when the parent is\n// being deleted — paragraph rows have no FK/cascade back to their parent, so they'd\n// leak otherwise). Returns the media those rows referenced, all removed.\nexport async function deleteParagraphField(\n db: DrizzlePostgresInstance,\n itemId: string,\n fieldName: string,\n pType: ParsedParagraphType\n): Promise<MediaDelta> {\n const removed = await fetchParagraphMediaIds(db, pType, itemId, fieldName)\n const tbl = sql.raw(quoteIdent(pType.db.table_name))\n await db.execute(sql`DELETE FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`)\n return { added: [], removed }\n}\n\n// Replaces every junction row for one many-to-many field of one parent.\nexport async function persistJunctionField(\n db: DrizzlePostgresInstance,\n itemId: string,\n junction: { table_name: string; left_column: string; right_column: string },\n relatedIds: string[]\n): Promise<void> {\n const tbl = sql.raw(quoteIdent(junction.table_name))\n const left = sql.raw(quoteIdent(junction.left_column))\n const right = sql.raw(quoteIdent(junction.right_column))\n await db.execute(sql`DELETE FROM ${tbl} WHERE ${left} = ${itemId}`)\n for (const rid of relatedIds) {\n await db.execute(sql`INSERT INTO ${tbl} (${left}, ${right}) VALUES (${itemId}, ${rid})`)\n }\n}\n\n// ─── Relation descriptors and resolution (read side) ──────────────────────────\n//\n// Relation read resolution. These were lifted out of the repository so that all\n// paragraph/junction/reference/media table SQL lives in one module alongside the\n// write persistence above. The repository's resolveRows() calls into here.\n\nexport type ParagraphRelationDef = {\n type: 'paragraph'\n table: string\n}\n\nexport type ReferenceRelationDef = {\n type: 'reference'\n table: string\n fk_column: string\n}\n\nexport type JunctionRelationDef = {\n type: 'junction'\n table: string\n junction_table: string\n left_column: string\n right_column: string\n // Only ordered junctions have an \"order\" column (db codegen emits it only when\n // order_column is true) — the resolver must not ORDER BY it otherwise.\n order_column: boolean\n}\n\nexport type MediaRelationDef = {\n type: 'media'\n fk_column: string\n}\n\nexport type RelationDef =\n | ParagraphRelationDef\n | ReferenceRelationDef\n | JunctionRelationDef\n | MediaRelationDef\n\n// Derives a content type's relations map from its parsed fields, so paragraph/\n// reference/media fields are actually resolved instead of silently dropped.\n// Junction and foreign_key info is already on each field's db_column; paragraph\n// fields store their target table on the paragraph type, hence the registry.\nexport function buildRelationsMap(\n fields: ParsedField[],\n registry: SchemaRegistry\n): Record<string, RelationDef> {\n const relations: Record<string, RelationDef> = {}\n\n for (const field of fields) {\n if (field.field_type === 'image' || field.field_type === 'video' || field.field_type === 'file') {\n if (!field.db_column) continue\n relations[field.name] = { type: 'media', fk_column: field.db_column.column_name }\n } else if (field.field_type === 'paragraph') {\n const ref = (field.ui_component as { ref?: string }).ref\n const pType = ref ? registry.paragraph_types[ref] : undefined\n if (!pType) continue\n relations[field.name] = { type: 'paragraph', table: pType.db.table_name }\n } else if (field.field_type === 'reference') {\n if (!field.db_column) continue\n if (field.db_column.junction) {\n const j = field.db_column.junction\n relations[field.name] = {\n type: 'junction',\n table: j.right_table,\n junction_table: j.table_name,\n left_column: j.left_column,\n right_column: j.right_column,\n order_column: j.order_column,\n }\n } else if (field.db_column.foreign_key) {\n relations[field.name] = {\n type: 'reference',\n table: field.db_column.foreign_key.table,\n fk_column: field.db_column.column_name,\n }\n }\n }\n }\n\n return relations\n}\n\nfunction groupBy<T extends Record<string, unknown>>(\n items: T[],\n key: string\n): Record<string, T[]> {\n const result: Record<string, T[]> = {}\n for (const item of items) {\n const k = String(item[key])\n if (!result[k]) result[k] = []\n result[k]!.push(item)\n }\n return result\n}\n\n// Fully resolves a relation field on a batch of rows: paragraph → ordered rows,\n// reference → the target row, junction → target rows, media → the media row.\n// The cache (keyed table:id) dedupes target lookups within a request.\nexport async function resolveRelationField(\n db: DrizzlePostgresInstance,\n rows: Record<string, unknown>[],\n fieldName: string,\n rel: RelationDef,\n cache: Map<string, unknown>\n): Promise<void> {\n if (rows.length === 0) return\n\n if (rel.type === 'paragraph') {\n const parentIds = rows.map((r) => r['id'] as string)\n const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `)\n const result = await db.execute(\n sql`SELECT * FROM ${sql.raw(quoteIdent(rel.table))} WHERE parent_id IN (${inList}) ORDER BY \"order\" ASC`\n )\n const byParent = groupBy(result.rows as Record<string, unknown>[], 'parent_id')\n for (const row of rows) {\n row[fieldName] = byParent[row['id'] as string] ?? []\n }\n } else if (rel.type === 'reference') {\n const fkValues = rows.map((r) => r[rel.fk_column] as string).filter(Boolean)\n if (fkValues.length === 0) {\n for (const row of rows) row[fieldName] = null\n return\n }\n const unique = [...new Set(fkValues)]\n const uncached = unique.filter((id) => !cache.has(`${rel.table}:${id}`))\n if (uncached.length > 0) {\n const inList = sql.join(uncached.map((id) => sql`${id}`), sql`, `)\n const result = await db.execute(\n sql`SELECT * FROM ${sql.raw(quoteIdent(rel.table))} WHERE id IN (${inList})`\n )\n for (const item of result.rows as Record<string, unknown>[]) {\n cache.set(`${rel.table}:${item['id']}`, item)\n }\n }\n for (const row of rows) {\n const fkVal = row[rel.fk_column] as string\n row[fieldName] = fkVal ? (cache.get(`${rel.table}:${fkVal}`) ?? null) : null\n }\n } else if (rel.type === 'junction') {\n const parentIds = rows.map((r) => r['id'] as string)\n const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `)\n const orderBy = rel.order_column ? sql` ORDER BY \"order\" ASC` : sql``\n const junctionResult = await db.execute(\n sql`SELECT * FROM ${sql.raw(quoteIdent(rel.junction_table))} WHERE ${sql.raw(quoteIdent(rel.left_column))} IN (${inList})${orderBy}`\n )\n const jRows = junctionResult.rows as Record<string, unknown>[]\n const rightIds = [...new Set(jRows.map((r) => r[rel.right_column] as string))]\n const uncached = rightIds.filter((id) => !cache.has(`${rel.table}:${id}`))\n if (uncached.length > 0) {\n const rightInList = sql.join(uncached.map((id) => sql`${id}`), sql`, `)\n const entitiesResult = await db.execute(\n sql`SELECT * FROM ${sql.raw(quoteIdent(rel.table))} WHERE id IN (${rightInList})`\n )\n for (const item of entitiesResult.rows as Record<string, unknown>[]) {\n cache.set(`${rel.table}:${item['id']}`, item)\n }\n }\n const byLeft = groupBy(jRows, rel.left_column)\n for (const row of rows) {\n const jEntries = byLeft[row['id'] as string] ?? []\n row[fieldName] = jEntries\n .map((jr) => cache.get(`${rel.table}:${jr[rel.right_column] as string}`))\n .filter(Boolean)\n }\n } else if (rel.type === 'media') {\n const fkValues = rows.map((r) => r[rel.fk_column] as string).filter(Boolean)\n if (fkValues.length === 0) {\n for (const row of rows) row[fieldName] = null\n return\n }\n const unique = [...new Set(fkValues)]\n const uncached = unique.filter((id) => !cache.has(`media:${id}`))\n if (uncached.length > 0) {\n const inList = sql.join(uncached.map((id) => sql`${id}`), sql`, `)\n const result = await db.execute(sql`SELECT * FROM \"media\" WHERE id IN (${inList})`)\n for (const item of result.rows as Record<string, unknown>[]) {\n cache.set(`media:${item['id']}`, item)\n }\n }\n for (const row of rows) {\n const fkVal = row[rel.fk_column] as string\n row[fieldName] = fkVal ? (cache.get(`media:${fkVal}`) ?? null) : null\n }\n }\n}\n\n// Populates paragraph/junction fields with bare ID arrays when they are not\n// explicitly included — they have no column on the owning table, so without this\n// they'd be silently absent. Reference fields already carry their bare ID as a\n// real column from `SELECT *`, so they need no extra query.\nexport async function resolveRelationBareIds(\n db: DrizzlePostgresInstance,\n rows: Record<string, unknown>[],\n fieldName: string,\n rel: RelationDef\n): Promise<void> {\n if (rows.length === 0) return\n\n if (rel.type === 'paragraph') {\n const parentIds = rows.map((r) => r['id'] as string)\n const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `)\n const result = await db.execute(\n sql`SELECT id, parent_id FROM ${sql.raw(quoteIdent(rel.table))} WHERE parent_id IN (${inList}) ORDER BY \"order\" ASC`\n )\n const byParent = groupBy(result.rows as Record<string, unknown>[], 'parent_id')\n for (const row of rows) {\n row[fieldName] = (byParent[row['id'] as string] ?? []).map((r) => r['id'])\n }\n } else if (rel.type === 'junction') {\n const parentIds = rows.map((r) => r['id'] as string)\n const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `)\n const result = await db.execute(\n sql`SELECT ${sql.raw(quoteIdent(rel.left_column))} AS left_id, ${sql.raw(quoteIdent(rel.right_column))} AS right_id FROM ${sql.raw(quoteIdent(rel.junction_table))} WHERE ${sql.raw(quoteIdent(rel.left_column))} IN (${inList})`\n )\n const byLeft = groupBy(result.rows as Record<string, unknown>[], 'left_id')\n for (const row of rows) {\n row[fieldName] = (byLeft[row['id'] as string] ?? []).map((r) => r['right_id'])\n }\n }\n}\n","// Admin media routes — upload, presigned URL, update, delete\nimport type { Context, Hono } from 'hono'\nimport type { MediaRepository, StorageAdapter } from '@bobbykim/manguito-cms-core'\nimport { sign, verify } from 'hono/jwt'\nimport type { createPermissionMiddleware } from '../../middleware/permission.js'\n\n// ─── Accepted MIME types ──────────────────────────────────────────────────────\n\nconst IMAGE_MIME_TYPES = new Set([\n 'image/jpeg',\n 'image/png',\n 'image/webp',\n 'image/gif',\n])\n\nconst VIDEO_MIME_TYPES = new Set(['video/mp4', 'video/webm', 'video/quicktime'])\n\nconst FILE_MIME_TYPES = new Set(['application/pdf'])\n\n// ─── Pending upload token ─────────────────────────────────────────────────────\n// The presigned-url request and the confirm request can land on different\n// serverless instances, so pending-upload state cannot live in memory. Encode it\n// in a short-lived token signed with AUTH_SECRET — stateless and tamper-proof —\n// and hand it to the client as media_id; confirm verifies it.\n\ntype PendingUpload = {\n key: string\n folder: 'image' | 'video' | 'file'\n mime_type: string\n}\n\nfunction authSecret(): string {\n const secret = process.env['AUTH_SECRET']\n if (!secret) throw new Error('AUTH_SECRET environment variable is not set')\n return secret\n}\n\nasync function signPendingUpload(data: PendingUpload, expiresAt: number): Promise<string> {\n return sign({ ...data, exp: expiresAt }, authSecret())\n}\n\nasync function verifyPendingUpload(token: string): Promise<PendingUpload | null> {\n try {\n const p = await verify(token, authSecret(), 'HS256') as {\n key: string\n folder: 'image' | 'video' | 'file'\n mime_type: string\n }\n return { key: p.key, folder: p.folder, mime_type: p.mime_type }\n } catch {\n return null\n }\n}\n\n\nasync function handleDirectUpload(\n folder: 'image' | 'video' | 'file',\n acceptedTypes: Set<string>,\n mediaRepo: MediaRepository,\n storage: StorageAdapter,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n c: Context<any, any, any>,\n altRequired: boolean,\n maxFileSize: number | undefined\n): Promise<Response> {\n // Direct uploads buffer the whole file in the CMS server's memory (unlike the\n // presigned flow, which uploads straight to storage). Reject oversized bodies\n // up front via Content-Length so we never buffer them — and keep max_file_size\n // under the platform payload limit on serverless (Lambda ~6 MB).\n if (maxFileSize !== undefined) {\n const contentLength = Number(c.req.header('content-length'))\n if (Number.isFinite(contentLength) && contentLength > maxFileSize) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'FILE_TOO_LARGE',\n message: `Upload exceeds the maximum size of ${maxFileSize} bytes`,\n },\n },\n 413\n )\n }\n }\n\n let formData: FormData\n try {\n formData = await c.req.formData()\n } catch {\n return c.json(\n { ok: false, error: { code: 'VALIDATION_ERROR', message: 'Expected multipart/form-data' } },\n 422\n )\n }\n\n const fileField = formData.get('file')\n if (!(fileField instanceof File)) {\n return c.json(\n { ok: false, error: { code: 'VALIDATION_ERROR', message: 'file field is required' } },\n 422\n )\n }\n\n // Defensive second check: Content-Length may be absent (chunked) or count\n // multipart overhead, so also reject on the actual decoded file size.\n if (maxFileSize !== undefined && fileField.size > maxFileSize) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'FILE_TOO_LARGE',\n message: `File exceeds the maximum size of ${maxFileSize} bytes`,\n },\n },\n 413\n )\n }\n\n const mimeType = fileField.type\n if (!acceptedTypes.has(mimeType)) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'UNSUPPORTED_MIME_TYPE',\n message: `MIME type '${mimeType}' is not accepted by this endpoint`,\n },\n },\n 415\n )\n }\n\n const altValue = formData.get('alt')\n const alt = typeof altValue === 'string' && altValue.trim() !== '' ? altValue.trim() : undefined\n\n if (altRequired && !alt) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: 'alt is required for this media type',\n details: [{ field: 'alt', message: 'alt is required' }],\n },\n },\n 422\n )\n }\n\n let presigned: Awaited<ReturnType<StorageAdapter['getPresignedUploadUrl']>>\n try {\n presigned = await storage.getPresignedUploadUrl({\n folder,\n filename: fileField.name,\n mime_type: mimeType,\n })\n } catch {\n return c.json(\n { ok: false, error: { code: 'STORAGE_ERROR', message: 'Failed to generate upload URL' } },\n 502\n )\n }\n\n if (storage.upload) {\n try {\n const bytes = new Uint8Array(await fileField.arrayBuffer())\n await storage.upload(presigned.key, bytes, mimeType)\n } catch (err) {\n console.error('[media] storage.upload error:', err)\n return c.json(\n { ok: false, error: { code: 'STORAGE_ERROR', message: 'Storage upload failed' } },\n 502\n )\n }\n }\n\n const url = storage.getUrl(presigned.key)\n const fileSize = fileField.size\n\n const item = await mediaRepo.create({\n type: folder,\n url,\n mime_type: mimeType,\n ...(alt !== undefined && { alt }),\n file_size: fileSize,\n })\n\n return c.json({ ok: true, data: item }, 201)\n}\n\n// ─── Register ─────────────────────────────────────────────────────────────────\n\nexport function registerAdminMediaRoutes(\n app: Hono,\n mediaRepo: MediaRepository,\n storage: StorageAdapter,\n requirePermission: ReturnType<typeof createPermissionMiddleware>,\n maxFileSize: number | undefined,\n): void {\n // POST /admin/api/media/image\n app.post(\n '/admin/api/media/image',\n requirePermission('media:create'),\n async (c) => {\n return handleDirectUpload('image', IMAGE_MIME_TYPES, mediaRepo, storage, c, false, maxFileSize)\n }\n )\n\n // POST /admin/api/media/video\n app.post(\n '/admin/api/media/video',\n requirePermission('media:create'),\n async (c) => {\n return handleDirectUpload('video', VIDEO_MIME_TYPES, mediaRepo, storage, c, true, maxFileSize)\n }\n )\n\n // POST /admin/api/media/file\n app.post(\n '/admin/api/media/file',\n requirePermission('media:create'),\n async (c) => {\n return handleDirectUpload('file', FILE_MIME_TYPES, mediaRepo, storage, c, true, maxFileSize)\n }\n )\n\n // GET /admin/api/media/presigned-url\n app.get('/admin/api/media/presigned-url', requirePermission('media:create'), async (c) => {\n const type = c.req.query('type') as 'image' | 'video' | 'file' | undefined\n const filename = c.req.query('filename')\n const mimeType = c.req.query('mime_type')\n\n if (!type || !['image', 'video', 'file'].includes(type)) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: 'type must be one of: image, video, file',\n details: [{ field: 'type', message: 'type is required' }],\n },\n },\n 422\n )\n }\n\n if (!filename || filename.trim() === '') {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: 'filename is required',\n details: [{ field: 'filename', message: 'filename is required' }],\n },\n },\n 422\n )\n }\n\n if (!mimeType || mimeType.trim() === '') {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: 'mime_type is required',\n details: [{ field: 'mime_type', message: 'mime_type is required' }],\n },\n },\n 422\n )\n }\n\n const accepted =\n type === 'image' ? IMAGE_MIME_TYPES : type === 'video' ? VIDEO_MIME_TYPES : FILE_MIME_TYPES\n\n if (!accepted.has(mimeType)) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'UNSUPPORTED_MIME_TYPE',\n message: `MIME type '${mimeType}' is not accepted for ${type} uploads`,\n },\n },\n 415\n )\n }\n\n let presigned: Awaited<ReturnType<StorageAdapter['getPresignedUploadUrl']>>\n try {\n presigned = await storage.getPresignedUploadUrl({\n folder: type,\n filename,\n mime_type: mimeType,\n })\n } catch {\n return c.json(\n { ok: false, error: { code: 'STORAGE_ERROR', message: 'Failed to generate upload URL' } },\n 502\n )\n }\n\n const media_id = await signPendingUpload(\n { key: presigned.key, folder: type, mime_type: mimeType },\n presigned.expires_at,\n )\n\n return c.json({\n ok: true,\n data: {\n upload_url: presigned.upload_url,\n // Signed, self-contained token the client posts back to /confirm/:id.\n media_id,\n expires_at: presigned.expires_at,\n // Present for storages that need a multipart POST with signed fields\n // (Cloudinary); absent for a raw PUT (S3).\n ...(presigned.method && { method: presigned.method }),\n ...(presigned.fields && { fields: presigned.fields }),\n },\n })\n })\n\n // POST /admin/api/media/confirm/:id\n app.post('/admin/api/media/confirm/:id', requirePermission('media:create'), async (c) => {\n const id = c.req.param('id')\n\n const pending = await verifyPendingUpload(id)\n if (!pending) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'PRESIGNED_URL_EXPIRED',\n message: 'Presigned upload not found or has expired',\n },\n },\n 410\n )\n }\n\n const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>\n const altValue = typeof body['alt'] === 'string' ? body['alt'].trim() : undefined\n\n const altRequired = pending.folder === 'video' || pending.folder === 'file'\n if (altRequired && !altValue) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: 'alt is required for video and file uploads',\n details: [{ field: 'alt', message: 'alt is required' }],\n },\n },\n 422\n )\n }\n\n // Validate the actual uploaded object where the adapter supports it.\n let fileSize = 0\n if (storage.stat) {\n const meta = await storage.stat(pending.key)\n if (!meta) {\n return c.json(\n { ok: false, error: { code: 'STORAGE_ERROR', message: 'Uploaded object not found' } },\n 502,\n )\n }\n const accepted =\n pending.folder === 'image' ? IMAGE_MIME_TYPES\n : pending.folder === 'video' ? VIDEO_MIME_TYPES\n : FILE_MIME_TYPES\n if (meta.content_type && !accepted.has(meta.content_type)) {\n await storage.delete(pending.key)\n return c.json(\n { ok: false, error: { code: 'UNSUPPORTED_MIME_TYPE', message: `Stored object type '${meta.content_type}' is not accepted` } },\n 415,\n )\n }\n if (maxFileSize !== undefined && meta.size > maxFileSize) {\n await storage.delete(pending.key)\n return c.json(\n { ok: false, error: { code: 'FILE_TOO_LARGE', message: `Uploaded file exceeds the ${maxFileSize} byte limit` } },\n 413,\n )\n }\n fileSize = meta.size\n }\n\n const url = storage.getUrl(pending.key)\n\n const item = await mediaRepo.create({\n type: pending.folder,\n url,\n mime_type: pending.mime_type,\n ...(altValue !== undefined && { alt: altValue }),\n file_size: fileSize,\n })\n\n return c.json({ ok: true, data: item }, 201)\n })\n\n // PATCH /admin/api/media/:id\n app.patch(\n '/admin/api/media/:id',\n requirePermission('media:edit'),\n async (c) => {\n const id = c.req.param('id')\n const existing = await mediaRepo.findOne(id)\n\n if (!existing) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Media item not found' } },\n 404\n )\n }\n\n const body = (await c.req.json()) as Record<string, unknown>\n const alt = typeof body['alt'] === 'string' ? body['alt'].trim() : undefined\n\n const updateData: Parameters<typeof mediaRepo.update>[1] = {}\n if (alt !== undefined) updateData.alt = alt\n\n const updated = await mediaRepo.update(id, updateData)\n if (!updated) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Media item not found' } },\n 404\n )\n }\n\n return c.json({ ok: true, data: updated })\n }\n )\n\n // DELETE /admin/api/media/:id\n app.delete(\n '/admin/api/media/:id',\n requirePermission('media:delete'),\n async (c) => {\n const id = c.req.param('id')\n const item = await mediaRepo.findOne(id)\n\n if (!item) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Media item not found' } },\n 404\n )\n }\n\n if (item.reference_count > 0) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'MEDIA_IN_USE',\n message: 'Cannot delete media that is referenced by content items',\n },\n },\n 409\n )\n }\n\n // Derive storage key from URL.\n // Cloudinary delivery URLs: https://res.cloudinary.com/{cloud}/{type}/upload/{public_id}\n // Local delivery URLs: http://localhost/uploads/{key}\n // For Cloudinary, the adapter key is the public_id — everything after \"/upload/\".\n // For local (and S3), the adapter key is the full path after the host.\n const pathname = new URL(item.url).pathname\n const uploadIdx = pathname.indexOf('/upload/')\n const key = uploadIdx >= 0\n ? pathname.slice(uploadIdx + '/upload/'.length)\n : pathname.replace(/^\\//, '')\n\n try {\n await storage.delete(key)\n } catch (err) {\n console.error('[media] storage.delete error:', err)\n return c.json(\n {\n ok: false,\n error: { code: 'STORAGE_ERROR', message: 'Failed to delete file from storage' },\n },\n 502\n )\n }\n\n await mediaRepo.delete(id)\n\n return c.json({ ok: true })\n }\n )\n\n // GET /admin/api/media\n app.get('/admin/api/media', requirePermission('media:read'), async (c) => {\n const pageStr = c.req.query('page')\n const perPageStr = c.req.query('per_page')\n const page = pageStr !== undefined ? Number(pageStr) : 1\n const per_page = perPageStr !== undefined ? Number(perPageStr) : 10\n\n if (\n !Number.isInteger(page) ||\n page < 1 ||\n !Number.isInteger(per_page) ||\n per_page < 1 ||\n per_page > 100\n ) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_PAGINATION',\n message: 'page must be ≥ 1 and per_page must be between 1 and 100',\n },\n },\n 400\n )\n }\n\n const typeParam = c.req.query('type')\n if (typeParam !== undefined && !['image', 'video', 'file'].includes(typeParam)) {\n return c.json(\n {\n ok: false,\n error: { code: 'VALIDATION_ERROR', message: 'type must be one of: image, video, file' },\n },\n 422\n )\n }\n\n const orphanedParam = c.req.query('orphaned')\n const orphaned =\n orphanedParam === 'true' ? true : orphanedParam === 'false' ? false : undefined\n\n const opts: Parameters<typeof mediaRepo.findMany>[0] = { page, per_page }\n if (typeParam !== undefined) opts.type = typeParam as 'image' | 'video' | 'file'\n if (orphaned !== undefined) opts.orphaned = orphaned\n\n const result = await mediaRepo.findMany(opts)\n\n return c.json(result)\n })\n\n // GET /admin/api/media/:id\n app.get('/admin/api/media/:id', requirePermission('media:read'), async (c) => {\n const id = c.req.param('id')\n const item = await mediaRepo.findOne(id)\n\n if (!item) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'Media item not found' } },\n 404\n )\n }\n\n return c.json({ ok: true, data: item })\n })\n\n}\n","import type { Hono } from 'hono'\nimport { getCookie } from 'hono/cookie'\nimport { sql } from 'drizzle-orm'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport { verifyPassword } from '../../auth/password.js'\nimport {\n signToken,\n verifyToken,\n setAuthCookie,\n clearAuthCookie,\n AUTH_TOKEN_TTL,\n REFRESH_TOKEN_TTL,\n} from '../../auth/jwt.js'\nimport { createAuthMiddleware } from '../../middleware/auth.js'\n\n// ─── Rate limiting ────────────────────────────────────────────────────────────\n\nconst loginAttempts = new Map<string, number[]>()\nconst globalLoginAttempts: number[] = []\n\nconst RATE_LIMIT_MAX = 10\nconst RATE_LIMIT_WINDOW_MS = 15 * 60 * 1000\n// Global backstop across all IPs/emails — blunts distributed spraying without\n// per-account lockout (ADR api/0005). Sized well above legitimate concurrent\n// login volume for a self-hosted CMS.\nconst GLOBAL_LOGIN_MAX = 100\n\n// A valid cost-12 bcrypt hash used only to equalize timing on the\n// user-not-found path, so login response time cannot enumerate accounts.\nconst DUMMY_PASSWORD_HASH =\n '$2a$12$y.MZw/Q4Ceg3x1y3xrRaeuTaM09zSDx1nn78guO3bqc9vgOqGda42'\n\nfunction checkRateLimit(key: string): { allowed: boolean; retryAfterSeconds: number } {\n const now = Date.now()\n const windowStart = now - RATE_LIMIT_WINDOW_MS\n\n // Global ceiling first — purge, then evaluate before recording the per-key hit.\n while (globalLoginAttempts.length > 0 && globalLoginAttempts[0]! <= windowStart) {\n globalLoginAttempts.shift()\n }\n if (globalLoginAttempts.length >= GLOBAL_LOGIN_MAX) {\n const retryAfterSeconds = Math.ceil((globalLoginAttempts[0]! + RATE_LIMIT_WINDOW_MS - now) / 1000)\n return { allowed: false, retryAfterSeconds }\n }\n\n const attempts = (loginAttempts.get(key) ?? []).filter((t) => t > windowStart)\n attempts.push(now)\n loginAttempts.set(key, attempts)\n globalLoginAttempts.push(now)\n\n if (attempts.length > RATE_LIMIT_MAX) {\n const oldestInWindow = attempts[0]!\n const retryAfterSeconds = Math.ceil((oldestInWindow + RATE_LIMIT_WINDOW_MS - now) / 1000)\n return { allowed: false, retryAfterSeconds }\n }\n\n return { allowed: true, retryAfterSeconds: 0 }\n}\n\n/** Test-only: clears in-process login rate-limit state. Not part of the public API. */\nexport function __resetLoginRateLimitStateForTests(): void {\n loginAttempts.clear()\n globalLoginAttempts.length = 0\n}\n\n// ─── DB row types ─────────────────────────────────────────────────────────────\n\ntype LoginUserRow = {\n id: string\n email: string\n password_hash: string\n role_id: string\n token_version: number\n}\n\ntype RefreshRow = {\n token_version: number\n role: string\n}\n\n// ─── Register ─────────────────────────────────────────────────────────────────\n\nexport function registerAuthRoutes(app: Hono, db: DrizzlePostgresInstance): void {\n const invalidCredentials = {\n ok: false,\n error: { code: 'INVALID_CREDENTIALS', message: 'Invalid email or password.' },\n } as const\n\n // POST /admin/api/auth/login\n app.post('/admin/api/auth/login', async (c) => {\n let body: { email?: unknown; password?: unknown }\n try {\n body = (await c.req.json()) as { email?: unknown; password?: unknown }\n } catch {\n return c.json(invalidCredentials, 401)\n }\n\n const email =\n typeof body.email === 'string' ? body.email.trim().toLowerCase() : null\n const password = typeof body.password === 'string' ? body.password : null\n\n if (!email || !password) {\n return c.json(invalidCredentials, 401)\n }\n\n const ip =\n c.req.header('x-forwarded-for')?.split(',')[0]?.trim() ?? 'unknown'\n const rateCheck = checkRateLimit(`${ip}:${email}`)\n if (!rateCheck.allowed) {\n c.header('Retry-After', String(rateCheck.retryAfterSeconds))\n return c.json(\n {\n ok: false,\n error: {\n code: 'RATE_LIMITED',\n message: 'Too many login attempts. Please try again later.',\n },\n },\n 429,\n )\n }\n\n const userResult = await db.execute(\n sql`SELECT id, email, password_hash, role_id, token_version\n FROM users WHERE email = ${email} LIMIT 1`,\n )\n const user = userResult.rows[0] as LoginUserRow | undefined\n\n if (!user) {\n // Run a dummy comparison so this path pays the same bcrypt cost as a\n // real user, closing the user-enumeration timing side-channel.\n await verifyPassword(password, DUMMY_PASSWORD_HASH)\n return c.json(invalidCredentials, 401)\n }\n\n const passwordValid = await verifyPassword(password, user.password_hash)\n if (!passwordValid) {\n return c.json(invalidCredentials, 401)\n }\n\n const roleResult = await db.execute(\n sql`SELECT r.name AS name, u.must_change_password\n FROM roles r\n JOIN users u ON u.role_id = r.id\n WHERE r.id = ${user.role_id} AND u.id = ${user.id}\n LIMIT 1`,\n )\n const roleRow = roleResult.rows[0] as { name: string; must_change_password: boolean } | undefined\n\n if (!roleRow) {\n return c.json(\n { ok: false, error: { code: 'INVALID_ROLE', message: 'User role not found.' } },\n 500,\n )\n }\n\n const [authToken, refreshToken] = await Promise.all([\n signToken(\n { user_id: user.id, role: roleRow.name, token_version: user.token_version },\n AUTH_TOKEN_TTL,\n ),\n signToken(\n { user_id: user.id, role: roleRow.name, token_version: user.token_version },\n REFRESH_TOKEN_TTL,\n ),\n ])\n\n setAuthCookie(c, 'auth_token', authToken, { path: '/' })\n setAuthCookie(c, 'refresh_token', refreshToken, { path: '/admin/api/auth' })\n\n return c.json({\n ok: true,\n data: {\n id: user.id,\n email: user.email,\n role: roleRow.name,\n must_change_password: roleRow.must_change_password,\n },\n })\n })\n\n // POST /admin/api/auth/refresh\n app.post('/admin/api/auth/refresh', async (c) => {\n const token = getCookie(c, 'refresh_token')\n if (!token) {\n return c.json(\n { ok: false, error: { code: 'UNAUTHORIZED', message: 'Authentication required.' } },\n 401,\n )\n }\n\n let payload\n try {\n payload = await verifyToken(token)\n } catch (err) {\n const code = (err as { code?: string }).code\n if (code === 'TOKEN_EXPIRED') {\n return c.json(\n { ok: false, error: { code: 'TOKEN_EXPIRED', message: 'Token has expired.' } },\n 401,\n )\n }\n return c.json(\n { ok: false, error: { code: 'TOKEN_INVALID', message: 'Token signature is invalid.' } },\n 401,\n )\n }\n\n const rowResult = await db.execute(\n sql`SELECT u.token_version, r.name AS role\n FROM users u\n JOIN roles r ON r.id = u.role_id\n WHERE u.id = ${payload.user_id}\n LIMIT 1`,\n )\n const row = rowResult.rows[0] as RefreshRow | undefined\n\n if (!row || payload.token_version !== row.token_version) {\n return c.json(\n { ok: false, error: { code: 'TOKEN_INVALID', message: 'Token has been invalidated.' } },\n 401,\n )\n }\n\n const newAuthToken = await signToken(\n { user_id: payload.user_id, role: row.role, token_version: row.token_version },\n AUTH_TOKEN_TTL,\n )\n setAuthCookie(c, 'auth_token', newAuthToken, { path: '/' })\n\n return c.json({ ok: true })\n })\n\n // POST /admin/api/auth/logout\n const authMiddleware = createAuthMiddleware(db)\n app.post('/admin/api/auth/logout', authMiddleware, async (c) => {\n const user = (c as unknown as { get(k: 'user'): { id: string; role: string } }).get('user')\n\n await db.execute(\n sql`UPDATE users SET token_version = token_version + 1 WHERE id = ${user.id}`,\n )\n\n clearAuthCookie(c, 'auth_token')\n clearAuthCookie(c, 'refresh_token', '/admin/api/auth')\n\n return c.json({ ok: true })\n })\n}\n","export { hashPassword, verifyPassword } from '@bobbykim/manguito-cms-core'\n","import type { Hono } from 'hono'\nimport { sql } from 'drizzle-orm'\nimport { randomBytes } from 'node:crypto'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport { hashPassword, verifyPassword } from '../../auth/password.js'\nimport type { createPermissionMiddleware } from '../../middleware/permission.js'\nimport type { createHierarchyMiddleware } from '../../middleware/hierarchy.js'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype ActingUser = { id: string; role: string }\n\ntype UserRow = {\n id: string\n email: string\n role: string\n must_change_password: boolean\n created_at: string\n updated_at: string\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction getActingUser(c: unknown): ActingUser {\n return (c as { get(k: 'user'): ActingUser }).get('user')\n}\n\nfunction generateTempPassword(): string {\n return randomBytes(8).toString('base64url')\n}\n\n// ─── Register ─────────────────────────────────────────────────────────────────\n\nexport function registerUserRoutes(\n app: Hono,\n db: DrizzlePostgresInstance,\n requirePermission: ReturnType<typeof createPermissionMiddleware>,\n requireHierarchy: ReturnType<typeof createHierarchyMiddleware>,\n): void {\n // POST /admin/api/users/change-password\n // Registered first — exempt from mustChangePasswordCheck (enforced by path check in that middleware).\n // No requirePermission — available to any authenticated user.\n app.post('/admin/api/users/change-password', async (c) => {\n const actingUser = getActingUser(c)\n\n let body: { current_password?: unknown; new_password?: unknown }\n try {\n body = (await c.req.json()) as { current_password?: unknown; new_password?: unknown }\n } catch {\n return c.json(\n { ok: false, error: { code: 'INVALID_CREDENTIALS', message: 'Invalid credentials.' } },\n 401,\n )\n }\n\n const currentPassword =\n typeof body.current_password === 'string' ? body.current_password : null\n const newPassword = typeof body.new_password === 'string' ? body.new_password : null\n\n if (!currentPassword || !newPassword) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'VALIDATION_ERROR',\n message: 'current_password and new_password are required.',\n },\n },\n 422,\n )\n }\n\n const hashResult = await db.execute(\n sql`SELECT password_hash FROM users WHERE id = ${actingUser.id} LIMIT 1`,\n )\n const hashRow = hashResult.rows[0] as { password_hash: string } | undefined\n\n if (!hashRow || !(await verifyPassword(currentPassword, hashRow.password_hash))) {\n return c.json(\n { ok: false, error: { code: 'INVALID_CREDENTIALS', message: 'Invalid credentials.' } },\n 401,\n )\n }\n\n const newHash = await hashPassword(newPassword)\n\n await db.execute(\n sql`UPDATE users\n SET password_hash = ${newHash},\n must_change_password = false,\n token_version = token_version + 1,\n updated_at = NOW()\n WHERE id = ${actingUser.id}`,\n )\n\n return c.json({ ok: true })\n })\n\n // GET /admin/api/users\n app.get('/admin/api/users', requirePermission('users:read'), async (c) => {\n const result = await db.execute(\n sql`SELECT u.id, u.email, r.name AS role, u.must_change_password,\n u.created_at, u.updated_at\n FROM users u\n JOIN roles r ON r.id = u.role_id\n ORDER BY u.created_at ASC`,\n )\n\n return c.json({ ok: true, data: result.rows as UserRow[] })\n })\n\n // GET /admin/api/users/:id\n app.get('/admin/api/users/:id', requirePermission('users:read'), async (c) => {\n const id = c.req.param('id')\n\n const result = await db.execute(\n sql`SELECT u.id, u.email, r.name AS role, u.must_change_password,\n u.created_at, u.updated_at\n FROM users u\n JOIN roles r ON r.id = u.role_id\n WHERE u.id = ${id}\n LIMIT 1`,\n )\n const row = result.rows[0] as UserRow | undefined\n\n if (!row) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'User not found.' } },\n 404,\n )\n }\n\n return c.json({ ok: true, data: row })\n })\n\n // POST /admin/api/users\n app.post(\n '/admin/api/users',\n requirePermission('users:create'),\n requireHierarchy(),\n async (c) => {\n let body: { email?: unknown; role?: unknown }\n try {\n body = (await c.req.json()) as { email?: unknown; role?: unknown }\n } catch {\n return c.json(\n { ok: false, error: { code: 'VALIDATION_ERROR', message: 'Request body is required.' } },\n 422,\n )\n }\n\n const email =\n typeof body.email === 'string' ? body.email.trim().toLowerCase() : null\n const roleName = typeof body.role === 'string' ? body.role : null\n\n if (!email) {\n return c.json(\n { ok: false, error: { code: 'VALIDATION_ERROR', message: 'email is required.' } },\n 422,\n )\n }\n if (!roleName) {\n return c.json(\n { ok: false, error: { code: 'INVALID_ROLE', message: 'role is required.' } },\n 400,\n )\n }\n\n const roleResult = await db.execute(\n sql`SELECT id FROM roles WHERE name = ${roleName} LIMIT 1`,\n )\n const roleRow = roleResult.rows[0] as { id: string } | undefined\n\n if (!roleRow) {\n return c.json(\n {\n ok: false,\n error: { code: 'INVALID_ROLE', message: `Role \"${roleName}\" does not exist.` },\n },\n 400,\n )\n }\n\n const tempPassword = generateTempPassword()\n const passwordHash = await hashPassword(tempPassword)\n\n const insertResult = await db.execute(\n sql`INSERT INTO users\n (id, email, password_hash, role_id, must_change_password, token_version,\n created_at, updated_at)\n VALUES\n (gen_random_uuid(), ${email}, ${passwordHash}, ${roleRow.id},\n true, 0, NOW(), NOW())\n RETURNING id, email, must_change_password, created_at, updated_at`,\n )\n const newUser = insertResult.rows[0] as\n | {\n id: string\n email: string\n must_change_password: boolean\n created_at: string\n updated_at: string\n }\n | undefined\n\n if (!newUser) {\n return c.json(\n { ok: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to create user.' } },\n 500,\n )\n }\n\n return c.json(\n {\n ok: true,\n data: {\n id: newUser.id,\n email: newUser.email,\n role: roleName,\n must_change_password: newUser.must_change_password,\n temporary_password: tempPassword,\n created_at: newUser.created_at,\n updated_at: newUser.updated_at,\n },\n },\n 201,\n )\n },\n )\n\n // PATCH /admin/api/users/:id\n app.patch(\n '/admin/api/users/:id',\n requirePermission('users:edit'),\n requireHierarchy(),\n async (c) => {\n const id = c.req.param('id')\n const actingUser = getActingUser(c)\n\n let body: { email?: unknown; role?: unknown }\n try {\n body = (await c.req.json()) as { email?: unknown; role?: unknown }\n } catch {\n body = {}\n }\n\n // Self role change is never permitted\n if (actingUser.id === id && body.role !== undefined) {\n return c.json(\n {\n ok: false,\n error: { code: 'INSUFFICIENT_PRIVILEGE', message: 'You cannot change your own role.' },\n },\n 403,\n )\n }\n\n const existingResult = await db.execute(\n sql`SELECT id, email, role_id FROM users WHERE id = ${id} LIMIT 1`,\n )\n const existing = existingResult.rows[0] as\n | { id: string; email: string; role_id: string }\n | undefined\n\n if (!existing) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'User not found.' } },\n 404,\n )\n }\n\n const newEmail =\n typeof body.email === 'string' ? body.email.trim().toLowerCase() : existing.email\n\n let newRoleId = existing.role_id\n let changeRole = false\n\n if (typeof body.role === 'string' && body.role) {\n const roleResult = await db.execute(\n sql`SELECT id FROM roles WHERE name = ${body.role} LIMIT 1`,\n )\n const roleRow = roleResult.rows[0] as { id: string } | undefined\n\n if (!roleRow) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INVALID_ROLE',\n message: `Role \"${body.role}\" does not exist.`,\n },\n },\n 400,\n )\n }\n\n newRoleId = roleRow.id\n changeRole = true\n }\n\n if (changeRole) {\n await db.execute(\n sql`UPDATE users\n SET email = ${newEmail},\n role_id = ${newRoleId},\n token_version = token_version + 1,\n updated_at = NOW()\n WHERE id = ${id}`,\n )\n } else {\n await db.execute(\n sql`UPDATE users\n SET email = ${newEmail},\n role_id = ${newRoleId},\n updated_at = NOW()\n WHERE id = ${id}`,\n )\n }\n\n const updatedResult = await db.execute(\n sql`SELECT u.id, u.email, r.name AS role, u.must_change_password,\n u.created_at, u.updated_at\n FROM users u\n JOIN roles r ON r.id = u.role_id\n WHERE u.id = ${id}\n LIMIT 1`,\n )\n const updated = updatedResult.rows[0] as UserRow | undefined\n\n if (!updated) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'User not found.' } },\n 404,\n )\n }\n\n return c.json({ ok: true, data: updated })\n },\n )\n\n // DELETE /admin/api/users/:id\n app.delete(\n '/admin/api/users/:id',\n requirePermission('users:delete'),\n requireHierarchy(),\n async (c) => {\n const id = c.req.param('id')\n const actingUser = getActingUser(c)\n\n if (actingUser.id === id) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INSUFFICIENT_PRIVILEGE',\n message: 'You cannot delete your own account.',\n },\n },\n 403,\n )\n }\n\n const result = await db.execute(\n sql`DELETE FROM users WHERE id = ${id} RETURNING id`,\n )\n\n if ((result.rows as unknown[]).length === 0) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'User not found.' } },\n 404,\n )\n }\n\n return c.json({ ok: true })\n },\n )\n\n // POST /admin/api/users/:id/reset-password\n app.post(\n '/admin/api/users/:id/reset-password',\n requirePermission('users:edit'),\n requireHierarchy(),\n async (c) => {\n const id = c.req.param('id')\n const actingUser = getActingUser(c)\n\n if (actingUser.id === id) {\n return c.json(\n {\n ok: false,\n error: {\n code: 'INSUFFICIENT_PRIVILEGE',\n message: 'You cannot reset your own password via this endpoint.',\n },\n },\n 403,\n )\n }\n\n const existingResult = await db.execute(\n sql`SELECT id FROM users WHERE id = ${id} LIMIT 1`,\n )\n const existing = existingResult.rows[0] as { id: string } | undefined\n\n if (!existing) {\n return c.json(\n { ok: false, error: { code: 'NOT_FOUND', message: 'User not found.' } },\n 404,\n )\n }\n\n const tempPassword = generateTempPassword()\n const passwordHash = await hashPassword(tempPassword)\n\n await db.execute(\n sql`UPDATE users\n SET password_hash = ${passwordHash},\n must_change_password = true,\n token_version = token_version + 1,\n updated_at = NOW()\n WHERE id = ${id}`,\n )\n\n return c.json({ ok: true, data: { temporary_password: tempPassword } })\n },\n )\n}\n","import { sql } from 'drizzle-orm'\nimport type { Hono } from 'hono'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport type { RolesRegistry } from '../../auth/registry.js'\n\n// ─── Version — injected by tsup at build time; falls back in test/dev ─────────\n\ndeclare const __API_VERSION__: string\n\nconst API_VERSION: string = (() => {\n try { return __API_VERSION__ } catch { return '0.0.0' }\n})()\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype ActingUser = { id: string; role: string }\n\ntype ConfigOptions = {\n name: string\n maxFileSize?: number\n presignedUploads?: boolean\n}\n\n// ─── Register ─────────────────────────────────────────────────────────────────\n\nexport function registerConfigRoute(\n app: Hono,\n config: ConfigOptions,\n registry: RolesRegistry,\n db: DrizzlePostgresInstance,\n): void {\n app.get('/admin/api/config', async (c) => {\n const actingUser = (c as unknown as { get(k: 'user'): ActingUser }).get('user')\n const actingRole = registry[actingUser.role]\n const actingLevel = actingRole?.hierarchy_level ?? Infinity\n\n // Subordinate roles only — used by user management forms for role assignment.\n const roles = Object.values(registry)\n .filter((r) => r.name !== 'admin' && r.hierarchy_level > actingLevel)\n .sort((a, b) => a.hierarchy_level - b.hierarchy_level)\n .map((r) => ({ name: r.name, label: r.label, hierarchy_level: r.hierarchy_level }))\n\n // All roles with full data — used by the admin panel to resolve permissions.\n const all_roles = Object.values(registry)\n .sort((a, b) => a.hierarchy_level - b.hierarchy_level)\n .map((r) => ({\n name: r.name,\n label: r.label,\n hierarchy_level: r.hierarchy_level,\n is_system: r.is_system,\n permissions: r.permissions,\n }))\n\n const userResult = await db.execute(\n sql`SELECT email, must_change_password FROM users WHERE id = ${actingUser.id} LIMIT 1`\n )\n const userRow = userResult.rows[0] as\n | { email: string; must_change_password: boolean }\n | undefined\n\n return c.json({\n ok: true,\n data: {\n cms_name: config.name ?? 'Manguito CMS',\n version: API_VERSION,\n roles,\n all_roles,\n user: {\n id: actingUser.id,\n email: userRow?.email ?? '',\n role: actingUser.role,\n must_change_password: userRow?.must_change_password ?? false,\n },\n media: {\n max_file_size: config.maxFileSize ?? null,\n presigned_uploads: config.presignedUploads ?? false,\n },\n },\n })\n })\n}\n","import { sql } from 'drizzle-orm'\nimport type { Hono } from 'hono'\nimport type { SchemaRegistry } from '@bobbykim/manguito-cms-core'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\n\n// ─── Register ─────────────────────────────────────────────────────────────────\n\nexport function registerSchemaRoute(\n app: Hono,\n registry: SchemaRegistry,\n db: DrizzlePostgresInstance,\n): void {\n // Full schema — used by the admin panel for form rendering, validation, and navigation.\n // Returns complete ParsedField objects (validation, ui_component) so the admin\n // forms can render inputs and run client-side validation without extra round-trips.\n app.get('/admin/api/schema', (c) => {\n const content_types = Object.values(registry.content_types).map((ct) => ({\n name: ct.name,\n label: ct.label,\n only_one: ct.only_one,\n ui: ct.ui,\n system_fields: ct.system_fields,\n fields: ct.fields,\n }))\n\n const taxonomy_types = Object.values(registry.taxonomy_types).map((tt) => ({\n name: tt.name,\n label: tt.label,\n system_fields: tt.system_fields,\n fields: tt.fields,\n }))\n\n const paragraph_types = Object.values(registry.paragraph_types).map((pt) => ({\n name: pt.name,\n label: pt.label,\n system_fields: pt.system_fields,\n fields: pt.fields,\n }))\n\n const enum_types = Object.values(registry.enum_types).map((et) => ({\n name: et.name,\n label: et.label,\n values: et.values,\n }))\n\n return c.json({\n ok: true,\n data: { content_types, taxonomy_types, paragraph_types, enum_types },\n })\n })\n\n // Navigation meta-endpoints — list types with item counts for admin sidebar\n app.get('/admin/api/content', async (c) => {\n const data = await Promise.all(\n Object.values(registry.content_types).map(async (ct) => {\n const result = await db.execute(\n sql`SELECT COUNT(*)::int AS count FROM ${sql.raw(`\"${ct.db.table_name}\"`)}`\n )\n const count = Number((result.rows[0] as { count: string | number }).count)\n return { name: ct.name, label: ct.label, only_one: ct.only_one, count }\n })\n )\n return c.json({ ok: true, data })\n })\n\n app.get('/admin/api/taxonomy', async (c) => {\n const data = await Promise.all(\n Object.values(registry.taxonomy_types).map(async (tt) => {\n const result = await db.execute(\n sql`SELECT COUNT(*)::int AS count FROM ${sql.raw(`\"${tt.db.table_name}\"`)}`\n )\n const count = Number((result.rows[0] as { count: string | number }).count)\n return { name: tt.name, label: tt.label, count }\n })\n )\n return c.json({ ok: true, data })\n })\n}\n","import crypto from 'node:crypto'\nimport { sql, type SQL } from 'drizzle-orm'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport type {\n ContentRepository,\n PaginatedResult,\n FindManyOptions,\n FindAllOptions,\n CreateInput,\n UpdateInput,\n FilterOperator,\n FilterValue,\n} from '@bobbykim/manguito-cms-core'\nimport {\n resolveRelationField,\n resolveRelationBareIds,\n type RelationDef,\n} from '../relations.js'\n\n// ─── Repository options ───────────────────────────────────────────────────────\n\nexport type ContentRepositoryOptions = {\n relations?: Record<string, RelationDef>\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nconst SORTABLE_FIELDS = new Set(['title', 'created_at', 'updated_at'])\n\nfunction codeError(code: string, message: string): Error {\n const err = new Error(message)\n ;(err as Error & { code: string }).code = code\n return err\n}\n\n// Only allow table/column identifiers that are developer-controlled.\n// Table names can include hyphens (e.g. content--blog_post).\nfunction quoteIdent(name: string): string {\n if (!/^[a-z][a-z0-9_-]*$/.test(name)) {\n throw new Error(`Unsafe SQL identifier: ${name}`)\n }\n return `\"${name.replace(/\"/g, '\"\"')}\"`\n}\n\n// User-supplied filter field names — strict, no hyphens.\nfunction quoteField(name: string): string {\n if (!/^[a-z][a-z0-9_]*$/.test(name)) {\n throw codeError('INVALID_FILTER_FIELD', `Invalid filter field name: ${name}`)\n }\n return `\"${name}\"`\n}\n\nfunction buildConditions(\n published_only: boolean | undefined,\n filters: Record<string, FilterValue> | undefined,\n search: { term: string; columns: string[] } | undefined\n): SQL[] {\n const conds: SQL[] = []\n\n if (published_only) {\n conds.push(sql`published = true`)\n }\n\n if (search && search.term.trim() !== '' && search.columns.length > 0) {\n const pattern = `%${search.term.trim()}%`\n const orConds = search.columns.map((col) => sql`${sql.raw(quoteField(col))} ILIKE ${pattern}`)\n conds.push(sql`(${sql.join(orConds, sql` OR `)})`)\n }\n\n if (filters) {\n for (const [field, value] of Object.entries(filters)) {\n const col = sql.raw(quoteField(field))\n\n if (Array.isArray(value)) {\n if (value.length === 0) continue\n const inList = sql.join(\n value.map((v) => sql`${v}`),\n sql`, `\n )\n conds.push(sql`${col} IN (${inList})`)\n } else if (\n typeof value === 'object' &&\n value !== null\n ) {\n const op = value as FilterOperator\n if (op.gt !== undefined) conds.push(sql`${col} > ${op.gt}`)\n if (op.gte !== undefined) conds.push(sql`${col} >= ${op.gte}`)\n if (op.lt !== undefined) conds.push(sql`${col} < ${op.lt}`)\n if (op.lte !== undefined) conds.push(sql`${col} <= ${op.lte}`)\n } else {\n conds.push(sql`${col} = ${value}`)\n }\n }\n }\n\n return conds\n}\n\nfunction whereFragment(conds: SQL[]): SQL {\n return conds.length > 0\n ? sql` WHERE ${sql.join(conds, sql` AND `)}`\n : sql``\n}\n\n// ─── Factory ──────────────────────────────────────────────────────────────────\n\nexport function createDrizzleContentRepository<T>(\n db: DrizzlePostgresInstance,\n tableName: string,\n options: ContentRepositoryOptions = {}\n): ContentRepository<T> {\n const { relations = {} } = options\n\n function tableRaw(): SQL {\n return sql.raw(quoteIdent(tableName))\n }\n\n async function resolveRows(\n rows: Record<string, unknown>[],\n include: string[],\n cache: Map<string, unknown>\n ): Promise<Record<string, unknown>[]> {\n if (rows.length === 0) return rows\n\n // Validate include fields\n for (const fieldName of include) {\n if (!relations[fieldName]) {\n throw codeError('INVALID_INCLUDE_FIELD', `'${fieldName}' is not a valid relation field`)\n }\n }\n\n // Always resolve media fields; for all other relations not explicitly\n // included, populate bare IDs so the field is never silently dropped.\n for (const [fieldName, rel] of Object.entries(relations)) {\n if (rel.type === 'media') {\n await resolveRelationField(db, rows, fieldName, rel, cache)\n } else if (!include.includes(fieldName)) {\n await resolveRelationBareIds(db, rows, fieldName, rel)\n }\n }\n\n // Resolve explicitly included non-media fields\n for (const fieldName of include) {\n const rel = relations[fieldName]!\n if (rel.type !== 'media') {\n await resolveRelationField(db, rows, fieldName, rel, cache)\n }\n }\n\n return rows\n }\n\n return {\n async findMany(opts: FindManyOptions): Promise<PaginatedResult<T>> {\n const {\n page = 1,\n per_page = 10,\n include = [],\n published_only,\n filters,\n sort_by = 'created_at',\n sort_order = 'asc',\n search,\n } = opts\n\n if (!SORTABLE_FIELDS.has(sort_by as string)) {\n throw codeError('INVALID_SORT_FIELD', `'${sort_by}' is not sortable. Allowed: title, created_at, updated_at`)\n }\n\n const offset = (page - 1) * per_page\n const conds = buildConditions(published_only, filters, search)\n const where = whereFragment(conds)\n const sortCol = sql.raw(quoteIdent(sort_by))\n const sortDir = sql.raw(sort_order === 'desc' ? 'DESC' : 'ASC')\n\n const countResult = await db.execute(\n sql`SELECT COUNT(*) AS total FROM ${tableRaw()}${where}`\n )\n const total = Number((countResult.rows[0] as Record<string, unknown>)?.['total'] ?? 0)\n\n const dataResult = await db.execute(\n sql`SELECT * FROM ${tableRaw()}${where} ORDER BY ${sortCol} ${sortDir} LIMIT ${per_page} OFFSET ${offset}`\n )\n\n const rows = dataResult.rows as Record<string, unknown>[]\n const cache = new Map<string, unknown>()\n await resolveRows(rows, include, cache)\n\n const total_pages = Math.ceil(total / per_page)\n return {\n ok: true,\n data: rows as T[],\n meta: {\n total,\n page,\n per_page,\n total_pages,\n has_next: page < total_pages,\n has_prev: page > 1,\n },\n }\n },\n\n async findOne(id: string, include: string[] = []): Promise<T | null> {\n const result = await db.execute(\n sql`SELECT * FROM ${tableRaw()} WHERE id = ${id} LIMIT 1`\n )\n if (result.rows.length === 0) return null\n\n const rows = result.rows as Record<string, unknown>[]\n const cache = new Map<string, unknown>()\n await resolveRows(rows, include, cache)\n return rows[0] as T\n },\n\n async findBySlug(slug: string, include: string[] = []): Promise<T | null> {\n const result = await db.execute(\n sql`SELECT * FROM ${tableRaw()} WHERE slug = ${slug} LIMIT 1`\n )\n if (result.rows.length === 0) return null\n\n const rows = result.rows as Record<string, unknown>[]\n const cache = new Map<string, unknown>()\n await resolveRows(rows, include, cache)\n return rows[0] as T\n },\n\n async create(data: CreateInput<T>): Promise<T> {\n const record: Record<string, unknown> = {\n ...(data as Record<string, unknown>),\n id: crypto.randomUUID(),\n created_at: new Date(),\n updated_at: new Date(),\n }\n const entries = Object.entries(record).filter(([, v]) => v !== undefined)\n const colsSql = sql.join(entries.map(([k]) => sql.raw(quoteIdent(k))), sql`, `)\n const valsSql = sql.join(entries.map(([, v]) => sql`${v}`), sql`, `)\n\n const result = await db.execute(\n sql`INSERT INTO ${tableRaw()} (${colsSql}) VALUES (${valsSql}) RETURNING *`\n )\n return result.rows[0] as T\n },\n\n async update(id: string, data: UpdateInput<T>): Promise<T | null> {\n const record: Record<string, unknown> = {\n ...(data as Record<string, unknown>),\n updated_at: new Date(),\n }\n const entries = Object.entries(record).filter(([k, v]) => k !== 'id' && v !== undefined)\n if (entries.length === 0) {\n return this.findOne(id)\n }\n\n const setClauses = sql.join(\n entries.map(([k, v]) => sql`${sql.raw(quoteIdent(k))} = ${v}`),\n sql`, `\n )\n\n const result = await db.execute(\n sql`UPDATE ${tableRaw()} SET ${setClauses} WHERE id = ${id} RETURNING *`\n )\n if (result.rows.length === 0) return null\n return result.rows[0] as T\n },\n\n async delete(id: string): Promise<void> {\n await db.execute(sql`DELETE FROM ${tableRaw()} WHERE id = ${id}`)\n },\n\n async findAll(opts: FindAllOptions = {}): Promise<T[]> {\n const { include = [], published_only } = opts\n const conds = buildConditions(published_only, undefined, undefined)\n const where = whereFragment(conds)\n\n const result = await db.execute(\n sql`SELECT * FROM ${tableRaw()}${where} ORDER BY created_at ASC`\n )\n\n const rows = result.rows as Record<string, unknown>[]\n const cache = new Map<string, unknown>()\n await resolveRows(rows, include, cache)\n return rows as T[]\n },\n }\n}\n","import crypto from 'node:crypto'\nimport { sql } from 'drizzle-orm'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport type {\n MediaRepository,\n MediaItem,\n CreateMediaInput,\n MediaFindManyOptions,\n PaginatedResult,\n} from '@bobbykim/manguito-cms-core'\n\nexport function createMediaRepository(db: DrizzlePostgresInstance): MediaRepository {\n const TABLE = sql.raw('\"media\"')\n\n return {\n async findMany(opts: MediaFindManyOptions = {}): Promise<PaginatedResult<MediaItem>> {\n const { page = 1, per_page = 10, type, orphaned } = opts\n const offset = (page - 1) * per_page\n\n const conds = []\n if (type) conds.push(sql`type = ${type}`)\n if (orphaned) conds.push(sql`reference_count = 0`)\n\n const where =\n conds.length > 0 ? sql` WHERE ${sql.join(conds, sql` AND `)}` : sql``\n\n const countResult = await db.execute(sql`SELECT COUNT(*) AS total FROM ${TABLE}${where}`)\n const total = Number(\n (countResult.rows[0] as Record<string, unknown>)?.['total'] ?? 0\n )\n\n const dataResult = await db.execute(\n sql`SELECT * FROM ${TABLE}${where} ORDER BY created_at DESC LIMIT ${per_page} OFFSET ${offset}`\n )\n\n const total_pages = Math.ceil(total / per_page)\n return {\n ok: true,\n data: dataResult.rows as MediaItem[],\n meta: {\n total,\n page,\n per_page,\n total_pages,\n has_next: page < total_pages,\n has_prev: page > 1,\n },\n }\n },\n\n async findOne(id: string): Promise<MediaItem | null> {\n const result = await db.execute(\n sql`SELECT * FROM ${TABLE} WHERE id = ${id} LIMIT 1`\n )\n if (result.rows.length === 0) return null\n return result.rows[0] as MediaItem\n },\n\n async create(data: CreateMediaInput): Promise<MediaItem> {\n const record: Record<string, unknown> = {\n ...data,\n id: crypto.randomUUID(),\n reference_count: 0,\n created_at: new Date(),\n updated_at: new Date(),\n }\n const entries = Object.entries(record).filter(([, v]) => v !== undefined)\n const colsSql = sql.join(entries.map(([k]) => sql.raw(`\"${k}\"`)), sql`, `)\n const valsSql = sql.join(entries.map(([, v]) => sql`${v}`), sql`, `)\n\n const result = await db.execute(\n sql`INSERT INTO ${TABLE} (${colsSql}) VALUES (${valsSql}) RETURNING *`\n )\n return result.rows[0] as MediaItem\n },\n\n async update(id: string, data: Partial<MediaItem>): Promise<MediaItem | null> {\n const record: Record<string, unknown> = { ...data, updated_at: new Date() }\n const entries = Object.entries(record).filter(\n ([k, v]) => k !== 'id' && v !== undefined\n )\n if (entries.length === 0) return this.findOne(id)\n\n const setClauses = sql.join(\n entries.map(([k, v]) => sql`${sql.raw(`\"${k}\"`)} = ${v}`),\n sql`, `\n )\n\n const result = await db.execute(\n sql`UPDATE ${TABLE} SET ${setClauses} WHERE id = ${id} RETURNING *`\n )\n if (result.rows.length === 0) return null\n return result.rows[0] as MediaItem\n },\n\n async delete(id: string): Promise<void> {\n await db.execute(sql`DELETE FROM ${TABLE} WHERE id = ${id}`)\n },\n\n async incrementReferenceCount(ids: string[]): Promise<void> {\n if (ids.length === 0) return\n const inList = sql.join(ids.map((id) => sql`${id}`), sql`, `)\n await db.execute(\n sql`UPDATE ${TABLE} SET reference_count = reference_count + 1, updated_at = NOW() WHERE id IN (${inList})`\n )\n },\n\n async decrementReferenceCount(ids: string[]): Promise<void> {\n if (ids.length === 0) return\n const inList = sql.join(ids.map((id) => sql`${id}`), sql`, `)\n await db.execute(\n sql`UPDATE ${TABLE} SET reference_count = GREATEST(reference_count - 1, 0), updated_at = NOW() WHERE id IN (${inList})`\n )\n },\n }\n}\n","import type { ServerAdapter, CorsConfig } from '@bobbykim/manguito-cms-core'\n\nexport type NodeServerOptions = {\n port?: number\n base_url?: string\n cors?: Partial<CorsConfig>\n}\n\nexport function createServer(options: NodeServerOptions = {}): ServerAdapter {\n const port = options.port ?? Number(process.env['PORT'] ?? 3000)\n const _base_url = options.base_url ?? `http://localhost:${port}`\n\n const cors: CorsConfig = {\n origin: options.cors?.origin ?? (process.env['ALLOWED_ORIGIN'] ?? '*'),\n methods: options.cors?.methods ?? [\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n ],\n credentials: options.cors?.credentials ?? true,\n }\n\n return {\n type: 'node',\n cors,\n getEntryPoint(): string {\n return 'node'\n },\n }\n}\n","import type { APIAdapter, ResolvedRateLimitConfig } from '@bobbykim/manguito-cms-core'\n\nexport { createCmsApp } from './app.js'\nexport type { CreateCmsAppOptions } from './app.js'\n\nexport { createServer } from './server/node.js'\nexport type { NodeServerOptions } from './server/node.js'\n\n// ─── User-facing config factory ───────────────────────────────────────────────\n\nexport type APIAdapterOptions = {\n prefix?: string\n media?: {\n max_file_size?: number\n }\n rateLimit?: ResolvedRateLimitConfig\n}\n\nexport function createAPIAdapter(options: APIAdapterOptions = {}): APIAdapter {\n const prefix = options.prefix ?? '/api'\n const media = options.media?.max_file_size !== undefined\n ? { max_file_size: options.media.max_file_size }\n : undefined\n\n return {\n prefix,\n ...(media !== undefined && { media }),\n ...(options.rateLimit !== undefined && { rateLimit: options.rateLimit }),\n }\n}\n"],"mappings":"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","names":["sql","parsePagination","sql","sql","quoteIdent","sql","row","sign","getCookie","sql","sql","getCookie","sql","sql","sql","sql","crypto","sql","SORTABLE_FIELDS","quoteIdent","sql","crypto","crypto","sql","sql"]}
|