CognxSafeTrack commited on
Commit
fe2f79f
·
1 Parent(s): fc3b1c4

fix(arch): HF inbound-only + seedDatabase export + DISABLE_WORKER + DISABLE_WHATSAPP_SEND

Browse files
apps/api/src/services/whatsapp.ts CHANGED
@@ -36,71 +36,20 @@ export class WhatsAppService {
36
  }
37
 
38
  if (normalizedText === 'SEED') {
39
- await scheduleMessage(user.id, "🔄 Lancement du seeding en cours...");
 
40
  try {
41
- // Run seed logic directly in-process (no child_process, no path issues)
42
- const existingFR = await prisma.track.findFirst({ where: { title: "Comprendre Son Business (FR)" } });
43
- if (existingFR) {
44
- await scheduleMessage(user.id, "ℹ️ Les données existent déjà en base. Aucun seeding nécessaire !");
45
- return;
46
- }
47
-
48
- // Seed FR track
49
- await prisma.track.create({
50
- data: {
51
- title: "Comprendre Son Business (FR)",
52
- description: "Apprenez à définir, tester et vendre votre projet en 12 leçons.",
53
- duration: 12,
54
- language: "FR",
55
- days: {
56
- create: [
57
- { dayNumber: 1, exerciseType: "AUDIO", lessonText: "Aujourd'hui, on commence simple. Beaucoup de personnes disent : je fais le commerce. Mais ça ne veut rien dire. Dis-moi clairement : Tu aides QUI, à faire QUOI, et comment tu gagnes de l'argent. Exemple : Je vends du jus bissap aux étudiants devant l'université. Maintenant, c'est à toi. Dis ta phrase en 15 secondes.", exercisePrompt: "Envoie-moi un court message vocal (ou texte) avec ta phrase d'activité :" },
58
- { dayNumber: 2, exerciseType: "AUDIO", lessonText: "Le client n'achète pas ton produit. Il achète un résultat. Il n'achète pas du savon. Il achète la propreté. Va demander à 2 clients : Pourquoi tu achètes ça ? Écoute bien leurs mots.", exercisePrompt: "Envoie un audio résumant les 2 réponses de tes clients." },
59
- { dayNumber: 3, exerciseType: "BUTTON", lessonText: "Si tu vends à tout le monde, tu ne vends à personne. Choisis un seul client principal. Qui est le plus intéressé par ton produit ?", exercisePrompt: "Sélectionne ton client principal ci-dessous :", buttonsJson: [{ id: "jeunes", title: "Jeunes" }, { id: "femmes", title: "Femmes" }, { id: "commercants", title: "Commerçants" }] },
60
- { dayNumber: 4, exerciseType: "TEXT", lessonText: "Ton client a un problème. Quel est son plus grand problème ? Parle à 3 personnes aujourd'hui. Pose cette question. Écoute sans expliquer ton produit.", exercisePrompt: "Quel est le problème N°1 que tes clients t'ont partagé ?" },
61
- { dayNumber: 5, exerciseType: "BUTTON", lessonText: "À quel moment ton client a ce problème ?", exercisePrompt: "Choisis le moment d'apparition du problème :", buttonsJson: [{ id: "matin_midi", title: "Matin ou Midi" }, { id: "soir", title: "Le Soir" }, { id: "tout_le_temps", title: "Tout le temps" }] },
62
- { dayNumber: 6, exerciseType: "TEXT", lessonText: "Avant toi, il faisait comment ?", exercisePrompt: "Donne-moi 2 solutions que ton client utilisait avant de te connaître :" },
63
- { dayNumber: 7, exerciseType: "TEXT", lessonText: "Explique ta solution en mots simples. Pas compliqué.", exercisePrompt: "Décris-moi ton offre très simplement en une phrase :" },
64
- { dayNumber: 8, exerciseType: "BUTTON", lessonText: "Tu ne peux pas promettre tout. Choisis une seule force.", exercisePrompt: "Quelle est ta promesse principale ?", buttonsJson: [{ id: "rapide", title: "Rapide" }, { id: "moins_cher", title: "Moins cher" }, { id: "fiable_proche", title: "Fiable / Proche" }] },
65
- { dayNumber: 9, exerciseType: "TEXT", lessonText: "Parle à 5 personnes. Dis ta phrase. Combien disent OUI ?", exercisePrompt: "Combien t'ont dit OUI ? (Envoie juste un chiffre)" },
66
- { dayNumber: 10, exerciseType: "TEXT", lessonText: "Ton prix doit couvrir tes coûts. Note 2 dépenses importantes.", exercisePrompt: "Quelles sont tes 2 plus grosses dépenses pour ce projet ?" },
67
- { dayNumber: 11, exerciseType: "BUTTON", lessonText: "Pourquoi toi et pas un autre ?", exercisePrompt: "Quel est ton vrai avantage concurrentiel ?", buttonsJson: [{ id: "qualite", title: "Qualité" }, { id: "rapidite", title: "Rapidité" }, { id: "confiance", title: "Confiance" }] },
68
- { dayNumber: 12, exerciseType: "AUDIO", lessonText: "Maintenant tu es prêt. Dis en 30 secondes : Je suis... J'aide... Parce que... Je vends... À...", exercisePrompt: "C'est l'heure du test ! Envoie-moi un audio avec ton Mini Pitch de 30 secondes :" },
69
- ]
70
- }
71
- }
72
- });
73
-
74
- // Seed WOLOF track
75
- await prisma.track.create({
76
- data: {
77
- title: "Comprendre Son Business (WOLOF)",
78
- description: "Apprenez à définir, tester et vendre votre projet en 12 leçons.",
79
- duration: 12,
80
- language: "WOLOF",
81
- days: {
82
- create: [
83
- { dayNumber: 1, exerciseType: "AUDIO", lessonText: "Tey, danuy tàmbalee ak lu yomb. Nit ñu bari dañuy wax : dama def commerce. Waaye loolu amul solo. Wax ma leer : Yaay jàppalé KAN, mu def LAN, te naka nga amee xaalis. Misaal : Damaa jaay jus bissap ci taalibe yu université. Léegi sa waxtu la. Wax sa activité ci 15 seconde.", exercisePrompt: "Yónnee ma ab kàddu (audio) walla message bu gatt ngir wax sa mbir :" },
84
- { dayNumber: 2, exerciseType: "AUDIO", lessonText: "Kiliifa bi du jënd sa produit rek. Mu jënd ab résultat. Du jënd savon rek. Mu jënd set. Dem laaj 2 kiliifa : Lu tax nga jënd lii ? Déggal bu baax li ñuy wax.", exercisePrompt: "Yónnee ma audio ngir tënk ñaari tontu ya." },
85
- { dayNumber: 3, exerciseType: "BUTTON", lessonText: "Su nga jaay ci ñépp, doo jaay ci kenn. Tànnal benn kiliifa bu mag. Kan moo gën a soxla sa produit ?", exercisePrompt: "Tànnal sa kiliifa bu mag ci suuf :", buttonsJson: [{ id: "ndaw_nyi", title: "Ndaw ñi / Jeunes" }, { id: "jigeen_nyi", title: "Jigeen ñi / Femmes" }, { id: "jaaykat_yi", title: "Jaaykat yi / Comms" }] },
86
- { dayNumber: 4, exerciseType: "TEXT", lessonText: "Sa kiliifa am na jafe jafe. Lan mooy jafe jafe bu gën a rëy ? Dem waxtaan ak 3 nit. Laaj leen. Bul def publicité.", exercisePrompt: "Lan mooy jafe jafe bu gën a mag bi sa kiliifa yi am ?" },
87
- { dayNumber: 5, exerciseType: "BUTTON", lessonText: "Kañ la jafe jafe bi di ñëw ?", exercisePrompt: "Tànnal jamono ji jafe jafe bi di faral di am :", buttonsJson: [{ id: "suba_bëccëg", title: "Suba walla Bëccëg" }, { id: "ngoon", title: "Ngoon / Guddi" }, { id: "saa_su_ne", title: "Saa su nekk" }] },
88
- { dayNumber: 6, exerciseType: "TEXT", lessonText: "Balaa yaw, naka la daan def ?", exercisePrompt: "Wax ma ñaari pexe yi kiliifa bi daan jëfandikoo balaa xam sa produit :" },
89
- { dayNumber: 7, exerciseType: "TEXT", lessonText: "Wax sa solution ci wax yu yomb.", exercisePrompt: "Tënkal sa solution ci benn phrase bu yomb :" },
90
- { dayNumber: 8, exerciseType: "BUTTON", lessonText: "Bul promettre lépp. Tànnal benn doole.", exercisePrompt: "Lan mooy sa dige bu mag ?", buttonsJson: [{ id: "gaaw", title: "Dafa gaaw" }, { id: "yomb", title: "Dafa yomb / Prix" }, { id: "woor", title: "Dafa woor" }] },
91
- { dayNumber: 9, exerciseType: "TEXT", lessonText: "Dem waxtaan ak 5 nit. Ñaata ñu wax WAAN ?", exercisePrompt: "Ñaata nit ñoo wax WAAN ? (Bind ma chiffre bi rek)" },
92
- { dayNumber: 10, exerciseType: "TEXT", lessonText: "Sa priix war na japp sa dépense. Bind ñaari dépense.", exercisePrompt: "Bind ma ñaari dépense yu gën a rëy ci sa mbir :" },
93
- { dayNumber: 11, exerciseType: "BUTTON", lessonText: "Lu tax yaw te du keneen ?", exercisePrompt: "Lan nga gën a mën ci ñeneen ñi ?", buttonsJson: [{ id: "baax", title: "Dafa baax" }, { id: "gaaw", title: "Dafa gaaw" }, { id: "koolute", title: "Kooluté / Confiance" }] },
94
- { dayNumber: 12, exerciseType: "AUDIO", lessonText: "Léegi nga hazır. Wax ci 30 seconde : Man ma... Damaa jàppalé... Ndax... Damaa jaay... Ci...", exercisePrompt: "Yónnee ma sa Pitch bu gatt ci 30 seconde :" },
95
- ]
96
- }
97
- }
98
- });
99
-
100
- await scheduleMessage(user.id, "✅ Seeding terminé ! Les 2 modules (FR + WOLOF) sont en base. Envoie INSCRIPTION pour commencer.");
101
  } catch (err: any) {
102
- console.error('Seed Error:', err);
103
- await scheduleMessage(user.id, `❌ Erreur de seed : ${err.message}`);
 
104
  }
105
  return;
106
  }
 
36
  }
37
 
38
  if (normalizedText === 'SEED') {
39
+ // Reply immediately so the webhook doesn't time out
40
+ console.log(`[SEED] Triggered by user ${user.id}`);
41
  try {
42
+ const { seedDatabase } = await import('@repo/database');
43
+ const result = await seedDatabase(prisma);
44
+ console.log('[SEED] Result:', result.message);
45
+ await scheduleMessage(user.id, result.seeded
46
+ ? "✅ Seeding terminé ! Les 2 modules (FR + WOLOF) sont en base.\nEnvoie INSCRIPTION pour commencer."
47
+ : "ℹ️ Les données existent déjà. Envoie INSCRIPTION pour commencer."
48
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  } catch (err: any) {
50
+ console.error('[SEED] Error:', err.message);
51
+ // Don't crash scheduleMessage a user-friendly error
52
+ await scheduleMessage(user.id, `❌ Erreur seed : ${err.message?.substring(0, 200)}`);
53
  }
54
  return;
55
  }
apps/whatsapp-worker/src/whatsapp-cloud.ts CHANGED
@@ -32,6 +32,12 @@ function getHeaders(): Record<string, string> {
32
  * @param text - Message body (supports basic WhatsApp markdown: *bold*, _italic_)
33
  */
34
  export async function sendTextMessage(to: string, text: string): Promise<void> {
 
 
 
 
 
 
35
  const body = {
36
  messaging_product: 'whatsapp',
37
  recipient_type: 'individual',
 
32
  * @param text - Message body (supports basic WhatsApp markdown: *bold*, _italic_)
33
  */
34
  export async function sendTextMessage(to: string, text: string): Promise<void> {
35
+ // Safety guard: HF is inbound-only. Only Railway worker should call this.
36
+ if (process.env.DISABLE_WHATSAPP_SEND === 'true') {
37
+ console.warn(`[WhatsApp] DISABLE_WHATSAPP_SEND=true — Skipping send to ${to}. Message: "${text.substring(0, 60)}..."`);
38
+ return;
39
+ }
40
+
41
  const body = {
42
  messaging_product: 'whatsapp',
43
  recipient_type: 'individual',
packages/database/index.ts CHANGED
@@ -1 +1,2 @@
1
  export * from '@prisma/client';
 
 
1
  export * from '@prisma/client';
2
+ export { seedDatabase } from './src/seed';
packages/database/package.json CHANGED
@@ -3,6 +3,10 @@
3
  "version": "0.0.0",
4
  "main": "./index.ts",
5
  "types": "./index.ts",
 
 
 
 
6
  "scripts": {
7
  "db:push": "prisma db push",
8
  "db:studio": "prisma studio",
 
3
  "version": "0.0.0",
4
  "main": "./index.ts",
5
  "types": "./index.ts",
6
+ "exports": {
7
+ ".": "./index.ts",
8
+ "./seed": "./src/seed.ts"
9
+ },
10
  "scripts": {
11
  "db:push": "prisma db push",
12
  "db:studio": "prisma studio",
packages/database/src/seed.ts ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { PrismaClient } from '@prisma/client';
2
+
3
+ /**
4
+ * Seeds the database with the Module 1 curriculum (FR + WOLOF).
5
+ * Safe to call multiple times — checks for existing data before inserting.
6
+ */
7
+ export async function seedDatabase(prisma: PrismaClient): Promise<{ seeded: boolean; message: string }> {
8
+ const existingFR = await prisma.track.findFirst({
9
+ where: { title: "Comprendre Son Business (FR)" }
10
+ });
11
+
12
+ if (existingFR) {
13
+ return { seeded: false, message: "Data already exists. No seeding needed." };
14
+ }
15
+
16
+ // ── MODULE 1 – FR ──────────────────────────────────────────────────────────
17
+ await prisma.track.create({
18
+ data: {
19
+ title: "Comprendre Son Business (FR)",
20
+ description: "Apprenez à définir, tester et vendre votre projet en 12 leçons.",
21
+ duration: 12,
22
+ language: "FR",
23
+ days: {
24
+ create: [
25
+ { dayNumber: 1, exerciseType: "AUDIO", lessonText: "Aujourd'hui, on commence simple. Beaucoup de personnes disent : je fais le commerce. Mais ça ne veut rien dire. Dis-moi clairement : Tu aides QUI, à faire QUOI, et comment tu gagnes de l'argent. Exemple : Je vends du jus bissap aux étudiants devant l'université. Maintenant, c'est à toi. Dis ta phrase en 15 secondes.", exercisePrompt: "Envoie-moi un court message vocal (ou texte) avec ta phrase d'activité :" },
26
+ { dayNumber: 2, exerciseType: "AUDIO", lessonText: "Le client n'achète pas ton produit. Il achète un résultat. Il n'achète pas du savon. Il achète la propreté. Va demander à 2 clients : Pourquoi tu achètes ça ? Écoute bien leurs mots.", exercisePrompt: "Envoie un audio résumant les 2 réponses de tes clients." },
27
+ { dayNumber: 3, exerciseType: "BUTTON", lessonText: "Si tu vends à tout le monde, tu ne vends à personne. Choisis un seul client principal. Qui est le plus intéressé par ton produit ?", exercisePrompt: "Sélectionne ton client principal ci-dessous :", buttonsJson: [{ id: "jeunes", title: "Jeunes" }, { id: "femmes", title: "Femmes" }, { id: "commercants", title: "Commerçants" }] },
28
+ { dayNumber: 4, exerciseType: "TEXT", lessonText: "Ton client a un problème. Quel est son plus grand problème ? Parle à 3 personnes aujourd'hui. Pose cette question. Écoute sans expliquer ton produit.", exercisePrompt: "Quel est le problème N°1 que tes clients t'ont partagé ?" },
29
+ { dayNumber: 5, exerciseType: "BUTTON", lessonText: "À quel moment ton client a ce problème ?", exercisePrompt: "Choisis le moment d'apparition du problème :", buttonsJson: [{ id: "matin_midi", title: "Matin ou Midi" }, { id: "soir", title: "Le Soir" }, { id: "tout_le_temps", title: "Tout le temps" }] },
30
+ { dayNumber: 6, exerciseType: "TEXT", lessonText: "Avant toi, il faisait comment ?", exercisePrompt: "Donne-moi 2 solutions que ton client utilisait avant de te connaître :" },
31
+ { dayNumber: 7, exerciseType: "TEXT", lessonText: "Explique ta solution en mots simples. Pas compliqué.", exercisePrompt: "Décris-moi ton offre très simplement en une phrase :" },
32
+ { dayNumber: 8, exerciseType: "BUTTON", lessonText: "Tu ne peux pas promettre tout. Choisis une seule force.", exercisePrompt: "Quelle est ta promesse principale ?", buttonsJson: [{ id: "rapide", title: "Rapide" }, { id: "moins_cher", title: "Moins cher" }, { id: "fiable_proche", title: "Fiable / Proche" }] },
33
+ { dayNumber: 9, exerciseType: "TEXT", lessonText: "Parle à 5 personnes. Dis ta phrase. Combien disent OUI ?", exercisePrompt: "Combien t'ont dit OUI ? (Envoie juste un chiffre)" },
34
+ { dayNumber: 10, exerciseType: "TEXT", lessonText: "Ton prix doit couvrir tes coûts. Note 2 dépenses importantes.", exercisePrompt: "Quelles sont tes 2 plus grosses dépenses pour ce projet ?" },
35
+ { dayNumber: 11, exerciseType: "BUTTON", lessonText: "Pourquoi toi et pas un autre ?", exercisePrompt: "Quel est ton vrai avantage concurrentiel ?", buttonsJson: [{ id: "qualite", title: "Qualité" }, { id: "rapidite", title: "Rapidité" }, { id: "confiance", title: "Confiance" }] },
36
+ { dayNumber: 12, exerciseType: "AUDIO", lessonText: "Maintenant tu es prêt. Dis en 30 secondes : Je suis... J'aide... Parce que... Je vends... À...", exercisePrompt: "C'est l'heure du test ! Envoie-moi un audio avec ton Mini Pitch de 30 secondes :" },
37
+ ]
38
+ }
39
+ }
40
+ });
41
+
42
+ // ── MODULE 1 – WOLOF ───────────────────────────────────────────────────────
43
+ await prisma.track.create({
44
+ data: {
45
+ title: "Comprendre Son Business (WOLOF)",
46
+ description: "Apprenez à définir, tester et vendre votre projet en 12 leçons.",
47
+ duration: 12,
48
+ language: "WOLOF",
49
+ days: {
50
+ create: [
51
+ { dayNumber: 1, exerciseType: "AUDIO", lessonText: "Tey, danuy tàmbalee ak lu yomb. Nit ñu bari dañuy wax : dama def commerce. Waaye loolu amul solo. Wax ma leer : Yaay jàppalé KAN, mu def LAN, te naka nga amee xaalis. Misaal : Damaa jaay jus bissap ci taalibe yu université. Léegi sa waxtu la. Wax sa activité ci 15 seconde.", exercisePrompt: "Yónnee ma ab kàddu (audio) walla message bu gatt ngir wax sa mbir :" },
52
+ { dayNumber: 2, exerciseType: "AUDIO", lessonText: "Kiliifa bi du jënd sa produit rek. Mu jënd ab résultat. Du jënd savon rek. Mu jënd set. Dem laaj 2 kiliifa : Lu tax nga jënd lii ? Déggal bu baax li ñuy wax.", exercisePrompt: "Yónnee ma audio ngir tënk ñaari tontu ya." },
53
+ { dayNumber: 3, exerciseType: "BUTTON", lessonText: "Su nga jaay ci ñépp, doo jaay ci kenn. Tànnal benn kiliifa bu mag. Kan moo gën a soxla sa produit ?", exercisePrompt: "Tànnal sa kiliifa bu mag ci suuf :", buttonsJson: [{ id: "ndaw_nyi", title: "Ndaw ñi / Jeunes" }, { id: "jigeen_nyi", title: "Jigeen ñi / Femmes" }, { id: "jaaykat_yi", title: "Jaaykat yi / Comms" }] },
54
+ { dayNumber: 4, exerciseType: "TEXT", lessonText: "Sa kiliifa am na jafe jafe. Lan mooy jafe jafe bu gën a rëy ? Dem waxtaan ak 3 nit. Laaj leen. Bul def publicité.", exercisePrompt: "Lan mooy jafe jafe bu gën a mag bi sa kiliifa yi am ?" },
55
+ { dayNumber: 5, exerciseType: "BUTTON", lessonText: "Kañ la jafe jafe bi di ñëw ?", exercisePrompt: "Tànnal jamono ji jafe jafe bi di faral di am :", buttonsJson: [{ id: "suba_bëccëg", title: "Suba walla Bëccëg" }, { id: "ngoon", title: "Ngoon / Guddi" }, { id: "saa_su_ne", title: "Saa su nekk" }] },
56
+ { dayNumber: 6, exerciseType: "TEXT", lessonText: "Balaa yaw, naka la daan def ?", exercisePrompt: "Wax ma ñaari pexe yi kiliifa bi daan jëfandikoo balaa xam sa produit :" },
57
+ { dayNumber: 7, exerciseType: "TEXT", lessonText: "Wax sa solution ci wax yu yomb.", exercisePrompt: "Tënkal sa solution ci benn phrase bu yomb :" },
58
+ { dayNumber: 8, exerciseType: "BUTTON", lessonText: "Bul promettre lépp. Tànnal benn doole.", exercisePrompt: "Lan mooy sa dige bu mag ?", buttonsJson: [{ id: "gaaw", title: "Dafa gaaw" }, { id: "yomb", title: "Dafa yomb / Prix" }, { id: "woor", title: "Dafa woor" }] },
59
+ { dayNumber: 9, exerciseType: "TEXT", lessonText: "Dem waxtaan ak 5 nit. Ñaata ñu wax WAAN ?", exercisePrompt: "Ñaata nit ñoo wax WAAN ? (Bind ma chiffre bi rek)" },
60
+ { dayNumber: 10, exerciseType: "TEXT", lessonText: "Sa priix war na japp sa dépense. Bind ñaari dépense.", exercisePrompt: "Bind ma ñaari dépense yu gën a rëy ci sa mbir :" },
61
+ { dayNumber: 11, exerciseType: "BUTTON", lessonText: "Lu tax yaw te du keneen ?", exercisePrompt: "Lan nga gën a mën ci ñeneen ñi ?", buttonsJson: [{ id: "baax", title: "Dafa baax" }, { id: "gaaw", title: "Dafa gaaw" }, { id: "koolute", title: "Kooluté / Confiance" }] },
62
+ { dayNumber: 12, exerciseType: "AUDIO", lessonText: "Léegi nga hazır. Wax ci 30 seconde : Man ma... Damaa jàppalé... Ndax... Damaa jaay... Ci...", exercisePrompt: "Yónnee ma sa Pitch bu gatt ci 30 seconde :" },
63
+ ]
64
+ }
65
+ }
66
+ });
67
+
68
+ return { seeded: true, message: "✅ Module 1 FR + WOLOF seeded successfully (24 lessons)." };
69
+ }
scripts/start-backend.sh CHANGED
@@ -4,14 +4,19 @@ set -ex
4
  echo "Running database migrations..."
5
  pnpm --filter @repo/database db:push
6
 
7
- echo "Starting WhatsApp Worker..."
8
- # The TypeScript build might output to dist/src/index.js or dist/index.js depending on rootDir
9
- if [ -f "apps/whatsapp-worker/dist/src/index.js" ]; then
10
- node apps/whatsapp-worker/dist/src/index.js &
11
- elif [ -f "apps/whatsapp-worker/dist/index.js" ]; then
12
- node apps/whatsapp-worker/dist/index.js &
 
 
 
 
 
13
  else
14
- echo "ERROR: WhatsApp Worker build output missing (dist/index.js or dist/src/index.js)"
15
  fi
16
 
17
  echo "Starting API..."
@@ -20,6 +25,6 @@ if [ -f "apps/api/dist/src/index.js" ]; then
20
  elif [ -f "apps/api/dist/index.js" ]; then
21
  exec node apps/api/dist/index.js
22
  else
23
- echo "ERROR: API build output missing (dist/index.js or dist/src/index.js)!"
24
  exit 1
25
  fi
 
4
  echo "Running database migrations..."
5
  pnpm --filter @repo/database db:push
6
 
7
+ # On Hugging Face (DISABLE_WORKER=true), only run the API.
8
+ # The WhatsApp worker runs exclusively on Railway and calls graph.facebook.com from there.
9
+ if [ "${DISABLE_WORKER}" != "true" ]; then
10
+ echo "Starting WhatsApp Worker..."
11
+ if [ -f "apps/whatsapp-worker/dist/src/index.js" ]; then
12
+ node apps/whatsapp-worker/dist/src/index.js &
13
+ elif [ -f "apps/whatsapp-worker/dist/index.js" ]; then
14
+ node apps/whatsapp-worker/dist/index.js &
15
+ else
16
+ echo "WARNING: WhatsApp Worker build output missing — worker not started."
17
+ fi
18
  else
19
+ echo "DISABLE_WORKER=true Skipping worker startup (HF inbound-only mode)."
20
  fi
21
 
22
  echo "Starting API..."
 
25
  elif [ -f "apps/api/dist/index.js" ]; then
26
  exec node apps/api/dist/index.js
27
  else
28
+ echo "ERROR: API build output missing!"
29
  exit 1
30
  fi