diff --git a/app/api/settings/route.ts b/app/api/settings/route.ts index 08f1aaa..2f6e6d0 100644 --- a/app/api/settings/route.ts +++ b/app/api/settings/route.ts @@ -3,12 +3,7 @@ export const dynamic = 'force-dynamic' import { NextRequest, NextResponse } from 'next/server' import { createClient } from '@/lib/supabase/server' -const ALLOWED_KEYS = [ - 'ANTHROPIC_API_KEY', - 'VOYAGE_API_KEY', - 'META_WHATSAPP_PHONE_NUMBER_ID', - 'META_WHATSAPP_ACCESS_TOKEN', -] as const +const ALLOWED_KEYS = ['ANTHROPIC_API_KEY', 'VOYAGE_API_KEY'] as const type SettingKey = typeof ALLOWED_KEYS[number] async function requireAdmin(supabase: Awaited>) { diff --git a/lib/notifications/whatsapp.ts b/lib/notifications/whatsapp.ts deleted file mode 100644 index fb6a6bb..0000000 --- a/lib/notifications/whatsapp.ts +++ /dev/null @@ -1,42 +0,0 @@ -export async function sendWhatsAppMessage( - phoneNumber: string, - templateName: string, - parameters: string[], - phoneNumberId: string, - accessToken: string, -): Promise { - const sanitized = phoneNumber.replace(/[^0-9]/g, '') - if (!sanitized) return - - const body = { - messaging_product: 'whatsapp', - to: sanitized, - type: 'template', - template: { - name: templateName, - language: { code: 'en_US' }, - components: [ - { - type: 'body', - parameters: parameters.map(text => ({ type: 'text', text })), - }, - ], - }, - } - - const res = await fetch( - `https://graph.facebook.com/v19.0/${phoneNumberId}/messages`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${accessToken}`, - }, - body: JSON.stringify(body), - } - ) - - if (!res.ok) { - throw new Error(`WhatsApp API error: ${res.status}`) - } -} diff --git a/supabase/migrations/20260711000015_phase4.sql b/supabase/migrations/20260711000015_phase4.sql deleted file mode 100644 index 48760ac..0000000 --- a/supabase/migrations/20260711000015_phase4.sql +++ /dev/null @@ -1,10 +0,0 @@ --- Add effectiveness recheck round tracking to capa_actions -ALTER TABLE capa_actions - ADD COLUMN IF NOT EXISTS effectiveness_recheck_round INT NOT NULL DEFAULT 0; - --- WhatsApp and effectiveness recheck credentials for Settings UI -INSERT INTO app_settings (key, value, updated_at, updated_by) -VALUES - ('META_WHATSAPP_PHONE_NUMBER_ID', '', now(), NULL), - ('META_WHATSAPP_ACCESS_TOKEN', '', now(), NULL) -ON CONFLICT (key) DO NOTHING; diff --git a/tests/lib/notifications/whatsapp.test.ts b/tests/lib/notifications/whatsapp.test.ts deleted file mode 100644 index 58977e4..0000000 --- a/tests/lib/notifications/whatsapp.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest' -import { sendWhatsAppMessage } from '@/lib/notifications/whatsapp' - -const fetchMock = vi.fn() -vi.stubGlobal('fetch', fetchMock) - -describe('sendWhatsAppMessage', () => { - beforeEach(() => fetchMock.mockClear()) - - it('posts to Meta Graph API with correct template structure', async () => { - fetchMock.mockResolvedValueOnce( - new Response('{"messages":[{"id":"wamid.abc"}]}', { status: 200 }) - ) - await sendWhatsAppMessage( - '60123456789', - 'ims_incident_alert', - ['SETIA-202407-0001', 'injury', 'Warehouse A'], - 'test-phone-id', - 'test-token' - ) - expect(fetchMock).toHaveBeenCalledOnce() - const [url, opts] = fetchMock.mock.calls[0] - expect(url).toBe('https://graph.facebook.com/v19.0/test-phone-id/messages') - expect(opts.method).toBe('POST') - expect(opts.headers['Authorization']).toBe('Bearer test-token') - const body = JSON.parse(opts.body) - expect(body.messaging_product).toBe('whatsapp') - expect(body.to).toBe('60123456789') - expect(body.type).toBe('template') - expect(body.template.name).toBe('ims_incident_alert') - expect(body.template.language.code).toBe('en_US') - expect(body.template.components[0].parameters).toHaveLength(3) - expect(body.template.components[0].parameters[0]).toEqual({ type: 'text', text: 'SETIA-202407-0001' }) - }) - - it('throws on non-2xx response', async () => { - fetchMock.mockResolvedValueOnce( - new Response('{"error":{"message":"Invalid token"}}', { status: 400 }) - ) - await expect( - sendWhatsAppMessage('60123456789', 'ims_incident_alert', ['a'], 'pid', 'tok') - ).rejects.toThrow('WhatsApp API error: 400') - }) - - it('returns immediately without calling fetch when phoneNumber is empty', async () => { - await sendWhatsAppMessage('', 'ims_incident_alert', ['a'], 'pid', 'tok') - expect(fetchMock).not.toHaveBeenCalled() - }) - - it('sanitizes phone number — strips spaces, dashes, and plus sign', async () => { - fetchMock.mockResolvedValueOnce( - new Response('{"messages":[{"id":"x"}]}', { status: 200 }) - ) - await sendWhatsAppMessage('+60 12-345 6789', 'ims_incident_alert', ['a'], 'pid', 'tok') - const body = JSON.parse(fetchMock.mock.calls[0][1].body) - expect(body.to).toBe('60123456789') - }) -})