CognxSafeTrack Claude Sonnet 4.6 commited on
Commit
76f2a6a
Β·
1 Parent(s): 3deace7

fix(build+worker): restore seedDatabase export + DISABLE_WORKER_CONSUMER guard

Browse files

1. packages/database/seed.ts: restore re-export of seedDatabase.
moduleResolution:node ignores package.json exports field β€” TypeScript
resolves @repo /database/seed to root seed.ts. Without the re-export,
tsc sees no seedDatabase export β†’ TS2339 build failure.
Runtime still uses dist/src/seed.js (via exports field) which is
compiled CJS with no TypeScript syntax.

2. apps/whatsapp-worker/src/index.ts: add DISABLE_WORKER_CONSUMER guard.
Both Railway and HuggingFace workers share the same BullMQ/Redis queue.
When HuggingFace wins a send-message job, it fails with SSL EPROTO
(HF cannot reach graph.facebook.com). Setting DISABLE_WORKER_CONSUMER=true
on HuggingFace keeps the bridge running (accepts forwarded webhooks) but
stops it from consuming jobs β€” only Railway's worker sends to Meta.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

apps/whatsapp-worker/src/index.ts CHANGED
@@ -171,7 +171,14 @@ server.post('/v1/internal/whatsapp/inbound', async (req: FastifyRequest, reply:
171
  server.get('/health', async () => ({ status: 'ok' }));
172
 
173
  // ─── WORKER ──────────────────────────────────────────────────────────────────
174
- const worker = new Worker('whatsapp-queue', async (job: Job<JobData>) => {
 
 
 
 
 
 
 
175
  const organizationId = job.data.organizationId;
176
  if (!organizationId) {
177
  logger.error({ jobId: job.id, jobName: job.name }, '[WORKER] Job missing organizationId β€” skipping to prevent cross-tenant contamination');
@@ -213,20 +220,21 @@ const worker = new Worker('whatsapp-queue', async (job: Job<JobData>) => {
213
  throw err;
214
  }
215
  });
216
- }, {
217
  connection,
218
  concurrency: parseInt(process.env.WORKER_CONCURRENCY || '5')
219
- });
220
-
221
- worker.on('completed', job => {
222
- logger.info(`[WORKER] Job ${job.id} has completed!`);
223
- });
224
 
225
- worker.on('failed', (job, err) => {
226
- logger.error(`[WORKER] Job ${job?.id} failed: ${err.message}`);
227
- });
 
 
 
 
 
228
 
229
- const notificationWorker = new Worker('notification-queue', async (job: Job<any>) => {
230
  logger.info(`[NOTIFICATION_WORKER] Processing job: ${job.name} (${job.id})`);
231
  try {
232
  const handler = handlers[job.name];
@@ -239,18 +247,19 @@ const notificationWorker = new Worker('notification-queue', async (job: Job<any>
239
  logger.error(`[NOTIFICATION_WORKER] Job ${job.id} failed:`, err);
240
  throw err;
241
  }
242
- }, {
243
  connection,
244
  concurrency: 2
245
- });
246
-
247
- notificationWorker.on('completed', job => {
248
- logger.info(`[NOTIFICATION_WORKER] Job ${job.id} has completed!`);
249
- });
250
 
251
- notificationWorker.on('failed', (job, err) => {
252
- logger.error(`[NOTIFICATION_WORKER] Job ${job?.id} failed: ${err?.message}`);
253
- });
 
 
 
 
 
254
 
255
  // ─── STARTUP ─────────────────────────────────────────────────────────────────
256
  const PORT = 8082; // Internal port for the bridge, avoids conflict with the main API on Railway's public PORT
@@ -279,8 +288,8 @@ const handleShutdown = async (signal: string) => {
279
  logger.info('[SHUTDOWN] HTTP Bridge server closed.');
280
 
281
  // 2. Close workers (stop processing new jobs)
282
- await worker.close();
283
- await notificationWorker.close();
284
  logger.info('[SHUTDOWN] Workers closed.');
285
 
286
  // 3. Close queues and Redis
 
171
  server.get('/health', async () => ({ status: 'ok' }));
172
 
173
  // ─── WORKER ──────────────────────────────────────────────────────────────────
174
+ // Set DISABLE_WORKER_CONSUMER=true on HuggingFace to prevent it from competing
175
+ // with the Railway worker on the shared BullMQ queue (HF can't reach graph.facebook.com).
176
+ const CONSUMER_ENABLED = process.env.DISABLE_WORKER_CONSUMER !== 'true';
177
+ if (!CONSUMER_ENABLED) {
178
+ logger.warn('[WORKER] DISABLE_WORKER_CONSUMER=true β€” BullMQ consumer disabled. Bridge only mode.');
179
+ }
180
+
181
+ const worker = CONSUMER_ENABLED ? new Worker('whatsapp-queue', async (job: Job<JobData>) => {
182
  const organizationId = job.data.organizationId;
183
  if (!organizationId) {
184
  logger.error({ jobId: job.id, jobName: job.name }, '[WORKER] Job missing organizationId β€” skipping to prevent cross-tenant contamination');
 
220
  throw err;
221
  }
222
  });
223
+ }, {
224
  connection,
225
  concurrency: parseInt(process.env.WORKER_CONCURRENCY || '5')
226
+ }) : null;
 
 
 
 
227
 
228
+ if (worker) {
229
+ worker.on('completed', job => {
230
+ logger.info(`[WORKER] Job ${job.id} has completed!`);
231
+ });
232
+ worker.on('failed', (job, err) => {
233
+ logger.error(`[WORKER] Job ${job?.id} failed: ${err.message}`);
234
+ });
235
+ }
236
 
237
+ const notificationWorker = CONSUMER_ENABLED ? new Worker('notification-queue', async (job: Job<any>) => {
238
  logger.info(`[NOTIFICATION_WORKER] Processing job: ${job.name} (${job.id})`);
239
  try {
240
  const handler = handlers[job.name];
 
247
  logger.error(`[NOTIFICATION_WORKER] Job ${job.id} failed:`, err);
248
  throw err;
249
  }
250
+ }, {
251
  connection,
252
  concurrency: 2
253
+ }) : null;
 
 
 
 
254
 
255
+ if (notificationWorker) {
256
+ notificationWorker.on('completed', job => {
257
+ logger.info(`[NOTIFICATION_WORKER] Job ${job.id} has completed!`);
258
+ });
259
+ notificationWorker.on('failed', (job, err) => {
260
+ logger.error(`[NOTIFICATION_WORKER] Job ${job?.id} failed: ${err?.message}`);
261
+ });
262
+ }
263
 
264
  // ─── STARTUP ─────────────────────────────────────────────────────────────────
265
  const PORT = 8082; // Internal port for the bridge, avoids conflict with the main API on Railway's public PORT
 
288
  logger.info('[SHUTDOWN] HTTP Bridge server closed.');
289
 
290
  // 2. Close workers (stop processing new jobs)
291
+ await worker?.close();
292
+ await notificationWorker?.close();
293
  logger.info('[SHUTDOWN] Workers closed.');
294
 
295
  // 3. Close queues and Redis
packages/database/seed.ts CHANGED
@@ -1,3 +1,6 @@
 
 
 
1
  /**
2
  * Seed Script β€” Module 1 : Eco-Moto Business
3
  * Inserts a realistic Track + 3 TrackDays for testing.
 
1
+ // Re-export for @repo/database/seed import in CommandHandler (moduleResolution:node ignores exports field)
2
+ export { seedDatabase } from './src/seed';
3
+
4
  /**
5
  * Seed Script β€” Module 1 : Eco-Moto Business
6
  * Inserts a realistic Track + 3 TrackDays for testing.