feat(db): phase 4 group 1 — lib/ settings + notifications to Drizzle
Convert lib/settings.ts, lib/notifications/{in-app,email,capa-escalation,
effectiveness-recheck}.ts from Supabase PostgREST to Drizzle asAdmin queries.
Drop supabase arg from all call sites in app/api/ and cron routes. Rewrite
notification unit tests to mock @/lib/db/with-user instead of SupabaseClient.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import 'server-only'
|
||||
import { Resend } from 'resend'
|
||||
import type { SupabaseClient } from '@supabase/supabase-js'
|
||||
import { asAdmin } from '@/lib/db/with-user'
|
||||
import { capaActions, incidents, users } from '@/lib/db/schema'
|
||||
import { and, eq, inArray, isNotNull, lt } from 'drizzle-orm'
|
||||
import { sendWhatsAppMessage } from '@/lib/notifications/whatsapp'
|
||||
import { getApiKey } from '@/lib/settings'
|
||||
|
||||
@@ -27,22 +30,31 @@ export function shouldSendRecheck(
|
||||
return recheckDate <= today
|
||||
}
|
||||
|
||||
export async function sendEffectivenessRecheckNotifications(
|
||||
supabase: SupabaseClient,
|
||||
): Promise<{ notified: number }> {
|
||||
export async function sendEffectivenessRecheckNotifications(): Promise<{ notified: number }> {
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
|
||||
const { data: capas } = await supabase
|
||||
.from('capa_actions')
|
||||
.select(`
|
||||
id, description, effectiveness_recheck_date, effectiveness_recheck_round,
|
||||
verified_at, incident_id,
|
||||
incidents (reference_no),
|
||||
verifier:users!verified_by (email, name, phone)
|
||||
`)
|
||||
.in('status', ['verified', 'closed'])
|
||||
.not('effectiveness_recheck_date', 'is', null)
|
||||
.lt('effectiveness_recheck_round', 3)
|
||||
const capas = await asAdmin(db =>
|
||||
db.select({
|
||||
id: capaActions.id,
|
||||
description: capaActions.description,
|
||||
effectivenessRecheckDate: capaActions.effectivenessRecheckDate,
|
||||
effectivenessRecheckRound: capaActions.effectivenessRecheckRound,
|
||||
verifiedAt: capaActions.verifiedAt,
|
||||
incidentId: capaActions.incidentId,
|
||||
incidentRef: incidents.referenceNo,
|
||||
verifierEmail: users.email,
|
||||
verifierName: users.name,
|
||||
verifierPhone: users.phone,
|
||||
})
|
||||
.from(capaActions)
|
||||
.leftJoin(incidents, eq(capaActions.incidentId, incidents.id))
|
||||
.leftJoin(users, eq(capaActions.verifiedBy, users.id))
|
||||
.where(and(
|
||||
inArray(capaActions.status, ['verified', 'closed']),
|
||||
isNotNull(capaActions.effectivenessRecheckDate),
|
||||
lt(capaActions.effectivenessRecheckRound, 3),
|
||||
))
|
||||
)
|
||||
|
||||
if (!capas || capas.length === 0) return { notified: 0 }
|
||||
|
||||
@@ -53,8 +65,8 @@ export async function sendEffectivenessRecheckNotifications(
|
||||
let whatsappPhoneId: string | null = null
|
||||
let whatsappToken: string | null = null
|
||||
try {
|
||||
whatsappPhoneId = await getApiKey(supabase, 'META_WHATSAPP_PHONE_NUMBER_ID')
|
||||
whatsappToken = await getApiKey(supabase, 'META_WHATSAPP_ACCESS_TOKEN')
|
||||
whatsappPhoneId = await getApiKey('META_WHATSAPP_PHONE_NUMBER_ID')
|
||||
whatsappToken = await getApiKey('META_WHATSAPP_ACCESS_TOKEN')
|
||||
} catch {
|
||||
// WhatsApp not configured — email only
|
||||
}
|
||||
@@ -62,21 +74,20 @@ export async function sendEffectivenessRecheckNotifications(
|
||||
let notified = 0
|
||||
|
||||
for (const capa of capas) {
|
||||
const recheckDate = (capa as { effectiveness_recheck_date: string | null }).effectiveness_recheck_date
|
||||
const round = (capa as { effectiveness_recheck_round: number }).effectiveness_recheck_round
|
||||
const recheckDate = capa.effectivenessRecheckDate
|
||||
const round = capa.effectivenessRecheckRound
|
||||
|
||||
if (!shouldSendRecheck(recheckDate, round, today)) continue
|
||||
|
||||
const verifierEmail = (capa.verifier as unknown as { email: string } | null)?.email
|
||||
const verifierName = (capa.verifier as unknown as { name: string } | null)?.name ?? 'HSE Officer'
|
||||
const verifierPhone = (capa.verifier as unknown as { phone: string | null } | null)?.phone ?? ''
|
||||
const verifierEmail = capa.verifierEmail
|
||||
const verifierName = capa.verifierName ?? 'HSE Officer'
|
||||
const verifierPhone = capa.verifierPhone ?? ''
|
||||
if (!verifierEmail) continue
|
||||
|
||||
const incidentRef = (capa.incidents as unknown as { reference_no: string | null } | null)?.reference_no
|
||||
?? (capa as { incident_id: string }).incident_id
|
||||
const incidentRef = capa.incidentRef ?? capa.incidentId
|
||||
const roundLabel = getRoundLabel(round)
|
||||
const capaDesc = (capa as { description: string }).description
|
||||
const capaUrl = `${siteUrl}/ims/hse/capa/${(capa as { id: string }).id}`
|
||||
const capaDesc = capa.description
|
||||
const capaUrl = `${siteUrl}/ims/hse/capa/${capa.id}`
|
||||
|
||||
const subject = `[IMS] ${roundLabel} effectiveness check — ${incidentRef}`
|
||||
const html = `
|
||||
@@ -99,22 +110,22 @@ export async function sendEffectivenessRecheckNotifications(
|
||||
sendWhatsAppMessage(
|
||||
verifierPhone,
|
||||
'ims_effectiveness_recheck',
|
||||
[roundLabel, incidentRef, capaDesc],
|
||||
[roundLabel, incidentRef ?? '', capaDesc],
|
||||
whatsappPhoneId,
|
||||
whatsappToken,
|
||||
).catch(err => console.error('WhatsApp recheck error:', err))
|
||||
}
|
||||
|
||||
// Advance round
|
||||
const verifiedAt = (capa as { verified_at: string }).verified_at
|
||||
const nextDate = getNextRecheckDate(verifiedAt, round)
|
||||
await supabase
|
||||
.from('capa_actions')
|
||||
.update({
|
||||
effectiveness_recheck_round: round + 1,
|
||||
effectiveness_recheck_date: nextDate ?? null,
|
||||
})
|
||||
.eq('id', (capa as { id: string }).id)
|
||||
const nextDate = getNextRecheckDate(capa.verifiedAt!.toISOString(), round)
|
||||
await asAdmin(db =>
|
||||
db.update(capaActions)
|
||||
.set({
|
||||
effectivenessRecheckRound: round + 1,
|
||||
effectivenessRecheckDate: nextDate ?? null,
|
||||
})
|
||||
.where(eq(capaActions.id, capa.id))
|
||||
)
|
||||
|
||||
notified++
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user