@soulbatical/tetra-core 0.10.4 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/README.md +78 -38
  2. package/dist/core/createApp.d.ts +1 -1
  3. package/dist/core/createApp.d.ts.map +1 -1
  4. package/dist/core/createApp.js +77 -2
  5. package/dist/core/createApp.js.map +1 -1
  6. package/dist/core/dualWriteProxy.d.ts +7 -2
  7. package/dist/core/dualWriteProxy.d.ts.map +1 -1
  8. package/dist/core/dualWriteProxy.js +16 -5
  9. package/dist/core/dualWriteProxy.js.map +1 -1
  10. package/dist/core/routeContext.d.ts +24 -0
  11. package/dist/core/routeContext.d.ts.map +1 -1
  12. package/dist/core/routeContext.js +31 -4
  13. package/dist/core/routeContext.js.map +1 -1
  14. package/dist/core/systemDb.d.ts +2 -2
  15. package/dist/core/systemDb.js +2 -2
  16. package/dist/generators/rls-checker.d.ts +1 -1
  17. package/dist/generators/rls-checker.js +1 -1
  18. package/dist/generators/rls-exec-sql.d.ts +1 -1
  19. package/dist/generators/rls-exec-sql.js +1 -1
  20. package/dist/generators/rpc/index.d.ts +1 -1
  21. package/dist/generators/rpc/index.js +1 -1
  22. package/dist/index.d.ts +3 -31
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +4 -32
  25. package/dist/index.js.map +1 -1
  26. package/dist/middleware/securityMiddleware.d.ts +1 -1
  27. package/dist/middleware/securityMiddleware.d.ts.map +1 -1
  28. package/dist/middleware/validateBody.d.ts.map +1 -1
  29. package/dist/middleware/validateBody.js +51 -8
  30. package/dist/middleware/validateBody.js.map +1 -1
  31. package/dist/shared/rfc7807ErrorResponse.d.ts +7 -0
  32. package/dist/shared/rfc7807ErrorResponse.d.ts.map +1 -1
  33. package/dist/shared/rfc7807ErrorResponse.js +19 -5
  34. package/dist/shared/rfc7807ErrorResponse.js.map +1 -1
  35. package/dist/utils/logger.d.ts.map +1 -1
  36. package/dist/utils/logger.js +16 -1
  37. package/dist/utils/logger.js.map +1 -1
  38. package/package.json +33 -77
  39. package/dist/affiliate.d.ts +0 -11
  40. package/dist/affiliate.d.ts.map +0 -1
  41. package/dist/affiliate.js +0 -10
  42. package/dist/affiliate.js.map +0 -1
  43. package/dist/billing.d.ts +0 -8
  44. package/dist/billing.d.ts.map +0 -1
  45. package/dist/billing.js +0 -7
  46. package/dist/billing.js.map +0 -1
  47. package/dist/email.d.ts +0 -9
  48. package/dist/email.d.ts.map +0 -1
  49. package/dist/email.js +0 -8
  50. package/dist/email.js.map +0 -1
  51. package/dist/generators/rls-exec-sql.sql +0 -57
  52. package/dist/generators.d.ts +0 -15
  53. package/dist/generators.d.ts.map +0 -1
  54. package/dist/generators.js +0 -12
  55. package/dist/generators.js.map +0 -1
  56. package/dist/mcp.d.ts +0 -8
  57. package/dist/mcp.d.ts.map +0 -1
  58. package/dist/mcp.js +0 -7
  59. package/dist/mcp.js.map +0 -1
  60. package/dist/planner.d.ts +0 -8
  61. package/dist/planner.d.ts.map +0 -1
  62. package/dist/planner.js +0 -7
  63. package/dist/planner.js.map +0 -1
  64. package/dist/shared/affiliate/AffiliateAttributionService.d.ts +0 -47
  65. package/dist/shared/affiliate/AffiliateAttributionService.d.ts.map +0 -1
  66. package/dist/shared/affiliate/AffiliateAttributionService.js +0 -308
  67. package/dist/shared/affiliate/AffiliateAttributionService.js.map +0 -1
  68. package/dist/shared/affiliate/AffiliateClickService.d.ts +0 -35
  69. package/dist/shared/affiliate/AffiliateClickService.d.ts.map +0 -1
  70. package/dist/shared/affiliate/AffiliateClickService.js +0 -87
  71. package/dist/shared/affiliate/AffiliateClickService.js.map +0 -1
  72. package/dist/shared/affiliate/affiliateFeatureConfig.d.ts +0 -11
  73. package/dist/shared/affiliate/affiliateFeatureConfig.d.ts.map +0 -1
  74. package/dist/shared/affiliate/affiliateFeatureConfig.js +0 -242
  75. package/dist/shared/affiliate/affiliateFeatureConfig.js.map +0 -1
  76. package/dist/shared/affiliate/index.d.ts +0 -11
  77. package/dist/shared/affiliate/index.d.ts.map +0 -1
  78. package/dist/shared/affiliate/index.js +0 -13
  79. package/dist/shared/affiliate/index.js.map +0 -1
  80. package/dist/shared/affiliate/routes.d.ts +0 -87
  81. package/dist/shared/affiliate/routes.d.ts.map +0 -1
  82. package/dist/shared/affiliate/routes.js +0 -404
  83. package/dist/shared/affiliate/routes.js.map +0 -1
  84. package/dist/shared/affiliate/types.d.ts +0 -170
  85. package/dist/shared/affiliate/types.d.ts.map +0 -1
  86. package/dist/shared/affiliate/types.js +0 -11
  87. package/dist/shared/affiliate/types.js.map +0 -1
  88. package/dist/shared/billing/BillingService.d.ts +0 -56
  89. package/dist/shared/billing/BillingService.d.ts.map +0 -1
  90. package/dist/shared/billing/BillingService.js +0 -588
  91. package/dist/shared/billing/BillingService.js.map +0 -1
  92. package/dist/shared/billing/SeatBillingService.d.ts +0 -106
  93. package/dist/shared/billing/SeatBillingService.d.ts.map +0 -1
  94. package/dist/shared/billing/SeatBillingService.js +0 -292
  95. package/dist/shared/billing/SeatBillingService.js.map +0 -1
  96. package/dist/shared/billing/index.d.ts +0 -30
  97. package/dist/shared/billing/index.d.ts.map +0 -1
  98. package/dist/shared/billing/index.js +0 -27
  99. package/dist/shared/billing/index.js.map +0 -1
  100. package/dist/shared/billing/routes.d.ts +0 -45
  101. package/dist/shared/billing/routes.d.ts.map +0 -1
  102. package/dist/shared/billing/routes.js +0 -184
  103. package/dist/shared/billing/routes.js.map +0 -1
  104. package/dist/shared/billing/seat-pricing.d.ts +0 -53
  105. package/dist/shared/billing/seat-pricing.d.ts.map +0 -1
  106. package/dist/shared/billing/seat-pricing.js +0 -81
  107. package/dist/shared/billing/seat-pricing.js.map +0 -1
  108. package/dist/shared/billing/types.d.ts +0 -109
  109. package/dist/shared/billing/types.d.ts.map +0 -1
  110. package/dist/shared/billing/types.js +0 -8
  111. package/dist/shared/billing/types.js.map +0 -1
  112. package/dist/shared/email/EmailService.d.ts +0 -64
  113. package/dist/shared/email/EmailService.d.ts.map +0 -1
  114. package/dist/shared/email/EmailService.js +0 -300
  115. package/dist/shared/email/EmailService.js.map +0 -1
  116. package/dist/shared/email/adminRoutes.d.ts +0 -30
  117. package/dist/shared/email/adminRoutes.d.ts.map +0 -1
  118. package/dist/shared/email/adminRoutes.js +0 -227
  119. package/dist/shared/email/adminRoutes.js.map +0 -1
  120. package/dist/shared/email/gmail.d.ts +0 -208
  121. package/dist/shared/email/gmail.d.ts.map +0 -1
  122. package/dist/shared/email/gmail.js +0 -626
  123. package/dist/shared/email/gmail.js.map +0 -1
  124. package/dist/shared/email/index.d.ts +0 -15
  125. package/dist/shared/email/index.d.ts.map +0 -1
  126. package/dist/shared/email/index.js +0 -18
  127. package/dist/shared/email/index.js.map +0 -1
  128. package/dist/shared/email/mailgun.d.ts +0 -18
  129. package/dist/shared/email/mailgun.d.ts.map +0 -1
  130. package/dist/shared/email/mailgun.js +0 -76
  131. package/dist/shared/email/mailgun.js.map +0 -1
  132. package/dist/shared/email/sanitize.d.ts +0 -25
  133. package/dist/shared/email/sanitize.d.ts.map +0 -1
  134. package/dist/shared/email/sanitize.js +0 -39
  135. package/dist/shared/email/sanitize.js.map +0 -1
  136. package/dist/shared/email/smtp.d.ts +0 -20
  137. package/dist/shared/email/smtp.d.ts.map +0 -1
  138. package/dist/shared/email/smtp.js +0 -53
  139. package/dist/shared/email/smtp.js.map +0 -1
  140. package/dist/shared/email/types.d.ts +0 -113
  141. package/dist/shared/email/types.d.ts.map +0 -1
  142. package/dist/shared/email/types.js +0 -7
  143. package/dist/shared/email/types.js.map +0 -1
  144. package/dist/shared/email/webhookRoutes.d.ts +0 -29
  145. package/dist/shared/email/webhookRoutes.d.ts.map +0 -1
  146. package/dist/shared/email/webhookRoutes.js +0 -125
  147. package/dist/shared/email/webhookRoutes.js.map +0 -1
  148. package/dist/shared/mcp/index.d.ts +0 -51
  149. package/dist/shared/mcp/index.d.ts.map +0 -1
  150. package/dist/shared/mcp/index.js +0 -51
  151. package/dist/shared/mcp/index.js.map +0 -1
  152. package/dist/shared/mcp/mcp-auth-routes.d.ts +0 -26
  153. package/dist/shared/mcp/mcp-auth-routes.d.ts.map +0 -1
  154. package/dist/shared/mcp/mcp-auth-routes.js +0 -141
  155. package/dist/shared/mcp/mcp-auth-routes.js.map +0 -1
  156. package/dist/shared/mcp/mcp-db.d.ts +0 -99
  157. package/dist/shared/mcp/mcp-db.d.ts.map +0 -1
  158. package/dist/shared/mcp/mcp-db.js +0 -106
  159. package/dist/shared/mcp/mcp-db.js.map +0 -1
  160. package/dist/shared/mcp/mcp-routes.d.ts +0 -29
  161. package/dist/shared/mcp/mcp-routes.d.ts.map +0 -1
  162. package/dist/shared/mcp/mcp-routes.js +0 -171
  163. package/dist/shared/mcp/mcp-routes.js.map +0 -1
  164. package/dist/shared/mcp/mcp-tokens-routes.d.ts +0 -35
  165. package/dist/shared/mcp/mcp-tokens-routes.d.ts.map +0 -1
  166. package/dist/shared/mcp/mcp-tokens-routes.js +0 -94
  167. package/dist/shared/mcp/mcp-tokens-routes.js.map +0 -1
  168. package/dist/shared/mcp/mcp-usage-routes.d.ts +0 -17
  169. package/dist/shared/mcp/mcp-usage-routes.d.ts.map +0 -1
  170. package/dist/shared/mcp/mcp-usage-routes.js +0 -81
  171. package/dist/shared/mcp/mcp-usage-routes.js.map +0 -1
  172. package/dist/shared/mcp/tenant-context.d.ts +0 -59
  173. package/dist/shared/mcp/tenant-context.d.ts.map +0 -1
  174. package/dist/shared/mcp/tenant-context.js +0 -136
  175. package/dist/shared/mcp/tenant-context.js.map +0 -1
  176. package/dist/shared/mcp/types.d.ts +0 -74
  177. package/dist/shared/mcp/types.d.ts.map +0 -1
  178. package/dist/shared/mcp/types.js +0 -7
  179. package/dist/shared/mcp/types.js.map +0 -1
  180. package/dist/shared/planner/GoogleCalendarService.d.ts +0 -137
  181. package/dist/shared/planner/GoogleCalendarService.d.ts.map +0 -1
  182. package/dist/shared/planner/GoogleCalendarService.js +0 -525
  183. package/dist/shared/planner/GoogleCalendarService.js.map +0 -1
  184. package/dist/shared/planner/PlannerService.d.ts +0 -264
  185. package/dist/shared/planner/PlannerService.d.ts.map +0 -1
  186. package/dist/shared/planner/PlannerService.js +0 -1393
  187. package/dist/shared/planner/PlannerService.js.map +0 -1
  188. package/dist/shared/planner/index.d.ts +0 -37
  189. package/dist/shared/planner/index.d.ts.map +0 -1
  190. package/dist/shared/planner/index.js +0 -35
  191. package/dist/shared/planner/index.js.map +0 -1
  192. package/dist/shared/planner/intervals.d.ts +0 -60
  193. package/dist/shared/planner/intervals.d.ts.map +0 -1
  194. package/dist/shared/planner/intervals.js +0 -141
  195. package/dist/shared/planner/intervals.js.map +0 -1
  196. package/dist/shared/planner/routes.d.ts +0 -69
  197. package/dist/shared/planner/routes.d.ts.map +0 -1
  198. package/dist/shared/planner/routes.js +0 -770
  199. package/dist/shared/planner/routes.js.map +0 -1
  200. package/dist/shared/planner/types.d.ts +0 -328
  201. package/dist/shared/planner/types.d.ts.map +0 -1
  202. package/dist/shared/planner/types.js +0 -9
  203. package/dist/shared/planner/types.js.map +0 -1
  204. package/dist/shared/storage/ImageProcessingService.d.ts +0 -32
  205. package/dist/shared/storage/ImageProcessingService.d.ts.map +0 -1
  206. package/dist/shared/storage/ImageProcessingService.js +0 -127
  207. package/dist/shared/storage/ImageProcessingService.js.map +0 -1
  208. package/dist/shared/storage/StorageProxyService.d.ts +0 -47
  209. package/dist/shared/storage/StorageProxyService.d.ts.map +0 -1
  210. package/dist/shared/storage/StorageProxyService.js +0 -196
  211. package/dist/shared/storage/StorageProxyService.js.map +0 -1
  212. package/dist/shared/storage/StorageUploadService.d.ts +0 -126
  213. package/dist/shared/storage/StorageUploadService.d.ts.map +0 -1
  214. package/dist/shared/storage/StorageUploadService.js +0 -206
  215. package/dist/shared/storage/StorageUploadService.js.map +0 -1
  216. package/dist/shared/storage/creative-urls.d.ts +0 -14
  217. package/dist/shared/storage/creative-urls.d.ts.map +0 -1
  218. package/dist/shared/storage/creative-urls.js +0 -30
  219. package/dist/shared/storage/creative-urls.js.map +0 -1
  220. package/dist/shared/storage/index.d.ts +0 -28
  221. package/dist/shared/storage/index.d.ts.map +0 -1
  222. package/dist/shared/storage/index.js +0 -27
  223. package/dist/shared/storage/index.js.map +0 -1
  224. package/dist/shared/storage/routes.d.ts +0 -42
  225. package/dist/shared/storage/routes.d.ts.map +0 -1
  226. package/dist/shared/storage/routes.js +0 -160
  227. package/dist/shared/storage/routes.js.map +0 -1
  228. package/dist/shared/storage/types.d.ts +0 -53
  229. package/dist/shared/storage/types.d.ts.map +0 -1
  230. package/dist/shared/storage/types.js +0 -2
  231. package/dist/shared/storage/types.js.map +0 -1
  232. package/dist/shared/telegram/index.d.ts +0 -4
  233. package/dist/shared/telegram/index.d.ts.map +0 -1
  234. package/dist/shared/telegram/index.js +0 -3
  235. package/dist/shared/telegram/index.js.map +0 -1
  236. package/dist/shared/telegram/routes.d.ts +0 -43
  237. package/dist/shared/telegram/routes.d.ts.map +0 -1
  238. package/dist/shared/telegram/routes.js +0 -868
  239. package/dist/shared/telegram/routes.js.map +0 -1
  240. package/dist/shared/telegram/types.d.ts +0 -168
  241. package/dist/shared/telegram/types.d.ts.map +0 -1
  242. package/dist/shared/telegram/types.js +0 -7
  243. package/dist/shared/telegram/types.js.map +0 -1
  244. package/dist/shared/telegram/utils.d.ts +0 -44
  245. package/dist/shared/telegram/utils.d.ts.map +0 -1
  246. package/dist/shared/telegram/utils.js +0 -121
  247. package/dist/shared/telegram/utils.js.map +0 -1
  248. package/dist/storage.d.ts +0 -9
  249. package/dist/storage.d.ts.map +0 -1
  250. package/dist/storage.js +0 -8
  251. package/dist/storage.js.map +0 -1
  252. package/dist/telemetry.d.ts +0 -9
  253. package/dist/telemetry.d.ts.map +0 -1
  254. package/dist/telemetry.js +0 -8
  255. package/dist/telemetry.js.map +0 -1
  256. package/scripts/postinstall.js +0 -79
  257. package/src/shared/affiliate/migrations/001_create_affiliates.sql +0 -49
  258. package/src/shared/affiliate/migrations/002_create_affiliate_commissions.sql +0 -31
  259. package/src/shared/affiliate/migrations/003_create_affiliate_clicks.sql +0 -26
  260. package/src/shared/affiliate/migrations/004_create_affiliate_payments.sql +0 -34
  261. package/src/shared/affiliate/migrations/005_create_affiliate_tier_history.sql +0 -19
  262. package/src/shared/affiliate/migrations/006_create_affiliate_rpc_functions.sql +0 -209
  263. package/src/shared/affiliate/migrations/007_create_affiliate_rls_policies.sql +0 -123
  264. package/src/shared/billing/migrations/00000000000001_billing.sql +0 -114
  265. package/src/shared/email/migrations/000_create_email_logs.sql +0 -27
  266. package/src/shared/email/migrations/001_create_email_templates.sql +0 -27
  267. package/src/shared/email/migrations/002_add_rls_baseline_policies.sql +0 -37
  268. package/src/shared/email/migrations/003_create_gmail_accounts.sql +0 -82
  269. package/src/shared/email/migrations/004_add_email_logs_tracking_columns.sql +0 -15
  270. package/src/shared/mcp/migrations/001_mcp_api_tokens.sql +0 -21
  271. package/src/shared/mcp/migrations/002_mcp_audit_log.sql +0 -16
@@ -1,300 +0,0 @@
1
- /**
2
- * EmailService — Core service for sending templated emails.
3
- *
4
- * Template lookup with language + org fallback, Handlebars rendering,
5
- * DB logging, and multi-transport delivery (Gmail, SMTP, Mailgun).
6
- *
7
- * @module @soulbatical/tetra-core/email
8
- */
9
- import Handlebars from 'handlebars';
10
- import { sendMailgunEmail } from './mailgun.js';
11
- import { sendSmtpEmail } from './smtp.js';
12
- import { getGmailClient } from './gmail.js';
13
- const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
14
- export class EmailService {
15
- supabase;
16
- config;
17
- templateCache = new Map();
18
- constructor(supabase, config) {
19
- this.supabase = supabase;
20
- this.config = config;
21
- }
22
- /**
23
- * Send a templated email.
24
- *
25
- * Template lookup order:
26
- * 1. slug + language + organization_id (org-specific translation)
27
- * 2. slug + language + org IS NULL (system default in requested language)
28
- * 3. slug + 'en' + org IS NULL (English fallback)
29
- * 4. fallbackSubject / fallbackHtml from code
30
- */
31
- async send(opts) {
32
- const language = opts.language || this.config.defaultLanguage;
33
- // 1. Load template with fallback chain
34
- const template = await this.loadTemplate(opts.templateSlug, language, opts.organizationId);
35
- const subject = template
36
- ? this.render(template.subject, opts.variables)
37
- : opts.fallbackSubject || opts.templateSlug;
38
- const html = template
39
- ? this.render(template.body_html, opts.variables)
40
- : opts.fallbackHtml || `<p>${subject}</p>`;
41
- const text = template?.body_text
42
- ? this.render(template.body_text, opts.variables)
43
- : opts.fallbackText || undefined;
44
- // 2. Create log entry (pending)
45
- const logId = await this.createLog({
46
- organizationId: opts.organizationId,
47
- templateId: template?.id,
48
- to: opts.to,
49
- subject,
50
- templateSlug: opts.templateSlug,
51
- variables: opts.variables,
52
- emailType: opts.emailType,
53
- metadata: opts.metadata,
54
- feedbackId: opts.feedbackId,
55
- });
56
- // 3. Send via configured transport
57
- const result = await this.deliver({
58
- from: this.config.fromAddress,
59
- to: opts.to,
60
- subject,
61
- html,
62
- text,
63
- replyTo: opts.replyTo,
64
- attachments: opts.attachments,
65
- });
66
- // 4. Update log
67
- if (logId) {
68
- await this.updateLog(logId, {
69
- status: result.success ? 'sent' : 'failed',
70
- mailgunMessageId: result.messageId,
71
- errorMessage: result.success ? undefined : result.message,
72
- });
73
- }
74
- return { success: result.success, logId: logId || undefined };
75
- }
76
- /**
77
- * Clear template cache. Call after template is updated via admin UI.
78
- */
79
- clearCache(slug) {
80
- if (slug) {
81
- // Clear all cache keys that start with this slug (all language/org variants)
82
- for (const key of this.templateCache.keys()) {
83
- if (key.startsWith(`${slug}:`)) {
84
- this.templateCache.delete(key);
85
- }
86
- }
87
- }
88
- else {
89
- this.templateCache.clear();
90
- }
91
- }
92
- /**
93
- * Resolve transport and send email.
94
- * Priority: explicit config.transport > auto-detect (gmail > smtp > mailgun).
95
- */
96
- async deliver(params) {
97
- const transport = this.resolveTransport();
98
- if (transport === 'gmail') {
99
- return this.deliverViaGmail(params);
100
- }
101
- if (transport === 'smtp') {
102
- return sendSmtpEmail({
103
- from: params.from,
104
- to: params.to,
105
- subject: params.subject,
106
- html: params.html,
107
- text: params.text,
108
- replyTo: params.replyTo,
109
- attachments: params.attachments,
110
- smtpHost: this.config.smtpHost,
111
- smtpPort: this.config.smtpPort ?? 587,
112
- smtpUser: this.config.smtpUser,
113
- smtpPass: this.config.smtpPass,
114
- smtpSecure: this.config.smtpSecure,
115
- });
116
- }
117
- return sendMailgunEmail({
118
- from: params.from,
119
- to: params.to,
120
- subject: params.subject,
121
- html: params.html,
122
- text: params.text,
123
- replyTo: params.replyTo,
124
- apiKey: this.config.mailgunApiKey,
125
- domain: this.config.mailgunDomain,
126
- endpoint: this.config.mailgunEndpoint,
127
- });
128
- }
129
- /**
130
- * Send via Gmail API using stored OAuth tokens.
131
- */
132
- async deliverViaGmail(params) {
133
- const userId = this.config.gmailUserId;
134
- const gmailEmail = this.config.gmailEmail;
135
- if (!userId) {
136
- return { success: false, message: 'Gmail transport requires gmailUserId in EmailConfig' };
137
- }
138
- try {
139
- const client = await getGmailClient(this.supabase, userId, gmailEmail);
140
- if (!client) {
141
- return { success: false, message: 'No active Gmail account found. Run Gmail connect first.' };
142
- }
143
- const result = await client.sendHtml(params.to, params.subject, params.html);
144
- return {
145
- success: true,
146
- message: `Email sent via Gmail (${client.getEmail()})`,
147
- messageId: result.id,
148
- };
149
- }
150
- catch (err) {
151
- const msg = err instanceof Error ? err.message : String(err);
152
- console.error('[email/gmail] send failed:', msg);
153
- return { success: false, message: `Gmail send failed: ${msg}` };
154
- }
155
- }
156
- resolveTransport() {
157
- if (this.config.transport)
158
- return this.config.transport;
159
- if (this.config.gmailUserId)
160
- return 'gmail';
161
- if (this.config.smtpHost && this.config.smtpUser && this.config.smtpPass)
162
- return 'smtp';
163
- return 'mailgun';
164
- }
165
- /**
166
- * Template lookup with language + org fallback chain.
167
- * Results are cached for 5 minutes.
168
- */
169
- async loadTemplate(slug, language, organizationId) {
170
- // Try: slug + language + org (if org provided)
171
- if (organizationId) {
172
- const cacheKey = `${slug}:${language}:${organizationId}`;
173
- const cached = this.getCached(cacheKey);
174
- if (cached !== undefined)
175
- return cached;
176
- const { data } = await this.supabase
177
- .from('email_templates')
178
- .select('*')
179
- .eq('slug', slug)
180
- .eq('language', language)
181
- .eq('organization_id', organizationId)
182
- .eq('is_active', true)
183
- .limit(1)
184
- .single();
185
- if (data) {
186
- this.setCache(cacheKey, data);
187
- return data;
188
- }
189
- }
190
- // Try: slug + language + system default (org IS NULL)
191
- const langKey = `${slug}:${language}:system`;
192
- const cachedLang = this.getCached(langKey);
193
- if (cachedLang !== undefined)
194
- return cachedLang;
195
- const { data: langDefault } = await this.supabase
196
- .from('email_templates')
197
- .select('*')
198
- .eq('slug', slug)
199
- .eq('language', language)
200
- .is('organization_id', null)
201
- .eq('is_active', true)
202
- .limit(1)
203
- .single();
204
- if (langDefault) {
205
- this.setCache(langKey, langDefault);
206
- return langDefault;
207
- }
208
- // Try: slug + English fallback + system default
209
- if (language !== 'en') {
210
- const enKey = `${slug}:en:system`;
211
- const cachedEn = this.getCached(enKey);
212
- if (cachedEn !== undefined)
213
- return cachedEn;
214
- const { data: enDefault } = await this.supabase
215
- .from('email_templates')
216
- .select('*')
217
- .eq('slug', slug)
218
- .eq('language', 'en')
219
- .is('organization_id', null)
220
- .eq('is_active', true)
221
- .limit(1)
222
- .single();
223
- if (enDefault) {
224
- this.setCache(enKey, enDefault);
225
- return enDefault;
226
- }
227
- }
228
- return null;
229
- }
230
- getCached(key) {
231
- const entry = this.templateCache.get(key);
232
- if (!entry)
233
- return undefined;
234
- if (Date.now() - entry.cachedAt > CACHE_TTL) {
235
- this.templateCache.delete(key);
236
- return undefined;
237
- }
238
- return entry.template;
239
- }
240
- setCache(key, template) {
241
- this.templateCache.set(key, { template, cachedAt: Date.now() });
242
- }
243
- /**
244
- * Render a Handlebars template string with variables.
245
- * Rejects templates with triple-brace {{{…}}} syntax to prevent escaping bypass.
246
- */
247
- render(template, variables) {
248
- if (/\{\{\{/.test(template)) {
249
- throw new Error('[email] Template contains unsafe triple-brace syntax — rejected');
250
- }
251
- const compiled = Handlebars.compile(template, { noEscape: false });
252
- return compiled(variables);
253
- }
254
- /**
255
- * Create a pending email log entry.
256
- */
257
- async createLog(opts) {
258
- const insert = {
259
- organization_id: opts.organizationId || null,
260
- template_id: opts.templateId || null,
261
- to_email: opts.to,
262
- subject: opts.subject,
263
- template_slug: opts.templateSlug || null,
264
- variables_used: opts.variables || {},
265
- email_type: opts.emailType || null,
266
- metadata: opts.metadata || null,
267
- status: 'pending',
268
- };
269
- // feedbackId is app-specific — only include if the column exists
270
- // Consuming projects add their own FK columns via migrations
271
- if (opts.feedbackId) {
272
- insert.feedback_id = opts.feedbackId;
273
- }
274
- const { data, error } = await this.supabase
275
- .from('email_logs')
276
- .insert(insert)
277
- .select('id')
278
- .single();
279
- if (error) {
280
- console.error('[email] log insert failed:', error.message);
281
- return null;
282
- }
283
- return data.id;
284
- }
285
- /**
286
- * Update an email log entry after send attempt.
287
- */
288
- async updateLog(logId, update) {
289
- await this.supabase
290
- .from('email_logs')
291
- .update({
292
- status: update.status,
293
- mailgun_message_id: update.mailgunMessageId || null,
294
- error_message: update.errorMessage || null,
295
- ...(update.status === 'sent' ? { sent_at: new Date().toISOString() } : {}),
296
- })
297
- .eq('id', logId);
298
- }
299
- }
300
- //# sourceMappingURL=EmailService.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EmailService.js","sourceRoot":"","sources":["../../../src/shared/email/EmailService.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,UAAU,MAAM,YAAY,CAAC;AAEpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAQ5C,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAE7C,MAAM,OAAO,YAAY;IAIb;IACA;IAJF,aAAa,GAAG,IAAI,GAAG,EAAsB,CAAC;IAEtD,YACU,QAAwB,EACxB,MAAmB;QADnB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,WAAM,GAAN,MAAM,CAAa;IAC1B,CAAC;IAEJ;;;;;;;;OAQG;IACH,KAAK,CAAC,IAAI,CAAC,IAAmB;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;QAE9D,uCAAuC;QACvC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAE3F,MAAM,OAAO,GAAG,QAAQ;YACtB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;YAC/C,CAAC,CAAC,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,YAAY,CAAC;QAE9C,MAAM,IAAI,GAAG,QAAQ;YACnB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC;YACjD,CAAC,CAAC,IAAI,CAAC,YAAY,IAAI,MAAM,OAAO,MAAM,CAAC;QAE7C,MAAM,IAAI,GAAG,QAAQ,EAAE,SAAS;YAC9B,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC;YACjD,CAAC,CAAC,IAAI,CAAC,YAAY,IAAI,SAAS,CAAC;QAEnC,gCAAgC;QAChC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC;YACjC,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,UAAU,EAAE,QAAQ,EAAE,EAAE;YACxB,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,OAAO;YACP,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B,CAAC,CAAC;QAEH,mCAAmC;QACnC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;YAChC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;YAC7B,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,OAAO;YACP,IAAI;YACJ,IAAI;YACJ,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC,CAAC;QAEH,gBAAgB;QAChB,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;gBAC1B,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;gBAC1C,gBAAgB,EAAE,MAAM,CAAC,SAAS;gBAClC,YAAY,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO;aAC1D,CAAC,CAAC;QACL,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,IAAI,SAAS,EAAE,CAAC;IAChE,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,IAAa;QACtB,IAAI,IAAI,EAAE,CAAC;YACT,6EAA6E;YAC7E,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC5C,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;oBAC/B,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjC,CAAC;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,OAAO,CAAC,MAQrB;QACC,MAAM,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE1C,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;QAED,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;YACzB,OAAO,aAAa,CAAC;gBACnB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAS;gBAC/B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,GAAG;gBACrC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAS;gBAC/B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAS;gBAC/B,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU;aACnC,CAAC,CAAC;QACL,CAAC;QAED,OAAO,gBAAgB,CAAC;YACtB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa;YACjC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa;YACjC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,eAAe;SACtC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,eAAe,CAAC,MAQ7B;QACC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;QAE1C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,qDAAqD,EAAE,CAAC;QAC5F,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;YACvE,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,yDAAyD,EAAE,CAAC;YAChG,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YAC7E,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,yBAAyB,MAAM,CAAC,QAAQ,EAAE,GAAG;gBACtD,SAAS,EAAE,MAAM,CAAC,EAAE;aACrB,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC;YACjD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,sBAAsB,GAAG,EAAE,EAAE,CAAC;QAClE,CAAC;IACH,CAAC;IAEO,gBAAgB;QACtB,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;QACxD,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW;YAAE,OAAO,OAAO,CAAC;QAC5C,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ;YAAE,OAAO,MAAM,CAAC;QACxF,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,YAAY,CACxB,IAAY,EACZ,QAAgB,EAChB,cAAuB;QAEvB,+CAA+C;QAC/C,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,QAAQ,GAAG,GAAG,IAAI,IAAI,QAAQ,IAAI,cAAc,EAAE,CAAC;YACzD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACxC,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,MAAM,CAAC;YAExC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ;iBACjC,IAAI,CAAC,iBAAiB,CAAC;iBACvB,MAAM,CAAC,GAAG,CAAC;iBACX,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC;iBAChB,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC;iBACxB,EAAE,CAAC,iBAAiB,EAAE,cAAc,CAAC;iBACrC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC;iBACrB,KAAK,CAAC,CAAC,CAAC;iBACR,MAAM,EAAE,CAAC;YACZ,IAAI,IAAI,EAAE,CAAC;gBACT,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAqB,CAAC,CAAC;gBAC/C,OAAO,IAAqB,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,sDAAsD;QACtD,MAAM,OAAO,GAAG,GAAG,IAAI,IAAI,QAAQ,SAAS,CAAC;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,UAAU,KAAK,SAAS;YAAE,OAAO,UAAU,CAAC;QAEhD,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ;aAC9C,IAAI,CAAC,iBAAiB,CAAC;aACvB,MAAM,CAAC,GAAG,CAAC;aACX,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC;aAChB,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC;aACxB,EAAE,CAAC,iBAAiB,EAAE,IAAI,CAAC;aAC3B,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC;aACrB,KAAK,CAAC,CAAC,CAAC;aACR,MAAM,EAAE,CAAC;QACZ,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,WAA4B,CAAC,CAAC;YACrD,OAAO,WAA4B,CAAC;QACtC,CAAC;QAED,gDAAgD;QAChD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,GAAG,IAAI,YAAY,CAAC;YAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACvC,IAAI,QAAQ,KAAK,SAAS;gBAAE,OAAO,QAAQ,CAAC;YAE5C,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ;iBAC5C,IAAI,CAAC,iBAAiB,CAAC;iBACvB,MAAM,CAAC,GAAG,CAAC;iBACX,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC;iBAChB,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC;iBACpB,EAAE,CAAC,iBAAiB,EAAE,IAAI,CAAC;iBAC3B,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC;iBACrB,KAAK,CAAC,CAAC,CAAC;iBACR,MAAM,EAAE,CAAC;YACZ,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,SAA0B,CAAC,CAAC;gBACjD,OAAO,SAA0B,CAAC;YACpC,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,SAAS,CAAC,GAAW;QAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,QAAQ,GAAG,SAAS,EAAE,CAAC;YAC5C,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC/B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,KAAK,CAAC,QAAQ,CAAC;IACxB,CAAC;IAEO,QAAQ,CAAC,GAAW,EAAE,QAAuB;QACnD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAClE,CAAC;IAED;;;OAGG;IACK,MAAM,CAAC,QAAgB,EAAE,SAAiC;QAChE,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;QACrF,CAAC;QACD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QACnE,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,SAAS,CAAC,IAUvB;QACC,MAAM,MAAM,GAA4B;YACtC,eAAe,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI;YAC5C,WAAW,EAAE,IAAI,CAAC,UAAU,IAAI,IAAI;YACpC,QAAQ,EAAE,IAAI,CAAC,EAAE;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,aAAa,EAAE,IAAI,CAAC,YAAY,IAAI,IAAI;YACxC,cAAc,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;YACpC,UAAU,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;YAClC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI;YAC/B,MAAM,EAAE,SAAS;SAClB,CAAC;QAEF,iEAAiE;QACjE,6DAA6D;QAC7D,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACvC,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ;aACxC,IAAI,CAAC,YAAY,CAAC;aAClB,MAAM,CAAC,MAAM,CAAC;aACd,MAAM,CAAC,IAAI,CAAC;aACZ,MAAM,EAAE,CAAC;QAEZ,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3D,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,SAAS,CACrB,KAAa,EACb,MAIC;QAED,MAAM,IAAI,CAAC,QAAQ;aAChB,IAAI,CAAC,YAAY,CAAC;aAClB,MAAM,CAAC;YACN,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,kBAAkB,EAAE,MAAM,CAAC,gBAAgB,IAAI,IAAI;YACnD,aAAa,EAAE,MAAM,CAAC,YAAY,IAAI,IAAI;YAC1C,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3E,CAAC;aACD,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrB,CAAC;CACF"}
@@ -1,30 +0,0 @@
1
- /**
2
- * Email Admin Routes — CRUD for email templates + send test email.
3
- *
4
- * Usage:
5
- * import { addEmailAdminRoutes } from '@soulbatical/tetra-core';
6
- * addEmailAdminRoutes(router, { supabase, emailService });
7
- *
8
- * Mounts:
9
- * GET /email/templates — List all templates
10
- * GET /email/templates/:id — Get single template
11
- * POST /email/templates — Create template
12
- * PUT /email/templates/:id — Update template
13
- * DELETE /email/templates/:id — Soft-delete (set is_active=false)
14
- * POST /email/templates/:id/test — Send test email
15
- * GET /email/logs — List email logs
16
- *
17
- * @module @soulbatical/tetra-core/email
18
- */
19
- import { Router, Request } from 'express';
20
- import type { SupabaseClient } from '@supabase/supabase-js';
21
- import type { EmailService } from './EmailService.js';
22
- interface EmailAdminRoutesConfig {
23
- supabase: SupabaseClient;
24
- emailService: EmailService;
25
- /** Organization ID getter from request (for multi-tenant filtering). Optional. */
26
- getOrganizationId?: (req: Request) => string | undefined;
27
- }
28
- export declare function addEmailAdminRoutes(router: Router, config: EmailAdminRoutesConfig): void;
29
- export {};
30
- //# sourceMappingURL=adminRoutes.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"adminRoutes.d.ts","sourceRoot":"","sources":["../../../src/shared/email/adminRoutes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,MAAM,EAAE,OAAO,EAAY,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,cAAc,CAAC;IACzB,YAAY,EAAE,YAAY,CAAC;IAC3B,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;CAC1D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB,GAAG,IAAI,CA2NxF"}
@@ -1,227 +0,0 @@
1
- /**
2
- * Email Admin Routes — CRUD for email templates + send test email.
3
- *
4
- * Usage:
5
- * import { addEmailAdminRoutes } from '@soulbatical/tetra-core';
6
- * addEmailAdminRoutes(router, { supabase, emailService });
7
- *
8
- * Mounts:
9
- * GET /email/templates — List all templates
10
- * GET /email/templates/:id — Get single template
11
- * POST /email/templates — Create template
12
- * PUT /email/templates/:id — Update template
13
- * DELETE /email/templates/:id — Soft-delete (set is_active=false)
14
- * POST /email/templates/:id/test — Send test email
15
- * GET /email/logs — List email logs
16
- *
17
- * @module @soulbatical/tetra-core/email
18
- */
19
- export function addEmailAdminRoutes(router, config) {
20
- const { supabase, emailService } = config;
21
- // GET /email/templates — List all templates
22
- router.get('/email/templates', async (req, res) => {
23
- try {
24
- const orgId = config.getOrganizationId?.(req);
25
- let query = supabase
26
- .from('email_templates')
27
- .select('id, slug, name, subject, language, category, is_active, organization_id, created_at, updated_at')
28
- .order('slug', { ascending: true });
29
- if (orgId) {
30
- // Show system templates (org IS NULL) + org-specific ones
31
- query = query.or(`organization_id.is.null,organization_id.eq.${orgId}`);
32
- }
33
- const { data, error } = await query;
34
- if (error) {
35
- res.status(500).json({ success: false, error: error.message });
36
- return;
37
- }
38
- res.json({ success: true, data });
39
- }
40
- catch (error) {
41
- res.status(500).json({ success: false, error: 'Internal error' });
42
- }
43
- });
44
- // GET /email/templates/:id — Get single template with full body
45
- router.get('/email/templates/:id', async (req, res) => {
46
- try {
47
- const { data, error } = await supabase
48
- .from('email_templates')
49
- .select('*')
50
- .eq('id', req.params.id)
51
- .single();
52
- if (error || !data) {
53
- res.status(404).json({ success: false, error: 'Template not found' });
54
- return;
55
- }
56
- res.json({ success: true, data });
57
- }
58
- catch (error) {
59
- res.status(500).json({ success: false, error: 'Internal error' });
60
- }
61
- });
62
- // POST /email/templates — Create template
63
- router.post('/email/templates', async (req, res) => {
64
- try {
65
- const { slug, name, subject, body_html, body_text, variables, language, category, organization_id } = req.body;
66
- if (!slug || !name || !subject || !body_html) {
67
- res.status(400).json({ success: false, error: 'slug, name, subject, and body_html are required' });
68
- return;
69
- }
70
- const { data, error } = await supabase
71
- .from('email_templates')
72
- .insert({
73
- slug,
74
- name,
75
- subject,
76
- body_html,
77
- body_text: body_text || null,
78
- variables: variables || [],
79
- language: language || 'nl',
80
- category: category || 'transactional',
81
- organization_id: organization_id || null,
82
- is_active: true,
83
- })
84
- .select()
85
- .single();
86
- if (error) {
87
- res.status(400).json({ success: false, error: error.message });
88
- return;
89
- }
90
- emailService.clearCache(slug);
91
- res.status(201).json({ success: true, data });
92
- }
93
- catch (error) {
94
- res.status(500).json({ success: false, error: 'Internal error' });
95
- }
96
- });
97
- // PUT /email/templates/:id — Update template
98
- router.put('/email/templates/:id', async (req, res) => {
99
- try {
100
- const { slug, name, subject, body_html, body_text, variables, language, category, is_active } = req.body;
101
- const updates = { updated_at: new Date().toISOString() };
102
- if (slug !== undefined)
103
- updates.slug = slug;
104
- if (name !== undefined)
105
- updates.name = name;
106
- if (subject !== undefined)
107
- updates.subject = subject;
108
- if (body_html !== undefined)
109
- updates.body_html = body_html;
110
- if (body_text !== undefined)
111
- updates.body_text = body_text;
112
- if (variables !== undefined)
113
- updates.variables = variables;
114
- if (language !== undefined)
115
- updates.language = language;
116
- if (category !== undefined)
117
- updates.category = category;
118
- if (is_active !== undefined)
119
- updates.is_active = is_active;
120
- const { data, error } = await supabase
121
- .from('email_templates')
122
- .update(updates)
123
- .eq('id', req.params.id)
124
- .select()
125
- .single();
126
- if (error || !data) {
127
- res.status(404).json({ success: false, error: error?.message || 'Template not found' });
128
- return;
129
- }
130
- // Clear cache for both old and new slug
131
- emailService.clearCache(data.slug);
132
- if (slug && slug !== data.slug)
133
- emailService.clearCache(slug);
134
- res.json({ success: true, data });
135
- }
136
- catch (error) {
137
- res.status(500).json({ success: false, error: 'Internal error' });
138
- }
139
- });
140
- // DELETE /email/templates/:id — Soft delete (set is_active=false)
141
- router.delete('/email/templates/:id', async (req, res) => {
142
- try {
143
- const { data, error } = await supabase
144
- .from('email_templates')
145
- .update({ is_active: false, updated_at: new Date().toISOString() })
146
- .eq('id', req.params.id)
147
- .select('slug')
148
- .single();
149
- if (error || !data) {
150
- res.status(404).json({ success: false, error: 'Template not found' });
151
- return;
152
- }
153
- emailService.clearCache(data.slug);
154
- res.json({ success: true, message: 'Template deactivated' });
155
- }
156
- catch (error) {
157
- res.status(500).json({ success: false, error: 'Internal error' });
158
- }
159
- });
160
- // POST /email/templates/:id/test — Send test email
161
- router.post('/email/templates/:id/test', async (req, res) => {
162
- try {
163
- const { to } = req.body;
164
- if (!to) {
165
- res.status(400).json({ success: false, error: 'to is required' });
166
- return;
167
- }
168
- // Load template
169
- const { data: template, error } = await supabase
170
- .from('email_templates')
171
- .select('*')
172
- .eq('id', req.params.id)
173
- .single();
174
- if (error || !template) {
175
- res.status(404).json({ success: false, error: 'Template not found' });
176
- return;
177
- }
178
- // Create sample variables from template's variable list
179
- const variables = {};
180
- if (Array.isArray(template.variables)) {
181
- for (const v of template.variables) {
182
- const key = typeof v === 'string' ? v : v.key;
183
- if (key)
184
- variables[key] = `[${key}]`;
185
- }
186
- }
187
- const result = await emailService.send({
188
- to,
189
- templateSlug: template.slug,
190
- variables,
191
- language: template.language,
192
- organizationId: template.organization_id || undefined,
193
- emailType: 'test',
194
- });
195
- res.json({ success: result.success, logId: result.logId });
196
- }
197
- catch (error) {
198
- res.status(500).json({ success: false, error: 'Internal error' });
199
- }
200
- });
201
- // GET /email/logs — List email logs with pagination
202
- router.get('/email/logs', async (req, res) => {
203
- try {
204
- const limit = Math.min(parseInt(req.query.limit) || 50, 100);
205
- const offset = parseInt(req.query.offset) || 0;
206
- const orgId = config.getOrganizationId?.(req);
207
- let query = supabase
208
- .from('email_logs')
209
- .select('id, to_email, subject, status, email_type, template_slug, sent_at, delivered_at, opened_at, clicked_at, error_message, created_at', { count: 'exact' })
210
- .order('created_at', { ascending: false })
211
- .range(offset, offset + limit - 1);
212
- if (orgId) {
213
- query = query.eq('organization_id', orgId);
214
- }
215
- const { data, error, count } = await query;
216
- if (error) {
217
- res.status(500).json({ success: false, error: error.message });
218
- return;
219
- }
220
- res.json({ success: true, data, total: count, limit, offset });
221
- }
222
- catch (error) {
223
- res.status(500).json({ success: false, error: 'Internal error' });
224
- }
225
- });
226
- }
227
- //# sourceMappingURL=adminRoutes.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"adminRoutes.js","sourceRoot":"","sources":["../../../src/shared/email/adminRoutes.ts"],"names":[],"mappings":"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"}