@dyrected/core 2.5.49 → 2.5.52
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/dist/__tests__/admin-auth.test.d.ts +2 -0
- package/dist/__tests__/admin-auth.test.d.ts.map +1 -0
- package/dist/__tests__/admin-auth.test.js +440 -0
- package/dist/__tests__/admin-auth.test.js.map +1 -0
- package/dist/__tests__/config.test.js +57 -0
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/email.service.test.d.ts +2 -0
- package/dist/__tests__/email.service.test.d.ts.map +1 -0
- package/dist/__tests__/email.service.test.js +46 -0
- package/dist/__tests__/email.service.test.js.map +1 -0
- package/dist/__tests__/fixtures/editorial-workflow.d.ts +9 -0
- package/dist/__tests__/fixtures/editorial-workflow.d.ts.map +1 -0
- package/dist/__tests__/fixtures/editorial-workflow.js +12 -0
- package/dist/__tests__/fixtures/editorial-workflow.js.map +1 -0
- package/dist/__tests__/mocks.d.ts +6 -0
- package/dist/__tests__/mocks.d.ts.map +1 -1
- package/dist/__tests__/mocks.js +8 -0
- package/dist/__tests__/mocks.js.map +1 -1
- package/dist/__tests__/openapi.test.d.ts +2 -0
- package/dist/__tests__/openapi.test.d.ts.map +1 -0
- package/dist/__tests__/openapi.test.js +95 -0
- package/dist/__tests__/openapi.test.js.map +1 -0
- package/dist/__tests__/parse-sort.test.d.ts +2 -0
- package/dist/__tests__/parse-sort.test.d.ts.map +1 -0
- package/dist/__tests__/parse-sort.test.js +24 -0
- package/dist/__tests__/parse-sort.test.js.map +1 -0
- package/dist/__tests__/preferences.test.d.ts +2 -0
- package/dist/__tests__/preferences.test.d.ts.map +1 -0
- package/dist/__tests__/preferences.test.js +168 -0
- package/dist/__tests__/preferences.test.js.map +1 -0
- package/dist/__tests__/router.test.js +4 -0
- package/dist/__tests__/router.test.js.map +1 -1
- package/dist/__tests__/where.test.js +9 -1
- package/dist/__tests__/where.test.js.map +1 -1
- package/dist/__tests__/workflow.test.d.ts +2 -0
- package/dist/__tests__/workflow.test.d.ts.map +1 -0
- package/dist/__tests__/workflow.test.js +497 -0
- package/dist/__tests__/workflow.test.js.map +1 -0
- package/dist/app-config-CaEQH0Kb.d.cts +1383 -0
- package/dist/app-config-CaEQH0Kb.d.ts +1383 -0
- package/dist/app-config-DLK6bCaP.d.cts +1390 -0
- package/dist/app-config-DLK6bCaP.d.ts +1390 -0
- package/dist/app-config-oOarB6Fw.d.cts +1411 -0
- package/dist/app-config-oOarB6Fw.d.ts +1411 -0
- package/dist/auth/token.d.ts +2 -0
- package/dist/auth/token.d.ts.map +1 -1
- package/dist/auth/token.js.map +1 -1
- package/dist/controllers/admin-auth.controller.d.ts +68 -0
- package/dist/controllers/admin-auth.controller.d.ts.map +1 -0
- package/dist/controllers/admin-auth.controller.js +414 -0
- package/dist/controllers/admin-auth.controller.js.map +1 -0
- package/dist/controllers/collection.controller.d.ts +69 -0
- package/dist/controllers/collection.controller.d.ts.map +1 -1
- package/dist/controllers/collection.controller.js +232 -7
- package/dist/controllers/collection.controller.js.map +1 -1
- package/dist/controllers/global.controller.js +19 -3
- package/dist/controllers/global.controller.js.map +1 -1
- package/dist/controllers/media.controller.d.ts +2 -0
- package/dist/controllers/media.controller.d.ts.map +1 -1
- package/dist/controllers/media.controller.js +13 -6
- package/dist/controllers/media.controller.js.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +168 -6
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +79 -11
- package/dist/server.d.cts +7 -1
- package/dist/server.d.ts +7 -1
- package/dist/server.js +79 -11
- package/dist/services/email-template.d.ts +46 -0
- package/dist/services/email-template.d.ts.map +1 -0
- package/dist/services/email-template.js +88 -0
- package/dist/services/email-template.js.map +1 -0
- package/dist/services/email.service.d.ts.map +1 -1
- package/dist/services/email.service.js +25 -148
- package/dist/services/email.service.js.map +1 -1
- package/dist/services/population.service.d.ts.map +1 -1
- package/dist/services/population.service.js +27 -1
- package/dist/services/population.service.js.map +1 -1
- package/dist/types/access.d.ts +38 -0
- package/dist/types/access.d.ts.map +1 -0
- package/dist/types/access.js +2 -0
- package/dist/types/access.js.map +1 -0
- package/dist/types/adapters.d.ts +146 -0
- package/dist/types/adapters.d.ts.map +1 -0
- package/dist/types/adapters.js +2 -0
- package/dist/types/adapters.js.map +1 -0
- package/dist/types/admin-auth.d.ts +132 -0
- package/dist/types/admin-auth.d.ts.map +1 -0
- package/dist/types/admin-auth.js +2 -0
- package/dist/types/admin-auth.js.map +1 -0
- package/dist/types/admin.d.ts +65 -0
- package/dist/types/admin.d.ts.map +1 -0
- package/dist/types/admin.js +2 -0
- package/dist/types/admin.js.map +1 -0
- package/dist/types/app-config.d.ts +143 -0
- package/dist/types/app-config.d.ts.map +1 -0
- package/dist/types/app-config.js +2 -0
- package/dist/types/app-config.js.map +1 -0
- package/dist/types/documents.d.ts +65 -0
- package/dist/types/documents.d.ts.map +1 -0
- package/dist/types/documents.js +2 -0
- package/dist/types/documents.js.map +1 -0
- package/dist/types/hooks.d.ts +122 -0
- package/dist/types/hooks.d.ts.map +1 -0
- package/dist/types/hooks.js +2 -0
- package/dist/types/hooks.js.map +1 -0
- package/dist/types/index.d.ts +12 -1748
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +12 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/request.d.ts +42 -0
- package/dist/types/request.d.ts.map +1 -0
- package/dist/types/request.js +2 -0
- package/dist/types/request.js.map +1 -0
- package/dist/types/schema-config.d.ts +303 -0
- package/dist/types/schema-config.d.ts.map +1 -0
- package/dist/types/schema-config.js +2 -0
- package/dist/types/schema-config.js.map +1 -0
- package/dist/types/schema-core.d.ts +294 -0
- package/dist/types/schema-core.d.ts.map +1 -0
- package/dist/types/schema-core.js +2 -0
- package/dist/types/schema-core.js.map +1 -0
- package/dist/types/schema-inference.d.ts +73 -0
- package/dist/types/schema-inference.d.ts.map +1 -0
- package/dist/types/schema-inference.js +2 -0
- package/dist/types/schema-inference.js.map +1 -0
- package/dist/types/workflows.d.ts +84 -0
- package/dist/types/workflows.d.ts.map +1 -0
- package/dist/types/workflows.js +7 -0
- package/dist/types/workflows.js.map +1 -0
- package/dist/utils/admin-auth.d.ts +5 -0
- package/dist/utils/admin-auth.d.ts.map +1 -0
- package/dist/utils/admin-auth.js +39 -0
- package/dist/utils/admin-auth.js.map +1 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +86 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/hooks.d.ts.map +1 -1
- package/dist/utils/hooks.js +12 -8
- package/dist/utils/hooks.js.map +1 -1
- package/dist/utils/openapi.d.ts.map +1 -1
- package/dist/utils/openapi.js +559 -48
- package/dist/utils/openapi.js.map +1 -1
- package/dist/utils/parse-sort.d.ts +7 -0
- package/dist/utils/parse-sort.d.ts.map +1 -0
- package/dist/utils/parse-sort.js +23 -0
- package/dist/utils/parse-sort.js.map +1 -0
- package/dist/utils/swagger.d.ts +6 -0
- package/dist/utils/swagger.d.ts.map +1 -1
- package/dist/utils/swagger.js +13 -2
- package/dist/utils/swagger.js.map +1 -1
- package/dist/utils/upload-validation.d.ts +23 -0
- package/dist/utils/upload-validation.d.ts.map +1 -0
- package/dist/utils/upload-validation.js +44 -0
- package/dist/utils/upload-validation.js.map +1 -0
- package/dist/utils/where-sanitizer.d.ts.map +1 -1
- package/dist/utils/where-sanitizer.js +4 -0
- package/dist/utils/where-sanitizer.js.map +1 -1
- package/dist/workflows.d.ts +54 -0
- package/dist/workflows.d.ts.map +1 -0
- package/dist/workflows.js +282 -0
- package/dist/workflows.js.map +1 -0
- package/package.json +1 -1
package/dist/server.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as hono_types from 'hono/types';
|
|
2
2
|
import * as hono from 'hono';
|
|
3
3
|
import { Hono, Context } from 'hono';
|
|
4
|
-
import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-
|
|
4
|
+
import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-oOarB6Fw.cjs';
|
|
5
5
|
import * as hono_utils_http_status from 'hono/utils/http-status';
|
|
6
6
|
import * as hono_utils_types from 'hono/utils/types';
|
|
7
7
|
import 'lucide-react';
|
|
@@ -192,6 +192,8 @@ declare class CollectionController {
|
|
|
192
192
|
}, 500, "json">) | (Response & hono.TypedResponse<{
|
|
193
193
|
message: string;
|
|
194
194
|
}, 400, "json">) | (Response & hono.TypedResponse<{
|
|
195
|
+
message: string;
|
|
196
|
+
}, 413 | 415, "json">) | (Response & hono.TypedResponse<{
|
|
195
197
|
[x: string]: hono_utils_types.JSONValue;
|
|
196
198
|
}, 201, "json">) | (Response & hono.TypedResponse<{
|
|
197
199
|
id: string;
|
|
@@ -207,6 +209,8 @@ declare class CollectionController {
|
|
|
207
209
|
}, 500, "json">) | (Response & hono.TypedResponse<{
|
|
208
210
|
message: string;
|
|
209
211
|
}, 400, "json">) | (Response & hono.TypedResponse<{
|
|
212
|
+
message: string;
|
|
213
|
+
}, 413 | 415, "json">) | (Response & hono.TypedResponse<{
|
|
210
214
|
[x: string]: hono_utils_types.JSONValue;
|
|
211
215
|
}, 201, "json">)>;
|
|
212
216
|
update(c: Context<DyrectedContext>): Promise<(Response & hono.TypedResponse<{
|
|
@@ -324,6 +328,8 @@ declare class MediaController {
|
|
|
324
328
|
}, 500, "json">) | (Response & hono.TypedResponse<{
|
|
325
329
|
message: string;
|
|
326
330
|
}, 400, "json">) | (Response & hono.TypedResponse<{
|
|
331
|
+
message: string;
|
|
332
|
+
}, 413 | 415, "json">) | (Response & hono.TypedResponse<{
|
|
327
333
|
[x: string]: any;
|
|
328
334
|
id: string;
|
|
329
335
|
}, 201, "json">)>;
|
package/dist/server.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as hono_types from 'hono/types';
|
|
2
2
|
import * as hono from 'hono';
|
|
3
3
|
import { Hono, Context } from 'hono';
|
|
4
|
-
import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-
|
|
4
|
+
import { D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-oOarB6Fw.js';
|
|
5
5
|
import * as hono_utils_http_status from 'hono/utils/http-status';
|
|
6
6
|
import * as hono_utils_types from 'hono/utils/types';
|
|
7
7
|
import 'lucide-react';
|
|
@@ -192,6 +192,8 @@ declare class CollectionController {
|
|
|
192
192
|
}, 500, "json">) | (Response & hono.TypedResponse<{
|
|
193
193
|
message: string;
|
|
194
194
|
}, 400, "json">) | (Response & hono.TypedResponse<{
|
|
195
|
+
message: string;
|
|
196
|
+
}, 413 | 415, "json">) | (Response & hono.TypedResponse<{
|
|
195
197
|
[x: string]: hono_utils_types.JSONValue;
|
|
196
198
|
}, 201, "json">) | (Response & hono.TypedResponse<{
|
|
197
199
|
id: string;
|
|
@@ -207,6 +209,8 @@ declare class CollectionController {
|
|
|
207
209
|
}, 500, "json">) | (Response & hono.TypedResponse<{
|
|
208
210
|
message: string;
|
|
209
211
|
}, 400, "json">) | (Response & hono.TypedResponse<{
|
|
212
|
+
message: string;
|
|
213
|
+
}, 413 | 415, "json">) | (Response & hono.TypedResponse<{
|
|
210
214
|
[x: string]: hono_utils_types.JSONValue;
|
|
211
215
|
}, 201, "json">)>;
|
|
212
216
|
update(c: Context<DyrectedContext>): Promise<(Response & hono.TypedResponse<{
|
|
@@ -324,6 +328,8 @@ declare class MediaController {
|
|
|
324
328
|
}, 500, "json">) | (Response & hono.TypedResponse<{
|
|
325
329
|
message: string;
|
|
326
330
|
}, 400, "json">) | (Response & hono.TypedResponse<{
|
|
331
|
+
message: string;
|
|
332
|
+
}, 413 | 415, "json">) | (Response & hono.TypedResponse<{
|
|
327
333
|
[x: string]: any;
|
|
328
334
|
id: string;
|
|
329
335
|
}, 201, "json">)>;
|
package/dist/server.js
CHANGED
|
@@ -64,6 +64,7 @@ var DefaultsService = class {
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
// src/services/population.service.ts
|
|
67
|
+
import jexl from "jexl";
|
|
67
68
|
var PopulationService = class {
|
|
68
69
|
db;
|
|
69
70
|
collections;
|
|
@@ -168,7 +169,28 @@ var PopulationService = class {
|
|
|
168
169
|
maxDepth
|
|
169
170
|
});
|
|
170
171
|
const identifier = docWithDefaults.slug || docWithDefaults.id;
|
|
171
|
-
|
|
172
|
+
let resolvedUrl = `/collections/${value.relationTo}/${identifier}`;
|
|
173
|
+
const previewUrlPattern = relatedCollection.admin?.previewUrl || relatedCollection.admin?.urlPattern;
|
|
174
|
+
if (previewUrlPattern) {
|
|
175
|
+
if (typeof previewUrlPattern === "function") {
|
|
176
|
+
try {
|
|
177
|
+
resolvedUrl = previewUrlPattern(docWithDefaults, { locale: "en" }) || resolvedUrl;
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
} else if (typeof previewUrlPattern === "string") {
|
|
181
|
+
if (previewUrlPattern.includes("{{")) {
|
|
182
|
+
resolvedUrl = previewUrlPattern.replace(/{{(.*?)}}/g, (_, key) => String(docWithDefaults[key.trim()] || ""));
|
|
183
|
+
} else if (previewUrlPattern.includes("+") || previewUrlPattern.includes("?") || previewUrlPattern.includes("==") || previewUrlPattern.includes("siteUrl")) {
|
|
184
|
+
try {
|
|
185
|
+
resolvedUrl = jexl.evalSync(previewUrlPattern, docWithDefaults);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
resolvedUrl = previewUrlPattern.replace(/:([a-zA-Z0-9_]+)/g, (_, key) => String(docWithDefaults[key] || ""));
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
resolvedUrl = previewUrlPattern.replace(/:([a-zA-Z0-9_]+)/g, (_, key) => String(docWithDefaults[key] || ""));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
172
194
|
populatedDoc[field.name] = {
|
|
173
195
|
...value,
|
|
174
196
|
url: resolvedUrl,
|
|
@@ -286,13 +308,44 @@ function createReadonlyDb(db) {
|
|
|
286
308
|
});
|
|
287
309
|
}
|
|
288
310
|
|
|
311
|
+
// src/utils/upload-validation.ts
|
|
312
|
+
function isMimeAllowed(mimeType, allowed) {
|
|
313
|
+
const type = (mimeType || "").toLowerCase().trim();
|
|
314
|
+
return allowed.some((raw) => {
|
|
315
|
+
const pattern = (raw || "").toLowerCase().trim();
|
|
316
|
+
if (!pattern) return false;
|
|
317
|
+
if (pattern === "*" || pattern === "*/*") return true;
|
|
318
|
+
if (pattern.endsWith("/*")) return type.startsWith(pattern.slice(0, -1));
|
|
319
|
+
return type === pattern;
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
function validateUpload(file, config) {
|
|
323
|
+
if (!config) return null;
|
|
324
|
+
const { allowedMimeTypes, maxFileSize } = config;
|
|
325
|
+
if (allowedMimeTypes && allowedMimeTypes.length > 0) {
|
|
326
|
+
if (!isMimeAllowed(file.type || "", allowedMimeTypes)) {
|
|
327
|
+
return {
|
|
328
|
+
status: 415,
|
|
329
|
+
message: `File type "${file.type || "unknown"}" is not allowed. Allowed types: ${allowedMimeTypes.join(", ")}.`
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (typeof maxFileSize === "number" && typeof file.size === "number" && file.size > maxFileSize) {
|
|
334
|
+
return {
|
|
335
|
+
status: 413,
|
|
336
|
+
message: `File is too large (${file.size} bytes). Maximum allowed size is ${maxFileSize} bytes.`
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
|
|
289
342
|
// src/auth/jexl.ts
|
|
290
|
-
import
|
|
343
|
+
import jexl2 from "jexl";
|
|
291
344
|
async function evaluateAccess(expression, context) {
|
|
292
345
|
if (expression === void 0 || expression === null) return false;
|
|
293
346
|
if (typeof expression === "boolean") return expression;
|
|
294
347
|
try {
|
|
295
|
-
const result = await
|
|
348
|
+
const result = await jexl2.eval(expression, context);
|
|
296
349
|
return !!result;
|
|
297
350
|
} catch (err) {
|
|
298
351
|
console.error("[dyrected/core] Jexl evaluation failed:", err);
|
|
@@ -582,6 +635,11 @@ var CollectionController = class {
|
|
|
582
635
|
const formData = await c.req.formData();
|
|
583
636
|
const file = formData.get("file");
|
|
584
637
|
if (!file) return c.json({ message: "No file uploaded" }, 400);
|
|
638
|
+
const uploadConfig = typeof this.collection.upload === "object" ? this.collection.upload : void 0;
|
|
639
|
+
const validationError = validateUpload(file, uploadConfig);
|
|
640
|
+
if (validationError) {
|
|
641
|
+
return c.json({ message: validationError.message }, validationError.status);
|
|
642
|
+
}
|
|
585
643
|
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
586
644
|
const siteId = c.get("siteId");
|
|
587
645
|
const workspaceId = c.get("workspaceId");
|
|
@@ -1119,18 +1177,23 @@ var MediaController = class {
|
|
|
1119
1177
|
if (!file) {
|
|
1120
1178
|
return c.json({ message: "No file uploaded" }, 400);
|
|
1121
1179
|
}
|
|
1122
|
-
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
1123
1180
|
const siteId = c.get("siteId");
|
|
1181
|
+
let colConfig = config.collections.find((col) => col.slug === this.collection);
|
|
1182
|
+
if (!colConfig && config.onSchemaFetch && siteId) {
|
|
1183
|
+
const dynamic = await config.onSchemaFetch(siteId);
|
|
1184
|
+
colConfig = dynamic.collections?.find((col) => col.slug === this.collection);
|
|
1185
|
+
}
|
|
1186
|
+
const uploadConfig = typeof colConfig?.upload === "object" ? colConfig.upload : void 0;
|
|
1187
|
+
const validationError = validateUpload(file, uploadConfig);
|
|
1188
|
+
if (validationError) {
|
|
1189
|
+
return c.json({ message: validationError.message }, validationError.status);
|
|
1190
|
+
}
|
|
1191
|
+
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
1124
1192
|
const workspaceId = c.get("workspaceId");
|
|
1125
1193
|
const prefix = workspaceId ? `${workspaceId}/${siteId}` : siteId || "default";
|
|
1126
1194
|
let imageMetadata = {};
|
|
1127
1195
|
let imageSizes = {};
|
|
1128
1196
|
if (imageService && file.type.startsWith("image/")) {
|
|
1129
|
-
let colConfig = config.collections.find((col) => col.slug === this.collection);
|
|
1130
|
-
if (!colConfig && config.onSchemaFetch && siteId) {
|
|
1131
|
-
const dynamic = await config.onSchemaFetch(siteId);
|
|
1132
|
-
colConfig = dynamic.collections?.find((col) => col.slug === this.collection);
|
|
1133
|
-
}
|
|
1134
1197
|
try {
|
|
1135
1198
|
const processed = await imageService.process({
|
|
1136
1199
|
buffer,
|
|
@@ -2206,7 +2269,7 @@ function optionalAuth() {
|
|
|
2206
2269
|
}
|
|
2207
2270
|
|
|
2208
2271
|
// src/utils/swagger.ts
|
|
2209
|
-
function getSwaggerHtml(specUrl
|
|
2272
|
+
function getSwaggerHtml(specUrl) {
|
|
2210
2273
|
return `
|
|
2211
2274
|
<!DOCTYPE html>
|
|
2212
2275
|
<html lang="en">
|
|
@@ -2226,7 +2289,12 @@ function getSwaggerHtml(specUrl = "/api/openapi.json") {
|
|
|
2226
2289
|
// Forward the apikey query param when loading the spec and making API calls
|
|
2227
2290
|
const params = new URLSearchParams(window.location.search);
|
|
2228
2291
|
const apiKey = params.get('apikey');
|
|
2229
|
-
|
|
2292
|
+
// Resolve the spec relative to this docs page so any mount prefix
|
|
2293
|
+
// (e.g. "/dyrected/api/docs") is preserved. Falls back to an explicit URL
|
|
2294
|
+
// when one is supplied by the caller.
|
|
2295
|
+
const explicitSpecUrl = ${JSON.stringify(specUrl ?? "")};
|
|
2296
|
+
const specUrl = explicitSpecUrl || (window.location.pathname.replace(/\\/docs\\/?$/, '') + '/openapi.json');
|
|
2297
|
+
const specUrlWithKey = apiKey ? specUrl + '?apikey=' + encodeURIComponent(apiKey) : specUrl;
|
|
2230
2298
|
|
|
2231
2299
|
window.ui = SwaggerUIBundle({
|
|
2232
2300
|
url: specUrlWithKey,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const emailTokens: {
|
|
2
|
+
readonly colors: {
|
|
3
|
+
readonly canvas: "#f6f7f2";
|
|
4
|
+
readonly surface: "#ffffff";
|
|
5
|
+
readonly text: "#171717";
|
|
6
|
+
readonly muted: "#62665b";
|
|
7
|
+
readonly subtle: "#8a8f82";
|
|
8
|
+
readonly border: "#dde0d7";
|
|
9
|
+
readonly accent: "#b6ff2e";
|
|
10
|
+
readonly code: "#f1f3ec";
|
|
11
|
+
readonly dangerSurface: "#fff2f0";
|
|
12
|
+
readonly dangerBorder: "#ffc9c2";
|
|
13
|
+
readonly dangerText: "#9f251b";
|
|
14
|
+
};
|
|
15
|
+
readonly font: "Arial, 'Helvetica Neue', Helvetica, sans-serif";
|
|
16
|
+
readonly mono: "'Courier New', Courier, monospace";
|
|
17
|
+
readonly radius: {
|
|
18
|
+
readonly card: "12px";
|
|
19
|
+
readonly control: "6px";
|
|
20
|
+
};
|
|
21
|
+
readonly width: "600px";
|
|
22
|
+
};
|
|
23
|
+
export declare function escapeHtml(value: string): string;
|
|
24
|
+
export declare function heading(content: string): string;
|
|
25
|
+
export declare function paragraph(content: string, margin?: string): string;
|
|
26
|
+
export declare function sectionLabel(content: string): string;
|
|
27
|
+
export declare function divider(): string;
|
|
28
|
+
export declare function spacer(height?: number): string;
|
|
29
|
+
export declare function row(content: string, cellStyle?: string): string;
|
|
30
|
+
export declare function table(content: string, style?: string): string;
|
|
31
|
+
export declare function detailBox(content: string, monospace?: boolean): string;
|
|
32
|
+
export declare function eventList(events: ReadonlyArray<{
|
|
33
|
+
label: string;
|
|
34
|
+
value: string;
|
|
35
|
+
}>): string;
|
|
36
|
+
export declare function ctaButton(label: string, href: string): string;
|
|
37
|
+
export declare function alertBox(content: string): string;
|
|
38
|
+
interface LayoutOptions {
|
|
39
|
+
preheader: string;
|
|
40
|
+
title: string;
|
|
41
|
+
content: string;
|
|
42
|
+
footer: string;
|
|
43
|
+
}
|
|
44
|
+
export declare function layout({ preheader, title, content, footer }: LayoutOptions): string;
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=email-template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-template.d.ts","sourceRoot":"","sources":["../../src/services/email-template.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;CAkBd,CAAC;AAEX,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD;AAWD,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAa,GAAG,MAAM,CAEtE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,wBAAgB,MAAM,CAAC,MAAM,SAAK,GAAG,MAAM,CAE1C;AAED,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,SAAK,GAAG,MAAM,CAE3D;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAe,GAAG,MAAM,CAEnE;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,MAAM,CAGpE;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAKzF;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAO7D;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,UAAU,aAAa;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,aAAa,GAAG,MAAM,CAkBnF"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export const emailTokens = {
|
|
2
|
+
colors: {
|
|
3
|
+
canvas: '#f6f7f2',
|
|
4
|
+
surface: '#ffffff',
|
|
5
|
+
text: '#171717',
|
|
6
|
+
muted: '#62665b',
|
|
7
|
+
subtle: '#8a8f82',
|
|
8
|
+
border: '#dde0d7',
|
|
9
|
+
accent: '#b6ff2e',
|
|
10
|
+
code: '#f1f3ec',
|
|
11
|
+
dangerSurface: '#fff2f0',
|
|
12
|
+
dangerBorder: '#ffc9c2',
|
|
13
|
+
dangerText: '#9f251b',
|
|
14
|
+
},
|
|
15
|
+
font: "Arial, 'Helvetica Neue', Helvetica, sans-serif",
|
|
16
|
+
mono: "'Courier New', Courier, monospace",
|
|
17
|
+
radius: { card: '12px', control: '6px' },
|
|
18
|
+
width: '600px',
|
|
19
|
+
};
|
|
20
|
+
export function escapeHtml(value) {
|
|
21
|
+
return value.replace(/[&<>'"]/g, (character) => ({
|
|
22
|
+
'&': '&',
|
|
23
|
+
'<': '<',
|
|
24
|
+
'>': '>',
|
|
25
|
+
"'": ''',
|
|
26
|
+
'"': '"',
|
|
27
|
+
})[character]);
|
|
28
|
+
}
|
|
29
|
+
function safeHttpUrl(value) {
|
|
30
|
+
try {
|
|
31
|
+
const url = new URL(value);
|
|
32
|
+
return url.protocol === 'https:' || url.protocol === 'http:' ? escapeHtml(url.toString()) : undefined;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function heading(content) {
|
|
39
|
+
return `<h1 style="margin:0;font-family:${emailTokens.font};font-size:24px;line-height:1.25;font-weight:700;color:${emailTokens.colors.text}">${escapeHtml(content)}</h1>`;
|
|
40
|
+
}
|
|
41
|
+
export function paragraph(content, margin = '0 0 16px') {
|
|
42
|
+
return `<p style="margin:${margin};font-family:${emailTokens.font};font-size:15px;line-height:1.6;color:${emailTokens.colors.muted}">${escapeHtml(content)}</p>`;
|
|
43
|
+
}
|
|
44
|
+
export function sectionLabel(content) {
|
|
45
|
+
return `<p style="margin:0 0 8px;font-family:${emailTokens.font};font-size:11px;line-height:1.4;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:${emailTokens.colors.subtle}">${escapeHtml(content)}</p>`;
|
|
46
|
+
}
|
|
47
|
+
export function divider() {
|
|
48
|
+
return `<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%"><tr><td style="height:1px;background:${emailTokens.colors.border};font-size:0;line-height:0"> </td></tr></table>`;
|
|
49
|
+
}
|
|
50
|
+
export function spacer(height = 16) {
|
|
51
|
+
return table(row(' ', `height:${height}px;font-size:0;line-height:0`));
|
|
52
|
+
}
|
|
53
|
+
export function row(content, cellStyle = '') {
|
|
54
|
+
return `<tr><td style="${cellStyle}">${content}</td></tr>`;
|
|
55
|
+
}
|
|
56
|
+
export function table(content, style = 'width:100%') {
|
|
57
|
+
return `<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" style="${style}">${content}</table>`;
|
|
58
|
+
}
|
|
59
|
+
export function detailBox(content, monospace = false) {
|
|
60
|
+
const font = monospace ? emailTokens.mono : emailTokens.font;
|
|
61
|
+
return table(row(escapeHtml(content), `padding:14px 16px;font-family:${font};font-size:13px;line-height:1.5;font-weight:${monospace ? '400' : '700'};color:${emailTokens.colors.text};word-break:break-all`), `width:100%;background:${emailTokens.colors.code};border-radius:${emailTokens.radius.control}`);
|
|
62
|
+
}
|
|
63
|
+
export function eventList(events) {
|
|
64
|
+
return table(events.map(({ label, value }) => row(`${sectionLabel(label)}${paragraph(value, '0')}`, `padding:12px 0;border-bottom:1px solid ${emailTokens.colors.border}`)).join(''));
|
|
65
|
+
}
|
|
66
|
+
export function ctaButton(label, href) {
|
|
67
|
+
const safeHref = safeHttpUrl(href);
|
|
68
|
+
if (!safeHref)
|
|
69
|
+
return '';
|
|
70
|
+
return table(row(`<a href="${safeHref}" style="display:inline-block;padding:13px 22px;border-radius:${emailTokens.radius.control};background:${emailTokens.colors.accent};font-family:${emailTokens.font};font-size:14px;line-height:1.2;font-weight:700;color:${emailTokens.colors.text};text-decoration:none">${escapeHtml(label)}</a>`, 'padding:8px 0 24px'), 'width:auto');
|
|
71
|
+
}
|
|
72
|
+
export function alertBox(content) {
|
|
73
|
+
return table(row(escapeHtml(content), `padding:13px 16px;font-family:${emailTokens.font};font-size:13px;line-height:1.5;color:${emailTokens.colors.dangerText}`), `width:100%;background:${emailTokens.colors.dangerSurface};border:1px solid ${emailTokens.colors.dangerBorder};border-radius:${emailTokens.radius.control}`);
|
|
74
|
+
}
|
|
75
|
+
export function layout({ preheader, title, content, footer }) {
|
|
76
|
+
return `<!doctype html>
|
|
77
|
+
<html lang="en">
|
|
78
|
+
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"></head>
|
|
79
|
+
<body style="margin:0;padding:0;background:${emailTokens.colors.canvas}">
|
|
80
|
+
<div style="display:none;max-height:0;overflow:hidden;opacity:0;color:transparent">${escapeHtml(preheader)}</div>
|
|
81
|
+
${table(row(table(row(' ', `height:5px;background:${emailTokens.colors.accent};font-size:0;line-height:0`) +
|
|
82
|
+
row(`${sectionLabel('Dyrected')}${heading(title)}`, 'padding:30px 32px 24px') +
|
|
83
|
+
row(content, 'padding:0 32px 32px') +
|
|
84
|
+
row(`${divider()}${paragraph(footer, '20px 0 6px')}${paragraph('Privacy: this message contains account-related information; please avoid forwarding it.', '0')}`, 'padding:0 32px 28px'), `width:100%;max-width:${emailTokens.width};background:${emailTokens.colors.surface};border:1px solid ${emailTokens.colors.border};border-radius:${emailTokens.radius.card};overflow:hidden`), 'padding:32px 12px'), `width:100%;background:${emailTokens.colors.canvas}`)}
|
|
85
|
+
</body>
|
|
86
|
+
</html>`;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=email-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-template.js","sourceRoot":"","sources":["../../src/services/email-template.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.service.d.ts","sourceRoot":"","sources":["../../src/services/email.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"email.service.d.ts","sourceRoot":"","sources":["../../src/services/email.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAwCxD,wBAAsB,SAAS,CAC7B,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrD,OAAO,CAAC,IAAI,CAAC,CASf;AAMD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACtB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAWnC;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAWnC;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAYnC;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACtB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAWnC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { alertBox, ctaButton, detailBox, layout, paragraph, sectionLabel, spacer } from './email-template.js';
|
|
1
2
|
// Lazy Ethereal singleton — created once on first send in dev
|
|
2
3
|
let _devSend = null;
|
|
3
4
|
let _devSendPromise = null;
|
|
@@ -47,77 +48,24 @@ export function buildWelcomeEmail(config, args) {
|
|
|
47
48
|
const custom = config.email?.templates?.welcome?.(args);
|
|
48
49
|
return {
|
|
49
50
|
subject: custom?.subject ?? 'Welcome — your account is ready',
|
|
50
|
-
html: custom?.html ??
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<td style="padding:32px 32px 0">
|
|
57
|
-
<p style="margin:0 0 4px;font-size:12px;font-weight:600;color:#6b7280;font-family:sans-serif;text-transform:uppercase;letter-spacing:0.05em">Dyrected</p>
|
|
58
|
-
<h1 style="margin:0 0 24px;font-size:22px;font-weight:700;color:#111827;font-family:sans-serif">Welcome!</h1>
|
|
59
|
-
</td>
|
|
60
|
-
</tr>
|
|
61
|
-
<tr>
|
|
62
|
-
<td style="padding:0 32px">
|
|
63
|
-
<p style="margin:0 0 12px;font-size:14px;color:#4b5563;line-height:1.6;font-family:sans-serif">Your account has been created. You can now log in with:</p>
|
|
64
|
-
<table cellpadding="0" cellspacing="0" border="0" style="width:100%;background-color:#f3f4f6;border-radius:6px;table-layout:fixed">
|
|
65
|
-
<tr>
|
|
66
|
-
<td style="padding:12px 16px;font-size:14px;font-weight:600;color:#111827;font-family:sans-serif;word-break:break-all">
|
|
67
|
-
${args.email}
|
|
68
|
-
</td>
|
|
69
|
-
</tr>
|
|
70
|
-
</table>
|
|
71
|
-
</td>
|
|
72
|
-
</tr>
|
|
73
|
-
<tr>
|
|
74
|
-
<td style="padding:32px">
|
|
75
|
-
<p style="margin:0;font-size:12px;color:#9ca3af;font-family:sans-serif">If you didn't create this account, you can safely ignore this email.</p>
|
|
76
|
-
</td>
|
|
77
|
-
</tr>
|
|
78
|
-
</table>
|
|
79
|
-
</td>
|
|
80
|
-
</tr>
|
|
81
|
-
</table>`,
|
|
51
|
+
html: custom?.html ?? layout({
|
|
52
|
+
preheader: 'Your Dyrected account is ready.',
|
|
53
|
+
title: 'Welcome — your account is ready',
|
|
54
|
+
content: `${paragraph('Your account has been created. You can now log in with:')}${detailBox(args.email)}`,
|
|
55
|
+
footer: "If you didn't create this account, you can safely ignore this email.",
|
|
56
|
+
}),
|
|
82
57
|
};
|
|
83
58
|
}
|
|
84
59
|
export function buildInviteEmail(config, args) {
|
|
85
60
|
const custom = config.email?.templates?.invite?.(args);
|
|
86
61
|
return {
|
|
87
62
|
subject: custom?.subject ?? "You've been invited",
|
|
88
|
-
html: custom?.html ??
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
<td style="padding:32px 32px 0">
|
|
95
|
-
<p style="margin:0 0 4px;font-size:12px;font-weight:600;color:#6b7280;font-family:sans-serif;text-transform:uppercase;letter-spacing:0.05em">Dyrected</p>
|
|
96
|
-
<h1 style="margin:0 0 24px;font-size:22px;font-weight:700;color:#111827;font-family:sans-serif">You've been invited</h1>
|
|
97
|
-
</td>
|
|
98
|
-
</tr>
|
|
99
|
-
<tr>
|
|
100
|
-
<td style="padding:0 32px">
|
|
101
|
-
${args.invitedByEmail ? `<p style="margin:0 0 12px;font-size:14px;color:#4b5563;line-height:1.6;font-family:sans-serif">You were invited by <strong style="color:#111827">${args.invitedByEmail}</strong>.</p>` : ''}
|
|
102
|
-
<p style="margin:0 0 16px;font-size:14px;color:#4b5563;line-height:1.6;font-family:sans-serif">Use the token below to accept your invitation. It expires in 7 days.</p>
|
|
103
|
-
<table cellpadding="0" cellspacing="0" border="0" style="width:100%;background-color:#f3f4f6;border-radius:6px;table-layout:fixed">
|
|
104
|
-
<tr>
|
|
105
|
-
<td style="padding:12px 16px;font-family:monospace;font-size:12px;color:#374151;word-break:break-all;white-space:normal;line-height:1.4">
|
|
106
|
-
${args.token}
|
|
107
|
-
</td>
|
|
108
|
-
</tr>
|
|
109
|
-
</table>
|
|
110
|
-
</td>
|
|
111
|
-
</tr>
|
|
112
|
-
<tr>
|
|
113
|
-
<td style="padding:32px">
|
|
114
|
-
<p style="margin:0;font-size:12px;color:#9ca3af;font-family:sans-serif">If you weren't expecting this invitation, you can safely ignore this email.</p>
|
|
115
|
-
</td>
|
|
116
|
-
</tr>
|
|
117
|
-
</table>
|
|
118
|
-
</td>
|
|
119
|
-
</tr>
|
|
120
|
-
</table>`,
|
|
63
|
+
html: custom?.html ?? layout({
|
|
64
|
+
preheader: "You've been invited to join a Dyrected account.",
|
|
65
|
+
title: "You've been invited",
|
|
66
|
+
content: `${args.invitedByEmail ? paragraph(`You were invited by ${args.invitedByEmail}.`) : ''}${paragraph('Use the token below to accept your invitation. It expires in 7 days.')}${sectionLabel('Invitation token')}${detailBox(args.token, true)}`,
|
|
67
|
+
footer: "If you weren't expecting this invitation, you can safely ignore this email.",
|
|
68
|
+
}),
|
|
121
69
|
};
|
|
122
70
|
}
|
|
123
71
|
export function buildResetPasswordEmail(config, args) {
|
|
@@ -125,95 +73,24 @@ export function buildResetPasswordEmail(config, args) {
|
|
|
125
73
|
const resetLink = args.url;
|
|
126
74
|
return {
|
|
127
75
|
subject: custom?.subject ?? 'Reset your password',
|
|
128
|
-
html: custom?.html ??
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<td style="padding:32px 32px 0">
|
|
135
|
-
<p style="margin:0 0 4px;font-size:12px;font-weight:600;color:#6b7280;font-family:sans-serif;text-transform:uppercase;letter-spacing:0.05em">Dyrected</p>
|
|
136
|
-
<h1 style="margin:0 0 24px;font-size:22px;font-weight:700;color:#111827;font-family:sans-serif">Reset your password</h1>
|
|
137
|
-
</td>
|
|
138
|
-
</tr>
|
|
139
|
-
<tr>
|
|
140
|
-
<td style="padding:0 32px">
|
|
141
|
-
<p style="margin:0 0 24px;font-size:14px;color:#4b5563;line-height:1.6;font-family:sans-serif">We received a request to reset your password. Use the button below to set a new password. It will expire in 1 hour.</p>
|
|
142
|
-
${resetLink ? `
|
|
143
|
-
<table cellpadding="0" cellspacing="0" border="0" style="margin-bottom:24px">
|
|
144
|
-
<tr>
|
|
145
|
-
<td style="border-radius:6px;background-color:#111827">
|
|
146
|
-
<a href="${resetLink}" style="display:inline-block;padding:12px 28px;font-size:14px;font-weight:600;color:#ffffff;text-decoration:none;font-family:sans-serif;border-radius:6px">
|
|
147
|
-
Reset Password
|
|
148
|
-
</a>
|
|
149
|
-
</td>
|
|
150
|
-
</tr>
|
|
151
|
-
</table>
|
|
152
|
-
` : ''}
|
|
153
|
-
<p style="margin:0 0 8px;font-size:12px;color:#9ca3af;font-family:sans-serif">Or copy and paste this token manually in the admin dashboard:</p>
|
|
154
|
-
<table cellpadding="0" cellspacing="0" border="0" style="width:100%;background-color:#f3f4f6;border-radius:6px;table-layout:fixed">
|
|
155
|
-
<tr>
|
|
156
|
-
<td style="padding:12px 16px;font-family:monospace;font-size:12px;color:#374151;word-break:break-all;white-space:normal;line-height:1.4">
|
|
157
|
-
${args.token}
|
|
158
|
-
</td>
|
|
159
|
-
</tr>
|
|
160
|
-
</table>
|
|
161
|
-
</td>
|
|
162
|
-
</tr>
|
|
163
|
-
<tr>
|
|
164
|
-
<td style="padding:32px">
|
|
165
|
-
<p style="margin:0;font-size:12px;color:#9ca3af;font-family:sans-serif">If you didn't request a password reset, you can safely ignore this email.</p>
|
|
166
|
-
</td>
|
|
167
|
-
</tr>
|
|
168
|
-
</table>
|
|
169
|
-
</td>
|
|
170
|
-
</tr>
|
|
171
|
-
</table>`,
|
|
76
|
+
html: custom?.html ?? layout({
|
|
77
|
+
preheader: 'Reset your Dyrected password.',
|
|
78
|
+
title: 'Reset your password',
|
|
79
|
+
content: `${paragraph('We received a request to reset your password. The reset link and token expire in 1 hour.')}${resetLink ? ctaButton('Reset password', resetLink) : ''}${sectionLabel(resetLink ? 'Or use this token' : 'Reset token')}${detailBox(args.token, true)}`,
|
|
80
|
+
footer: "If you didn't request a password reset, you can safely ignore this email.",
|
|
81
|
+
}),
|
|
172
82
|
};
|
|
173
83
|
}
|
|
174
84
|
export function buildPasswordChangedEmail(config, args) {
|
|
175
85
|
const custom = config.email?.templates?.passwordChanged?.(args);
|
|
176
86
|
return {
|
|
177
87
|
subject: custom?.subject ?? 'Your password has been changed',
|
|
178
|
-
html: custom?.html ??
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
<td style="padding:32px 32px 0">
|
|
185
|
-
<p style="margin:0 0 4px;font-size:12px;font-weight:600;color:#6b7280;font-family:sans-serif;text-transform:uppercase;letter-spacing:0.05em">Dyrected</p>
|
|
186
|
-
<h1 style="margin:0 0 24px;font-size:22px;font-weight:700;color:#111827;font-family:sans-serif">Password changed</h1>
|
|
187
|
-
</td>
|
|
188
|
-
</tr>
|
|
189
|
-
<tr>
|
|
190
|
-
<td style="padding:0 32px">
|
|
191
|
-
<p style="margin:0 0 12px;font-size:14px;color:#4b5563;line-height:1.6;font-family:sans-serif">The password for your account was just changed:</p>
|
|
192
|
-
<table cellpadding="0" cellspacing="0" border="0" style="width:100%;background-color:#f3f4f6;border-radius:6px;table-layout:fixed">
|
|
193
|
-
<tr>
|
|
194
|
-
<td style="padding:12px 16px;font-size:14px;font-weight:600;color:#111827;font-family:sans-serif;word-break:break-all">
|
|
195
|
-
${args.email}
|
|
196
|
-
</td>
|
|
197
|
-
</tr>
|
|
198
|
-
</table>
|
|
199
|
-
<table cellpadding="0" cellspacing="0" border="0" style="width:100%;margin-top:16px;background-color:#fef2f2;border-radius:6px;border:1px solid #fecaca;table-layout:fixed">
|
|
200
|
-
<tr>
|
|
201
|
-
<td style="padding:12px 16px;font-size:13px;color:#b91c1c;line-height:1.5;font-family:sans-serif">
|
|
202
|
-
If you did not make this change, please contact support immediately.
|
|
203
|
-
</td>
|
|
204
|
-
</tr>
|
|
205
|
-
</table>
|
|
206
|
-
</td>
|
|
207
|
-
</tr>
|
|
208
|
-
<tr>
|
|
209
|
-
<td style="padding:32px">
|
|
210
|
-
<p style="margin:0;font-size:12px;color:#9ca3af;font-family:sans-serif">This is an automated security notification.</p>
|
|
211
|
-
</td>
|
|
212
|
-
</tr>
|
|
213
|
-
</table>
|
|
214
|
-
</td>
|
|
215
|
-
</tr>
|
|
216
|
-
</table>`,
|
|
88
|
+
html: custom?.html ?? layout({
|
|
89
|
+
preheader: 'Your Dyrected password was changed.',
|
|
90
|
+
title: 'Password changed',
|
|
91
|
+
content: `${paragraph('The password for this account was just changed:')}${detailBox(args.email)}${spacer()}${alertBox('If you did not make this change, please contact support immediately.')}`,
|
|
92
|
+
footer: 'This is an automated security notification.',
|
|
93
|
+
}),
|
|
217
94
|
};
|
|
218
95
|
}
|
|
219
96
|
//# sourceMappingURL=email.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.service.js","sourceRoot":"","sources":["../../src/services/email.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"email.service.js","sourceRoot":"","sources":["../../src/services/email.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"population.service.d.ts","sourceRoot":"","sources":["../../src/services/population.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"population.service.d.ts","sourceRoot":"","sources":["../../src/services/population.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAInG,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,EAAE,CAAkB;IAC5B,OAAO,CAAC,WAAW,CAAqB;gBAE5B,EAAE,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE;IAKhE;;OAEG;IACG,QAAQ,CAAC,IAAI,EAAE;QACnB,IAAI,EAAE,GAAG,CAAC;QACV,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,GAAG,CAAC;IAgLhB;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;CAe3G"}
|