@develit-services/bank 5.7.1 → 5.9.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/base.cjs +296 -32
- package/dist/base.d.cts +38 -11
- package/dist/base.d.mts +38 -11
- package/dist/base.d.ts +38 -11
- package/dist/base.mjs +296 -32
- package/dist/database/schema.cjs +1 -1
- package/dist/database/schema.d.cts +1 -1
- package/dist/database/schema.d.mts +1 -1
- package/dist/database/schema.d.ts +1 -1
- package/dist/database/schema.mjs +1 -1
- package/dist/export/workflows.cjs +213 -42
- package/dist/export/workflows.d.cts +1 -0
- package/dist/export/workflows.d.mts +1 -0
- package/dist/export/workflows.d.ts +1 -0
- package/dist/export/workflows.mjs +214 -43
- package/dist/service.d.cts +3 -0
- package/dist/service.d.mts +3 -0
- package/dist/service.d.ts +3 -0
- package/dist/shared/{bank.BzDNLxB_.mjs → bank.BT7HayCV.mjs} +4 -0
- package/dist/shared/{bank.BUzoc8p6.d.cts → bank.Bhh_O_5-.d.mts} +1 -1
- package/dist/shared/{bank.DZ3Ow4bP.d.mts → bank.BhyIbhvK.d.cts} +1 -1
- package/dist/shared/{bank.CBpXmaTL.d.ts → bank.Bnc_DoG4.d.ts} +1 -1
- package/dist/shared/{bank.CQjuudum.cjs → bank.BsWBG0gb.cjs} +1 -1
- package/dist/shared/{bank.9Yw4KHyl.cjs → bank.Ca3jzmIb.cjs} +4 -0
- package/dist/shared/{bank.DLU1sOBm.mjs → bank.DAjqsBQN.mjs} +30 -3
- package/dist/shared/{bank.B0DNtuUM.cjs → bank.DBTtRCcf.cjs} +33 -2
- package/dist/shared/{bank.BVXtqHnq.mjs → bank.kz-PKVi5.mjs} +1 -1
- package/dist/shared/{bank.BQwwtIR4.d.ts → bank.mkq4IlpC.d.cts} +68 -2
- package/dist/shared/{bank.BQwwtIR4.d.cts → bank.mkq4IlpC.d.mts} +68 -2
- package/dist/shared/{bank.BQwwtIR4.d.mts → bank.mkq4IlpC.d.ts} +68 -2
- package/dist/types.cjs +2 -2
- package/dist/types.d.cts +4 -4
- package/dist/types.d.mts +4 -4
- package/dist/types.d.ts +4 -4
- package/dist/types.mjs +2 -2
- package/package.json +1 -1
package/dist/base.mjs
CHANGED
|
@@ -1,33 +1,49 @@
|
|
|
1
1
|
import { uuidv4, first, buildMultiFilterConditions as buildMultiFilterConditions$1, bankAccountMetadataSchema, structuredAddressSchema, workflowInstanceStatusSchema, develitWorker, createInternalError, action, service } from '@develit-io/backend-sdk';
|
|
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.
|
|
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 { j as encrypt, d as createCredentialsResolver, i as initiateConnector, b as getAccountByIdQuery, p as parseIterationBudgetParam, e as updatePaymentRequestStatusCommand, a as getPaymentRequestsByBatchIdQuery, g as getBatchByIdQuery, u as upsertBatchCommand, k as resolveCurrentSyncInstanceId, l as importAesKey, h as createPaymentCommand } from './shared/bank.DAjqsBQN.mjs';
|
|
3
4
|
import { eq, sql, and, like, asc, desc, inArray, gte, lte, isNull, count } from 'drizzle-orm';
|
|
4
5
|
import { WorkerEntrypoint } from 'cloudflare:workers';
|
|
5
6
|
import { drizzle } from 'drizzle-orm/d1';
|
|
6
7
|
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.DLU1sOBm.mjs';
|
|
8
8
|
import { z } from 'zod';
|
|
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.
|
|
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';
|
|
11
11
|
import 'date-fns';
|
|
12
12
|
import 'node:crypto';
|
|
13
13
|
import 'drizzle-orm/zod';
|
|
14
14
|
import 'drizzle-orm/sqlite-core';
|
|
15
15
|
|
|
16
|
+
const STALE_SYNC_INTERVAL_MULTIPLIER = 3;
|
|
17
|
+
function assessSyncStaleness({ syncIntervalS, lastSyncAt, createdAt }, nowMs, minGraceMs) {
|
|
18
|
+
const baseline = lastSyncAt ?? createdAt;
|
|
19
|
+
if (baseline == null) return null;
|
|
20
|
+
return {
|
|
21
|
+
staleForMs: nowMs - baseline.getTime(),
|
|
22
|
+
thresholdMs: Math.max(
|
|
23
|
+
syncIntervalS * STALE_SYNC_INTERVAL_MULTIPLIER * 1e3,
|
|
24
|
+
minGraceMs
|
|
25
|
+
)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
16
29
|
const DEAD_STATUSES = /* @__PURE__ */ new Set([
|
|
17
30
|
"complete",
|
|
18
31
|
"terminated",
|
|
19
32
|
"errored",
|
|
20
33
|
"unknown"
|
|
21
34
|
]);
|
|
35
|
+
const STALE_MIN_GRACE_MS = 15 * 60 * 1e3;
|
|
22
36
|
async function heartbeatSyncWorkflows({
|
|
23
37
|
entities,
|
|
24
38
|
resetAfterIterations,
|
|
25
39
|
getInstance,
|
|
26
40
|
createInstance,
|
|
27
|
-
logger
|
|
41
|
+
logger,
|
|
42
|
+
now = Date.now
|
|
28
43
|
}) {
|
|
29
44
|
await Promise.all(
|
|
30
|
-
entities.map(async (
|
|
45
|
+
entities.map(async (entity) => {
|
|
46
|
+
const { id, iterationCount, lastSyncAt } = entity;
|
|
31
47
|
try {
|
|
32
48
|
const instance = await getInstance(id);
|
|
33
49
|
const { status } = await instance.status();
|
|
@@ -39,6 +55,24 @@ async function heartbeatSyncWorkflows({
|
|
|
39
55
|
});
|
|
40
56
|
return;
|
|
41
57
|
}
|
|
58
|
+
const staleness = assessSyncStaleness(entity, now(), STALE_MIN_GRACE_MS);
|
|
59
|
+
if (staleness == null) {
|
|
60
|
+
logger.warn("sync-workflow.heartbeat.sync-age-unknown", {
|
|
61
|
+
id,
|
|
62
|
+
status
|
|
63
|
+
});
|
|
64
|
+
} else if (staleness.staleForMs > staleness.thresholdMs) {
|
|
65
|
+
await instance.restart();
|
|
66
|
+
logger.info("sync-workflow.heartbeat.stale-recovery", {
|
|
67
|
+
id,
|
|
68
|
+
status,
|
|
69
|
+
iterationCount,
|
|
70
|
+
lastSyncAt: lastSyncAt?.toISOString() ?? null,
|
|
71
|
+
staleForS: Math.round(staleness.staleForMs / 1e3),
|
|
72
|
+
thresholdS: Math.round(staleness.thresholdMs / 1e3)
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
42
76
|
if (iterationCount == null) {
|
|
43
77
|
logger.warn("sync-workflow.heartbeat.iteration-count-missing", {
|
|
44
78
|
id,
|
|
@@ -71,6 +105,88 @@ async function heartbeatSyncWorkflows({
|
|
|
71
105
|
);
|
|
72
106
|
}
|
|
73
107
|
|
|
108
|
+
const DISPATCH_RECOVERY_WINDOW_MS = 5 * 60 * 1e3;
|
|
109
|
+
function buildDispatchInstanceId(accountId, nowMs, recoveryWindowMs = DISPATCH_RECOVERY_WINDOW_MS) {
|
|
110
|
+
return `${accountId}-${Math.floor(nowMs / recoveryWindowMs)}`;
|
|
111
|
+
}
|
|
112
|
+
function buildDispatchCreateOptions(instanceId, accountId, maxIterations) {
|
|
113
|
+
return {
|
|
114
|
+
id: instanceId,
|
|
115
|
+
// Omitted (not undefined) when unresolved: the workflow treats a missing
|
|
116
|
+
// budget as unbounded — degraded but syncing.
|
|
117
|
+
params: maxIterations == null ? { accountId } : { accountId, maxIterations },
|
|
118
|
+
// Dispatch mints ~24 instances per account per day and step outputs carry
|
|
119
|
+
// bank payloads — the default 30-day retention would pile up billable
|
|
120
|
+
// storage for state nobody reads after success.
|
|
121
|
+
retention: {
|
|
122
|
+
successRetention: "1 day",
|
|
123
|
+
errorRetention: "7 days"
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function isAlreadyExists(err) {
|
|
128
|
+
const message = err instanceof Error ? err.message : typeof err === "object" && err !== null && "message" in err ? String(err.message) : "";
|
|
129
|
+
return /already exists/i.test(message);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const DEFAULT_MIN_GRACE_MS = 3 * 60 * 1e3;
|
|
133
|
+
async function dispatchSyncWorkflows({
|
|
134
|
+
entities,
|
|
135
|
+
createInstance,
|
|
136
|
+
logger,
|
|
137
|
+
now = Date.now,
|
|
138
|
+
minGraceMs = DEFAULT_MIN_GRACE_MS,
|
|
139
|
+
recoveryWindowMs = DISPATCH_RECOVERY_WINDOW_MS
|
|
140
|
+
}) {
|
|
141
|
+
const at = now();
|
|
142
|
+
await Promise.all(
|
|
143
|
+
entities.map(async (entity) => {
|
|
144
|
+
const { id, lastSyncAt } = entity;
|
|
145
|
+
const staleness = assessSyncStaleness(entity, at, minGraceMs);
|
|
146
|
+
if (staleness == null) {
|
|
147
|
+
logger.warn("sync-workflow.dispatch.sync-age-unknown", { id });
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (staleness.staleForMs <= staleness.thresholdMs) return;
|
|
151
|
+
const instanceId = buildDispatchInstanceId(id, at, recoveryWindowMs);
|
|
152
|
+
try {
|
|
153
|
+
await createInstance(instanceId, id);
|
|
154
|
+
logger.info("sync-workflow.dispatch.created", {
|
|
155
|
+
id,
|
|
156
|
+
instanceId,
|
|
157
|
+
lastSyncAt: lastSyncAt?.toISOString() ?? null,
|
|
158
|
+
staleForS: Math.round(staleness.staleForMs / 1e3),
|
|
159
|
+
thresholdS: Math.round(staleness.thresholdMs / 1e3)
|
|
160
|
+
});
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (isAlreadyExists(err)) {
|
|
163
|
+
logger.info("sync-workflow.dispatch.already-running", {
|
|
164
|
+
id,
|
|
165
|
+
instanceId
|
|
166
|
+
});
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
logger.error("sync-workflow.dispatch.failed", {
|
|
170
|
+
id,
|
|
171
|
+
instanceId,
|
|
172
|
+
error: err instanceof Error ? err.message : String(err)
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isDispatchEnabled(accountId, selection) {
|
|
180
|
+
const raw = selection?.trim();
|
|
181
|
+
if (!raw) return false;
|
|
182
|
+
if (raw.toLowerCase() === "all") return true;
|
|
183
|
+
return raw.split(",").map((id) => id.trim()).includes(accountId);
|
|
184
|
+
}
|
|
185
|
+
function isDispatchActive(accountId, selection, dispatchCron) {
|
|
186
|
+
if (!dispatchCron?.trim()) return false;
|
|
187
|
+
return isDispatchEnabled(accountId, selection);
|
|
188
|
+
}
|
|
189
|
+
|
|
74
190
|
const FINITE_STATUSES = /* @__PURE__ */ new Set([
|
|
75
191
|
"complete",
|
|
76
192
|
"errored",
|
|
@@ -90,6 +206,26 @@ function isAlreadyFiniteError(err) {
|
|
|
90
206
|
return /cannot_terminate|finite state/i.test(message);
|
|
91
207
|
}
|
|
92
208
|
|
|
209
|
+
async function disableAccountSync({
|
|
210
|
+
accountId,
|
|
211
|
+
persistDisabled,
|
|
212
|
+
terminateInstance,
|
|
213
|
+
logger
|
|
214
|
+
}) {
|
|
215
|
+
await persistDisabled();
|
|
216
|
+
try {
|
|
217
|
+
const instanceId = await terminateInstance();
|
|
218
|
+
logger.info("sync-workflow.disable.terminated", { accountId, instanceId });
|
|
219
|
+
return { terminated: true, instanceId };
|
|
220
|
+
} catch (err) {
|
|
221
|
+
logger.warn("sync-workflow.disable.terminate-skipped", {
|
|
222
|
+
accountId,
|
|
223
|
+
error: err instanceof Error ? err.message : String(err)
|
|
224
|
+
});
|
|
225
|
+
return { terminated: false, instanceId: null };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
93
229
|
const upsertAccountCommand = (db, { account }) => {
|
|
94
230
|
const id = account.id || uuidv4();
|
|
95
231
|
const { id: _id, ...accountWithoutId } = account;
|
|
@@ -177,6 +313,13 @@ const createPaymentRequestCommand = (db, { paymentRequest }) => {
|
|
|
177
313
|
};
|
|
178
314
|
};
|
|
179
315
|
|
|
316
|
+
const updateAccountSyncEnabledCommand = (db, { accountId, syncEnabled }) => {
|
|
317
|
+
const command = db.update(tables.account).set({ syncEnabled }).where(eq(tables.account.id, accountId)).returning();
|
|
318
|
+
return {
|
|
319
|
+
command
|
|
320
|
+
};
|
|
321
|
+
};
|
|
322
|
+
|
|
180
323
|
const getAccountBatchCountsQuery = async (db, { accountId }) => {
|
|
181
324
|
const result = await db.select({
|
|
182
325
|
totalCount: sql`COUNT(*)`.as("totalCount"),
|
|
@@ -446,6 +589,21 @@ const getPaymentRequestsQuery = async (db, params) => {
|
|
|
446
589
|
return { paymentRequests, totalCount };
|
|
447
590
|
};
|
|
448
591
|
|
|
592
|
+
const getSyncCandidateAccountsQuery = async (db) => {
|
|
593
|
+
return await db.select({
|
|
594
|
+
id: tables.account.id,
|
|
595
|
+
syncIntervalS: tables.account.syncIntervalS,
|
|
596
|
+
lastSyncAt: tables.account.lastSyncAt,
|
|
597
|
+
lastSyncMetadata: tables.account.lastSyncMetadata,
|
|
598
|
+
createdAt: tables.account.createdAt
|
|
599
|
+
}).from(tables.account).where(
|
|
600
|
+
and(
|
|
601
|
+
eq(tables.account.status, "AUTHORIZED"),
|
|
602
|
+
eq(tables.account.syncEnabled, true)
|
|
603
|
+
)
|
|
604
|
+
).all();
|
|
605
|
+
};
|
|
606
|
+
|
|
449
607
|
const SIGNAL_MAP = {
|
|
450
608
|
OPENED: "paymentRequestOpened",
|
|
451
609
|
AUTHORIZED: "paymentRequestAuthorized",
|
|
@@ -672,7 +830,9 @@ const syncAccountTerminateInputSchema = z.object({
|
|
|
672
830
|
accountId: z.uuid()
|
|
673
831
|
});
|
|
674
832
|
z.object({
|
|
675
|
-
|
|
833
|
+
terminated: z.boolean(),
|
|
834
|
+
// Null when the flag was persisted but no live instance existed to kill.
|
|
835
|
+
instanceId: z.string().nullable()
|
|
676
836
|
});
|
|
677
837
|
|
|
678
838
|
const updateAccountInputSchema = z.object({
|
|
@@ -748,6 +908,16 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
748
908
|
if (kind && result) __defProp(target, key, result);
|
|
749
909
|
return result;
|
|
750
910
|
};
|
|
911
|
+
const accountNotFoundError = () => createInternalError(null, {
|
|
912
|
+
message: "Account not found",
|
|
913
|
+
code: "DB-B-005",
|
|
914
|
+
status: 404
|
|
915
|
+
});
|
|
916
|
+
const syncEventConsoleLogger = {
|
|
917
|
+
info: (event, data) => console.log(JSON.stringify({ level: "info", event, ...data })),
|
|
918
|
+
warn: (event, data) => console.warn(JSON.stringify({ level: "warn", event, ...data })),
|
|
919
|
+
error: (event, data) => console.error(JSON.stringify({ level: "error", event, ...data }))
|
|
920
|
+
};
|
|
751
921
|
let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
752
922
|
constructor(ctx, env, config) {
|
|
753
923
|
super(ctx, env);
|
|
@@ -850,6 +1020,40 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
850
1020
|
{ data: input, schema: syncAccountInputSchema },
|
|
851
1021
|
{ successMessage: "Account sync workflow started" },
|
|
852
1022
|
async ({ accountId }) => {
|
|
1023
|
+
const account = await getAccountByIdQuery(this.db, { accountId });
|
|
1024
|
+
if (!account) {
|
|
1025
|
+
throw accountNotFoundError();
|
|
1026
|
+
}
|
|
1027
|
+
if (!account.syncEnabled) {
|
|
1028
|
+
throw createInternalError(null, {
|
|
1029
|
+
message: `Account sync is disabled for ${accountId}. Enable it via restart-sync first.`,
|
|
1030
|
+
code: "VALID-B-018",
|
|
1031
|
+
status: 422
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
if (isDispatchActive(
|
|
1035
|
+
accountId,
|
|
1036
|
+
this.env.SYNC_DISPATCH_ACCOUNT_IDS,
|
|
1037
|
+
this.env.CRON_SYNC_WORKFLOW_DISPATCH
|
|
1038
|
+
)) {
|
|
1039
|
+
const instanceId = buildDispatchInstanceId(accountId, Date.now());
|
|
1040
|
+
const maxIterations = parseIterationBudgetParam(
|
|
1041
|
+
this.env.SYNC_WORKFLOW_MAX_ITERATIONS
|
|
1042
|
+
);
|
|
1043
|
+
let instance2;
|
|
1044
|
+
try {
|
|
1045
|
+
instance2 = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create(
|
|
1046
|
+
buildDispatchCreateOptions(instanceId, accountId, maxIterations)
|
|
1047
|
+
);
|
|
1048
|
+
} catch (err) {
|
|
1049
|
+
if (!isAlreadyExists(err)) throw err;
|
|
1050
|
+
instance2 = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(instanceId);
|
|
1051
|
+
}
|
|
1052
|
+
return {
|
|
1053
|
+
instanceId: instance2.id,
|
|
1054
|
+
details: await instance2.status()
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
853
1057
|
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create({
|
|
854
1058
|
id: accountId,
|
|
855
1059
|
params: {
|
|
@@ -868,7 +1072,7 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
868
1072
|
{ data: input, schema: syncAccountStatusInputSchema },
|
|
869
1073
|
{ successMessage: "Account sync workflow status retrieved" },
|
|
870
1074
|
async ({ accountId }) => {
|
|
871
|
-
const instance = await this.
|
|
1075
|
+
const instance = await this.getCurrentSyncInstance(accountId);
|
|
872
1076
|
return {
|
|
873
1077
|
instanceId: instance.id,
|
|
874
1078
|
details: await instance.status()
|
|
@@ -881,7 +1085,8 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
881
1085
|
{ data: input, schema: syncAccountRestartInputSchema },
|
|
882
1086
|
{ successMessage: "Account sync workflow restarted" },
|
|
883
1087
|
async ({ accountId }) => {
|
|
884
|
-
|
|
1088
|
+
await this.setSyncEnabledOrThrow(accountId, true);
|
|
1089
|
+
const instance = await this.getCurrentSyncInstance(accountId);
|
|
885
1090
|
await instance.restart();
|
|
886
1091
|
return {
|
|
887
1092
|
instanceId: instance.id,
|
|
@@ -895,11 +1100,16 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
895
1100
|
{ data: input, schema: syncAccountTerminateInputSchema },
|
|
896
1101
|
{ successMessage: "Account sync workflow terminated" },
|
|
897
1102
|
async ({ accountId }) => {
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1103
|
+
return disableAccountSync({
|
|
1104
|
+
accountId,
|
|
1105
|
+
persistDisabled: () => this.setSyncEnabledOrThrow(accountId, false),
|
|
1106
|
+
terminateInstance: async () => {
|
|
1107
|
+
const instance = await this.getCurrentSyncInstance(accountId);
|
|
1108
|
+
await terminateSyncWorkflow(instance);
|
|
1109
|
+
return instance.id;
|
|
1110
|
+
},
|
|
1111
|
+
logger: syncEventConsoleLogger
|
|
1112
|
+
});
|
|
903
1113
|
}
|
|
904
1114
|
);
|
|
905
1115
|
}
|
|
@@ -1179,18 +1389,61 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
1179
1389
|
if (controller.cron === this.env.CRON_PAYMENT_STATUSES) {
|
|
1180
1390
|
console.log("Scheduled CRON payment request statuses");
|
|
1181
1391
|
await this.updatePaymentRequestStatuses();
|
|
1182
|
-
|
|
1392
|
+
}
|
|
1393
|
+
if (controller.cron === this.env.CRON_SYNC_WORKFLOW_DISPATCH) {
|
|
1394
|
+
await this.dispatchSyncWorkflows();
|
|
1183
1395
|
}
|
|
1184
1396
|
if (controller.cron === this.env.CRON_SYNC_WORKFLOW_HEARTBEAT) {
|
|
1185
1397
|
console.log("Scheduled CRON sync workflow heartbeat");
|
|
1186
1398
|
await this.heartbeatSyncWorkflows();
|
|
1187
1399
|
}
|
|
1188
1400
|
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Lifecycle actions must target the instance that actually syncs the
|
|
1403
|
+
* account — under dispatch that is the windowed instance recorded with the
|
|
1404
|
+
* last sync write, not the legacy canonical id.
|
|
1405
|
+
*/
|
|
1406
|
+
async setSyncEnabledOrThrow(accountId, syncEnabled) {
|
|
1407
|
+
const [updated] = await updateAccountSyncEnabledCommand(this.db, {
|
|
1408
|
+
accountId,
|
|
1409
|
+
syncEnabled
|
|
1410
|
+
}).command;
|
|
1411
|
+
if (!updated) {
|
|
1412
|
+
throw accountNotFoundError();
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
async getCurrentSyncInstance(accountId) {
|
|
1416
|
+
const account = await getAccountByIdQuery(this.db, { accountId });
|
|
1417
|
+
return this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
1418
|
+
resolveCurrentSyncInstanceId(
|
|
1419
|
+
accountId,
|
|
1420
|
+
account?.lastSyncMetadata?.instanceId
|
|
1421
|
+
)
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
async dispatchSyncWorkflows() {
|
|
1425
|
+
const selection = this.env.SYNC_DISPATCH_ACCOUNT_IDS;
|
|
1426
|
+
if (!selection?.trim()) return;
|
|
1427
|
+
const accounts = await getSyncCandidateAccountsQuery(this.db);
|
|
1428
|
+
const maxIterations = parseIterationBudgetParam(
|
|
1429
|
+
this.env.SYNC_WORKFLOW_MAX_ITERATIONS
|
|
1430
|
+
);
|
|
1431
|
+
if (maxIterations == null) {
|
|
1432
|
+
syncEventConsoleLogger.error(
|
|
1433
|
+
"sync-workflow.dispatch.invalid-max-iterations",
|
|
1434
|
+
{ value: this.env.SYNC_WORKFLOW_MAX_ITERATIONS }
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
await dispatchSyncWorkflows({
|
|
1438
|
+
entities: accounts.filter((a) => isDispatchEnabled(a.id, selection)),
|
|
1439
|
+
createInstance: (instanceId, accountId) => this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.create(
|
|
1440
|
+
buildDispatchCreateOptions(instanceId, accountId, maxIterations)
|
|
1441
|
+
),
|
|
1442
|
+
logger: syncEventConsoleLogger
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1189
1445
|
async heartbeatSyncWorkflows() {
|
|
1190
|
-
const accounts = await this.db
|
|
1191
|
-
id: tables.account.id,
|
|
1192
|
-
lastSyncMetadata: tables.account.lastSyncMetadata
|
|
1193
|
-
}).from(tables.account).where(eq(tables.account.status, "AUTHORIZED")).all();
|
|
1446
|
+
const accounts = await getSyncCandidateAccountsQuery(this.db);
|
|
1194
1447
|
const resetAfterIterations = Number(
|
|
1195
1448
|
this.env.SYNC_WORKFLOW_RESET_AFTER_ITERATIONS
|
|
1196
1449
|
);
|
|
@@ -1202,9 +1455,18 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
1202
1455
|
return;
|
|
1203
1456
|
}
|
|
1204
1457
|
await heartbeatSyncWorkflows({
|
|
1205
|
-
entities: accounts.
|
|
1458
|
+
entities: accounts.filter(
|
|
1459
|
+
(a) => !isDispatchActive(
|
|
1460
|
+
a.id,
|
|
1461
|
+
this.env.SYNC_DISPATCH_ACCOUNT_IDS,
|
|
1462
|
+
this.env.CRON_SYNC_WORKFLOW_DISPATCH
|
|
1463
|
+
)
|
|
1464
|
+
).map((a) => ({
|
|
1206
1465
|
id: a.id,
|
|
1207
|
-
iterationCount: a.lastSyncMetadata?.iterationCount
|
|
1466
|
+
iterationCount: a.lastSyncMetadata?.iterationCount,
|
|
1467
|
+
syncIntervalS: a.syncIntervalS,
|
|
1468
|
+
lastSyncAt: a.lastSyncAt,
|
|
1469
|
+
createdAt: a.createdAt
|
|
1208
1470
|
})),
|
|
1209
1471
|
resetAfterIterations,
|
|
1210
1472
|
getInstance: (id) => this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(id),
|
|
@@ -1212,11 +1474,7 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
1212
1474
|
id,
|
|
1213
1475
|
params: { accountId: id }
|
|
1214
1476
|
}),
|
|
1215
|
-
logger:
|
|
1216
|
-
info: (event, data) => console.log(JSON.stringify({ level: "info", event, ...data })),
|
|
1217
|
-
warn: (event, data) => console.warn(JSON.stringify({ level: "warn", event, ...data })),
|
|
1218
|
-
error: (event, data) => console.error(JSON.stringify({ level: "error", event, ...data }))
|
|
1219
|
-
}
|
|
1477
|
+
logger: syncEventConsoleLogger
|
|
1220
1478
|
});
|
|
1221
1479
|
}
|
|
1222
1480
|
async handleAuthorizationCallback(input) {
|
|
@@ -1975,7 +2233,12 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
1975
2233
|
if (includeWorkflow) {
|
|
1976
2234
|
let status;
|
|
1977
2235
|
try {
|
|
1978
|
-
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
2236
|
+
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
2237
|
+
resolveCurrentSyncInstanceId(
|
|
2238
|
+
a.id,
|
|
2239
|
+
a.lastSyncMetadata?.instanceId
|
|
2240
|
+
)
|
|
2241
|
+
);
|
|
1979
2242
|
status = await instance.status();
|
|
1980
2243
|
} catch (_) {
|
|
1981
2244
|
status = null;
|
|
@@ -2072,14 +2335,15 @@ let BankServiceBase = class extends develitWorker(WorkerEntrypoint) {
|
|
|
2072
2335
|
async ({ accountId }) => {
|
|
2073
2336
|
const account = await getAccountByIdQuery(this.db, { accountId });
|
|
2074
2337
|
if (!account) {
|
|
2075
|
-
throw
|
|
2076
|
-
message: "Account not found",
|
|
2077
|
-
code: "DB-B-005",
|
|
2078
|
-
status: 404
|
|
2079
|
-
});
|
|
2338
|
+
throw accountNotFoundError();
|
|
2080
2339
|
}
|
|
2081
2340
|
try {
|
|
2082
|
-
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
2341
|
+
const instance = await this.env.SYNC_ACCOUNT_PAYMENTS_WORKFLOW.get(
|
|
2342
|
+
resolveCurrentSyncInstanceId(
|
|
2343
|
+
accountId,
|
|
2344
|
+
account.lastSyncMetadata?.instanceId
|
|
2345
|
+
)
|
|
2346
|
+
);
|
|
2083
2347
|
await terminateSyncWorkflow(instance);
|
|
2084
2348
|
} catch (error) {
|
|
2085
2349
|
this.log({
|
package/dist/database/schema.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { aC as account, aD as accountCredentials, aE as batch, aF as ott, aG as payment, aH as paymentRequest } from '../shared/bank.
|
|
1
|
+
export { aC as account, aD as accountCredentials, aE as batch, aF as ott, aG as payment, aH as paymentRequest } from '../shared/bank.mkq4IlpC.cjs';
|
|
2
2
|
import 'drizzle-orm/sqlite-core';
|
|
3
3
|
import 'drizzle-orm';
|
|
4
4
|
import '@develit-io/general-codes';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { aC as account, aD as accountCredentials, aE as batch, aF as ott, aG as payment, aH as paymentRequest } from '../shared/bank.
|
|
1
|
+
export { aC as account, aD as accountCredentials, aE as batch, aF as ott, aG as payment, aH as paymentRequest } from '../shared/bank.mkq4IlpC.mjs';
|
|
2
2
|
import 'drizzle-orm/sqlite-core';
|
|
3
3
|
import 'drizzle-orm';
|
|
4
4
|
import '@develit-io/general-codes';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { aC as account, aD as accountCredentials, aE as batch, aF as ott, aG as payment, aH as paymentRequest } from '../shared/bank.
|
|
1
|
+
export { aC as account, aD as accountCredentials, aE as batch, aF as ott, aG as payment, aH as paymentRequest } from '../shared/bank.mkq4IlpC.js';
|
|
2
2
|
import 'drizzle-orm/sqlite-core';
|
|
3
3
|
import 'drizzle-orm';
|
|
4
4
|
import '@develit-io/general-codes';
|
package/dist/database/schema.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { h as account, i as accountCredentials, j as batch, o as ott, p as payment, k as paymentRequest } from '../shared/bank.
|
|
1
|
+
export { h as account, i as accountCredentials, j as batch, o as ott, p as payment, k as paymentRequest } from '../shared/bank.BT7HayCV.mjs';
|
|
2
2
|
import '@develit-io/backend-sdk';
|
|
3
3
|
import 'drizzle-orm/sqlite-core';
|
|
4
4
|
import '@develit-io/general-codes';
|