@unifiedcommerce/plugin-notifications 0.0.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/dist/adapters/console.d.ts +17 -0
- package/dist/adapters/console.d.ts.map +1 -0
- package/dist/adapters/console.js +44 -0
- package/dist/adapters/types.d.ts +62 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/routes/notifications.d.ts +11 -0
- package/dist/routes/notifications.d.ts.map +1 -0
- package/dist/routes/notifications.js +170 -0
- package/dist/schema.d.ts +624 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +59 -0
- package/dist/services/notification-service.d.ts +52 -0
- package/dist/services/notification-service.d.ts.map +1 -0
- package/dist/services/notification-service.js +220 -0
- package/dist/services/preference-service.d.ts +23 -0
- package/dist/services/preference-service.d.ts.map +1 -0
- package/dist/services/preference-service.js +69 -0
- package/dist/services/print-service.d.ts +32 -0
- package/dist/services/print-service.d.ts.map +1 -0
- package/dist/services/print-service.js +91 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/package.json +37 -0
- package/src/adapters/console.ts +52 -0
- package/src/adapters/types.ts +56 -0
- package/src/index.ts +53 -0
- package/src/routes/notifications.ts +199 -0
- package/src/schema.ts +67 -0
- package/src/services/notification-service.ts +270 -0
- package/src/services/preference-service.ts +92 -0
- package/src/services/print-service.ts +99 -0
- package/src/types.ts +16 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SMSAdapter, PushAdapter, PrintAdapter } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Console SMS adapter — logs SMS messages to stdout.
|
|
4
|
+
* Use for development and testing. Replace with a real adapter (e.g. Twilio) in production.
|
|
5
|
+
*/
|
|
6
|
+
export declare function consoleSMSAdapter(): SMSAdapter;
|
|
7
|
+
/**
|
|
8
|
+
* Console Push adapter — logs push notifications to stdout.
|
|
9
|
+
* Use for development and testing. Replace with a real adapter (e.g. FCM) in production.
|
|
10
|
+
*/
|
|
11
|
+
export declare function consolePushAdapter(): PushAdapter;
|
|
12
|
+
/**
|
|
13
|
+
* Console Print adapter — logs print jobs to stdout.
|
|
14
|
+
* Use for development and testing. Replace with a real adapter (e.g. ESC/POS) in production.
|
|
15
|
+
*/
|
|
16
|
+
export declare function consolePrintAdapter(): PrintAdapter;
|
|
17
|
+
//# sourceMappingURL=console.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../src/adapters/console.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAErE;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,UAAU,CAS9C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,WAAW,CAWhD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,YAAY,CAWlD"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { Ok } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* Console SMS adapter — logs SMS messages to stdout.
|
|
5
|
+
* Use for development and testing. Replace with a real adapter (e.g. Twilio) in production.
|
|
6
|
+
*/
|
|
7
|
+
export function consoleSMSAdapter() {
|
|
8
|
+
return {
|
|
9
|
+
providerId: "console",
|
|
10
|
+
async send(params) {
|
|
11
|
+
const messageId = `sms_${randomUUID()}`;
|
|
12
|
+
console.log(`[SMS:console] to=${params.to} body="${params.body}" messageId=${messageId}`);
|
|
13
|
+
return Ok({ messageId });
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Console Push adapter — logs push notifications to stdout.
|
|
19
|
+
* Use for development and testing. Replace with a real adapter (e.g. FCM) in production.
|
|
20
|
+
*/
|
|
21
|
+
export function consolePushAdapter() {
|
|
22
|
+
return {
|
|
23
|
+
providerId: "console",
|
|
24
|
+
async send(params) {
|
|
25
|
+
const messageId = `push_${randomUUID()}`;
|
|
26
|
+
console.log(`[Push:console] token=${params.deviceToken} title="${params.title}" body="${params.body}" messageId=${messageId}`);
|
|
27
|
+
return Ok({ messageId });
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Console Print adapter — logs print jobs to stdout.
|
|
33
|
+
* Use for development and testing. Replace with a real adapter (e.g. ESC/POS) in production.
|
|
34
|
+
*/
|
|
35
|
+
export function consolePrintAdapter() {
|
|
36
|
+
return {
|
|
37
|
+
providerId: "console",
|
|
38
|
+
async print(params) {
|
|
39
|
+
const jobId = `print_${randomUUID()}`;
|
|
40
|
+
console.log(`[Print:console] printer=${params.printerId} format=${params.format} jobId=${jobId}`);
|
|
41
|
+
return Ok({ jobId });
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Result } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* SMS Adapter interface.
|
|
4
|
+
*
|
|
5
|
+
* Implement this interface to integrate with an SMS provider (e.g. Twilio, Vonage).
|
|
6
|
+
* The `send` method delivers a text message to the given phone number.
|
|
7
|
+
*/
|
|
8
|
+
export interface SMSAdapter {
|
|
9
|
+
/** Unique provider identifier (e.g. "twilio", "vonage", "console"). */
|
|
10
|
+
providerId: string;
|
|
11
|
+
/** Send an SMS message to the given phone number. */
|
|
12
|
+
send(params: {
|
|
13
|
+
to: string;
|
|
14
|
+
body: string;
|
|
15
|
+
}): Promise<Result<{
|
|
16
|
+
messageId: string;
|
|
17
|
+
}>>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Push Notification Adapter interface.
|
|
21
|
+
*
|
|
22
|
+
* Implement this interface to integrate with a push notification service
|
|
23
|
+
* (e.g. Firebase Cloud Messaging, Apple Push Notification Service).
|
|
24
|
+
*/
|
|
25
|
+
export interface PushAdapter {
|
|
26
|
+
/** Unique provider identifier (e.g. "fcm", "apns", "console"). */
|
|
27
|
+
providerId: string;
|
|
28
|
+
/** Send a push notification to the given device token. */
|
|
29
|
+
send(params: {
|
|
30
|
+
deviceToken: string;
|
|
31
|
+
title: string;
|
|
32
|
+
body: string;
|
|
33
|
+
data?: Record<string, unknown>;
|
|
34
|
+
}): Promise<Result<{
|
|
35
|
+
messageId: string;
|
|
36
|
+
}>>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Print Adapter interface.
|
|
40
|
+
*
|
|
41
|
+
* Implement this interface to integrate with a receipt/label printer
|
|
42
|
+
* (e.g. Epson ESC/POS, Star Line Mode printers).
|
|
43
|
+
*/
|
|
44
|
+
export interface PrintAdapter {
|
|
45
|
+
/** Unique provider identifier (e.g. "esc_pos", "star", "console"). */
|
|
46
|
+
providerId: string;
|
|
47
|
+
/** Send a print job to the given printer. */
|
|
48
|
+
print(params: {
|
|
49
|
+
printerId: string;
|
|
50
|
+
content: Record<string, unknown>;
|
|
51
|
+
format: "esc_pos" | "star_line" | "label";
|
|
52
|
+
}): Promise<Result<{
|
|
53
|
+
jobId: string;
|
|
54
|
+
}>>;
|
|
55
|
+
}
|
|
56
|
+
/** Configuration object for notification adapters. */
|
|
57
|
+
export interface NotificationAdapters {
|
|
58
|
+
sms?: SMSAdapter;
|
|
59
|
+
push?: PushAdapter;
|
|
60
|
+
print?: PrintAdapter;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,IAAI,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CACpF;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,IAAI,CAAC,MAAM,EAAE;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CAC5C;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,KAAK,CAAC,MAAM,EAAE;QACZ,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;KAC3C,GAAG,OAAO,CAAC,MAAM,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CACxC;AAED,sDAAsD;AACtD,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { NotificationAdapters } from "./adapters/types";
|
|
2
|
+
export type { Db } from "./types";
|
|
3
|
+
export type { NotificationTemplate, CustomerNotificationPref, NotificationLogEntry, PrintJob, Result, ResultErr, Channel, PrefChannel, NotificationStatus, PrintJobStatus, PrintJobType } from "./types";
|
|
4
|
+
export type { SMSAdapter, PushAdapter, PrintAdapter, NotificationAdapters } from "./adapters/types";
|
|
5
|
+
export { NotificationService } from "./services/notification-service";
|
|
6
|
+
export { PreferenceService } from "./services/preference-service";
|
|
7
|
+
export { PrintService } from "./services/print-service";
|
|
8
|
+
export { consoleSMSAdapter, consolePushAdapter, consolePrintAdapter } from "./adapters/console";
|
|
9
|
+
/**
|
|
10
|
+
* Notifications Plugin (RFC-030)
|
|
11
|
+
*
|
|
12
|
+
* Provides:
|
|
13
|
+
* - Notification template CRUD with Handlebars-style rendering
|
|
14
|
+
* - Multi-channel dispatch (email, SMS, push, print)
|
|
15
|
+
* - Customer notification preferences (opt-out model)
|
|
16
|
+
* - Notification log with status tracking
|
|
17
|
+
* - Print job management (receipt, label, sticker, KOT)
|
|
18
|
+
* - Pluggable adapter architecture for SMS, Push, and Print providers
|
|
19
|
+
*
|
|
20
|
+
* @param adapters - Optional adapter configuration. Pass console adapters for dev,
|
|
21
|
+
* or real adapters (Twilio, FCM, ESC/POS) for production.
|
|
22
|
+
*/
|
|
23
|
+
export declare function notificationsPlugin(adapters?: NotificationAdapters): import("@unifiedcommerce/core").CommercePlugin;
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAG7D,YAAY,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAClC,YAAY,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACzM,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACpG,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEhG;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,CAAC,EAAE,oBAAoB,kDAoBlE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { defineCommercePlugin } from "@unifiedcommerce/core";
|
|
2
|
+
import { notificationTemplates, customerNotificationPrefs, notificationLog, printJobs } from "./schema";
|
|
3
|
+
import { NotificationService } from "./services/notification-service";
|
|
4
|
+
import { PreferenceService } from "./services/preference-service";
|
|
5
|
+
import { PrintService } from "./services/print-service";
|
|
6
|
+
import { buildNotificationRoutes } from "./routes/notifications";
|
|
7
|
+
export { NotificationService } from "./services/notification-service";
|
|
8
|
+
export { PreferenceService } from "./services/preference-service";
|
|
9
|
+
export { PrintService } from "./services/print-service";
|
|
10
|
+
export { consoleSMSAdapter, consolePushAdapter, consolePrintAdapter } from "./adapters/console";
|
|
11
|
+
/**
|
|
12
|
+
* Notifications Plugin (RFC-030)
|
|
13
|
+
*
|
|
14
|
+
* Provides:
|
|
15
|
+
* - Notification template CRUD with Handlebars-style rendering
|
|
16
|
+
* - Multi-channel dispatch (email, SMS, push, print)
|
|
17
|
+
* - Customer notification preferences (opt-out model)
|
|
18
|
+
* - Notification log with status tracking
|
|
19
|
+
* - Print job management (receipt, label, sticker, KOT)
|
|
20
|
+
* - Pluggable adapter architecture for SMS, Push, and Print providers
|
|
21
|
+
*
|
|
22
|
+
* @param adapters - Optional adapter configuration. Pass console adapters for dev,
|
|
23
|
+
* or real adapters (Twilio, FCM, ESC/POS) for production.
|
|
24
|
+
*/
|
|
25
|
+
export function notificationsPlugin(adapters) {
|
|
26
|
+
return defineCommercePlugin({
|
|
27
|
+
id: "notifications",
|
|
28
|
+
version: "1.0.0",
|
|
29
|
+
permissions: [
|
|
30
|
+
{ scope: "notifications:admin", description: "Manage notification templates, send notifications, view log, manage print jobs." },
|
|
31
|
+
{ scope: "notifications:write", description: "Set customer notification preferences." },
|
|
32
|
+
{ scope: "notifications:read", description: "View customer notification preferences." },
|
|
33
|
+
],
|
|
34
|
+
schema: () => ({ notificationTemplates, customerNotificationPrefs, notificationLog, printJobs }),
|
|
35
|
+
hooks: () => [],
|
|
36
|
+
routes: (ctx) => {
|
|
37
|
+
const db = ctx.database.db;
|
|
38
|
+
if (!db)
|
|
39
|
+
return [];
|
|
40
|
+
const notifService = new NotificationService(db, adapters);
|
|
41
|
+
const prefService = new PreferenceService(db);
|
|
42
|
+
const printService = new PrintService(db, adapters?.print);
|
|
43
|
+
return buildNotificationRoutes(notifService, prefService, printService, ctx);
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { NotificationService } from "../services/notification-service";
|
|
2
|
+
import type { PreferenceService } from "../services/preference-service";
|
|
3
|
+
import type { PrintService } from "../services/print-service";
|
|
4
|
+
import type { PluginRouteRegistration } from "@unifiedcommerce/core";
|
|
5
|
+
export declare function buildNotificationRoutes(notifService: NotificationService, prefService: PreferenceService, printService: PrintService, ctx: {
|
|
6
|
+
services?: Record<string, unknown>;
|
|
7
|
+
database?: {
|
|
8
|
+
db: unknown;
|
|
9
|
+
};
|
|
10
|
+
}): PluginRouteRegistration[];
|
|
11
|
+
//# sourceMappingURL=notifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/routes/notifications.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAErE,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,mBAAmB,EACjC,WAAW,EAAE,iBAAiB,EAC9B,YAAY,EAAE,YAAY,EAC1B,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACtE,uBAAuB,EAAE,CA0L3B"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { router } from "@unifiedcommerce/core";
|
|
2
|
+
import { z } from "@hono/zod-openapi";
|
|
3
|
+
export function buildNotificationRoutes(notifService, prefService, printService, ctx) {
|
|
4
|
+
// ── Template Routes ────────────────────────────────────────────────
|
|
5
|
+
const tmpl = router("Notification Templates", "/notifications/templates", ctx);
|
|
6
|
+
tmpl.post("/").summary("Create notification template").permission("notifications:admin")
|
|
7
|
+
.input(z.object({
|
|
8
|
+
event: z.string().min(1),
|
|
9
|
+
channel: z.enum(["email", "sms", "push", "print"]),
|
|
10
|
+
subject: z.string().optional(),
|
|
11
|
+
bodyTemplate: z.string().min(1),
|
|
12
|
+
}))
|
|
13
|
+
.handler(async ({ input, orgId }) => {
|
|
14
|
+
const body = input;
|
|
15
|
+
const result = await notifService.createTemplate(orgId, body);
|
|
16
|
+
if (!result.ok)
|
|
17
|
+
throw new Error(result.error);
|
|
18
|
+
return result.value;
|
|
19
|
+
});
|
|
20
|
+
tmpl.get("/").summary("List notification templates").permission("notifications:admin")
|
|
21
|
+
.query(z.object({
|
|
22
|
+
event: z.string().optional(),
|
|
23
|
+
channel: z.enum(["email", "sms", "push", "print"]).optional(),
|
|
24
|
+
}))
|
|
25
|
+
.handler(async ({ query, orgId }) => {
|
|
26
|
+
const q = query;
|
|
27
|
+
const result = await notifService.listTemplates(orgId, q);
|
|
28
|
+
if (!result.ok)
|
|
29
|
+
throw new Error(result.error);
|
|
30
|
+
return result.value;
|
|
31
|
+
});
|
|
32
|
+
tmpl.get("/{id}").summary("Get notification template").permission("notifications:admin")
|
|
33
|
+
.handler(async ({ params, orgId }) => {
|
|
34
|
+
const result = await notifService.getTemplate(orgId, params.id);
|
|
35
|
+
if (!result.ok)
|
|
36
|
+
throw new Error(result.error);
|
|
37
|
+
return result.value;
|
|
38
|
+
});
|
|
39
|
+
tmpl.patch("/{id}").summary("Update notification template").permission("notifications:admin")
|
|
40
|
+
.input(z.object({
|
|
41
|
+
subject: z.string().optional(),
|
|
42
|
+
bodyTemplate: z.string().optional(),
|
|
43
|
+
isActive: z.boolean().optional(),
|
|
44
|
+
}))
|
|
45
|
+
.handler(async ({ params, input, orgId }) => {
|
|
46
|
+
const body = input;
|
|
47
|
+
const result = await notifService.updateTemplate(orgId, params.id, body);
|
|
48
|
+
if (!result.ok)
|
|
49
|
+
throw new Error(result.error);
|
|
50
|
+
return result.value;
|
|
51
|
+
});
|
|
52
|
+
tmpl.delete("/{id}").summary("Soft-delete notification template").permission("notifications:admin")
|
|
53
|
+
.handler(async ({ params, orgId }) => {
|
|
54
|
+
const result = await notifService.deleteTemplate(orgId, params.id);
|
|
55
|
+
if (!result.ok)
|
|
56
|
+
throw new Error(result.error);
|
|
57
|
+
return result.value;
|
|
58
|
+
});
|
|
59
|
+
// ── Send Route ─────────────────────────────────────────────────────
|
|
60
|
+
const send = router("Notifications", "/notifications", ctx);
|
|
61
|
+
send.post("/send").summary("Send notification").permission("notifications:admin")
|
|
62
|
+
.input(z.object({
|
|
63
|
+
event: z.string().min(1),
|
|
64
|
+
recipient: z.string().min(1),
|
|
65
|
+
channel: z.enum(["email", "sms", "push", "print"]),
|
|
66
|
+
customerId: z.string().uuid().optional(),
|
|
67
|
+
data: z.record(z.string(), z.unknown()).optional(),
|
|
68
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
69
|
+
}))
|
|
70
|
+
.handler(async ({ input, orgId }) => {
|
|
71
|
+
const body = input;
|
|
72
|
+
const result = await notifService.send(orgId, body);
|
|
73
|
+
if (!result.ok)
|
|
74
|
+
throw new Error(result.error);
|
|
75
|
+
return result.value;
|
|
76
|
+
});
|
|
77
|
+
// ── Log Route ──────────────────────────────────────────────────────
|
|
78
|
+
send.get("/log").summary("Query notification log").permission("notifications:admin")
|
|
79
|
+
.query(z.object({
|
|
80
|
+
channel: z.string().optional(),
|
|
81
|
+
event: z.string().optional(),
|
|
82
|
+
status: z.enum(["queued", "sent", "delivered", "failed"]).optional(),
|
|
83
|
+
limit: z.coerce.number().int().positive().optional(),
|
|
84
|
+
}))
|
|
85
|
+
.handler(async ({ query, orgId }) => {
|
|
86
|
+
const q = query;
|
|
87
|
+
const result = await notifService.listLog(orgId, q);
|
|
88
|
+
if (!result.ok)
|
|
89
|
+
throw new Error(result.error);
|
|
90
|
+
return result.value;
|
|
91
|
+
});
|
|
92
|
+
// ── Preference Routes ──────────────────────────────────────────────
|
|
93
|
+
const pref = router("Notification Preferences", "/notifications/preferences", ctx);
|
|
94
|
+
pref.post("/").summary("Set customer notification preference").permission("notifications:write")
|
|
95
|
+
.input(z.object({
|
|
96
|
+
customerId: z.string().uuid(),
|
|
97
|
+
channel: z.enum(["email", "sms", "push"]),
|
|
98
|
+
isEnabled: z.boolean(),
|
|
99
|
+
destination: z.string().optional(),
|
|
100
|
+
}))
|
|
101
|
+
.handler(async ({ input, orgId }) => {
|
|
102
|
+
const body = input;
|
|
103
|
+
const result = await prefService.setPreference(orgId, body.customerId, body.channel, body.isEnabled, body.destination);
|
|
104
|
+
if (!result.ok)
|
|
105
|
+
throw new Error(result.error);
|
|
106
|
+
return result.value;
|
|
107
|
+
});
|
|
108
|
+
pref.get("/{customerId}").summary("Get customer notification preferences").permission("notifications:read")
|
|
109
|
+
.handler(async ({ params, orgId }) => {
|
|
110
|
+
const result = await prefService.getPreferences(orgId, params.customerId);
|
|
111
|
+
if (!result.ok)
|
|
112
|
+
throw new Error(result.error);
|
|
113
|
+
return result.value;
|
|
114
|
+
});
|
|
115
|
+
// ── Print Routes ───────────────────────────────────────────────────
|
|
116
|
+
const print = router("Print Jobs", "/notifications/print", ctx);
|
|
117
|
+
print.post("/").summary("Submit print job").permission("notifications:admin")
|
|
118
|
+
.input(z.object({
|
|
119
|
+
type: z.enum(["receipt", "label", "sticker", "kot"]),
|
|
120
|
+
printerId: z.string().min(1),
|
|
121
|
+
content: z.record(z.string(), z.unknown()),
|
|
122
|
+
format: z.enum(["esc_pos", "star_line", "label"]).optional(),
|
|
123
|
+
}))
|
|
124
|
+
.handler(async ({ input, orgId }) => {
|
|
125
|
+
const body = input;
|
|
126
|
+
const result = await printService.submitJob(orgId, body);
|
|
127
|
+
if (!result.ok)
|
|
128
|
+
throw new Error(result.error);
|
|
129
|
+
return result.value;
|
|
130
|
+
});
|
|
131
|
+
print.get("/{id}").summary("Get print job").permission("notifications:admin")
|
|
132
|
+
.handler(async ({ params, orgId }) => {
|
|
133
|
+
const result = await printService.getJob(orgId, params.id);
|
|
134
|
+
if (!result.ok)
|
|
135
|
+
throw new Error(result.error);
|
|
136
|
+
return result.value;
|
|
137
|
+
});
|
|
138
|
+
print.get("/").summary("List print jobs").permission("notifications:admin")
|
|
139
|
+
.query(z.object({
|
|
140
|
+
status: z.enum(["queued", "printing", "printed", "failed"]).optional(),
|
|
141
|
+
printerId: z.string().optional(),
|
|
142
|
+
type: z.enum(["receipt", "label", "sticker", "kot"]).optional(),
|
|
143
|
+
limit: z.coerce.number().int().positive().optional(),
|
|
144
|
+
}))
|
|
145
|
+
.handler(async ({ query, orgId }) => {
|
|
146
|
+
const q = query;
|
|
147
|
+
const result = await printService.listJobs(orgId, q);
|
|
148
|
+
if (!result.ok)
|
|
149
|
+
throw new Error(result.error);
|
|
150
|
+
return result.value;
|
|
151
|
+
});
|
|
152
|
+
print.patch("/{id}/status").summary("Update print job status").permission("notifications:admin")
|
|
153
|
+
.input(z.object({
|
|
154
|
+
status: z.enum(["queued", "printing", "printed", "failed"]),
|
|
155
|
+
error: z.string().optional(),
|
|
156
|
+
}))
|
|
157
|
+
.handler(async ({ params, input, orgId }) => {
|
|
158
|
+
const body = input;
|
|
159
|
+
const result = await printService.updateJobStatus(orgId, params.id, body.status, body.error);
|
|
160
|
+
if (!result.ok)
|
|
161
|
+
throw new Error(result.error);
|
|
162
|
+
return result.value;
|
|
163
|
+
});
|
|
164
|
+
return [
|
|
165
|
+
...tmpl.routes(),
|
|
166
|
+
...send.routes(),
|
|
167
|
+
...pref.routes(),
|
|
168
|
+
...print.routes(),
|
|
169
|
+
];
|
|
170
|
+
}
|