{"version":3,"file":"email.mjs","names":[],"sources":["../../../../../src/astro/routes/api/settings/email.ts"],"sourcesContent":["/**\n * Email Settings API endpoint\n *\n * GET  /_emdash/api/settings/email      — current provider, available providers, middleware\n * POST /_emdash/api/settings/email/test — send a test email through the full pipeline\n */\n\nimport { escapeHtml } from \"@premium-cms/auth\";\nimport type { APIRoute } from \"astro\";\nimport { z } from \"zod\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { isParseError, parseBody } from \"#api/parse.js\";\nimport { OptionsRepository } from \"#db/repositories/options.js\";\n\nexport const prerender = false;\n\nconst EMAIL_DELIVER_HOOK = \"email:deliver\";\nconst EMAIL_BEFORE_SEND_HOOK = \"email:beforeSend\";\nconst EMAIL_AFTER_SEND_HOOK = \"email:afterSend\";\n\n/**\n * GET /_emdash/api/settings/email\n *\n * Returns the email configuration state:\n * - Current provider selection\n * - Available providers (plugins with email:deliver)\n * - Active middleware (email:beforeSend / email:afterSend plugins)\n * - Whether email is available\n */\nexport const GET: APIRoute = async ({ 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, \"settings:manage\");\n\tif (denied) return denied;\n\n\ttry {\n\t\tconst pipeline = emdash.hooks;\n\t\tconst optionsRepo = new OptionsRepository(emdash.db);\n\n\t\t// Get email:deliver providers and current selection\n\t\tconst providers = pipeline.getExclusiveHookProviders(EMAIL_DELIVER_HOOK);\n\t\tconst selectedProviderId = await optionsRepo.get<string>(\n\t\t\t`emdash:exclusive_hook:${EMAIL_DELIVER_HOOK}`,\n\t\t);\n\n\t\t// Get middleware hooks (beforeSend / afterSend). These are non-exclusive —\n\t\t// many plugins can subscribe — so we enumerate non-exclusive providers.\n\t\tconst beforeSendPlugins = pipeline\n\t\t\t.getHookProviders(EMAIL_BEFORE_SEND_HOOK)\n\t\t\t.map((p) => p.pluginId);\n\t\tconst afterSendPlugins = pipeline\n\t\t\t.getHookProviders(EMAIL_AFTER_SEND_HOOK)\n\t\t\t.map((p) => p.pluginId);\n\n\t\treturn apiSuccess({\n\t\t\tavailable: emdash.email?.isAvailable() ?? false,\n\t\t\tproviders: providers.map((p) => ({\n\t\t\t\tpluginId: p.pluginId,\n\t\t\t})),\n\t\t\tselectedProviderId: selectedProviderId ?? null,\n\t\t\tmiddleware: {\n\t\t\t\tbeforeSend: beforeSendPlugins,\n\t\t\t\tafterSend: afterSendPlugins,\n\t\t\t},\n\t\t});\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to get email settings\", \"EMAIL_SETTINGS_READ_ERROR\");\n\t}\n};\n\n/**\n * POST /_emdash/api/settings/email/test\n *\n * Send a test email through the full pipeline.\n * Validates the pipeline is configured and the provider works.\n */\nconst testEmailBody = z.object({\n\tto: z.string().email(),\n});\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, \"settings:manage\");\n\tif (denied) return denied;\n\n\tif (!emdash.email?.isAvailable()) {\n\t\treturn apiError(\n\t\t\t\"EMAIL_NOT_CONFIGURED\",\n\t\t\t\"No email provider is configured. Install and activate an email provider plugin.\",\n\t\t\t503,\n\t\t);\n\t}\n\n\ttry {\n\t\tconst body = await parseBody(request, testEmailBody);\n\t\tif (isParseError(body)) return body;\n\n\t\tconst optionsRepo = new OptionsRepository(emdash.db);\n\t\tconst siteName = (await optionsRepo.get<string>(\"emdash:site_title\")) ?? \"EmDash\";\n\t\tconst safeName = escapeHtml(siteName);\n\n\t\tawait emdash.email.send(\n\t\t\t{\n\t\t\t\tto: body.to,\n\t\t\t\tsubject: `Test email from ${siteName}`,\n\t\t\t\ttext: `This is a test email from ${siteName}.\\n\\nIf you received this, your email provider is working correctly.`,\n\t\t\t\thtml: `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n</head>\n<body style=\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.5; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;\">\n  <h1 style=\"font-size: 24px; margin-bottom: 20px;\">Test Email</h1>\n  <p>This is a test email from <strong>${safeName}</strong>.</p>\n  <p>If you received this, your email provider is working correctly.</p>\n  <p style=\"color: #666; font-size: 14px; margin-top: 30px;\">\n    Sent via the EmDash email pipeline.\n  </p>\n</body>\n</html>`,\n\t\t\t},\n\t\t\t\"admin\",\n\t\t);\n\n\t\treturn apiSuccess({\n\t\t\tsuccess: true,\n\t\t\tmessage: `Test email sent to ${body.to}`,\n\t\t});\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to send test email\", \"EMAIL_TEST_ERROR\");\n\t}\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,MAAa,YAAY;AAEzB,MAAM,qBAAqB;AAC3B,MAAM,yBAAyB;AAC/B,MAAM,wBAAwB;;;;;;;;;;AAW9B,MAAa,MAAgB,OAAO,EAAE,aAAa;CAClD,MAAM,EAAE,QAAQ,SAAS;AAEzB,KAAI,CAAC,QAAQ,GACZ,QAAO,SAAS,kBAAkB,6BAA6B,IAAI;CAGpE,MAAM,SAAS,YAAY,MAAM,kBAAkB;AACnD,KAAI,OAAQ,QAAO;AAEnB,KAAI;EACH,MAAM,WAAW,OAAO;EACxB,MAAM,cAAc,IAAI,kBAAkB,OAAO,GAAG;EAGpD,MAAM,YAAY,SAAS,0BAA0B,mBAAmB;EACxE,MAAM,qBAAqB,MAAM,YAAY,IAC5C,yBAAyB,qBACzB;EAID,MAAM,oBAAoB,SACxB,iBAAiB,uBAAuB,CACxC,KAAK,MAAM,EAAE,SAAS;EACxB,MAAM,mBAAmB,SACvB,iBAAiB,sBAAsB,CACvC,KAAK,MAAM,EAAE,SAAS;AAExB,SAAO,WAAW;GACjB,WAAW,OAAO,OAAO,aAAa,IAAI;GAC1C,WAAW,UAAU,KAAK,OAAO,EAChC,UAAU,EAAE,UACZ,EAAE;GACH,oBAAoB,sBAAsB;GAC1C,YAAY;IACX,YAAY;IACZ,WAAW;IACX;GACD,CAAC;UACM,OAAO;AACf,SAAO,YAAY,OAAO,gCAAgC,4BAA4B;;;;;;;;;AAUxF,MAAM,gBAAgB,EAAE,OAAO,EAC9B,IAAI,EAAE,QAAQ,CAAC,OAAO,EACtB,CAAC;AAEF,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,kBAAkB;AACnD,KAAI,OAAQ,QAAO;AAEnB,KAAI,CAAC,OAAO,OAAO,aAAa,CAC/B,QAAO,SACN,wBACA,mFACA,IACA;AAGF,KAAI;EACH,MAAM,OAAO,MAAM,UAAU,SAAS,cAAc;AACpD,MAAI,aAAa,KAAK,CAAE,QAAO;EAG/B,MAAM,WAAY,MADE,IAAI,kBAAkB,OAAO,GAAG,CAChB,IAAY,oBAAoB,IAAK;EACzE,MAAM,WAAW,WAAW,SAAS;AAErC,QAAM,OAAO,MAAM,KAClB;GACC,IAAI,KAAK;GACT,SAAS,mBAAmB;GAC5B,MAAM,6BAA6B,SAAS;GAC5C,MAAM;;;;;;;;;yCAS+B,SAAS;;;;;;;GAO9C,EACD,QACA;AAED,SAAO,WAAW;GACjB,SAAS;GACT,SAAS,sBAAsB,KAAK;GACpC,CAAC;UACM,OAAO;AACf,SAAO,YAAY,OAAO,6BAA6B,mBAAmB"}