@develit-services/bank 6.0.0 → 6.1.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/base.cjs +23 -136
- package/dist/base.d.cts +0 -6
- package/dist/base.d.mts +0 -6
- package/dist/base.d.ts +0 -6
- package/dist/base.mjs +22 -135
- package/dist/export/workflows.cjs +58 -49
- package/dist/export/workflows.mjs +58 -49
- package/dist/service.d.cts +0 -2
- package/dist/service.d.mts +0 -2
- package/dist/service.d.ts +0 -2
- package/dist/shared/{bank.D9DoIABz.mjs → bank.BWR4dqQ5.mjs} +1 -48
- package/dist/shared/{bank.DQZxSHKV.cjs → bank.CSmuzdUJ.cjs} +0 -55
- package/package.json +1 -1
package/dist/base.cjs
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
const backendSdk = require('@develit-io/backend-sdk');
|
|
4
4
|
const paymentDirection = require('./shared/bank.BsWBG0gb.cjs');
|
|
5
|
-
const bank = require('./shared/bank.DQZxSHKV.cjs');
|
|
6
5
|
const drizzleOrm = require('drizzle-orm');
|
|
7
6
|
const cloudflare_workers = require('cloudflare:workers');
|
|
8
7
|
const d1 = require('drizzle-orm/d1');
|
|
9
8
|
require('jose');
|
|
9
|
+
const bank = require('./shared/bank.CSmuzdUJ.cjs');
|
|
10
10
|
const zod = require('zod');
|
|
11
11
|
const database_schema = require('./shared/bank.Ca3jzmIb.cjs');
|
|
12
12
|
const generalCodes = require('@develit-io/general-codes');
|
|
@@ -107,50 +107,14 @@ async function heartbeatSyncWorkflows({
|
|
|
107
107
|
);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
createInstance,
|
|
113
|
-
logger,
|
|
114
|
-
now = Date.now,
|
|
115
|
-
recoveryWindowMs = bank.DISPATCH_RECOVERY_WINDOW_MS
|
|
116
|
-
}) {
|
|
117
|
-
const at = now();
|
|
118
|
-
await Promise.all(
|
|
119
|
-
entities.map(async (entity) => {
|
|
120
|
-
const { id, lastSyncAt } = entity;
|
|
121
|
-
if (!lastSyncAt) {
|
|
122
|
-
logger.warn("sync-workflow.dispatch.sync-age-unknown", { id });
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
const instanceId = bank.buildDispatchInstanceId(id, at, recoveryWindowMs);
|
|
126
|
-
try {
|
|
127
|
-
await createInstance(instanceId, id);
|
|
128
|
-
logger.info("sync-workflow.dispatch.created", {
|
|
129
|
-
id,
|
|
130
|
-
instanceId,
|
|
131
|
-
lastSyncAt: lastSyncAt.toISOString()
|
|
132
|
-
});
|
|
133
|
-
} catch (err) {
|
|
134
|
-
if (bank.isAlreadyExists(err)) return;
|
|
135
|
-
logger.error("sync-workflow.dispatch.failed", {
|
|
136
|
-
id,
|
|
137
|
-
instanceId,
|
|
138
|
-
error: err instanceof Error ? err.message : String(err)
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
})
|
|
142
|
-
);
|
|
110
|
+
function isAlreadyExists(err) {
|
|
111
|
+
return /already exists/i.test(extractMessage(err));
|
|
143
112
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const raw = selection?.trim();
|
|
147
|
-
if (!raw) return false;
|
|
148
|
-
if (raw.toLowerCase() === "all") return true;
|
|
149
|
-
return raw.split(",").map((id) => id.trim()).includes(accountId);
|
|
113
|
+
function isInstanceNotFound(err) {
|
|
114
|
+
return /instance.*not.?found|not.?found.*instance/i.test(extractMessage(err));
|
|
150
115
|
}
|
|
151
|
-
function
|
|
152
|
-
|
|
153
|
-
return isDispatchEnabled(accountId, selection);
|
|
116
|
+
function extractMessage(err) {
|
|
117
|
+
return err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : "";
|
|
154
118
|
}
|
|
155
119
|
|
|
156
120
|
const FINITE_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -987,32 +951,18 @@ exports.BankServiceBase = class BankServiceBase extends backendSdk.develitWorker
|
|
|
987
951
|
{ successMessage: "Account sync workflow started" },
|
|
988
952
|
async ({ accountId }) => {
|
|
989
953
|
await this.setSyncEnabledOrThrow(accountId, true);
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
this.env.
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
} catch (err) {
|
|
1002
|
-
if (!bank.isAlreadyExists(err)) throw err;
|
|
1003
|
-
instance2 = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(instanceId);
|
|
1004
|
-
}
|
|
1005
|
-
return {
|
|
1006
|
-
instanceId: instance2.id,
|
|
1007
|
-
details: await instance2.status()
|
|
1008
|
-
};
|
|
954
|
+
let instance;
|
|
955
|
+
try {
|
|
956
|
+
instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create({
|
|
957
|
+
id: accountId,
|
|
958
|
+
params: {
|
|
959
|
+
accountId
|
|
960
|
+
}
|
|
961
|
+
});
|
|
962
|
+
} catch (err) {
|
|
963
|
+
if (!isAlreadyExists(err)) throw err;
|
|
964
|
+
instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(accountId);
|
|
1009
965
|
}
|
|
1010
|
-
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create({
|
|
1011
|
-
id: accountId,
|
|
1012
|
-
params: {
|
|
1013
|
-
accountId
|
|
1014
|
-
}
|
|
1015
|
-
});
|
|
1016
966
|
return {
|
|
1017
967
|
instanceId: instance.id,
|
|
1018
968
|
details: await instance.status()
|
|
@@ -1039,34 +989,13 @@ exports.BankServiceBase = class BankServiceBase extends backendSdk.develitWorker
|
|
|
1039
989
|
{ successMessage: "Account sync workflow restarted" },
|
|
1040
990
|
async ({ accountId }) => {
|
|
1041
991
|
await this.setSyncEnabledOrThrow(accountId, true);
|
|
1042
|
-
if (isDispatchActive(
|
|
1043
|
-
accountId,
|
|
1044
|
-
this.env.SYNC_DISPATCH_ACCOUNT_IDS,
|
|
1045
|
-
this.env.CRON_SYNC_WORKFLOW_DISPATCH
|
|
1046
|
-
)) {
|
|
1047
|
-
const instanceId = bank.buildDispatchInstanceId(accountId, Date.now());
|
|
1048
|
-
let instance2;
|
|
1049
|
-
try {
|
|
1050
|
-
instance2 = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create(
|
|
1051
|
-
bank.buildDispatchCreateOptions(instanceId, accountId)
|
|
1052
|
-
);
|
|
1053
|
-
} catch (err) {
|
|
1054
|
-
if (!bank.isAlreadyExists(err)) throw err;
|
|
1055
|
-
instance2 = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(instanceId);
|
|
1056
|
-
await instance2.restart();
|
|
1057
|
-
}
|
|
1058
|
-
return {
|
|
1059
|
-
instanceId: instance2.id,
|
|
1060
|
-
details: await instance2.status()
|
|
1061
|
-
};
|
|
1062
|
-
}
|
|
1063
992
|
let instance;
|
|
1064
993
|
try {
|
|
1065
994
|
const existing = await this.getCurrentSyncInstance(accountId);
|
|
1066
995
|
await existing.restart();
|
|
1067
996
|
instance = existing;
|
|
1068
997
|
} catch (err) {
|
|
1069
|
-
if (!
|
|
998
|
+
if (!isInstanceNotFound(err)) throw err;
|
|
1070
999
|
}
|
|
1071
1000
|
if (!instance) {
|
|
1072
1001
|
instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create({
|
|
@@ -1376,19 +1305,11 @@ exports.BankServiceBase = class BankServiceBase extends backendSdk.develitWorker
|
|
|
1376
1305
|
console.log("Scheduled CRON payment request statuses");
|
|
1377
1306
|
await this.updatePaymentRequestStatuses();
|
|
1378
1307
|
}
|
|
1379
|
-
if (controller.cron === this.env.CRON_SYNC_WORKFLOW_DISPATCH) {
|
|
1380
|
-
await this.dispatchSyncWorkflows();
|
|
1381
|
-
}
|
|
1382
1308
|
if (controller.cron === this.env.CRON_SYNC_WORKFLOW_HEARTBEAT) {
|
|
1383
1309
|
console.log("Scheduled CRON sync workflow heartbeat");
|
|
1384
1310
|
await this.heartbeatSyncWorkflows();
|
|
1385
1311
|
}
|
|
1386
1312
|
}
|
|
1387
|
-
/**
|
|
1388
|
-
* Lifecycle actions must target the instance that actually syncs the
|
|
1389
|
-
* account — under dispatch that is the windowed instance recorded with the
|
|
1390
|
-
* last sync write, not the legacy canonical id.
|
|
1391
|
-
*/
|
|
1392
1313
|
async setSyncEnabledOrThrow(accountId, syncEnabled) {
|
|
1393
1314
|
const [updated] = await updateAccountSyncEnabledCommand(this.db, {
|
|
1394
1315
|
accountId,
|
|
@@ -1399,25 +1320,7 @@ exports.BankServiceBase = class BankServiceBase extends backendSdk.develitWorker
|
|
|
1399
1320
|
}
|
|
1400
1321
|
}
|
|
1401
1322
|
async getCurrentSyncInstance(accountId) {
|
|
1402
|
-
|
|
1403
|
-
return this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
1404
|
-
bank.resolveCurrentSyncInstanceId(
|
|
1405
|
-
accountId,
|
|
1406
|
-
account?.lastSyncMetadata?.instanceId
|
|
1407
|
-
)
|
|
1408
|
-
);
|
|
1409
|
-
}
|
|
1410
|
-
async dispatchSyncWorkflows() {
|
|
1411
|
-
const selection = this.env.SYNC_DISPATCH_ACCOUNT_IDS;
|
|
1412
|
-
if (!selection?.trim()) return;
|
|
1413
|
-
const accounts = await getSyncCandidateAccountsQuery(this.db);
|
|
1414
|
-
await dispatchSyncWorkflows({
|
|
1415
|
-
entities: accounts.filter((a) => isDispatchEnabled(a.id, selection)),
|
|
1416
|
-
createInstance: (instanceId, accountId) => this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create(
|
|
1417
|
-
bank.buildDispatchCreateOptions(instanceId, accountId)
|
|
1418
|
-
),
|
|
1419
|
-
logger: syncEventConsoleLogger
|
|
1420
|
-
});
|
|
1323
|
+
return this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(accountId);
|
|
1421
1324
|
}
|
|
1422
1325
|
async heartbeatSyncWorkflows() {
|
|
1423
1326
|
const accounts = await getSyncCandidateAccountsQuery(this.db);
|
|
@@ -1432,13 +1335,7 @@ exports.BankServiceBase = class BankServiceBase extends backendSdk.develitWorker
|
|
|
1432
1335
|
return;
|
|
1433
1336
|
}
|
|
1434
1337
|
await heartbeatSyncWorkflows({
|
|
1435
|
-
entities: accounts.
|
|
1436
|
-
(a) => !isDispatchActive(
|
|
1437
|
-
a.id,
|
|
1438
|
-
this.env.SYNC_DISPATCH_ACCOUNT_IDS,
|
|
1439
|
-
this.env.CRON_SYNC_WORKFLOW_DISPATCH
|
|
1440
|
-
)
|
|
1441
|
-
).map((a) => ({
|
|
1338
|
+
entities: accounts.map((a) => ({
|
|
1442
1339
|
id: a.id,
|
|
1443
1340
|
iterationCount: a.lastSyncMetadata?.iterationCount,
|
|
1444
1341
|
syncIntervalS: a.syncIntervalS,
|
|
@@ -2210,12 +2107,7 @@ exports.BankServiceBase = class BankServiceBase extends backendSdk.develitWorker
|
|
|
2210
2107
|
if (includeWorkflow) {
|
|
2211
2108
|
let status;
|
|
2212
2109
|
try {
|
|
2213
|
-
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
2214
|
-
bank.resolveCurrentSyncInstanceId(
|
|
2215
|
-
a.id,
|
|
2216
|
-
a.lastSyncMetadata?.instanceId
|
|
2217
|
-
)
|
|
2218
|
-
);
|
|
2110
|
+
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(a.id);
|
|
2219
2111
|
status = await instance.status();
|
|
2220
2112
|
} catch (_) {
|
|
2221
2113
|
status = null;
|
|
@@ -2315,12 +2207,7 @@ exports.BankServiceBase = class BankServiceBase extends backendSdk.develitWorker
|
|
|
2315
2207
|
throw accountNotFoundError();
|
|
2316
2208
|
}
|
|
2317
2209
|
try {
|
|
2318
|
-
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
2319
|
-
bank.resolveCurrentSyncInstanceId(
|
|
2320
|
-
accountId,
|
|
2321
|
-
account.lastSyncMetadata?.instanceId
|
|
2322
|
-
)
|
|
2323
|
-
);
|
|
2210
|
+
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(accountId);
|
|
2324
2211
|
await terminateSyncWorkflow(instance);
|
|
2325
2212
|
} catch (error) {
|
|
2326
2213
|
this.log({
|
package/dist/base.d.cts
CHANGED
|
@@ -4212,14 +4212,8 @@ declare class BankServiceBase extends BankServiceBase_base {
|
|
|
4212
4212
|
statusChanged: number;
|
|
4213
4213
|
}>>;
|
|
4214
4214
|
scheduled(controller: ScheduledController): Promise<void>;
|
|
4215
|
-
/**
|
|
4216
|
-
* Lifecycle actions must target the instance that actually syncs the
|
|
4217
|
-
* account — under dispatch that is the windowed instance recorded with the
|
|
4218
|
-
* last sync write, not the legacy canonical id.
|
|
4219
|
-
*/
|
|
4220
4215
|
private setSyncEnabledOrThrow;
|
|
4221
4216
|
private getCurrentSyncInstance;
|
|
4222
|
-
private dispatchSyncWorkflows;
|
|
4223
4217
|
private heartbeatSyncWorkflows;
|
|
4224
4218
|
handleAuthorizationCallback(input: HandleAuthorizationCallbackInput): Promise<IRPCResponse<HandleAuthorizationCallbackOutput>>;
|
|
4225
4219
|
closePaymentRequest(input: ClosePaymentRequestInput): Promise<IRPCResponse<ClosePaymentRequestOutput>>;
|
package/dist/base.d.mts
CHANGED
|
@@ -4212,14 +4212,8 @@ declare class BankServiceBase extends BankServiceBase_base {
|
|
|
4212
4212
|
statusChanged: number;
|
|
4213
4213
|
}>>;
|
|
4214
4214
|
scheduled(controller: ScheduledController): Promise<void>;
|
|
4215
|
-
/**
|
|
4216
|
-
* Lifecycle actions must target the instance that actually syncs the
|
|
4217
|
-
* account — under dispatch that is the windowed instance recorded with the
|
|
4218
|
-
* last sync write, not the legacy canonical id.
|
|
4219
|
-
*/
|
|
4220
4215
|
private setSyncEnabledOrThrow;
|
|
4221
4216
|
private getCurrentSyncInstance;
|
|
4222
|
-
private dispatchSyncWorkflows;
|
|
4223
4217
|
private heartbeatSyncWorkflows;
|
|
4224
4218
|
handleAuthorizationCallback(input: HandleAuthorizationCallbackInput): Promise<IRPCResponse<HandleAuthorizationCallbackOutput>>;
|
|
4225
4219
|
closePaymentRequest(input: ClosePaymentRequestInput): Promise<IRPCResponse<ClosePaymentRequestOutput>>;
|
package/dist/base.d.ts
CHANGED
|
@@ -4212,14 +4212,8 @@ declare class BankServiceBase extends BankServiceBase_base {
|
|
|
4212
4212
|
statusChanged: number;
|
|
4213
4213
|
}>>;
|
|
4214
4214
|
scheduled(controller: ScheduledController): Promise<void>;
|
|
4215
|
-
/**
|
|
4216
|
-
* Lifecycle actions must target the instance that actually syncs the
|
|
4217
|
-
* account — under dispatch that is the windowed instance recorded with the
|
|
4218
|
-
* last sync write, not the legacy canonical id.
|
|
4219
|
-
*/
|
|
4220
4215
|
private setSyncEnabledOrThrow;
|
|
4221
4216
|
private getCurrentSyncInstance;
|
|
4222
|
-
private dispatchSyncWorkflows;
|
|
4223
4217
|
private heartbeatSyncWorkflows;
|
|
4224
4218
|
handleAuthorizationCallback(input: HandleAuthorizationCallbackInput): Promise<IRPCResponse<HandleAuthorizationCallbackOutput>>;
|
|
4225
4219
|
closePaymentRequest(input: ClosePaymentRequestInput): Promise<IRPCResponse<ClosePaymentRequestOutput>>;
|
package/dist/base.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { uuidv4, first, buildMultiFilterConditions as buildMultiFilterConditions$1, bankAccountMetadataSchema, structuredAddressSchema, workflowInstanceStatusSchema, develitWorker, createInternalError, action, service } from '@develit-io/backend-sdk';
|
|
2
2
|
import { G as tables, g as accountInsertSchema, H as relations, o as isProcessedStatus, p as isTerminalStatus, L as getNonTerminalPaymentRequestsQuery, x as toIncomingPayment, N as calculateCzechIban, j as assignAccount, u as toBatchedPayment, y as toPaymentRequestInsert, a as FinbricksClient, F as FINBRICKS_ENDPOINTS } from './shared/bank.kz-PKVi5.mjs';
|
|
3
|
-
import { k as isAlreadyExists, D as DISPATCH_RECOVERY_WINDOW_MS, l as buildDispatchInstanceId, m as encrypt, d as createCredentialsResolver, i as initiateConnector, n as buildDispatchCreateOptions, o as isInstanceNotFound, e as updatePaymentRequestStatusCommand, a as getPaymentRequestsByBatchIdQuery, g as getBatchByIdQuery, u as upsertBatchCommand, b as getAccountByIdQuery, r as resolveCurrentSyncInstanceId, p as importAesKey, j as createPaymentCommand } from './shared/bank.D9DoIABz.mjs';
|
|
4
3
|
import { eq, sql, and, like, asc, desc, inArray, gte, lte, isNull, count } from 'drizzle-orm';
|
|
5
4
|
import { WorkerEntrypoint } from 'cloudflare:workers';
|
|
6
5
|
import { drizzle } from 'drizzle-orm/d1';
|
|
7
6
|
import 'jose';
|
|
7
|
+
import { h as encrypt, d as createCredentialsResolver, i as initiateConnector, e as updatePaymentRequestStatusCommand, a as getPaymentRequestsByBatchIdQuery, g as getBatchByIdQuery, u as upsertBatchCommand, j as importAesKey, f as createPaymentCommand, b as getAccountByIdQuery } from './shared/bank.BWR4dqQ5.mjs';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import { I as INSTRUCTION_PRIORITIES, C as CHARGE_BEARERS, g as PAYMENT_TYPES, b as CONNECTOR_KEYS, a as BATCH_STATUSES, f as PAYMENT_STATUSES, P as PAYMENT_DIRECTIONS, e as PAYMENT_REQUEST_STATUSES } from './shared/bank.BT7HayCV.mjs';
|
|
10
10
|
import { CURRENCY_CODES } from '@develit-io/general-codes';
|
|
@@ -105,50 +105,14 @@ async function heartbeatSyncWorkflows({
|
|
|
105
105
|
);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
createInstance,
|
|
111
|
-
logger,
|
|
112
|
-
now = Date.now,
|
|
113
|
-
recoveryWindowMs = DISPATCH_RECOVERY_WINDOW_MS
|
|
114
|
-
}) {
|
|
115
|
-
const at = now();
|
|
116
|
-
await Promise.all(
|
|
117
|
-
entities.map(async (entity) => {
|
|
118
|
-
const { id, lastSyncAt } = entity;
|
|
119
|
-
if (!lastSyncAt) {
|
|
120
|
-
logger.warn("sync-workflow.dispatch.sync-age-unknown", { id });
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
const instanceId = buildDispatchInstanceId(id, at, recoveryWindowMs);
|
|
124
|
-
try {
|
|
125
|
-
await createInstance(instanceId, id);
|
|
126
|
-
logger.info("sync-workflow.dispatch.created", {
|
|
127
|
-
id,
|
|
128
|
-
instanceId,
|
|
129
|
-
lastSyncAt: lastSyncAt.toISOString()
|
|
130
|
-
});
|
|
131
|
-
} catch (err) {
|
|
132
|
-
if (isAlreadyExists(err)) return;
|
|
133
|
-
logger.error("sync-workflow.dispatch.failed", {
|
|
134
|
-
id,
|
|
135
|
-
instanceId,
|
|
136
|
-
error: err instanceof Error ? err.message : String(err)
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
})
|
|
140
|
-
);
|
|
108
|
+
function isAlreadyExists(err) {
|
|
109
|
+
return /already exists/i.test(extractMessage(err));
|
|
141
110
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const raw = selection?.trim();
|
|
145
|
-
if (!raw) return false;
|
|
146
|
-
if (raw.toLowerCase() === "all") return true;
|
|
147
|
-
return raw.split(",").map((id) => id.trim()).includes(accountId);
|
|
111
|
+
function isInstanceNotFound(err) {
|
|
112
|
+
return /instance.*not.?found|not.?found.*instance/i.test(extractMessage(err));
|
|
148
113
|
}
|
|
149
|
-
function
|
|
150
|
-
|
|
151
|
-
return isDispatchEnabled(accountId, selection);
|
|
114
|
+
function extractMessage(err) {
|
|
115
|
+
return err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : "";
|
|
152
116
|
}
|
|
153
117
|
|
|
154
118
|
const FINITE_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -985,32 +949,18 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
985
949
|
{ successMessage: "Account sync workflow started" },
|
|
986
950
|
async ({ accountId }) => {
|
|
987
951
|
await this.setSyncEnabledOrThrow(accountId, true);
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
this.env.
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
} catch (err) {
|
|
1000
|
-
if (!isAlreadyExists(err)) throw err;
|
|
1001
|
-
instance2 = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(instanceId);
|
|
1002
|
-
}
|
|
1003
|
-
return {
|
|
1004
|
-
instanceId: instance2.id,
|
|
1005
|
-
details: await instance2.status()
|
|
1006
|
-
};
|
|
952
|
+
let instance;
|
|
953
|
+
try {
|
|
954
|
+
instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create({
|
|
955
|
+
id: accountId,
|
|
956
|
+
params: {
|
|
957
|
+
accountId
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
} catch (err) {
|
|
961
|
+
if (!isAlreadyExists(err)) throw err;
|
|
962
|
+
instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(accountId);
|
|
1007
963
|
}
|
|
1008
|
-
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create({
|
|
1009
|
-
id: accountId,
|
|
1010
|
-
params: {
|
|
1011
|
-
accountId
|
|
1012
|
-
}
|
|
1013
|
-
});
|
|
1014
964
|
return {
|
|
1015
965
|
instanceId: instance.id,
|
|
1016
966
|
details: await instance.status()
|
|
@@ -1037,27 +987,6 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
1037
987
|
{ successMessage: "Account sync workflow restarted" },
|
|
1038
988
|
async ({ accountId }) => {
|
|
1039
989
|
await this.setSyncEnabledOrThrow(accountId, true);
|
|
1040
|
-
if (isDispatchActive(
|
|
1041
|
-
accountId,
|
|
1042
|
-
this.env.SYNC_DISPATCH_ACCOUNT_IDS,
|
|
1043
|
-
this.env.CRON_SYNC_WORKFLOW_DISPATCH
|
|
1044
|
-
)) {
|
|
1045
|
-
const instanceId = buildDispatchInstanceId(accountId, Date.now());
|
|
1046
|
-
let instance2;
|
|
1047
|
-
try {
|
|
1048
|
-
instance2 = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create(
|
|
1049
|
-
buildDispatchCreateOptions(instanceId, accountId)
|
|
1050
|
-
);
|
|
1051
|
-
} catch (err) {
|
|
1052
|
-
if (!isAlreadyExists(err)) throw err;
|
|
1053
|
-
instance2 = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(instanceId);
|
|
1054
|
-
await instance2.restart();
|
|
1055
|
-
}
|
|
1056
|
-
return {
|
|
1057
|
-
instanceId: instance2.id,
|
|
1058
|
-
details: await instance2.status()
|
|
1059
|
-
};
|
|
1060
|
-
}
|
|
1061
990
|
let instance;
|
|
1062
991
|
try {
|
|
1063
992
|
const existing = await this.getCurrentSyncInstance(accountId);
|
|
@@ -1374,19 +1303,11 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
1374
1303
|
console.log("Scheduled CRON payment request statuses");
|
|
1375
1304
|
await this.updatePaymentRequestStatuses();
|
|
1376
1305
|
}
|
|
1377
|
-
if (controller.cron === this.env.CRON_SYNC_WORKFLOW_DISPATCH) {
|
|
1378
|
-
await this.dispatchSyncWorkflows();
|
|
1379
|
-
}
|
|
1380
1306
|
if (controller.cron === this.env.CRON_SYNC_WORKFLOW_HEARTBEAT) {
|
|
1381
1307
|
console.log("Scheduled CRON sync workflow heartbeat");
|
|
1382
1308
|
await this.heartbeatSyncWorkflows();
|
|
1383
1309
|
}
|
|
1384
1310
|
}
|
|
1385
|
-
/**
|
|
1386
|
-
* Lifecycle actions must target the instance that actually syncs the
|
|
1387
|
-
* account — under dispatch that is the windowed instance recorded with the
|
|
1388
|
-
* last sync write, not the legacy canonical id.
|
|
1389
|
-
*/
|
|
1390
1311
|
async setSyncEnabledOrThrow(accountId, syncEnabled) {
|
|
1391
1312
|
const [updated] = await updateAccountSyncEnabledCommand(this.db, {
|
|
1392
1313
|
accountId,
|
|
@@ -1397,25 +1318,7 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
1397
1318
|
}
|
|
1398
1319
|
}
|
|
1399
1320
|
async getCurrentSyncInstance(accountId) {
|
|
1400
|
-
|
|
1401
|
-
return this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
1402
|
-
resolveCurrentSyncInstanceId(
|
|
1403
|
-
accountId,
|
|
1404
|
-
account?.lastSyncMetadata?.instanceId
|
|
1405
|
-
)
|
|
1406
|
-
);
|
|
1407
|
-
}
|
|
1408
|
-
async dispatchSyncWorkflows() {
|
|
1409
|
-
const selection = this.env.SYNC_DISPATCH_ACCOUNT_IDS;
|
|
1410
|
-
if (!selection?.trim()) return;
|
|
1411
|
-
const accounts = await getSyncCandidateAccountsQuery(this.db);
|
|
1412
|
-
await dispatchSyncWorkflows({
|
|
1413
|
-
entities: accounts.filter((a) => isDispatchEnabled(a.id, selection)),
|
|
1414
|
-
createInstance: (instanceId, accountId) => this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create(
|
|
1415
|
-
buildDispatchCreateOptions(instanceId, accountId)
|
|
1416
|
-
),
|
|
1417
|
-
logger: syncEventConsoleLogger
|
|
1418
|
-
});
|
|
1321
|
+
return this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(accountId);
|
|
1419
1322
|
}
|
|
1420
1323
|
async heartbeatSyncWorkflows() {
|
|
1421
1324
|
const accounts = await getSyncCandidateAccountsQuery(this.db);
|
|
@@ -1430,13 +1333,7 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
1430
1333
|
return;
|
|
1431
1334
|
}
|
|
1432
1335
|
await heartbeatSyncWorkflows({
|
|
1433
|
-
entities: accounts.
|
|
1434
|
-
(a) => !isDispatchActive(
|
|
1435
|
-
a.id,
|
|
1436
|
-
this.env.SYNC_DISPATCH_ACCOUNT_IDS,
|
|
1437
|
-
this.env.CRON_SYNC_WORKFLOW_DISPATCH
|
|
1438
|
-
)
|
|
1439
|
-
).map((a) => ({
|
|
1336
|
+
entities: accounts.map((a) => ({
|
|
1440
1337
|
id: a.id,
|
|
1441
1338
|
iterationCount: a.lastSyncMetadata?.iterationCount,
|
|
1442
1339
|
syncIntervalS: a.syncIntervalS,
|
|
@@ -2208,12 +2105,7 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
2208
2105
|
if (includeWorkflow) {
|
|
2209
2106
|
let status;
|
|
2210
2107
|
try {
|
|
2211
|
-
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
2212
|
-
resolveCurrentSyncInstanceId(
|
|
2213
|
-
a.id,
|
|
2214
|
-
a.lastSyncMetadata?.instanceId
|
|
2215
|
-
)
|
|
2216
|
-
);
|
|
2108
|
+
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(a.id);
|
|
2217
2109
|
status = await instance.status();
|
|
2218
2110
|
} catch (_) {
|
|
2219
2111
|
status = null;
|
|
@@ -2313,12 +2205,7 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
2313
2205
|
throw accountNotFoundError();
|
|
2314
2206
|
}
|
|
2315
2207
|
try {
|
|
2316
|
-
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
2317
|
-
resolveCurrentSyncInstanceId(
|
|
2318
|
-
accountId,
|
|
2319
|
-
account.lastSyncMetadata?.instanceId
|
|
2320
|
-
)
|
|
2321
|
-
);
|
|
2208
|
+
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(accountId);
|
|
2322
2209
|
await terminateSyncWorkflow(instance);
|
|
2323
2210
|
} catch (error) {
|
|
2324
2211
|
this.log({
|
|
@@ -4,7 +4,7 @@ const backendSdk = require('@develit-io/backend-sdk');
|
|
|
4
4
|
const paymentDirection = require('../shared/bank.BsWBG0gb.cjs');
|
|
5
5
|
const batchLifecycle = require('../shared/bank.NF8bZBy0.cjs');
|
|
6
6
|
const drizzleOrm = require('drizzle-orm');
|
|
7
|
-
const bank = require('../shared/bank.
|
|
7
|
+
const bank = require('../shared/bank.CSmuzdUJ.cjs');
|
|
8
8
|
const cloudflare_workers = require('cloudflare:workers');
|
|
9
9
|
const cloudflare_workflows = require('cloudflare:workflows');
|
|
10
10
|
const d1 = require('drizzle-orm/d1');
|
|
@@ -66,18 +66,54 @@ const mergeLastSyncBankRefIds = (previous, incoming, cap = LAST_SYNC_BANK_REF_ID
|
|
|
66
66
|
return [...new Set(merged)].slice(-cap);
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
const MAX_BATCH_MESSAGES = 100;
|
|
70
|
+
const MESSAGE_BYTE_CEILING = 112 * 1024;
|
|
71
|
+
const BATCH_BYTE_CEILING = 224 * 1024;
|
|
72
|
+
function estimateMessageBytes(message) {
|
|
73
|
+
return new TextEncoder().encode(JSON.stringify(message)).length;
|
|
74
|
+
}
|
|
75
|
+
function planQueueBatches(messages) {
|
|
76
|
+
const chunks = [];
|
|
77
|
+
const oversized = [];
|
|
78
|
+
let current = [];
|
|
79
|
+
let currentBytes = 0;
|
|
80
|
+
for (const message of messages) {
|
|
81
|
+
const bytes = estimateMessageBytes(message);
|
|
82
|
+
if (bytes > MESSAGE_BYTE_CEILING) {
|
|
83
|
+
oversized.push({ message, bytes });
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (current.length >= MAX_BATCH_MESSAGES || currentBytes + bytes > BATCH_BYTE_CEILING) {
|
|
87
|
+
chunks.push(current);
|
|
88
|
+
current = [];
|
|
89
|
+
currentBytes = 0;
|
|
90
|
+
}
|
|
91
|
+
current.push(message);
|
|
92
|
+
currentBytes += bytes;
|
|
73
93
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
94
|
+
if (current.length > 0) {
|
|
95
|
+
chunks.push(current);
|
|
96
|
+
}
|
|
97
|
+
return { chunks, oversized };
|
|
98
|
+
}
|
|
99
|
+
async function pushToQueue(queue, message) {
|
|
100
|
+
const messages = Array.isArray(message) ? message : [message];
|
|
101
|
+
const { chunks, oversized } = planQueueBatches(messages);
|
|
102
|
+
for (const chunk of chunks) {
|
|
103
|
+
if (chunk.length === 1) {
|
|
104
|
+
await queue.send(chunk[0], { contentType: "v8" });
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
await queue.sendBatch(
|
|
108
|
+
chunk.map((m) => ({
|
|
109
|
+
body: m,
|
|
110
|
+
contentType: "v8"
|
|
111
|
+
}))
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
return { sent: messages.length - oversized.length, oversized };
|
|
80
115
|
}
|
|
116
|
+
|
|
81
117
|
async function failBatchAndPayments(db, batch, paymentRequests, reason) {
|
|
82
118
|
const prCmds = paymentRequests.filter((p) => p.status !== "REJECTED").map(
|
|
83
119
|
(p) => bank.updatePaymentRequestStatusCommand(db, {
|
|
@@ -289,7 +325,7 @@ class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
289
325
|
timestamp: /* @__PURE__ */ new Date()
|
|
290
326
|
}
|
|
291
327
|
}));
|
|
292
|
-
await pushToQueue
|
|
328
|
+
await pushToQueue(
|
|
293
329
|
this.env.QUEUE_BUS_QUEUE,
|
|
294
330
|
eventsToEmit
|
|
295
331
|
);
|
|
@@ -301,7 +337,7 @@ class BankProcessBatch extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
301
337
|
}
|
|
302
338
|
if (!isMockConnector) {
|
|
303
339
|
await step.do("enqueue authorization email", async () => {
|
|
304
|
-
await pushToQueue
|
|
340
|
+
await pushToQueue(this.env.NOTIFICATIONS_QUEUE, {
|
|
305
341
|
type: "email",
|
|
306
342
|
payload: {
|
|
307
343
|
email: {
|
|
@@ -411,18 +447,6 @@ async function fetchBoundedByWindow({
|
|
|
411
447
|
}
|
|
412
448
|
}
|
|
413
449
|
|
|
414
|
-
async function pushToQueue(queue, message) {
|
|
415
|
-
if (!Array.isArray(message)) {
|
|
416
|
-
await queue.send(message, { contentType: "v8" });
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
await queue.sendBatch(
|
|
420
|
-
message.map((m) => ({
|
|
421
|
-
body: m,
|
|
422
|
-
contentType: "v8"
|
|
423
|
-
}))
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
450
|
class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
|
|
427
451
|
async run(event, step) {
|
|
428
452
|
const { accountId } = event.payload;
|
|
@@ -431,7 +455,6 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
431
455
|
if (!accountId) {
|
|
432
456
|
throw new cloudflare_workflows.NonRetryableError(`Haven't obtained accountId to load.`);
|
|
433
457
|
}
|
|
434
|
-
const deadlineMs = bank.computeWindowDeadline(event.instanceId, accountId);
|
|
435
458
|
const workflowStartedAt = await step.do(
|
|
436
459
|
"capture workflow start time",
|
|
437
460
|
async () => Date.now()
|
|
@@ -455,19 +478,6 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
455
478
|
if (!account.lastSyncAt) {
|
|
456
479
|
throw new Error(`lastSyncedAt is not set for account: ${accountId}`);
|
|
457
480
|
}
|
|
458
|
-
if (bank.isSupersededBy(
|
|
459
|
-
event.instanceId,
|
|
460
|
-
account.lastSyncMetadata?.instanceId,
|
|
461
|
-
accountId
|
|
462
|
-
)) {
|
|
463
|
-
logger.info("sync.superseded", {
|
|
464
|
-
accountId,
|
|
465
|
-
instanceId: event.instanceId,
|
|
466
|
-
supersededBy: account.lastSyncMetadata?.instanceId,
|
|
467
|
-
iteration
|
|
468
|
-
});
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
481
|
const lastSyncAtMs = account.lastSyncAt.getTime();
|
|
472
482
|
const windowMs = await step.do("resolve sync window", async () => {
|
|
473
483
|
const window = resolveSyncWindow(
|
|
@@ -481,15 +491,6 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
481
491
|
isCatchUp: window.isCatchUp
|
|
482
492
|
};
|
|
483
493
|
});
|
|
484
|
-
if (deadlineMs != null && windowMs.dateFromMs >= deadlineMs) {
|
|
485
|
-
logger.info("sync.window-elapsed", {
|
|
486
|
-
accountId,
|
|
487
|
-
instanceId: event.instanceId,
|
|
488
|
-
iteration,
|
|
489
|
-
deadline: new Date(deadlineMs).toISOString()
|
|
490
|
-
});
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
494
|
const syncWindow = {
|
|
494
495
|
dateFrom: new Date(windowMs.dateFromMs),
|
|
495
496
|
dateTo: new Date(windowMs.dateToMs),
|
|
@@ -705,10 +706,18 @@ class BankSyncAccountPayments extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
705
706
|
seenInstanceId: account.lastSyncMetadata?.instanceId ?? null
|
|
706
707
|
}).command;
|
|
707
708
|
if (eventsToEmit.length) {
|
|
708
|
-
await pushToQueue(
|
|
709
|
+
const { oversized } = await pushToQueue(
|
|
709
710
|
this.env.QUEUE_BUS_QUEUE,
|
|
710
711
|
eventsToEmit
|
|
711
712
|
);
|
|
713
|
+
for (const { message, bytes } of oversized) {
|
|
714
|
+
logger.error("queue.event-oversized", {
|
|
715
|
+
accountId,
|
|
716
|
+
bytes,
|
|
717
|
+
eventSignal: message.eventSignal,
|
|
718
|
+
bankRefId: message.bankPayment.bankRefId
|
|
719
|
+
});
|
|
720
|
+
}
|
|
712
721
|
}
|
|
713
722
|
let syncStateRows;
|
|
714
723
|
if (createCommands.length) {
|
|
@@ -2,7 +2,7 @@ import { first, uuidv4, asNonEmpty } from '@develit-io/backend-sdk';
|
|
|
2
2
|
import { G as tables, H as relations, v as toBatchedPaymentFromPaymentRequest, z as toPreparedPayment, m as isPaymentCompleted } from '../shared/bank.kz-PKVi5.mjs';
|
|
3
3
|
import { i as isBatchAuthorized, b as isBatchFailed, d as isBatchProcessing } from '../shared/bank.XqSw509X.mjs';
|
|
4
4
|
import { sql, and, eq, inArray } from 'drizzle-orm';
|
|
5
|
-
import { g as getBatchByIdQuery, a as getPaymentRequestsByBatchIdQuery, c as checksum, u as upsertBatchCommand, b as getAccountByIdQuery, d as createCredentialsResolver, i as initiateConnector, e as updatePaymentRequestStatusCommand, f as
|
|
5
|
+
import { g as getBatchByIdQuery, a as getPaymentRequestsByBatchIdQuery, c as checksum, u as upsertBatchCommand, b as getAccountByIdQuery, d as createCredentialsResolver, i as initiateConnector, e as updatePaymentRequestStatusCommand, f as createPaymentCommand } from '../shared/bank.BWR4dqQ5.mjs';
|
|
6
6
|
import { WorkflowEntrypoint } from 'cloudflare:workers';
|
|
7
7
|
import { NonRetryableError } from 'cloudflare:workflows';
|
|
8
8
|
import { drizzle } from 'drizzle-orm/d1';
|
|
@@ -64,18 +64,54 @@ const mergeLastSyncBankRefIds = (previous, incoming, cap = LAST_SYNC_BANK_REF_ID
|
|
|
64
64
|
return [...new Set(merged)].slice(-cap);
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
const MAX_BATCH_MESSAGES = 100;
|
|
68
|
+
const MESSAGE_BYTE_CEILING = 112 * 1024;
|
|
69
|
+
const BATCH_BYTE_CEILING = 224 * 1024;
|
|
70
|
+
function estimateMessageBytes(message) {
|
|
71
|
+
return new TextEncoder().encode(JSON.stringify(message)).length;
|
|
72
|
+
}
|
|
73
|
+
function planQueueBatches(messages) {
|
|
74
|
+
const chunks = [];
|
|
75
|
+
const oversized = [];
|
|
76
|
+
let current = [];
|
|
77
|
+
let currentBytes = 0;
|
|
78
|
+
for (const message of messages) {
|
|
79
|
+
const bytes = estimateMessageBytes(message);
|
|
80
|
+
if (bytes > MESSAGE_BYTE_CEILING) {
|
|
81
|
+
oversized.push({ message, bytes });
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (current.length >= MAX_BATCH_MESSAGES || currentBytes + bytes > BATCH_BYTE_CEILING) {
|
|
85
|
+
chunks.push(current);
|
|
86
|
+
current = [];
|
|
87
|
+
currentBytes = 0;
|
|
88
|
+
}
|
|
89
|
+
current.push(message);
|
|
90
|
+
currentBytes += bytes;
|
|
71
91
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
if (current.length > 0) {
|
|
93
|
+
chunks.push(current);
|
|
94
|
+
}
|
|
95
|
+
return { chunks, oversized };
|
|
96
|
+
}
|
|
97
|
+
async function pushToQueue(queue, message) {
|
|
98
|
+
const messages = Array.isArray(message) ? message : [message];
|
|
99
|
+
const { chunks, oversized } = planQueueBatches(messages);
|
|
100
|
+
for (const chunk of chunks) {
|
|
101
|
+
if (chunk.length === 1) {
|
|
102
|
+
await queue.send(chunk[0], { contentType: "v8" });
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
await queue.sendBatch(
|
|
106
|
+
chunk.map((m) => ({
|
|
107
|
+
body: m,
|
|
108
|
+
contentType: "v8"
|
|
109
|
+
}))
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return { sent: messages.length - oversized.length, oversized };
|
|
78
113
|
}
|
|
114
|
+
|
|
79
115
|
async function failBatchAndPayments(db, batch, paymentRequests, reason) {
|
|
80
116
|
const prCmds = paymentRequests.filter((p) => p.status !== "REJECTED").map(
|
|
81
117
|
(p) => updatePaymentRequestStatusCommand(db, {
|
|
@@ -287,7 +323,7 @@ class BankProcessBatch extends WorkflowEntrypoint {
|
|
|
287
323
|
timestamp: /* @__PURE__ */ new Date()
|
|
288
324
|
}
|
|
289
325
|
}));
|
|
290
|
-
await pushToQueue
|
|
326
|
+
await pushToQueue(
|
|
291
327
|
this.env.QUEUE_BUS_QUEUE,
|
|
292
328
|
eventsToEmit
|
|
293
329
|
);
|
|
@@ -299,7 +335,7 @@ class BankProcessBatch extends WorkflowEntrypoint {
|
|
|
299
335
|
}
|
|
300
336
|
if (!isMockConnector) {
|
|
301
337
|
await step.do("enqueue authorization email", async () => {
|
|
302
|
-
await pushToQueue
|
|
338
|
+
await pushToQueue(this.env.NOTIFICATIONS_QUEUE, {
|
|
303
339
|
type: "email",
|
|
304
340
|
payload: {
|
|
305
341
|
email: {
|
|
@@ -409,18 +445,6 @@ async function fetchBoundedByWindow({
|
|
|
409
445
|
}
|
|
410
446
|
}
|
|
411
447
|
|
|
412
|
-
async function pushToQueue(queue, message) {
|
|
413
|
-
if (!Array.isArray(message)) {
|
|
414
|
-
await queue.send(message, { contentType: "v8" });
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
await queue.sendBatch(
|
|
418
|
-
message.map((m) => ({
|
|
419
|
-
body: m,
|
|
420
|
-
contentType: "v8"
|
|
421
|
-
}))
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
448
|
class BankSyncAccountPayments extends WorkflowEntrypoint {
|
|
425
449
|
async run(event, step) {
|
|
426
450
|
const { accountId } = event.payload;
|
|
@@ -429,7 +453,6 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
|
|
|
429
453
|
if (!accountId) {
|
|
430
454
|
throw new NonRetryableError(`Haven't obtained accountId to load.`);
|
|
431
455
|
}
|
|
432
|
-
const deadlineMs = computeWindowDeadline(event.instanceId, accountId);
|
|
433
456
|
const workflowStartedAt = await step.do(
|
|
434
457
|
"capture workflow start time",
|
|
435
458
|
async () => Date.now()
|
|
@@ -453,19 +476,6 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
|
|
|
453
476
|
if (!account.lastSyncAt) {
|
|
454
477
|
throw new Error(`lastSyncedAt is not set for account: ${accountId}`);
|
|
455
478
|
}
|
|
456
|
-
if (isSupersededBy(
|
|
457
|
-
event.instanceId,
|
|
458
|
-
account.lastSyncMetadata?.instanceId,
|
|
459
|
-
accountId
|
|
460
|
-
)) {
|
|
461
|
-
logger.info("sync.superseded", {
|
|
462
|
-
accountId,
|
|
463
|
-
instanceId: event.instanceId,
|
|
464
|
-
supersededBy: account.lastSyncMetadata?.instanceId,
|
|
465
|
-
iteration
|
|
466
|
-
});
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
479
|
const lastSyncAtMs = account.lastSyncAt.getTime();
|
|
470
480
|
const windowMs = await step.do("resolve sync window", async () => {
|
|
471
481
|
const window = resolveSyncWindow(
|
|
@@ -479,15 +489,6 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
|
|
|
479
489
|
isCatchUp: window.isCatchUp
|
|
480
490
|
};
|
|
481
491
|
});
|
|
482
|
-
if (deadlineMs != null && windowMs.dateFromMs >= deadlineMs) {
|
|
483
|
-
logger.info("sync.window-elapsed", {
|
|
484
|
-
accountId,
|
|
485
|
-
instanceId: event.instanceId,
|
|
486
|
-
iteration,
|
|
487
|
-
deadline: new Date(deadlineMs).toISOString()
|
|
488
|
-
});
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
492
|
const syncWindow = {
|
|
492
493
|
dateFrom: new Date(windowMs.dateFromMs),
|
|
493
494
|
dateTo: new Date(windowMs.dateToMs),
|
|
@@ -703,10 +704,18 @@ class BankSyncAccountPayments extends WorkflowEntrypoint {
|
|
|
703
704
|
seenInstanceId: account.lastSyncMetadata?.instanceId ?? null
|
|
704
705
|
}).command;
|
|
705
706
|
if (eventsToEmit.length) {
|
|
706
|
-
await pushToQueue(
|
|
707
|
+
const { oversized } = await pushToQueue(
|
|
707
708
|
this.env.QUEUE_BUS_QUEUE,
|
|
708
709
|
eventsToEmit
|
|
709
710
|
);
|
|
711
|
+
for (const { message, bytes } of oversized) {
|
|
712
|
+
logger.error("queue.event-oversized", {
|
|
713
|
+
accountId,
|
|
714
|
+
bytes,
|
|
715
|
+
eventSignal: message.eventSignal,
|
|
716
|
+
bankRefId: message.bankPayment.bankRefId
|
|
717
|
+
});
|
|
718
|
+
}
|
|
710
719
|
}
|
|
711
720
|
let syncStateRows;
|
|
712
721
|
if (createCommands.length) {
|
package/dist/service.d.cts
CHANGED
|
@@ -17,8 +17,6 @@ interface BankServiceVariables {
|
|
|
17
17
|
DBUCS_TX_AUTH_URI: string;
|
|
18
18
|
REDIRECT_URI: string;
|
|
19
19
|
SYNC_WORKFLOW_RESET_AFTER_ITERATIONS: string;
|
|
20
|
-
CRON_SYNC_WORKFLOW_DISPATCH: string;
|
|
21
|
-
SYNC_DISPATCH_ACCOUNT_IDS: string;
|
|
22
20
|
[key: string]: string | number | boolean;
|
|
23
21
|
}
|
|
24
22
|
declare const BANK_SERVICE_BINDINGS: {
|
package/dist/service.d.mts
CHANGED
|
@@ -17,8 +17,6 @@ interface BankServiceVariables {
|
|
|
17
17
|
DBUCS_TX_AUTH_URI: string;
|
|
18
18
|
REDIRECT_URI: string;
|
|
19
19
|
SYNC_WORKFLOW_RESET_AFTER_ITERATIONS: string;
|
|
20
|
-
CRON_SYNC_WORKFLOW_DISPATCH: string;
|
|
21
|
-
SYNC_DISPATCH_ACCOUNT_IDS: string;
|
|
22
20
|
[key: string]: string | number | boolean;
|
|
23
21
|
}
|
|
24
22
|
declare const BANK_SERVICE_BINDINGS: {
|
package/dist/service.d.ts
CHANGED
|
@@ -17,8 +17,6 @@ interface BankServiceVariables {
|
|
|
17
17
|
DBUCS_TX_AUTH_URI: string;
|
|
18
18
|
REDIRECT_URI: string;
|
|
19
19
|
SYNC_WORKFLOW_RESET_AFTER_ITERATIONS: string;
|
|
20
|
-
CRON_SYNC_WORKFLOW_DISPATCH: string;
|
|
21
|
-
SYNC_DISPATCH_ACCOUNT_IDS: string;
|
|
22
20
|
[key: string]: string | number | boolean;
|
|
23
21
|
}
|
|
24
22
|
declare const BANK_SERVICE_BINDINGS: {
|
|
@@ -7,53 +7,6 @@ import 'jose';
|
|
|
7
7
|
import '@develit-io/general-codes';
|
|
8
8
|
import { createHash } from 'node:crypto';
|
|
9
9
|
|
|
10
|
-
const DISPATCH_RECOVERY_WINDOW_MS = 5 * 60 * 1e3;
|
|
11
|
-
function parseInstanceWindow(instanceId, accountId) {
|
|
12
|
-
const prefix = `${accountId}-`;
|
|
13
|
-
if (!instanceId.startsWith(prefix)) return -1;
|
|
14
|
-
const suffix = instanceId.slice(prefix.length);
|
|
15
|
-
if (!/^\d+$/.test(suffix)) return -1;
|
|
16
|
-
return Number(suffix);
|
|
17
|
-
}
|
|
18
|
-
function buildDispatchInstanceId(accountId, nowMs, recoveryWindowMs = DISPATCH_RECOVERY_WINDOW_MS) {
|
|
19
|
-
return `${accountId}-${Math.floor(nowMs / recoveryWindowMs)}`;
|
|
20
|
-
}
|
|
21
|
-
function buildDispatchCreateOptions(instanceId, accountId) {
|
|
22
|
-
return {
|
|
23
|
-
id: instanceId,
|
|
24
|
-
params: { accountId },
|
|
25
|
-
// Dispatch mints ~24 instances per account per day and step outputs carry
|
|
26
|
-
// bank payloads — the default 30-day retention would pile up billable
|
|
27
|
-
// storage for state nobody reads after success.
|
|
28
|
-
retention: {
|
|
29
|
-
successRetention: "1 day",
|
|
30
|
-
errorRetention: "7 days"
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
function computeWindowDeadline(instanceId, accountId, recoveryWindowMs = DISPATCH_RECOVERY_WINDOW_MS) {
|
|
35
|
-
const window = parseInstanceWindow(instanceId, accountId);
|
|
36
|
-
if (window < 0) return null;
|
|
37
|
-
return (window + 1) * recoveryWindowMs;
|
|
38
|
-
}
|
|
39
|
-
function isAlreadyExists(err) {
|
|
40
|
-
return /already exists/i.test(extractMessage(err));
|
|
41
|
-
}
|
|
42
|
-
function isInstanceNotFound(err) {
|
|
43
|
-
return /instance.*not.?found|not.?found.*instance/i.test(extractMessage(err));
|
|
44
|
-
}
|
|
45
|
-
function extractMessage(err) {
|
|
46
|
-
return err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : "";
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function resolveCurrentSyncInstanceId(accountId, recordedInstanceId) {
|
|
50
|
-
return recordedInstanceId ?? accountId;
|
|
51
|
-
}
|
|
52
|
-
function isSupersededBy(myInstanceId, recordedInstanceId, accountId) {
|
|
53
|
-
if (recordedInstanceId == null) return false;
|
|
54
|
-
return parseInstanceWindow(recordedInstanceId, accountId) > parseInstanceWindow(myInstanceId, accountId);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
10
|
const createPaymentCommand = (db, { payment }) => {
|
|
58
11
|
return {
|
|
59
12
|
command: db.insert(tables.payment).values({
|
|
@@ -406,4 +359,4 @@ const initiateConnector = async ({
|
|
|
406
359
|
}
|
|
407
360
|
};
|
|
408
361
|
|
|
409
|
-
export {
|
|
362
|
+
export { getPaymentRequestsByBatchIdQuery as a, getAccountByIdQuery as b, checksum as c, createCredentialsResolver as d, updatePaymentRequestStatusCommand as e, createPaymentCommand as f, getBatchByIdQuery as g, encrypt as h, initiateConnector as i, importAesKey as j, upsertBatchCommand as u };
|
|
@@ -9,53 +9,6 @@ require('jose');
|
|
|
9
9
|
require('@develit-io/general-codes');
|
|
10
10
|
const node_crypto = require('node:crypto');
|
|
11
11
|
|
|
12
|
-
const DISPATCH_RECOVERY_WINDOW_MS = 5 * 60 * 1e3;
|
|
13
|
-
function parseInstanceWindow(instanceId, accountId) {
|
|
14
|
-
const prefix = `${accountId}-`;
|
|
15
|
-
if (!instanceId.startsWith(prefix)) return -1;
|
|
16
|
-
const suffix = instanceId.slice(prefix.length);
|
|
17
|
-
if (!/^\d+$/.test(suffix)) return -1;
|
|
18
|
-
return Number(suffix);
|
|
19
|
-
}
|
|
20
|
-
function buildDispatchInstanceId(accountId, nowMs, recoveryWindowMs = DISPATCH_RECOVERY_WINDOW_MS) {
|
|
21
|
-
return `${accountId}-${Math.floor(nowMs / recoveryWindowMs)}`;
|
|
22
|
-
}
|
|
23
|
-
function buildDispatchCreateOptions(instanceId, accountId) {
|
|
24
|
-
return {
|
|
25
|
-
id: instanceId,
|
|
26
|
-
params: { accountId },
|
|
27
|
-
// Dispatch mints ~24 instances per account per day and step outputs carry
|
|
28
|
-
// bank payloads — the default 30-day retention would pile up billable
|
|
29
|
-
// storage for state nobody reads after success.
|
|
30
|
-
retention: {
|
|
31
|
-
successRetention: "1 day",
|
|
32
|
-
errorRetention: "7 days"
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
function computeWindowDeadline(instanceId, accountId, recoveryWindowMs = DISPATCH_RECOVERY_WINDOW_MS) {
|
|
37
|
-
const window = parseInstanceWindow(instanceId, accountId);
|
|
38
|
-
if (window < 0) return null;
|
|
39
|
-
return (window + 1) * recoveryWindowMs;
|
|
40
|
-
}
|
|
41
|
-
function isAlreadyExists(err) {
|
|
42
|
-
return /already exists/i.test(extractMessage(err));
|
|
43
|
-
}
|
|
44
|
-
function isInstanceNotFound(err) {
|
|
45
|
-
return /instance.*not.?found|not.?found.*instance/i.test(extractMessage(err));
|
|
46
|
-
}
|
|
47
|
-
function extractMessage(err) {
|
|
48
|
-
return err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : "";
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function resolveCurrentSyncInstanceId(accountId, recordedInstanceId) {
|
|
52
|
-
return recordedInstanceId ?? accountId;
|
|
53
|
-
}
|
|
54
|
-
function isSupersededBy(myInstanceId, recordedInstanceId, accountId) {
|
|
55
|
-
if (recordedInstanceId == null) return false;
|
|
56
|
-
return parseInstanceWindow(recordedInstanceId, accountId) > parseInstanceWindow(myInstanceId, accountId);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
12
|
const createPaymentCommand = (db, { payment }) => {
|
|
60
13
|
return {
|
|
61
14
|
command: db.insert(paymentDirection.tables.payment).values({
|
|
@@ -408,11 +361,7 @@ const initiateConnector = async ({
|
|
|
408
361
|
}
|
|
409
362
|
};
|
|
410
363
|
|
|
411
|
-
exports.DISPATCH_RECOVERY_WINDOW_MS = DISPATCH_RECOVERY_WINDOW_MS;
|
|
412
|
-
exports.buildDispatchCreateOptions = buildDispatchCreateOptions;
|
|
413
|
-
exports.buildDispatchInstanceId = buildDispatchInstanceId;
|
|
414
364
|
exports.checksum = checksum;
|
|
415
|
-
exports.computeWindowDeadline = computeWindowDeadline;
|
|
416
365
|
exports.createCredentialsResolver = createCredentialsResolver;
|
|
417
366
|
exports.createPaymentCommand = createPaymentCommand;
|
|
418
367
|
exports.encrypt = encrypt;
|
|
@@ -421,9 +370,5 @@ exports.getBatchByIdQuery = getBatchByIdQuery;
|
|
|
421
370
|
exports.getPaymentRequestsByBatchIdQuery = getPaymentRequestsByBatchIdQuery;
|
|
422
371
|
exports.importAesKey = importAesKey;
|
|
423
372
|
exports.initiateConnector = initiateConnector;
|
|
424
|
-
exports.isAlreadyExists = isAlreadyExists;
|
|
425
|
-
exports.isInstanceNotFound = isInstanceNotFound;
|
|
426
|
-
exports.isSupersededBy = isSupersededBy;
|
|
427
|
-
exports.resolveCurrentSyncInstanceId = resolveCurrentSyncInstanceId;
|
|
428
373
|
exports.updatePaymentRequestStatusCommand = updatePaymentRequestStatusCommand;
|
|
429
374
|
exports.upsertBatchCommand = upsertBatchCommand;
|