Revert "feat: WhatsApp notification helper + phase 4 migration (recheck_round, WhatsApp settings)"
This reverts commit b6845f5bb5.
This commit is contained in:
@@ -3,12 +3,7 @@ export const dynamic = 'force-dynamic'
|
|||||||
import { NextRequest, NextResponse } from 'next/server'
|
import { NextRequest, NextResponse } from 'next/server'
|
||||||
import { createClient } from '@/lib/supabase/server'
|
import { createClient } from '@/lib/supabase/server'
|
||||||
|
|
||||||
const ALLOWED_KEYS = [
|
const ALLOWED_KEYS = ['ANTHROPIC_API_KEY', 'VOYAGE_API_KEY'] as const
|
||||||
'ANTHROPIC_API_KEY',
|
|
||||||
'VOYAGE_API_KEY',
|
|
||||||
'META_WHATSAPP_PHONE_NUMBER_ID',
|
|
||||||
'META_WHATSAPP_ACCESS_TOKEN',
|
|
||||||
] as const
|
|
||||||
type SettingKey = typeof ALLOWED_KEYS[number]
|
type SettingKey = typeof ALLOWED_KEYS[number]
|
||||||
|
|
||||||
async function requireAdmin(supabase: Awaited<ReturnType<typeof createClient>>) {
|
async function requireAdmin(supabase: Awaited<ReturnType<typeof createClient>>) {
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
export async function sendWhatsAppMessage(
|
|
||||||
phoneNumber: string,
|
|
||||||
templateName: string,
|
|
||||||
parameters: string[],
|
|
||||||
phoneNumberId: string,
|
|
||||||
accessToken: string,
|
|
||||||
): Promise<void> {
|
|
||||||
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}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
@@ -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')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Reference in New Issue
Block a user