@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.
Files changed (55) hide show
  1. package/dist/billing/crypto/charge-store.d.ts +10 -0
  2. package/dist/billing/crypto/charge-store.js +9 -1
  3. package/dist/billing/crypto/payment-method-store.d.ts +2 -0
  4. package/dist/billing/crypto/payment-method-store.js +6 -0
  5. package/dist/db/schema/crypto.d.ts +34 -0
  6. package/dist/db/schema/crypto.js +3 -1
  7. package/dist/db/schema/index.d.ts +1 -0
  8. package/dist/db/schema/index.js +1 -0
  9. package/dist/db/schema/notification-preferences.d.ts +17 -0
  10. package/dist/db/schema/notification-preferences.js +1 -0
  11. package/dist/db/schema/notification-templates.d.ts +165 -0
  12. package/dist/db/schema/notification-templates.js +18 -0
  13. package/dist/email/default-templates.d.ts +15 -0
  14. package/dist/email/default-templates.js +443 -0
  15. package/dist/email/drizzle-notification-template-repository.d.ts +21 -0
  16. package/dist/email/drizzle-notification-template-repository.js +90 -0
  17. package/dist/email/handlebars-renderer.d.ts +17 -0
  18. package/dist/email/handlebars-renderer.js +56 -0
  19. package/dist/email/index.d.ts +4 -0
  20. package/dist/email/index.js +3 -0
  21. package/dist/email/notification-preferences-store.js +5 -0
  22. package/dist/email/notification-preferences-store.test.js +1 -0
  23. package/dist/email/notification-repository-types.d.ts +30 -0
  24. package/dist/email/notification-service.d.ts +2 -0
  25. package/dist/email/notification-service.js +22 -0
  26. package/dist/email/notification-template-repository.d.ts +7 -0
  27. package/dist/email/notification-template-repository.js +7 -0
  28. package/dist/email/notification-templates.d.ts +1 -1
  29. package/dist/email/notification-templates.js +52 -0
  30. package/dist/trpc/index.d.ts +1 -0
  31. package/dist/trpc/index.js +1 -0
  32. package/dist/trpc/notification-template-router.d.ts +59 -0
  33. package/dist/trpc/notification-template-router.js +81 -0
  34. package/drizzle/migrations/0010_oracle_address.sql +11 -0
  35. package/drizzle/migrations/0011_notification_templates.sql +14 -0
  36. package/drizzle/migrations/meta/_journal.json +14 -0
  37. package/package.json +2 -1
  38. package/src/billing/crypto/charge-store.ts +14 -1
  39. package/src/billing/crypto/payment-method-store.ts +8 -0
  40. package/src/db/schema/crypto.ts +3 -1
  41. package/src/db/schema/index.ts +1 -0
  42. package/src/db/schema/notification-preferences.ts +1 -0
  43. package/src/db/schema/notification-templates.ts +19 -0
  44. package/src/email/default-templates.ts +680 -0
  45. package/src/email/drizzle-notification-template-repository.ts +108 -0
  46. package/src/email/handlebars-renderer.ts +64 -0
  47. package/src/email/index.ts +4 -0
  48. package/src/email/notification-preferences-store.test.ts +1 -0
  49. package/src/email/notification-preferences-store.ts +4 -0
  50. package/src/email/notification-repository-types.ts +41 -0
  51. package/src/email/notification-service.ts +31 -0
  52. package/src/email/notification-template-repository.ts +8 -0
  53. package/src/email/notification-templates.ts +61 -0
  54. package/src/trpc/index.ts +1 -0
  55. 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
@@ -10,6 +10,8 @@ export interface PaymentMethodRecord {
10
10
  enabled: boolean;
11
11
  displayOrder: number;
12
12
  rpcUrl: string | null;
13
+ oracleAddress: string | null;
14
+ xpub: string | null;
13
15
  confirmations: number;
14
16
  }
15
17
  export interface IPaymentMethodStore {
@@ -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";
@@ -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"), // override per-chain RPC (null = use default)
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";
@@ -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 {};