@technomoron/mail-magic 1.0.40 → 1.0.42

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 (87) hide show
  1. package/CHANGES +45 -2
  2. package/README.md +5 -0
  3. package/dist/cjs/index.d.ts +1 -0
  4. package/dist/cjs/index.js +1 -1
  5. package/dist/esm/api/assets.d.ts +11 -0
  6. package/dist/esm/api/assets.js +48 -18
  7. package/dist/esm/api/auth.d.ts +2 -0
  8. package/dist/esm/api/auth.js +18 -9
  9. package/dist/esm/api/forms.d.ts +9 -0
  10. package/dist/esm/api/forms.js +42 -7
  11. package/dist/esm/api/mailer.d.ts +11 -0
  12. package/dist/esm/api/mailer.js +37 -8
  13. package/dist/esm/bin/mail-magic.d.ts +2 -0
  14. package/dist/esm/index.d.ts +12 -0
  15. package/dist/esm/index.js +5 -4
  16. package/dist/esm/models/db.d.ts +5 -0
  17. package/dist/esm/models/domain.d.ts +24 -0
  18. package/dist/esm/models/form.d.ts +50 -0
  19. package/dist/esm/models/form.js +16 -13
  20. package/dist/esm/models/init.d.ts +12 -0
  21. package/dist/esm/models/recipient.d.ts +24 -0
  22. package/dist/esm/models/txmail.d.ts +42 -0
  23. package/dist/esm/models/user.d.ts +33 -0
  24. package/dist/esm/server.d.ts +8 -0
  25. package/dist/esm/store/envloader.d.ts +188 -0
  26. package/dist/esm/store/envloader.js +9 -4
  27. package/dist/esm/store/store.d.ts +38 -0
  28. package/dist/esm/store/store.js +20 -16
  29. package/dist/esm/swagger.d.ts +10 -0
  30. package/dist/esm/types.d.ts +36 -0
  31. package/dist/esm/util/captcha.d.ts +7 -0
  32. package/dist/esm/util/captcha.js +4 -1
  33. package/dist/esm/util/email.d.ts +3 -0
  34. package/dist/esm/util/form-replyto.d.ts +6 -0
  35. package/dist/esm/util/form-submission.d.ts +24 -0
  36. package/dist/esm/util/forms.d.ts +140 -0
  37. package/dist/esm/util/forms.js +42 -39
  38. package/dist/esm/util/paths.d.ts +15 -0
  39. package/dist/esm/util/paths.js +17 -0
  40. package/dist/esm/util/ratelimit.d.ts +7 -0
  41. package/dist/esm/util/ratelimit.js +10 -41
  42. package/dist/esm/util/route.d.ts +1 -0
  43. package/dist/esm/util/shared-template-flatten.d.ts +17 -0
  44. package/dist/esm/util/uploads.d.ts +11 -0
  45. package/dist/esm/util/uploads.js +16 -11
  46. package/dist/esm/util/utils.d.ts +25 -0
  47. package/dist/esm/util/utils.js +0 -18
  48. package/dist/esm/util.d.ts +7 -0
  49. package/docs/swagger/openapi.json +16 -12
  50. package/examples/.env-dist +21 -0
  51. package/examples/README.md +74 -0
  52. package/examples/data/example.test/form-template/base.njk +4 -0
  53. package/examples/data/example.test/form-template/en/base.njk +1 -0
  54. package/examples/data/example.test/form-template/en/change-password.njk +5 -0
  55. package/examples/data/example.test/form-template/en/confirm-account.njk +5 -0
  56. package/examples/data/example.test/form-template/en/contact.njk +5 -0
  57. package/examples/data/example.test/form-template/en/partials/fields.njk +5 -0
  58. package/examples/data/example.test/form-template/en/welcome-signup.njk +5 -0
  59. package/examples/data/example.test/form-template/nb/base.njk +1 -0
  60. package/examples/data/example.test/form-template/nb/change-password.njk +5 -0
  61. package/examples/data/example.test/form-template/nb/confirm-account.njk +5 -0
  62. package/examples/data/example.test/form-template/nb/contact.njk +5 -0
  63. package/examples/data/example.test/form-template/nb/partials/fields.njk +5 -0
  64. package/examples/data/example.test/form-template/nb/welcome-signup.njk +5 -0
  65. package/examples/data/example.test/form-template/partials/header.njk +1 -0
  66. package/examples/data/example.test/tx-template/base.njk +16 -0
  67. package/examples/data/example.test/tx-template/en/base.njk +1 -0
  68. package/examples/data/example.test/tx-template/en/change-password.njk +7 -0
  69. package/examples/data/example.test/tx-template/en/confirm.njk +6 -0
  70. package/examples/data/example.test/tx-template/en/invoice.njk +8 -0
  71. package/examples/data/example.test/tx-template/en/partials/header.njk +1 -0
  72. package/examples/data/example.test/tx-template/en/partials/line-items.njk +14 -0
  73. package/examples/data/example.test/tx-template/en/receipt.njk +7 -0
  74. package/examples/data/example.test/tx-template/en/welcome.njk +5 -0
  75. package/examples/data/example.test/tx-template/nb/base.njk +1 -0
  76. package/examples/data/example.test/tx-template/nb/change-password.njk +6 -0
  77. package/examples/data/example.test/tx-template/nb/confirm.njk +6 -0
  78. package/examples/data/example.test/tx-template/nb/invoice.njk +7 -0
  79. package/examples/data/example.test/tx-template/nb/partials/header.njk +1 -0
  80. package/examples/data/example.test/tx-template/nb/receipt.njk +6 -0
  81. package/examples/data/example.test/tx-template/nb/welcome.njk +5 -0
  82. package/examples/data/example.test/tx-template/partials/header.njk +7 -0
  83. package/examples/data/init-data.json +213 -0
  84. package/examples/scripts/mm-api.ts +206 -0
  85. package/examples/scripts/public-form.ts +100 -0
  86. package/examples/scripts/send-messages.ts +114 -0
  87. package/package.json +7 -5
@@ -0,0 +1,74 @@
1
+ # Mail Magic Examples
2
+
3
+ The canonical example setup for Mail Magic. Shipped inside the `@technomoron/mail-magic` package so it is accessible
4
+ without a private repo clone:
5
+
6
+ ```bash
7
+ ls node_modules/@technomoron/mail-magic/examples/
8
+ ```
9
+
10
+ It contains:
11
+
12
+ - `data/` — runnable server config: `init-data.json` + `example.test/` domain with transactional and form templates
13
+ - `scripts/` — helper TypeScript scripts for sending messages and testing the API
14
+ - `.env-dist` — example environment file for local development
15
+
16
+ Template families: welcome, confirm, change-password, receipt, invoice (tx) and contact, welcome-signup,
17
+ confirm-account, change-password (form). Locale variants: `en`, `nb`.
18
+
19
+ ## Run The Example Server
20
+
21
+ From repo root:
22
+
23
+ ```bash
24
+ pnpm examples
25
+ ```
26
+
27
+ This runs the `mail-magic` server bin with:
28
+
29
+ - env file: `packages/server/examples/.env-dist`
30
+ - config dir: `packages/server/examples/data`
31
+ - default API host: `http://127.0.0.1:3776`
32
+
33
+ ## Default Demo Credentials
34
+
35
+ Defined in `data/init-data.json`:
36
+
37
+ - domain: `example.test`
38
+ - token: `example-token`
39
+
40
+ ```text
41
+ Authorization: Bearer apikey-example-token
42
+ ```
43
+
44
+ ## Validate Templates With `mm-cli compile`
45
+
46
+ From repo root:
47
+
48
+ ```bash
49
+ node packages/cli/dist/cli.js compile \
50
+ --input ./packages/server/examples/data \
51
+ --output /tmp/mm-compiled-examples \
52
+ --domain example.test
53
+ ```
54
+
55
+ ## Helper Scripts
56
+
57
+ From repo root (requires `tsx`):
58
+
59
+ ```bash
60
+ tsx packages/server/examples/scripts/send-messages.ts
61
+ tsx packages/server/examples/scripts/public-form.ts
62
+ tsx packages/server/examples/scripts/mm-api.ts template \
63
+ --file packages/server/examples/data/example.test/tx-template/en/welcome.njk \
64
+ --name welcome --domain example.test
65
+ ```
66
+
67
+ ## Production Adaptation
68
+
69
+ Copy the `data/` directory and `.env-dist` to your project and adapt:
70
+
71
+ 1. `.env-dist` → `.env`: set `API_TOKEN_PEPPER`, SMTP settings, `API_URL`, DB path.
72
+ 2. `data/init-data.json`: users, domains, tokens, sender/recipient addresses.
73
+ 3. `data/<your-domain>/tx-template/*`: brand, content, locale text, assets.
74
+ 4. `data/<your-domain>/form-template/*`: subjects, recipient routing, exposed fields.
@@ -0,0 +1,4 @@
1
+ {% include 'partials/header.njk' %}
2
+ <h1>{{ title or 'Form message' }}</h1>
3
+ {% block content %}{% endblock %}
4
+ <p style="font-size: 12px; color: #666;">Generated at {{ _meta_.ts or '' }}</p>
@@ -0,0 +1 @@
1
+ {% extends '../base.njk' %}
@@ -0,0 +1,5 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <p>Password change form submission.</p>
4
+ {% include 'partials/fields.njk' %}
5
+ {% endblock %}
@@ -0,0 +1,5 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <p>New account confirmation request.</p>
4
+ {% include 'partials/fields.njk' %}
5
+ {% endblock %}
@@ -0,0 +1,5 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <p>New contact submission.</p>
4
+ {% include 'partials/fields.njk' %}
5
+ {% endblock %}
@@ -0,0 +1,5 @@
1
+ <ul>
2
+ {% for key, value in _fields_ %}
3
+ <li><strong>{{ key }}</strong>: {{ value }}</li>
4
+ {% endfor %}
5
+ </ul>
@@ -0,0 +1,5 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <p>New welcome signup.</p>
4
+ {% include 'partials/fields.njk' %}
5
+ {% endblock %}
@@ -0,0 +1 @@
1
+ {% extends '../base.njk' %}
@@ -0,0 +1,5 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <p>Skjema for passordendring.</p>
4
+ {% include 'partials/fields.njk' %}
5
+ {% endblock %}
@@ -0,0 +1,5 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <p>Ny bekreft-konto foresporsel.</p>
4
+ {% include 'partials/fields.njk' %}
5
+ {% endblock %}
@@ -0,0 +1,5 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <p>Ny kontakthenvendelse.</p>
4
+ {% include 'partials/fields.njk' %}
5
+ {% endblock %}
@@ -0,0 +1,5 @@
1
+ <ul>
2
+ {% for key, value in _fields_ %}
3
+ <li><strong>{{ key }}</strong>: {{ value }}</li>
4
+ {% endfor %}
5
+ </ul>
@@ -0,0 +1,5 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <p>Ny registrering.</p>
4
+ {% include 'partials/fields.njk' %}
5
+ {% endblock %}
@@ -0,0 +1 @@
1
+ <p><strong>{{ brand_name or 'Mail Magic Forms' }}</strong></p>
@@ -0,0 +1,16 @@
1
+ <!doctype html>
2
+ <html lang="{{ locale or 'en' }}">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <title>{{ subject or 'Mail Magic' }}</title>
7
+ </head>
8
+ <body style="font-family: Arial, Helvetica, sans-serif; color: #102030;">
9
+ {% include 'partials/header.njk' %}
10
+ <main>
11
+ {% block content %}{% endblock %}
12
+ </main>
13
+ <hr />
14
+ <p style="font-size: 12px; color: #666;">{{ company_name or 'Example Inc.' }} - {{ support_email or 'support@example.test' }}</p>
15
+ </body>
16
+ </html>
@@ -0,0 +1 @@
1
+ {% extends '../base.njk' %}
@@ -0,0 +1,7 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <h1>Change your password</h1>
4
+ <p>Use this secure link to set a new password:</p>
5
+ <p><a href="{{ reset_url }}">Reset password</a></p>
6
+ <p style="font-size: 12px; color: #666;">Link expires in {{ expires_minutes or 30 }} minutes.</p>
7
+ {% endblock %}
@@ -0,0 +1,6 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <h1>Confirm your email</h1>
4
+ <p>Hi {{ name }}, click below to confirm your account.</p>
5
+ <p><a href="{{ confirm_url }}">Confirm account</a></p>
6
+ {% endblock %}
@@ -0,0 +1,8 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <h1>Invoice {{ invoice_no }}</h1>
4
+ <p>Hello {{ name }}, here is your invoice summary.</p>
5
+ {% include 'partials/line-items.njk' %}
6
+ <p><strong>Total:</strong> {{ currency or 'USD' }} {{ total }}</p>
7
+ <p>Due: {{ due_date }}</p>
8
+ {% endblock %}
@@ -0,0 +1 @@
1
+ {% include '../../partials/header.njk' %}
@@ -0,0 +1,14 @@
1
+ <table role="presentation" width="100%" cellpadding="6" cellspacing="0" style="border-collapse: collapse; margin-top: 12px;">
2
+ <tr style="background: #f5f7fb;">
3
+ <th align="left">Item</th>
4
+ <th align="right">Qty</th>
5
+ <th align="right">Price</th>
6
+ </tr>
7
+ {% for item in items or [] %}
8
+ <tr>
9
+ <td>{{ item.name }}</td>
10
+ <td align="right">{{ item.qty }}</td>
11
+ <td align="right">{{ currency or 'USD' }} {{ item.price }}</td>
12
+ </tr>
13
+ {% endfor %}
14
+ </table>
@@ -0,0 +1,7 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <h1>Payment receipt {{ receipt_no }}</h1>
4
+ <p>Thanks for your payment, {{ name }}.</p>
5
+ <p>Amount: {{ currency or 'USD' }} {{ amount }}</p>
6
+ <p>Date: {{ paid_at }}</p>
7
+ {% endblock %}
@@ -0,0 +1,5 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <h1>Welcome, {{ name }}!</h1>
4
+ <p>Your account is ready. You can now sign in and start using {{ brand_name or 'Mail Magic' }}.</p>
5
+ {% endblock %}
@@ -0,0 +1 @@
1
+ {% extends '../base.njk' %}
@@ -0,0 +1,6 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <h1>Endre passord</h1>
4
+ <p>Bruk lenken under for a sette nytt passord:</p>
5
+ <p><a href="{{ reset_url }}">Nullstill passord</a></p>
6
+ {% endblock %}
@@ -0,0 +1,6 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <h1>Bekreft e-post</h1>
4
+ <p>Hei {{ name }}, klikk under for a bekrefte kontoen din.</p>
5
+ <p><a href="{{ confirm_url }}">Bekreft konto</a></p>
6
+ {% endblock %}
@@ -0,0 +1,7 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <h1>Faktura {{ invoice_no }}</h1>
4
+ <p>Hei {{ name }}, her er fakturaen din.</p>
5
+ <p><strong>Sum:</strong> {{ currency or 'NOK' }} {{ total }}</p>
6
+ <p>Forfall: {{ due_date }}</p>
7
+ {% endblock %}
@@ -0,0 +1 @@
1
+ {% include '../../partials/header.njk' %}
@@ -0,0 +1,6 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <h1>Kvittering {{ receipt_no }}</h1>
4
+ <p>Takk for betalingen, {{ name }}.</p>
5
+ <p>Belop: {{ currency or 'NOK' }} {{ amount }}</p>
6
+ {% endblock %}
@@ -0,0 +1,5 @@
1
+ {% extends 'base.njk' %}
2
+ {% block content %}
3
+ <h1>Velkommen, {{ name }}!</h1>
4
+ <p>Kontoen din er klar, og du kan begynne med en gang.</p>
5
+ {% endblock %}
@@ -0,0 +1,7 @@
1
+ <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin-bottom: 24px;">
2
+ <tr>
3
+ <td style="padding: 12px 0; border-bottom: 2px solid #dbe2ea;">
4
+ <strong>{{ brand_name or 'Mail Magic' }}</strong>
5
+ </td>
6
+ </tr>
7
+ </table>
@@ -0,0 +1,213 @@
1
+ {
2
+ "user": [
3
+ {
4
+ "user_id": 1,
5
+ "idname": "example",
6
+ "token": "example-token",
7
+ "name": "Example User",
8
+ "email": "noreply@example.test",
9
+ "domain": 1,
10
+ "locale": "en"
11
+ }
12
+ ],
13
+ "domain": [
14
+ {
15
+ "domain_id": 1,
16
+ "user_id": 1,
17
+ "name": "example.test",
18
+ "sender": "Example <noreply@example.test>",
19
+ "locale": "en",
20
+ "is_default": true
21
+ }
22
+ ],
23
+ "template": [
24
+ {
25
+ "template_id": 1,
26
+ "user_id": 1,
27
+ "domain_id": 1,
28
+ "name": "welcome",
29
+ "locale": "en",
30
+ "sender": "Example <noreply@example.test>",
31
+ "subject": "Welcome"
32
+ },
33
+ {
34
+ "template_id": 2,
35
+ "user_id": 1,
36
+ "domain_id": 1,
37
+ "name": "confirm",
38
+ "locale": "en",
39
+ "sender": "Example <noreply@example.test>",
40
+ "subject": "Confirm your account"
41
+ },
42
+ {
43
+ "template_id": 3,
44
+ "user_id": 1,
45
+ "domain_id": 1,
46
+ "name": "change-password",
47
+ "locale": "en",
48
+ "sender": "Example <noreply@example.test>",
49
+ "subject": "Change password"
50
+ },
51
+ {
52
+ "template_id": 4,
53
+ "user_id": 1,
54
+ "domain_id": 1,
55
+ "name": "receipt",
56
+ "locale": "en",
57
+ "sender": "Example <noreply@example.test>",
58
+ "subject": "Your receipt"
59
+ },
60
+ {
61
+ "template_id": 5,
62
+ "user_id": 1,
63
+ "domain_id": 1,
64
+ "name": "invoice",
65
+ "locale": "en",
66
+ "sender": "Example <noreply@example.test>",
67
+ "subject": "Your invoice"
68
+ },
69
+ {
70
+ "template_id": 6,
71
+ "user_id": 1,
72
+ "domain_id": 1,
73
+ "name": "welcome",
74
+ "locale": "nb",
75
+ "sender": "Example <noreply@example.test>",
76
+ "subject": "Velkommen"
77
+ },
78
+ {
79
+ "template_id": 7,
80
+ "user_id": 1,
81
+ "domain_id": 1,
82
+ "name": "confirm",
83
+ "locale": "nb",
84
+ "sender": "Example <noreply@example.test>",
85
+ "subject": "Bekreft konto"
86
+ },
87
+ {
88
+ "template_id": 8,
89
+ "user_id": 1,
90
+ "domain_id": 1,
91
+ "name": "change-password",
92
+ "locale": "nb",
93
+ "sender": "Example <noreply@example.test>",
94
+ "subject": "Endre passord"
95
+ },
96
+ {
97
+ "template_id": 9,
98
+ "user_id": 1,
99
+ "domain_id": 1,
100
+ "name": "receipt",
101
+ "locale": "nb",
102
+ "sender": "Example <noreply@example.test>",
103
+ "subject": "Kvittering"
104
+ },
105
+ {
106
+ "template_id": 10,
107
+ "user_id": 1,
108
+ "domain_id": 1,
109
+ "name": "invoice",
110
+ "locale": "nb",
111
+ "sender": "Example <noreply@example.test>",
112
+ "subject": "Faktura"
113
+ }
114
+ ],
115
+ "form": [
116
+ {
117
+ "form_id": 1,
118
+ "form_key": "example-contact-en",
119
+ "user_id": 1,
120
+ "domain_id": 1,
121
+ "idname": "contact",
122
+ "locale": "en",
123
+ "sender": "Example Forms <forms@example.test>",
124
+ "recipient": "owner@example.test",
125
+ "subject": "Contact form",
126
+ "secret": "form-secret"
127
+ },
128
+ {
129
+ "form_id": 2,
130
+ "form_key": "example-welcome-signup-en",
131
+ "user_id": 1,
132
+ "domain_id": 1,
133
+ "idname": "welcome-signup",
134
+ "locale": "en",
135
+ "sender": "Example Forms <forms@example.test>",
136
+ "recipient": "owner@example.test",
137
+ "subject": "Welcome signup",
138
+ "secret": "form-secret"
139
+ },
140
+ {
141
+ "form_id": 3,
142
+ "form_key": "example-confirm-account-en",
143
+ "user_id": 1,
144
+ "domain_id": 1,
145
+ "idname": "confirm-account",
146
+ "locale": "en",
147
+ "sender": "Example Forms <forms@example.test>",
148
+ "recipient": "owner@example.test",
149
+ "subject": "Confirm account form",
150
+ "secret": "form-secret"
151
+ },
152
+ {
153
+ "form_id": 4,
154
+ "form_key": "example-change-password-en",
155
+ "user_id": 1,
156
+ "domain_id": 1,
157
+ "idname": "change-password",
158
+ "locale": "en",
159
+ "sender": "Example Forms <forms@example.test>",
160
+ "recipient": "owner@example.test",
161
+ "subject": "Change password form",
162
+ "secret": "form-secret"
163
+ },
164
+ {
165
+ "form_id": 5,
166
+ "form_key": "example-contact-nb",
167
+ "user_id": 1,
168
+ "domain_id": 1,
169
+ "idname": "contact",
170
+ "locale": "nb",
171
+ "sender": "Example Forms <forms@example.test>",
172
+ "recipient": "owner@example.test",
173
+ "subject": "Kontakt",
174
+ "secret": "form-secret"
175
+ },
176
+ {
177
+ "form_id": 6,
178
+ "form_key": "example-welcome-signup-nb",
179
+ "user_id": 1,
180
+ "domain_id": 1,
181
+ "idname": "welcome-signup",
182
+ "locale": "nb",
183
+ "sender": "Example Forms <forms@example.test>",
184
+ "recipient": "owner@example.test",
185
+ "subject": "Registrering",
186
+ "secret": "form-secret"
187
+ },
188
+ {
189
+ "form_id": 7,
190
+ "form_key": "example-confirm-account-nb",
191
+ "user_id": 1,
192
+ "domain_id": 1,
193
+ "idname": "confirm-account",
194
+ "locale": "nb",
195
+ "sender": "Example Forms <forms@example.test>",
196
+ "recipient": "owner@example.test",
197
+ "subject": "Bekreft konto form",
198
+ "secret": "form-secret"
199
+ },
200
+ {
201
+ "form_id": 8,
202
+ "form_key": "example-change-password-nb",
203
+ "user_id": 1,
204
+ "domain_id": 1,
205
+ "idname": "change-password",
206
+ "locale": "nb",
207
+ "sender": "Example Forms <forms@example.test>",
208
+ "recipient": "owner@example.test",
209
+ "subject": "Endre passord form",
210
+ "secret": "form-secret"
211
+ }
212
+ ]
213
+ }