@technomoron/mail-magic 1.0.40 → 1.0.41
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/CHANGES +23 -2
- package/README.md +5 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -1
- package/dist/esm/api/assets.d.ts +9 -0
- package/dist/esm/api/auth.d.ts +2 -0
- package/dist/esm/api/auth.js +17 -8
- package/dist/esm/api/forms.d.ts +9 -0
- package/dist/esm/api/forms.js +7 -4
- package/dist/esm/api/mailer.d.ts +11 -0
- package/dist/esm/api/mailer.js +4 -4
- package/dist/esm/bin/mail-magic.d.ts +2 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/models/db.d.ts +5 -0
- package/dist/esm/models/domain.d.ts +24 -0
- package/dist/esm/models/form.d.ts +50 -0
- package/dist/esm/models/form.js +16 -13
- package/dist/esm/models/init.d.ts +12 -0
- package/dist/esm/models/recipient.d.ts +24 -0
- package/dist/esm/models/txmail.d.ts +42 -0
- package/dist/esm/models/user.d.ts +33 -0
- package/dist/esm/server.d.ts +8 -0
- package/dist/esm/store/envloader.d.ts +188 -0
- package/dist/esm/store/envloader.js +9 -4
- package/dist/esm/store/store.d.ts +37 -0
- package/dist/esm/store/store.js +1 -1
- package/dist/esm/swagger.d.ts +10 -0
- package/dist/esm/types.d.ts +32 -0
- package/dist/esm/util/captcha.d.ts +7 -0
- package/dist/esm/util/captcha.js +4 -1
- package/dist/esm/util/email.d.ts +3 -0
- package/dist/esm/util/form-replyto.d.ts +6 -0
- package/dist/esm/util/form-submission.d.ts +24 -0
- package/dist/esm/util/forms.d.ts +140 -0
- package/dist/esm/util/forms.js +17 -29
- package/dist/esm/util/paths.d.ts +15 -0
- package/dist/esm/util/paths.js +17 -0
- package/dist/esm/util/ratelimit.d.ts +16 -0
- package/dist/esm/util/ratelimit.js +6 -1
- package/dist/esm/util/route.d.ts +1 -0
- package/dist/esm/util/shared-template-flatten.d.ts +17 -0
- package/dist/esm/util/uploads.d.ts +10 -0
- package/dist/esm/util/utils.d.ts +27 -0
- package/dist/esm/util.d.ts +7 -0
- package/docs/swagger/openapi.json +16 -12
- package/examples/.env-dist +21 -0
- package/examples/README.md +74 -0
- package/examples/data/example.test/form-template/base.njk +4 -0
- package/examples/data/example.test/form-template/en/base.njk +1 -0
- package/examples/data/example.test/form-template/en/change-password.njk +5 -0
- package/examples/data/example.test/form-template/en/confirm-account.njk +5 -0
- package/examples/data/example.test/form-template/en/contact.njk +5 -0
- package/examples/data/example.test/form-template/en/partials/fields.njk +5 -0
- package/examples/data/example.test/form-template/en/welcome-signup.njk +5 -0
- package/examples/data/example.test/form-template/nb/base.njk +1 -0
- package/examples/data/example.test/form-template/nb/change-password.njk +5 -0
- package/examples/data/example.test/form-template/nb/confirm-account.njk +5 -0
- package/examples/data/example.test/form-template/nb/contact.njk +5 -0
- package/examples/data/example.test/form-template/nb/partials/fields.njk +5 -0
- package/examples/data/example.test/form-template/nb/welcome-signup.njk +5 -0
- package/examples/data/example.test/form-template/partials/header.njk +1 -0
- package/examples/data/example.test/tx-template/base.njk +16 -0
- package/examples/data/example.test/tx-template/en/base.njk +1 -0
- package/examples/data/example.test/tx-template/en/change-password.njk +7 -0
- package/examples/data/example.test/tx-template/en/confirm.njk +6 -0
- package/examples/data/example.test/tx-template/en/invoice.njk +8 -0
- package/examples/data/example.test/tx-template/en/partials/header.njk +1 -0
- package/examples/data/example.test/tx-template/en/partials/line-items.njk +14 -0
- package/examples/data/example.test/tx-template/en/receipt.njk +7 -0
- package/examples/data/example.test/tx-template/en/welcome.njk +5 -0
- package/examples/data/example.test/tx-template/nb/base.njk +1 -0
- package/examples/data/example.test/tx-template/nb/change-password.njk +6 -0
- package/examples/data/example.test/tx-template/nb/confirm.njk +6 -0
- package/examples/data/example.test/tx-template/nb/invoice.njk +7 -0
- package/examples/data/example.test/tx-template/nb/partials/header.njk +1 -0
- package/examples/data/example.test/tx-template/nb/receipt.njk +6 -0
- package/examples/data/example.test/tx-template/nb/welcome.njk +5 -0
- package/examples/data/example.test/tx-template/partials/header.njk +7 -0
- package/examples/data/init-data.json +213 -0
- package/examples/scripts/mm-api.ts +206 -0
- package/examples/scripts/public-form.ts +100 -0
- package/examples/scripts/send-messages.ts +114 -0
- package/package.json +6 -4
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { UploadedFile } from '../types.js';
|
|
2
|
+
export declare function buildAttachments(rawFiles: UploadedFile[]): {
|
|
3
|
+
attachments: Array<{
|
|
4
|
+
filename: string;
|
|
5
|
+
path: string;
|
|
6
|
+
}>;
|
|
7
|
+
attachmentMap: Record<string, string>;
|
|
8
|
+
};
|
|
9
|
+
export declare function cleanupUploadedFiles(files: UploadedFile[]): Promise<void>;
|
|
10
|
+
export declare function moveUploadedFiles(files: UploadedFile[], targetDir: string): Promise<void>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { api_domain } from '../models/domain.js';
|
|
2
|
+
import { api_user } from '../models/user.js';
|
|
3
|
+
import type { RequestMeta } from '../types.js';
|
|
4
|
+
import type { Response } from 'express';
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a string into a safe identifier for slugs, filenames, etc.
|
|
7
|
+
*
|
|
8
|
+
* - Lowercases all characters
|
|
9
|
+
* - Replaces any character that is not `a-z`, `0-9`, `-`, '.' or `_` with `-`
|
|
10
|
+
* - Collapses multiple consecutive dashes into one
|
|
11
|
+
* - Trims leading and trailing dashes
|
|
12
|
+
*
|
|
13
|
+
* Examples:
|
|
14
|
+
* normalizeSlug("Hello World!") -> "hello-world"
|
|
15
|
+
* normalizeSlug(" Áccêntš ") -> "cc-nt"
|
|
16
|
+
* normalizeSlug("My--Slug__Test") -> "my-slug__test"
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeSlug(input: string): string;
|
|
19
|
+
export declare function user_and_domain(domain_id: number): Promise<{
|
|
20
|
+
user: api_user;
|
|
21
|
+
domain: api_domain;
|
|
22
|
+
}>;
|
|
23
|
+
export declare function buildRequestMeta(rawReq: unknown): RequestMeta;
|
|
24
|
+
export declare function decodeComponent(value: string | string[] | undefined): string;
|
|
25
|
+
export declare function getBodyValue(body: Record<string, unknown>, ...keys: string[]): string;
|
|
26
|
+
export declare function normalizeBoolean(value: unknown): boolean;
|
|
27
|
+
export declare function sendFileAsync(res: Pick<Response, 'sendFile'>, file: string, options?: Parameters<Response['sendFile']>[1]): Promise<void>;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Mail Magic API",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.41",
|
|
6
6
|
"description": "OpenAPI definition for the Mail Magic server. Authenticated endpoints require an API key provided as `Authorization: Bearer apikey-<token>`."
|
|
7
7
|
},
|
|
8
8
|
"servers": [
|
|
@@ -897,7 +897,7 @@
|
|
|
897
897
|
"properties": {
|
|
898
898
|
"domain": {
|
|
899
899
|
"type": "string",
|
|
900
|
-
"description": "Domain name
|
|
900
|
+
"description": "Optional. Domain name. If omitted, the API key's default domain is used."
|
|
901
901
|
},
|
|
902
902
|
"name": {
|
|
903
903
|
"type": "string",
|
|
@@ -920,13 +920,14 @@
|
|
|
920
920
|
"default": ""
|
|
921
921
|
}
|
|
922
922
|
},
|
|
923
|
-
"required": ["
|
|
923
|
+
"required": ["name", "template"]
|
|
924
924
|
},
|
|
925
925
|
"TxSendRequest": {
|
|
926
926
|
"type": "object",
|
|
927
927
|
"properties": {
|
|
928
928
|
"domain": {
|
|
929
|
-
"type": "string"
|
|
929
|
+
"type": "string",
|
|
930
|
+
"description": "Optional. Domain name. If omitted, the API key's default domain is used."
|
|
930
931
|
},
|
|
931
932
|
"name": {
|
|
932
933
|
"type": "string",
|
|
@@ -969,14 +970,15 @@
|
|
|
969
970
|
"description": "Custom email headers."
|
|
970
971
|
}
|
|
971
972
|
},
|
|
972
|
-
"required": ["
|
|
973
|
+
"required": ["name", "rcpt"]
|
|
973
974
|
},
|
|
974
975
|
"TxSendMultipartRequest": {
|
|
975
976
|
"type": "object",
|
|
976
977
|
"description": "Multipart version of TxSendRequest. Attach files in any multipart field; `files` is a conventional field name.",
|
|
977
978
|
"properties": {
|
|
978
979
|
"domain": {
|
|
979
|
-
"type": "string"
|
|
980
|
+
"type": "string",
|
|
981
|
+
"description": "Optional. Domain name. If omitted, the API key's default domain is used."
|
|
980
982
|
},
|
|
981
983
|
"name": {
|
|
982
984
|
"type": "string"
|
|
@@ -1012,7 +1014,7 @@
|
|
|
1012
1014
|
}
|
|
1013
1015
|
}
|
|
1014
1016
|
},
|
|
1015
|
-
"required": ["
|
|
1017
|
+
"required": ["name", "rcpt"]
|
|
1016
1018
|
},
|
|
1017
1019
|
"TxSendResponseData": {
|
|
1018
1020
|
"type": "object",
|
|
@@ -1031,7 +1033,8 @@
|
|
|
1031
1033
|
"type": "object",
|
|
1032
1034
|
"properties": {
|
|
1033
1035
|
"domain": {
|
|
1034
|
-
"type": "string"
|
|
1036
|
+
"type": "string",
|
|
1037
|
+
"description": "Optional. Domain name. If omitted, the API key's default domain is used."
|
|
1035
1038
|
},
|
|
1036
1039
|
"idname": {
|
|
1037
1040
|
"type": "string",
|
|
@@ -1053,7 +1056,7 @@
|
|
|
1053
1056
|
"type": "string"
|
|
1054
1057
|
}
|
|
1055
1058
|
},
|
|
1056
|
-
"required": ["
|
|
1059
|
+
"required": ["idname", "email"]
|
|
1057
1060
|
},
|
|
1058
1061
|
"FormRecipientUpsertResponseData": {
|
|
1059
1062
|
"type": "object",
|
|
@@ -1075,7 +1078,8 @@
|
|
|
1075
1078
|
"type": "object",
|
|
1076
1079
|
"properties": {
|
|
1077
1080
|
"domain": {
|
|
1078
|
-
"type": "string"
|
|
1081
|
+
"type": "string",
|
|
1082
|
+
"description": "Optional. Domain name. If omitted, the API key's default domain is used."
|
|
1079
1083
|
},
|
|
1080
1084
|
"idname": {
|
|
1081
1085
|
"type": "string",
|
|
@@ -1131,7 +1135,7 @@
|
|
|
1131
1135
|
"type": "boolean"
|
|
1132
1136
|
}
|
|
1133
1137
|
},
|
|
1134
|
-
"required": ["
|
|
1138
|
+
"required": ["idname", "template", "sender", "recipient"]
|
|
1135
1139
|
},
|
|
1136
1140
|
"FormTemplateUpsertResponseData": {
|
|
1137
1141
|
"type": "object",
|
|
@@ -1262,7 +1266,7 @@
|
|
|
1262
1266
|
},
|
|
1263
1267
|
"FormMessageResponseData": {
|
|
1264
1268
|
"type": "object",
|
|
1265
|
-
"description": "On success, data is
|
|
1269
|
+
"description": "On success, data is an empty object.",
|
|
1266
1270
|
"additionalProperties": true
|
|
1267
1271
|
},
|
|
1268
1272
|
"AssetsUploadMultipartRequest": {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
NODE_ENV=development
|
|
2
|
+
API_PORT=3776
|
|
3
|
+
API_HOST=127.0.0.1
|
|
4
|
+
API_URL=http://127.0.0.1:3776/api
|
|
5
|
+
API_BASE_PATH=/api
|
|
6
|
+
ASSET_ROUTE=/asset
|
|
7
|
+
CONFIG_PATH=./data
|
|
8
|
+
DB_TYPE=sqlite
|
|
9
|
+
DB_NAME=./maildata.db
|
|
10
|
+
DB_FORCE_SYNC=true
|
|
11
|
+
DB_AUTO_RELOAD=false
|
|
12
|
+
DB_SYNC_ALTER=false
|
|
13
|
+
DB_LOG=false
|
|
14
|
+
DEBUG=false
|
|
15
|
+
AUTOESCAPE_HTML=true
|
|
16
|
+
UPLOAD_PATH=./{domain}/uploads
|
|
17
|
+
SMTP_HOST=127.0.0.1
|
|
18
|
+
SMTP_PORT=1025
|
|
19
|
+
SMTP_SECURE=false
|
|
20
|
+
SMTP_TLS_REJECT=false
|
|
21
|
+
API_TOKEN_PEPPER=example-token-pepper-value
|
|
@@ -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
|
|
4
|
+
accessible 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 @@
|
|
|
1
|
+
{% extends '../base.njk' %}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{% extends '../base.njk' %}
|
|
@@ -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,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 @@
|
|
|
1
|
+
{% extends '../base.njk' %}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{% include '../../partials/header.njk' %}
|