akeyless-server-commons 1.0.206 → 1.0.207
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/cjs/helpers/index.d.ts +1 -0
- package/dist/cjs/helpers/index.js +1 -0
- package/dist/cjs/helpers/index.js.map +1 -1
- package/dist/cjs/helpers/postponed_actions.d.ts +7 -17
- package/dist/cjs/helpers/postponed_actions.js +25 -26
- package/dist/cjs/helpers/postponed_actions.js.map +1 -1
- package/dist/cjs/helpers/tasks.d.ts +12 -0
- package/dist/cjs/helpers/tasks.js +62 -0
- package/dist/cjs/helpers/tasks.js.map +1 -0
- package/dist/cjs/managers/cacche_manager2.d.ts +18 -0
- package/dist/cjs/managers/cacche_manager2.js +41 -0
- package/dist/cjs/managers/cacche_manager2.js.map +1 -0
- package/dist/cjs/managers/socket_manager.js +245 -0
- package/dist/cjs/managers/socket_manager.js.map +1 -0
- package/dist/esm/helpers/index.d.ts +1 -0
- package/dist/esm/helpers/index.js +1 -0
- package/dist/esm/helpers/index.js.map +1 -1
- package/dist/esm/helpers/postponed_actions.d.ts +7 -17
- package/dist/esm/helpers/postponed_actions.js +12 -12
- package/dist/esm/helpers/postponed_actions.js.map +1 -1
- package/dist/esm/helpers/tasks.d.ts +12 -0
- package/dist/esm/helpers/tasks.js +58 -0
- package/dist/esm/helpers/tasks.js.map +1 -0
- package/dist/esm/managers/cacche_manager2.d.ts +18 -0
- package/dist/esm/managers/cacche_manager2.js +37 -0
- package/dist/esm/managers/cacche_manager2.js.map +1 -0
- package/dist/esm/managers/socket_manager.js +243 -0
- package/dist/esm/managers/socket_manager.js.map +1 -0
- package/package.json +1 -1
- package/dist/cjs/helpers/rabbitmq_helpers.d.ts +0 -64
- package/dist/cjs/helpers/rabbitmq_helpers.js +0 -193
- package/dist/cjs/helpers/rabbitmq_helpers.js.map +0 -1
- package/dist/esm/helpers/rabbitmq_helpers.d.ts +0 -64
- package/dist/esm/helpers/rabbitmq_helpers.js +0 -186
- package/dist/esm/helpers/rabbitmq_helpers.js.map +0 -1
- package/dist/helpers/boards_helpers.js +0 -62
- package/dist/helpers/email_helpers.js +0 -153
- package/dist/helpers/firebase_helpers.js +0 -601
- package/dist/helpers/global_helpers.js +0 -147
- package/dist/helpers/index.js +0 -13
- package/dist/helpers/location_helpers.js +0 -15
- package/dist/helpers/login_helpers.js +0 -18
- package/dist/helpers/notification_helpers.js +0 -248
- package/dist/helpers/phone_number_helpers.js +0 -96
- package/dist/helpers/postponed_actions.js +0 -358
- package/dist/helpers/redis/helpers.js +0 -215
- package/dist/helpers/redis/index.js +0 -4
- package/dist/helpers/redis/initialize.js +0 -139
- package/dist/helpers/redis/keys.js +0 -23
- package/dist/helpers/redis/snapshot.js +0 -166
- package/dist/helpers/start.js +0 -83
- package/dist/helpers/tasks_helpers.js +0 -139
- package/dist/helpers/time_helpers.js +0 -167
- package/dist/index.js +0 -5
- package/dist/managers/cache_manager.js +0 -37
- package/dist/managers/index.js +0 -4
- package/dist/managers/logger_manager.js +0 -68
- package/dist/managers/rabbitmq_manager.js +0 -182
- package/dist/managers/translation_manager.js +0 -38
- package/dist/middlewares/auth_mw.js +0 -50
- package/dist/middlewares/error_handling.js +0 -20
- package/dist/middlewares/global_mw.js +0 -119
- package/dist/middlewares/index.js +0 -4
- package/dist/middlewares/trim_mw.js +0 -7
- package/dist/test.js +0 -1
- package/dist/types/enums/global.js +0 -8
- package/dist/types/enums/index.js +0 -1
- package/dist/types/helpers/boards_helpers.d.ts +0 -10
- package/dist/types/helpers/email_helpers.d.ts +0 -12
- package/dist/types/helpers/firebase_helpers.d.ts +0 -56
- package/dist/types/helpers/global_helpers.d.ts +0 -16
- package/dist/types/helpers/index.d.ts +0 -13
- package/dist/types/helpers/location_helpers.d.ts +0 -3
- package/dist/types/helpers/login_helpers.d.ts +0 -4
- package/dist/types/helpers/notification_helpers.d.ts +0 -11
- package/dist/types/helpers/phone_number_helpers.d.ts +0 -19
- package/dist/types/helpers/postponed_actions.d.ts +0 -113
- package/dist/types/helpers/redis/helpers.d.ts +0 -11
- package/dist/types/helpers/redis/index.d.ts +0 -4
- package/dist/types/helpers/redis/initialize.d.ts +0 -6
- package/dist/types/helpers/redis/keys.d.ts +0 -5
- package/dist/types/helpers/redis/snapshot.d.ts +0 -2
- package/dist/types/helpers/start.d.ts +0 -4
- package/dist/types/helpers/tasks_helpers.d.ts +0 -25
- package/dist/types/helpers/time_helpers.d.ts +0 -40
- package/dist/types/index.d.ts +0 -5
- package/dist/types/index.js +0 -3
- package/dist/types/interfaces/index.js +0 -2
- package/dist/types/managers/cache_manager.d.ts +0 -12
- package/dist/types/managers/index.d.ts +0 -4
- package/dist/types/managers/logger_manager.d.ts +0 -14
- package/dist/types/managers/rabbitmq_manager.d.ts +0 -60
- package/dist/types/managers/translation_manager.d.ts +0 -14
- package/dist/types/middlewares/auth_mw.d.ts +0 -4
- package/dist/types/middlewares/error_handling.d.ts +0 -11
- package/dist/types/middlewares/global_mw.d.ts +0 -5
- package/dist/types/middlewares/index.d.ts +0 -4
- package/dist/types/middlewares/trim_mw.d.ts +0 -2
- package/dist/types/test.d.ts +0 -0
- package/dist/types/types/enums/global.d.ts +0 -9
- package/dist/types/types/enums/index.d.ts +0 -1
- package/dist/types/types/firebase_types.js +0 -1
- package/dist/types/types/global.js +0 -1
- package/dist/types/types/index.d.ts +0 -3
- package/dist/types/types/index.js +0 -2
- package/dist/types/types/interfaces/email.d.ts +0 -33
- package/dist/types/types/interfaces/global.d.ts +0 -27
- package/dist/types/types/interfaces/index.d.ts +0 -2
- package/dist/types/types/types/firebase_types.d.ts +0 -50
- package/dist/types/types/types/global.d.ts +0 -17
- package/dist/types/types/types/index.d.ts +0 -2
- /package/dist/{types/interfaces/email.js → cjs/managers/socket_manager.d.ts} +0 -0
- /package/dist/{types/interfaces/global.js → esm/managers/socket_manager.d.ts} +0 -0
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
import { FieldValue, Timestamp } from "firebase-admin/firestore";
|
|
2
|
-
import { db } from "./firebase_helpers";
|
|
3
|
-
import { logger } from "../managers";
|
|
4
|
-
const POSTPONED_ACTIONS_COLLECTION = "nx-postponed-actions";
|
|
5
|
-
export var PostponedActionStatus;
|
|
6
|
-
(function (PostponedActionStatus) {
|
|
7
|
-
PostponedActionStatus["pending"] = "pending";
|
|
8
|
-
PostponedActionStatus["processing"] = "processing";
|
|
9
|
-
PostponedActionStatus["completed"] = "completed";
|
|
10
|
-
PostponedActionStatus["cancelled"] = "cancelled";
|
|
11
|
-
PostponedActionStatus["failed"] = "failed";
|
|
12
|
-
})(PostponedActionStatus || (PostponedActionStatus = {}));
|
|
13
|
-
const get_ref = (key) => {
|
|
14
|
-
if (!key.trim()) {
|
|
15
|
-
throw new Error("postponed action key must not be empty");
|
|
16
|
-
}
|
|
17
|
-
if (key.includes("/")) {
|
|
18
|
-
throw new Error("postponed action key must not contain '/'");
|
|
19
|
-
}
|
|
20
|
-
return db.collection(POSTPONED_ACTIONS_COLLECTION).doc(key);
|
|
21
|
-
};
|
|
22
|
-
export const resolve_postponed_action_time = (value, now = new Date()) => {
|
|
23
|
-
if (value instanceof Date || typeof value === "string") {
|
|
24
|
-
const date = value instanceof Date ? new Date(value.getTime()) : new Date(value);
|
|
25
|
-
if (!Number.isFinite(date.getTime())) {
|
|
26
|
-
throw new Error("execute_at must be a valid date");
|
|
27
|
-
}
|
|
28
|
-
return date;
|
|
29
|
-
}
|
|
30
|
-
const { minutes = 0, hours = 0 } = value;
|
|
31
|
-
if (!Number.isFinite(minutes) || !Number.isFinite(hours) || minutes < 0 || hours < 0 || minutes + hours <= 0) {
|
|
32
|
-
throw new Error("relative execute_at must contain positive finite minutes or hours");
|
|
33
|
-
}
|
|
34
|
-
return new Date(now.getTime() + (minutes + hours * 60) * 60_000);
|
|
35
|
-
};
|
|
36
|
-
const validate_text = (value, field_name) => {
|
|
37
|
-
if (!value.trim()) {
|
|
38
|
-
throw new Error(`postponed action ${field_name} must not be empty`);
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
const validate_details = (details) => {
|
|
42
|
-
if (!details || Array.isArray(details) || typeof details !== "object") {
|
|
43
|
-
throw new Error("postponed action details must be a JSON object");
|
|
44
|
-
}
|
|
45
|
-
try {
|
|
46
|
-
JSON.stringify(details, (_key, value) => {
|
|
47
|
-
if (value === undefined || typeof value === "bigint" || typeof value === "function" || typeof value === "symbol") {
|
|
48
|
-
throw new Error("unsupported JSON value");
|
|
49
|
-
}
|
|
50
|
-
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
51
|
-
throw new Error("JSON numbers must be finite");
|
|
52
|
-
}
|
|
53
|
-
return value;
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
throw new Error("postponed action details must be JSON serializable", { cause: error });
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
const timestamp_to_date = (value, field_name) => {
|
|
61
|
-
if (value instanceof Timestamp)
|
|
62
|
-
return value.toDate();
|
|
63
|
-
if (value instanceof Date)
|
|
64
|
-
return value;
|
|
65
|
-
throw new Error(`postponed action has invalid ${field_name}`);
|
|
66
|
-
};
|
|
67
|
-
const optional_timestamp_to_date = (value, field_name) => {
|
|
68
|
-
return value === undefined ? undefined : timestamp_to_date(value, field_name);
|
|
69
|
-
};
|
|
70
|
-
const to_postponed_action = (snapshot) => {
|
|
71
|
-
const value = snapshot.data();
|
|
72
|
-
if (!snapshot.exists || !value) {
|
|
73
|
-
throw new Error(`postponed action '${snapshot.id}' does not exist`);
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
key: snapshot.id,
|
|
77
|
-
details: value.details,
|
|
78
|
-
action_type: value.action_type,
|
|
79
|
-
description: value.description,
|
|
80
|
-
execute_at: timestamp_to_date(value.execute_at, "execute_at"),
|
|
81
|
-
status: value.status,
|
|
82
|
-
attempt_count: typeof value.attempt_count === "number" ? value.attempt_count : 0,
|
|
83
|
-
created_at: timestamp_to_date(value.created_at, "created_at"),
|
|
84
|
-
updated_at: timestamp_to_date(value.updated_at, "updated_at"),
|
|
85
|
-
started_at: optional_timestamp_to_date(value.started_at, "started_at"),
|
|
86
|
-
completed_at: optional_timestamp_to_date(value.completed_at, "completed_at"),
|
|
87
|
-
cancelled_at: optional_timestamp_to_date(value.cancelled_at, "cancelled_at"),
|
|
88
|
-
failed_at: optional_timestamp_to_date(value.failed_at, "failed_at"),
|
|
89
|
-
error: typeof value.error === "string" ? value.error : undefined,
|
|
90
|
-
};
|
|
91
|
-
};
|
|
92
|
-
const assert_status = (key, actual, allowed) => {
|
|
93
|
-
if (!allowed.includes(actual)) {
|
|
94
|
-
throw new Error(`postponed action '${key}' has status '${String(actual)}'; expected ${allowed.join(" or ")}`);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
const get_postponed_action_core = async (key) => {
|
|
98
|
-
const snapshot = await get_ref(key).get();
|
|
99
|
-
return snapshot.exists ? to_postponed_action(snapshot) : null;
|
|
100
|
-
};
|
|
101
|
-
const add_postponed_action_core = async (input) => {
|
|
102
|
-
validate_details(input.details);
|
|
103
|
-
validate_text(input.action_type, "action_type");
|
|
104
|
-
validate_text(input.description, "description");
|
|
105
|
-
const ref = get_ref(input.key);
|
|
106
|
-
const execute_at = resolve_postponed_action_time(input.execute_at);
|
|
107
|
-
const now = new Date();
|
|
108
|
-
return db.runTransaction(async (transaction) => {
|
|
109
|
-
const snapshot = await transaction.get(ref);
|
|
110
|
-
if (snapshot.exists) {
|
|
111
|
-
assert_status(input.key, snapshot.get("status"), [PostponedActionStatus.pending]);
|
|
112
|
-
transaction.update(ref, {
|
|
113
|
-
details: input.details,
|
|
114
|
-
action_type: input.action_type,
|
|
115
|
-
description: input.description,
|
|
116
|
-
execute_at,
|
|
117
|
-
updated_at: now,
|
|
118
|
-
});
|
|
119
|
-
return {
|
|
120
|
-
...to_postponed_action(snapshot),
|
|
121
|
-
details: input.details,
|
|
122
|
-
action_type: input.action_type,
|
|
123
|
-
description: input.description,
|
|
124
|
-
execute_at,
|
|
125
|
-
updated_at: now,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
const action = {
|
|
129
|
-
key: input.key,
|
|
130
|
-
details: input.details,
|
|
131
|
-
action_type: input.action_type,
|
|
132
|
-
description: input.description,
|
|
133
|
-
execute_at,
|
|
134
|
-
status: PostponedActionStatus.pending,
|
|
135
|
-
attempt_count: 0,
|
|
136
|
-
created_at: now,
|
|
137
|
-
updated_at: now,
|
|
138
|
-
};
|
|
139
|
-
transaction.create(ref, action);
|
|
140
|
-
return action;
|
|
141
|
-
});
|
|
142
|
-
};
|
|
143
|
-
const update_postponed_action_core = async (key, input) => {
|
|
144
|
-
if (input.details === undefined && input.action_type === undefined && input.description === undefined && input.execute_at === undefined) {
|
|
145
|
-
throw new Error("postponed action update must contain details, action_type, description or execute_at");
|
|
146
|
-
}
|
|
147
|
-
if (input.details !== undefined)
|
|
148
|
-
validate_details(input.details);
|
|
149
|
-
if (input.action_type !== undefined)
|
|
150
|
-
validate_text(input.action_type, "action_type");
|
|
151
|
-
if (input.description !== undefined)
|
|
152
|
-
validate_text(input.description, "description");
|
|
153
|
-
const ref = get_ref(key);
|
|
154
|
-
await db.runTransaction(async (transaction) => {
|
|
155
|
-
const snapshot = await transaction.get(ref);
|
|
156
|
-
if (!snapshot.exists)
|
|
157
|
-
throw new Error(`postponed action '${key}' does not exist`);
|
|
158
|
-
assert_status(key, snapshot.get("status"), [PostponedActionStatus.pending]);
|
|
159
|
-
transaction.update(ref, {
|
|
160
|
-
...(input.details === undefined ? {} : { details: input.details }),
|
|
161
|
-
...(input.action_type === undefined ? {} : { action_type: input.action_type }),
|
|
162
|
-
...(input.description === undefined ? {} : { description: input.description }),
|
|
163
|
-
...(input.execute_at === undefined ? {} : { execute_at: resolve_postponed_action_time(input.execute_at) }),
|
|
164
|
-
updated_at: new Date(),
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
};
|
|
168
|
-
const repostpone_postponed_action_core = async (key, execute_at) => {
|
|
169
|
-
const ref = get_ref(key);
|
|
170
|
-
await db.runTransaction(async (transaction) => {
|
|
171
|
-
const snapshot = await transaction.get(ref);
|
|
172
|
-
if (!snapshot.exists)
|
|
173
|
-
throw new Error(`postponed action '${key}' does not exist`);
|
|
174
|
-
assert_status(key, snapshot.get("status"), [
|
|
175
|
-
PostponedActionStatus.pending,
|
|
176
|
-
PostponedActionStatus.completed,
|
|
177
|
-
PostponedActionStatus.cancelled,
|
|
178
|
-
PostponedActionStatus.failed,
|
|
179
|
-
]);
|
|
180
|
-
transaction.update(ref, {
|
|
181
|
-
execute_at: resolve_postponed_action_time(execute_at),
|
|
182
|
-
status: PostponedActionStatus.pending,
|
|
183
|
-
updated_at: new Date(),
|
|
184
|
-
started_at: FieldValue.delete(),
|
|
185
|
-
completed_at: FieldValue.delete(),
|
|
186
|
-
cancelled_at: FieldValue.delete(),
|
|
187
|
-
failed_at: FieldValue.delete(),
|
|
188
|
-
error: FieldValue.delete(),
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
};
|
|
192
|
-
const cancel_postponed_action_core = async (key) => {
|
|
193
|
-
const ref = get_ref(key);
|
|
194
|
-
await db.runTransaction(async (transaction) => {
|
|
195
|
-
const snapshot = await transaction.get(ref);
|
|
196
|
-
if (!snapshot.exists)
|
|
197
|
-
throw new Error(`postponed action '${key}' does not exist`);
|
|
198
|
-
assert_status(key, snapshot.get("status"), [PostponedActionStatus.pending]);
|
|
199
|
-
const now = new Date();
|
|
200
|
-
transaction.update(ref, { status: PostponedActionStatus.cancelled, cancelled_at: now, updated_at: now });
|
|
201
|
-
});
|
|
202
|
-
};
|
|
203
|
-
const complete_postponed_action_core = async (key) => {
|
|
204
|
-
const ref = get_ref(key);
|
|
205
|
-
await db.runTransaction(async (transaction) => {
|
|
206
|
-
const snapshot = await transaction.get(ref);
|
|
207
|
-
if (!snapshot.exists)
|
|
208
|
-
throw new Error(`postponed action '${key}' does not exist`);
|
|
209
|
-
assert_status(key, snapshot.get("status"), [PostponedActionStatus.processing]);
|
|
210
|
-
const now = new Date();
|
|
211
|
-
transaction.update(ref, { status: PostponedActionStatus.completed, completed_at: now, updated_at: now });
|
|
212
|
-
});
|
|
213
|
-
};
|
|
214
|
-
const fail_postponed_action_core = async (key, error) => {
|
|
215
|
-
const ref = get_ref(key);
|
|
216
|
-
await db.runTransaction(async (transaction) => {
|
|
217
|
-
const snapshot = await transaction.get(ref);
|
|
218
|
-
if (!snapshot.exists)
|
|
219
|
-
throw new Error(`postponed action '${key}' does not exist`);
|
|
220
|
-
assert_status(key, snapshot.get("status"), [PostponedActionStatus.processing]);
|
|
221
|
-
const now = new Date();
|
|
222
|
-
transaction.update(ref, {
|
|
223
|
-
status: PostponedActionStatus.failed,
|
|
224
|
-
failed_at: now,
|
|
225
|
-
updated_at: now,
|
|
226
|
-
error: error instanceof Error ? error.message : String(error),
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
logger.error(`[PostponedActions] action '${key}' failed`, error);
|
|
230
|
-
};
|
|
231
|
-
const delete_postponed_action_core = async (key) => {
|
|
232
|
-
await get_ref(key).delete();
|
|
233
|
-
};
|
|
234
|
-
const claim_postponed_action_core = async (key, action_type, now = new Date()) => {
|
|
235
|
-
validate_text(action_type, "action_type");
|
|
236
|
-
const ref = get_ref(key);
|
|
237
|
-
return db.runTransaction(async (transaction) => {
|
|
238
|
-
const snapshot = await transaction.get(ref);
|
|
239
|
-
if (!snapshot.exists || snapshot.get("status") !== PostponedActionStatus.pending || snapshot.get("action_type") !== action_type) {
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
|
-
const action = to_postponed_action(snapshot);
|
|
243
|
-
if (action.execute_at.getTime() > now.getTime())
|
|
244
|
-
return null;
|
|
245
|
-
const claimed = {
|
|
246
|
-
...action,
|
|
247
|
-
status: PostponedActionStatus.processing,
|
|
248
|
-
attempt_count: action.attempt_count + 1,
|
|
249
|
-
started_at: now,
|
|
250
|
-
updated_at: now,
|
|
251
|
-
};
|
|
252
|
-
transaction.update(ref, {
|
|
253
|
-
status: claimed.status,
|
|
254
|
-
attempt_count: claimed.attempt_count,
|
|
255
|
-
started_at: now,
|
|
256
|
-
updated_at: now,
|
|
257
|
-
});
|
|
258
|
-
return claimed;
|
|
259
|
-
});
|
|
260
|
-
};
|
|
261
|
-
const claim_due_postponed_actions_core = async (action_type, limit = 100, now = new Date()) => {
|
|
262
|
-
validate_text(action_type, "action_type");
|
|
263
|
-
if (!Number.isInteger(limit) || limit <= 0) {
|
|
264
|
-
throw new Error("limit must be a positive integer");
|
|
265
|
-
}
|
|
266
|
-
const snapshot = await db
|
|
267
|
-
.collection(POSTPONED_ACTIONS_COLLECTION)
|
|
268
|
-
.where("action_type", "==", action_type)
|
|
269
|
-
.where("status", "==", PostponedActionStatus.pending)
|
|
270
|
-
.where("execute_at", "<=", now)
|
|
271
|
-
.orderBy("execute_at")
|
|
272
|
-
.limit(limit)
|
|
273
|
-
.get();
|
|
274
|
-
const claimed = await Promise.all(snapshot.docs.map((document) => claim_postponed_action_core(document.id, action_type, now)));
|
|
275
|
-
return claimed.filter((action) => action !== null);
|
|
276
|
-
};
|
|
277
|
-
const with_error_logging = (operation, action, get_key) => {
|
|
278
|
-
return async (...args) => {
|
|
279
|
-
try {
|
|
280
|
-
return await action(...args);
|
|
281
|
-
}
|
|
282
|
-
catch (error) {
|
|
283
|
-
const key = get_key?.(...args);
|
|
284
|
-
logger.error(`[PostponedActions] ${operation} failed${key ? ` for '${key}'` : ""}`, error);
|
|
285
|
-
throw error;
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
};
|
|
289
|
-
export const get_postponed_action = with_error_logging("get", get_postponed_action_core, (key) => key);
|
|
290
|
-
export const add_postponed_action = with_error_logging("add", add_postponed_action_core, (input) => input.key);
|
|
291
|
-
export const update_postponed_action = with_error_logging("update", update_postponed_action_core, (key) => key);
|
|
292
|
-
export const repostpone_postponed_action = with_error_logging("repostpone", repostpone_postponed_action_core, (key) => key);
|
|
293
|
-
export const cancel_postponed_action = with_error_logging("cancel", cancel_postponed_action_core, (key) => key);
|
|
294
|
-
export const complete_postponed_action = with_error_logging("complete", complete_postponed_action_core, (key) => key);
|
|
295
|
-
export const fail_postponed_action = with_error_logging("mark as failed", fail_postponed_action_core, (key) => key);
|
|
296
|
-
export const delete_postponed_action = with_error_logging("delete", delete_postponed_action_core, (key) => key);
|
|
297
|
-
export const claim_postponed_action = with_error_logging("claim", claim_postponed_action_core, (key) => key);
|
|
298
|
-
export const claim_due_postponed_actions = with_error_logging("claim due", claim_due_postponed_actions_core);
|
|
299
|
-
/**
|
|
300
|
-
* @example Add an action for a specific date and time.
|
|
301
|
-
* ```ts
|
|
302
|
-
* await postponed_actions.add({
|
|
303
|
-
* key: "subscription:123:expire",
|
|
304
|
-
* action_type: "subscription_expiration",
|
|
305
|
-
* description: "Expire subscription 123",
|
|
306
|
-
* details: { subscription_id: "123", notify_customer: true },
|
|
307
|
-
* execute_at: new Date("2026-08-01T12:00:00Z"),
|
|
308
|
-
* });
|
|
309
|
-
* ```
|
|
310
|
-
*
|
|
311
|
-
* @example Add an action relative to the current time. Adding the same key
|
|
312
|
-
* updates the pending action.
|
|
313
|
-
* ```ts
|
|
314
|
-
* await postponed_actions.add({
|
|
315
|
-
* key: "invoice:456:reminder",
|
|
316
|
-
* action_type: "invoice_reminder",
|
|
317
|
-
* description: "Send invoice 456 reminder",
|
|
318
|
-
* details: { invoice_id: "456" },
|
|
319
|
-
* execute_at: { hours: 2, minutes: 30 },
|
|
320
|
-
* });
|
|
321
|
-
* ```
|
|
322
|
-
*
|
|
323
|
-
* @example Update, repostpone, or cancel a pending action.
|
|
324
|
-
* ```ts
|
|
325
|
-
* await postponed_actions.update("invoice:456:reminder", {
|
|
326
|
-
* description: "Send the final invoice reminder",
|
|
327
|
-
* execute_at: { minutes: 15 },
|
|
328
|
-
* });
|
|
329
|
-
* await postponed_actions.repostpone("invoice:456:reminder", { hours: 1 });
|
|
330
|
-
* await postponed_actions.cancel("invoice:456:reminder");
|
|
331
|
-
* ```
|
|
332
|
-
*
|
|
333
|
-
* @example Claim and execute due actions. Claiming is atomic, so concurrent
|
|
334
|
-
* workers cannot claim the same action.
|
|
335
|
-
* ```ts
|
|
336
|
-
* const actions = await postponed_actions.claim_due("invoice_reminder", 25);
|
|
337
|
-
* for (const action of actions) {
|
|
338
|
-
* try {
|
|
339
|
-
* await execute_action(action.details);
|
|
340
|
-
* await postponed_actions.complete(action.key);
|
|
341
|
-
* } catch (error) {
|
|
342
|
-
* await postponed_actions.fail(action.key, error);
|
|
343
|
-
* }
|
|
344
|
-
* }
|
|
345
|
-
* ```
|
|
346
|
-
*/
|
|
347
|
-
export const postponed_actions = {
|
|
348
|
-
get: get_postponed_action,
|
|
349
|
-
add: add_postponed_action,
|
|
350
|
-
update: update_postponed_action,
|
|
351
|
-
repostpone: repostpone_postponed_action,
|
|
352
|
-
cancel: cancel_postponed_action,
|
|
353
|
-
complete: complete_postponed_action,
|
|
354
|
-
fail: fail_postponed_action,
|
|
355
|
-
delete: delete_postponed_action,
|
|
356
|
-
claim: claim_postponed_action,
|
|
357
|
-
claim_due: claim_due_postponed_actions,
|
|
358
|
-
};
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
import { logger } from "../../managers";
|
|
2
|
-
import { get_redis_commander, redis_commander_connected } from "./initialize";
|
|
3
|
-
import { get_doc_key, get_collection_keys, scan_redis_keys } from "./keys";
|
|
4
|
-
import { get_nx_settings, get_all_documents, query_documents, query_documents_by_conditions, query_document_by_conditions, query_document, query_document_optional, get_document_by_id, get_document_by_id_optional, } from "../firebase_helpers";
|
|
5
|
-
const should_use_redis = async (collection_path) => {
|
|
6
|
-
if (!redis_commander_connected) {
|
|
7
|
-
logger.warn(`⚠️ Redis commander not connected, falling back to Firebase for collection: "${collection_path}"`);
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
try {
|
|
11
|
-
const nx_settings = await get_nx_settings();
|
|
12
|
-
const cache_collections_config = nx_settings.cache_collections_config || {};
|
|
13
|
-
const config = cache_collections_config[collection_path];
|
|
14
|
-
if (!config) {
|
|
15
|
-
logger.warn(`⚠️ Collection "${collection_path}" not found in cache_collections_config, falling back to Firebase`);
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
logger.warn(`⚠️ Failed to validate Redis config for collection: "${collection_path}", falling back to Firebase`);
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
return true;
|
|
24
|
-
};
|
|
25
|
-
const parse_redis_value = (raw) => {
|
|
26
|
-
const parsed = JSON.parse(raw);
|
|
27
|
-
return parsed.data ?? parsed;
|
|
28
|
-
};
|
|
29
|
-
const get_all_collection_docs = async (collection_path) => {
|
|
30
|
-
const commander = get_redis_commander();
|
|
31
|
-
const keys = await scan_redis_keys(get_collection_keys(collection_path), commander);
|
|
32
|
-
if (keys.length === 0)
|
|
33
|
-
return [];
|
|
34
|
-
const values = await commander.mget(keys);
|
|
35
|
-
return values.filter(Boolean).map((v) => {
|
|
36
|
-
const data = parse_redis_value(v);
|
|
37
|
-
const key = keys[values.indexOf(v)];
|
|
38
|
-
/// example key: "nx-users(collection):123(doc_id)"
|
|
39
|
-
const id = key.split(":").slice(1).join(":");
|
|
40
|
-
return { ...data, id };
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
const apply_operator = (doc_value, operator, value) => {
|
|
44
|
-
switch (operator) {
|
|
45
|
-
case "==":
|
|
46
|
-
return doc_value === value;
|
|
47
|
-
case "!=":
|
|
48
|
-
return doc_value !== value;
|
|
49
|
-
case "<":
|
|
50
|
-
return doc_value < value;
|
|
51
|
-
case "<=":
|
|
52
|
-
return doc_value <= value;
|
|
53
|
-
case ">":
|
|
54
|
-
return doc_value > value;
|
|
55
|
-
case ">=":
|
|
56
|
-
return doc_value >= value;
|
|
57
|
-
case "in":
|
|
58
|
-
return Array.isArray(value) && value.includes(doc_value);
|
|
59
|
-
case "not-in":
|
|
60
|
-
return Array.isArray(value) && !value.includes(doc_value);
|
|
61
|
-
case "array-contains":
|
|
62
|
-
return Array.isArray(doc_value) && doc_value.includes(value);
|
|
63
|
-
case "array-contains-any":
|
|
64
|
-
return Array.isArray(doc_value) && Array.isArray(value) && value.some((v) => doc_value.includes(v));
|
|
65
|
-
default:
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
const filter_by_condition = (docs, field_name, operator, value) => {
|
|
70
|
-
return docs.filter((doc) => apply_operator(doc[field_name], operator, value));
|
|
71
|
-
};
|
|
72
|
-
const filter_by_conditions = (docs, conditions) => {
|
|
73
|
-
return conditions.reduce((filtered, { field_name, operator, value }) => {
|
|
74
|
-
return filter_by_condition(filtered, field_name, operator, value);
|
|
75
|
-
}, docs);
|
|
76
|
-
};
|
|
77
|
-
// ── extract ──
|
|
78
|
-
export const redis_simple_extract_data = (raw) => {
|
|
79
|
-
const data = parse_redis_value(raw);
|
|
80
|
-
return data;
|
|
81
|
-
};
|
|
82
|
-
// ── documents ──
|
|
83
|
-
export const redis_get_all_documents = async (collection_path) => {
|
|
84
|
-
if (!(await should_use_redis(collection_path))) {
|
|
85
|
-
return get_all_documents(collection_path);
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
return await get_all_collection_docs(collection_path);
|
|
89
|
-
}
|
|
90
|
-
catch (error) {
|
|
91
|
-
logger.error("Error fetching documents from Redis:", error);
|
|
92
|
-
throw error;
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
export const redis_query_documents = async (collection_path, field_name, operator, value) => {
|
|
96
|
-
if (!(await should_use_redis(collection_path))) {
|
|
97
|
-
return query_documents(collection_path, field_name, operator, value);
|
|
98
|
-
}
|
|
99
|
-
try {
|
|
100
|
-
const all_docs = await get_all_collection_docs(collection_path);
|
|
101
|
-
return filter_by_condition(all_docs, field_name, operator, value);
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
logger.error(`Error querying documents from Redis: ${collection_path} - ${field_name} - ${operator} - ${value} `, error);
|
|
105
|
-
throw error;
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
export const redis_query_documents_by_conditions = async (collection_path, where_conditions) => {
|
|
109
|
-
if (!(await should_use_redis(collection_path))) {
|
|
110
|
-
return query_documents_by_conditions(collection_path, where_conditions);
|
|
111
|
-
}
|
|
112
|
-
try {
|
|
113
|
-
const all_docs = await get_all_collection_docs(collection_path);
|
|
114
|
-
return filter_by_conditions(all_docs, where_conditions);
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
logger.error(`Error querying documents from Redis: ${collection_path} - ${JSON.stringify(where_conditions)} `, error);
|
|
118
|
-
throw error;
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
export const redis_query_document_by_conditions = async (collection_path, where_conditions, ignore_logs = false) => {
|
|
122
|
-
if (!(await should_use_redis(collection_path))) {
|
|
123
|
-
return query_document_by_conditions(collection_path, where_conditions, !ignore_logs);
|
|
124
|
-
}
|
|
125
|
-
try {
|
|
126
|
-
const all_docs = await get_all_collection_docs(collection_path);
|
|
127
|
-
const documents = filter_by_conditions(all_docs, where_conditions);
|
|
128
|
-
if (!documents[0]) {
|
|
129
|
-
throw "no data returned from Redis";
|
|
130
|
-
}
|
|
131
|
-
return documents[0];
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
if (!ignore_logs) {
|
|
135
|
-
logger.error(`Error querying document from Redis: ${collection_path} - ${JSON.stringify(where_conditions)} `, error);
|
|
136
|
-
}
|
|
137
|
-
throw error;
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
// ── document ──
|
|
141
|
-
export const redis_query_document = async (collection_path, field_name, operator, value, ignore_log = false) => {
|
|
142
|
-
if (!(await should_use_redis(collection_path))) {
|
|
143
|
-
return query_document(collection_path, field_name, operator, value, ignore_log);
|
|
144
|
-
}
|
|
145
|
-
try {
|
|
146
|
-
const all_docs = await get_all_collection_docs(collection_path);
|
|
147
|
-
const documents = filter_by_condition(all_docs, field_name, operator, value);
|
|
148
|
-
const base_error = `collection: ${collection_path}, field_name: ${field_name}, operator: ${operator}, value:${value}`;
|
|
149
|
-
if (documents.length < 1) {
|
|
150
|
-
throw `[Redis] No data to return from: ${base_error}`;
|
|
151
|
-
}
|
|
152
|
-
if (documents.length > 1) {
|
|
153
|
-
throw `[Redis] Multiple documents found in: ${base_error}`;
|
|
154
|
-
}
|
|
155
|
-
return documents[0];
|
|
156
|
-
}
|
|
157
|
-
catch (error) {
|
|
158
|
-
if (!ignore_log) {
|
|
159
|
-
logger.error("Error querying document from Redis: " + JSON.stringify({ collection_path, field_name, operator, value }), error);
|
|
160
|
-
}
|
|
161
|
-
throw error;
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
export const redis_query_document_optional = async (collection_path, field_name, operator, value, ignore_log = true) => {
|
|
165
|
-
if (!(await should_use_redis(collection_path))) {
|
|
166
|
-
return query_document_optional(collection_path, field_name, operator, value, ignore_log);
|
|
167
|
-
}
|
|
168
|
-
try {
|
|
169
|
-
const all_docs = await get_all_collection_docs(collection_path);
|
|
170
|
-
const documents = filter_by_condition(all_docs, field_name, operator, value);
|
|
171
|
-
return documents[0] || null;
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
if (!ignore_log) {
|
|
175
|
-
logger.error("Error querying optional document from Redis: " + JSON.stringify({ collection_path, field_name, operator, value }), error);
|
|
176
|
-
}
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
export const redis_get_document_by_id = async (collection_path, doc_id) => {
|
|
181
|
-
if (!(await should_use_redis(collection_path))) {
|
|
182
|
-
return get_document_by_id(collection_path, doc_id);
|
|
183
|
-
}
|
|
184
|
-
try {
|
|
185
|
-
const commander = get_redis_commander();
|
|
186
|
-
const key = get_doc_key(collection_path, doc_id);
|
|
187
|
-
const raw = await commander.get(key);
|
|
188
|
-
if (!raw) {
|
|
189
|
-
throw "Document not found in Redis, document id: " + doc_id;
|
|
190
|
-
}
|
|
191
|
-
return redis_simple_extract_data(raw);
|
|
192
|
-
}
|
|
193
|
-
catch (error) {
|
|
194
|
-
logger.error("error from redis_get_document_by_id", error);
|
|
195
|
-
throw error;
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
export const redis_get_document_by_id_optional = async (collection_path, doc_id) => {
|
|
199
|
-
if (!(await should_use_redis(collection_path))) {
|
|
200
|
-
return get_document_by_id_optional(collection_path, doc_id);
|
|
201
|
-
}
|
|
202
|
-
try {
|
|
203
|
-
const commander = get_redis_commander();
|
|
204
|
-
const key = get_doc_key(collection_path, doc_id);
|
|
205
|
-
const raw = await commander.get(key);
|
|
206
|
-
if (!raw) {
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
return redis_simple_extract_data(raw);
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
logger.error("error from redis_get_document_by_id_optional", error);
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
};
|