@contentgrowth/content-emailing 0.7.8 → 0.7.9
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/{TemplateManager-Db41KyPN.d.cts → TemplateManager-DbAGIGdI.d.cts} +1 -1
- package/dist/{TemplateManager-Db41KyPN.d.ts → TemplateManager-DbAGIGdI.d.ts} +1 -1
- package/dist/backend/index.cjs +30 -0
- package/dist/backend/index.cjs.map +1 -1
- package/dist/backend/index.js +30 -0
- package/dist/backend/index.js.map +1 -1
- package/dist/backend/routes/index.cjs +86 -56
- package/dist/backend/routes/index.cjs.map +1 -1
- package/dist/backend/routes/index.d.cts +3 -0
- package/dist/backend/routes/index.d.ts +3 -0
- package/dist/backend/routes/index.js +86 -56
- package/dist/backend/routes/index.js.map +1 -1
- package/dist/frontend/index.cjs +4 -4
- package/dist/frontend/index.cjs.map +1 -1
- package/dist/frontend/index.d.cts +1 -1
- package/dist/frontend/index.d.ts +1 -1
- package/dist/frontend/index.js +4 -4
- package/dist/frontend/index.js.map +1 -1
- package/dist/index.cjs +31 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +31 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/backend/index.js","../../src/backend/EmailService.js","../../src/common/htmlWrapper.js","../../src/backend/EmailingCacheDO.js","../../src/backend/EmailLogger.js","../../src/backend/routes/index.js","../../src/backend/routes/settings.js","../../src/backend/routes/templates.js","../../src/backend/routes/tracking.js","../../src/backend/routes/logs.js","../../src/common/utils.js"],"sourcesContent":["// Backend services\nexport { EmailService } from './EmailService.js';\nexport { EmailingCacheDO, createDOCacheProvider } from './EmailingCacheDO.js';\nexport { EmailLogger, createEmailLoggerCallback } from './EmailLogger.js';\n\n// Routes\nexport {\n createEmailRoutes,\n createTemplateRoutes,\n createTrackingRoutes\n} from './routes/index.js';\n\n// Common utilities (re-export for convenience)\nexport {\n wrapInEmailTemplate,\n getWebsiteUrl,\n resetWebsiteUrlCache,\n encodeTrackingLinks,\n markdownToPlainText,\n extractVariables\n} from '../common/index.js';\n","/**\n * Email Service\n * Unified service for email delivery and template management.\n */\nimport { marked } from 'marked';\nimport Mustache from 'mustache';\nimport { wrapInEmailTemplate } from '../common/htmlWrapper.js';\nimport { createDOCacheProvider } from './EmailingCacheDO.js';\nimport { EmailLogger } from './EmailLogger.js';\n\nexport class EmailService {\n /**\n * @param {Object} env - Cloudflare environment bindings (DB, etc.)\n * @param {Object} config - Configuration options\n * @param {string} [config.emailTablePrefix='system_email_'] - Prefix for D1 tables\n * @param {Object} [config.defaults] - Default settings (fromName, fromAddress)\n * @param {boolean|Function} [config.emailLogger] - Email logger: true (default), false (disabled), or custom callback\n * @param {Object} [cacheProvider] - Optional cache interface (DO stub or KV wrapper)\n */\n constructor(env, config = {}, cacheProvider = null) {\n this.env = env;\n this.db = env.DB;\n this.config = {\n emailTablePrefix: config.emailTablePrefix || config.tableNamePrefix || 'system_email_',\n defaults: config.defaults || {\n fromName: 'System',\n fromAddress: 'noreply@example.com',\n provider: 'mailchannels'\n },\n // Loader function to fetch settings from backend (DB, KV, etc.)\n // Signature: async (profile, tenantId) => SettingsObject\n settingsLoader: config.settingsLoader || null,\n\n // Updater function to save settings to backend\n // Signature: async (profile, tenantId, settings) => void\n settingsUpdater: config.settingsUpdater || null,\n\n // Settings configuration\n settingsTableName: config.settingsTableName || 'system_settings',\n settingsKeyPrefix: config.settingsKeyPrefix || 'system_email.',\n\n // Branding configuration for email templates\n branding: {\n brandName: config.branding?.brandName || 'Your App',\n portalUrl: config.branding?.portalUrl || 'https://app.example.com',\n primaryColor: config.branding?.primaryColor || '#667eea',\n ...config.branding\n },\n ...config\n };\n\n // Email Logger Auto-detection:\n // - If emailLogger is explicitly false, disable logging\n // - If emailLogger is a function, use it as custom logger\n // - Otherwise (default), auto-create logger using env.DB if available\n if (config.emailLogger === false) {\n this.emailLogger = null;\n } else if (typeof config.emailLogger === 'function') {\n this.emailLogger = config.emailLogger;\n } else if (env.DB) {\n // Auto-enable logging by default when DB is available\n const logger = new EmailLogger(env.DB);\n this.emailLogger = logger.createCallback();\n } else {\n this.emailLogger = null;\n }\n\n // Cache Auto-detection:\n // If no provider explicitly passed, try to use built-in DO provider if binding exists\n if (!cacheProvider && env.EMAIL_TEMPLATE_CACHE) {\n this.cache = createDOCacheProvider(env.EMAIL_TEMPLATE_CACHE);\n } else {\n this.cache = cacheProvider;\n }\n }\n\n // --- Configuration & Settings ---\n\n /**\n * Load email configuration\n * @param {string} profile - 'system' or 'tenant' (or custom profile string)\n * @param {string} tenantId - Context ID (optional)\n * @returns {Promise<Object>} Email configuration\n */\n async loadSettings(profile = 'system', tenantId = null) {\n // 1. Try cache (now with Read-Through logic in DO)\n if (this.cache && this.cache.getSettings) {\n try {\n const cached = await this.cache.getSettings(profile, tenantId);\n if (cached) return this._normalizeConfig(cached);\n } catch (e) {\n // Ignore cache/rpc errors\n }\n }\n\n let settings = null;\n\n // 2. Use settingsLoader (Custom Worker Logic) if provided\n if (this.config.settingsLoader) {\n try {\n settings = await this.config.settingsLoader(profile, tenantId);\n } catch (e) {\n console.warn('[EmailService] settingsLoader failed:', e);\n }\n }\n\n // 3. System profile: Load from D1 system_settings table\n if (!settings && profile === 'system' && this.db) {\n try {\n const tableName = this.config.settingsTableName;\n const prefix = this.config.settingsKeyPrefix;\n\n // Fetch all keys starting with prefix\n const { results } = await this.db.prepare(`SELECT * FROM ${tableName} WHERE key LIKE ?`)\n .bind(`${prefix}%`)\n .all();\n\n if (results && results.length > 0) {\n settings = {};\n for (const row of results) {\n const cleanKey = row.key.slice(prefix.length);\n settings[cleanKey] = row.value;\n }\n }\n } catch (e) {\n console.warn('[EmailService] Failed to load settings from DB:', e.message);\n }\n }\n\n if (settings) {\n // 4. Populate Cache (Write-Back)\n if (this.cache && this.cache.putSettings) {\n try {\n this.cache.putSettings(profile, tenantId, settings);\n } catch (e) { /* ignore */ }\n }\n return this._normalizeConfig(settings);\n }\n\n // 5. Fallback to defaults\n return {\n ...this.config.defaults,\n };\n }\n\n /**\n * Normalize config keys to standard format\n * Handles both snake_case (DB) and camelCase inputs\n */\n _normalizeConfig(config) {\n return {\n provider: config.email_provider || config.provider || this.config.defaults.provider,\n fromAddress: config.email_from_address || config.fromAddress || this.config.defaults.fromAddress,\n fromName: config.email_from_name || config.fromName || this.config.defaults.fromName,\n\n // Provider-specific settings (normalize DB keys to service keys)\n sendgridApiKey: config.sendgrid_api_key || config.sendgridApiKey,\n resendApiKey: config.resend_api_key || config.resendApiKey,\n sendpulseClientId: config.sendpulse_client_id || config.sendpulseClientId,\n sendpulseClientSecret: config.sendpulse_client_secret || config.sendpulseClientSecret,\n\n // SMTP\n smtpHost: config.smtp_host || config.smtpHost,\n smtpPort: config.smtp_port || config.smtpPort,\n smtpUsername: config.smtp_username || config.smtpUsername,\n smtpPassword: config.smtp_password || config.smtpPassword,\n\n // Tracking\n trackingUrl: config.tracking_url || config.trackingUrl,\n\n // Pass through others\n // Pass through others\n ...config,\n smtpSecure: config.smtp_secure === 'true',\n };\n }\n\n // --- Template Management ---\n\n async getTemplate(templateId) {\n // Try cache first\n if (this.cache) {\n try {\n const cached = await this.cache.getTemplate(templateId);\n if (cached) return cached;\n } catch (e) {\n console.warn('[EmailService] Template cache lookup failed:', e);\n }\n }\n\n const table = `${this.config.emailTablePrefix}templates`;\n return await this.db.prepare(`SELECT * FROM ${table} WHERE template_id = ?`)\n .bind(templateId)\n .first();\n }\n\n async getAllTemplates() {\n const table = `${this.config.emailTablePrefix}templates`;\n const result = await this.db.prepare(`SELECT * FROM ${table} ORDER BY template_name`).all();\n return result.results || [];\n }\n\n /**\n * Save email configuration\n * @param {Object} settings - Config object\n * @param {string} profile - 'system' or 'tenant'\n * @param {string} tenantId - Context ID\n */\n async saveSettings(settings, profile = 'system', tenantId = null) {\n // 1. Convert to DB format if needed (not implemented here, assumes settingsUpdater handles it)\n\n // 2. Use settingsUpdater if provided (Write-Aside)\n if (this.config.settingsUpdater) {\n try {\n await this.config.settingsUpdater(profile, tenantId, settings);\n } catch (e) {\n console.error('[EmailService] settingsUpdater failed:', e);\n throw e;\n }\n }\n // 3. Helper for saving to D1 (if no updater, legacy/default tables)\n else if (profile === 'system' && this.db) {\n // NOTE: We could implement a default D1 upsert here similar to the DO fallback,\n // but for writing it's safer to rely on explicit updaters or the DO write-through if implemented.\n // Currently DO supports write-through for 'system' keys.\n }\n\n // 4. Update Cache (Write-Invalidate)\n if (this.cache && this.cache.invalidateSettings) {\n try {\n await this.cache.invalidateSettings(profile, tenantId);\n } catch (e) {\n console.warn('[EmailService] Failed to invalidate settings cache:', e);\n }\n }\n }\n\n async saveTemplate(template, userId = 'system') {\n const table = `${this.config.emailTablePrefix}templates`;\n const now = Math.floor(Date.now() / 1000);\n const existing = await this.getTemplate(template.template_id);\n\n if (existing) {\n await this.db.prepare(`\n UPDATE ${table} SET \n template_name = ?, template_type = ?, subject_template = ?, \n body_markdown = ?, variables = ?, description = ?, is_active = ?, \n updated_at = ?, updated_by = ?\n WHERE template_id = ?\n `).bind(\n template.template_name, template.template_type, template.subject_template,\n template.body_markdown, template.variables, template.description,\n template.is_active, now, userId, template.template_id\n ).run();\n } else {\n await this.db.prepare(`\n INSERT INTO ${table} (\n template_id, template_name, template_type, subject_template,\n body_markdown, variables, description, is_active, created_at, updated_at, updated_by\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).bind(\n template.template_id, template.template_name, template.template_type,\n template.subject_template, template.body_markdown, template.variables || '[]',\n template.description, template.is_active || 1, now, now, userId\n ).run();\n }\n\n // Invalidate cache if exists\n if (this.cache) {\n await this.cache.putTemplate(template);\n }\n }\n\n async deleteTemplate(templateId) {\n const table = `${this.config.emailTablePrefix}templates`;\n await this.db.prepare(`DELETE FROM ${table} WHERE template_id = ?`).bind(templateId).run();\n if (this.cache) {\n await this.cache.deleteTemplate(templateId);\n }\n }\n\n // --- Rendering ---\n\n /**\n * Pre-process template data to auto-format URLs\n * Scans for strings starting with http:// or https:// and wraps them in Markdown links\n */\n _preprocessData(data) {\n if (!data || typeof data !== 'object') return data;\n\n const processed = { ...data };\n for (const [key, value] of Object.entries(processed)) {\n if (typeof value === 'string' && (value.startsWith('http://') || value.startsWith('https://'))) {\n // Check if it's already a markdown link to avoid double wrapping\n if (!value.trim().startsWith('[') && !value.includes('](')) {\n processed[key] = `[${value}](${value})`;\n }\n }\n }\n return processed;\n }\n\n async renderTemplate(templateId, data) {\n const template = await this.getTemplate(templateId);\n if (!template) throw new Error(`Template not found: ${templateId}`);\n\n // Pre-process data to auto-format links\n const processedData = this._preprocessData(data);\n\n // Render Subject\n const subject = Mustache.render(template.subject_template, processedData);\n\n // Render Body (Markdown -> HTML)\n let markdown = Mustache.render(template.body_markdown, processedData);\n\n // Normalize escaped newlines from DB storage to actual newlines\n markdown = markdown.replace(/\\\\n/g, '\\n');\n\n // Configure marked for email-friendly output\n marked.use({\n mangle: false,\n headerIds: false,\n breaks: true // Convert single line breaks to <br>\n });\n const htmlContent = marked.parse(markdown);\n\n // Wrap in Base Template (could be another template or hardcoded default)\n const html = this.wrapInBaseTemplate(htmlContent, subject, data);\n const plainText = markdown.replace(/<[^>]*>/g, ''); // Simple strip\n\n return { subject, html, plainText };\n }\n\n wrapInBaseTemplate(content, subject, data = {}) {\n // Merge branding config with template data\n const templateData = {\n ...data,\n brandName: data.brandName || this.config.branding.brandName,\n portalUrl: data.portalUrl || this.config.branding.portalUrl,\n unsubscribeUrl: data.unsubscribeUrl || '{{unsubscribe_url}}'\n };\n\n // Use the full branded HTML wrapper from common\n return wrapInEmailTemplate(content, subject, templateData);\n }\n\n // --- Delivery ---\n\n /**\n * Send an email using a template\n * @param {string} templateId - Template ID\n * @param {Object} data - Template variables\n * @param {Object} options - Sending options (to, provider, etc.)\n * @returns {Promise<Object>} Delivery result\n */\n async sendViaTemplate(templateId, data, options) {\n try {\n const { subject, html, plainText } = await this.renderTemplate(templateId, data);\n\n return await this.sendEmail({\n ...options,\n subject, // Can be overridden by options.subject if needed, but usually template subject is preferred\n html,\n text: plainText,\n metadata: {\n ...options.metadata,\n templateId\n }\n });\n } catch (error) {\n console.error(`[EmailService] sendViaTemplate failed for ${templateId}:`, error);\n return { success: false, error: error.message };\n }\n }\n\n /**\n * Send a single email\n * @param {Object} params - Email parameters\n * @param {string} params.to - Recipient email\n * @param {string} params.subject - Email subject\n * @param {string} params.html - HTML body\n * @param {string} params.text - Plain text body\n * @param {string} [params.provider] - Override provider\n * @param {string} [params.profile='system'] - 'system' or 'tenant'\n * @param {string} [params.tenantId] - Required if profile is 'tenant'\n * @param {Object} [params.metadata] - Additional metadata\n * @returns {Promise<Object>} Delivery result\n */\n async sendEmail({ to, subject, html, htmlBody, text, textBody, provider, profile = 'system', tenantId = null, metadata = {}, batchId = null, userId = null }) {\n // Backward compatibility: accept htmlBody/textBody as aliases\n const htmlContent = html || htmlBody;\n const textContent = text || textBody;\n const templateId = metadata?.templateId || 'direct';\n\n // Log pending status\n if (this.emailLogger) {\n try {\n await this.emailLogger({\n event: 'pending',\n recipientEmail: to,\n recipientUserId: userId,\n templateId,\n subject,\n batchId,\n metadata\n });\n } catch (e) {\n console.warn('[EmailService] emailLogger pending failed:', e);\n }\n }\n\n try {\n const settings = await this.loadSettings(profile, tenantId);\n const useProvider = provider || settings.provider || 'mailchannels';\n\n let result;\n let providerMessageId = null;\n\n switch (useProvider) {\n case 'mailchannels':\n result = await this.sendViaMailChannels(to, subject, htmlContent, textContent, settings, metadata);\n break;\n case 'sendgrid':\n result = await this.sendViaSendGrid(to, subject, htmlContent, textContent, settings, metadata);\n break;\n case 'resend':\n result = await this.sendViaResend(to, subject, htmlContent, textContent, settings, metadata);\n if (result && typeof result === 'object' && result.id) {\n providerMessageId = result.id;\n result = true;\n }\n break;\n case 'sendpulse':\n result = await this.sendViaSendPulse(to, subject, htmlContent, textContent, settings, metadata);\n break;\n default:\n console.error(`[EmailService] Unknown provider: ${useProvider}`);\n // Log failure\n if (this.emailLogger) {\n try {\n await this.emailLogger({\n event: 'failed',\n recipientEmail: to,\n recipientUserId: userId,\n templateId,\n subject,\n provider: useProvider,\n batchId,\n error: `Unknown email provider: ${useProvider}`,\n metadata\n });\n } catch (e) { /* ignore */ }\n }\n return { success: false, error: `Unknown email provider: ${useProvider}` };\n }\n\n if (result) {\n const messageId = providerMessageId || crypto.randomUUID();\n // Log success\n if (this.emailLogger) {\n try {\n await this.emailLogger({\n event: 'sent',\n recipientEmail: to,\n recipientUserId: userId,\n templateId,\n subject,\n provider: useProvider,\n messageId,\n batchId,\n metadata\n });\n } catch (e) {\n console.warn('[EmailService] emailLogger sent failed:', e);\n }\n }\n return { success: true, messageId };\n } else {\n console.error('[EmailService] Failed to send email to:', to);\n // Log failure\n if (this.emailLogger) {\n try {\n await this.emailLogger({\n event: 'failed',\n recipientEmail: to,\n recipientUserId: userId,\n templateId,\n subject,\n provider: useProvider,\n batchId,\n error: 'Failed to send email',\n metadata\n });\n } catch (e) { /* ignore */ }\n }\n return { success: false, error: 'Failed to send email' };\n }\n } catch (error) {\n console.error('[EmailService] Error sending email:', error);\n // Log exception\n if (this.emailLogger) {\n try {\n await this.emailLogger({\n event: 'failed',\n recipientEmail: to,\n recipientUserId: userId,\n templateId,\n subject,\n batchId,\n error: error.message,\n metadata\n });\n } catch (e) { /* ignore */ }\n }\n return { success: false, error: error.message };\n }\n }\n\n /**\n * Send multiple emails in batch\n * @param {Array} emails - Array of email objects\n * @returns {Promise<Array>} Array of delivery results\n */\n async sendBatch(emails) {\n console.log('[EmailService] Sending batch of', emails.length, 'emails');\n const results = await Promise.all(\n emails.map(email => this.sendEmail(email))\n );\n return results;\n }\n\n /**\n * Send email via MailChannels HTTP API\n * MailChannels is specifically designed for Cloudflare Workers\n */\n async sendViaMailChannels(to, subject, html, text, settings, metadata) {\n try {\n const response = await fetch('https://api.mailchannels.net/tx/v1/send', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n personalizations: [{ to: [{ email: to, name: metadata.recipientName || '' }] }],\n from: { email: settings.fromAddress, name: settings.fromName },\n subject,\n content: [\n { type: 'text/plain', value: text || html.replace(/<[^>]*>/g, '') },\n { type: 'text/html', value: html }\n ]\n })\n });\n\n if (response.status === 202) {\n return true;\n } else {\n const contentType = response.headers.get('content-type');\n const errorBody = contentType?.includes('application/json')\n ? await response.json()\n : await response.text();\n console.error('[EmailService] MailChannels error:', response.status, errorBody);\n return false;\n }\n } catch (error) {\n console.error('[EmailService] MailChannels exception:', error.message);\n return false;\n }\n }\n\n /**\n * Send email via SendGrid HTTP API\n */\n async sendViaSendGrid(to, subject, html, text, settings, metadata) {\n try {\n if (!settings.sendgridApiKey) {\n console.error('[EmailService] SendGrid API key missing');\n return false;\n }\n\n const response = await fetch('https://api.sendgrid.com/v3/mail/send', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${settings.sendgridApiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n personalizations: [{ to: [{ email: to, name: metadata.recipientName || '' }] }],\n from: { email: settings.fromAddress, name: settings.fromName },\n subject,\n content: [\n { type: 'text/html', value: html },\n { type: 'text/plain', value: text || html.replace(/<[^>]*>/g, '') },\n ],\n }),\n });\n\n if (response.status === 202) {\n return true;\n } else {\n const errorText = await response.text();\n console.error('[EmailService] SendGrid error:', response.status, errorText);\n return false;\n }\n } catch (error) {\n console.error('[EmailService] SendGrid exception:', error.message);\n return false;\n }\n }\n\n /**\n * Send email via Resend HTTP API\n */\n async sendViaResend(to, subject, html, text, settings, metadata) {\n try {\n if (!settings.resendApiKey) {\n console.error('[EmailService] Resend API key missing');\n return false;\n }\n\n const response = await fetch('https://api.resend.com/emails', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${settings.resendApiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n from: `${settings.fromName} <${settings.fromAddress}>`,\n to: [to],\n subject,\n html,\n text: text || html.replace(/<[^>]*>/g, ''),\n }),\n });\n\n if (response.ok) {\n return true;\n } else {\n const errorText = await response.text();\n console.error('[EmailService] Resend error:', response.status, errorText);\n return false;\n }\n } catch (error) {\n console.error('[EmailService] Resend exception:', error.message);\n return false;\n }\n }\n\n /**\n * Send email via SendPulse HTTP API\n * SendPulse offers 15,000 free emails/month\n */\n async sendViaSendPulse(to, subject, html, text, settings, metadata) {\n try {\n if (!settings.sendpulseClientId || !settings.sendpulseClientSecret) {\n console.error('[EmailService] SendPulse credentials missing');\n return false;\n }\n\n // SendPulse uses OAuth2 token-based authentication\n const tokenParams = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: settings.sendpulseClientId,\n client_secret: settings.sendpulseClientSecret,\n });\n\n const tokenResponse = await fetch('https://api.sendpulse.com/oauth/access_token', {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: tokenParams.toString(),\n });\n\n if (!tokenResponse.ok) {\n const error = await tokenResponse.text();\n console.error('[EmailService] SendPulse auth error:', error);\n return false;\n }\n\n const tokenData = await tokenResponse.json();\n if (!tokenData.access_token) {\n console.error('[EmailService] SendPulse: No access token in response');\n return false;\n }\n\n const { access_token } = tokenData;\n\n // Safe base64 encoding\n const toBase64 = (str) => {\n if (!str) return '';\n try {\n return btoa(unescape(encodeURIComponent(String(str))));\n } catch (e) {\n console.error('[EmailService] Base64 encoding failed:', e);\n return '';\n }\n };\n\n // Ensure html/text are strings\n const htmlSafe = html || '';\n const textSafe = text || (htmlSafe ? htmlSafe.replace(/<[^>]*>/g, '') : '');\n\n // Send the email\n const response = await fetch('https://api.sendpulse.com/smtp/emails', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${access_token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n email: {\n html: toBase64(htmlSafe),\n text: toBase64(textSafe),\n subject,\n from: { name: settings.fromName, email: settings.fromAddress },\n to: [{ name: metadata.recipientName || '', email: to }],\n },\n }),\n });\n\n if (response.ok) {\n return true;\n } else {\n const errorText = await response.text();\n console.error('[EmailService] SendPulse send error:', response.status, errorText);\n return false;\n }\n } catch (error) {\n console.error('[EmailService] SendPulse exception:', error.message);\n return false;\n }\n }\n}\n\n","/**\n * HTML Email Wrapper\n * Full styled HTML template for wrapping email content\n */\n\n/**\n * Wrap content HTML in email template with styling\n * @param {string} contentHtml - The HTML content to wrap\n * @param {string} subject - Email subject for the title\n * @param {Object} data - Template data (portalUrl, unsubscribeUrl, etc.)\n * @returns {string} Complete HTML email\n */\nexport function wrapInEmailTemplate(contentHtml, subject, data = {}) {\n const portalUrl = data.portalUrl || 'https://app.x0start.com';\n const unsubscribeUrl = data.unsubscribeUrl || '{{unsubscribe_url}}';\n const brandName = data.brandName || 'X0 Start';\n\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${subject}</title>\n <style>\n body {\n margin: 0;\n padding: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n background-color: #f5f5f5;\n line-height: 1.6;\n }\n .email-wrapper {\n background-color: #f5f5f5;\n padding: 40px 20px;\n }\n .email-container {\n max-width: 600px;\n margin: 0 auto;\n background-color: #ffffff;\n border-radius: 8px;\n overflow: hidden;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n }\n .email-header {\n padding: 40px 40px 20px;\n text-align: center;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: #ffffff;\n }\n .email-header h1 {\n margin: 0;\n font-size: 28px;\n font-weight: 600;\n }\n .email-content {\n padding: 30px 40px;\n color: #333333;\n }\n .email-content h1 {\n font-size: 24px;\n margin-top: 0;\n margin-bottom: 20px;\n color: #333333;\n }\n .email-content h2 {\n font-size: 20px;\n margin-top: 30px;\n margin-bottom: 15px;\n color: #333333;\n }\n .email-content h3 {\n font-size: 16px;\n margin-top: 20px;\n margin-bottom: 10px;\n color: #333333;\n }\n .email-content p {\n margin: 0 0 15px;\n color: #666666;\n }\n .email-content a {\n color: #667eea;\n text-decoration: none;\n }\n .email-content ul, .email-content ol {\n margin: 0 0 15px;\n padding-left: 25px;\n }\n .email-content li {\n margin-bottom: 8px;\n color: #666666;\n }\n .email-content blockquote {\n margin: 20px 0;\n padding: 15px 20px;\n background-color: #f8f9fa;\n border-left: 4px solid #667eea;\n color: #666666;\n }\n .email-content code {\n padding: 2px 6px;\n background-color: #f8f9fa;\n border-radius: 3px;\n font-family: 'Courier New', monospace;\n font-size: 14px;\n }\n .email-content pre {\n padding: 15px;\n background-color: #f8f9fa;\n border-radius: 6px;\n overflow-x: auto;\n }\n .email-content pre code {\n padding: 0;\n background: none;\n }\n .btn {\n display: inline-block;\n padding: 12px 24px;\n background-color: #667eea;\n color: #ffffff !important;\n text-decoration: none;\n border-radius: 6px;\n font-weight: 600;\n margin: 10px 0;\n }\n .btn:hover {\n background-color: #5568d3;\n }\n .email-footer {\n padding: 20px 40px;\n background-color: #f8f9fa;\n text-align: center;\n font-size: 12px;\n color: #666666;\n }\n .email-footer a {\n color: #667eea;\n text-decoration: none;\n }\n hr {\n border: none;\n border-top: 1px solid #e0e0e0;\n margin: 30px 0;\n }\n </style>\n</head>\n<body>\n <div class=\"email-wrapper\">\n <div class=\"email-container\">\n <div class=\"email-content\">\n ${contentHtml}\n </div>\n <div class=\"email-footer\">\n <p style=\"margin: 0 0 10px;\">\n You're receiving this email from ${brandName}.\n </p>\n <p style=\"margin: 0;\">\n <a href=\"${unsubscribeUrl}\">Unsubscribe</a> | \n <a href=\"${portalUrl}/settings/notifications\">Manage Preferences</a>\n </p>\n </div>\n </div>\n </div>\n</body>\n</html>\n `.trim();\n}\n","/**\n * EmailingCacheDO - Optional read-through cache for email templates and settings\n * \n * This is a Cloudflare Durable Object that provides caching for email templates and settings.\n * \n * Configurable via environment variable:\n * - EMAIL_TABLE_PREFIX: Prefix for tables (default: 'system_email_')\n */\nexport class EmailingCacheDO {\n constructor(state, env) {\n this.state = state;\n this.env = env;\n this.cache = new Map(); // templateId -> { data, timestamp }\n this.settingsCache = new Map(); // key -> { data, timestamp }\n this.cacheTTL = 3600000; // 1 hour in milliseconds\n // Templates use the prefix\n this.emailTablePrefix = env.EMAIL_TABLE_PREFIX || 'system_email_';\n // Settings use a specific table name (defaulting to system_settings)\n // This allows decoupling template prefix from settings table\n this.settingsTableName = env.EMAIL_SETTINGS_TABLE || 'system_settings';\n\n // Optional: Filter settings by key prefix (e.g. 'email_')\n // This allows sharing the system_settings table with other apps\n this.settingsKeyPrefix = env.EMAIL_SETTINGS_KEY_PREFIX || '';\n }\n\n /**\n * Handle HTTP requests to this Durable Object\n */\n async fetch(request) {\n const url = new URL(request.url);\n const path = url.pathname;\n\n try {\n if (path === '/get' && request.method === 'GET') {\n return this.handleGet(request);\n } else if (path === '/invalidate' && request.method === 'POST') {\n return this.handleInvalidate(request);\n } else if (path === '/clear' && request.method === 'POST') {\n return this.handleClear(request);\n } else if (path === '/stats' && request.method === 'GET') {\n return this.handleStats(request);\n }\n // Settings endpoints\n else if (path === '/settings/get' && request.method === 'GET') {\n return this.handleGetSettings(request);\n } else if (path === '/settings/put' && request.method === 'POST') {\n return this.handlePutSettings(request);\n } else if (path === '/settings/invalidate' && request.method === 'POST') {\n return this.handleInvalidateSettings(request);\n } else {\n return new Response('Not Found', { status: 404 });\n }\n } catch (error) {\n console.error('[EmailingCacheDO] Error:', error);\n return new Response(JSON.stringify({ error: error.message }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n }\n\n /**\n * Get template (from cache or D1)\n */\n async handleGet(request) {\n const url = new URL(request.url);\n const templateId = url.searchParams.get('templateId');\n const forceRefresh = url.searchParams.get('refresh') === 'true';\n\n if (!templateId) {\n return new Response(JSON.stringify({ error: 'templateId is required' }), {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // Check cache (unless force refresh)\n if (!forceRefresh) {\n const cached = this.cache.get(templateId);\n if (cached && Date.now() - cached.timestamp < this.cacheTTL) {\n console.log('[EmailingCacheDO] Cache HIT:', templateId);\n return new Response(JSON.stringify({\n template: cached.data,\n cached: true,\n age: Date.now() - cached.timestamp,\n }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n }\n\n // Cache miss or expired - fetch from D1\n console.log('[EmailingCacheDO] Cache MISS - fetching from D1:', templateId);\n const template = await this.fetchTemplateFromD1(templateId);\n\n if (!template) {\n return new Response(JSON.stringify({\n error: 'Template not found',\n templateId\n }), {\n status: 404,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // Cache the result\n this.cache.set(templateId, {\n data: template,\n timestamp: Date.now(),\n });\n\n return new Response(JSON.stringify({\n template,\n cached: false,\n }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // --- Settings Cache Handlers ---\n\n async handleGetSettings(request) {\n const url = new URL(request.url);\n const key = url.searchParams.get('key');\n\n if (!key) return new Response('Key required', { status: 400 });\n\n // Check Cache\n const cached = this.settingsCache.get(key);\n if (cached && Date.now() - cached.timestamp < this.cacheTTL) {\n console.log('[EmailingCacheDO] Settings Cache HIT:', key);\n return new Response(JSON.stringify({ settings: cached.data }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // Cache Miss - Fetch from D1 (Read-Through)\n // Only fetch from D1 for system settings (consistent with handlePutSettings)\n if (key.startsWith('system')) {\n console.log('[EmailingCacheDO] Settings Cache MISS - fetching from D1:', key);\n const settings = await this.fetchSettingsFromD1(key);\n\n // Even if null, we might want to cache the \"null\" result to prevent hammer? \n // For now, only cache if we found something or explicitly handle negative caching.\n if (settings) {\n this.settingsCache.set(key, {\n data: settings,\n timestamp: Date.now()\n });\n return new Response(JSON.stringify({ settings }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n }\n\n return new Response(JSON.stringify({ settings: null }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n async handlePutSettings(request) {\n try {\n const body = await request.json();\n const { key, settings } = body;\n\n if (!key || !settings) return new Response('Key and settings required', { status: 400 });\n\n // Write to Cache\n this.settingsCache.set(key, {\n data: settings,\n timestamp: Date.now()\n });\n\n // Write to D1 (Write-Through)\n // Note: only supports writing to \"system\" (default table) currently\n if (key.startsWith('system')) {\n await this.saveSettingsToD1(settings);\n }\n\n return new Response(JSON.stringify({ success: true }), {\n headers: { 'Content-Type': 'application/json' },\n });\n } catch (e) {\n console.error('[EmailingCacheDO] PutSettings error:', e);\n return new Response('Error parsing body/saving', { status: 400 });\n }\n }\n\n async handleInvalidateSettings(request) {\n try {\n const body = await request.json();\n const { key } = body;\n\n if (!key) return new Response('Key required', { status: 400 });\n\n const existed = this.settingsCache.has(key);\n this.settingsCache.delete(key);\n console.log('[EmailingCacheDO] Invalidated settings:', key, existed ? '(existed)' : '(not in cache)');\n\n return new Response(JSON.stringify({ success: true, existed }), {\n headers: { 'Content-Type': 'application/json' },\n });\n } catch (e) {\n console.error('[EmailingCacheDO] InvalidateSettings error:', e);\n return new Response('Error invalidated settings', { status: 400 });\n }\n }\n\n /**\n * Invalidate specific template(s) from cache\n * Body: { templateId: 'template_id' } or { templateId: '*' } for all\n */\n async handleInvalidate(request) {\n const body = await request.json();\n const { templateId } = body;\n\n if (!templateId) {\n return new Response(JSON.stringify({ error: 'templateId is required' }), {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n if (templateId === '*') {\n // Invalidate all templates\n const count = this.cache.size;\n this.cache.clear();\n console.log('[EmailingCacheDO] Invalidated ALL templates:', count);\n return new Response(JSON.stringify({\n success: true,\n message: `Invalidated ${count} templates`,\n }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // Invalidate specific template\n const existed = this.cache.has(templateId);\n this.cache.delete(templateId);\n console.log('[EmailingCacheDO] Invalidated template:', templateId, existed ? '(existed)' : '(not in cache)');\n\n return new Response(JSON.stringify({\n success: true,\n message: existed ? 'Template invalidated' : 'Template was not in cache',\n templateId,\n }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n /**\n * Clear entire cache (admin operation)\n */\n async handleClear(request) {\n const count = this.cache.size + this.settingsCache.size;\n this.cache.clear();\n this.settingsCache.clear();\n console.log('[EmailingCacheDO] Cache cleared:', count, 'entries (templates + settings)');\n\n return new Response(JSON.stringify({\n success: true,\n message: `Cleared ${count} cached items`,\n }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n /**\n * Get cache statistics\n */\n async handleStats(request) {\n const stats = {\n templates: this.cache.size,\n settings: this.settingsCache.size,\n cacheTTL: this.cacheTTL,\n };\n\n return new Response(JSON.stringify(stats), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n /**\n * Fetch template from D1\n */\n async fetchTemplateFromD1(templateId) {\n const db = this.env.DB;\n const tableName = `${this.emailTablePrefix}templates`;\n\n try {\n const template = await db\n .prepare(`SELECT * FROM ${tableName} WHERE template_id = ? AND is_active = 1`)\n .bind(templateId)\n .first();\n return template;\n } catch (e) {\n console.error(`[EmailingCacheDO] DB Error (${tableName}):`, e);\n return null;\n }\n }\n\n /**\n * Fetch settings from D1\n * Only supports default table (e.g. system_settings) for now\n */\n async fetchSettingsFromD1(key) {\n const db = this.env.DB;\n const tableName = this.settingsTableName;\n const prefix = this.settingsKeyPrefix;\n\n try {\n let results;\n\n if (prefix) {\n // Filter by prefix (e.g. WHERE setting_key LIKE 'email_%')\n // Note: We need to handle both schema variants (setting_key vs key)\n // BUT standard D1 SQL doesn't support \"OR\" in column names easily with LIKE parameter binding\n // for flexible schemas.\n // We will assume 'setting_key' matches if table is system_settings, or try both.\n\n // Construct query dynamically based on likely schema or just try standard first\n try {\n results = await db.prepare(`SELECT * FROM ${tableName} WHERE setting_key LIKE ?`).bind(`${prefix}%`).all();\n } catch (e) {\n // Fallback to 'key' column\n results = await db.prepare(`SELECT * FROM ${tableName} WHERE key LIKE ?`).bind(`${prefix}%`).all();\n }\n } else {\n // No prefix, fetch all\n results = await db.prepare(`SELECT * FROM ${tableName}`).all();\n }\n\n if (!results.results || results.results.length === 0) return null;\n\n const settings = {};\n results.results.forEach(row => {\n const k = row.setting_key || row.key;\n const v = row.setting_value || row.value;\n if (k) settings[k] = v;\n });\n return settings;\n } catch (e) {\n console.warn(`[EmailingCacheDO] Failed to load settings from ${tableName}:`, e);\n return null;\n }\n }\n\n async saveSettingsToD1(settings) {\n const db = this.env.DB;\n const tableName = this.settingsTableName;\n\n // Simple upset not easy in D1 without specific keys.\n // We'll rely on calling code/migration. \n // For now, let's just log or try to update if possible.\n // Actually, writing settings is complex because we don't know the schema perfectly.\n // Let's implement a BEST EFFORT update for x0start schema\n\n const entries = Object.entries(settings);\n for (const [k, v] of entries) {\n try {\n // Heuristic: Try to determine column names based on common conventions\n // We'll try Schema 1 (setting_key/value) first as it's the default internal convention\n // If that fails, we might fall back to Schema 2 (key/value) in a real implementation\n // For now, let's try a best-effort upsert based on x0start schema or generic\n\n try {\n const res = await db.prepare(`UPDATE ${tableName} SET setting_value = ? WHERE setting_key = ?`).bind(v, k).run();\n if (res.meta.changes === 0) {\n await db.prepare(`INSERT INTO ${tableName} (setting_key, setting_value) VALUES (?, ?)`).bind(k, v).run();\n }\n } catch (e) {\n // Fallback to key/value\n await db.prepare(`INSERT OR REPLACE INTO ${tableName} (key, value) VALUES (?, ?)`).bind(k, v).run();\n }\n } catch (e) {\n console.warn('[EmailingCacheDO] Failed to save setting to D1:', k);\n }\n }\n }\n}\n\n/**\n * Create a cache provider wrapper for the DO\n */\nexport function createDOCacheProvider(doStub, instanceName = 'global') {\n if (!doStub) {\n return null;\n }\n\n const stub = doStub.get(doStub.idFromName(instanceName));\n\n return {\n async getTemplate(templateId) {\n try {\n const response = await stub.fetch(`http://do/get?templateId=${templateId}`);\n const data = await response.json();\n return data.template || null;\n } catch (e) {\n // Silently fall back to DB - DO cache is optional\n return null;\n }\n },\n\n async getSettings(profile, tenantId) {\n const key = `${profile}:${tenantId || ''}`;\n try {\n const response = await stub.fetch(`http://do/settings/get?key=${encodeURIComponent(key)}`);\n const data = await response.json();\n return data.settings || null;\n } catch (e) {\n return null;\n }\n },\n\n async putSettings(profile, tenantId, settings) {\n const key = `${profile}:${tenantId || ''}`;\n try {\n await stub.fetch('http://do/settings/put', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ key, settings }),\n });\n } catch (e) {\n console.warn('[DOCacheProvider] Failed to cache settings:', e);\n }\n },\n\n async putTemplate(template) {\n try {\n await stub.fetch('http://do/invalidate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ templateId: template.template_id }),\n });\n } catch (e) {\n console.warn('[DOCacheProvider] Failed to invalidate template:', e);\n }\n },\n\n async deleteTemplate(templateId) {\n try {\n await stub.fetch('http://do/invalidate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ templateId }),\n });\n } catch (e) {\n console.warn('[DOCacheProvider] Failed to invalidate template:', e);\n }\n },\n\n async invalidateSettings(profile, tenantId) {\n const key = `${profile}:${tenantId || ''}`;\n try {\n await stub.fetch('http://do/settings/invalidate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ key }),\n });\n } catch (e) {\n console.warn('[DOCacheProvider] Failed to invalidate settings:', e);\n }\n },\n };\n}\n","/**\n * Email Logger Utility\n * \n * Provides built-in email logging to D1 database.\n * Can be used directly or passed to EmailService as the emailLogger callback.\n */\n\nexport class EmailLogger {\n /**\n * @param {Object} db - D1 database binding\n * @param {Object} options - Configuration options\n * @param {string} [options.tableName='system_email_logs'] - Table name for logs\n */\n constructor(db, options = {}) {\n this.db = db;\n this.tableName = options.tableName || 'system_email_logs';\n }\n\n /**\n * Creates a logger callback function for use with EmailService.\n * Usage: new EmailService(env, { emailLogger: emailLogger.createCallback() })\n */\n createCallback() {\n return async (entry) => {\n await this.log(entry);\n };\n }\n\n /**\n * Log an email event (pending, sent, or failed)\n * @param {Object} entry - Log entry\n */\n async log(entry) {\n const {\n event,\n recipientEmail,\n recipientUserId,\n templateId,\n subject,\n provider,\n messageId,\n batchId,\n error,\n errorCode,\n metadata\n } = entry;\n\n try {\n if (event === 'pending') {\n // Create new log entry\n const id = crypto.randomUUID().replace(/-/g, '');\n await this.db.prepare(`\n INSERT INTO ${this.tableName} \n (id, batch_id, recipient_email, recipient_user_id, template_id, subject, status, metadata, created_at)\n VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, strftime('%s', 'now'))\n `).bind(\n id,\n batchId || null,\n recipientEmail,\n recipientUserId || null,\n templateId || 'direct',\n subject || null,\n metadata ? JSON.stringify(metadata) : null\n ).run();\n } else if (event === 'sent') {\n // Update existing entry to sent\n await this.db.prepare(`\n UPDATE ${this.tableName} \n SET status = 'sent', \n provider = ?, \n provider_message_id = ?, \n sent_at = strftime('%s', 'now')\n WHERE recipient_email = ? \n AND template_id = ? \n AND status = 'pending'\n ORDER BY created_at DESC\n LIMIT 1\n `).bind(\n provider || null,\n messageId || null,\n recipientEmail,\n templateId || 'direct'\n ).run();\n } else if (event === 'failed') {\n // Update existing entry to failed\n await this.db.prepare(`\n UPDATE ${this.tableName} \n SET status = 'failed', \n provider = ?, \n error_message = ?,\n error_code = ?\n WHERE recipient_email = ? \n AND template_id = ? \n AND status = 'pending'\n ORDER BY created_at DESC\n LIMIT 1\n `).bind(\n provider || null,\n error || null,\n errorCode || null,\n recipientEmail,\n templateId || 'direct'\n ).run();\n }\n } catch (e) {\n console.error('[EmailLogger] Failed to log:', e);\n }\n }\n\n /**\n * Query email logs with filtering\n * @param {Object} options - Query options\n */\n async query(options = {}) {\n const {\n recipientEmail,\n recipientUserId,\n templateId,\n status,\n batchId,\n limit = 50,\n offset = 0\n } = options;\n\n const conditions = [];\n const bindings = [];\n\n if (recipientEmail) {\n conditions.push('recipient_email = ?');\n bindings.push(recipientEmail);\n }\n if (recipientUserId) {\n conditions.push('recipient_user_id = ?');\n bindings.push(recipientUserId);\n }\n if (templateId) {\n conditions.push('template_id = ?');\n bindings.push(templateId);\n }\n if (status) {\n conditions.push('status = ?');\n bindings.push(status);\n }\n if (batchId) {\n conditions.push('batch_id = ?');\n bindings.push(batchId);\n }\n\n const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n\n // Get total count\n const countResult = await this.db.prepare(\n `SELECT COUNT(*) as count FROM ${this.tableName} ${whereClause}`\n ).bind(...bindings).first();\n\n // Get logs\n const { results } = await this.db.prepare(`\n SELECT id, batch_id, recipient_email, recipient_user_id, template_id, subject,\n status, provider, provider_message_id, error_message, error_code, metadata,\n created_at, sent_at\n FROM ${this.tableName}\n ${whereClause}\n ORDER BY created_at DESC\n LIMIT ? OFFSET ?\n `).bind(...bindings, limit, offset).all();\n\n const logs = (results || []).map(row => ({\n id: row.id,\n batchId: row.batch_id,\n recipientEmail: row.recipient_email,\n recipientUserId: row.recipient_user_id,\n templateId: row.template_id,\n subject: row.subject,\n status: row.status,\n provider: row.provider,\n providerMessageId: row.provider_message_id,\n errorMessage: row.error_message,\n errorCode: row.error_code,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n createdAt: row.created_at,\n sentAt: row.sent_at\n }));\n\n return { logs, total: countResult?.count || 0 };\n }\n\n /**\n * Get email sending statistics\n * @param {number} sinceDays - Number of days to look back\n */\n async getStats(sinceDays = 7) {\n const sinceTimestamp = Math.floor(Date.now() / 1000) - (sinceDays * 24 * 60 * 60);\n\n const statusResult = await this.db.prepare(`\n SELECT status, COUNT(*) as count\n FROM ${this.tableName}\n WHERE created_at >= ?\n GROUP BY status\n `).bind(sinceTimestamp).all();\n\n const templateResult = await this.db.prepare(`\n SELECT template_id, COUNT(*) as count\n FROM ${this.tableName}\n WHERE created_at >= ?\n GROUP BY template_id\n `).bind(sinceTimestamp).all();\n\n const stats = {\n total: 0,\n sent: 0,\n failed: 0,\n pending: 0,\n byTemplate: {}\n };\n\n (statusResult.results || []).forEach(row => {\n const count = row.count || 0;\n stats.total += count;\n if (row.status === 'sent') stats.sent = count;\n if (row.status === 'failed') stats.failed = count;\n if (row.status === 'pending') stats.pending = count;\n });\n\n (templateResult.results || []).forEach(row => {\n stats.byTemplate[row.template_id] = row.count;\n });\n\n return stats;\n }\n\n /**\n * Get recent failed emails for debugging\n * @param {number} limit - Number of failed emails to retrieve\n */\n async getRecentFailures(limit = 20) {\n const { results } = await this.db.prepare(`\n SELECT id, recipient_email, template_id, subject, error_message, error_code, created_at\n FROM ${this.tableName}\n WHERE status = 'failed'\n ORDER BY created_at DESC\n LIMIT ?\n `).bind(limit).all();\n\n return results || [];\n }\n}\n\n/**\n * Create a simple logger callback for EmailService that logs to D1.\n * This is a convenience function for quick setup.\n * \n * Usage:\n * const emailService = new EmailService(env, {\n * emailLogger: createEmailLoggerCallback(env.DB)\n * });\n */\nexport function createEmailLoggerCallback(db, tableName = 'system_email_logs') {\n const logger = new EmailLogger(db, { tableName });\n return logger.createCallback();\n}\n","/**\n * Email Routes Index\n * Aggregates and exports all route factories\n */\nimport { Hono } from 'hono';\nimport { createSettingsRoutes } from './settings.js';\nimport { createTemplateRoutes } from './templates.js';\nimport { createTrackingRoutes } from './tracking.js';\nimport { createLogRoutes } from './logs.js';\n\n// Re-export individual route factories\n// Re-export individual route factories\nexport {\n createSettingsRoutes,\n createTemplateRoutes,\n createTrackingRoutes,\n createLogRoutes\n};\n\n/**\n * Create combined email routes\n * @param {Object} config - Configuration\n * @param {Object} cacheProvider - Optional cache provider\n * @returns {Hono} Combined Hono router\n */\nexport function createEmailRoutes(config = {}, cacheProvider = null) {\n const app = new Hono();\n\n // Mount template routes at /api/email typically, but here we just return the router\n // Users will mount this router at /api/email or similar\n\n // We want hierarchical routes:\n // /templates -> Template Routes\n // /logs -> Log Routes\n // /settings -> Settings Routes\n // / -> Tracking? No, tracking usually goes to public endpoint\n\n // Let's assume this router is mounted at /api/email\n\n app.route('/templates', createTemplateRoutes(config, cacheProvider));\n app.route('/logs', createLogRoutes(config));\n app.route('/settings', createSettingsRoutes({\n ...config,\n tableName: config.settingsTableName || 'system_settings',\n keyPrefix: config.settingsKeyPrefix || 'system_email.'\n }));\n\n // Support root level send-test if desired, but templates route handles it now\n\n // Tracking usually needs to be public, so we don't mix it with admin routes usually\n // But for convenience we can export it separately or mount if config says so\n\n return app;\n}\n","import { Hono } from 'hono';\n\n/**\n * Create settings routes for generic key-value storage\n * @param {Object} env - Environment bindings\n * @param {Object} config - Configuration\n * @param {string} [config.tableName='system_settings'] - Table name for settings\n * @param {string} [config.keyPrefix='system_email.'] - Prefix for keys managed by this router\n */\nexport function createSettingsRoutes(config = {}) {\n const app = new Hono();\n const tableName = config.tableName || 'system_settings';\n const keyPrefix = config.keyPrefix || 'system_email.';\n\n // Helper to get settings with prefix\n const getSettings = async (db) => {\n // We select ALL and filter in code because 'LIKE' might be slower or we want full flexibility\n // But for efficiency, let's use LIKE if possible.\n // Assuming keys are stored with prefix strings.\n\n try {\n const { results } = await db.prepare(`SELECT * FROM ${tableName} WHERE key LIKE ?`)\n .bind(`${keyPrefix}%`)\n .all();\n\n const settings = {};\n for (const row of results) {\n // Remove prefix for the API response\n const cleanKey = row.key.slice(keyPrefix.length);\n settings[cleanKey] = row.value;\n }\n return settings;\n } catch (e) {\n // Table might not exist yet if migration hasn't run\n console.warn(`[Settings] Failed to fetch from ${tableName}:`, e.message);\n return {};\n }\n };\n\n // GET / - Retrieve settings (scoped by prefix)\n app.get('/', async (c) => {\n try {\n const settings = await getSettings(c.env.DB);\n\n // Map known keys to frontend expected format if needed\n // But since I changed the DB strategy, let's just return what we have\n // Frontend expects camelCase properties usually.\n\n // We need to map snake_case DB keys (minus prefix) to camelCase frontend props\n // OR we just store them as camelCase in the DB?\n // \"system_email.fromName\" vs \"system_email.from_name\"\n\n // Let's standardize on camelCase for the keys stored in DB for simplicity with frontend?\n // User requested \"system_email.xxx\".\n\n // Let's assume keys are stored as `system_email.provider`, `system_email.fromName` etc.\n // This matches the frontend JSON payloads directly.\n\n return c.json(settings);\n } catch (error) {\n console.error('Failed to fetch settings:', error);\n return c.json({ error: error.message }, 500);\n }\n });\n\n // POST / - Update settings\n app.post('/', async (c) => {\n try {\n const body = await c.req.json();\n const db = c.env.DB;\n\n // Body is { provider: '...', fromName: '...' }\n // We need to upsert these with prefix.\n\n const updates = Object.entries(body).map(([k, v]) => ({\n key: `${keyPrefix}${k}`,\n value: v\n }));\n\n if (updates.length === 0) return c.json({ success: true });\n\n const stmt = db.prepare(`\n INSERT INTO ${tableName} (key, value, updated_at) \n VALUES (?, ?, strftime('%s', 'now'))\n ON CONFLICT(key) DO UPDATE SET \n value = excluded.value, \n updated_at = excluded.updated_at\n `);\n\n const batch = updates.map(u => stmt.bind(u.key, u.value === undefined || u.value === null ? '' : String(u.value)));\n\n await db.batch(batch);\n\n return c.json({ success: true });\n } catch (error) {\n console.error('Failed to save settings:', error);\n return c.json({ error: error.message }, 500);\n }\n });\n\n return app;\n}\n","/**\n * Email Template Routes Factory\n * Creates a Hono router with admin endpoints for template management.\n */\nimport { Hono } from 'hono';\nimport { EmailService } from '../EmailService.js';\n\n/**\n * Create email template routes\n * @param {Object} config - Configuration\n * @returns {Hono} Hono router\n */\nexport function createTemplateRoutes(config = {}) {\n const app = new Hono();\n\n // List all templates\n app.get('/', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n const templates = await emailService.getAllTemplates();\n // Wrap in object if needed, or return array directly to match current API expectation\n // Current API returns array directly\n return c.json(templates);\n } catch (err) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n // Get single template\n app.get('/:id', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n const template = await emailService.getTemplate(c.req.param('id'));\n if (!template) return c.json({ error: 'Template not found' }, 404);\n return c.json(template);\n } catch (err) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n // Create/Update template\n app.post('/', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n const data = await c.req.json();\n\n // Try to get user ID from various common contexts\n let userId = 'admin';\n const user = c.get('dbUser') || c.get('user');\n if (user && user.id) userId = user.id;\n\n // Basic validation\n if (!data.template_id || !data.subject_template) {\n return c.json({ error: 'Missing required fields (template_id, subject_template)' }, 400);\n }\n\n const result = await emailService.saveTemplate(data, userId);\n\n // Return format matching current API\n return c.json({ success: true, message: 'Template saved', result });\n } catch (err) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n // Send test email\n // This was mounted at /templates/:id/test in library but /send-test in backend API\n // Let's support the backend API style for compatibility\n app.post('/send-test', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n // Support both old library way (param) and new flat way (body only)\n // But here we are mounting at /api/email/templates usually, so /send-test might need to be at root of email router\n // We will address this in the index router composition\n\n const { template_id, to, variables } = await c.req.json();\n let tid = template_id;\n\n if (!tid || !to) {\n return c.json({ error: 'Missing required fields (template_id, to)' }, 400);\n }\n\n const user = c.get('dbUser') || c.get('user');\n const recipientUserId = user?.id || null;\n\n const result = await emailService.sendViaTemplate(tid, variables || {}, {\n to,\n profile: 'test',\n recipientUserId\n });\n\n return c.json({ success: true, result });\n } catch (err) {\n return c.json({ error: `Failed to send test email: ${err.message}` }, 500);\n }\n });\n\n // Delete template\n app.delete('/:id', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n await emailService.deleteTemplate(c.req.param('id'));\n return c.json({ success: true, message: 'Template deleted' });\n } catch (err) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n // Preview template\n app.post('/:id/preview', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n const id = c.req.param('id');\n const data = await c.req.json(); // Variables\n const result = await emailService.renderTemplate(id, data);\n return c.json({ success: true, preview: result });\n } catch (err) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n return app;\n}\n","/**\n * Email Tracking Routes Factory\n * Creates a Hono router with tracking endpoints for the email system.\n */\nimport { Hono } from 'hono';\n\n// 1x1 transparent GIF pixel\nconst TRACKING_PIXEL = new Uint8Array([\n 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00,\n 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x21,\n 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00,\n 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x01, 0x44,\n 0x00, 0x3B\n]);\n\n/**\n * Create email tracking routes\n * @param {Object} env - Environment bindings\n * @param {Object} config - Configuration\n * @param {string} [config.emailTablePrefix='system_email_'] - Prefix for D1 tables\n * @returns {Hono} Hono router\n */\nexport function createTrackingRoutes(env, config = {}) {\n const app = new Hono();\n const db = env.DB;\n const tablePrefix = config.emailTablePrefix || config.tableNamePrefix || 'system_email_';\n\n /**\n * GET /track/open/:token\n * Track email opens via a 1x1 pixel\n */\n app.get('/track/open/:token', async (c) => {\n const token = c.req.param('token');\n\n try {\n const sendId = token;\n\n // Look up the send record\n const send = await db\n .prepare(`SELECT * FROM ${tablePrefix}sends WHERE send_id = ?`)\n .bind(sendId)\n .first();\n\n if (send) {\n // Log the open event\n const eventId = crypto.randomUUID();\n const now = Math.floor(Date.now() / 1000);\n\n await db\n .prepare(\n `INSERT INTO ${tablePrefix}events (\n event_id, send_id, user_id, tenant_id, email_kind, event_type, metadata, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .bind(\n eventId,\n sendId,\n send.user_id,\n send.tenant_id,\n send.email_kind,\n 'opened',\n JSON.stringify({\n user_agent: c.req.header('user-agent'),\n referer: c.req.header('referer'),\n }),\n now\n )\n .run();\n }\n } catch (error) {\n console.error('[EmailTracking] Error tracking email open:', error);\n // Don't fail the request - just serve the pixel\n }\n\n // Always return the tracking pixel\n return new Response(TRACKING_PIXEL, {\n headers: {\n 'Content-Type': 'image/gif',\n 'Cache-Control': 'no-cache, no-store, must-revalidate',\n 'Pragma': 'no-cache',\n 'Expires': '0',\n },\n });\n });\n\n /**\n * POST /track/click/:token\n * Track email clicks\n */\n app.post('/track/click/:token', async (c) => {\n const token = c.req.param('token');\n\n let url;\n try {\n const body = await c.req.json();\n url = body.url;\n } catch {\n return c.json({ success: false, error: 'Invalid request body' }, 400);\n }\n\n if (!url) {\n return c.json({ success: false, error: 'Missing URL' }, 400);\n }\n\n try {\n const sendId = token;\n\n // Look up the send record\n const send = await db\n .prepare(`SELECT * FROM ${tablePrefix}sends WHERE send_id = ?`)\n .bind(sendId)\n .first();\n\n if (send) {\n // Log the click event\n const eventId = crypto.randomUUID();\n const now = Math.floor(Date.now() / 1000);\n\n await db\n .prepare(\n `INSERT INTO ${tablePrefix}events (\n event_id, send_id, user_id, tenant_id, email_kind, event_type, metadata, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .bind(\n eventId,\n sendId,\n send.user_id,\n send.tenant_id,\n send.email_kind,\n 'clicked',\n JSON.stringify({\n url: url,\n user_agent: c.req.header('user-agent'),\n referer: c.req.header('referer'),\n }),\n now\n )\n .run();\n }\n\n return c.json({ success: true, tracked: !!send });\n } catch (error) {\n console.error('[EmailTracking] Error tracking email click:', error);\n return c.json({ success: false, error: 'Failed to track click' }, 500);\n }\n });\n\n /**\n * GET /unsubscribe/:token\n * Unsubscribe a user from all email notifications\n */\n app.get('/unsubscribe/:token', async (c) => {\n const unsubToken = c.req.param('token');\n\n try {\n // Find the user by their unsubscribe token\n const prefs = await db\n .prepare(`SELECT * FROM ${tablePrefix}preferences WHERE unsub_token = ?`)\n .bind(unsubToken)\n .first();\n\n if (!prefs) {\n return c.json({ success: false, error: 'Invalid unsubscribe link' }, 404);\n }\n\n // Check if already unsubscribed\n const currentSettings = JSON.parse(prefs.email_settings || '{}');\n const alreadyUnsubscribed = Object.keys(currentSettings).length === 0;\n\n if (!alreadyUnsubscribed) {\n // Disable all email types by setting email_settings to empty object\n const now = Math.floor(Date.now() / 1000);\n await db\n .prepare(\n `UPDATE ${tablePrefix}preferences \n SET email_settings = '{}',\n updated_at = ?\n WHERE unsub_token = ?`\n )\n .bind(now, unsubToken)\n .run();\n\n // Log the unsubscribe event\n const eventId = crypto.randomUUID();\n await db\n .prepare(\n `INSERT INTO ${tablePrefix}events (\n event_id, send_id, user_id, tenant_id, email_kind, event_type, metadata, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .bind(\n eventId,\n 'unsubscribe',\n prefs.user_id,\n prefs.tenant_id,\n 'all',\n 'unsubscribed',\n JSON.stringify({\n user_agent: c.req.header('user-agent'),\n }),\n now\n )\n .run();\n }\n\n return c.json({ success: true, alreadyUnsubscribed });\n } catch (error) {\n console.error('[EmailTracking] Error processing unsubscribe:', error);\n return c.json({ success: false, error: 'An error occurred' }, 500);\n }\n });\n\n return app;\n}\n","/**\n * Email Log Routes Factory\n * Creates a Hono router with admin endpoints for email logs.\n */\nimport { Hono } from 'hono';\n\n/**\n * Create email log routes\n * @param {Object} config - Configuration\n * @returns {Hono} Hono router\n */\nexport function createLogRoutes(config = {}) {\n const app = new Hono();\n\n // List logs (paginated)\n app.get('/', async (c) => {\n const limit = Math.min(parseInt(c.req.query('limit') || '50'), 100);\n const offset = parseInt(c.req.query('offset') || '0');\n const env = c.env;\n\n try {\n const table = `${config.emailTablePrefix || 'system_email_'}logs`;\n\n const { results } = await env.DB.prepare(`\n SELECT * FROM ${table} \n ORDER BY created_at DESC \n LIMIT ? OFFSET ?\n `).bind(limit, offset).all();\n\n // Also get total count for pagination\n const countResult = await env.DB.prepare(`SELECT COUNT(*) as exact_count FROM ${table}`).first();\n\n return c.json({\n logs: results.map(row => ({\n ...row,\n metadata: row.metadata ? JSON.parse(row.metadata) : null\n })),\n total: countResult.exact_count,\n limit,\n offset\n });\n } catch (error) {\n console.error('Failed to fetch logs:', error);\n return c.json({ error: 'Failed to fetch logs' }, 500);\n }\n });\n\n return app;\n}\n","/**\n * Email Utility Functions\n * Common helpers for email processing\n */\n\nimport mustache from 'mustache';\n\n// Module-level cache for website URL\nlet cachedWebsiteUrl = null;\n\n/**\n * Build website URL based on environment configuration (with caching)\n * @param {Object} env - Environment bindings\n * @returns {string} Website URL (e.g., 'https://www.x0start.com')\n */\nexport function getWebsiteUrl(env) {\n if (cachedWebsiteUrl) {\n return cachedWebsiteUrl;\n }\n\n const domain = env.DOMAIN || 'x0start.com';\n const isDev = env.ENVIRONMENT === 'development' || !env.ENVIRONMENT;\n const protocol = isDev ? 'http' : 'https';\n\n cachedWebsiteUrl = `${protocol}://www.${domain}`;\n\n return cachedWebsiteUrl;\n}\n\n/**\n * Reset the cached website URL (for testing or config changes)\n */\nexport function resetWebsiteUrlCache() {\n cachedWebsiteUrl = null;\n}\n\n/**\n * Encode all links in HTML to go through tracking redirect\n * @param {string} html - HTML content with links\n * @param {string} sendId - Unique send ID for tracking\n * @param {Object} env - Environment bindings (optional, for auto-detecting URL)\n * @param {string} websiteUrl - Base website URL (optional, overrides env detection)\n * @returns {string} HTML with encoded tracking links\n */\nexport function encodeTrackingLinks(html, sendId, env = null, websiteUrl = null) {\n const baseUrl = websiteUrl || (env ? getWebsiteUrl(env) : 'https://www.x0start.com');\n\n // Helper to decode HTML entities\n const decodeHtmlEntities = (text) => {\n return text\n .replace(///g, '/')\n .replace(/:/g, ':')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n };\n\n // Replace all href attributes with tracking URLs\n return html.replace(/href=\"([^\"]+)\"/g, (match, url) => {\n // Skip certain URLs that shouldn't be tracked\n if (\n url.startsWith('mailto:') ||\n url.startsWith('tel:') ||\n url.startsWith('#') ||\n url.includes('{{') || // Skip template variables\n url.includes('/email/unsubscribe/') ||\n url.includes('/email/track/')\n ) {\n return match;\n }\n\n // Decode HTML entities first (marked encodes URLs with HTML entities)\n const decodedUrl = decodeHtmlEntities(url);\n\n // Encode the decoded URL in base64\n const encodedUrl = Buffer.from(decodedUrl).toString('base64');\n\n // Create tracking redirect URL\n const trackingUrl = `${baseUrl}/r/${sendId}?url=${encodedUrl}`;\n\n return `href=\"${trackingUrl}\"`;\n });\n}\n\n/**\n * Convert markdown to plain text\n * @param {string} markdown - Markdown content\n * @returns {string} Plain text version\n */\nexport function markdownToPlainText(markdown) {\n return markdown\n .replace(/#{1,6}\\s+/g, '') // Remove headers\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1') // Remove bold\n .replace(/\\*(.+?)\\*/g, '$1') // Remove italic\n .replace(/\\[(.+?)\\]\\((.+?)\\)/g, '$1: $2') // Keep both link text and URL\n .replace(/`(.+?)`/g, '$1') // Remove code\n .replace(/>\\s+/g, '') // Remove blockquotes\n .replace(/\\n{3,}/g, '\\n\\n') // Normalize line breaks\n .trim();\n}\n\n/**\n * Extract variables from a Mustache template string\n * @param {string} templateString - The mustache template string\n * @returns {string[]} Array of unique variable names\n */\nexport function extractVariables(templateString) {\n if (!templateString) return [];\n\n try {\n // Parse the template to get tokens\n const tokens = mustache.parse(templateString);\n const variables = new Set();\n\n // Recursively extract variables from tokens\n const collectVariables = (tokenList) => {\n tokenList.forEach(token => {\n const type = token[0];\n const value = token[1];\n\n // Types: 'name' ({{var}}), '#' (section), '^' (inverted section), '&' (unescaped)\n if (type === 'name' || type === '#' || type === '^' || type === '&') {\n variables.add(value);\n }\n\n // Variable is in token[4] for sections\n if ((type === '#' || type === '^') && token[4]) {\n collectVariables(token[4]);\n }\n });\n };\n\n collectVariables(tokens);\n return Array.from(variables);\n } catch (error) {\n console.error('Error parsing template variables:', error);\n return [];\n }\n}\n"],"mappings":"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","names":["Mustache","import_hono","import_hono","import_hono","import_hono","import_mustache","mustache"]}
|
|
1
|
+
{"version":3,"sources":["../../src/backend/index.js","../../src/backend/EmailService.js","../../src/common/htmlWrapper.js","../../src/backend/EmailingCacheDO.js","../../src/backend/EmailLogger.js","../../src/backend/routes/index.js","../../src/backend/routes/settings.js","../../src/backend/routes/templates.js","../../src/backend/routes/tracking.js","../../src/backend/routes/logs.js","../../src/common/utils.js"],"sourcesContent":["// Backend services\nexport { EmailService } from './EmailService.js';\nexport { EmailingCacheDO, createDOCacheProvider } from './EmailingCacheDO.js';\nexport { EmailLogger, createEmailLoggerCallback } from './EmailLogger.js';\n\n// Routes\nexport {\n createEmailRoutes,\n createTemplateRoutes,\n createTrackingRoutes\n} from './routes/index.js';\n\n// Common utilities (re-export for convenience)\nexport {\n wrapInEmailTemplate,\n getWebsiteUrl,\n resetWebsiteUrlCache,\n encodeTrackingLinks,\n markdownToPlainText,\n extractVariables\n} from '../common/index.js';\n","/**\n * Email Service\n * Unified service for email delivery and template management.\n */\nimport { marked } from 'marked';\nimport Mustache from 'mustache';\nimport { wrapInEmailTemplate } from '../common/htmlWrapper.js';\nimport { createDOCacheProvider } from './EmailingCacheDO.js';\nimport { EmailLogger } from './EmailLogger.js';\n\nexport class EmailService {\n /**\n * @param {Object} env - Cloudflare environment bindings (DB, etc.)\n * @param {Object} config - Configuration options\n * @param {string} [config.emailTablePrefix='system_email_'] - Prefix for D1 tables\n * @param {Object} [config.defaults] - Default settings (fromName, fromAddress)\n * @param {boolean|Function} [config.emailLogger] - Email logger: true (default), false (disabled), or custom callback\n * @param {Object} [cacheProvider] - Optional cache interface (DO stub or KV wrapper)\n */\n constructor(env, config = {}, cacheProvider = null) {\n this.env = env;\n this.db = env.DB;\n this.config = {\n emailTablePrefix: config.emailTablePrefix || config.tableNamePrefix || 'system_email_',\n defaults: config.defaults || {\n fromName: 'System',\n fromAddress: 'noreply@example.com',\n provider: 'mailchannels'\n },\n // Loader function to fetch settings from backend (DB, KV, etc.)\n // Signature: async (profile, tenantId) => SettingsObject\n settingsLoader: config.settingsLoader || null,\n\n // Updater function to save settings to backend\n // Signature: async (profile, tenantId, settings) => void\n settingsUpdater: config.settingsUpdater || null,\n\n // Settings configuration\n settingsTableName: config.settingsTableName || 'system_settings',\n settingsKeyPrefix: config.settingsKeyPrefix || 'system_email.',\n\n // Branding configuration for email templates\n branding: {\n brandName: config.branding?.brandName || 'Your App',\n portalUrl: config.branding?.portalUrl || 'https://app.example.com',\n primaryColor: config.branding?.primaryColor || '#667eea',\n ...config.branding\n },\n ...config\n };\n\n // Email Logger Auto-detection:\n // - If emailLogger is explicitly false, disable logging\n // - If emailLogger is a function, use it as custom logger\n // - Otherwise (default), auto-create logger using env.DB if available\n if (config.emailLogger === false) {\n this.emailLogger = null;\n } else if (typeof config.emailLogger === 'function') {\n this.emailLogger = config.emailLogger;\n } else if (env.DB) {\n // Auto-enable logging by default when DB is available\n const logger = new EmailLogger(env.DB);\n this.emailLogger = logger.createCallback();\n } else {\n this.emailLogger = null;\n }\n\n // Cache Auto-detection:\n // If no provider explicitly passed, try to use built-in DO provider if binding exists\n if (!cacheProvider && env.EMAIL_TEMPLATE_CACHE) {\n this.cache = createDOCacheProvider(env.EMAIL_TEMPLATE_CACHE);\n } else {\n this.cache = cacheProvider;\n }\n }\n\n // --- Configuration & Settings ---\n\n /**\n * Load email configuration\n * @param {string} profile - 'system' or 'tenant' (or custom profile string)\n * @param {string} tenantId - Context ID (optional)\n * @returns {Promise<Object>} Email configuration\n */\n async loadSettings(profile = 'system', tenantId = null) {\n // 1. Try cache (now with Read-Through logic in DO)\n if (this.cache && this.cache.getSettings) {\n try {\n const cached = await this.cache.getSettings(profile, tenantId);\n if (cached) return this._normalizeConfig(cached);\n } catch (e) {\n // Ignore cache/rpc errors\n }\n }\n\n let settings = null;\n\n // 2. Use settingsLoader (Custom Worker Logic) if provided\n if (this.config.settingsLoader) {\n try {\n settings = await this.config.settingsLoader(profile, tenantId);\n } catch (e) {\n console.warn('[EmailService] settingsLoader failed:', e);\n }\n }\n\n // 3. System profile: Load from D1 system_settings table\n if (!settings && profile === 'system' && this.db) {\n try {\n const tableName = this.config.settingsTableName;\n const prefix = this.config.settingsKeyPrefix;\n\n // Fetch all keys starting with prefix\n const { results } = await this.db.prepare(`SELECT * FROM ${tableName} WHERE key LIKE ?`)\n .bind(`${prefix}%`)\n .all();\n\n if (results && results.length > 0) {\n settings = {};\n for (const row of results) {\n const cleanKey = row.key.slice(prefix.length);\n settings[cleanKey] = row.value;\n }\n }\n } catch (e) {\n console.warn('[EmailService] Failed to load settings from DB:', e.message);\n }\n }\n\n if (settings) {\n // 4. Populate Cache (Write-Back)\n if (this.cache && this.cache.putSettings) {\n try {\n this.cache.putSettings(profile, tenantId, settings);\n } catch (e) { /* ignore */ }\n }\n return this._normalizeConfig(settings);\n }\n\n // 5. Fallback to defaults\n return {\n ...this.config.defaults,\n };\n }\n\n /**\n * Normalize config keys to standard format\n * Handles both snake_case (DB) and camelCase inputs\n */\n _normalizeConfig(config) {\n return {\n provider: config.email_provider || config.provider || this.config.defaults.provider,\n fromAddress: config.email_from_address || config.fromAddress || this.config.defaults.fromAddress,\n fromName: config.email_from_name || config.fromName || this.config.defaults.fromName,\n\n // Provider-specific settings (normalize DB keys to service keys)\n sendgridApiKey: config.sendgrid_api_key || config.sendgridApiKey,\n resendApiKey: config.resend_api_key || config.resendApiKey,\n sendpulseClientId: config.sendpulse_client_id || config.sendpulseClientId,\n sendpulseClientSecret: config.sendpulse_client_secret || config.sendpulseClientSecret,\n\n // SMTP\n smtpHost: config.smtp_host || config.smtpHost,\n smtpPort: config.smtp_port || config.smtpPort,\n smtpUsername: config.smtp_username || config.smtpUsername,\n smtpPassword: config.smtp_password || config.smtpPassword,\n\n // Tracking\n trackingUrl: config.tracking_url || config.trackingUrl,\n\n // Pass through others\n // Pass through others\n ...config,\n smtpSecure: config.smtp_secure === 'true',\n };\n }\n\n // --- Template Management ---\n\n async getTemplate(templateId) {\n // Try cache first\n if (this.cache) {\n try {\n const cached = await this.cache.getTemplate(templateId);\n if (cached) return cached;\n } catch (e) {\n console.warn('[EmailService] Template cache lookup failed:', e);\n }\n }\n\n const table = `${this.config.emailTablePrefix}templates`;\n return await this.db.prepare(`SELECT * FROM ${table} WHERE template_id = ?`)\n .bind(templateId)\n .first();\n }\n\n async getAllTemplates() {\n const table = `${this.config.emailTablePrefix}templates`;\n const result = await this.db.prepare(`SELECT * FROM ${table} ORDER BY template_name`).all();\n return result.results || [];\n }\n\n /**\n * Save email configuration\n * @param {Object} settings - Config object\n * @param {string} profile - 'system' or 'tenant'\n * @param {string} tenantId - Context ID\n */\n async saveSettings(settings, profile = 'system', tenantId = null) {\n // 1. Convert to DB format if needed (not implemented here, assumes settingsUpdater handles it)\n\n // 2. Use settingsUpdater if provided (Write-Aside)\n if (this.config.settingsUpdater) {\n try {\n await this.config.settingsUpdater(profile, tenantId, settings);\n } catch (e) {\n console.error('[EmailService] settingsUpdater failed:', e);\n throw e;\n }\n }\n // 3. Helper for saving to D1 (if no updater, legacy/default tables)\n else if (profile === 'system' && this.db) {\n // NOTE: We could implement a default D1 upsert here similar to the DO fallback,\n // but for writing it's safer to rely on explicit updaters or the DO write-through if implemented.\n // Currently DO supports write-through for 'system' keys.\n }\n\n // 4. Update Cache (Write-Invalidate)\n if (this.cache && this.cache.invalidateSettings) {\n try {\n await this.cache.invalidateSettings(profile, tenantId);\n } catch (e) {\n console.warn('[EmailService] Failed to invalidate settings cache:', e);\n }\n }\n }\n\n async saveTemplate(template, userId = 'system') {\n const table = `${this.config.emailTablePrefix}templates`;\n const now = Math.floor(Date.now() / 1000);\n const existing = await this.getTemplate(template.template_id);\n\n if (existing) {\n await this.db.prepare(`\n UPDATE ${table} SET \n template_name = ?, template_type = ?, subject_template = ?, \n body_markdown = ?, variables = ?, description = ?, is_active = ?, \n updated_at = ?, updated_by = ?\n WHERE template_id = ?\n `).bind(\n template.template_name, template.template_type, template.subject_template,\n template.body_markdown, template.variables, template.description,\n template.is_active, now, userId, template.template_id\n ).run();\n } else {\n await this.db.prepare(`\n INSERT INTO ${table} (\n template_id, template_name, template_type, subject_template,\n body_markdown, variables, description, is_active, created_at, updated_at, updated_by\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).bind(\n template.template_id, template.template_name, template.template_type,\n template.subject_template, template.body_markdown, template.variables || '[]',\n template.description, template.is_active || 1, now, now, userId\n ).run();\n }\n\n // Invalidate cache if exists\n if (this.cache) {\n await this.cache.putTemplate(template);\n }\n }\n\n async deleteTemplate(templateId) {\n const table = `${this.config.emailTablePrefix}templates`;\n await this.db.prepare(`DELETE FROM ${table} WHERE template_id = ?`).bind(templateId).run();\n if (this.cache) {\n await this.cache.deleteTemplate(templateId);\n }\n }\n\n // --- Rendering ---\n\n /**\n * Pre-process template data to auto-format URLs\n * Scans for strings starting with http:// or https:// and wraps them in Markdown links\n */\n _preprocessData(data) {\n if (!data || typeof data !== 'object') return data;\n\n const processed = { ...data };\n for (const [key, value] of Object.entries(processed)) {\n if (typeof value === 'string' && (value.startsWith('http://') || value.startsWith('https://'))) {\n // Check if it's already a markdown link to avoid double wrapping\n if (!value.trim().startsWith('[') && !value.includes('](')) {\n processed[key] = `[${value}](${value})`;\n }\n }\n }\n return processed;\n }\n\n async renderTemplate(templateId, data) {\n const template = await this.getTemplate(templateId);\n if (!template) throw new Error(`Template not found: ${templateId}`);\n\n // Pre-process data to auto-format links\n const processedData = this._preprocessData(data);\n\n // Render Subject\n const subject = Mustache.render(template.subject_template, processedData);\n\n // Render Body (Markdown -> HTML)\n let markdown = Mustache.render(template.body_markdown, processedData);\n\n // Normalize escaped newlines from DB storage to actual newlines\n markdown = markdown.replace(/\\\\n/g, '\\n');\n\n // Configure marked for email-friendly output\n marked.use({\n mangle: false,\n headerIds: false,\n breaks: true // Convert single line breaks to <br>\n });\n const htmlContent = marked.parse(markdown);\n\n // Wrap in Base Template (could be another template or hardcoded default)\n const html = this.wrapInBaseTemplate(htmlContent, subject, data);\n const plainText = markdown.replace(/<[^>]*>/g, ''); // Simple strip\n\n return { subject, html, plainText };\n }\n\n wrapInBaseTemplate(content, subject, data = {}) {\n // Merge branding config with template data\n const templateData = {\n ...data,\n brandName: data.brandName || this.config.branding.brandName,\n portalUrl: data.portalUrl || this.config.branding.portalUrl,\n unsubscribeUrl: data.unsubscribeUrl || '{{unsubscribe_url}}'\n };\n\n // Use the full branded HTML wrapper from common\n return wrapInEmailTemplate(content, subject, templateData);\n }\n\n // --- Delivery ---\n\n /**\n * Send an email using a template\n * @param {string} templateId - Template ID\n * @param {Object} data - Template variables\n * @param {Object} options - Sending options (to, provider, etc.)\n * @returns {Promise<Object>} Delivery result\n */\n async sendViaTemplate(templateId, data, options) {\n try {\n const { subject, html, plainText } = await this.renderTemplate(templateId, data);\n\n return await this.sendEmail({\n ...options,\n subject, // Can be overridden by options.subject if needed, but usually template subject is preferred\n html,\n text: plainText,\n metadata: {\n ...options.metadata,\n templateId\n }\n });\n } catch (error) {\n console.error(`[EmailService] sendViaTemplate failed for ${templateId}:`, error);\n return { success: false, error: error.message };\n }\n }\n\n /**\n * Send a single email\n * @param {Object} params - Email parameters\n * @param {string} params.to - Recipient email\n * @param {string} params.subject - Email subject\n * @param {string} params.html - HTML body\n * @param {string} params.text - Plain text body\n * @param {string} [params.provider] - Override provider\n * @param {string} [params.profile='system'] - 'system' or 'tenant'\n * @param {string} [params.tenantId] - Required if profile is 'tenant'\n * @param {Object} [params.metadata] - Additional metadata\n * @returns {Promise<Object>} Delivery result\n */\n async sendEmail({ to, subject, html, htmlBody, text, textBody, provider, profile = 'system', tenantId = null, metadata = {}, batchId = null, userId = null }) {\n // Backward compatibility: accept htmlBody/textBody as aliases\n const htmlContent = html || htmlBody;\n const textContent = text || textBody;\n const templateId = metadata?.templateId || 'direct';\n\n // Log pending status\n if (this.emailLogger) {\n try {\n await this.emailLogger({\n event: 'pending',\n recipientEmail: to,\n recipientUserId: userId,\n templateId,\n subject,\n batchId,\n metadata\n });\n } catch (e) {\n console.warn('[EmailService] emailLogger pending failed:', e);\n }\n }\n\n try {\n const settings = await this.loadSettings(profile, tenantId);\n const useProvider = provider || settings.provider || 'mailchannels';\n\n let result;\n let providerMessageId = null;\n\n switch (useProvider) {\n case 'mailchannels':\n result = await this.sendViaMailChannels(to, subject, htmlContent, textContent, settings, metadata);\n break;\n case 'sendgrid':\n result = await this.sendViaSendGrid(to, subject, htmlContent, textContent, settings, metadata);\n break;\n case 'resend':\n result = await this.sendViaResend(to, subject, htmlContent, textContent, settings, metadata);\n if (result && typeof result === 'object' && result.id) {\n providerMessageId = result.id;\n result = true;\n }\n break;\n case 'sendpulse':\n result = await this.sendViaSendPulse(to, subject, htmlContent, textContent, settings, metadata);\n break;\n default:\n console.error(`[EmailService] Unknown provider: ${useProvider}`);\n // Log failure\n if (this.emailLogger) {\n try {\n await this.emailLogger({\n event: 'failed',\n recipientEmail: to,\n recipientUserId: userId,\n templateId,\n subject,\n provider: useProvider,\n batchId,\n error: `Unknown email provider: ${useProvider}`,\n metadata\n });\n } catch (e) { /* ignore */ }\n }\n return { success: false, error: `Unknown email provider: ${useProvider}` };\n }\n\n if (result) {\n const messageId = providerMessageId || crypto.randomUUID();\n // Log success\n if (this.emailLogger) {\n try {\n await this.emailLogger({\n event: 'sent',\n recipientEmail: to,\n recipientUserId: userId,\n templateId,\n subject,\n provider: useProvider,\n messageId,\n batchId,\n metadata\n });\n } catch (e) {\n console.warn('[EmailService] emailLogger sent failed:', e);\n }\n }\n return { success: true, messageId };\n } else {\n console.error('[EmailService] Failed to send email to:', to);\n // Log failure\n if (this.emailLogger) {\n try {\n await this.emailLogger({\n event: 'failed',\n recipientEmail: to,\n recipientUserId: userId,\n templateId,\n subject,\n provider: useProvider,\n batchId,\n error: 'Failed to send email',\n metadata\n });\n } catch (e) { /* ignore */ }\n }\n return { success: false, error: 'Failed to send email' };\n }\n } catch (error) {\n console.error('[EmailService] Error sending email:', error);\n // Log exception\n if (this.emailLogger) {\n try {\n await this.emailLogger({\n event: 'failed',\n recipientEmail: to,\n recipientUserId: userId,\n templateId,\n subject,\n batchId,\n error: error.message,\n metadata\n });\n } catch (e) { /* ignore */ }\n }\n return { success: false, error: error.message };\n }\n }\n\n /**\n * Send multiple emails in batch\n * @param {Array} emails - Array of email objects\n * @returns {Promise<Array>} Array of delivery results\n */\n async sendBatch(emails) {\n console.log('[EmailService] Sending batch of', emails.length, 'emails');\n const results = await Promise.all(\n emails.map(email => this.sendEmail(email))\n );\n return results;\n }\n\n /**\n * Send email via MailChannels HTTP API\n * MailChannels is specifically designed for Cloudflare Workers\n */\n async sendViaMailChannels(to, subject, html, text, settings, metadata) {\n try {\n const response = await fetch('https://api.mailchannels.net/tx/v1/send', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n personalizations: [{ to: [{ email: to, name: metadata.recipientName || '' }] }],\n from: { email: settings.fromAddress, name: settings.fromName },\n subject,\n content: [\n { type: 'text/plain', value: text || html.replace(/<[^>]*>/g, '') },\n { type: 'text/html', value: html }\n ]\n })\n });\n\n if (response.status === 202) {\n return true;\n } else {\n const contentType = response.headers.get('content-type');\n const errorBody = contentType?.includes('application/json')\n ? await response.json()\n : await response.text();\n console.error('[EmailService] MailChannels error:', response.status, errorBody);\n return false;\n }\n } catch (error) {\n console.error('[EmailService] MailChannels exception:', error.message);\n return false;\n }\n }\n\n /**\n * Send email via SendGrid HTTP API\n */\n async sendViaSendGrid(to, subject, html, text, settings, metadata) {\n try {\n if (!settings.sendgridApiKey) {\n console.error('[EmailService] SendGrid API key missing');\n return false;\n }\n\n const response = await fetch('https://api.sendgrid.com/v3/mail/send', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${settings.sendgridApiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n personalizations: [{ to: [{ email: to, name: metadata.recipientName || '' }] }],\n from: { email: settings.fromAddress, name: settings.fromName },\n subject,\n content: [\n { type: 'text/html', value: html },\n { type: 'text/plain', value: text || html.replace(/<[^>]*>/g, '') },\n ],\n }),\n });\n\n if (response.status === 202) {\n return true;\n } else {\n const errorText = await response.text();\n console.error('[EmailService] SendGrid error:', response.status, errorText);\n return false;\n }\n } catch (error) {\n console.error('[EmailService] SendGrid exception:', error.message);\n return false;\n }\n }\n\n /**\n * Send email via Resend HTTP API\n */\n async sendViaResend(to, subject, html, text, settings, metadata) {\n try {\n if (!settings.resendApiKey) {\n console.error('[EmailService] Resend API key missing');\n return false;\n }\n\n const response = await fetch('https://api.resend.com/emails', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${settings.resendApiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n from: `${settings.fromName} <${settings.fromAddress}>`,\n to: [to],\n subject,\n html,\n text: text || html.replace(/<[^>]*>/g, ''),\n }),\n });\n\n if (response.ok) {\n return true;\n } else {\n const errorText = await response.text();\n console.error('[EmailService] Resend error:', response.status, errorText);\n return false;\n }\n } catch (error) {\n console.error('[EmailService] Resend exception:', error.message);\n return false;\n }\n }\n\n /**\n * Send email via SendPulse HTTP API\n * SendPulse offers 15,000 free emails/month\n */\n async sendViaSendPulse(to, subject, html, text, settings, metadata) {\n try {\n if (!settings.sendpulseClientId || !settings.sendpulseClientSecret) {\n console.error('[EmailService] SendPulse credentials missing');\n return false;\n }\n\n // SendPulse uses OAuth2 token-based authentication\n const tokenParams = new URLSearchParams({\n grant_type: 'client_credentials',\n client_id: settings.sendpulseClientId,\n client_secret: settings.sendpulseClientSecret,\n });\n\n const tokenResponse = await fetch('https://api.sendpulse.com/oauth/access_token', {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: tokenParams.toString(),\n });\n\n if (!tokenResponse.ok) {\n const error = await tokenResponse.text();\n console.error('[EmailService] SendPulse auth error:', error);\n return false;\n }\n\n const tokenData = await tokenResponse.json();\n if (!tokenData.access_token) {\n console.error('[EmailService] SendPulse: No access token in response');\n return false;\n }\n\n const { access_token } = tokenData;\n\n // Safe base64 encoding\n const toBase64 = (str) => {\n if (!str) return '';\n try {\n return btoa(unescape(encodeURIComponent(String(str))));\n } catch (e) {\n console.error('[EmailService] Base64 encoding failed:', e);\n return '';\n }\n };\n\n // Ensure html/text are strings\n const htmlSafe = html || '';\n const textSafe = text || (htmlSafe ? htmlSafe.replace(/<[^>]*>/g, '') : '');\n\n // Send the email\n const response = await fetch('https://api.sendpulse.com/smtp/emails', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${access_token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n email: {\n html: toBase64(htmlSafe),\n text: toBase64(textSafe),\n subject,\n from: { name: settings.fromName, email: settings.fromAddress },\n to: [{ name: metadata.recipientName || '', email: to }],\n },\n }),\n });\n\n if (response.ok) {\n return true;\n } else {\n const errorText = await response.text();\n console.error('[EmailService] SendPulse send error:', response.status, errorText);\n return false;\n }\n } catch (error) {\n console.error('[EmailService] SendPulse exception:', error.message);\n return false;\n }\n }\n}\n\n","/**\n * HTML Email Wrapper\n * Full styled HTML template for wrapping email content\n */\n\n/**\n * Wrap content HTML in email template with styling\n * @param {string} contentHtml - The HTML content to wrap\n * @param {string} subject - Email subject for the title\n * @param {Object} data - Template data (portalUrl, unsubscribeUrl, etc.)\n * @returns {string} Complete HTML email\n */\nexport function wrapInEmailTemplate(contentHtml, subject, data = {}) {\n const portalUrl = data.portalUrl || 'https://app.x0start.com';\n const unsubscribeUrl = data.unsubscribeUrl || '{{unsubscribe_url}}';\n const brandName = data.brandName || 'X0 Start';\n\n return `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${subject}</title>\n <style>\n body {\n margin: 0;\n padding: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n background-color: #f5f5f5;\n line-height: 1.6;\n }\n .email-wrapper {\n background-color: #f5f5f5;\n padding: 40px 20px;\n }\n .email-container {\n max-width: 600px;\n margin: 0 auto;\n background-color: #ffffff;\n border-radius: 8px;\n overflow: hidden;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n }\n .email-header {\n padding: 40px 40px 20px;\n text-align: center;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: #ffffff;\n }\n .email-header h1 {\n margin: 0;\n font-size: 28px;\n font-weight: 600;\n }\n .email-content {\n padding: 30px 40px;\n color: #333333;\n }\n .email-content h1 {\n font-size: 24px;\n margin-top: 0;\n margin-bottom: 20px;\n color: #333333;\n }\n .email-content h2 {\n font-size: 20px;\n margin-top: 30px;\n margin-bottom: 15px;\n color: #333333;\n }\n .email-content h3 {\n font-size: 16px;\n margin-top: 20px;\n margin-bottom: 10px;\n color: #333333;\n }\n .email-content p {\n margin: 0 0 15px;\n color: #666666;\n }\n .email-content a {\n color: #667eea;\n text-decoration: none;\n }\n .email-content ul, .email-content ol {\n margin: 0 0 15px;\n padding-left: 25px;\n }\n .email-content li {\n margin-bottom: 8px;\n color: #666666;\n }\n .email-content blockquote {\n margin: 20px 0;\n padding: 15px 20px;\n background-color: #f8f9fa;\n border-left: 4px solid #667eea;\n color: #666666;\n }\n .email-content code {\n padding: 2px 6px;\n background-color: #f8f9fa;\n border-radius: 3px;\n font-family: 'Courier New', monospace;\n font-size: 14px;\n }\n .email-content pre {\n padding: 15px;\n background-color: #f8f9fa;\n border-radius: 6px;\n overflow-x: auto;\n }\n .email-content pre code {\n padding: 0;\n background: none;\n }\n .btn {\n display: inline-block;\n padding: 12px 24px;\n background-color: #667eea;\n color: #ffffff !important;\n text-decoration: none;\n border-radius: 6px;\n font-weight: 600;\n margin: 10px 0;\n }\n .btn:hover {\n background-color: #5568d3;\n }\n .email-footer {\n padding: 20px 40px;\n background-color: #f8f9fa;\n text-align: center;\n font-size: 12px;\n color: #666666;\n }\n .email-footer a {\n color: #667eea;\n text-decoration: none;\n }\n hr {\n border: none;\n border-top: 1px solid #e0e0e0;\n margin: 30px 0;\n }\n </style>\n</head>\n<body>\n <div class=\"email-wrapper\">\n <div class=\"email-container\">\n <div class=\"email-content\">\n ${contentHtml}\n </div>\n <div class=\"email-footer\">\n <p style=\"margin: 0 0 10px;\">\n You're receiving this email from ${brandName}.\n </p>\n <p style=\"margin: 0;\">\n <a href=\"${unsubscribeUrl}\">Unsubscribe</a> | \n <a href=\"${portalUrl}/settings/notifications\">Manage Preferences</a>\n </p>\n </div>\n </div>\n </div>\n</body>\n</html>\n `.trim();\n}\n","/**\n * EmailingCacheDO - Optional read-through cache for email templates and settings\n * \n * This is a Cloudflare Durable Object that provides caching for email templates and settings.\n * \n * Configurable via environment variable:\n * - EMAIL_TABLE_PREFIX: Prefix for tables (default: 'system_email_')\n */\nexport class EmailingCacheDO {\n constructor(state, env) {\n this.state = state;\n this.env = env;\n this.cache = new Map(); // templateId -> { data, timestamp }\n this.settingsCache = new Map(); // key -> { data, timestamp }\n this.cacheTTL = 3600000; // 1 hour in milliseconds\n // Templates use the prefix\n this.emailTablePrefix = env.EMAIL_TABLE_PREFIX || 'system_email_';\n // Settings use a specific table name (defaulting to system_settings)\n // This allows decoupling template prefix from settings table\n this.settingsTableName = env.EMAIL_SETTINGS_TABLE || 'system_settings';\n\n // Optional: Filter settings by key prefix (e.g. 'email_')\n // This allows sharing the system_settings table with other apps\n this.settingsKeyPrefix = env.EMAIL_SETTINGS_KEY_PREFIX || '';\n }\n\n /**\n * Handle HTTP requests to this Durable Object\n */\n async fetch(request) {\n const url = new URL(request.url);\n const path = url.pathname;\n\n try {\n if (path === '/get' && request.method === 'GET') {\n return this.handleGet(request);\n } else if (path === '/invalidate' && request.method === 'POST') {\n return this.handleInvalidate(request);\n } else if (path === '/clear' && request.method === 'POST') {\n return this.handleClear(request);\n } else if (path === '/stats' && request.method === 'GET') {\n return this.handleStats(request);\n }\n // Settings endpoints\n else if (path === '/settings/get' && request.method === 'GET') {\n return this.handleGetSettings(request);\n } else if (path === '/settings/put' && request.method === 'POST') {\n return this.handlePutSettings(request);\n } else if (path === '/settings/invalidate' && request.method === 'POST') {\n return this.handleInvalidateSettings(request);\n } else {\n return new Response('Not Found', { status: 404 });\n }\n } catch (error) {\n console.error('[EmailingCacheDO] Error:', error);\n return new Response(JSON.stringify({ error: error.message }), {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n }\n\n /**\n * Get template (from cache or D1)\n */\n async handleGet(request) {\n const url = new URL(request.url);\n const templateId = url.searchParams.get('templateId');\n const forceRefresh = url.searchParams.get('refresh') === 'true';\n\n if (!templateId) {\n return new Response(JSON.stringify({ error: 'templateId is required' }), {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // Check cache (unless force refresh)\n if (!forceRefresh) {\n const cached = this.cache.get(templateId);\n if (cached && Date.now() - cached.timestamp < this.cacheTTL) {\n console.log('[EmailingCacheDO] Cache HIT:', templateId);\n return new Response(JSON.stringify({\n template: cached.data,\n cached: true,\n age: Date.now() - cached.timestamp,\n }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n }\n\n // Cache miss or expired - fetch from D1\n console.log('[EmailingCacheDO] Cache MISS - fetching from D1:', templateId);\n const template = await this.fetchTemplateFromD1(templateId);\n\n if (!template) {\n return new Response(JSON.stringify({\n error: 'Template not found',\n templateId\n }), {\n status: 404,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // Cache the result\n this.cache.set(templateId, {\n data: template,\n timestamp: Date.now(),\n });\n\n return new Response(JSON.stringify({\n template,\n cached: false,\n }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // --- Settings Cache Handlers ---\n\n async handleGetSettings(request) {\n const url = new URL(request.url);\n const key = url.searchParams.get('key');\n\n if (!key) return new Response('Key required', { status: 400 });\n\n // Check Cache\n const cached = this.settingsCache.get(key);\n if (cached && Date.now() - cached.timestamp < this.cacheTTL) {\n console.log('[EmailingCacheDO] Settings Cache HIT:', key);\n return new Response(JSON.stringify({ settings: cached.data }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // Cache Miss - Fetch from D1 (Read-Through)\n // Only fetch from D1 for system settings (consistent with handlePutSettings)\n if (key.startsWith('system')) {\n console.log('[EmailingCacheDO] Settings Cache MISS - fetching from D1:', key);\n const settings = await this.fetchSettingsFromD1(key);\n\n // Even if null, we might want to cache the \"null\" result to prevent hammer? \n // For now, only cache if we found something or explicitly handle negative caching.\n if (settings) {\n this.settingsCache.set(key, {\n data: settings,\n timestamp: Date.now()\n });\n return new Response(JSON.stringify({ settings }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n }\n\n return new Response(JSON.stringify({ settings: null }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n async handlePutSettings(request) {\n try {\n const body = await request.json();\n const { key, settings } = body;\n\n if (!key || !settings) return new Response('Key and settings required', { status: 400 });\n\n // Write to Cache\n this.settingsCache.set(key, {\n data: settings,\n timestamp: Date.now()\n });\n\n // Write to D1 (Write-Through)\n // Note: only supports writing to \"system\" (default table) currently\n if (key.startsWith('system')) {\n await this.saveSettingsToD1(settings);\n }\n\n return new Response(JSON.stringify({ success: true }), {\n headers: { 'Content-Type': 'application/json' },\n });\n } catch (e) {\n console.error('[EmailingCacheDO] PutSettings error:', e);\n return new Response('Error parsing body/saving', { status: 400 });\n }\n }\n\n async handleInvalidateSettings(request) {\n try {\n const body = await request.json();\n const { key } = body;\n\n if (!key) return new Response('Key required', { status: 400 });\n\n const existed = this.settingsCache.has(key);\n this.settingsCache.delete(key);\n console.log('[EmailingCacheDO] Invalidated settings:', key, existed ? '(existed)' : '(not in cache)');\n\n return new Response(JSON.stringify({ success: true, existed }), {\n headers: { 'Content-Type': 'application/json' },\n });\n } catch (e) {\n console.error('[EmailingCacheDO] InvalidateSettings error:', e);\n return new Response('Error invalidated settings', { status: 400 });\n }\n }\n\n /**\n * Invalidate specific template(s) from cache\n * Body: { templateId: 'template_id' } or { templateId: '*' } for all\n */\n async handleInvalidate(request) {\n const body = await request.json();\n const { templateId } = body;\n\n if (!templateId) {\n return new Response(JSON.stringify({ error: 'templateId is required' }), {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n if (templateId === '*') {\n // Invalidate all templates\n const count = this.cache.size;\n this.cache.clear();\n console.log('[EmailingCacheDO] Invalidated ALL templates:', count);\n return new Response(JSON.stringify({\n success: true,\n message: `Invalidated ${count} templates`,\n }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n // Invalidate specific template\n const existed = this.cache.has(templateId);\n this.cache.delete(templateId);\n console.log('[EmailingCacheDO] Invalidated template:', templateId, existed ? '(existed)' : '(not in cache)');\n\n return new Response(JSON.stringify({\n success: true,\n message: existed ? 'Template invalidated' : 'Template was not in cache',\n templateId,\n }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n /**\n * Clear entire cache (admin operation)\n */\n async handleClear(request) {\n const count = this.cache.size + this.settingsCache.size;\n this.cache.clear();\n this.settingsCache.clear();\n console.log('[EmailingCacheDO] Cache cleared:', count, 'entries (templates + settings)');\n\n return new Response(JSON.stringify({\n success: true,\n message: `Cleared ${count} cached items`,\n }), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n /**\n * Get cache statistics\n */\n async handleStats(request) {\n const stats = {\n templates: this.cache.size,\n settings: this.settingsCache.size,\n cacheTTL: this.cacheTTL,\n };\n\n return new Response(JSON.stringify(stats), {\n headers: { 'Content-Type': 'application/json' },\n });\n }\n\n /**\n * Fetch template from D1\n */\n async fetchTemplateFromD1(templateId) {\n const db = this.env.DB;\n const tableName = `${this.emailTablePrefix}templates`;\n\n try {\n const template = await db\n .prepare(`SELECT * FROM ${tableName} WHERE template_id = ? AND is_active = 1`)\n .bind(templateId)\n .first();\n return template;\n } catch (e) {\n console.error(`[EmailingCacheDO] DB Error (${tableName}):`, e);\n return null;\n }\n }\n\n /**\n * Fetch settings from D1\n * Only supports default table (e.g. system_settings) for now\n */\n async fetchSettingsFromD1(key) {\n const db = this.env.DB;\n const tableName = this.settingsTableName;\n const prefix = this.settingsKeyPrefix;\n\n try {\n let results;\n\n if (prefix) {\n // Filter by prefix (e.g. WHERE setting_key LIKE 'email_%')\n // Note: We need to handle both schema variants (setting_key vs key)\n // BUT standard D1 SQL doesn't support \"OR\" in column names easily with LIKE parameter binding\n // for flexible schemas.\n // We will assume 'setting_key' matches if table is system_settings, or try both.\n\n // Construct query dynamically based on likely schema or just try standard first\n try {\n results = await db.prepare(`SELECT * FROM ${tableName} WHERE setting_key LIKE ?`).bind(`${prefix}%`).all();\n } catch (e) {\n // Fallback to 'key' column\n results = await db.prepare(`SELECT * FROM ${tableName} WHERE key LIKE ?`).bind(`${prefix}%`).all();\n }\n } else {\n // No prefix, fetch all\n results = await db.prepare(`SELECT * FROM ${tableName}`).all();\n }\n\n if (!results.results || results.results.length === 0) return null;\n\n const settings = {};\n results.results.forEach(row => {\n const k = row.setting_key || row.key;\n const v = row.setting_value || row.value;\n if (k) settings[k] = v;\n });\n return settings;\n } catch (e) {\n console.warn(`[EmailingCacheDO] Failed to load settings from ${tableName}:`, e);\n return null;\n }\n }\n\n async saveSettingsToD1(settings) {\n const db = this.env.DB;\n const tableName = this.settingsTableName;\n\n // Simple upset not easy in D1 without specific keys.\n // We'll rely on calling code/migration. \n // For now, let's just log or try to update if possible.\n // Actually, writing settings is complex because we don't know the schema perfectly.\n // Let's implement a BEST EFFORT update for x0start schema\n\n const entries = Object.entries(settings);\n for (const [k, v] of entries) {\n try {\n // Heuristic: Try to determine column names based on common conventions\n // We'll try Schema 1 (setting_key/value) first as it's the default internal convention\n // If that fails, we might fall back to Schema 2 (key/value) in a real implementation\n // For now, let's try a best-effort upsert based on x0start schema or generic\n\n try {\n const res = await db.prepare(`UPDATE ${tableName} SET setting_value = ? WHERE setting_key = ?`).bind(v, k).run();\n if (res.meta.changes === 0) {\n await db.prepare(`INSERT INTO ${tableName} (setting_key, setting_value) VALUES (?, ?)`).bind(k, v).run();\n }\n } catch (e) {\n // Fallback to key/value\n await db.prepare(`INSERT OR REPLACE INTO ${tableName} (key, value) VALUES (?, ?)`).bind(k, v).run();\n }\n } catch (e) {\n console.warn('[EmailingCacheDO] Failed to save setting to D1:', k);\n }\n }\n }\n}\n\n/**\n * Create a cache provider wrapper for the DO\n */\nexport function createDOCacheProvider(doStub, instanceName = 'global') {\n if (!doStub) {\n return null;\n }\n\n const stub = doStub.get(doStub.idFromName(instanceName));\n\n return {\n async getTemplate(templateId) {\n try {\n const response = await stub.fetch(`http://do/get?templateId=${templateId}`);\n const data = await response.json();\n return data.template || null;\n } catch (e) {\n // Silently fall back to DB - DO cache is optional\n return null;\n }\n },\n\n async getSettings(profile, tenantId) {\n const key = `${profile}:${tenantId || ''}`;\n try {\n const response = await stub.fetch(`http://do/settings/get?key=${encodeURIComponent(key)}`);\n const data = await response.json();\n return data.settings || null;\n } catch (e) {\n return null;\n }\n },\n\n async putSettings(profile, tenantId, settings) {\n const key = `${profile}:${tenantId || ''}`;\n try {\n await stub.fetch('http://do/settings/put', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ key, settings }),\n });\n } catch (e) {\n console.warn('[DOCacheProvider] Failed to cache settings:', e);\n }\n },\n\n async putTemplate(template) {\n try {\n await stub.fetch('http://do/invalidate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ templateId: template.template_id }),\n });\n } catch (e) {\n console.warn('[DOCacheProvider] Failed to invalidate template:', e);\n }\n },\n\n async deleteTemplate(templateId) {\n try {\n await stub.fetch('http://do/invalidate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ templateId }),\n });\n } catch (e) {\n console.warn('[DOCacheProvider] Failed to invalidate template:', e);\n }\n },\n\n async invalidateSettings(profile, tenantId) {\n const key = `${profile}:${tenantId || ''}`;\n try {\n await stub.fetch('http://do/settings/invalidate', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ key }),\n });\n } catch (e) {\n console.warn('[DOCacheProvider] Failed to invalidate settings:', e);\n }\n },\n };\n}\n","/**\n * Email Logger Utility\n * \n * Provides built-in email logging to D1 database.\n * Can be used directly or passed to EmailService as the emailLogger callback.\n */\n\nexport class EmailLogger {\n /**\n * @param {Object} db - D1 database binding\n * @param {Object} options - Configuration options\n * @param {string} [options.tableName='system_email_logs'] - Table name for logs\n */\n constructor(db, options = {}) {\n this.db = db;\n this.tableName = options.tableName || 'system_email_logs';\n }\n\n /**\n * Creates a logger callback function for use with EmailService.\n * Usage: new EmailService(env, { emailLogger: emailLogger.createCallback() })\n */\n createCallback() {\n return async (entry) => {\n await this.log(entry);\n };\n }\n\n /**\n * Log an email event (pending, sent, or failed)\n * @param {Object} entry - Log entry\n */\n async log(entry) {\n const {\n event,\n recipientEmail,\n recipientUserId,\n templateId,\n subject,\n provider,\n messageId,\n batchId,\n error,\n errorCode,\n metadata\n } = entry;\n\n try {\n if (event === 'pending') {\n // Create new log entry\n const id = crypto.randomUUID().replace(/-/g, '');\n await this.db.prepare(`\n INSERT INTO ${this.tableName} \n (id, batch_id, recipient_email, recipient_user_id, template_id, subject, status, metadata, created_at)\n VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, strftime('%s', 'now'))\n `).bind(\n id,\n batchId || null,\n recipientEmail,\n recipientUserId || null,\n templateId || 'direct',\n subject || null,\n metadata ? JSON.stringify(metadata) : null\n ).run();\n } else if (event === 'sent') {\n // Update existing entry to sent\n await this.db.prepare(`\n UPDATE ${this.tableName} \n SET status = 'sent', \n provider = ?, \n provider_message_id = ?, \n sent_at = strftime('%s', 'now')\n WHERE recipient_email = ? \n AND template_id = ? \n AND status = 'pending'\n ORDER BY created_at DESC\n LIMIT 1\n `).bind(\n provider || null,\n messageId || null,\n recipientEmail,\n templateId || 'direct'\n ).run();\n } else if (event === 'failed') {\n // Update existing entry to failed\n await this.db.prepare(`\n UPDATE ${this.tableName} \n SET status = 'failed', \n provider = ?, \n error_message = ?,\n error_code = ?\n WHERE recipient_email = ? \n AND template_id = ? \n AND status = 'pending'\n ORDER BY created_at DESC\n LIMIT 1\n `).bind(\n provider || null,\n error || null,\n errorCode || null,\n recipientEmail,\n templateId || 'direct'\n ).run();\n }\n } catch (e) {\n console.error('[EmailLogger] Failed to log:', e);\n }\n }\n\n /**\n * Query email logs with filtering\n * @param {Object} options - Query options\n */\n async query(options = {}) {\n const {\n recipientEmail,\n recipientUserId,\n templateId,\n status,\n batchId,\n limit = 50,\n offset = 0\n } = options;\n\n const conditions = [];\n const bindings = [];\n\n if (recipientEmail) {\n conditions.push('recipient_email = ?');\n bindings.push(recipientEmail);\n }\n if (recipientUserId) {\n conditions.push('recipient_user_id = ?');\n bindings.push(recipientUserId);\n }\n if (templateId) {\n conditions.push('template_id = ?');\n bindings.push(templateId);\n }\n if (status) {\n conditions.push('status = ?');\n bindings.push(status);\n }\n if (batchId) {\n conditions.push('batch_id = ?');\n bindings.push(batchId);\n }\n\n const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n\n // Get total count\n const countResult = await this.db.prepare(\n `SELECT COUNT(*) as count FROM ${this.tableName} ${whereClause}`\n ).bind(...bindings).first();\n\n // Get logs\n const { results } = await this.db.prepare(`\n SELECT id, batch_id, recipient_email, recipient_user_id, template_id, subject,\n status, provider, provider_message_id, error_message, error_code, metadata,\n created_at, sent_at\n FROM ${this.tableName}\n ${whereClause}\n ORDER BY created_at DESC\n LIMIT ? OFFSET ?\n `).bind(...bindings, limit, offset).all();\n\n const logs = (results || []).map(row => ({\n id: row.id,\n batchId: row.batch_id,\n recipientEmail: row.recipient_email,\n recipientUserId: row.recipient_user_id,\n templateId: row.template_id,\n subject: row.subject,\n status: row.status,\n provider: row.provider,\n providerMessageId: row.provider_message_id,\n errorMessage: row.error_message,\n errorCode: row.error_code,\n metadata: row.metadata ? JSON.parse(row.metadata) : undefined,\n createdAt: row.created_at,\n sentAt: row.sent_at\n }));\n\n return { logs, total: countResult?.count || 0 };\n }\n\n /**\n * Get email sending statistics\n * @param {number} sinceDays - Number of days to look back\n */\n async getStats(sinceDays = 7) {\n const sinceTimestamp = Math.floor(Date.now() / 1000) - (sinceDays * 24 * 60 * 60);\n\n const statusResult = await this.db.prepare(`\n SELECT status, COUNT(*) as count\n FROM ${this.tableName}\n WHERE created_at >= ?\n GROUP BY status\n `).bind(sinceTimestamp).all();\n\n const templateResult = await this.db.prepare(`\n SELECT template_id, COUNT(*) as count\n FROM ${this.tableName}\n WHERE created_at >= ?\n GROUP BY template_id\n `).bind(sinceTimestamp).all();\n\n const stats = {\n total: 0,\n sent: 0,\n failed: 0,\n pending: 0,\n byTemplate: {}\n };\n\n (statusResult.results || []).forEach(row => {\n const count = row.count || 0;\n stats.total += count;\n if (row.status === 'sent') stats.sent = count;\n if (row.status === 'failed') stats.failed = count;\n if (row.status === 'pending') stats.pending = count;\n });\n\n (templateResult.results || []).forEach(row => {\n stats.byTemplate[row.template_id] = row.count;\n });\n\n return stats;\n }\n\n /**\n * Get recent failed emails for debugging\n * @param {number} limit - Number of failed emails to retrieve\n */\n async getRecentFailures(limit = 20) {\n const { results } = await this.db.prepare(`\n SELECT id, recipient_email, template_id, subject, error_message, error_code, created_at\n FROM ${this.tableName}\n WHERE status = 'failed'\n ORDER BY created_at DESC\n LIMIT ?\n `).bind(limit).all();\n\n return results || [];\n }\n}\n\n/**\n * Create a simple logger callback for EmailService that logs to D1.\n * This is a convenience function for quick setup.\n * \n * Usage:\n * const emailService = new EmailService(env, {\n * emailLogger: createEmailLoggerCallback(env.DB)\n * });\n */\nexport function createEmailLoggerCallback(db, tableName = 'system_email_logs') {\n const logger = new EmailLogger(db, { tableName });\n return logger.createCallback();\n}\n","/**\n * Email Routes Index\n * Aggregates and exports all route factories\n */\nimport { Hono } from 'hono';\nimport { createSettingsRoutes } from './settings.js';\nimport { createTemplateRoutes } from './templates.js';\nimport { createTrackingRoutes } from './tracking.js';\nimport { createLogRoutes } from './logs.js';\n\n// Re-export individual route factories\n// Re-export individual route factories\nexport {\n createSettingsRoutes,\n createTemplateRoutes,\n createTrackingRoutes,\n createLogRoutes\n};\n\n/**\n * Create combined email routes\n * @param {Object} config - Configuration\n * @param {Object} cacheProvider - Optional cache provider\n * @returns {Hono} Combined Hono router\n */\nexport function createEmailRoutes(config = {}, cacheProvider = null) {\n const app = new Hono();\n\n // Mount template routes at /api/email typically, but here we just return the router\n // Users will mount this router at /api/email or similar\n\n // We want hierarchical routes:\n // /templates -> Template Routes\n // /logs -> Log Routes\n // /settings -> Settings Routes\n // / -> Tracking? No, tracking usually goes to public endpoint\n\n // Let's assume this router is mounted at /api/email\n\n app.route('/templates', createTemplateRoutes(config, cacheProvider));\n app.route('/logs', createLogRoutes(config));\n app.route('/settings', createSettingsRoutes({\n ...config,\n tableName: config.settingsTableName || 'system_settings',\n keyPrefix: config.settingsKeyPrefix || 'system_email.'\n }));\n\n // Support root level send-test if desired, but templates route handles it now\n\n // Tracking usually needs to be public, so we don't mix it with admin routes usually\n // But for convenience we can export it separately or mount if config says so\n\n return app;\n}\n","import { Hono } from 'hono';\nimport { EmailService } from '../EmailService.js';\n\n/**\n * Create settings routes for generic key-value storage\n\n/**\n * Create settings routes for generic key-value storage\n * @param {Object} env - Environment bindings\n * @param {Object} config - Configuration\n * @param {string} [config.tableName='system_settings'] - Table name for settings\n * @param {string} [config.keyPrefix='system_email.'] - Prefix for keys managed by this router\n */\nexport function createSettingsRoutes(config = {}) {\n const app = new Hono();\n const tableName = config.tableName || 'system_settings';\n const keyPrefix = config.keyPrefix || 'system_email.';\n\n // Helper to get settings with prefix\n const getSettings = async (db) => {\n // We select ALL and filter in code because 'LIKE' might be slower or we want full flexibility\n // But for efficiency, let's use LIKE if possible.\n // Assuming keys are stored with prefix strings.\n\n try {\n const { results } = await db.prepare(`SELECT * FROM ${tableName} WHERE key LIKE ?`)\n .bind(`${keyPrefix}%`)\n .all();\n\n const settings = {};\n for (const row of results) {\n // Remove prefix for the API response\n const cleanKey = row.key.slice(keyPrefix.length);\n settings[cleanKey] = row.value;\n }\n return settings;\n } catch (e) {\n // Table might not exist yet if migration hasn't run\n console.warn(`[Settings] Failed to fetch from ${tableName}:`, e.message);\n return {};\n }\n };\n\n // GET / - Retrieve settings (scoped by prefix)\n app.get('/', async (c) => {\n try {\n const settings = await getSettings(c.env.DB);\n\n // Map known keys to frontend expected format if needed\n // But since I changed the DB strategy, let's just return what we have\n // Frontend expects camelCase properties usually.\n\n // We need to map snake_case DB keys (minus prefix) to camelCase frontend props\n // OR we just store them as camelCase in the DB?\n // \"system_email.fromName\" vs \"system_email.from_name\"\n\n // Let's standardize on camelCase for the keys stored in DB for simplicity with frontend?\n // User requested \"system_email.xxx\".\n\n // Let's assume keys are stored as `system_email.provider`, `system_email.fromName` etc.\n // This matches the frontend JSON payloads directly.\n\n return c.json(settings);\n } catch (error) {\n console.error('Failed to fetch settings:', error);\n return c.json({ error: error.message }, 500);\n }\n });\n\n // POST / - Update settings\n app.post('/', async (c) => {\n try {\n const body = await c.req.json();\n const db = c.env.DB;\n\n // Body is { provider: '...', fromName: '...' }\n // We need to upsert these with prefix.\n\n const updates = Object.entries(body).map(([k, v]) => ({\n key: `${keyPrefix}${k}`,\n value: v\n }));\n\n if (updates.length === 0) return c.json({ success: true });\n\n const stmt = db.prepare(`\n INSERT INTO ${tableName} (key, value, updated_at) \n VALUES (?, ?, strftime('%s', 'now'))\n ON CONFLICT(key) DO UPDATE SET \n value = excluded.value, \n updated_at = excluded.updated_at\n `);\n\n const batch = updates.map(u => stmt.bind(u.key, u.value === undefined || u.value === null ? '' : String(u.value)));\n\n await db.batch(batch);\n\n return c.json({ success: true });\n } catch (error) {\n console.error('Failed to save settings:', error);\n return c.json({ error: error.message }, 500);\n }\n });\n\n // POST /test - Test settings configuration\n app.post('/test', async (c) => {\n try {\n const settings = await c.req.json();\n const user = c.get('dbUser') || c.get('user');\n\n if (!user || !user.email) {\n // Try to fallback to a default email if no auth context (shouldn't happen in admin)\n // Or accept 'to' in body?\n // Let's assume user.email is available.\n return c.json({ error: 'User email not found for testing' }, 400);\n }\n\n // Create service ONLY for this request with the provided settings\n // We pass a settingsLoader that simply returns the provided settings\n // This bypasses DB lookup so we test what's in the form.\n const emailService = new EmailService(c.env, {\n ...config,\n settingsLoader: async () => settings\n });\n\n const result = await emailService.sendEmail({\n to: user.email,\n subject: 'Test Email Configuration',\n html: '<h1>It Works!</h1><p>Your email settings are configured correctly.</p>',\n text: 'It Works! Your email settings are configured correctly.',\n profile: 'system', // Use system defaults logic in service, but our loader overrides it\n userId: user.id\n });\n\n if (result.success) {\n return c.json({ success: true, message: `Test email sent to ${user.email}` });\n } else {\n return c.json({ success: false, error: result.error || 'Failed to send test email' }, 400);\n }\n } catch (error) {\n console.error('Test settings failed:', error);\n return c.json({ error: error.message }, 500);\n }\n });\n\n return app;\n}\n","/**\n * Email Template Routes Factory\n * Creates a Hono router with admin endpoints for template management.\n */\nimport { Hono } from 'hono';\nimport { EmailService } from '../EmailService.js';\n\n/**\n * Create email template routes\n * @param {Object} config - Configuration\n * @returns {Hono} Hono router\n */\nexport function createTemplateRoutes(config = {}) {\n const app = new Hono();\n\n // List all templates\n app.get('/', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n const templates = await emailService.getAllTemplates();\n // Wrap in object if needed, or return array directly to match current API expectation\n // Current API returns array directly\n return c.json(templates);\n } catch (err) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n // Get single template\n app.get('/:id', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n const template = await emailService.getTemplate(c.req.param('id'));\n if (!template) return c.json({ error: 'Template not found' }, 404);\n return c.json(template);\n } catch (err) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n // Create/Update template\n app.post('/', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n const data = await c.req.json();\n\n // Try to get user ID from various common contexts\n let userId = 'admin';\n const user = c.get('dbUser') || c.get('user');\n if (user && user.id) userId = user.id;\n\n // Basic validation\n if (!data.template_id || !data.subject_template) {\n return c.json({ error: 'Missing required fields (template_id, subject_template)' }, 400);\n }\n\n const result = await emailService.saveTemplate(data, userId);\n\n // Return format matching current API\n return c.json({ success: true, message: 'Template saved', result });\n } catch (err) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n // Send test email\n // This was mounted at /templates/:id/test in library but /send-test in backend API\n // Let's support the backend API style for compatibility\n app.post('/send-test', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n // Support both old library way (param) and new flat way (body only)\n // But here we are mounting at /api/email/templates usually, so /send-test might need to be at root of email router\n // We will address this in the index router composition\n\n const { template_id, to, variables } = await c.req.json();\n let tid = template_id;\n\n if (!tid || !to) {\n return c.json({ error: 'Missing required fields (template_id, to)' }, 400);\n }\n\n const user = c.get('dbUser') || c.get('user');\n const recipientUserId = user?.id || null;\n\n const result = await emailService.sendViaTemplate(tid, variables || {}, {\n to,\n profile: 'test',\n recipientUserId\n });\n\n return c.json({ success: true, result });\n } catch (err) {\n return c.json({ error: `Failed to send test email: ${err.message}` }, 500);\n }\n });\n\n // Delete template\n app.delete('/:id', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n await emailService.deleteTemplate(c.req.param('id'));\n return c.json({ success: true, message: 'Template deleted' });\n } catch (err) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n // Preview template\n app.post('/:id/preview', async (c) => {\n const emailService = new EmailService(c.env, config);\n try {\n const id = c.req.param('id');\n const data = await c.req.json(); // Variables\n const result = await emailService.renderTemplate(id, data);\n return c.json({ success: true, preview: result });\n } catch (err) {\n return c.json({ error: err.message }, 500);\n }\n });\n\n return app;\n}\n","/**\n * Email Tracking Routes Factory\n * Creates a Hono router with tracking endpoints for the email system.\n */\nimport { Hono } from 'hono';\n\n// 1x1 transparent GIF pixel\nconst TRACKING_PIXEL = new Uint8Array([\n 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00,\n 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x21,\n 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00,\n 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x01, 0x44,\n 0x00, 0x3B\n]);\n\n/**\n * Create email tracking routes\n * @param {Object} env - Environment bindings\n * @param {Object} config - Configuration\n * @param {string} [config.emailTablePrefix='system_email_'] - Prefix for D1 tables\n * @returns {Hono} Hono router\n */\nexport function createTrackingRoutes(env, config = {}) {\n const app = new Hono();\n const db = env.DB;\n const tablePrefix = config.emailTablePrefix || config.tableNamePrefix || 'system_email_';\n\n /**\n * GET /track/open/:token\n * Track email opens via a 1x1 pixel\n */\n app.get('/track/open/:token', async (c) => {\n const token = c.req.param('token');\n\n try {\n const sendId = token;\n\n // Look up the send record\n const send = await db\n .prepare(`SELECT * FROM ${tablePrefix}sends WHERE send_id = ?`)\n .bind(sendId)\n .first();\n\n if (send) {\n // Log the open event\n const eventId = crypto.randomUUID();\n const now = Math.floor(Date.now() / 1000);\n\n await db\n .prepare(\n `INSERT INTO ${tablePrefix}events (\n event_id, send_id, user_id, tenant_id, email_kind, event_type, metadata, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .bind(\n eventId,\n sendId,\n send.user_id,\n send.tenant_id,\n send.email_kind,\n 'opened',\n JSON.stringify({\n user_agent: c.req.header('user-agent'),\n referer: c.req.header('referer'),\n }),\n now\n )\n .run();\n }\n } catch (error) {\n console.error('[EmailTracking] Error tracking email open:', error);\n // Don't fail the request - just serve the pixel\n }\n\n // Always return the tracking pixel\n return new Response(TRACKING_PIXEL, {\n headers: {\n 'Content-Type': 'image/gif',\n 'Cache-Control': 'no-cache, no-store, must-revalidate',\n 'Pragma': 'no-cache',\n 'Expires': '0',\n },\n });\n });\n\n /**\n * POST /track/click/:token\n * Track email clicks\n */\n app.post('/track/click/:token', async (c) => {\n const token = c.req.param('token');\n\n let url;\n try {\n const body = await c.req.json();\n url = body.url;\n } catch {\n return c.json({ success: false, error: 'Invalid request body' }, 400);\n }\n\n if (!url) {\n return c.json({ success: false, error: 'Missing URL' }, 400);\n }\n\n try {\n const sendId = token;\n\n // Look up the send record\n const send = await db\n .prepare(`SELECT * FROM ${tablePrefix}sends WHERE send_id = ?`)\n .bind(sendId)\n .first();\n\n if (send) {\n // Log the click event\n const eventId = crypto.randomUUID();\n const now = Math.floor(Date.now() / 1000);\n\n await db\n .prepare(\n `INSERT INTO ${tablePrefix}events (\n event_id, send_id, user_id, tenant_id, email_kind, event_type, metadata, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .bind(\n eventId,\n sendId,\n send.user_id,\n send.tenant_id,\n send.email_kind,\n 'clicked',\n JSON.stringify({\n url: url,\n user_agent: c.req.header('user-agent'),\n referer: c.req.header('referer'),\n }),\n now\n )\n .run();\n }\n\n return c.json({ success: true, tracked: !!send });\n } catch (error) {\n console.error('[EmailTracking] Error tracking email click:', error);\n return c.json({ success: false, error: 'Failed to track click' }, 500);\n }\n });\n\n /**\n * GET /unsubscribe/:token\n * Unsubscribe a user from all email notifications\n */\n app.get('/unsubscribe/:token', async (c) => {\n const unsubToken = c.req.param('token');\n\n try {\n // Find the user by their unsubscribe token\n const prefs = await db\n .prepare(`SELECT * FROM ${tablePrefix}preferences WHERE unsub_token = ?`)\n .bind(unsubToken)\n .first();\n\n if (!prefs) {\n return c.json({ success: false, error: 'Invalid unsubscribe link' }, 404);\n }\n\n // Check if already unsubscribed\n const currentSettings = JSON.parse(prefs.email_settings || '{}');\n const alreadyUnsubscribed = Object.keys(currentSettings).length === 0;\n\n if (!alreadyUnsubscribed) {\n // Disable all email types by setting email_settings to empty object\n const now = Math.floor(Date.now() / 1000);\n await db\n .prepare(\n `UPDATE ${tablePrefix}preferences \n SET email_settings = '{}',\n updated_at = ?\n WHERE unsub_token = ?`\n )\n .bind(now, unsubToken)\n .run();\n\n // Log the unsubscribe event\n const eventId = crypto.randomUUID();\n await db\n .prepare(\n `INSERT INTO ${tablePrefix}events (\n event_id, send_id, user_id, tenant_id, email_kind, event_type, metadata, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .bind(\n eventId,\n 'unsubscribe',\n prefs.user_id,\n prefs.tenant_id,\n 'all',\n 'unsubscribed',\n JSON.stringify({\n user_agent: c.req.header('user-agent'),\n }),\n now\n )\n .run();\n }\n\n return c.json({ success: true, alreadyUnsubscribed });\n } catch (error) {\n console.error('[EmailTracking] Error processing unsubscribe:', error);\n return c.json({ success: false, error: 'An error occurred' }, 500);\n }\n });\n\n return app;\n}\n","/**\n * Email Log Routes Factory\n * Creates a Hono router with admin endpoints for email logs.\n */\nimport { Hono } from 'hono';\n\n/**\n * Create email log routes\n * @param {Object} config - Configuration\n * @returns {Hono} Hono router\n */\nexport function createLogRoutes(config = {}) {\n const app = new Hono();\n\n // List logs (paginated)\n app.get('/', async (c) => {\n const limit = Math.min(parseInt(c.req.query('limit') || '50'), 100);\n const offset = parseInt(c.req.query('offset') || '0');\n const env = c.env;\n\n try {\n const table = `${config.emailTablePrefix || 'system_email_'}logs`;\n\n const { results } = await env.DB.prepare(`\n SELECT * FROM ${table} \n ORDER BY created_at DESC \n LIMIT ? OFFSET ?\n `).bind(limit, offset).all();\n\n // Also get total count for pagination\n const countResult = await env.DB.prepare(`SELECT COUNT(*) as exact_count FROM ${table}`).first();\n\n return c.json({\n logs: results.map(row => ({\n ...row,\n metadata: row.metadata ? JSON.parse(row.metadata) : null\n })),\n total: countResult.exact_count,\n limit,\n offset\n });\n } catch (error) {\n console.error('Failed to fetch logs:', error);\n return c.json({ error: 'Failed to fetch logs' }, 500);\n }\n });\n\n return app;\n}\n","/**\n * Email Utility Functions\n * Common helpers for email processing\n */\n\nimport mustache from 'mustache';\n\n// Module-level cache for website URL\nlet cachedWebsiteUrl = null;\n\n/**\n * Build website URL based on environment configuration (with caching)\n * @param {Object} env - Environment bindings\n * @returns {string} Website URL (e.g., 'https://www.x0start.com')\n */\nexport function getWebsiteUrl(env) {\n if (cachedWebsiteUrl) {\n return cachedWebsiteUrl;\n }\n\n const domain = env.DOMAIN || 'x0start.com';\n const isDev = env.ENVIRONMENT === 'development' || !env.ENVIRONMENT;\n const protocol = isDev ? 'http' : 'https';\n\n cachedWebsiteUrl = `${protocol}://www.${domain}`;\n\n return cachedWebsiteUrl;\n}\n\n/**\n * Reset the cached website URL (for testing or config changes)\n */\nexport function resetWebsiteUrlCache() {\n cachedWebsiteUrl = null;\n}\n\n/**\n * Encode all links in HTML to go through tracking redirect\n * @param {string} html - HTML content with links\n * @param {string} sendId - Unique send ID for tracking\n * @param {Object} env - Environment bindings (optional, for auto-detecting URL)\n * @param {string} websiteUrl - Base website URL (optional, overrides env detection)\n * @returns {string} HTML with encoded tracking links\n */\nexport function encodeTrackingLinks(html, sendId, env = null, websiteUrl = null) {\n const baseUrl = websiteUrl || (env ? getWebsiteUrl(env) : 'https://www.x0start.com');\n\n // Helper to decode HTML entities\n const decodeHtmlEntities = (text) => {\n return text\n .replace(///g, '/')\n .replace(/:/g, ':')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\");\n };\n\n // Replace all href attributes with tracking URLs\n return html.replace(/href=\"([^\"]+)\"/g, (match, url) => {\n // Skip certain URLs that shouldn't be tracked\n if (\n url.startsWith('mailto:') ||\n url.startsWith('tel:') ||\n url.startsWith('#') ||\n url.includes('{{') || // Skip template variables\n url.includes('/email/unsubscribe/') ||\n url.includes('/email/track/')\n ) {\n return match;\n }\n\n // Decode HTML entities first (marked encodes URLs with HTML entities)\n const decodedUrl = decodeHtmlEntities(url);\n\n // Encode the decoded URL in base64\n const encodedUrl = Buffer.from(decodedUrl).toString('base64');\n\n // Create tracking redirect URL\n const trackingUrl = `${baseUrl}/r/${sendId}?url=${encodedUrl}`;\n\n return `href=\"${trackingUrl}\"`;\n });\n}\n\n/**\n * Convert markdown to plain text\n * @param {string} markdown - Markdown content\n * @returns {string} Plain text version\n */\nexport function markdownToPlainText(markdown) {\n return markdown\n .replace(/#{1,6}\\s+/g, '') // Remove headers\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1') // Remove bold\n .replace(/\\*(.+?)\\*/g, '$1') // Remove italic\n .replace(/\\[(.+?)\\]\\((.+?)\\)/g, '$1: $2') // Keep both link text and URL\n .replace(/`(.+?)`/g, '$1') // Remove code\n .replace(/>\\s+/g, '') // Remove blockquotes\n .replace(/\\n{3,}/g, '\\n\\n') // Normalize line breaks\n .trim();\n}\n\n/**\n * Extract variables from a Mustache template string\n * @param {string} templateString - The mustache template string\n * @returns {string[]} Array of unique variable names\n */\nexport function extractVariables(templateString) {\n if (!templateString) return [];\n\n try {\n // Parse the template to get tokens\n const tokens = mustache.parse(templateString);\n const variables = new Set();\n\n // Recursively extract variables from tokens\n const collectVariables = (tokenList) => {\n tokenList.forEach(token => {\n const type = token[0];\n const value = token[1];\n\n // Types: 'name' ({{var}}), '#' (section), '^' (inverted section), '&' (unescaped)\n if (type === 'name' || type === '#' || type === '^' || type === '&') {\n variables.add(value);\n }\n\n // Variable is in token[4] for sections\n if ((type === '#' || type === '^') && token[4]) {\n collectVariables(token[4]);\n }\n });\n };\n\n collectVariables(tokens);\n return Array.from(variables);\n } catch (error) {\n console.error('Error parsing template variables:', error);\n return [];\n }\n}\n"],"mappings":"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","names":["Mustache","import_hono","import_hono","import_hono","import_hono","import_mustache","mustache"]}
|