Initial commit: MOPC platform with Docker deployment setup

Full Next.js 15 platform with tRPC, Prisma, PostgreSQL, NextAuth.
Includes production Dockerfile (multi-stage, port 7600), docker-compose
with registry-based image pull, Gitea Actions CI workflow, nginx config
for portal.monaco-opc.com, deployment scripts, and DEPLOYMENT.md guide.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-30 13:41:32 +01:00
commit a606292aaa
290 changed files with 70691 additions and 0 deletions

151
src/lib/whatsapp/index.ts Normal file
View File

@@ -0,0 +1,151 @@
/**
* WhatsApp Provider Abstraction Layer
*
* Supports multiple WhatsApp providers with a common interface:
* - Meta WhatsApp Business Cloud API
* - Twilio WhatsApp
*/
import { prisma } from '@/lib/prisma'
import { MetaWhatsAppProvider } from './meta-provider'
import { TwilioWhatsAppProvider } from './twilio-provider'
export interface WhatsAppResult {
success: boolean
messageId?: string
error?: string
}
export interface WhatsAppProvider {
sendText(to: string, body: string): Promise<WhatsAppResult>
sendTemplate(
to: string,
template: string,
params: Record<string, string>
): Promise<WhatsAppResult>
testConnection(): Promise<{ success: boolean; error?: string }>
}
export type WhatsAppProviderType = 'META' | 'TWILIO'
/**
* Get the configured WhatsApp provider
* Returns null if WhatsApp is not enabled or not configured
*/
export async function getWhatsAppProvider(): Promise<WhatsAppProvider | null> {
try {
// Check if WhatsApp is enabled
const enabledSetting = await prisma.systemSettings.findUnique({
where: { key: 'whatsapp_enabled' },
})
if (enabledSetting?.value !== 'true') {
return null
}
// Get provider type
const providerSetting = await prisma.systemSettings.findUnique({
where: { key: 'whatsapp_provider' },
})
const providerType = (providerSetting?.value || 'META') as WhatsAppProviderType
if (providerType === 'META') {
return await createMetaProvider()
} else if (providerType === 'TWILIO') {
return await createTwilioProvider()
}
return null
} catch (error) {
console.error('Failed to get WhatsApp provider:', error)
return null
}
}
/**
* Create Meta WhatsApp provider from settings
*/
async function createMetaProvider(): Promise<WhatsAppProvider | null> {
const [phoneNumberIdSetting, accessTokenSetting] = await Promise.all([
prisma.systemSettings.findUnique({
where: { key: 'whatsapp_meta_phone_number_id' },
}),
prisma.systemSettings.findUnique({
where: { key: 'whatsapp_meta_access_token' },
}),
])
if (!phoneNumberIdSetting?.value || !accessTokenSetting?.value) {
console.warn('Meta WhatsApp not fully configured')
return null
}
return new MetaWhatsAppProvider(
phoneNumberIdSetting.value,
accessTokenSetting.value
)
}
/**
* Create Twilio WhatsApp provider from settings
*/
async function createTwilioProvider(): Promise<WhatsAppProvider | null> {
const [accountSidSetting, authTokenSetting, phoneNumberSetting] = await Promise.all([
prisma.systemSettings.findUnique({
where: { key: 'whatsapp_twilio_account_sid' },
}),
prisma.systemSettings.findUnique({
where: { key: 'whatsapp_twilio_auth_token' },
}),
prisma.systemSettings.findUnique({
where: { key: 'whatsapp_twilio_phone_number' },
}),
])
if (
!accountSidSetting?.value ||
!authTokenSetting?.value ||
!phoneNumberSetting?.value
) {
console.warn('Twilio WhatsApp not fully configured')
return null
}
return new TwilioWhatsAppProvider(
accountSidSetting.value,
authTokenSetting.value,
phoneNumberSetting.value
)
}
/**
* Check if WhatsApp is configured and available
*/
export async function isWhatsAppEnabled(): Promise<boolean> {
const provider = await getWhatsAppProvider()
return provider !== null
}
/**
* Get the current provider type
*/
export async function getWhatsAppProviderType(): Promise<WhatsAppProviderType | null> {
try {
const enabledSetting = await prisma.systemSettings.findUnique({
where: { key: 'whatsapp_enabled' },
})
if (enabledSetting?.value !== 'true') {
return null
}
const providerSetting = await prisma.systemSettings.findUnique({
where: { key: 'whatsapp_provider' },
})
return (providerSetting?.value || 'META') as WhatsAppProviderType
} catch {
return null
}
}