@antelopejs/dms-mailing 0.7.0 → 0.7.1
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/README.md +8 -7
- package/dist/constants.d.ts +0 -2
- package/dist/constants.js +1 -3
- package/dist/constants.js.map +1 -1
- package/dist/data/templates-table.js +1 -8
- package/dist/data/templates-table.js.map +1 -1
- package/dist/db/models/index.d.ts +0 -1
- package/dist/db/models/index.js +0 -1
- package/dist/db/models/index.js.map +1 -1
- package/dist/db/models/send-events.model.js +2 -3
- package/dist/db/models/send-events.model.js.map +1 -1
- package/dist/db/models/sends.model.js +2 -3
- package/dist/db/models/sends.model.js.map +1 -1
- package/dist/db/models/templates.model.js +2 -9
- package/dist/db/models/templates.model.js.map +1 -1
- package/dist/db/tables/index.d.ts +0 -1
- package/dist/db/tables/index.js +0 -1
- package/dist/db/tables/index.js.map +1 -1
- package/dist/db/tables/settings.table.d.ts +0 -1
- package/dist/db/tables/settings.table.js +0 -4
- package/dist/db/tables/settings.table.js.map +1 -1
- package/dist/db/tables/templates.table.d.ts +0 -3
- package/dist/db/tables/templates.table.js +1 -13
- package/dist/db/tables/templates.table.js.map +1 -1
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/index.js +0 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/implementations/dms-mailing/index.d.ts +3 -4
- package/dist/implementations/dms-mailing/index.js +8 -9
- package/dist/implementations/dms-mailing/index.js.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/pages/settings/form.js +8 -0
- package/dist/pages/settings/form.js.map +1 -1
- package/dist/routes/templates.js +2 -6
- package/dist/routes/templates.js.map +1 -1
- package/dist/services/declarations.js +42 -184
- package/dist/services/declarations.js.map +1 -1
- package/dist/services/send-import.js +0 -2
- package/dist/services/send-import.js.map +1 -1
- package/dist/services/settings.d.ts +1 -1
- package/dist/services/settings.js +1 -5
- package/dist/services/settings.js.map +1 -1
- package/dist/services/templates.d.ts +2 -2
- package/dist/services/templates.js +48 -27
- package/dist/services/templates.js.map +1 -1
- package/dist/types/settings.d.ts +2 -0
- package/dist/types/settings.js +8 -1
- package/dist/types/settings.js.map +1 -1
- package/dist/validation/settings.schema.d.ts +16 -16
- package/frontend-vue/package.json +1 -1
- package/package.json +6 -6
- package/skills/dms-mailing/SKILL.md +8 -14
- package/src/constants.ts +0 -2
- package/src/data/templates-table.ts +3 -15
- package/src/db/models/index.ts +0 -1
- package/src/db/models/send-events.model.ts +2 -2
- package/src/db/models/sends.model.ts +7 -8
- package/src/db/models/templates.model.ts +3 -13
- package/src/db/tables/index.ts +0 -1
- package/src/db/tables/settings.table.ts +0 -8
- package/src/db/tables/templates.table.ts +2 -16
- package/src/hooks/index.ts +0 -1
- package/src/implementations/dms-mailing/index.ts +5 -18
- package/src/index.ts +1 -6
- package/src/pages/settings/form.ts +16 -0
- package/src/routes/templates.ts +11 -13
- package/src/services/declarations.ts +64 -318
- package/src/services/send-import.ts +2 -2
- package/src/services/settings.ts +3 -10
- package/src/services/templates.ts +59 -44
- package/src/types/settings.ts +20 -0
- package/dist/db/models/template-slugs.model.d.ts +0 -27
- package/dist/db/models/template-slugs.model.js +0 -43
- package/dist/db/models/template-slugs.model.js.map +0 -1
- package/dist/db/tables/template-slugs.table.d.ts +0 -7
- package/dist/db/tables/template-slugs.table.js +0 -35
- package/dist/db/tables/template-slugs.table.js.map +0 -1
- package/dist/hooks/database-ready.d.ts +0 -7
- package/dist/hooks/database-ready.js +0 -26
- package/dist/hooks/database-ready.js.map +0 -1
- package/dist/services/fingerprint.d.ts +0 -1
- package/dist/services/fingerprint.js +0 -20
- package/dist/services/fingerprint.js.map +0 -1
- package/src/db/models/template-slugs.model.ts +0 -61
- package/src/db/tables/template-slugs.table.ts +0 -29
- package/src/hooks/database-ready.ts +0 -47
- package/src/services/fingerprint.ts +0 -22
package/src/types/settings.ts
CHANGED
|
@@ -52,3 +52,23 @@ export const DEFAULT_CATEGORIES: TemplateCategory[] = [
|
|
|
52
52
|
icon: "i-ph-textbox",
|
|
53
53
|
},
|
|
54
54
|
];
|
|
55
|
+
|
|
56
|
+
export const DEFAULT_FALLBACK_LOCALE = "en";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The module defaults: what a tenant's settings start with, and what the
|
|
60
|
+
* settings form offers to put back ("Use default"). The sender is empty and
|
|
61
|
+
* the webhook secret is drawn per tenant, so neither has one.
|
|
62
|
+
*/
|
|
63
|
+
export const MODULE_DEFAULT_SETTINGS: Pick<
|
|
64
|
+
MailingSettingsValues,
|
|
65
|
+
| "fallbackLocale"
|
|
66
|
+
| "logRetentionDays"
|
|
67
|
+
| "blockOnMissingVariables"
|
|
68
|
+
| "categories"
|
|
69
|
+
> = {
|
|
70
|
+
fallbackLocale: DEFAULT_FALLBACK_LOCALE,
|
|
71
|
+
logRetentionDays: 90,
|
|
72
|
+
blockOnMissingVariables: false,
|
|
73
|
+
categories: DEFAULT_CATEGORIES,
|
|
74
|
+
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { MailingTemplateSlug } from "../tables";
|
|
2
|
-
export declare function templateSlugIdOf(tenantId: string, slug: string): string;
|
|
3
|
-
declare const TemplateSlugModel_base: {
|
|
4
|
-
new (database: import("@antelopejs/interface-database").SchemaInstance<any>): {
|
|
5
|
-
readonly table: import("@antelopejs/interface-database").Table<MailingTemplateSlug>;
|
|
6
|
-
readonly database: import("@antelopejs/interface-database").SchemaInstance<any>;
|
|
7
|
-
get(id: string): PromiseLike<MailingTemplateSlug | undefined>;
|
|
8
|
-
getBy(index: keyof MailingTemplateSlug, ...keys: any[]): PromiseLike<MailingTemplateSlug[]>;
|
|
9
|
-
getAll(): PromiseLike<MailingTemplateSlug[]>;
|
|
10
|
-
insert(obj: import("@antelopejs/interface-database-decorators").DeepPartial<MailingTemplateSlug> | import("@antelopejs/interface-database-decorators").DeepPartial<MailingTemplateSlug>[], options?: import("@antelopejs/interface-database-decorators").ValidateOptions): Promise<string[]>;
|
|
11
|
-
update(id: string, obj: import("@antelopejs/interface-database-decorators").DeepPartial<MailingTemplateSlug>, options?: import("@antelopejs/interface-database-decorators").ValidateOptions): Promise<number>;
|
|
12
|
-
update(obj: import("@antelopejs/interface-database-decorators").DeepPartial<MailingTemplateSlug>, options?: import("@antelopejs/interface-database-decorators").ValidateOptions): Promise<number>;
|
|
13
|
-
delete(id: string): Promise<number>;
|
|
14
|
-
};
|
|
15
|
-
readonly schemaName: string;
|
|
16
|
-
readonly tableClass: import("@antelopejs/interface-database-decorators").Constructible<MailingTemplateSlug>;
|
|
17
|
-
fromPlainData(obj: any): MailingTemplateSlug;
|
|
18
|
-
fromDatabase(obj: any): MailingTemplateSlug | undefined;
|
|
19
|
-
toDatabase(obj: any): Record<string, any>;
|
|
20
|
-
validate(obj: unknown, options?: import("@antelopejs/interface-database-decorators").ValidateInputOptions): import("@antelopejs/interface-database-decorators").ValidationResult;
|
|
21
|
-
};
|
|
22
|
-
export declare class TemplateSlugModel extends TemplateSlugModel_base {
|
|
23
|
-
insertClaim(id: string, slug: string, templateId: string): Promise<boolean>;
|
|
24
|
-
dropClaimOf(id: string, templateId: string): Promise<void>;
|
|
25
|
-
releaseTemplates(templateIds: string[]): Promise<void>;
|
|
26
|
-
}
|
|
27
|
-
export {};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TemplateSlugModel = void 0;
|
|
4
|
-
exports.templateSlugIdOf = templateSlugIdOf;
|
|
5
|
-
const node_crypto_1 = require("node:crypto");
|
|
6
|
-
const interface_database_decorators_1 = require("@antelopejs/interface-database-decorators");
|
|
7
|
-
const tables_1 = require("../tables");
|
|
8
|
-
const ID_LENGTH = 32;
|
|
9
|
-
function templateSlugIdOf(tenantId, slug) {
|
|
10
|
-
return (0, node_crypto_1.createHash)("sha256")
|
|
11
|
-
.update(`${tenantId}\n${slug}`)
|
|
12
|
-
.digest("hex")
|
|
13
|
-
.slice(0, ID_LENGTH);
|
|
14
|
-
}
|
|
15
|
-
class TemplateSlugModel extends (0, interface_database_decorators_1.BasicDataModel)(tables_1.MailingTemplateSlug, tables_1.MAILING_TEMPLATE_SLUGS_TABLE_NAME) {
|
|
16
|
-
async insertClaim(id, slug, templateId) {
|
|
17
|
-
try {
|
|
18
|
-
await this.insert({ _id: id, slug, templateId });
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
if (await this.get(id))
|
|
23
|
-
return false;
|
|
24
|
-
throw error;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
async dropClaimOf(id, templateId) {
|
|
28
|
-
await this.table
|
|
29
|
-
.atomicMutation(id, {
|
|
30
|
-
type: "deleteIfEqual",
|
|
31
|
-
field: "templateId",
|
|
32
|
-
expectedValue: templateId,
|
|
33
|
-
})
|
|
34
|
-
.run();
|
|
35
|
-
}
|
|
36
|
-
async releaseTemplates(templateIds) {
|
|
37
|
-
if (!templateIds.length)
|
|
38
|
-
return;
|
|
39
|
-
await this.table.getAll(templateIds, "templateId").delete().run();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
exports.TemplateSlugModel = TemplateSlugModel;
|
|
43
|
-
//# sourceMappingURL=template-slugs.model.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"template-slugs.model.js","sourceRoot":"","sources":["../../../src/db/models/template-slugs.model.ts"],"names":[],"mappings":";;;AAcA,4CAKC;AAnBD,6CAAyC;AACzC,6FAA2E;AAC3E,sCAGmB;AAEnB,MAAM,SAAS,GAAG,EAAE,CAAC;AAOrB,SAAgB,gBAAgB,CAAC,QAAgB,EAAE,IAAY;IAC7D,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC;SACxB,MAAM,CAAC,GAAG,QAAQ,KAAK,IAAI,EAAE,CAAC;SAC9B,MAAM,CAAC,KAAK,CAAC;SACb,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AACzB,CAAC;AAED,MAAa,iBAAkB,SAAQ,IAAA,8CAAc,EACnD,4BAAmB,EACnB,0CAAiC,CAClC;IAMC,KAAK,CAAC,WAAW,CACf,EAAU,EACV,IAAY,EACZ,UAAkB;QAElB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;YACjD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO,KAAK,CAAC;YACrC,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAGD,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,UAAkB;QAC9C,MAAM,IAAI,CAAC,KAAK;aACb,cAAc,CAAC,EAAE,EAAE;YAClB,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,YAAY;YACnB,aAAa,EAAE,UAAU;SAC1B,CAAC;aACD,GAAG,EAAE,CAAC;IACX,CAAC;IAGD,KAAK,CAAC,gBAAgB,CAAC,WAAqB;QAC1C,IAAI,CAAC,WAAW,CAAC,MAAM;YAAE,OAAO;QAChC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC;IACpE,CAAC;CACF;AAvCD,8CAuCC"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.MailingTemplateSlug = exports.MAILING_TEMPLATE_SLUGS_TABLE_NAME = void 0;
|
|
13
|
-
const interface_database_decorators_1 = require("@antelopejs/interface-database-decorators");
|
|
14
|
-
const constants_1 = require("@antelopejs/interface-dms/constants");
|
|
15
|
-
exports.MAILING_TEMPLATE_SLUGS_TABLE_NAME = "mailing_template_slugs";
|
|
16
|
-
let MailingTemplateSlug = class MailingTemplateSlug extends interface_database_decorators_1.Table {
|
|
17
|
-
};
|
|
18
|
-
exports.MailingTemplateSlug = MailingTemplateSlug;
|
|
19
|
-
__decorate([
|
|
20
|
-
(0, interface_database_decorators_1.Field)("string"),
|
|
21
|
-
__metadata("design:type", String)
|
|
22
|
-
], MailingTemplateSlug.prototype, "_id", void 0);
|
|
23
|
-
__decorate([
|
|
24
|
-
(0, interface_database_decorators_1.Field)("string"),
|
|
25
|
-
__metadata("design:type", String)
|
|
26
|
-
], MailingTemplateSlug.prototype, "slug", void 0);
|
|
27
|
-
__decorate([
|
|
28
|
-
(0, interface_database_decorators_1.Index)(),
|
|
29
|
-
(0, interface_database_decorators_1.Field)("string"),
|
|
30
|
-
__metadata("design:type", String)
|
|
31
|
-
], MailingTemplateSlug.prototype, "templateId", void 0);
|
|
32
|
-
exports.MailingTemplateSlug = MailingTemplateSlug = __decorate([
|
|
33
|
-
(0, interface_database_decorators_1.RegisterTable)(exports.MAILING_TEMPLATE_SLUGS_TABLE_NAME, constants_1.TENANT_SCHEMA_NAME)
|
|
34
|
-
], MailingTemplateSlug);
|
|
35
|
-
//# sourceMappingURL=template-slugs.table.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"template-slugs.table.js","sourceRoot":"","sources":["../../../src/db/tables/template-slugs.table.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6FAKmD;AACnD,mEAAyE;AAE5D,QAAA,iCAAiC,GAAG,wBAAwB,CAAC;AASnE,IAAM,mBAAmB,GAAzB,MAAM,mBAAoB,SAAQ,qCAAK;CAW7C,CAAA;AAXY,kDAAmB;AAEtB;IADP,IAAA,qCAAK,EAAC,QAAQ,CAAC;;gDACI;AAGZ;IADP,IAAA,qCAAK,EAAC,QAAQ,CAAC;;iDACK;AAKb;IAFP,IAAA,qCAAK,GAAE;IACP,IAAA,qCAAK,EAAC,QAAQ,CAAC;;uDACW;8BAVhB,mBAAmB;IAD/B,IAAA,6CAAa,EAAC,yCAAiC,EAAE,8BAAkB,CAAC;GACxD,mBAAmB,CAW/B"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export interface ReadyGate {
|
|
2
|
-
open(): void;
|
|
3
|
-
after<Args extends unknown[], Result>(task: (...args: Args) => Promise<Result>): (...args: Args) => Promise<Result>;
|
|
4
|
-
}
|
|
5
|
-
export declare function createReadyGate(): ReadyGate;
|
|
6
|
-
export declare function registerDatabaseReady(): void;
|
|
7
|
-
export declare const afterDatabaseReady: ReadyGate["after"];
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.afterDatabaseReady = void 0;
|
|
4
|
-
exports.createReadyGate = createReadyGate;
|
|
5
|
-
exports.registerDatabaseReady = registerDatabaseReady;
|
|
6
|
-
const hooks_1 = require("@antelopejs/interface-dms/hooks");
|
|
7
|
-
function createReadyGate() {
|
|
8
|
-
let open = () => undefined;
|
|
9
|
-
const opened = new Promise((resolve) => {
|
|
10
|
-
open = resolve;
|
|
11
|
-
});
|
|
12
|
-
return {
|
|
13
|
-
open: () => open(),
|
|
14
|
-
after: (task) => async (...args) => {
|
|
15
|
-
await opened;
|
|
16
|
-
return task(...args);
|
|
17
|
-
},
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
const databaseGate = createReadyGate();
|
|
21
|
-
function registerDatabaseReady() {
|
|
22
|
-
(0, hooks_1.RegisterHook)(hooks_1.Hook.DATABASE_INITIALIZED, () => databaseGate.open());
|
|
23
|
-
}
|
|
24
|
-
const afterDatabaseReady = (task) => databaseGate.after(task);
|
|
25
|
-
exports.afterDatabaseReady = afterDatabaseReady;
|
|
26
|
-
//# sourceMappingURL=database-ready.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"database-ready.js","sourceRoot":"","sources":["../../src/hooks/database-ready.ts"],"names":[],"mappings":";;;AAWA,0CAcC;AAeD,sDAEC;AA1CD,2DAAqE;AAWrE,SAAgB,eAAe;IAC7B,IAAI,IAAI,GAAe,GAAG,EAAE,CAAC,SAAS,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC3C,IAAI,GAAG,OAAO,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,OAAO;QACL,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE;QAClB,KAAK,EACH,CAAC,IAAI,EAAE,EAAE,CACT,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE;YAChB,MAAM,MAAM,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QACvB,CAAC;KACJ,CAAC;AACJ,CAAC;AAOD,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;AAQvC,SAAgB,qBAAqB;IACnC,IAAA,oBAAY,EAAC,YAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;AACrE,CAAC;AAGM,MAAM,kBAAkB,GAAuB,CAAC,IAAI,EAAE,EAAE,CAC7D,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AADd,QAAA,kBAAkB,sBACJ"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function fingerprintOf(value: unknown): string;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.fingerprintOf = fingerprintOf;
|
|
4
|
-
const node_crypto_1 = require("node:crypto");
|
|
5
|
-
function stableJson(value) {
|
|
6
|
-
if (Array.isArray(value))
|
|
7
|
-
return `[${value.map(stableJson).join(",")}]`;
|
|
8
|
-
if (value && typeof value === "object") {
|
|
9
|
-
const entries = Object.entries(value)
|
|
10
|
-
.filter(([, entry]) => entry !== undefined)
|
|
11
|
-
.sort(([left], [right]) => Number(left > right) - Number(left < right))
|
|
12
|
-
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`);
|
|
13
|
-
return `{${entries.join(",")}}`;
|
|
14
|
-
}
|
|
15
|
-
return JSON.stringify(value);
|
|
16
|
-
}
|
|
17
|
-
function fingerprintOf(value) {
|
|
18
|
-
return (0, node_crypto_1.createHash)("sha256").update(stableJson(value)).digest("hex");
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=fingerprint.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../src/services/fingerprint.ts"],"names":[],"mappings":";;AAmBA,sCAEC;AArBD,6CAAyC;AAGzC,SAAS,UAAU,CAAC,KAAc;IAChC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACxE,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;aAC7D,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC;aAC1C,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;aACtE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC/B,CAAC;AAMD,SAAgB,aAAa,CAAC,KAAc;IAC1C,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { BasicDataModel } from "@antelopejs/interface-database-decorators";
|
|
3
|
-
import {
|
|
4
|
-
MAILING_TEMPLATE_SLUGS_TABLE_NAME,
|
|
5
|
-
MailingTemplateSlug,
|
|
6
|
-
} from "../tables";
|
|
7
|
-
|
|
8
|
-
const ID_LENGTH = 32;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* The id of a slug's claim, the same whoever creates the template. The tenant
|
|
12
|
-
* is part of it: a driver may keep every tenant's rows in one collection,
|
|
13
|
-
* where a key made of the slug alone would collide across tenants.
|
|
14
|
-
*/
|
|
15
|
-
export function templateSlugIdOf(tenantId: string, slug: string): string {
|
|
16
|
-
return createHash("sha256")
|
|
17
|
-
.update(`${tenantId}\n${slug}`)
|
|
18
|
-
.digest("hex")
|
|
19
|
-
.slice(0, ID_LENGTH);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export class TemplateSlugModel extends BasicDataModel(
|
|
23
|
-
MailingTemplateSlug,
|
|
24
|
-
MAILING_TEMPLATE_SLUGS_TABLE_NAME,
|
|
25
|
-
) {
|
|
26
|
-
/**
|
|
27
|
-
* Inserts the claim; false when the key is taken. Probed by state rather
|
|
28
|
-
* than by error, since the database interface exposes no typed conflict
|
|
29
|
-
* error: a claim present after a failed insert is the lost race.
|
|
30
|
-
*/
|
|
31
|
-
async insertClaim(
|
|
32
|
-
id: string,
|
|
33
|
-
slug: string,
|
|
34
|
-
templateId: string,
|
|
35
|
-
): Promise<boolean> {
|
|
36
|
-
try {
|
|
37
|
-
await this.insert({ _id: id, slug, templateId });
|
|
38
|
-
return true;
|
|
39
|
-
} catch (error) {
|
|
40
|
-
if (await this.get(id)) return false;
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Drops the claim if `templateId` still holds it, never a newer one. */
|
|
46
|
-
async dropClaimOf(id: string, templateId: string): Promise<void> {
|
|
47
|
-
await this.table
|
|
48
|
-
.atomicMutation(id, {
|
|
49
|
-
type: "deleteIfEqual",
|
|
50
|
-
field: "templateId",
|
|
51
|
-
expectedValue: templateId,
|
|
52
|
-
})
|
|
53
|
-
.run();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Frees the slugs the templates held. */
|
|
57
|
-
async releaseTemplates(templateIds: string[]): Promise<void> {
|
|
58
|
-
if (!templateIds.length) return;
|
|
59
|
-
await this.table.getAll(templateIds, "templateId").delete().run();
|
|
60
|
-
}
|
|
61
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Field,
|
|
3
|
-
Index,
|
|
4
|
-
RegisterTable,
|
|
5
|
-
Table,
|
|
6
|
-
} from "@antelopejs/interface-database-decorators";
|
|
7
|
-
import { TENANT_SCHEMA_NAME } from "@antelopejs/interface-dms/constants";
|
|
8
|
-
|
|
9
|
-
export const MAILING_TEMPLATE_SLUGS_TABLE_NAME = "mailing_template_slugs";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The template a slug belongs to. Its id derives from the tenant and the
|
|
13
|
-
* slug, so the primary key is what keeps a slug to one template: the database
|
|
14
|
-
* interface declares no unique secondary index, and two creations racing
|
|
15
|
-
* past the free-slug check both insert their template, but only one claim.
|
|
16
|
-
*/
|
|
17
|
-
@RegisterTable(MAILING_TEMPLATE_SLUGS_TABLE_NAME, TENANT_SCHEMA_NAME)
|
|
18
|
-
export class MailingTemplateSlug extends Table {
|
|
19
|
-
@Field("string")
|
|
20
|
-
declare _id: string;
|
|
21
|
-
|
|
22
|
-
@Field("string")
|
|
23
|
-
declare slug: string;
|
|
24
|
-
|
|
25
|
-
/** The template holding the slug; a deleted one leaves the claim free. */
|
|
26
|
-
@Index()
|
|
27
|
-
@Field("string")
|
|
28
|
-
declare templateId: string;
|
|
29
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { Hook, RegisterHook } from "@antelopejs/interface-dms/hooks";
|
|
2
|
-
|
|
3
|
-
/** A promise opened once, and the calls that wait for it. */
|
|
4
|
-
export interface ReadyGate {
|
|
5
|
-
open(): void;
|
|
6
|
-
/** `task`, run once the gate is open. */
|
|
7
|
-
after<Args extends unknown[], Result>(
|
|
8
|
-
task: (...args: Args) => Promise<Result>,
|
|
9
|
-
): (...args: Args) => Promise<Result>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function createReadyGate(): ReadyGate {
|
|
13
|
-
let open: () => void = () => undefined;
|
|
14
|
-
const opened = new Promise<void>((resolve) => {
|
|
15
|
-
open = resolve;
|
|
16
|
-
});
|
|
17
|
-
return {
|
|
18
|
-
open: () => open(),
|
|
19
|
-
after:
|
|
20
|
-
(task) =>
|
|
21
|
-
async (...args) => {
|
|
22
|
-
await opened;
|
|
23
|
-
return task(...args);
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Opens once the DMS reports its schemas registered, which it does from its
|
|
30
|
-
* own `start()`. Modules construct and start in parallel, so a project
|
|
31
|
-
* declaring its content early waits here instead of failing.
|
|
32
|
-
*/
|
|
33
|
-
const databaseGate = createReadyGate();
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* The DMS fires the hook on every start and runs a handler registered after
|
|
37
|
-
* that at once: whatever the load order, the first firing opens the gate.
|
|
38
|
-
* Its handlers run in series, so this one is registered first thing in
|
|
39
|
-
* `construct()`, ahead of handlers that may wait on the gate.
|
|
40
|
-
*/
|
|
41
|
-
export function registerDatabaseReady(): void {
|
|
42
|
-
RegisterHook(Hook.DATABASE_INITIALIZED, () => databaseGate.open());
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** `task`, run once the database is ready. */
|
|
46
|
-
export const afterDatabaseReady: ReadyGate["after"] = (task) =>
|
|
47
|
-
databaseGate.after(task);
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
|
|
3
|
-
/** JSON with object keys sorted, so equal values always serialize alike. */
|
|
4
|
-
function stableJson(value: unknown): string {
|
|
5
|
-
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
6
|
-
if (value && typeof value === "object") {
|
|
7
|
-
const entries = Object.entries(value as Record<string, unknown>)
|
|
8
|
-
.filter(([, entry]) => entry !== undefined)
|
|
9
|
-
.sort(([left], [right]) => Number(left > right) - Number(left < right))
|
|
10
|
-
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`);
|
|
11
|
-
return `{${entries.join(",")}}`;
|
|
12
|
-
}
|
|
13
|
-
return JSON.stringify(value);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* A short digest of `value` that ignores key order: what a declaration
|
|
18
|
-
* remembers of what it wrote, to tell later whether a user changed it.
|
|
19
|
-
*/
|
|
20
|
-
export function fingerprintOf(value: unknown): string {
|
|
21
|
-
return createHash("sha256").update(stableJson(value)).digest("hex");
|
|
22
|
-
}
|