@technomoron/mail-magic 1.0.13 → 1.0.14

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 CHANGED
@@ -1,3 +1,8 @@
1
+ Version 1.0.14 (2026-02-01)
2
+
3
+ - Consolidated the API domain/user assertion into a shared helper to keep
4
+ validation logic consistent across modules.
5
+
1
6
  Version 1.0.13 (2026-01-31)
2
7
 
3
8
  - Fixed the per-package release script name and tag format used to trigger
@@ -1,10 +1,9 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { ApiError, ApiModule } from '@technomoron/api-server-base';
4
- import { api_domain } from '../models/domain.js';
4
+ import { assert_domain_and_user } from './auth.js';
5
5
  import { api_form } from '../models/form.js';
6
6
  import { api_txmail } from '../models/txmail.js';
7
- import { api_user } from '../models/user.js';
8
7
  import { decodeComponent, sendFileAsync } from '../util.js';
9
8
  const DOMAIN_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
10
9
  const SEGMENT_PATTERN = /^[a-zA-Z0-9._-]+$/;
@@ -21,25 +20,6 @@ export class AssetAPI extends ApiModule {
21
20
  }
22
21
  return '';
23
22
  }
24
- async assertDomainAndUser(apireq) {
25
- const domainName = this.getBodyValue(apireq.req.body ?? {}, 'domain');
26
- if (!domainName) {
27
- throw new ApiError({ code: 401, message: 'Missing domain' });
28
- }
29
- const user = await api_user.findOne({ where: { token: apireq.token } });
30
- if (!user) {
31
- throw new ApiError({ code: 401, message: `Invalid/Unknown API Key/Token '${apireq.token}'` });
32
- }
33
- const dbdomain = await api_domain.findOne({ where: { name: domainName } });
34
- if (!dbdomain) {
35
- throw new ApiError({ code: 401, message: `Unable to look up the domain ${domainName}` });
36
- }
37
- if (dbdomain.user_id !== user.user_id) {
38
- throw new ApiError({ code: 403, message: `Domain ${domainName} is not owned by this user` });
39
- }
40
- apireq.domain = dbdomain;
41
- apireq.user = user;
42
- }
43
23
  normalizeSubdir(value) {
44
24
  if (!value) {
45
25
  return '';
@@ -129,7 +109,7 @@ export class AssetAPI extends ApiModule {
129
109
  throw new ApiError({ code: 400, message: 'templateType must be "tx" or "form"' });
130
110
  }
131
111
  async postAssets(apireq) {
132
- await this.assertDomainAndUser(apireq);
112
+ await assert_domain_and_user(apireq);
133
113
  const rawFiles = Array.isArray(apireq.req.files) ? apireq.req.files : [];
134
114
  if (!rawFiles.length) {
135
115
  throw new ApiError({ code: 400, message: 'No files uploaded' });
@@ -0,0 +1,37 @@
1
+ import { ApiError } from '@technomoron/api-server-base';
2
+ import { api_domain } from '../models/domain.js';
3
+ import { api_user } from '../models/user.js';
4
+ function getBodyValue(body, ...keys) {
5
+ for (const key of keys) {
6
+ const value = body[key];
7
+ if (Array.isArray(value) && value.length > 0) {
8
+ return String(value[0]);
9
+ }
10
+ if (value !== undefined && value !== null) {
11
+ return String(value);
12
+ }
13
+ }
14
+ return '';
15
+ }
16
+ export async function assert_domain_and_user(apireq) {
17
+ const body = apireq.req.body ?? {};
18
+ const domain = getBodyValue(body, 'domain');
19
+ const locale = getBodyValue(body, 'locale');
20
+ if (!domain) {
21
+ throw new ApiError({ code: 401, message: 'Missing domain' });
22
+ }
23
+ const user = await api_user.findOne({ where: { token: apireq.token } });
24
+ if (!user) {
25
+ throw new ApiError({ code: 401, message: `Invalid/Unknown API Key/Token '${apireq.token}'` });
26
+ }
27
+ const dbdomain = await api_domain.findOne({ where: { name: domain } });
28
+ if (!dbdomain) {
29
+ throw new ApiError({ code: 401, message: `Unable to look up the domain ${domain}` });
30
+ }
31
+ if (dbdomain.user_id !== user.user_id) {
32
+ throw new ApiError({ code: 403, message: `Domain ${domain} is not owned by this user` });
33
+ }
34
+ apireq.domain = dbdomain;
35
+ apireq.user = user;
36
+ apireq.locale = locale || 'en';
37
+ }
package/dist/api/forms.js CHANGED
@@ -2,9 +2,9 @@ import path from 'path';
2
2
  import { ApiModule, ApiError } from '@technomoron/api-server-base';
3
3
  import emailAddresses from 'email-addresses';
4
4
  import nunjucks from 'nunjucks';
5
+ import { assert_domain_and_user } from './auth.js';
5
6
  import { api_domain } from '../models/domain.js';
6
7
  import { api_form } from '../models/form.js';
7
- import { api_user } from '../models/user.js';
8
8
  import { buildRequestMeta, normalizeSlug } from '../util.js';
9
9
  export class FormAPI extends ApiModule {
10
10
  validateEmail(email) {
@@ -14,28 +14,8 @@ export class FormAPI extends ApiModule {
14
14
  }
15
15
  return undefined;
16
16
  }
17
- async assertDomainAndUser(apireq) {
18
- const { domain, locale } = apireq.req.body;
19
- if (!domain) {
20
- throw new ApiError({ code: 401, message: 'Missing domain' });
21
- }
22
- const user = await api_user.findOne({ where: { token: apireq.token } });
23
- if (!user) {
24
- throw new ApiError({ code: 401, message: `Invalid/Unknown API Key/Token '${apireq.token}'` });
25
- }
26
- const dbdomain = await api_domain.findOne({ where: { name: domain } });
27
- if (!dbdomain) {
28
- throw new ApiError({ code: 401, message: `Unable to look up the domain ${domain}` });
29
- }
30
- if (dbdomain.user_id !== user.user_id) {
31
- throw new ApiError({ code: 403, message: `Domain ${domain} is not owned by this user` });
32
- }
33
- apireq.domain = dbdomain;
34
- apireq.locale = locale || 'en';
35
- apireq.user = user;
36
- }
37
17
  async postFormTemplate(apireq) {
38
- await this.assertDomainAndUser(apireq);
18
+ await assert_domain_and_user(apireq);
39
19
  const { template, sender = '', recipient = '', idname, subject = '', locale = '', secret = '' } = apireq.req.body;
40
20
  if (!template) {
41
21
  throw new ApiError({ code: 400, message: 'Missing template data' });
@@ -2,9 +2,8 @@ import { ApiModule, ApiError } from '@technomoron/api-server-base';
2
2
  import emailAddresses from 'email-addresses';
3
3
  import { convert } from 'html-to-text';
4
4
  import nunjucks from 'nunjucks';
5
- import { api_domain } from '../models/domain.js';
5
+ import { assert_domain_and_user } from './auth.js';
6
6
  import { api_txmail } from '../models/txmail.js';
7
- import { api_user } from '../models/user.js';
8
7
  import { buildRequestMeta } from '../util.js';
9
8
  export class MailerAPI extends ApiModule {
10
9
  //
@@ -38,29 +37,9 @@ export class MailerAPI extends ApiModule {
38
37
  });
39
38
  return { valid, invalid };
40
39
  }
41
- async assert_domain_and_user(apireq) {
42
- const { domain, locale } = apireq.req.body;
43
- if (!domain) {
44
- throw new ApiError({ code: 401, message: 'Missing domain' });
45
- }
46
- const user = await api_user.findOne({ where: { token: apireq.token } });
47
- if (!user) {
48
- throw new ApiError({ code: 401, message: `Invalid/Unknown API Key/Token '${apireq.token}'` });
49
- }
50
- const dbdomain = await api_domain.findOne({ where: { name: domain } });
51
- if (!dbdomain) {
52
- throw new ApiError({ code: 401, message: `Unable to look up the domain ${domain}` });
53
- }
54
- if (dbdomain.user_id !== user.user_id) {
55
- throw new ApiError({ code: 403, message: `Domain ${domain} is not owned by this user` });
56
- }
57
- apireq.domain = dbdomain;
58
- apireq.locale = locale || 'en';
59
- apireq.user = user;
60
- }
61
40
  // Store a template in the database
62
41
  async post_template(apireq) {
63
- await this.assert_domain_and_user(apireq);
42
+ await assert_domain_and_user(apireq);
64
43
  const { template, sender = '', name, subject = '', locale = '' } = apireq.req.body;
65
44
  if (!template) {
66
45
  throw new ApiError({ code: 400, message: 'Missing template data' });
@@ -100,8 +79,8 @@ export class MailerAPI extends ApiModule {
100
79
  }
101
80
  // Send a template using posted arguments.
102
81
  async post_send(apireq) {
103
- await this.assert_domain_and_user(apireq);
104
82
  const { name, rcpt, domain = '', locale = '', vars = {}, replyTo, reply_to, headers } = apireq.req.body;
83
+ await assert_domain_and_user(apireq);
105
84
  if (!name || !rcpt || !domain) {
106
85
  throw new ApiError({ code: 400, message: 'name/rcpt/domain required' });
107
86
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@technomoron/mail-magic",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "bin": {