{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/auth/invite/index.ts"],"sourcesContent":["/**\n * POST /_emdash/api/auth/invite\n *\n * Create an invite for a new user. Admin only.\n *\n * When an email provider is configured (via the plugin email pipeline),\n * the invite email is sent automatically.\n * When no provider is configured, returns the invite URL for the admin\n * to share manually (copy-link fallback).\n */\n\nimport type { APIRoute } from \"astro\";\n\nexport const prerender = false;\n\nimport { builtinRoleForLevel, createInvite, InviteError, Role } from \"@premium-cms/auth\";\nimport { createKyselyAdapter } from \"@premium-cms/auth/adapters/kysely\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { isParseError, parseBody } from \"#api/parse.js\";\nimport { inviteCreateBody } from \"#api/schemas.js\";\nimport { getSiteBaseUrl } from \"#api/site-url.js\";\nimport { AuthzRepository } from \"#db/repositories/authz.js\";\nimport { OptionsRepository } from \"#db/repositories/options.js\";\n\nexport const POST: APIRoute = async ({ request, locals }) => {\n\tconst { emdash, user } = locals;\n\n\tif (!emdash?.db) {\n\t\treturn apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\t}\n\n\tconst denied = requirePerm(user, \"users:invite\");\n\tif (denied) return denied;\n\n\tconst adapter = createKyselyAdapter(emdash.db);\n\n\ttry {\n\t\tconst body = await parseBody(request, inviteCreateBody);\n\t\tif (isParseError(body)) return body;\n\n\t\t// Resolve the invited role: `roleId` (id or slug) wins, else the legacy\n\t\t// level maps to its built-in role. Default is Author, as before.\n\t\tlet role = body.role ?? Role.AUTHOR;\n\t\tlet roleId: string;\n\t\tif (body.roleId !== undefined) {\n\t\t\tconst summary = await new AuthzRepository(emdash.db).getRole(body.roleId);\n\t\t\tif (!summary) return apiError(\"UNKNOWN_ROLE\", \"Role not found\", 400);\n\t\t\troleId = summary.id;\n\t\t\trole = summary.level as typeof role;\n\t\t} else {\n\t\t\troleId = `role:${builtinRoleForLevel(role).slug}`;\n\t\t}\n\n\t\t// Get site config for invite email\n\t\tconst options = new OptionsRepository(emdash.db);\n\t\tconst siteName = (await options.get<string>(\"emdash:site_title\")) || \"EmDash\";\n\n\t\t// Use the configured site URL (stored option as fallback) to prevent Host header spoofing in invite emails\n\t\tconst baseUrl = await getSiteBaseUrl(emdash.db, request, emdash.config);\n\n\t\t// Build email sender from the plugin pipeline (if available)\n\t\tconst emailSend = emdash.email?.isAvailable()\n\t\t\t? (message: { to: string; subject: string; text: string; html?: string }) =>\n\t\t\t\t\temdash.email!.send(message, \"system\")\n\t\t\t: undefined;\n\n\t\tconst result = await createInvite(\n\t\t\t{\n\t\t\t\tbaseUrl,\n\t\t\t\tsiteName,\n\t\t\t\temail: emailSend,\n\t\t\t},\n\t\t\tadapter,\n\t\t\tbody.email,\n\t\t\trole,\n\t\t\tuser!.id,\n\t\t\troleId,\n\t\t);\n\n\t\tif (emailSend) {\n\t\t\t// Email was sent\n\t\t\treturn apiSuccess({\n\t\t\t\tsuccess: true,\n\t\t\t\tmessage: `Invite sent to ${body.email}`,\n\t\t\t});\n\t\t}\n\n\t\t// No email provider — return the invite URL for manual sharing\n\t\treturn apiSuccess(\n\t\t\t{\n\t\t\t\tsuccess: true,\n\t\t\t\tmessage: \"Invite created. No email provider configured — share the link manually.\",\n\t\t\t\tinviteUrl: result.url,\n\t\t\t},\n\t\t\t200,\n\t\t);\n\t} catch (error) {\n\t\tif (error instanceof InviteError) {\n\t\t\tconst statusMap: Record<string, number> = {\n\t\t\t\tuser_exists: 409,\n\t\t\t\tinvalid_token: 400,\n\t\t\t\ttoken_expired: 400,\n\t\t\t};\n\t\t\treturn apiError(error.code.toUpperCase(), error.message, statusMap[error.code] ?? 400);\n\t\t}\n\n\t\treturn handleError(error, \"Failed to create invite\", \"INVITE_CREATE_ERROR\");\n\t}\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAaA,MAAa,YAAY;AAazB,MAAa,OAAiB,OAAO,EAAE,SAAS,aAAa;CAC5D,MAAM,EAAE,QAAQ,SAAS;AAEzB,KAAI,CAAC,QAAQ,GACZ,QAAO,SAAS,kBAAkB,6BAA6B,IAAI;CAGpE,MAAM,SAAS,YAAY,MAAM,eAAe;AAChD,KAAI,OAAQ,QAAO;CAEnB,MAAM,UAAU,oBAAoB,OAAO,GAAG;AAE9C,KAAI;EACH,MAAM,OAAO,MAAM,UAAU,SAAS,iBAAiB;AACvD,MAAI,aAAa,KAAK,CAAE,QAAO;EAI/B,IAAI,OAAO,KAAK,QAAQ,KAAK;EAC7B,IAAI;AACJ,MAAI,KAAK,WAAW,QAAW;GAC9B,MAAM,UAAU,MAAM,IAAI,gBAAgB,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO;AACzE,OAAI,CAAC,QAAS,QAAO,SAAS,gBAAgB,kBAAkB,IAAI;AACpE,YAAS,QAAQ;AACjB,UAAO,QAAQ;QAEf,UAAS,QAAQ,oBAAoB,KAAK,CAAC;EAK5C,MAAM,WAAY,MADF,IAAI,kBAAkB,OAAO,GAAG,CAChB,IAAY,oBAAoB,IAAK;EAGrE,MAAM,UAAU,MAAM,eAAe,OAAO,IAAI,SAAS,OAAO,OAAO;EAGvE,MAAM,YAAY,OAAO,OAAO,aAAa,IACzC,YACD,OAAO,MAAO,KAAK,SAAS,SAAS,GACrC;EAEH,MAAM,SAAS,MAAM,aACpB;GACC;GACA;GACA,OAAO;GACP,EACD,SACA,KAAK,OACL,MACA,KAAM,IACN,OACA;AAED,MAAI,UAEH,QAAO,WAAW;GACjB,SAAS;GACT,SAAS,kBAAkB,KAAK;GAChC,CAAC;AAIH,SAAO,WACN;GACC,SAAS;GACT,SAAS;GACT,WAAW,OAAO;GAClB,EACD,IACA;UACO,OAAO;AACf,MAAI,iBAAiB,YAMpB,QAAO,SAAS,MAAM,KAAK,aAAa,EAAE,MAAM,SALN;GACzC,aAAa;GACb,eAAe;GACf,eAAe;GACf,CACkE,MAAM,SAAS,IAAI;AAGvF,SAAO,YAAY,OAAO,2BAA2B,sBAAsB"}