{"version":3,"file":"manifest.mjs","names":[],"sources":["../../../../src/astro/routes/api/manifest.ts"],"sourcesContent":["/**\n * Admin manifest endpoint - injected by EmDash integration\n *\n * GET /_emdash/api/manifest\n *\n * Returns the admin manifest with collection definitions and plugin info.\n * The manifest is generated from the user's live.config.ts at runtime.\n */\n\nimport type { APIRoute } from \"astro\";\n\nimport { apiSuccess, handleError } from \"#api/error.js\";\nimport { getAuthMode } from \"#auth/mode.js\";\nimport { OptionsRepository } from \"#db/repositories/options.js\";\n\nimport { COMMIT, VERSION } from \"../../../version.js\";\nimport type { EmDashManifest } from \"../../types.js\";\n\nexport const prerender = false;\n\nexport const GET: APIRoute = async ({ locals }) => {\n\tconst { emdash } = locals;\n\n\ttry {\n\t\t// Manifest is built fresh from the live database per admin request.\n\t\t// `requestCached` inside `getManifest` dedupes if multiple consumers\n\t\t// share the request. Wrapped in try/catch so any future DB-touching\n\t\t// additions to `getManifest()` (plugin manifest loading, marketplace\n\t\t// lookup, etc.) return the standard error envelope rather than an\n\t\t// unstructured 500 — matches the pattern used by the WP execute\n\t\t// routes.\n\t\tconst emdashManifest = emdash ? await emdash.getManifest() : null;\n\n\t\t// Determine auth mode from config\n\t\tconst authMode = getAuthMode(emdash?.config);\n\n\t\t// Read admin branding from the per-request config plumbed through middleware\n\t\t// (same source admin.astro reads from). Reading from a build-time global\n\t\t// here was unreliable -- the virtual config module exports the config but\n\t\t// doesn't assign it to globalThis, so getStoredConfig() always returned\n\t\t// null and the React SPA never received custom logo/siteName/favicon.\n\t\t// See issue #835.\n\t\tlet adminBranding = emdash?.config?.admin;\n\n\t\t// When no build-time `admin.siteName` is configured, brand the admin with\n\t\t// the site's own title so multi-site operators can tell backends apart\n\t\t// (WordPress-style: wp-admin always shows the site name). Precedence:\n\t\t// explicit `admin.siteName` → Site Title (Settings → General) → the title\n\t\t// captured by the setup wizard → the bundled \"EmDash\" default in the SPA.\n\t\tif (!adminBranding?.siteName && emdash?.db) {\n\t\t\ttry {\n\t\t\t\tconst options = new OptionsRepository(emdash.db);\n\t\t\t\tconst titles = await options.getMany<string>([\"site:title\", \"emdash:site_title\"]);\n\t\t\t\tconst siteTitle = titles.get(\"site:title\") || titles.get(\"emdash:site_title\");\n\t\t\t\tif (siteTitle) {\n\t\t\t\t\tadminBranding = { ...adminBranding, siteName: siteTitle };\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// options table may not exist yet (pre-setup) — keep the default.\n\t\t\t}\n\t\t}\n\n\t\t// Check if self-signup is enabled (any allowed domain with enabled = 1)\n\t\t// Only relevant for passkey auth — external auth providers handle their own signup\n\t\tlet signupEnabled = false;\n\t\tif (emdash?.db && authMode.type === \"passkey\") {\n\t\t\ttry {\n\t\t\t\tconst { sql } = await import(\"kysely\");\n\t\t\t\tconst result = await sql<{ cnt: unknown }>`\n\t\t\t\t\tSELECT COUNT(*) as cnt FROM allowed_domains WHERE enabled = 1\n\t\t\t\t`.execute(emdash.db);\n\t\t\t\tsignupEnabled = Number(result.rows[0]?.cnt ?? 0) > 0;\n\t\t\t} catch {\n\t\t\t\t// Table may not exist yet, that's fine\n\t\t\t}\n\t\t}\n\n\t\t// Managed static-frontend hosting status (platform-provisioned instances\n\t\t// only). Present when the hosting parent seeded `frontend:connect_url` +\n\t\t// `credits:project_id`; drives the Settings → General \"Connect GitHub\" UI.\n\t\tlet frontend: EmDashManifest[\"frontend\"];\n\t\tlet customDomain: EmDashManifest[\"customDomain\"];\n\t\tif (emdash?.db) {\n\t\t\ttry {\n\t\t\t\tconst options = new OptionsRepository(emdash.db);\n\t\t\t\tconst map = await options.getMany<string>([\n\t\t\t\t\t\"frontend:connect_url\",\n\t\t\t\t\t\"credits:project_id\",\n\t\t\t\t\t\"frontend:enabled\",\n\t\t\t\t\t\"frontend:pages_url\",\n\t\t\t\t\t\"frontend:repo_url\",\n\t\t\t\t\t\"site:url\",\n\t\t\t\t\t\"custom_domain:api_url\",\n\t\t\t\t\t\"custom_domain:default_url\",\n\t\t\t\t]);\n\t\t\t\tconst connectBase = map.get(\"frontend:connect_url\") ?? \"\";\n\t\t\t\tconst projectId = map.get(\"credits:project_id\") ?? \"\";\n\t\t\t\tif (connectBase && projectId) {\n\t\t\t\t\tconst ret = encodeURIComponent(`${map.get(\"site:url\") ?? \"\"}/_emdash/admin/settings/general`);\n\t\t\t\t\tfrontend = {\n\t\t\t\t\t\tmanaged: true,\n\t\t\t\t\t\tenabled: String(map.get(\"frontend:enabled\") ?? \"\") === \"true\",\n\t\t\t\t\t\tpagesUrl: map.get(\"frontend:pages_url\") || null,\n\t\t\t\t\t\trepoUrl: map.get(\"frontend:repo_url\") || null,\n\t\t\t\t\t\tconnectUrl: `${connectBase}?project=${encodeURIComponent(projectId)}&return=${ret}`,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\t// Managed custom-domain status: present when the hosting parent\n\t\t\t\t// seeded `custom_domain:api_url` + the instance's default URL.\n\t\t\t\tconst cdApi = map.get(\"custom_domain:api_url\") ?? \"\";\n\t\t\t\tconst defaultUrl = map.get(\"custom_domain:default_url\") ?? \"\";\n\t\t\t\tif (cdApi && projectId && defaultUrl) {\n\t\t\t\t\tcustomDomain = { managed: true, defaultUrl };\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// options unavailable (pre-setup) — no managed status surfaced.\n\t\t\t}\n\t\t}\n\n\t\tconst manifest: EmDashManifest = emdashManifest\n\t\t\t? {\n\t\t\t\t\t...emdashManifest,\n\t\t\t\t\tauthMode: authMode.type === \"external\" ? authMode.providerType : \"passkey\",\n\t\t\t\t\tsignupEnabled,\n\t\t\t\t\tadmin: adminBranding,\n\t\t\t\t\t...(frontend ? { frontend } : {}),\n\t\t\t\t\t...(customDomain ? { customDomain } : {}),\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tversion: VERSION,\n\t\t\t\t\tcommit: COMMIT,\n\t\t\t\t\thash: \"default\",\n\t\t\t\t\tcollections: {},\n\t\t\t\t\tplugins: {},\n\t\t\t\t\ttaxonomies: [],\n\t\t\t\t\tauthMode: \"passkey\",\n\t\t\t\t\tsignupEnabled,\n\t\t\t\t\tadmin: adminBranding,\n\t\t\t\t};\n\n\t\treturn apiSuccess(manifest);\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to build manifest\", \"MANIFEST_BUILD_ERROR\");\n\t}\n};\n"],"mappings":";;;;;;;;AAkBA,MAAa,YAAY;AAEzB,MAAa,MAAgB,OAAO,EAAE,aAAa;CAClD,MAAM,EAAE,WAAW;AAEnB,KAAI;EAQH,MAAM,iBAAiB,SAAS,MAAM,OAAO,aAAa,GAAG;EAG7D,MAAM,WAAW,YAAY,QAAQ,OAAO;EAQ5C,IAAI,gBAAgB,QAAQ,QAAQ;AAOpC,MAAI,CAAC,eAAe,YAAY,QAAQ,GACvC,KAAI;GAEH,MAAM,SAAS,MADC,IAAI,kBAAkB,OAAO,GAAG,CACnB,QAAgB,CAAC,cAAc,oBAAoB,CAAC;GACjF,MAAM,YAAY,OAAO,IAAI,aAAa,IAAI,OAAO,IAAI,oBAAoB;AAC7E,OAAI,UACH,iBAAgB;IAAE,GAAG;IAAe,UAAU;IAAW;UAEnD;EAOT,IAAI,gBAAgB;AACpB,MAAI,QAAQ,MAAM,SAAS,SAAS,UACnC,KAAI;GACH,MAAM,EAAE,QAAQ,MAAM,OAAO;GAC7B,MAAM,SAAS,MAAM,GAAqB;;MAExC,QAAQ,OAAO,GAAG;AACpB,mBAAgB,OAAO,OAAO,KAAK,IAAI,OAAO,EAAE,GAAG;UAC5C;EAQT,IAAI;EACJ,IAAI;AACJ,MAAI,QAAQ,GACX,KAAI;GAEH,MAAM,MAAM,MADI,IAAI,kBAAkB,OAAO,GAAG,CACtB,QAAgB;IACzC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,CAAC;GACF,MAAM,cAAc,IAAI,IAAI,uBAAuB,IAAI;GACvD,MAAM,YAAY,IAAI,IAAI,qBAAqB,IAAI;AACnD,OAAI,eAAe,WAAW;IAC7B,MAAM,MAAM,mBAAmB,GAAG,IAAI,IAAI,WAAW,IAAI,GAAG,iCAAiC;AAC7F,eAAW;KACV,SAAS;KACT,SAAS,OAAO,IAAI,IAAI,mBAAmB,IAAI,GAAG,KAAK;KACvD,UAAU,IAAI,IAAI,qBAAqB,IAAI;KAC3C,SAAS,IAAI,IAAI,oBAAoB,IAAI;KACzC,YAAY,GAAG,YAAY,WAAW,mBAAmB,UAAU,CAAC,UAAU;KAC9E;;GAIF,MAAM,QAAQ,IAAI,IAAI,wBAAwB,IAAI;GAClD,MAAM,aAAa,IAAI,IAAI,4BAA4B,IAAI;AAC3D,OAAI,SAAS,aAAa,WACzB,gBAAe;IAAE,SAAS;IAAM;IAAY;UAEtC;AA0BT,SAAO,WArB0B,iBAC9B;GACA,GAAG;GACH,UAAU,SAAS,SAAS,aAAa,SAAS,eAAe;GACjE;GACA,OAAO;GACP,GAAI,WAAW,EAAE,UAAU,GAAG,EAAE;GAChC,GAAI,eAAe,EAAE,cAAc,GAAG,EAAE;GACxC,GACA;GACA,SAAS;GACT,QAAQ;GACR,MAAM;GACN,aAAa,EAAE;GACf,SAAS,EAAE;GACX,YAAY,EAAE;GACd,UAAU;GACV;GACA,OAAO;GACP,CAEwB;UACnB,OAAO;AACf,SAAO,YAAY,OAAO,4BAA4B,uBAAuB"}