@technomoron/mail-magic 1.0.9 → 1.0.11

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 (44) hide show
  1. package/CHANGES +19 -0
  2. package/README.md +5 -0
  3. package/dist/api/assets.js +153 -0
  4. package/dist/api/forms.js +2 -0
  5. package/dist/api/mailer.js +1 -0
  6. package/dist/bin/mail-magic.js +63 -0
  7. package/dist/index.js +61 -2
  8. package/dist/store/envloader.js +3 -3
  9. package/dist/store/store.js +66 -1
  10. package/package.json +17 -3
  11. package/.do-realease.sh +0 -54
  12. package/.editorconfig +0 -9
  13. package/.env-dist +0 -71
  14. package/.prettierrc +0 -14
  15. package/.vscode/extensions.json +0 -3
  16. package/.vscode/settings.json +0 -22
  17. package/config-example/form-template/default.njk +0 -102
  18. package/config-example/forms.config.json +0 -8
  19. package/config-example/init-data.json +0 -33
  20. package/config-example/tx-template/default.njk +0 -107
  21. package/ecosystem.config.cjs +0 -42
  22. package/eslint.config.mjs +0 -196
  23. package/lintconfig.cjs +0 -81
  24. package/src/api/assets.ts +0 -92
  25. package/src/api/forms.ts +0 -239
  26. package/src/api/mailer.ts +0 -270
  27. package/src/index.ts +0 -71
  28. package/src/models/db.ts +0 -112
  29. package/src/models/domain.ts +0 -72
  30. package/src/models/form.ts +0 -209
  31. package/src/models/init.ts +0 -240
  32. package/src/models/txmail.ts +0 -206
  33. package/src/models/user.ts +0 -79
  34. package/src/server.ts +0 -27
  35. package/src/store/envloader.ts +0 -109
  36. package/src/store/store.ts +0 -195
  37. package/src/types.ts +0 -39
  38. package/src/util.ts +0 -137
  39. package/tests/fixtures/certs/test.crt +0 -19
  40. package/tests/fixtures/certs/test.key +0 -28
  41. package/tests/helpers/test-setup.ts +0 -317
  42. package/tests/mail-magic.test.ts +0 -171
  43. package/tsconfig.json +0 -14
  44. package/vitest.config.ts +0 -11
package/.prettierrc DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "tabWidth": 4,
3
- "useTabs": true,
4
- "singleQuote": true,
5
- "semi": true,
6
- "trailingComma": "none",
7
- "bracketSpacing": true,
8
- "bracketSameLine": true,
9
- "arrowParens": "always",
10
- "printWidth": 120,
11
- "vueIndentScriptAndStyle": false,
12
- "htmlWhitespaceSensitivity": "ignore",
13
- "singleAttributePerLine": false
14
- }
@@ -1,3 +0,0 @@
1
- {
2
- "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
3
- }
@@ -1,22 +0,0 @@
1
- {
2
- "editor.formatOnSave": true,
3
- "editor.defaultFormatter": "esbenp.prettier-vscode",
4
- "[javascript]": {
5
- "editor.defaultFormatter": "esbenp.prettier-vscode"
6
- },
7
- "[typescript]": {
8
- "editor.defaultFormatter": "esbenp.prettier-vscode"
9
- },
10
- "[vue]": {
11
- "editor.defaultFormatter": "esbenp.prettier-vscode"
12
- },
13
- "[json]": {
14
- "editor.defaultFormatter": "esbenp.prettier-vscode"
15
- },
16
- "editor.codeActionsOnSave": {
17
- "source.fixAll.eslint": "explicit"
18
- },
19
- "eslint.enable": true,
20
- "eslint.workingDirectories": ["./"],
21
- "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"]
22
- }
@@ -1,102 +0,0 @@
1
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
- <html xmlns="http://www.w3.org/1999/xhtml">
3
- <html>
4
- <head>
5
- <meta charset="utf-8" />
6
- <title>Form Submission Details</title>
7
- <style>
8
- body {
9
- font-family: Arial, sans-serif;
10
- line-height: 1.6;
11
- color: #333;
12
- }
13
- h3 {
14
- color: #444;
15
- }
16
- table {
17
- width: 100%;
18
- border-collapse: collapse;
19
- margin-bottom: 20px;
20
- }
21
- th,
22
- td {
23
- border: 1px solid #ddd;
24
- padding: 8px;
25
- text-align: left;
26
- }
27
- th {
28
- background-color: #f5f5f5;
29
- }
30
- </style>
31
- </head>
32
- <body>
33
- <h3>Form Fields</h3>
34
- {% if formFields %}
35
- <table>
36
- <thead>
37
- <tr>
38
- <th>Field</th>
39
- <th>Value</th>
40
- </tr>
41
- </thead>
42
- <tbody>
43
- {% for field, value in formFields %}
44
- <tr>
45
- <td>{{ field }}</td>
46
- <td>{{ value }}</td>
47
- </tr>
48
- {% endfor %}
49
- </tbody>
50
- </table>
51
- {% else %}
52
- <p>No form fields submitted.</p>
53
- {% endif %}
54
-
55
- <h3>File Metadata</h3>
56
- {% if files and files.length %}
57
- <table>
58
- <thead>
59
- <tr>
60
- <th>Filename</th>
61
- <th>Path</th>
62
- </tr>
63
- </thead>
64
- <tbody>
65
- {% for file in files %}
66
- <tr>
67
- <td>{{ file.originalname }}</td>
68
- <td>{{ file.path }}</td>
69
- </tr>
70
- {% endfor %}
71
- </tbody>
72
- </table>
73
- {% else %}
74
- <p>No files attached.</p>
75
- {% endif %}
76
-
77
- <h3>Request Metadata</h3>
78
- {% if _meta_ %}
79
- <table>
80
- <tbody>
81
- <tr>
82
- <th>Client IP</th>
83
- <td>{{ _meta_.client_ip | default('Unknown') }}</td>
84
- </tr>
85
- <tr>
86
- <th>Received At</th>
87
- <td>{{ _meta_.received_at | default('Unknown') }}</td>
88
- </tr>
89
- {% if _meta_.ip_chain and _meta_.ip_chain.length %}
90
- <tr>
91
- <th>IP Chain</th>
92
- <td>{{ _meta_.ip_chain | join(', ') }}</td>
93
- </tr>
94
- {% endif %}
95
- </tbody>
96
- </table>
97
- {% else %}
98
- <p>No metadata provided.</p>
99
- {% endif %}
100
- </body>
101
- </html>
102
- </html>
@@ -1,8 +0,0 @@
1
- {
2
- "testform": {
3
- "rcpt": "someone@example.com",
4
- "sender": "Mother of All Forms <noreply@example.com>",
5
- "subject": "A New Form Has Been Gifted You",
6
- "template": "default.njk"
7
- }
8
- }
@@ -1,33 +0,0 @@
1
- {
2
- "user": [
3
- {
4
- "user_id": 1,
5
- "idname": "testuser",
6
- "token": "SomethingWeirdWhoKnows",
7
- "name": "Test Inc.",
8
- "email": "noreply@example.com"
9
- }
10
- ],
11
- "domain": [
12
- {
13
- "domain_id": 1,
14
- "user_id": 1,
15
- "domain": "mail.example.com",
16
- "sender": "Some User <noreply@example.com>"
17
- }
18
- ],
19
- "template": [
20
- {
21
- "template_id": 1,
22
- "user_id": 1,
23
- "domain_id": 1,
24
- "name": "test",
25
- "locale": "",
26
- "template": "",
27
- "template_file": "",
28
- "sender": "someone@mail.example.com",
29
- "subject": "Example Mail",
30
- "slug": "test"
31
- }
32
- ]
33
- }
@@ -1,107 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <title>Email Details</title>
6
- <style>
7
- body {
8
- font-family: Arial, sans-serif;
9
- color: #333;
10
- line-height: 1.6;
11
- }
12
- h2 {
13
- margin-top: 30px;
14
- border-bottom: 1px solid #ccc;
15
- }
16
- table {
17
- width: 100%;
18
- border-collapse: collapse;
19
- margin-top: 10px;
20
- }
21
- th,
22
- td {
23
- padding: 8px;
24
- border: 1px solid #ddd;
25
- }
26
- th {
27
- background: #f0f0f0;
28
- }
29
- </style>
30
- </head>
31
- <body>
32
- <h2>Email Recipient</h2>
33
- <p>
34
- <strong>To:</strong>
35
- {{ _rcpt_email_ }}
36
- </p>
37
-
38
- <h2>Template Variables</h2>
39
-
40
- {% if _vars_ %}
41
- <table>
42
- <thead>
43
- <tr>
44
- <th>Key</th>
45
- <th>Value</th>
46
- </tr>
47
- </thead>
48
- <tbody>
49
- {% for key, value in _vars_ %}
50
- <tr>
51
- <td>{{ key }}</td>
52
- <td>{{ value }}</td>
53
- </tr>
54
- {% endfor %}
55
- </tbody>
56
- </table>
57
- {% else %}
58
- <p>No variables found.</p>
59
- {% endif %}
60
-
61
- <h2>Attachments</h2>
62
- {% if _attachments_ and _attachments_ | length > 0 %}
63
- <table>
64
- <thead>
65
- <tr>
66
- <th>Label</th>
67
- <th>Filename</th>
68
- </tr>
69
- </thead>
70
- <tbody>
71
- {% for label, filename in _attachments_ %}
72
- <tr>
73
- <td>{{ label }}</td>
74
- <td>{{ filename }}</td>
75
- </tr>
76
- {% endfor %}
77
- </tbody>
78
- </table>
79
- {% else %}
80
- <p>No attachments included.</p>
81
- {% endif %}
82
-
83
- <h2>Request Metadata</h2>
84
- {% if _meta_ %}
85
- <table>
86
- <tbody>
87
- <tr>
88
- <th>Client IP</th>
89
- <td>{{ _meta_.client_ip | default('Unknown') }}</td>
90
- </tr>
91
- <tr>
92
- <th>Received At</th>
93
- <td>{{ _meta_.received_at | default('Unknown') }}</td>
94
- </tr>
95
- {% if _meta_.ip_chain and _meta_.ip_chain.length %}
96
- <tr>
97
- <th>IP Chain</th>
98
- <td>{{ _meta_.ip_chain | join(', ') }}</td>
99
- </tr>
100
- {% endif %}
101
- </tbody>
102
- </table>
103
- {% else %}
104
- <p>No request metadata provided.</p>
105
- {% endif %}
106
- </body>
107
- </html>
@@ -1,42 +0,0 @@
1
- const tplsrv = 'mail-magic';
2
-
3
- module.exports = {
4
- apps: [
5
- {
6
- name: tplsrv,
7
- script: 'npm',
8
- args: 'run start',
9
- cwd: `/root/deploy/${tplsrv}/source`,
10
- env: {
11
- NODE_ENV: 'production'
12
- }
13
- },
14
- {
15
- name: 'listmonk',
16
- script: './listmonk',
17
- cwd: '/var/www/ml.yesmedia.no',
18
- exec_mode: 'fork',
19
- instances: 1,
20
- autorestart: true,
21
- watch: false,
22
- max_memory_restart: '1G',
23
- user: 'listmonk',
24
- group: 'listmonk',
25
- env: {
26
- NODE_ENV: 'production'
27
- }
28
- }
29
- ],
30
- deploy: {
31
- 'mail-magic': {
32
- user: 'root',
33
- host: 'localhost',
34
- ref: 'origin/main',
35
- path: `/root/deploy/${tplsrv}`,
36
- repo: `git@github.com:technomoron/${tplsrv}`,
37
- 'pre-deploy-local': '',
38
- 'pre-setup': '',
39
- 'post-deploy': `cd /root/deploy/${tplsrv}/source && pnpm install && pnpm upgrade && pnpm run build && pm2 start /root/deploy/ecosystem.config.cjs`
40
- }
41
- }
42
- };
package/eslint.config.mjs DELETED
@@ -1,196 +0,0 @@
1
- import tsPlugin from '@typescript-eslint/eslint-plugin';
2
- import tsParser from '@typescript-eslint/parser';
3
- import eslintConfigPrettier from 'eslint-config-prettier/flat';
4
- import pluginImport from 'eslint-plugin-import';
5
- import jsoncParser from 'jsonc-eslint-parser';
6
- const TS_FILE_GLOBS = ['**/*.{ts,tsx,mts,cts,vue}'];
7
- const TS_PLUGIN_FILE_GLOBS = ['**/*.{ts,tsx,mts,cts,js,mjs,cjs,vue}'];
8
- const VUE_FILE_GLOBS = ['**/*.vue'];
9
-
10
- const { hasVueSupport, pluginVue, vueTypeScriptConfigs } = await loadVueSupport();
11
- const scopedVueTypeScriptConfigs = hasVueSupport
12
- ? scopeVueConfigs(vueTypeScriptConfigs).map(stripTypeScriptPlugin)
13
- : [];
14
- const vueSpecificBlocks = hasVueSupport
15
- ? [
16
- ...scopedVueTypeScriptConfigs,
17
- {
18
- files: VUE_FILE_GLOBS,
19
- plugins: {
20
- vue: pluginVue
21
- },
22
- rules: {
23
- 'vue/html-indent': 'off', // Let Prettier handle indentation
24
- 'vue/max-attributes-per-line': 'off', // Let Prettier handle line breaks
25
- 'vue/first-attribute-linebreak': 'off', // Let Prettier handle attribute positioning
26
- 'vue/singleline-html-element-content-newline': 'off',
27
- 'vue/html-self-closing': [
28
- 'error',
29
- {
30
- html: {
31
- void: 'always',
32
- normal: 'always',
33
- component: 'always'
34
- }
35
- }
36
- ],
37
- 'vue/multi-word-component-names': 'off', // Disable multi-word name restriction
38
- 'vue/attribute-hyphenation': ['error', 'always']
39
- }
40
- }
41
- ]
42
- : [];
43
-
44
- export default [
45
- {
46
- ignores: [
47
- 'node_modules',
48
- 'dist',
49
- '.output',
50
- '.nuxt',
51
- '.netlify',
52
- 'node_modules/.netlify',
53
- '4000/.nuxt',
54
- 'coverage',
55
- '**/*.d.ts',
56
- 'configure-eslint.cjs',
57
- 'configure-eslint.js',
58
- '*.config.js',
59
- 'public'
60
- ]
61
- },
62
- {
63
- files: TS_PLUGIN_FILE_GLOBS,
64
- plugins: {
65
- '@typescript-eslint': tsPlugin
66
- }
67
- },
68
- ...vueSpecificBlocks,
69
- {
70
- files: ['**/*.json'],
71
- languageOptions: {
72
- parser: jsoncParser
73
- },
74
- rules: {
75
- quotes: ['error', 'double'] // Enforce double quotes in JSON
76
- }
77
- },
78
- {
79
- files: ['**/*.{ts,mts,tsx,js,mjs,cjs}'],
80
- languageOptions: {
81
- parser: tsParser,
82
- parserOptions: {
83
- ecmaVersion: 2023,
84
- sourceType: 'module',
85
- extraFileExtensions: ['.vue']
86
- },
87
- globals: {
88
- RequestInit: 'readonly',
89
- process: 'readonly',
90
- Capacitor: 'readonly',
91
- chrome: 'readonly'
92
- }
93
- },
94
- plugins: {
95
- import: pluginImport
96
- },
97
- rules: {
98
- 'import/order': [
99
- 'error',
100
- {
101
- groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
102
- 'newlines-between': 'always',
103
- alphabetize: { order: 'asc', caseInsensitive: true }
104
- }
105
- ],
106
- '@typescript-eslint/no-explicit-any': ['warn'],
107
- '@typescript-eslint/no-unused-vars': ['warn'],
108
- '@typescript-eslint/no-require-imports': 'off'
109
- }
110
- },
111
- {
112
- ...eslintConfigPrettier
113
- }
114
- ];
115
-
116
- async function loadVueSupport() {
117
- try {
118
- const [vuePluginModule, vueConfigModule] = await Promise.all([
119
- import('eslint-plugin-vue'),
120
- import('@vue/eslint-config-typescript')
121
- ]);
122
-
123
- const pluginVue = unwrapDefault(vuePluginModule);
124
- const { defineConfigWithVueTs, vueTsConfigs } = vueConfigModule;
125
- const configs = defineConfigWithVueTs(vueTsConfigs.recommended);
126
-
127
- return {
128
- hasVueSupport: Boolean(pluginVue && configs.length),
129
- pluginVue,
130
- vueTypeScriptConfigs: configs
131
- };
132
- } catch (error) {
133
- if (isModuleNotFoundError(error)) {
134
- return {
135
- hasVueSupport: false,
136
- pluginVue: null,
137
- vueTypeScriptConfigs: []
138
- };
139
- }
140
-
141
- throw error;
142
- }
143
- }
144
-
145
- function scopeVueConfigs(configs) {
146
- return configs.map((config) => {
147
- const files = config.files ?? [];
148
- const referencesOnlyVueFiles = files.length > 0 && files.every((pattern) => pattern.includes('.vue'));
149
- const hasVuePlugin = Boolean(config.plugins?.vue);
150
-
151
- if (hasVuePlugin || referencesOnlyVueFiles) {
152
- return {
153
- ...config,
154
- files: VUE_FILE_GLOBS
155
- };
156
- }
157
-
158
- return {
159
- ...config,
160
- files: TS_FILE_GLOBS
161
- };
162
- });
163
- }
164
-
165
- function stripTypeScriptPlugin(config) {
166
- const { plugins = {}, ...rest } = config;
167
-
168
- if (!plugins['@typescript-eslint']) {
169
- return config;
170
- }
171
-
172
- const otherPlugins = { ...plugins };
173
- delete otherPlugins['@typescript-eslint'];
174
- const hasOtherPlugins = Object.keys(otherPlugins).length > 0;
175
-
176
- return {
177
- ...rest,
178
- ...(hasOtherPlugins ? { plugins: otherPlugins } : {})
179
- };
180
- }
181
-
182
- function unwrapDefault(module) {
183
- return module?.default ?? module;
184
- }
185
-
186
- function isModuleNotFoundError(error) {
187
- if (!error) {
188
- return false;
189
- }
190
-
191
- if (error.code === 'ERR_MODULE_NOT_FOUND' || error.code === 'MODULE_NOT_FOUND') {
192
- return true;
193
- }
194
-
195
- return typeof error.message === 'string' && error.message.includes('Cannot find module');
196
- }
package/lintconfig.cjs DELETED
@@ -1,81 +0,0 @@
1
- #!/usr/bin/env node
2
- const { execSync, spawnSync } = require('child_process');
3
- const fs = require('fs');
4
- const os = require('os');
5
- const path = require('path');
6
-
7
- const RELEASE_API_URL = 'https://api.github.com/repos/technomoron/vscode-eslint-defaults/releases/latest';
8
- const INSTALLER_ASSET_NAME = 'installer.tgz';
9
-
10
- async function fetch_json(url) {
11
- const response = await fetch(url, {
12
- headers: {
13
- 'User-Agent': 'vscode-eslint-defaults-lintconfig',
14
- Accept: 'application/vnd.github+json'
15
- }
16
- });
17
-
18
- if (!response.ok) {
19
- throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
20
- }
21
-
22
- return response.json();
23
- }
24
-
25
- async function download_asset(url, destination) {
26
- const response = await fetch(url, {
27
- headers: {
28
- 'User-Agent': 'vscode-eslint-defaults-lintconfig'
29
- }
30
- });
31
-
32
- if (!response.ok) {
33
- throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);
34
- }
35
-
36
- const buffer = Buffer.from(await response.arrayBuffer());
37
- fs.writeFileSync(destination, buffer);
38
- }
39
-
40
- async function run() {
41
- const release = await fetch_json(RELEASE_API_URL);
42
- const assets = Array.isArray(release.assets) ? release.assets : [];
43
- const asset = assets.find((item) => item.name === INSTALLER_ASSET_NAME);
44
-
45
- if (!asset?.browser_download_url) {
46
- throw new Error('Latest release does not include installer.tgz.');
47
- }
48
-
49
- const temp_dir = fs.mkdtempSync(path.join(os.tmpdir(), 'lintconfig-'));
50
- const tgz_path = path.join(temp_dir, INSTALLER_ASSET_NAME);
51
- const args = process.argv.slice(2);
52
- let exit_code = 0;
53
-
54
- try {
55
- await download_asset(asset.browser_download_url, tgz_path);
56
- execSync(`tar -xzf "${tgz_path}" -C "${process.cwd()}"`, { stdio: 'inherit' });
57
-
58
- const configure_path = path.join(process.cwd(), 'configure-eslint.cjs');
59
- if (!fs.existsSync(configure_path)) {
60
- throw new Error('configure-eslint.cjs not found after extraction.');
61
- }
62
-
63
- const result = spawnSync(process.execPath, [configure_path, ...args], { stdio: 'inherit' });
64
- if (result.status !== 0) {
65
- exit_code = result.status ?? 1;
66
- } else {
67
- fs.unlinkSync(configure_path);
68
- }
69
- } finally {
70
- fs.rmSync(temp_dir, { recursive: true, force: true });
71
- }
72
-
73
- if (exit_code !== 0) {
74
- process.exit(exit_code);
75
- }
76
- }
77
-
78
- run().catch((error) => {
79
- console.error(error.message || error);
80
- process.exit(1);
81
- });