servcraft 0.1.0 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/.claude/settings.local.json +30 -0
  2. package/.github/CODEOWNERS +18 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +46 -0
  4. package/.github/dependabot.yml +59 -0
  5. package/.github/workflows/ci.yml +188 -0
  6. package/.github/workflows/release.yml +195 -0
  7. package/AUDIT.md +602 -0
  8. package/LICENSE +21 -0
  9. package/README.md +1102 -1
  10. package/dist/cli/index.cjs +2026 -2168
  11. package/dist/cli/index.cjs.map +1 -1
  12. package/dist/cli/index.js +2026 -2168
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/index.cjs +595 -616
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +114 -52
  17. package/dist/index.d.ts +114 -52
  18. package/dist/index.js +595 -616
  19. package/dist/index.js.map +1 -1
  20. package/docs/CLI-001_MULTI_DB_PLAN.md +546 -0
  21. package/docs/DATABASE_MULTI_ORM.md +399 -0
  22. package/docs/PHASE1_BREAKDOWN.md +346 -0
  23. package/docs/PROGRESS.md +550 -0
  24. package/docs/modules/ANALYTICS.md +226 -0
  25. package/docs/modules/API-VERSIONING.md +252 -0
  26. package/docs/modules/AUDIT.md +192 -0
  27. package/docs/modules/AUTH.md +431 -0
  28. package/docs/modules/CACHE.md +346 -0
  29. package/docs/modules/EMAIL.md +254 -0
  30. package/docs/modules/FEATURE-FLAG.md +291 -0
  31. package/docs/modules/I18N.md +294 -0
  32. package/docs/modules/MEDIA-PROCESSING.md +281 -0
  33. package/docs/modules/MFA.md +266 -0
  34. package/docs/modules/NOTIFICATION.md +311 -0
  35. package/docs/modules/OAUTH.md +237 -0
  36. package/docs/modules/PAYMENT.md +804 -0
  37. package/docs/modules/QUEUE.md +540 -0
  38. package/docs/modules/RATE-LIMIT.md +339 -0
  39. package/docs/modules/SEARCH.md +288 -0
  40. package/docs/modules/SECURITY.md +327 -0
  41. package/docs/modules/SESSION.md +382 -0
  42. package/docs/modules/SWAGGER.md +305 -0
  43. package/docs/modules/UPLOAD.md +296 -0
  44. package/docs/modules/USER.md +505 -0
  45. package/docs/modules/VALIDATION.md +294 -0
  46. package/docs/modules/WEBHOOK.md +270 -0
  47. package/docs/modules/WEBSOCKET.md +691 -0
  48. package/package.json +53 -38
  49. package/prisma/schema.prisma +395 -1
  50. package/src/cli/commands/add-module.ts +520 -87
  51. package/src/cli/commands/db.ts +3 -4
  52. package/src/cli/commands/docs.ts +256 -6
  53. package/src/cli/commands/generate.ts +12 -19
  54. package/src/cli/commands/init.ts +384 -214
  55. package/src/cli/index.ts +0 -4
  56. package/src/cli/templates/repository.ts +6 -1
  57. package/src/cli/templates/routes.ts +6 -21
  58. package/src/cli/utils/docs-generator.ts +6 -7
  59. package/src/cli/utils/env-manager.ts +717 -0
  60. package/src/cli/utils/field-parser.ts +16 -7
  61. package/src/cli/utils/interactive-prompt.ts +223 -0
  62. package/src/cli/utils/template-manager.ts +346 -0
  63. package/src/config/database.config.ts +183 -0
  64. package/src/config/env.ts +0 -10
  65. package/src/config/index.ts +0 -14
  66. package/src/core/server.ts +1 -1
  67. package/src/database/adapters/mongoose.adapter.ts +132 -0
  68. package/src/database/adapters/prisma.adapter.ts +118 -0
  69. package/src/database/connection.ts +190 -0
  70. package/src/database/interfaces/database.interface.ts +85 -0
  71. package/src/database/interfaces/index.ts +7 -0
  72. package/src/database/interfaces/repository.interface.ts +129 -0
  73. package/src/database/models/mongoose/index.ts +7 -0
  74. package/src/database/models/mongoose/payment.schema.ts +347 -0
  75. package/src/database/models/mongoose/user.schema.ts +154 -0
  76. package/src/database/prisma.ts +1 -4
  77. package/src/database/redis.ts +101 -0
  78. package/src/database/repositories/mongoose/index.ts +7 -0
  79. package/src/database/repositories/mongoose/payment.repository.ts +380 -0
  80. package/src/database/repositories/mongoose/user.repository.ts +255 -0
  81. package/src/database/seed.ts +6 -1
  82. package/src/index.ts +9 -20
  83. package/src/middleware/security.ts +2 -6
  84. package/src/modules/analytics/analytics.routes.ts +80 -0
  85. package/src/modules/analytics/analytics.service.ts +364 -0
  86. package/src/modules/analytics/index.ts +18 -0
  87. package/src/modules/analytics/types.ts +180 -0
  88. package/src/modules/api-versioning/index.ts +15 -0
  89. package/src/modules/api-versioning/types.ts +86 -0
  90. package/src/modules/api-versioning/versioning.middleware.ts +120 -0
  91. package/src/modules/api-versioning/versioning.routes.ts +54 -0
  92. package/src/modules/api-versioning/versioning.service.ts +189 -0
  93. package/src/modules/audit/audit.repository.ts +206 -0
  94. package/src/modules/audit/audit.service.ts +27 -59
  95. package/src/modules/auth/auth.controller.ts +2 -2
  96. package/src/modules/auth/auth.middleware.ts +3 -9
  97. package/src/modules/auth/auth.routes.ts +10 -107
  98. package/src/modules/auth/auth.service.ts +126 -23
  99. package/src/modules/auth/index.ts +3 -4
  100. package/src/modules/cache/cache.service.ts +367 -0
  101. package/src/modules/cache/index.ts +10 -0
  102. package/src/modules/cache/types.ts +44 -0
  103. package/src/modules/email/email.service.ts +3 -10
  104. package/src/modules/email/templates.ts +2 -8
  105. package/src/modules/feature-flag/feature-flag.repository.ts +303 -0
  106. package/src/modules/feature-flag/feature-flag.routes.ts +247 -0
  107. package/src/modules/feature-flag/feature-flag.service.ts +566 -0
  108. package/src/modules/feature-flag/index.ts +20 -0
  109. package/src/modules/feature-flag/types.ts +192 -0
  110. package/src/modules/i18n/i18n.middleware.ts +186 -0
  111. package/src/modules/i18n/i18n.routes.ts +191 -0
  112. package/src/modules/i18n/i18n.service.ts +456 -0
  113. package/src/modules/i18n/index.ts +18 -0
  114. package/src/modules/i18n/types.ts +118 -0
  115. package/src/modules/media-processing/index.ts +17 -0
  116. package/src/modules/media-processing/media-processing.routes.ts +111 -0
  117. package/src/modules/media-processing/media-processing.service.ts +245 -0
  118. package/src/modules/media-processing/types.ts +156 -0
  119. package/src/modules/mfa/index.ts +20 -0
  120. package/src/modules/mfa/mfa.repository.ts +206 -0
  121. package/src/modules/mfa/mfa.routes.ts +595 -0
  122. package/src/modules/mfa/mfa.service.ts +572 -0
  123. package/src/modules/mfa/totp.ts +150 -0
  124. package/src/modules/mfa/types.ts +57 -0
  125. package/src/modules/notification/index.ts +20 -0
  126. package/src/modules/notification/notification.repository.ts +356 -0
  127. package/src/modules/notification/notification.service.ts +483 -0
  128. package/src/modules/notification/types.ts +119 -0
  129. package/src/modules/oauth/index.ts +20 -0
  130. package/src/modules/oauth/oauth.repository.ts +219 -0
  131. package/src/modules/oauth/oauth.routes.ts +446 -0
  132. package/src/modules/oauth/oauth.service.ts +293 -0
  133. package/src/modules/oauth/providers/apple.provider.ts +250 -0
  134. package/src/modules/oauth/providers/facebook.provider.ts +181 -0
  135. package/src/modules/oauth/providers/github.provider.ts +248 -0
  136. package/src/modules/oauth/providers/google.provider.ts +189 -0
  137. package/src/modules/oauth/providers/twitter.provider.ts +214 -0
  138. package/src/modules/oauth/types.ts +94 -0
  139. package/src/modules/payment/index.ts +19 -0
  140. package/src/modules/payment/payment.repository.ts +733 -0
  141. package/src/modules/payment/payment.routes.ts +390 -0
  142. package/src/modules/payment/payment.service.ts +354 -0
  143. package/src/modules/payment/providers/mobile-money.provider.ts +274 -0
  144. package/src/modules/payment/providers/paypal.provider.ts +190 -0
  145. package/src/modules/payment/providers/stripe.provider.ts +215 -0
  146. package/src/modules/payment/types.ts +140 -0
  147. package/src/modules/queue/cron.ts +438 -0
  148. package/src/modules/queue/index.ts +87 -0
  149. package/src/modules/queue/queue.routes.ts +600 -0
  150. package/src/modules/queue/queue.service.ts +842 -0
  151. package/src/modules/queue/types.ts +222 -0
  152. package/src/modules/queue/workers.ts +366 -0
  153. package/src/modules/rate-limit/index.ts +59 -0
  154. package/src/modules/rate-limit/rate-limit.middleware.ts +134 -0
  155. package/src/modules/rate-limit/rate-limit.routes.ts +269 -0
  156. package/src/modules/rate-limit/rate-limit.service.ts +348 -0
  157. package/src/modules/rate-limit/stores/memory.store.ts +165 -0
  158. package/src/modules/rate-limit/stores/redis.store.ts +322 -0
  159. package/src/modules/rate-limit/types.ts +153 -0
  160. package/src/modules/search/adapters/elasticsearch.adapter.ts +326 -0
  161. package/src/modules/search/adapters/meilisearch.adapter.ts +261 -0
  162. package/src/modules/search/adapters/memory.adapter.ts +278 -0
  163. package/src/modules/search/index.ts +21 -0
  164. package/src/modules/search/search.service.ts +234 -0
  165. package/src/modules/search/types.ts +214 -0
  166. package/src/modules/security/index.ts +40 -0
  167. package/src/modules/security/sanitize.ts +223 -0
  168. package/src/modules/security/security-audit.service.ts +388 -0
  169. package/src/modules/security/security.middleware.ts +398 -0
  170. package/src/modules/session/index.ts +3 -0
  171. package/src/modules/session/session.repository.ts +159 -0
  172. package/src/modules/session/session.service.ts +340 -0
  173. package/src/modules/session/types.ts +38 -0
  174. package/src/modules/swagger/index.ts +7 -1
  175. package/src/modules/swagger/schema-builder.ts +16 -4
  176. package/src/modules/swagger/swagger.service.ts +9 -10
  177. package/src/modules/swagger/types.ts +0 -2
  178. package/src/modules/upload/index.ts +14 -0
  179. package/src/modules/upload/types.ts +83 -0
  180. package/src/modules/upload/upload.repository.ts +199 -0
  181. package/src/modules/upload/upload.routes.ts +311 -0
  182. package/src/modules/upload/upload.service.ts +448 -0
  183. package/src/modules/user/index.ts +3 -3
  184. package/src/modules/user/user.controller.ts +15 -9
  185. package/src/modules/user/user.repository.ts +237 -113
  186. package/src/modules/user/user.routes.ts +39 -164
  187. package/src/modules/user/user.service.ts +4 -3
  188. package/src/modules/validation/validator.ts +12 -17
  189. package/src/modules/webhook/index.ts +91 -0
  190. package/src/modules/webhook/retry.ts +196 -0
  191. package/src/modules/webhook/signature.ts +135 -0
  192. package/src/modules/webhook/types.ts +181 -0
  193. package/src/modules/webhook/webhook.repository.ts +358 -0
  194. package/src/modules/webhook/webhook.routes.ts +442 -0
  195. package/src/modules/webhook/webhook.service.ts +457 -0
  196. package/src/modules/websocket/features.ts +504 -0
  197. package/src/modules/websocket/index.ts +106 -0
  198. package/src/modules/websocket/middlewares.ts +298 -0
  199. package/src/modules/websocket/types.ts +181 -0
  200. package/src/modules/websocket/websocket.service.ts +692 -0
  201. package/src/utils/errors.ts +7 -0
  202. package/src/utils/pagination.ts +4 -1
  203. package/tests/helpers/db-check.ts +79 -0
  204. package/tests/integration/auth-redis.test.ts +94 -0
  205. package/tests/integration/cache-redis.test.ts +387 -0
  206. package/tests/integration/mongoose-repositories.test.ts +410 -0
  207. package/tests/integration/payment-prisma.test.ts +637 -0
  208. package/tests/integration/queue-bullmq.test.ts +417 -0
  209. package/tests/integration/user-prisma.test.ts +441 -0
  210. package/tests/integration/websocket-socketio.test.ts +552 -0
  211. package/tests/setup.ts +11 -9
  212. package/vitest.config.ts +3 -8
  213. package/npm-cache/_cacache/content-v2/sha512/1c/d0/03440d500a0487621aad1d6402978340698976602046db8e24fa03c01ee6c022c69b0582f969042d9442ee876ac35c038e960dd427d1e622fa24b8eb7dba +0 -0
  214. package/npm-cache/_cacache/content-v2/sha512/42/55/28b493ca491833e5aab0e9c3108d29ab3f36c248ca88f45d4630674fce9130959e56ae308797ac2b6328fa7f09a610b9550ed09cb971d039876d293fc69d +0 -0
  215. package/npm-cache/_cacache/content-v2/sha512/e0/12/f360dc9315ee5f17844a0c8c233ee6bf7c30837c4a02ea0d56c61c7f7ab21c0e958e50ed2c57c59f983c762b93056778c9009b2398ffc26def0183999b13 +0 -0
  216. package/npm-cache/_cacache/content-v2/sha512/ed/b0/fae1161902898f4c913c67d7f6cdf6be0665aec3b389b9c4f4f0a101ca1da59badf1b59c4e0030f5223023b8d63cfe501c46a32c20c895d4fb3f11ca2232 +0 -0
  217. package/npm-cache/_cacache/index-v5/58/94/c2cba79e0f16b4c10e95a87e32255741149e8222cc314a476aab67c39cc0 +0 -5
@@ -0,0 +1,438 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { logger } from '../../core/logger.js';
3
+ import { NotFoundError, BadRequestError } from '../../utils/errors.js';
4
+ import type { CronJob, JobOptions } from './types.js';
5
+ import type { QueueService } from './queue.service.js';
6
+
7
+ // In-memory storage
8
+ const cronJobs = new Map<string, CronJob>();
9
+ const cronTimers = new Map<string, NodeJS.Timeout>();
10
+
11
+ /**
12
+ * Cron Job Manager
13
+ * Manages scheduled jobs with cron expressions
14
+ */
15
+ export class CronJobManager {
16
+ constructor(private queueService: QueueService) {}
17
+
18
+ /**
19
+ * Create a cron job
20
+ */
21
+ async createCronJob(
22
+ name: string,
23
+ cron: string,
24
+ queueName: string,
25
+ jobName: string,
26
+ data?: unknown,
27
+ _options?: JobOptions
28
+ ): Promise<CronJob> {
29
+ // Validate cron expression
30
+ if (!this.isValidCron(cron)) {
31
+ throw new BadRequestError('Invalid cron expression');
32
+ }
33
+
34
+ const cronJob: CronJob = {
35
+ id: randomUUID(),
36
+ name,
37
+ cron,
38
+ queueName,
39
+ jobName,
40
+ data,
41
+ enabled: true,
42
+ createdAt: new Date(),
43
+ nextRun: this.calculateNextRun(cron) ?? undefined,
44
+ };
45
+
46
+ cronJobs.set(cronJob.id, cronJob);
47
+
48
+ // Schedule the cron job
49
+ this.scheduleCronJob(cronJob.id);
50
+
51
+ logger.info(
52
+ { cronJobId: cronJob.id, name, cron, nextRun: cronJob.nextRun },
53
+ 'Cron job created'
54
+ );
55
+
56
+ return cronJob;
57
+ }
58
+
59
+ /**
60
+ * Get a cron job
61
+ */
62
+ async getCronJob(id: string): Promise<CronJob> {
63
+ const cronJob = cronJobs.get(id);
64
+ if (!cronJob) {
65
+ throw new NotFoundError('Cron job not found');
66
+ }
67
+ return cronJob;
68
+ }
69
+
70
+ /**
71
+ * List all cron jobs
72
+ */
73
+ async listCronJobs(): Promise<CronJob[]> {
74
+ return Array.from(cronJobs.values());
75
+ }
76
+
77
+ /**
78
+ * Update a cron job
79
+ */
80
+ async updateCronJob(
81
+ id: string,
82
+ updates: {
83
+ name?: string;
84
+ cron?: string;
85
+ data?: unknown;
86
+ enabled?: boolean;
87
+ }
88
+ ): Promise<CronJob> {
89
+ const cronJob = await this.getCronJob(id);
90
+
91
+ if (updates.name) {
92
+ cronJob.name = updates.name;
93
+ }
94
+
95
+ if (updates.cron) {
96
+ if (!this.isValidCron(updates.cron)) {
97
+ throw new BadRequestError('Invalid cron expression');
98
+ }
99
+ cronJob.cron = updates.cron;
100
+ cronJob.nextRun = this.calculateNextRun(updates.cron) ?? undefined;
101
+ }
102
+
103
+ if (updates.data !== undefined) {
104
+ cronJob.data = updates.data;
105
+ }
106
+
107
+ if (updates.enabled !== undefined) {
108
+ cronJob.enabled = updates.enabled;
109
+
110
+ if (updates.enabled) {
111
+ this.scheduleCronJob(id);
112
+ } else {
113
+ this.unscheduleCronJob(id);
114
+ }
115
+ }
116
+
117
+ cronJobs.set(id, cronJob);
118
+
119
+ logger.info({ cronJobId: id, updates }, 'Cron job updated');
120
+
121
+ return cronJob;
122
+ }
123
+
124
+ /**
125
+ * Delete a cron job
126
+ */
127
+ async deleteCronJob(id: string): Promise<void> {
128
+ const cronJob = await this.getCronJob(id);
129
+
130
+ this.unscheduleCronJob(id);
131
+ cronJobs.delete(id);
132
+
133
+ logger.info({ cronJobId: id, name: cronJob.name }, 'Cron job deleted');
134
+ }
135
+
136
+ /**
137
+ * Manually trigger a cron job
138
+ */
139
+ async triggerCronJob(id: string): Promise<void> {
140
+ const cronJob = await this.getCronJob(id);
141
+
142
+ await this.executeCronJob(cronJob);
143
+
144
+ logger.info({ cronJobId: id, name: cronJob.name }, 'Cron job triggered manually');
145
+ }
146
+
147
+ /**
148
+ * Schedule a cron job
149
+ */
150
+ private scheduleCronJob(id: string): void {
151
+ const cronJob = cronJobs.get(id);
152
+ if (!cronJob || !cronJob.enabled) {
153
+ return;
154
+ }
155
+
156
+ // Clear existing timer
157
+ this.unscheduleCronJob(id);
158
+
159
+ // Calculate next run time
160
+ const nextRun = this.calculateNextRun(cronJob.cron);
161
+ if (!nextRun) {
162
+ logger.warn({ cronJobId: id }, 'Could not calculate next run time');
163
+ return;
164
+ }
165
+
166
+ cronJob.nextRun = nextRun;
167
+
168
+ // Schedule execution
169
+ const delay = nextRun.getTime() - Date.now();
170
+
171
+ const timer = setTimeout(async () => {
172
+ await this.executeCronJob(cronJob);
173
+ this.scheduleCronJob(id); // Reschedule for next run
174
+ }, delay);
175
+
176
+ cronTimers.set(id, timer);
177
+
178
+ logger.debug({ cronJobId: id, name: cronJob.name, nextRun }, 'Cron job scheduled');
179
+ }
180
+
181
+ /**
182
+ * Unschedule a cron job
183
+ */
184
+ private unscheduleCronJob(id: string): void {
185
+ const timer = cronTimers.get(id);
186
+ if (timer) {
187
+ clearTimeout(timer);
188
+ cronTimers.delete(id);
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Execute a cron job
194
+ */
195
+ private async executeCronJob(cronJob: CronJob): Promise<void> {
196
+ try {
197
+ cronJob.lastRun = new Date();
198
+
199
+ logger.info({ cronJobId: cronJob.id, name: cronJob.name }, 'Executing cron job');
200
+
201
+ // Add job to queue
202
+ await this.queueService.addJob(cronJob.queueName, cronJob.jobName, cronJob.data || {});
203
+
204
+ logger.info({ cronJobId: cronJob.id, name: cronJob.name }, 'Cron job executed successfully');
205
+ } catch (error) {
206
+ logger.error(
207
+ { error, cronJobId: cronJob.id, name: cronJob.name },
208
+ 'Error executing cron job'
209
+ );
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Validate cron expression
215
+ */
216
+ private isValidCron(cron: string): boolean {
217
+ // Basic validation for cron expression
218
+ // Format: minute hour day month dayOfWeek
219
+ // Support for */n, ranges, lists
220
+ const parts = cron.split(' ');
221
+
222
+ if (parts.length !== 5) {
223
+ return false;
224
+ }
225
+
226
+ // Validate each part
227
+ const ranges = [
228
+ [0, 59], // minute
229
+ [0, 23], // hour
230
+ [1, 31], // day
231
+ [1, 12], // month
232
+ [0, 6], // dayOfWeek
233
+ ];
234
+
235
+ for (let i = 0; i < 5; i++) {
236
+ const part = parts[i];
237
+ const range = ranges[i];
238
+ if (!part || !range) continue;
239
+ const [min, max] = range;
240
+ if (min === undefined || max === undefined) continue;
241
+
242
+ if (part === '*') continue;
243
+
244
+ // Handle */n
245
+ if (part.startsWith('*/')) {
246
+ const step = parseInt(part.substring(2), 10);
247
+ if (isNaN(step) || step < 1) return false;
248
+ continue;
249
+ }
250
+
251
+ // Handle ranges (e.g., 1-5)
252
+ if (part.includes('-')) {
253
+ const rangeParts = part.split('-').map((n) => parseInt(n, 10));
254
+ const start = rangeParts[0];
255
+ const end = rangeParts[1];
256
+ if (
257
+ start === undefined ||
258
+ end === undefined ||
259
+ isNaN(start) ||
260
+ isNaN(end) ||
261
+ start < min ||
262
+ end > max ||
263
+ start > end
264
+ ) {
265
+ return false;
266
+ }
267
+ continue;
268
+ }
269
+
270
+ // Handle lists (e.g., 1,3,5)
271
+ if (part.includes(',')) {
272
+ const values = part.split(',').map((n) => parseInt(n, 10));
273
+ if (values.some((v) => isNaN(v) || v < min || v > max)) {
274
+ return false;
275
+ }
276
+ continue;
277
+ }
278
+
279
+ // Handle single value
280
+ const value = parseInt(part, 10);
281
+ if (isNaN(value) || value < min || value > max) {
282
+ return false;
283
+ }
284
+ }
285
+
286
+ return true;
287
+ }
288
+
289
+ /**
290
+ * Calculate next run time for cron expression
291
+ */
292
+ private calculateNextRun(cron: string): Date | null {
293
+ const now = new Date();
294
+ const parts = cron.split(' ');
295
+
296
+ if (parts.length !== 5) {
297
+ return null;
298
+ }
299
+
300
+ // Parse cron parts
301
+ const minute = this.parseCronPart(parts[0] ?? '*', 0, 59);
302
+ const hour = this.parseCronPart(parts[1] ?? '*', 0, 23);
303
+ const day = this.parseCronPart(parts[2] ?? '*', 1, 31);
304
+ const month = this.parseCronPart(parts[3] ?? '*', 1, 12);
305
+ const dayOfWeek = this.parseCronPart(parts[4] ?? '*', 0, 6);
306
+
307
+ // Find next valid time
308
+ // This is a simplified implementation
309
+ // For production, use a library like node-cron or cron-parser
310
+
311
+ const next = new Date(now);
312
+ next.setSeconds(0);
313
+ next.setMilliseconds(0);
314
+
315
+ // Add one minute to start from next minute
316
+ next.setMinutes(next.getMinutes() + 1);
317
+
318
+ // Simple implementation: check next 1000 minutes
319
+ for (let i = 0; i < 1000; i++) {
320
+ const m = next.getMinutes();
321
+ const h = next.getHours();
322
+ const d = next.getDate();
323
+ const mo = next.getMonth() + 1;
324
+ const dow = next.getDay();
325
+
326
+ if (
327
+ minute.includes(m) &&
328
+ hour.includes(h) &&
329
+ day.includes(d) &&
330
+ month.includes(mo) &&
331
+ dayOfWeek.includes(dow)
332
+ ) {
333
+ return next;
334
+ }
335
+
336
+ next.setMinutes(next.getMinutes() + 1);
337
+ }
338
+
339
+ return null;
340
+ }
341
+
342
+ /**
343
+ * Parse a cron part into array of values
344
+ */
345
+ private parseCronPart(part: string, min: number, max: number): number[] {
346
+ if (part === '*') {
347
+ const values = [];
348
+ for (let i = min; i <= max; i++) {
349
+ values.push(i);
350
+ }
351
+ return values;
352
+ }
353
+
354
+ if (part.startsWith('*/')) {
355
+ const step = parseInt(part.substring(2), 10);
356
+ const values = [];
357
+ for (let i = min; i <= max; i += step) {
358
+ values.push(i);
359
+ }
360
+ return values;
361
+ }
362
+
363
+ if (part.includes('-')) {
364
+ const rangeParts = part.split('-').map((n) => parseInt(n, 10));
365
+ const start = rangeParts[0] ?? min;
366
+ const end = rangeParts[1] ?? max;
367
+ const values: number[] = [];
368
+ for (let i = start; i <= end; i++) {
369
+ values.push(i);
370
+ }
371
+ return values;
372
+ }
373
+
374
+ if (part.includes(',')) {
375
+ return part.split(',').map((n) => parseInt(n, 10));
376
+ }
377
+
378
+ return [parseInt(part, 10)];
379
+ }
380
+
381
+ /**
382
+ * Initialize cron jobs (call on startup)
383
+ */
384
+ async initialize(): Promise<void> {
385
+ const jobs = Array.from(cronJobs.values());
386
+
387
+ for (const job of jobs) {
388
+ if (job.enabled) {
389
+ this.scheduleCronJob(job.id);
390
+ }
391
+ }
392
+
393
+ logger.info({ count: jobs.length }, 'Cron jobs initialized');
394
+ }
395
+
396
+ /**
397
+ * Shutdown cron jobs (call on app shutdown)
398
+ */
399
+ async shutdown(): Promise<void> {
400
+ for (const id of cronTimers.keys()) {
401
+ this.unscheduleCronJob(id);
402
+ }
403
+
404
+ logger.info('Cron jobs shut down');
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Pre-configured cron schedules
410
+ */
411
+ export const CronSchedules = {
412
+ /** Every minute */
413
+ EVERY_MINUTE: '* * * * *',
414
+ /** Every 5 minutes */
415
+ EVERY_5_MINUTES: '*/5 * * * *',
416
+ /** Every 15 minutes */
417
+ EVERY_15_MINUTES: '*/15 * * * *',
418
+ /** Every 30 minutes */
419
+ EVERY_30_MINUTES: '*/30 * * * *',
420
+ /** Every hour */
421
+ EVERY_HOUR: '0 * * * *',
422
+ /** Every 6 hours */
423
+ EVERY_6_HOURS: '0 */6 * * *',
424
+ /** Every 12 hours */
425
+ EVERY_12_HOURS: '0 */12 * * *',
426
+ /** Daily at midnight */
427
+ DAILY: '0 0 * * *',
428
+ /** Daily at noon */
429
+ DAILY_NOON: '0 12 * * *',
430
+ /** Weekly on Sunday at midnight */
431
+ WEEKLY: '0 0 * * 0',
432
+ /** Monthly on 1st at midnight */
433
+ MONTHLY: '0 0 1 * *',
434
+ /** Weekdays at 9 AM */
435
+ WEEKDAYS_9AM: '0 9 * * 1-5',
436
+ /** Weekends at 10 AM */
437
+ WEEKENDS_10AM: '0 10 * * 0,6',
438
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Queue/Jobs Module
3
+ *
4
+ * Provides background job processing with:
5
+ * - Multiple queue management
6
+ * - Job priority and retry strategies
7
+ * - Cron-based scheduling
8
+ * - Pre-built workers for common tasks
9
+ * - Real-time monitoring and metrics
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { QueueService, CronJobManager, defaultWorkers, CronSchedules } from './modules/queue';
14
+ *
15
+ * // Create queue service
16
+ * const queueService = new QueueService({
17
+ * redis: {
18
+ * host: 'localhost',
19
+ * port: 6379
20
+ * },
21
+ * metrics: true
22
+ * });
23
+ *
24
+ * // Register workers
25
+ * import { emailWorker, imageProcessingWorker } from './modules/queue';
26
+ * queueService.registerWorker('emails', emailWorker);
27
+ * queueService.registerWorker('images', imageProcessingWorker);
28
+ *
29
+ * // Add jobs
30
+ * await queueService.addJob('emails', 'send-email', {
31
+ * to: 'user@example.com',
32
+ * subject: 'Welcome!',
33
+ * html: '<h1>Welcome to our service</h1>'
34
+ * });
35
+ *
36
+ * // Create cron jobs
37
+ * const cronManager = new CronJobManager(queueService);
38
+ * await cronManager.createCronJob(
39
+ * 'Daily Backup',
40
+ * CronSchedules.DAILY,
41
+ * 'maintenance',
42
+ * 'database-backup',
43
+ * { databases: ['main', 'analytics'] }
44
+ * );
45
+ *
46
+ * // Add routes
47
+ * app.use('/api/queue', authMiddleware, createQueueRoutes(queueService, cronManager));
48
+ * ```
49
+ *
50
+ * ## Pre-built Workers
51
+ *
52
+ * - `emailWorker` - Send emails
53
+ * - `imageProcessingWorker` - Process images (resize, crop, watermark)
54
+ * - `notificationWorker` - Send push/SMS/email notifications
55
+ * - `webhookWorker` - Send HTTP webhooks
56
+ * - `dataExportWorker` - Export data to CSV/Excel/PDF
57
+ * - `reportGenerationWorker` - Generate reports
58
+ * - `databaseBackupWorker` - Create database backups
59
+ * - `cacheWarmingWorker` - Warm up cache
60
+ * - `dataCleanupWorker` - Clean old data
61
+ * - `batchProcessingWorker` - Process items in batches
62
+ */
63
+
64
+ // Types
65
+ export * from './types.js';
66
+
67
+ // Services
68
+ export { QueueService } from './queue.service.js';
69
+ export { CronJobManager, CronSchedules } from './cron.js';
70
+
71
+ // Workers
72
+ export {
73
+ emailWorker,
74
+ imageProcessingWorker,
75
+ notificationWorker,
76
+ webhookWorker,
77
+ dataExportWorker,
78
+ reportGenerationWorker,
79
+ databaseBackupWorker,
80
+ cacheWarmingWorker,
81
+ dataCleanupWorker,
82
+ batchProcessingWorker,
83
+ defaultWorkers,
84
+ } from './workers.js';
85
+
86
+ // Routes
87
+ export { createQueueRoutes } from './queue.routes.js';