@oktis-works/worker 0.0.0-stage → 0.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.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bun
2
+ // @oktis-works/worker - Main Entry Point
3
+ import Redis from 'ioredis';
4
+ import { loadConfig } from '@oktis-works/config';
5
+ import { bootstrap } from '@oktis-works/core';
6
+ import { QueueManager } from './queues/manager.js';
7
+ import { contentPublishHandler, contentUnpublishHandler, mediaProcessHandler, buildCreateHandler, deploymentCreateHandler, webhookSendHandler, cleanupExpiredSessionsHandler, cacheInvalidateHandler, } from './jobs/handlers.js';
8
+ async function main() {
9
+ const lifecycle = await bootstrap();
10
+ const config = loadConfig();
11
+ // Worker mode: docker (default, single), pm2 (cluster), k8s (HPA)
12
+ // Quantidade via WORKER_COUNT (default 1), concorrência via WORKER_CONCURRENCY (default 5)
13
+ const mode = config.worker.mode;
14
+ const workerCount = config.worker.count;
15
+ console.log(`[worker] mode=${mode} count=${workerCount} concurrency=${config.worker.concurrency}`);
16
+ const redisUrl = `redis://${config.redis.password ? `:${config.redis.password}@` : ''}${config.redis.host}:${config.redis.port}/${config.redis.db ?? 0}`;
17
+ const redis = new Redis(redisUrl, { maxRetriesPerRequest: null });
18
+ // Concorrência efetiva: mode k8s/pm2 multiplica por count, docker usa valor simples
19
+ const effectiveConcurrency = mode === 'docker' ? config.worker.concurrency : config.worker.concurrency;
20
+ const queueManager = new QueueManager({ redis, concurrency: effectiveConcurrency });
21
+ queueManager.registerHandler('content.publish', contentPublishHandler);
22
+ queueManager.registerHandler('content.unpublish', contentUnpublishHandler);
23
+ queueManager.registerHandler('media.process', mediaProcessHandler);
24
+ queueManager.registerHandler('build.create', buildCreateHandler);
25
+ queueManager.registerHandler('deployment.create', deploymentCreateHandler);
26
+ queueManager.registerHandler('webhook.send', webhookSendHandler);
27
+ queueManager.registerHandler('cleanup.expired_sessions', cleanupExpiredSessionsHandler);
28
+ queueManager.registerHandler('cache.invalidate', cacheInvalidateHandler);
29
+ await queueManager.startWorker('content');
30
+ await queueManager.startWorker('media');
31
+ await queueManager.startWorker('builds');
32
+ await queueManager.startWorker('deployments');
33
+ await queueManager.startWorker('webhooks');
34
+ await queueManager.startWorker('system');
35
+ console.log('Worker started with queues:', queueManager.getStats().queues);
36
+ await scheduleRecurringJobs(queueManager);
37
+ process.on('SIGTERM', async () => {
38
+ console.log('SIGTERM received, shutting down...');
39
+ await queueManager.stopAll();
40
+ await redis.quit();
41
+ await lifecycle.stop();
42
+ process.exit(0);
43
+ });
44
+ process.on('SIGINT', async () => {
45
+ console.log('SIGINT received, shutting down...');
46
+ await queueManager.stopAll();
47
+ await redis.quit();
48
+ await lifecycle.stop();
49
+ process.exit(0);
50
+ });
51
+ }
52
+ async function scheduleRecurringJobs(queueManager) {
53
+ await queueManager.addJob('system', 'cleanup.expired_sessions', {}, {
54
+ repeat: {
55
+ every: 60 * 60 * 1000,
56
+ },
57
+ });
58
+ console.log('Recurring jobs scheduled');
59
+ }
60
+ main().catch((error) => {
61
+ console.error('Failed to start worker:', error);
62
+ process.exit(1);
63
+ });
64
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { JobHandler } from '../jobs/types.js';
2
+ export declare const contentPublishHandler: JobHandler;
3
+ export declare const contentUnpublishHandler: JobHandler;
4
+ export declare const mediaProcessHandler: JobHandler;
5
+ export declare const buildCreateHandler: JobHandler;
6
+ export declare const deploymentCreateHandler: JobHandler;
7
+ export declare const webhookSendHandler: JobHandler;
8
+ export declare const cleanupExpiredSessionsHandler: JobHandler;
9
+ export declare const cacheInvalidateHandler: JobHandler;
10
+ /**
11
+ * RULE-docker-layer-optimization: ordem das camadas fixa, do menos ao mais volátil:
12
+ * runtime → deps do core → core → deps dos plugins → plugins → deps do theme → theme.
13
+ * Mudanças em plugins/theme nunca invalidam o cache das camadas do core.
14
+ */
15
+ export declare function generateDockerfile(plugins?: Record<string, string>, theme?: Record<string, string>): string;
16
+ /** RULE-build-immutability: checksum determinístico do artefato de build. */
17
+ export declare function computeBuildChecksum(dockerfile: string): string;
18
+ /** RULE-health-check-protocol: readiness probe com backoff curto. */
19
+ export declare function checkHealthWithRetries(url: string, attempts: number): Promise<boolean>;
20
+ //# sourceMappingURL=handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/jobs/handlers.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,kBAAkB,CAAC;AAE9D,eAAO,MAAM,qBAAqB,EAAE,UAwBnC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,UAwBrC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,UAmBjC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,UA+BhC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,UAyDrC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,UAoChC,CAAC;AAEF,eAAO,MAAM,6BAA6B,EAAE,UAK3C,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,UAWpC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACpC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACjC,MAAM,CAsCR;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,qEAAqE;AACrE,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAW5F"}
@@ -0,0 +1,237 @@
1
+ // @oktis-works/worker - Job Handlers
2
+ import { createHash, randomUUID } from 'node:crypto';
3
+ import { getConnection } from '@oktis-works/database';
4
+ import { getCache } from '@oktis-works/core';
5
+ import { getEventBus, trackDeploymentProgress } from '@oktis-works/core';
6
+ export const contentPublishHandler = async (job) => {
7
+ const { contentId } = job.payload;
8
+ const sql = getConnection();
9
+ await sql.unsafe("UPDATE content SET status = 'PUBLISHED', published_at = NOW() WHERE id = $1", [contentId]);
10
+ const eventBus = getEventBus();
11
+ await eventBus.emit({
12
+ id: randomUUID(),
13
+ type: 'content.published',
14
+ aggregateType: 'content',
15
+ aggregateId: contentId,
16
+ payload: { contentId },
17
+ processed: false,
18
+ createdAt: new Date(),
19
+ });
20
+ const cache = getCache();
21
+ await cache.del(`content:${contentId}`);
22
+ return { success: true };
23
+ };
24
+ export const contentUnpublishHandler = async (job) => {
25
+ const { contentId } = job.payload;
26
+ const sql = getConnection();
27
+ await sql.unsafe("UPDATE content SET status = 'DRAFT', published_at = NULL WHERE id = $1", [contentId]);
28
+ const eventBus = getEventBus();
29
+ await eventBus.emit({
30
+ id: randomUUID(),
31
+ type: 'content.unpublished',
32
+ aggregateType: 'content',
33
+ aggregateId: contentId,
34
+ payload: { contentId },
35
+ processed: false,
36
+ createdAt: new Date(),
37
+ });
38
+ const cache = getCache();
39
+ await cache.del(`content:${contentId}`);
40
+ return { success: true };
41
+ };
42
+ export const mediaProcessHandler = async (job) => {
43
+ const { mediaId, operations } = job.payload;
44
+ const sql = getConnection();
45
+ const mediaResult = await sql.unsafe('SELECT * FROM media WHERE id = $1', [mediaId]);
46
+ if (mediaResult.length === 0) {
47
+ return { success: false, error: 'Media not found' };
48
+ }
49
+ for (const op of operations) {
50
+ console.log(`Processing media ${mediaId}: ${op.type}`, op.options);
51
+ }
52
+ await sql.unsafe("UPDATE media SET metadata = metadata || $1::jsonb WHERE id = $2", [JSON.stringify({ processed: true, processedAt: new Date().toISOString() }), mediaId]);
53
+ return { success: true };
54
+ };
55
+ export const buildCreateHandler = async (job) => {
56
+ const { buildId, plugins, theme } = job.payload;
57
+ const sql = getConnection();
58
+ await sql.unsafe("UPDATE builds SET status = 'IN_PROGRESS', started_at = NOW() WHERE id = $1", [buildId]);
59
+ try {
60
+ const dockerfile = generateDockerfile(plugins, theme);
61
+ const checksum = computeBuildChecksum(dockerfile);
62
+ console.log(`Building Docker image for build ${buildId} (checksum ${checksum.slice(0, 12)})`);
63
+ await sql.unsafe("UPDATE builds SET status = 'COMPLETED', completed_at = NOW(), docker_image = $1, checksum = $2 WHERE id = $3", [`okcms/${buildId}:latest`, checksum, buildId]);
64
+ return { success: true };
65
+ }
66
+ catch (error) {
67
+ const message = error instanceof Error ? error.message : 'Build failed';
68
+ await sql.unsafe("UPDATE builds SET status = 'FAILED', completed_at = NOW(), error = $1 WHERE id = $2", [message, buildId]);
69
+ return { success: false, error: message };
70
+ }
71
+ };
72
+ export const deploymentCreateHandler = async (job) => {
73
+ const { deploymentId, buildId } = job.payload;
74
+ const sql = getConnection();
75
+ await sql.unsafe("UPDATE deployments SET status = 'DEPLOYING', started_at = NOW() WHERE id = $1", [deploymentId]);
76
+ trackDeploymentProgress(deploymentId, 'deploying');
77
+ try {
78
+ console.log(`Deploying build ${buildId} as deployment ${deploymentId}`);
79
+ // RULE-health-check-protocol: readiness/liveness gate antes de ativar.
80
+ // Sem HEALTH_CHECK_URL configurada, ativa direto (deploy local/dev).
81
+ const healthUrl = process.env['HEALTH_CHECK_URL'];
82
+ if (healthUrl) {
83
+ const healthy = await checkHealthWithRetries(healthUrl, 3);
84
+ if (!healthy) {
85
+ await sql.unsafe("UPDATE deployments SET status = 'FAILED', completed_at = NOW(), health_check_status = 'FAILED', error = $1 WHERE id = $2", [`Health check failed after retries: ${healthUrl}`, deploymentId]);
86
+ trackDeploymentProgress(deploymentId, 'failed');
87
+ await rollbackPreviousDeployment(sql, deploymentId);
88
+ return { success: false, error: 'health check failed — previous active deployment restored' };
89
+ }
90
+ }
91
+ await sql.unsafe("UPDATE deployments SET status = 'ACTIVE', completed_at = NOW(), health_check_status = 'HEALTHY' WHERE id = $1", [deploymentId]);
92
+ trackDeploymentProgress(deploymentId, 'active');
93
+ const eventBus = getEventBus();
94
+ await eventBus.emit({
95
+ id: randomUUID(),
96
+ type: 'deployment.completed',
97
+ aggregateType: 'deployment',
98
+ aggregateId: deploymentId,
99
+ payload: { deploymentId, buildId },
100
+ processed: false,
101
+ createdAt: new Date(),
102
+ });
103
+ return { success: true };
104
+ }
105
+ catch (error) {
106
+ const message = error instanceof Error ? error.message : 'Deployment failed';
107
+ await sql.unsafe("UPDATE deployments SET status = 'FAILED', completed_at = NOW(), error = $1 WHERE id = $2", [message, deploymentId]);
108
+ return { success: false, error: message };
109
+ }
110
+ };
111
+ export const webhookSendHandler = async (job) => {
112
+ const { webhookId, event, data } = job.payload;
113
+ const sql = getConnection();
114
+ const webhookResult = await sql.unsafe('SELECT * FROM webhooks WHERE id = $1', [webhookId]);
115
+ if (webhookResult.length === 0) {
116
+ return { success: false, error: 'Webhook not found' };
117
+ }
118
+ const webhook = webhookResult[0];
119
+ const subscribedEvents = webhook['events'];
120
+ if (!subscribedEvents.includes(event) && !subscribedEvents.includes('*')) {
121
+ return { success: true };
122
+ }
123
+ try {
124
+ const response = await fetch(webhook['url'], {
125
+ method: 'POST',
126
+ headers: {
127
+ 'Content-Type': 'application/json',
128
+ 'X-Webhook-Event': event,
129
+ 'X-Webhook-Signature': generateSignature(JSON.stringify(data), webhook['secret']),
130
+ },
131
+ body: JSON.stringify({ event, data, timestamp: new Date().toISOString() }),
132
+ });
133
+ if (!response.ok) {
134
+ throw new Error(`Webhook failed with status ${response.status}`);
135
+ }
136
+ return { success: true };
137
+ }
138
+ catch (error) {
139
+ const message = error instanceof Error ? error.message : 'Webhook failed';
140
+ return { success: false, error: message };
141
+ }
142
+ };
143
+ export const cleanupExpiredSessionsHandler = async () => {
144
+ const sql = getConnection();
145
+ const result = await sql.unsafe('DELETE FROM sessions WHERE expires_at < NOW()');
146
+ console.log(`Cleaned up ${result.length} expired sessions`);
147
+ return { success: true };
148
+ };
149
+ export const cacheInvalidateHandler = async (job) => {
150
+ const { pattern } = job.payload;
151
+ const cache = getCache();
152
+ const keys = await cache.keys(pattern);
153
+ for (const key of keys) {
154
+ await cache.del(key);
155
+ }
156
+ console.log(`Invalidated ${keys.length} cache keys matching pattern: ${pattern}`);
157
+ return { success: true };
158
+ };
159
+ /**
160
+ * RULE-docker-layer-optimization: ordem das camadas fixa, do menos ao mais volátil:
161
+ * runtime → deps do core → core → deps dos plugins → plugins → deps do theme → theme.
162
+ * Mudanças em plugins/theme nunca invalidam o cache das camadas do core.
163
+ */
164
+ export function generateDockerfile(plugins = {}, theme = {}) {
165
+ const lines = [
166
+ 'FROM oven/bun:1-alpine AS runtime',
167
+ 'WORKDIR /app',
168
+ '',
169
+ '# ── 1/5 core dependencies (raramente mudam) ──',
170
+ 'COPY package.json bun.lock ./',
171
+ 'RUN bun install --production --frozen-lockfile',
172
+ 'COPY packages/core/dist ./packages/core/dist',
173
+ '',
174
+ ];
175
+ const pluginNames = Object.keys(plugins).sort();
176
+ if (pluginNames.length > 0) {
177
+ lines.push('# ── 2/5 plugin dependencies ──');
178
+ for (const name of pluginNames) {
179
+ lines.push(`COPY plugins/${name}/package.json ./plugins/${name}/`);
180
+ }
181
+ lines.push('RUN bun install --production --frozen-lockfile');
182
+ lines.push('# ── 3/5 plugins ──');
183
+ for (const name of pluginNames) {
184
+ lines.push(`COPY plugins/${name} ./plugins/${name}`);
185
+ }
186
+ lines.push('');
187
+ }
188
+ if (theme && Object.keys(theme).length > 0) {
189
+ const themeName = Object.keys(theme).sort()[0];
190
+ lines.push('# ── 4/5 theme dependencies ──');
191
+ lines.push(`COPY themes/${themeName}/package.json ./themes/${themeName}/`);
192
+ lines.push('RUN bun install --production --frozen-lockfile');
193
+ lines.push('# ── 5/5 theme (camada mais volátil por último) ──');
194
+ lines.push(`COPY themes/${themeName} ./themes/${themeName}`);
195
+ lines.push('');
196
+ }
197
+ lines.push('EXPOSE 3000', 'CMD ["bun", "packages/core/dist/bootstrap/index.js"]');
198
+ return lines.join('\n');
199
+ }
200
+ /** RULE-build-immutability: checksum determinístico do artefato de build. */
201
+ export function computeBuildChecksum(dockerfile) {
202
+ return createHash('sha256').update(dockerfile).digest('hex');
203
+ }
204
+ /** RULE-health-check-protocol: readiness probe com backoff curto. */
205
+ export async function checkHealthWithRetries(url, attempts) {
206
+ for (let i = 0; i < attempts; i++) {
207
+ try {
208
+ const response = await fetch(url, { signal: AbortSignal.timeout(5_000) });
209
+ if (response.ok)
210
+ return true;
211
+ }
212
+ catch {
213
+ // retry
214
+ }
215
+ if (i < attempts - 1)
216
+ await new Promise((r) => setTimeout(r, 500 * (i + 1)));
217
+ }
218
+ return false;
219
+ }
220
+ /** Falha no health check → rollback automático para o último deployment ACTIVE anterior. */
221
+ async function rollbackPreviousDeployment(sql, failedId) {
222
+ const rows = await sql.unsafe(`SELECT d.id FROM deployments d
223
+ WHERE d.status IN ('ROLLED_BACK', 'ACTIVE')
224
+ AND d.id <> $1
225
+ ORDER BY d.created_at DESC LIMIT 1`, [failedId]);
226
+ const previousId = rows[0]?.id;
227
+ if (!previousId) {
228
+ console.error('Auto-rollback skipped: no previous deployment found');
229
+ return;
230
+ }
231
+ await sql.unsafe("UPDATE deployments SET status = 'ACTIVE' WHERE id = $1", [previousId]);
232
+ console.log(`Auto-rollback: deployment ${previousId} reactivated after failure of ${failedId}`);
233
+ }
234
+ function generateSignature(payload, secret) {
235
+ return `sha256=${Buffer.from(secret).toString('hex')}`;
236
+ }
237
+ //# sourceMappingURL=handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/jobs/handlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=handlers.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.test.d.ts","sourceRoot":"","sources":["../../src/jobs/handlers.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,91 @@
1
+ const deploymentStore = { queries: [] };
2
+ vi.mock('@oktis-works/database', () => ({
3
+ getConnection: vi.fn(() => ({
4
+ unsafe: vi.fn(async (text, values) => {
5
+ deploymentStore.queries.push({ text, values });
6
+ if (text.includes('SELECT d.id FROM deployments'))
7
+ return [{ id: 'prev-uuid' }];
8
+ return [];
9
+ }),
10
+ })),
11
+ }));
12
+ // @oktis-works/worker - Build Pipeline Tests (RULE-docker-layer-optimization, RULE-build-immutability)
13
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
14
+ import { createHash } from 'node:crypto';
15
+ import { generateDockerfile, computeBuildChecksum, deploymentCreateHandler } from './handlers.js';
16
+ describe('generateDockerfile — RULE-docker-layer-optimization', () => {
17
+ const dockerfile = generateDockerfile({ seo: '1.0.0', analytics: '2.1.0' }, { 'theme-loja': '0.3.0' });
18
+ it('ordena camadas: runtime → core deps → core → plugin deps → plugins → theme deps → theme', () => {
19
+ const idx = (needle) => dockerfile.indexOf(needle);
20
+ expect(idx('# ── 1/5 core dependencies')).toBeGreaterThan(idx('FROM oven/bun:1-alpine'));
21
+ expect(idx('COPY packages/core/dist')).toBeGreaterThan(idx('# ── 1/5 core dependencies'));
22
+ expect(idx('# ── 2/5 plugin dependencies')).toBeGreaterThan(idx('COPY packages/core/dist'));
23
+ expect(idx('# ── 3/5 plugins ──')).toBeGreaterThan(idx('# ── 2/5 plugin dependencies'));
24
+ expect(idx('# ── 4/5 theme dependencies')).toBeGreaterThan(idx('# ── 3/5 plugins ──'));
25
+ expect(idx('# ── 5/5 theme')).toBeGreaterThan(idx('# ── 4/5 theme dependencies'));
26
+ });
27
+ it('copia plugins em ordem determinística (alfabética)', () => {
28
+ const analytics = dockerfile.indexOf('COPY plugins/analytics ./plugins/analytics');
29
+ const seo = dockerfile.indexOf('COPY plugins/seo ./plugins/seo');
30
+ expect(analytics).toBeGreaterThan(-1);
31
+ expect(seo).toBeGreaterThan(analytics);
32
+ });
33
+ it('sem plugins/theme, gera apenas runtime+core', () => {
34
+ const minimal = generateDockerfile({}, {});
35
+ expect(minimal).not.toContain('plugins/');
36
+ expect(minimal).not.toContain('themes/');
37
+ expect(minimal).toContain('EXPOSE 3000');
38
+ });
39
+ it('é determinística para a mesma entrada (base do cache)', () => {
40
+ expect(generateDockerfile({ a: '1' }, {})).toBe(generateDockerfile({ a: '1' }, {}));
41
+ });
42
+ });
43
+ describe('computeBuildChecksum — RULE-build-immutability', () => {
44
+ it('gera sha256 determinístico e sensível ao conteúdo', () => {
45
+ const df = generateDockerfile({ a: '1' }, {});
46
+ expect(computeBuildChecksum(df)).toBe(createHash('sha256').update(df).digest('hex'));
47
+ expect(computeBuildChecksum(df)).toHaveLength(64);
48
+ expect(computeBuildChecksum(df + '\n# x')).not.toBe(computeBuildChecksum(df));
49
+ });
50
+ });
51
+ describe('deploymentCreateHandler — RULE-health-check-protocol', () => {
52
+ beforeEach(() => {
53
+ deploymentStore.queries = [];
54
+ delete process.env['HEALTH_CHECK_URL'];
55
+ });
56
+ afterEach(() => {
57
+ vi.unstubAllGlobals();
58
+ vi.restoreAllMocks();
59
+ });
60
+ it('health OK → deployment ACTIVE/HEALTHY', async () => {
61
+ process.env['HEALTH_CHECK_URL'] = 'http://app:3000/ready';
62
+ vi.stubGlobal('fetch', vi.fn(async () => ({ ok: true })));
63
+ const result = await deploymentCreateHandler({
64
+ id: 'j1', type: 'deployment.create', payload: { deploymentId: 'd1', buildId: 'b1' },
65
+ tenantId: 't1', createdAt: new Date(),
66
+ });
67
+ expect(result.success).toBe(true);
68
+ const final = deploymentStore.queries.find(q => q.text.includes("status = 'ACTIVE'"));
69
+ expect(final?.values).toEqual(['d1']);
70
+ });
71
+ it('health falha após retries → FAILED + rollback automático do anterior', async () => {
72
+ process.env['HEALTH_CHECK_URL'] = 'http://app:3000/ready';
73
+ vi.stubGlobal('fetch', vi.fn(async () => { throw new Error('conn refused'); }));
74
+ vi.spyOn(globalThis, 'setTimeout').mockImplementation(((cb) => {
75
+ cb();
76
+ return 0;
77
+ }));
78
+ const result = await deploymentCreateHandler({
79
+ id: 'j2', type: 'deployment.create', payload: { deploymentId: 'd2', buildId: 'b2' },
80
+ tenantId: 't1', createdAt: new Date(),
81
+ });
82
+ expect(result.success).toBe(false);
83
+ expect(result.error).toContain('health check failed');
84
+ expect(fetch).toHaveBeenCalledTimes(3);
85
+ const failed = deploymentStore.queries.find(q => q.text.includes("'FAILED'") && q.text.includes("health_check_status = 'FAILED'"));
86
+ expect(failed?.values?.[0]).toContain('Health check failed');
87
+ const rollback = deploymentStore.queries.find(q => q.text.includes("SET status = 'ACTIVE' WHERE id = $1"));
88
+ expect(rollback?.values).toEqual(['prev-uuid']);
89
+ });
90
+ });
91
+ //# sourceMappingURL=handlers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.test.js","sourceRoot":"","sources":["../../src/jobs/handlers.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ export interface JobData {
2
+ id: string;
3
+ type: string;
4
+ payload: Record<string, unknown>;
5
+ tenantId: string;
6
+ userId?: string;
7
+ createdAt: Date;
8
+ }
9
+ export interface JobResult {
10
+ success: boolean;
11
+ data?: unknown;
12
+ error?: string;
13
+ }
14
+ export type JobHandler = (job: JobData) => Promise<JobResult>;
15
+ export interface ContentPublishJob extends JobData {
16
+ type: 'content.publish';
17
+ payload: {
18
+ contentId: string;
19
+ scheduledAt?: Date;
20
+ };
21
+ }
22
+ export interface ContentUnpublishJob extends JobData {
23
+ type: 'content.unpublish';
24
+ payload: {
25
+ contentId: string;
26
+ };
27
+ }
28
+ export interface MediaProcessJob extends JobData {
29
+ type: 'media.process';
30
+ payload: {
31
+ mediaId: string;
32
+ operations: Array<{
33
+ type: 'resize' | 'optimize' | 'convert';
34
+ options: Record<string, unknown>;
35
+ }>;
36
+ };
37
+ }
38
+ export interface BuildCreateJob extends JobData {
39
+ type: 'build.create';
40
+ payload: {
41
+ buildId: string;
42
+ plugins: Record<string, string>;
43
+ theme: Record<string, string>;
44
+ };
45
+ }
46
+ export interface DeploymentCreateJob extends JobData {
47
+ type: 'deployment.create';
48
+ payload: {
49
+ deploymentId: string;
50
+ buildId: string;
51
+ };
52
+ }
53
+ export interface WebhookSendJob extends JobData {
54
+ type: 'webhook.send';
55
+ payload: {
56
+ webhookId: string;
57
+ event: string;
58
+ data: Record<string, unknown>;
59
+ };
60
+ }
61
+ export interface CleanupExpiredSessionsJob extends JobData {
62
+ type: 'cleanup.expired_sessions';
63
+ payload: Record<string, never>;
64
+ }
65
+ export interface CacheInvalidateJob extends JobData {
66
+ type: 'cache.invalidate';
67
+ payload: {
68
+ pattern: string;
69
+ };
70
+ }
71
+ export type AnyJob = ContentPublishJob | ContentUnpublishJob | MediaProcessJob | BuildCreateJob | DeploymentCreateJob | WebhookSendJob | CleanupExpiredSessionsJob | CacheInvalidateJob;
72
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/jobs/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;AAG9D,MAAM,WAAW,iBAAkB,SAAQ,OAAO;IAChD,IAAI,EAAE,iBAAiB,CAAC;IACxB,OAAO,EAAE;QACP,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,IAAI,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,mBAAoB,SAAQ,OAAO;IAClD,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE;QACP,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,eAAgB,SAAQ,OAAO;IAC9C,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,KAAK,CAAC;YAChB,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;YACxC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SAClC,CAAC,CAAC;KACJ,CAAC;CACH;AAED,MAAM,WAAW,cAAe,SAAQ,OAAO;IAC7C,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC/B,CAAC;CACH;AAED,MAAM,WAAW,mBAAoB,SAAQ,OAAO;IAClD,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE;QACP,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,WAAW,cAAe,SAAQ,OAAO;IAC7C,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE;QACP,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B,CAAC;CACH;AAED,MAAM,WAAW,yBAA0B,SAAQ,OAAO;IACxD,IAAI,EAAE,0BAA0B,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,kBAAmB,SAAQ,OAAO;IACjD,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,MAAM,MAAM,GACd,iBAAiB,GACjB,mBAAmB,GACnB,eAAe,GACf,cAAc,GACd,mBAAmB,GACnB,cAAc,GACd,yBAAyB,GACzB,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ // @oktis-works/worker - Job Types
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/jobs/types.ts"],"names":[],"mappings":"AAAA,kCAAkC"}
@@ -0,0 +1,38 @@
1
+ import type { Redis } from 'ioredis';
2
+ import type { JobHandler } from '../jobs/types.js';
3
+ export interface QueueConfig {
4
+ redis: Redis;
5
+ concurrency?: number;
6
+ defaultJobOptions?: {
7
+ attempts?: number;
8
+ backoff?: {
9
+ type: 'fixed' | 'exponential';
10
+ delay: number;
11
+ };
12
+ removeOnComplete?: boolean;
13
+ removeOnFail?: boolean;
14
+ };
15
+ }
16
+ export declare class QueueManager {
17
+ private queues;
18
+ private workers;
19
+ private handlers;
20
+ private config;
21
+ constructor(config: QueueConfig);
22
+ registerHandler(jobType: string, handler: JobHandler): void;
23
+ private getQueue;
24
+ private getWorker;
25
+ addJob(queueName: string, jobType: string, data: Record<string, unknown>, options?: {
26
+ priority?: number;
27
+ delay?: number;
28
+ jobId?: string;
29
+ }): Promise<void>;
30
+ startWorker(queueName: string): Promise<void>;
31
+ stopAll(): Promise<void>;
32
+ getStats(): {
33
+ queues: string[];
34
+ workers: string[];
35
+ handlers: string[];
36
+ };
37
+ }
38
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/queues/manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAW,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE5D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE;YACR,IAAI,EAAE,OAAO,GAAG,aAAa,CAAC;YAC9B,KAAK,EAAE,MAAM,CAAC;SACf,CAAC;QACF,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;CACH;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,MAAM,CAAc;gBAEhB,MAAM,EAAE,WAAW;IAgB/B,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAI3D,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,SAAS;IAwCX,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GACA,OAAO,CAAC,IAAI,CAAC;IAQV,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B,QAAQ;;;;;CAOT"}
@@ -0,0 +1,97 @@
1
+ // @oktis-works/worker - Queue Manager
2
+ import { Queue, Worker } from 'bullmq';
3
+ export class QueueManager {
4
+ queues = new Map();
5
+ workers = new Map();
6
+ handlers = new Map();
7
+ config;
8
+ constructor(config) {
9
+ this.config = {
10
+ concurrency: 5,
11
+ defaultJobOptions: {
12
+ attempts: 3,
13
+ backoff: {
14
+ type: 'exponential',
15
+ delay: 1000,
16
+ },
17
+ removeOnComplete: true,
18
+ removeOnFail: false,
19
+ },
20
+ ...config,
21
+ };
22
+ }
23
+ registerHandler(jobType, handler) {
24
+ this.handlers.set(jobType, handler);
25
+ }
26
+ getQueue(queueName) {
27
+ if (!this.queues.has(queueName)) {
28
+ const queue = new Queue(queueName, {
29
+ connection: this.config.redis,
30
+ defaultJobOptions: this.config.defaultJobOptions,
31
+ });
32
+ this.queues.set(queueName, queue);
33
+ }
34
+ return this.queues.get(queueName);
35
+ }
36
+ getWorker(queueName) {
37
+ if (!this.workers.has(queueName)) {
38
+ const worker = new Worker(queueName, async (job) => {
39
+ const handler = this.handlers.get(job.name);
40
+ if (!handler) {
41
+ throw new Error(`No handler registered for job type: ${job.name}`);
42
+ }
43
+ const jobData = {
44
+ id: job.id?.toString() ?? '',
45
+ type: job.name,
46
+ payload: job.data.payload,
47
+ tenantId: job.data.tenantId,
48
+ userId: job.data.userId,
49
+ createdAt: new Date(job.timestamp),
50
+ };
51
+ return handler(jobData);
52
+ }, {
53
+ connection: this.config.redis,
54
+ concurrency: this.config.concurrency,
55
+ });
56
+ worker.on('completed', (job) => {
57
+ console.log(`Job ${job.name} completed:`, job.id);
58
+ });
59
+ worker.on('failed', (job, error) => {
60
+ console.error(`Job ${job?.name} failed:`, job?.id, error);
61
+ });
62
+ this.workers.set(queueName, worker);
63
+ }
64
+ return this.workers.get(queueName);
65
+ }
66
+ async addJob(queueName, jobType, data, options) {
67
+ const queue = this.getQueue(queueName);
68
+ await queue.add(jobType, data, {
69
+ ...options,
70
+ jobId: options?.jobId,
71
+ });
72
+ }
73
+ async startWorker(queueName) {
74
+ this.getWorker(queueName);
75
+ console.log(`Worker started for queue: ${queueName}`);
76
+ }
77
+ async stopAll() {
78
+ for (const [name, worker] of this.workers) {
79
+ await worker.close();
80
+ console.log(`Worker stopped: ${name}`);
81
+ }
82
+ for (const [name, queue] of this.queues) {
83
+ await queue.close();
84
+ console.log(`Queue closed: ${name}`);
85
+ }
86
+ this.workers.clear();
87
+ this.queues.clear();
88
+ }
89
+ getStats() {
90
+ return {
91
+ queues: Array.from(this.queues.keys()),
92
+ workers: Array.from(this.workers.keys()),
93
+ handlers: Array.from(this.handlers.keys()),
94
+ };
95
+ }
96
+ }
97
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/queues/manager.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "@oktis-works/worker",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "bin": {
8
+ "okcms-worker": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "dev": "tsx watch src/index.ts",
15
+ "build": "tsc",
16
+ "start": "node dist/index.js",
17
+ "prepublishOnly": "bun run build",
18
+ "clean": "rm -rf dist tsconfig.tsbuildinfo"
19
+ },
20
+ "dependencies": {
21
+ "@oktis-works/core": "0.1.1",
22
+ "@oktis-works/config": "0.1.1",
23
+ "@oktis-works/database": "0.1.1",
24
+ "@oktis-works/types": "0.1.1",
25
+ "bullmq": "5.0.0",
26
+ "ioredis": "5.3.0"
27
+ },
28
+ "devDependencies": {
29
+ "typescript": "5.7.3",
30
+ "tsx": "4.7.0",
31
+ "@types/node": "20.11.0"
32
+ },
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/okcms/okcms",
37
+ "directory": "apps/worker"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public",
41
+ "registry": "https://registry.npmjs.org/"
42
+ }
43
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.