@wopr-network/platform-core 1.27.1 → 1.29.0
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/billing/crypto/charge-store.d.ts +10 -0
- package/dist/billing/crypto/charge-store.js +9 -1
- package/dist/billing/crypto/payment-method-store.d.ts +2 -0
- package/dist/billing/crypto/payment-method-store.js +6 -0
- package/dist/db/schema/crypto.d.ts +34 -0
- package/dist/db/schema/crypto.js +3 -1
- package/dist/db/schema/index.d.ts +1 -0
- package/dist/db/schema/index.js +1 -0
- package/dist/db/schema/notification-preferences.d.ts +17 -0
- package/dist/db/schema/notification-preferences.js +1 -0
- package/dist/db/schema/notification-templates.d.ts +165 -0
- package/dist/db/schema/notification-templates.js +18 -0
- package/dist/email/default-templates.d.ts +15 -0
- package/dist/email/default-templates.js +443 -0
- package/dist/email/drizzle-notification-template-repository.d.ts +21 -0
- package/dist/email/drizzle-notification-template-repository.js +90 -0
- package/dist/email/handlebars-renderer.d.ts +17 -0
- package/dist/email/handlebars-renderer.js +56 -0
- package/dist/email/index.d.ts +4 -0
- package/dist/email/index.js +3 -0
- package/dist/email/notification-preferences-store.js +5 -0
- package/dist/email/notification-preferences-store.test.js +1 -0
- package/dist/email/notification-repository-types.d.ts +30 -0
- package/dist/email/notification-service.d.ts +2 -0
- package/dist/email/notification-service.js +22 -0
- package/dist/email/notification-template-repository.d.ts +7 -0
- package/dist/email/notification-template-repository.js +7 -0
- package/dist/email/notification-templates.d.ts +1 -1
- package/dist/email/notification-templates.js +52 -0
- package/dist/trpc/index.d.ts +1 -0
- package/dist/trpc/index.js +1 -0
- package/dist/trpc/notification-template-router.d.ts +59 -0
- package/dist/trpc/notification-template-router.js +81 -0
- package/drizzle/migrations/0010_oracle_address.sql +11 -0
- package/drizzle/migrations/0011_notification_templates.sql +14 -0
- package/drizzle/migrations/meta/_journal.json +14 -0
- package/package.json +2 -1
- package/src/billing/crypto/charge-store.ts +14 -1
- package/src/billing/crypto/payment-method-store.ts +8 -0
- package/src/db/schema/crypto.ts +3 -1
- package/src/db/schema/index.ts +1 -0
- package/src/db/schema/notification-preferences.ts +1 -0
- package/src/db/schema/notification-templates.ts +19 -0
- package/src/email/default-templates.ts +680 -0
- package/src/email/drizzle-notification-template-repository.ts +108 -0
- package/src/email/handlebars-renderer.ts +64 -0
- package/src/email/index.ts +4 -0
- package/src/email/notification-preferences-store.test.ts +1 -0
- package/src/email/notification-preferences-store.ts +4 -0
- package/src/email/notification-repository-types.ts +41 -0
- package/src/email/notification-service.ts +31 -0
- package/src/email/notification-template-repository.ts +8 -0
- package/src/email/notification-templates.ts +61 -0
- package/src/trpc/index.ts +1 -0
- package/src/trpc/notification-template-router.ts +91 -0
|
@@ -33,6 +33,11 @@ export interface ICryptoChargeRepository {
|
|
|
33
33
|
createStablecoinCharge(input: CryptoDepositChargeInput): Promise<void>;
|
|
34
34
|
getByDepositAddress(address: string): Promise<CryptoChargeRecord | null>;
|
|
35
35
|
getNextDerivationIndex(): Promise<number>;
|
|
36
|
+
/** List deposit addresses with pending (uncredited) charges, grouped by chain. */
|
|
37
|
+
listActiveDepositAddresses(): Promise<{
|
|
38
|
+
chain: string;
|
|
39
|
+
address: string;
|
|
40
|
+
}[]>;
|
|
36
41
|
}
|
|
37
42
|
/**
|
|
38
43
|
* Manages crypto charge records in PostgreSQL.
|
|
@@ -62,6 +67,11 @@ export declare class DrizzleCryptoChargeRepository implements ICryptoChargeRepos
|
|
|
62
67
|
createStablecoinCharge(input: CryptoDepositChargeInput): Promise<void>;
|
|
63
68
|
/** Look up a charge by its deposit address. */
|
|
64
69
|
getByDepositAddress(address: string): Promise<CryptoChargeRecord | null>;
|
|
70
|
+
/** List deposit addresses with pending (uncredited) charges. */
|
|
71
|
+
listActiveDepositAddresses(): Promise<{
|
|
72
|
+
chain: string;
|
|
73
|
+
address: string;
|
|
74
|
+
}[]>;
|
|
65
75
|
/** Get the next available HD derivation index (max + 1, or 0 if empty). */
|
|
66
76
|
getNextDerivationIndex(): Promise<number>;
|
|
67
77
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { eq, sql } from "drizzle-orm";
|
|
1
|
+
import { and, eq, isNotNull, isNull, sql } from "drizzle-orm";
|
|
2
2
|
import { cryptoCharges } from "../../db/schema/crypto.js";
|
|
3
3
|
/**
|
|
4
4
|
* Manages crypto charge records in PostgreSQL.
|
|
@@ -98,6 +98,14 @@ export class DrizzleCryptoChargeRepository {
|
|
|
98
98
|
return null;
|
|
99
99
|
return this.toRecord(row);
|
|
100
100
|
}
|
|
101
|
+
/** List deposit addresses with pending (uncredited) charges. */
|
|
102
|
+
async listActiveDepositAddresses() {
|
|
103
|
+
const rows = await this.db
|
|
104
|
+
.select({ chain: cryptoCharges.chain, address: cryptoCharges.depositAddress })
|
|
105
|
+
.from(cryptoCharges)
|
|
106
|
+
.where(and(isNull(cryptoCharges.creditedAt), isNotNull(cryptoCharges.depositAddress), isNotNull(cryptoCharges.chain)));
|
|
107
|
+
return rows.filter((r) => r.chain !== null && r.address !== null);
|
|
108
|
+
}
|
|
101
109
|
/** Get the next available HD derivation index (max + 1, or 0 if empty). */
|
|
102
110
|
async getNextDerivationIndex() {
|
|
103
111
|
const result = await this.db
|
|
@@ -43,6 +43,8 @@ export class DrizzlePaymentMethodStore {
|
|
|
43
43
|
enabled: method.enabled,
|
|
44
44
|
displayOrder: method.displayOrder,
|
|
45
45
|
rpcUrl: method.rpcUrl,
|
|
46
|
+
oracleAddress: method.oracleAddress,
|
|
47
|
+
xpub: method.xpub,
|
|
46
48
|
confirmations: method.confirmations,
|
|
47
49
|
})
|
|
48
50
|
.onConflictDoUpdate({
|
|
@@ -57,6 +59,8 @@ export class DrizzlePaymentMethodStore {
|
|
|
57
59
|
enabled: method.enabled,
|
|
58
60
|
displayOrder: method.displayOrder,
|
|
59
61
|
rpcUrl: method.rpcUrl,
|
|
62
|
+
oracleAddress: method.oracleAddress,
|
|
63
|
+
xpub: method.xpub,
|
|
60
64
|
confirmations: method.confirmations,
|
|
61
65
|
},
|
|
62
66
|
});
|
|
@@ -77,6 +81,8 @@ function toRecord(row) {
|
|
|
77
81
|
enabled: row.enabled,
|
|
78
82
|
displayOrder: row.displayOrder,
|
|
79
83
|
rpcUrl: row.rpcUrl,
|
|
84
|
+
oracleAddress: row.oracleAddress,
|
|
85
|
+
xpub: row.xpub,
|
|
80
86
|
confirmations: row.confirmations,
|
|
81
87
|
};
|
|
82
88
|
}
|
|
@@ -475,6 +475,40 @@ export declare const paymentMethods: import("drizzle-orm/pg-core").PgTableWithCo
|
|
|
475
475
|
identity: undefined;
|
|
476
476
|
generated: undefined;
|
|
477
477
|
}, {}, {}>;
|
|
478
|
+
oracleAddress: import("drizzle-orm/pg-core").PgColumn<{
|
|
479
|
+
name: "oracle_address";
|
|
480
|
+
tableName: "payment_methods";
|
|
481
|
+
dataType: "string";
|
|
482
|
+
columnType: "PgText";
|
|
483
|
+
data: string;
|
|
484
|
+
driverParam: string;
|
|
485
|
+
notNull: false;
|
|
486
|
+
hasDefault: false;
|
|
487
|
+
isPrimaryKey: false;
|
|
488
|
+
isAutoincrement: false;
|
|
489
|
+
hasRuntimeDefault: false;
|
|
490
|
+
enumValues: [string, ...string[]];
|
|
491
|
+
baseColumn: never;
|
|
492
|
+
identity: undefined;
|
|
493
|
+
generated: undefined;
|
|
494
|
+
}, {}, {}>;
|
|
495
|
+
xpub: import("drizzle-orm/pg-core").PgColumn<{
|
|
496
|
+
name: "xpub";
|
|
497
|
+
tableName: "payment_methods";
|
|
498
|
+
dataType: "string";
|
|
499
|
+
columnType: "PgText";
|
|
500
|
+
data: string;
|
|
501
|
+
driverParam: string;
|
|
502
|
+
notNull: false;
|
|
503
|
+
hasDefault: false;
|
|
504
|
+
isPrimaryKey: false;
|
|
505
|
+
isAutoincrement: false;
|
|
506
|
+
hasRuntimeDefault: false;
|
|
507
|
+
enumValues: [string, ...string[]];
|
|
508
|
+
baseColumn: never;
|
|
509
|
+
identity: undefined;
|
|
510
|
+
generated: undefined;
|
|
511
|
+
}, {}, {}>;
|
|
478
512
|
confirmations: import("drizzle-orm/pg-core").PgColumn<{
|
|
479
513
|
name: "confirmations";
|
|
480
514
|
tableName: "payment_methods";
|
package/dist/db/schema/crypto.js
CHANGED
|
@@ -54,7 +54,9 @@ export const paymentMethods = pgTable("payment_methods", {
|
|
|
54
54
|
displayName: text("display_name").notNull(),
|
|
55
55
|
enabled: boolean("enabled").notNull().default(true),
|
|
56
56
|
displayOrder: integer("display_order").notNull().default(0),
|
|
57
|
-
rpcUrl: text("rpc_url"), //
|
|
57
|
+
rpcUrl: text("rpc_url"), // chain node RPC endpoint
|
|
58
|
+
oracleAddress: text("oracle_address"), // Chainlink feed address for price (null = 1:1 stablecoin)
|
|
59
|
+
xpub: text("xpub"), // HD wallet extended public key for deposit address derivation
|
|
58
60
|
confirmations: integer("confirmations").notNull().default(1),
|
|
59
61
|
createdAt: text("created_at").notNull().default(sql `(now())`),
|
|
60
62
|
});
|
|
@@ -34,6 +34,7 @@ export * from "./node-transitions.js";
|
|
|
34
34
|
export * from "./nodes.js";
|
|
35
35
|
export * from "./notification-preferences.js";
|
|
36
36
|
export * from "./notification-queue.js";
|
|
37
|
+
export * from "./notification-templates.js";
|
|
37
38
|
export * from "./oauth-states.js";
|
|
38
39
|
export * from "./onboarding-scripts.js";
|
|
39
40
|
export * from "./onboarding-sessions.js";
|
package/dist/db/schema/index.js
CHANGED
|
@@ -34,6 +34,7 @@ export * from "./node-transitions.js";
|
|
|
34
34
|
export * from "./nodes.js";
|
|
35
35
|
export * from "./notification-preferences.js";
|
|
36
36
|
export * from "./notification-queue.js";
|
|
37
|
+
export * from "./notification-templates.js";
|
|
37
38
|
export * from "./oauth-states.js";
|
|
38
39
|
export * from "./onboarding-scripts.js";
|
|
39
40
|
export * from "./onboarding-sessions.js";
|
|
@@ -143,6 +143,23 @@ export declare const notificationPreferences: import("drizzle-orm/pg-core").PgTa
|
|
|
143
143
|
identity: undefined;
|
|
144
144
|
generated: undefined;
|
|
145
145
|
}, {}, {}>;
|
|
146
|
+
fleetUpdates: import("drizzle-orm/pg-core").PgColumn<{
|
|
147
|
+
name: "fleet_updates";
|
|
148
|
+
tableName: "notification_preferences";
|
|
149
|
+
dataType: "boolean";
|
|
150
|
+
columnType: "PgBoolean";
|
|
151
|
+
data: boolean;
|
|
152
|
+
driverParam: boolean;
|
|
153
|
+
notNull: true;
|
|
154
|
+
hasDefault: true;
|
|
155
|
+
isPrimaryKey: false;
|
|
156
|
+
isAutoincrement: false;
|
|
157
|
+
hasRuntimeDefault: false;
|
|
158
|
+
enumValues: undefined;
|
|
159
|
+
baseColumn: never;
|
|
160
|
+
identity: undefined;
|
|
161
|
+
generated: undefined;
|
|
162
|
+
}, {}, {}>;
|
|
146
163
|
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
147
164
|
name: "updated_at";
|
|
148
165
|
tableName: "notification_preferences";
|
|
@@ -14,5 +14,6 @@ export const notificationPreferences = pgTable("notification_preferences", {
|
|
|
14
14
|
agentStatusChanges: boolean("agent_status_changes").notNull().default(false),
|
|
15
15
|
accountRoleChanges: boolean("account_role_changes").notNull().default(true),
|
|
16
16
|
accountTeamInvites: boolean("account_team_invites").notNull().default(true),
|
|
17
|
+
fleetUpdates: boolean("fleet_updates").notNull().default(true),
|
|
17
18
|
updatedAt: bigint("updated_at", { mode: "number" }).notNull().default(sql `(extract(epoch from now()))::bigint`),
|
|
18
19
|
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DB-driven email templates with Handlebars syntax.
|
|
3
|
+
* Each row stores a named template (subject + HTML + text bodies).
|
|
4
|
+
* Admins can edit these at runtime without code deploys.
|
|
5
|
+
*/
|
|
6
|
+
export declare const notificationTemplates: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
7
|
+
name: "notification_templates";
|
|
8
|
+
schema: undefined;
|
|
9
|
+
columns: {
|
|
10
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
11
|
+
name: "id";
|
|
12
|
+
tableName: "notification_templates";
|
|
13
|
+
dataType: "string";
|
|
14
|
+
columnType: "PgText";
|
|
15
|
+
data: string;
|
|
16
|
+
driverParam: string;
|
|
17
|
+
notNull: true;
|
|
18
|
+
hasDefault: false;
|
|
19
|
+
isPrimaryKey: true;
|
|
20
|
+
isAutoincrement: false;
|
|
21
|
+
hasRuntimeDefault: false;
|
|
22
|
+
enumValues: [string, ...string[]];
|
|
23
|
+
baseColumn: never;
|
|
24
|
+
identity: undefined;
|
|
25
|
+
generated: undefined;
|
|
26
|
+
}, {}, {}>;
|
|
27
|
+
name: import("drizzle-orm/pg-core").PgColumn<{
|
|
28
|
+
name: "name";
|
|
29
|
+
tableName: "notification_templates";
|
|
30
|
+
dataType: "string";
|
|
31
|
+
columnType: "PgText";
|
|
32
|
+
data: string;
|
|
33
|
+
driverParam: string;
|
|
34
|
+
notNull: true;
|
|
35
|
+
hasDefault: false;
|
|
36
|
+
isPrimaryKey: false;
|
|
37
|
+
isAutoincrement: false;
|
|
38
|
+
hasRuntimeDefault: false;
|
|
39
|
+
enumValues: [string, ...string[]];
|
|
40
|
+
baseColumn: never;
|
|
41
|
+
identity: undefined;
|
|
42
|
+
generated: undefined;
|
|
43
|
+
}, {}, {}>;
|
|
44
|
+
description: import("drizzle-orm/pg-core").PgColumn<{
|
|
45
|
+
name: "description";
|
|
46
|
+
tableName: "notification_templates";
|
|
47
|
+
dataType: "string";
|
|
48
|
+
columnType: "PgText";
|
|
49
|
+
data: string;
|
|
50
|
+
driverParam: string;
|
|
51
|
+
notNull: false;
|
|
52
|
+
hasDefault: false;
|
|
53
|
+
isPrimaryKey: false;
|
|
54
|
+
isAutoincrement: false;
|
|
55
|
+
hasRuntimeDefault: false;
|
|
56
|
+
enumValues: [string, ...string[]];
|
|
57
|
+
baseColumn: never;
|
|
58
|
+
identity: undefined;
|
|
59
|
+
generated: undefined;
|
|
60
|
+
}, {}, {}>;
|
|
61
|
+
subject: import("drizzle-orm/pg-core").PgColumn<{
|
|
62
|
+
name: "subject";
|
|
63
|
+
tableName: "notification_templates";
|
|
64
|
+
dataType: "string";
|
|
65
|
+
columnType: "PgText";
|
|
66
|
+
data: string;
|
|
67
|
+
driverParam: string;
|
|
68
|
+
notNull: true;
|
|
69
|
+
hasDefault: false;
|
|
70
|
+
isPrimaryKey: false;
|
|
71
|
+
isAutoincrement: false;
|
|
72
|
+
hasRuntimeDefault: false;
|
|
73
|
+
enumValues: [string, ...string[]];
|
|
74
|
+
baseColumn: never;
|
|
75
|
+
identity: undefined;
|
|
76
|
+
generated: undefined;
|
|
77
|
+
}, {}, {}>;
|
|
78
|
+
htmlBody: import("drizzle-orm/pg-core").PgColumn<{
|
|
79
|
+
name: "html_body";
|
|
80
|
+
tableName: "notification_templates";
|
|
81
|
+
dataType: "string";
|
|
82
|
+
columnType: "PgText";
|
|
83
|
+
data: string;
|
|
84
|
+
driverParam: string;
|
|
85
|
+
notNull: true;
|
|
86
|
+
hasDefault: false;
|
|
87
|
+
isPrimaryKey: false;
|
|
88
|
+
isAutoincrement: false;
|
|
89
|
+
hasRuntimeDefault: false;
|
|
90
|
+
enumValues: [string, ...string[]];
|
|
91
|
+
baseColumn: never;
|
|
92
|
+
identity: undefined;
|
|
93
|
+
generated: undefined;
|
|
94
|
+
}, {}, {}>;
|
|
95
|
+
textBody: import("drizzle-orm/pg-core").PgColumn<{
|
|
96
|
+
name: "text_body";
|
|
97
|
+
tableName: "notification_templates";
|
|
98
|
+
dataType: "string";
|
|
99
|
+
columnType: "PgText";
|
|
100
|
+
data: string;
|
|
101
|
+
driverParam: string;
|
|
102
|
+
notNull: true;
|
|
103
|
+
hasDefault: false;
|
|
104
|
+
isPrimaryKey: false;
|
|
105
|
+
isAutoincrement: false;
|
|
106
|
+
hasRuntimeDefault: false;
|
|
107
|
+
enumValues: [string, ...string[]];
|
|
108
|
+
baseColumn: never;
|
|
109
|
+
identity: undefined;
|
|
110
|
+
generated: undefined;
|
|
111
|
+
}, {}, {}>;
|
|
112
|
+
active: import("drizzle-orm/pg-core").PgColumn<{
|
|
113
|
+
name: "active";
|
|
114
|
+
tableName: "notification_templates";
|
|
115
|
+
dataType: "boolean";
|
|
116
|
+
columnType: "PgBoolean";
|
|
117
|
+
data: boolean;
|
|
118
|
+
driverParam: boolean;
|
|
119
|
+
notNull: true;
|
|
120
|
+
hasDefault: true;
|
|
121
|
+
isPrimaryKey: false;
|
|
122
|
+
isAutoincrement: false;
|
|
123
|
+
hasRuntimeDefault: false;
|
|
124
|
+
enumValues: undefined;
|
|
125
|
+
baseColumn: never;
|
|
126
|
+
identity: undefined;
|
|
127
|
+
generated: undefined;
|
|
128
|
+
}, {}, {}>;
|
|
129
|
+
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
130
|
+
name: "created_at";
|
|
131
|
+
tableName: "notification_templates";
|
|
132
|
+
dataType: "number";
|
|
133
|
+
columnType: "PgBigInt53";
|
|
134
|
+
data: number;
|
|
135
|
+
driverParam: string | number;
|
|
136
|
+
notNull: true;
|
|
137
|
+
hasDefault: true;
|
|
138
|
+
isPrimaryKey: false;
|
|
139
|
+
isAutoincrement: false;
|
|
140
|
+
hasRuntimeDefault: false;
|
|
141
|
+
enumValues: undefined;
|
|
142
|
+
baseColumn: never;
|
|
143
|
+
identity: undefined;
|
|
144
|
+
generated: undefined;
|
|
145
|
+
}, {}, {}>;
|
|
146
|
+
updatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
147
|
+
name: "updated_at";
|
|
148
|
+
tableName: "notification_templates";
|
|
149
|
+
dataType: "number";
|
|
150
|
+
columnType: "PgBigInt53";
|
|
151
|
+
data: number;
|
|
152
|
+
driverParam: string | number;
|
|
153
|
+
notNull: true;
|
|
154
|
+
hasDefault: true;
|
|
155
|
+
isPrimaryKey: false;
|
|
156
|
+
isAutoincrement: false;
|
|
157
|
+
hasRuntimeDefault: false;
|
|
158
|
+
enumValues: undefined;
|
|
159
|
+
baseColumn: never;
|
|
160
|
+
identity: undefined;
|
|
161
|
+
generated: undefined;
|
|
162
|
+
}, {}, {}>;
|
|
163
|
+
};
|
|
164
|
+
dialect: "pg";
|
|
165
|
+
}>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
import { bigint, boolean, pgTable, text } from "drizzle-orm/pg-core";
|
|
3
|
+
/**
|
|
4
|
+
* DB-driven email templates with Handlebars syntax.
|
|
5
|
+
* Each row stores a named template (subject + HTML + text bodies).
|
|
6
|
+
* Admins can edit these at runtime without code deploys.
|
|
7
|
+
*/
|
|
8
|
+
export const notificationTemplates = pgTable("notification_templates", {
|
|
9
|
+
id: text("id").primaryKey(),
|
|
10
|
+
name: text("name").notNull().unique(),
|
|
11
|
+
description: text("description"),
|
|
12
|
+
subject: text("subject").notNull(),
|
|
13
|
+
htmlBody: text("html_body").notNull(),
|
|
14
|
+
textBody: text("text_body").notNull(),
|
|
15
|
+
active: boolean("active").notNull().default(true),
|
|
16
|
+
createdAt: bigint("created_at", { mode: "number" }).notNull().default(sql `(extract(epoch from now()))::bigint`),
|
|
17
|
+
updatedAt: bigint("updated_at", { mode: "number" }).notNull().default(sql `(extract(epoch from now()))::bigint`),
|
|
18
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default email templates — Handlebars versions of every notification template.
|
|
3
|
+
*
|
|
4
|
+
* These are seeded into the notification_templates table on first run.
|
|
5
|
+
* Admin edits are preserved (seed uses INSERT OR IGNORE).
|
|
6
|
+
*/
|
|
7
|
+
interface DefaultTemplate {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
subject: string;
|
|
11
|
+
htmlBody: string;
|
|
12
|
+
textBody: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const DEFAULT_TEMPLATES: DefaultTemplate[];
|
|
15
|
+
export {};
|