feat(engagement): templates — the email block family, renderer and seeded set (engagement Phase 5a)
Every subject and body moves out of `mailer.js` into `engagement_templates` rows an operator can edit. A relocation, not a regression: nothing that sends mail today starts depending on an operator authoring something first. - `email.*` block family in its own registry, sharing the page family's envelope walk and validate-then-sanitize order by binding rather than by copy. - A server-side renderer producing both parts of a multipart message; the text part is byte-identical to the literals this commit deletes. - Nine seeded templates, six of them wired now; the seeder's `customized = 0` guard lives in the UPDATE's own WHERE. - `renderByKey` falls back to the shipped seed when a row is missing or unusable, so no failure of the table can stop a password reset. Also fixes `check:hosts` reading the template key `auth.email-verify` as the hostname `auth.email`. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -43,8 +43,15 @@ const CODE = new Set(['.js', '.jsx', '.mjs', '.cjs'])
|
||||
// A URL, or a bare dotted hostname with a real TLD. The TLD length floor is what
|
||||
// keeps `emailConfig.model` and `foo.js` out of it — a two-plus-letter final
|
||||
// label after at least one dot, with no path characters, is a host.
|
||||
// The `(?![-\w])` after the TLD is not redundant with `\b`: `\b` matches between
|
||||
// `l` and `-`, so `auth.email-verify` — an engagement TEMPLATE KEY, and one the
|
||||
// plan names (§4.6.1) — was read as the host `auth.email` with a stray suffix.
|
||||
// A real hostname's TLD is the last label, so a `-` or a word character following
|
||||
// it means the match is a truncation of a longer identifier rather than a
|
||||
// destination. Everything a host IS followed by (a quote, `/`, `:`, `?`) still
|
||||
// matches.
|
||||
const URL_LITERAL = /\b(?:https?|smtps?):\/\/[^\s'"`]+/
|
||||
const HOSTNAME_LITERAL = /\b(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:com|net|org|io|dev|co|email|mail|cloud|app|us|eu)\b/i
|
||||
const HOSTNAME_LITERAL = /\b(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:com|net|org|io|dev|co|email|mail|cloud|app|us|eu)(?![-\w])/i
|
||||
|
||||
// Hosts that are not destinations: the loopback family, and the RFC 2606 names
|
||||
// reserved for documentation. A placeholder in an admin form's help text is the
|
||||
|
||||
Reference in New Issue
Block a user