@technomoron/mail-magic 1.0.4

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 (45) hide show
  1. package/.do-realease.sh +10 -0
  2. package/.editorconfig +9 -0
  3. package/.env-dist +62 -0
  4. package/.prettierrc +14 -0
  5. package/.vscode/extensions.json +15 -0
  6. package/.vscode/settings.json +123 -0
  7. package/CHANGES +25 -0
  8. package/README.md +63 -0
  9. package/config-example/form-template/default.njk +102 -0
  10. package/config-example/forms.config.json +8 -0
  11. package/config-example/init-data.json +33 -0
  12. package/config-example/tx-template/default.njk +107 -0
  13. package/dist/api/forms.js +175 -0
  14. package/dist/api/mailer.js +213 -0
  15. package/dist/index.js +50 -0
  16. package/dist/models/db.js +99 -0
  17. package/dist/models/domain.js +58 -0
  18. package/dist/models/form.js +168 -0
  19. package/dist/models/init.js +176 -0
  20. package/dist/models/txmail.js +167 -0
  21. package/dist/models/user.js +65 -0
  22. package/dist/server.js +22 -0
  23. package/dist/store/envloader.js +116 -0
  24. package/dist/store/store.js +85 -0
  25. package/dist/types.js +1 -0
  26. package/dist/util.js +94 -0
  27. package/ecosystem.config.cjs +42 -0
  28. package/eslint.config.mjs +104 -0
  29. package/package.json +67 -0
  30. package/src/api/forms.ts +209 -0
  31. package/src/api/mailer.ts +242 -0
  32. package/src/index.ts +67 -0
  33. package/src/models/db.ts +112 -0
  34. package/src/models/domain.ts +72 -0
  35. package/src/models/form.ts +198 -0
  36. package/src/models/init.ts +237 -0
  37. package/src/models/txmail.ts +199 -0
  38. package/src/models/user.ts +79 -0
  39. package/src/server.ts +27 -0
  40. package/src/store/envloader.ts +117 -0
  41. package/src/store/store.ts +116 -0
  42. package/src/types.ts +39 -0
  43. package/src/util.ts +111 -0
  44. package/test1.sh +13 -0
  45. package/tsconfig.json +14 -0
@@ -0,0 +1,10 @@
1
+ #!/bin/sh
2
+
3
+ VERSION=$(node -p "require('./package.json').version")
4
+
5
+ echo "Creating release for ${VERSION}"
6
+
7
+ git tag -a "v${VERSION}" -m "Release version ${VERSION}"
8
+ git push origin "v${VERSION}"
9
+
10
+ npm publish --access=public
package/.editorconfig ADDED
@@ -0,0 +1,9 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ indent_style = tab
6
+ indent_size = 4
7
+ end_of_line = lf
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
package/.env-dist ADDED
@@ -0,0 +1,62 @@
1
+ # Specifies the environment in which the app is running Possible values: development, production, staging
2
+ NODE_ENV=development
3
+
4
+ # Defines the port on which the app listens. Default 3780 [number]
5
+ API_PORT=3776
6
+
7
+ # Sets the local IP address for the API to listen at
8
+ API_HOST=0.0.0.0
9
+
10
+ # Reload init-data.db automatically on change [boolean]
11
+ DB_AUTO_RELOAD=true
12
+
13
+ # Whether to force sync on table definitions (ALTER TABLE) [boolean]
14
+ DB_FORCE_SYNC=false
15
+
16
+ # Sets the public URL for the API (i.e. https://ml.example.com:3790)
17
+ API_URL=http://localhost:3776
18
+
19
+ # Path to directory where config files are located
20
+ CONFIG_PATH=./config/
21
+
22
+ # Database username for API database
23
+ DB_USER=
24
+
25
+ # Password for API database
26
+ DB_PASS=
27
+
28
+ # Name of API database. Filename for sqlite3, database name for others
29
+ DB_NAME=maildata
30
+
31
+ # Host of API database
32
+ DB_HOST=localhost
33
+
34
+ # Database type of WP database Possible values: sqlite
35
+ DB_TYPE=sqlite
36
+
37
+ # Log SQL statements [boolean]
38
+ DB_LOG=false
39
+
40
+ # Enable debug output, including nodemailer and API [boolean]
41
+ DEBUG=false
42
+
43
+ # Hostname of SMTP sending host
44
+ SMTP_HOST=localhost
45
+
46
+ # SMTP host server port [number]
47
+ SMTP_PORT=587
48
+
49
+ # Use secure connection to SMTP host (SSL/TSL) [boolean]
50
+ SMTP_SECURE=false
51
+
52
+ # Reject bad cert/TLS connection to SMTP host [boolean]
53
+ SMTP_TLS_REJECT=false
54
+
55
+ # Username for SMTP host
56
+ SMTP_USER=
57
+
58
+ # Password for SMTP host
59
+ SMTP_PASSWORD=
60
+
61
+ # Path for attached files
62
+ UPLOAD_PATH=./uploads/
package/.prettierrc ADDED
@@ -0,0 +1,14 @@
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
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "recommendations": [
3
+ "dbaeumer.vscode-eslint",
4
+ "esbenp.prettier-vscode",
5
+ "editorconfig.editorconfig",
6
+ "Vue.volar",
7
+ "wayou.vscode-todo-highlight"
8
+ ],
9
+ "unwantedRecommendations": [
10
+ "octref.vetur",
11
+ "hookyqr.beautify",
12
+ "dbaeumer.jshint",
13
+ "ms-vscode.vscode-typescript-tslint-plugin"
14
+ ]
15
+ }
@@ -0,0 +1,123 @@
1
+ {
2
+ "editor.detectIndentation": false,
3
+ "editor.insertSpaces": false,
4
+ "editor.tabSize": 4,
5
+ "editor.bracketPairColorization.enabled": true,
6
+ "editor.guides.bracketPairs": true,
7
+ "editor.formatOnSave": true,
8
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
9
+ "editor.codeActionsOnSave": {
10
+ "source.fixAll": "always",
11
+ "source.fixAll.eslint": "always"
12
+ },
13
+ "[json]": {
14
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
15
+ },
16
+ "[jsonc]": {
17
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
18
+ },
19
+ "eslint.alwaysShowStatus": true,
20
+ "prettier.enable": true,
21
+ "typescript.tsdk": "node_modules/typescript/lib",
22
+ "eslint.run": "onSave",
23
+ "eslint.format.enable": true,
24
+ "editor.formatOnType": true,
25
+ "eslint.useFlatConfig": true,
26
+ "eslint.probe": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"],
27
+ "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"],
28
+ "explorer.fileNesting.enabled": true,
29
+ "explorer.fileNesting.expand": false,
30
+ "explorer.fileNesting.patterns": {
31
+ ".clang-tidy": ".clang-format, .clangd, compile_commands.json",
32
+ ".env": "*.env, .env.*, .envrc, env.d.ts",
33
+ ".gitignore": ".gitattributes, .gitmodules, .gitmessage, .lfsconfig, .mailmap, .git-blame*",
34
+ ".project": ".classpath",
35
+ "+layout.svelte": "+layout.ts,+layout.ts,+layout.js,+layout.server.ts,+layout.server.js,+layout.gql",
36
+ "+page.svelte": "+page.server.ts,+page.server.js,+page.ts,+page.js,+page.gql",
37
+ "ansible.cfg": "ansible.cfg, .ansible-lint, requirements.yml",
38
+ "app.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, react-router.config.*, rspack.config.*, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, windi.config.*",
39
+ "application.properties": "*.properties",
40
+ "artisan": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, react-router.config.*, rspack.config.*, server.php, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, webpack.mix.js, windi.config.*",
41
+ "astro.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, react-router.config.*, rspack.config.*, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, windi.config.*",
42
+ "build-wrapper.log": "build-wrapper*.log, build-wrapper-dump*.json, build-wrapper-win*.exe, build-wrapper-linux*, build-wrapper-macosx*",
43
+ "BUILD.bazel": "*.bzl, *.bazel, *.bazelrc, bazel.rc, .bazelignore, .bazelproject, .bazelversion, MODULE.bazel.lock, WORKSPACE",
44
+ "Cargo.toml": ".clippy.toml, .rustfmt.toml, Cargo.Bazel.lock, Cargo.lock, clippy.toml, cross.toml, insta.yaml, rust-toolchain.toml, rustfmt.toml",
45
+ "CMakeLists.txt": "*.cmake, *.cmake.in, .cmake-format.yaml, CMakePresets.json, CMakeCache.txt",
46
+ "composer.json": ".php*.cache, composer.lock, phpunit.xml*, psalm*.xml",
47
+ "default.nix": "shell.nix",
48
+ "deno.json*": "*.env, .env.*, .envrc, api-extractor.json, deno.lock, env.d.ts, import-map.json, import_map.json, jsconfig.*, tsconfig.*, tsdoc.*",
49
+ "Dockerfile": "*.dockerfile, .devcontainer.*, .dockerignore, captain-definition, compose.*, docker-compose.*, dockerfile*",
50
+ "flake.nix": "flake.lock",
51
+ "gatsby-config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, gatsby-browser.*, gatsby-node.*, gatsby-ssr.*, gatsby-transformer.*, histoire.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, react-router.config.*, rspack.config.*, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, windi.config.*",
52
+ "gemfile": ".ruby-version, gemfile.lock",
53
+ "go.mod": ".air*, go.sum",
54
+ "go.work": "go.work.sum",
55
+ "hatch.toml": ".editorconfig, .flake8, .isort.cfg, .python-version, hatch.toml, requirements*.in, requirements*.pip, requirements*.txt, tox.ini",
56
+ "I*.cs": "$(capture).cs",
57
+ "Makefile": "*.mk",
58
+ "mix.exs": ".credo.exs, .dialyzer_ignore.exs, .formatter.exs, .iex.exs, .tool-versions, mix.lock",
59
+ "next.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, next-env.d.ts, next-i18next.config.*, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, react-router.config.*, rspack.config.*, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, windi.config.*",
60
+ "nuxt.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .nuxtignore, .nuxtrc, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, react-router.config.*, rspack.config.*, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, windi.config.*",
61
+ "package.json": "*.code-workspace, .browserslist*, .circleci*, .commitlint*, .cspell*, .cursor*, .cz-config.js, .czrc, .dlint.json, .dprint.json*, .editorconfig, .eslint*, .firebase*, .flowconfig, .github*, .gitlab*, .gitmojirc.json, .gitpod*, .huskyrc*, .jslint*, .knip.*, .lintstagedrc*, .ls-lint.yml, .markdownlint*, .node-version, .nodemon*, .npm*, .nvmrc, .pm2*, .pnp.*, .pnpm*, .prettier*, .pylintrc, .release-please*.json, .releaserc*, .ruff.toml, .sentry*, .shellcheckrc, .simple-git-hooks*, .stackblitz*, .styleci*, .stylelint*, .tazerc*, .textlint*, .tool-versions, .travis*, .versionrc*, .vscode*, .watchman*, .windsurfrules, .xo-config*, .yamllint*, .yarnrc*, Procfile, apollo.config.*, appveyor*, azure-pipelines*, biome.json*, bower.json, build.config.*, bun.lock, bun.lockb, bunfig.toml, colada.options.ts, commitlint*, crowdin*, cspell*, dangerfile*, dlint.json, dprint.json*, ec.config.*, electron-builder.*, eslint*, firebase.json, grunt*, gulp*, jenkins*, knip.*, lerna*, lint-staged*, nest-cli.*, netlify*, nixpacks*, nodemon*, npm-shrinkwrap.json, nx.*, package-lock.json, package.nls*.json, phpcs.xml, pm2.*, pnpm*, prettier*, pullapprove*, pyrightconfig.json, release-please*.json, release-tasks.sh, release.config.*, renovate*, rolldown.config.*, rollup.config.*, rspack*, ruff.toml, sentry.*.config.ts, simple-git-hooks*, sonar-project.properties, stylelint*, tsdown.config.*, tslint*, tsup.config.*, turbo*, typedoc*, unlighthouse*, vercel*, vetur.config.*, webpack*, workspace.json, wrangler.*, xo.config.*, yarn*",
62
+ "Pipfile": ".editorconfig, .flake8, .isort.cfg, .python-version, Pipfile, Pipfile.lock, requirements*.in, requirements*.pip, requirements*.txt, tox.ini",
63
+ "pom.xml": "mvnw*",
64
+ "pubspec.yaml": ".metadata, .packages, all_lint_rules.yaml, analysis_options.yaml, build.yaml, pubspec.lock, pubspec_overrides.yaml",
65
+ "pyproject.toml": ".commitlint*, .cspell*, .dlint.json, .dprint.json*, .editorconfig, .eslint*, .flake8, .flowconfig, .isort.cfg, .jslint*, .lintstagedrc*, .ls-lint.yml, .markdownlint*, .pdm-python, .pdm.toml, .prettier*, .pylintrc, .python-version, .ruff.toml, .shellcheckrc, .stylelint*, .textlint*, .xo-config*, .yamllint*, MANIFEST.in, Pipfile, Pipfile.lock, biome.json*, commitlint*, cspell*, dangerfile*, dlint.json, dprint.json*, eslint*, hatch.toml, lint-staged*, pdm.lock, phpcs.xml, poetry.lock, poetry.toml, prettier*, pyproject.toml, pyrightconfig.json, requirements*.in, requirements*.pip, requirements*.txt, ruff.toml, setup.cfg, setup.py, stylelint*, tox.ini, tslint*, uv.lock, uv.toml, xo.config.*",
66
+ "quasar.conf.js": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, quasar.extensions.json, react-router.config.*, rspack.config.*, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, windi.config.*",
67
+ "readme*": "AUTHORS, Authors, BACKERS*, Backers*, CHANGELOG*, CITATION*, CODEOWNERS, CODE_OF_CONDUCT*, CONTRIBUTING*, CONTRIBUTORS, COPYING*, CREDITS, Changelog*, Citation*, Code_Of_Conduct*, Codeowners, Contributing*, Contributors, Copying*, Credits, GOVERNANCE.MD, Governance.md, HISTORY.MD, History.md, LICENSE*, License*, MAINTAINERS, Maintainers, README-*, README_*, RELEASE_NOTES*, ROADMAP.MD, Readme-*, Readme_*, Release_Notes*, Roadmap.md, SECURITY.MD, SPONSORS*, Security.md, Sponsors*, authors, backers*, changelog*, citation*, code_of_conduct*, codeowners, contributing*, contributors, copying*, credits, governance.md, history.md, license*, maintainers, readme-*, readme_*, release_notes*, roadmap.md, security.md, sponsors*",
68
+ "Readme*": "AUTHORS, Authors, BACKERS*, Backers*, CHANGELOG*, CITATION*, CODEOWNERS, CODE_OF_CONDUCT*, CONTRIBUTING*, CONTRIBUTORS, COPYING*, CREDITS, Changelog*, Citation*, Code_Of_Conduct*, Codeowners, Contributing*, Contributors, Copying*, Credits, GOVERNANCE.MD, Governance.md, HISTORY.MD, History.md, LICENSE*, License*, MAINTAINERS, Maintainers, README-*, README_*, RELEASE_NOTES*, ROADMAP.MD, Readme-*, Readme_*, Release_Notes*, Roadmap.md, SECURITY.MD, SPONSORS*, Security.md, Sponsors*, authors, backers*, changelog*, citation*, code_of_conduct*, codeowners, contributing*, contributors, copying*, credits, governance.md, history.md, license*, maintainers, readme-*, readme_*, release_notes*, roadmap.md, security.md, sponsors*",
69
+ "README*": "AUTHORS, Authors, BACKERS*, Backers*, CHANGELOG*, CITATION*, CODEOWNERS, CODE_OF_CONDUCT*, CONTRIBUTING*, CONTRIBUTORS, COPYING*, CREDITS, Changelog*, Citation*, Code_Of_Conduct*, Codeowners, Contributing*, Contributors, Copying*, Credits, GOVERNANCE.MD, Governance.md, HISTORY.MD, History.md, LICENSE*, License*, MAINTAINERS, Maintainers, README-*, README_*, RELEASE_NOTES*, ROADMAP.MD, Readme-*, Readme_*, Release_Notes*, Roadmap.md, SECURITY.MD, SPONSORS*, Security.md, Sponsors*, authors, backers*, changelog*, citation*, code_of_conduct*, codeowners, contributing*, contributors, copying*, credits, governance.md, history.md, license*, maintainers, readme-*, readme_*, release_notes*, roadmap.md, security.md, sponsors*",
70
+ "remix.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, react-router.config.*, remix.*, rspack.config.*, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, windi.config.*",
71
+ "requirements.txt": ".editorconfig, .flake8, .isort.cfg, .python-version, requirements*.in, requirements*.pip, requirements*.txt, tox.ini",
72
+ "rush.json": "*.code-workspace, .browserslist*, .circleci*, .commitlint*, .cspell*, .cursor*, .cz-config.js, .czrc, .dlint.json, .dprint.json*, .editorconfig, .eslint*, .firebase*, .flowconfig, .github*, .gitlab*, .gitmojirc.json, .gitpod*, .huskyrc*, .jslint*, .knip.*, .lintstagedrc*, .ls-lint.yml, .markdownlint*, .node-version, .nodemon*, .npm*, .nvmrc, .pm2*, .pnp.*, .pnpm*, .prettier*, .pylintrc, .release-please*.json, .releaserc*, .ruff.toml, .sentry*, .shellcheckrc, .simple-git-hooks*, .stackblitz*, .styleci*, .stylelint*, .tazerc*, .textlint*, .tool-versions, .travis*, .versionrc*, .vscode*, .watchman*, .windsurfrules, .xo-config*, .yamllint*, .yarnrc*, Procfile, apollo.config.*, appveyor*, azure-pipelines*, biome.json*, bower.json, build.config.*, bun.lock, bun.lockb, bunfig.toml, colada.options.ts, commitlint*, crowdin*, cspell*, dangerfile*, dlint.json, dprint.json*, ec.config.*, electron-builder.*, eslint*, firebase.json, grunt*, gulp*, jenkins*, knip.*, lerna*, lint-staged*, nest-cli.*, netlify*, nixpacks*, nodemon*, npm-shrinkwrap.json, nx.*, package-lock.json, package.nls*.json, phpcs.xml, pm2.*, pnpm*, prettier*, pullapprove*, pyrightconfig.json, release-please*.json, release-tasks.sh, release.config.*, renovate*, rolldown.config.*, rollup.config.*, rspack*, ruff.toml, sentry.*.config.ts, simple-git-hooks*, sonar-project.properties, stylelint*, tsdown.config.*, tslint*, tsup.config.*, turbo*, typedoc*, unlighthouse*, vercel*, vetur.config.*, webpack*, workspace.json, wrangler.*, xo.config.*, yarn*",
73
+ "sanity.config.*": "sanity.cli.*, sanity.types.ts, schema.json",
74
+ "setup.cfg": ".editorconfig, .flake8, .isort.cfg, .python-version, MANIFEST.in, requirements*.in, requirements*.pip, requirements*.txt, setup.cfg, tox.ini",
75
+ "setup.py": ".editorconfig, .flake8, .isort.cfg, .python-version, MANIFEST.in, requirements*.in, requirements*.pip, requirements*.txt, setup.cfg, setup.py, tox.ini",
76
+ "shims.d.ts": "*.d.ts",
77
+ "svelte.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, houdini.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, mdsvex.config.js, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, react-router.config.*, rspack.config.*, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vite.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, windi.config.*",
78
+ "vite.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, react-router.config.*, rspack.config.*, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, windi.config.*",
79
+ "vue.config.*": "*.env, .babelrc*, .codecov, .cssnanorc*, .env.*, .envrc, .htmlnanorc*, .lighthouserc.*, .mocha*, .postcssrc*, .terserrc*, api-extractor.json, ava.config.*, babel.config.*, capacitor.config.*, content.config.*, contentlayer.config.*, cssnano.config.*, cypress.*, env.d.ts, formkit.config.*, formulate.config.*, histoire.config.*, htmlnanorc.*, i18n.config.*, ionic.config.*, jasmine.*, jest.config.*, jsconfig.*, karma*, lighthouserc.*, panda.config.*, playwright.config.*, postcss.config.*, puppeteer.config.*, react-router.config.*, rspack.config.*, sst.config.*, svgo.config.*, tailwind.config.*, tsconfig.*, tsdoc.*, uno.config.*, unocss.config.*, vitest.config.*, vuetify.config.*, webpack.config.*, windi.config.*",
80
+ "*.asax": "$(capture).*.cs, $(capture).*.vb",
81
+ "*.ascx": "$(capture).*.cs, $(capture).*.vb",
82
+ "*.ashx": "$(capture).*.cs, $(capture).*.vb",
83
+ "*.aspx": "$(capture).*.cs, $(capture).*.vb",
84
+ "*.axaml": "$(capture).axaml.cs",
85
+ "*.bloc.dart": "$(capture).event.dart, $(capture).state.dart",
86
+ "*.c": "$(capture).h",
87
+ "*.cc": "$(capture).hpp, $(capture).h, $(capture).hxx, $(capture).hh",
88
+ "*.cjs": "$(capture).cjs.map, $(capture).*.cjs, $(capture)_*.cjs",
89
+ "*.component.ts": "$(capture).component.html, $(capture).component.spec.ts, $(capture).component.css, $(capture).component.scss, $(capture).component.sass, $(capture).component.less",
90
+ "*.cpp": "$(capture).hpp, $(capture).h, $(capture).hxx, $(capture).hh",
91
+ "*.cs": "$(capture).*.cs",
92
+ "*.cshtml": "$(capture).cshtml.cs, $(capture).cshtml.css",
93
+ "*.csproj": "*.config, *proj.user, appsettings.*, bundleconfig.json",
94
+ "*.css": "$(capture).css.map, $(capture).*.css",
95
+ "*.cxx": "$(capture).hpp, $(capture).h, $(capture).hxx, $(capture).hh",
96
+ "*.dart": "$(capture).freezed.dart, $(capture).g.dart",
97
+ "*.db": "*.db-shm, *.db-wal",
98
+ "*.ex": "$(capture).html.eex, $(capture).html.heex, $(capture).html.leex",
99
+ "*.fs": "$(capture).fs.js, $(capture).fs.js.map, $(capture).fs.jsx, $(capture).fs.ts, $(capture).fs.tsx, $(capture).fs.rs, $(capture).fs.php, $(capture).fs.dart",
100
+ "*.go": "$(capture)_test.go",
101
+ "*.java": "$(capture).class",
102
+ "*.js": "$(capture).js.map, $(capture).*.js, $(capture)_*.js, $(capture).d.ts, $(capture).d.ts.map, $(capture).js.flow",
103
+ "*.jsx": "$(capture).js, $(capture).*.jsx, $(capture)_*.js, $(capture)_*.jsx, $(capture).css, $(capture).module.css, $(capture).less, $(capture).module.less, $(capture).module.less.d.ts, $(capture).scss, $(capture).module.scss, $(capture).module.scss.d.ts",
104
+ "*.master": "$(capture).*.cs, $(capture).*.vb",
105
+ "*.md": "$(capture).*",
106
+ "*.mjs": "$(capture).mjs.map, $(capture).*.mjs, $(capture)_*.mjs",
107
+ "*.module.ts": "$(capture).resolver.ts, $(capture).controller.ts, $(capture).service.ts",
108
+ "*.mts": "$(capture).mts.map, $(capture).*.mts, $(capture)_*.mts",
109
+ "*.proto": "$(capture).pb.go, $(capture).pb.micro.go",
110
+ "*.pubxml": "$(capture).pubxml.user",
111
+ "*.py": "$(capture).pyi",
112
+ "*.razor": "$(capture).razor.cs, $(capture).razor.css, $(capture).razor.scss",
113
+ "*.resx": "$(capture).*.resx, $(capture).designer.cs, $(capture).designer.vb",
114
+ "*.tex": "$(capture).acn, $(capture).acr, $(capture).alg, $(capture).aux, $(capture).bbl, $(capture).bbl-SAVE-ERROR, $(capture).bcf, $(capture).blg, $(capture).fdb_latexmk, $(capture).fls, $(capture).glg, $(capture).glo, $(capture).gls, $(capture).idx, $(capture).ind, $(capture).ist, $(capture).lof, $(capture).log, $(capture).lot, $(capture).nav, $(capture).out, $(capture).run.xml, $(capture).snm, $(capture).synctex.gz, $(capture).toc, $(capture).xdv",
115
+ "*.ts": "$(capture).js, $(capture).d.ts.map, $(capture).*.ts, $(capture)_*.js, $(capture)_*.ts",
116
+ "*.tsx": "$(capture).ts, $(capture).*.tsx, $(capture)_*.ts, $(capture)_*.tsx, $(capture).css, $(capture).module.css, $(capture).less, $(capture).module.less, $(capture).module.less.d.ts, $(capture).scss, $(capture).module.scss, $(capture).module.scss.d.ts, $(capture).css.ts",
117
+ "*.vbproj": "*.config, *proj.user, appsettings.*, bundleconfig.json",
118
+ "*.vue": "$(capture).*.ts, $(capture).*.js, $(capture).story.vue",
119
+ "*.w": "$(capture).*.w, I$(capture).w",
120
+ "*.wat": "$(capture).wasm",
121
+ "*.xaml": "$(capture).xaml.cs"
122
+ }
123
+ }
package/CHANGES ADDED
@@ -0,0 +1,25 @@
1
+ CHANGES
2
+
3
+ Version 1.0.4 (2025-09-27)
4
+
5
+ - Surface request metadata (client IP, IP chain, received-at) to form and transactional
6
+ template rendering and update the bundled templates to display it.
7
+ - Correct domain lookups to match `api_domain.name` during form and mail submission
8
+ authentication so legitimate domains are accepted.
9
+ - Export `createMailMagicServer`/`startMailMagicServer` helpers and only auto-start when
10
+ executed directly, printing the bound host/port after.
11
+
12
+ Version 1.0.3 (2025-09-26)
13
+
14
+ - Introduced the transactional mail model and API route updates so stored templates,
15
+ attachments, and message delivery flow through `api_txmail` instead of the
16
+ removed `api_template`.
17
+ - Let authenticated clients upload form templates via the API, aligning filename/slug
18
+ generation with the new singular config structure.
19
+ - Replaced the Nunjucks loader with `@technomoron/unyuck` to flatten includes
20
+ and collect referenced assets before persisting them.
21
+ - Refreshed tooling and metadata (ESLint/Prettier configs, README, ecosystem config)
22
+ to match the renamed package and repository.
23
+ - Added workspace metadata and sample templates so pnpm and new installs pick up example
24
+ assets.
25
+
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # @technomoron/mail-magic
2
+
3
+ Mail Magic is a TypeScript service for managing, templating, and delivering transactional emails. It exposes a small REST API built on `@technomoron/api-server-base`, persists data with Sequelize/SQLite, and renders outbound messages with Nunjucks templates.
4
+
5
+ ## Features
6
+
7
+ - Upload, store, and send templated email content through a JSON API
8
+ - Preprocess template assets with `@technomoron/unyuck` before persisting
9
+ - Nodemailer transport configuration driven by environment variables
10
+ - SQLite-backed data models for domains, users, forms, and templates
11
+ - Type-safe configuration loader powered by `@technomoron/env-loader`
12
+
13
+ ## Getting Started
14
+
15
+ 1. Clone the repository: `git clone git@github.com:technomoron/mail-magic.git`
16
+ 2. Install dependencies: `npm install`
17
+ 3. Create your environment file: copy `.env-dist` to `.env` and adjust values
18
+ 4. Populate the config directory (see `config-example/` for a reference layout)
19
+ 5. Build the project: `npm run build`
20
+ 6. Start the API server: `npm run start`
21
+
22
+ During development you can run `npm run dev` for a watch mode that recompiles on change and restarts via `nodemon`.
23
+
24
+ ## Configuration
25
+
26
+ - **Environment variables** are defined in `src/store/envloader.ts`. Important settings include SMTP credentials, API host/port, the config directory path, and database options.
27
+ - **Config directory** (`CONFIG_PATH`) contains JSON seed data (`init-data.json`), optional API key files, and template assets. Review `config-example/` for the recommended layout, in particular the `form-template/` and `tx-template/` folders used for compiled Nunjucks templates.
28
+ - **Database** defaults to SQLite (`maildata.db`). You can switch dialects by updating the environment options if your deployment requires another database.
29
+
30
+ When `DB_AUTO_RELOAD` is enabled the service watches `init-data.json` and refreshes templates and forms without a restart.
31
+
32
+ ## API Overview
33
+
34
+ | Method | Path | Description |
35
+ | ------ | ------------------- | --------------------------------------------- |
36
+ | POST | `/v1/tx/template` | Store or update a transactional mail template |
37
+ | POST | `/v1/tx/message` | Render and send a stored transactional mail |
38
+ | POST | `/v1/form/template` | Store or update a form submission template |
39
+ | POST | `/v1/form/message` | Submit a form payload and deliver the email |
40
+
41
+ All authenticated routes expect an API token associated with a configured user. Attachments can be uploaded alongside the `/v1/tx/message` request and are forwarded by Nodemailer.
42
+
43
+ ## Available Scripts
44
+
45
+ - `npm run dev` – Start the API server in watch mode
46
+ - `npm run build` – Compile TypeScript to the `dist/` directory
47
+ - `npm run start` – Launch the compiled server from `dist/`
48
+ - `npm run lint` – Lint the project with ESLint
49
+ - `npm run format` – Apply ESLint autofixes followed by Prettier formatting
50
+ - `npm run cleanbuild` – Clean, lint, format, and rebuild the project
51
+
52
+ ## Repository & Support
53
+
54
+ - Repository: https://github.com/technomoron/mail-magic
55
+ - Issues: https://github.com/technomoron/mail-magic/issues
56
+
57
+ ## License
58
+
59
+ This project is released under the MIT License. See the [LICENSE](LICENSE) file for details.
60
+
61
+ ## Copyright
62
+
63
+ Copyright (c) 2025 Bjørn Erik Jacobsen. All rights reserved.
@@ -0,0 +1,102 @@
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>
@@ -0,0 +1,8 @@
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
+ }
@@ -0,0 +1,33 @@
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
+ }
@@ -0,0 +1,107 @@
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>