import { prisma } from '@/lib/db'; import { encrypt, decrypt, maskValue } from './encryption'; import { SystemSettings } from '@/types/admin'; // Keys that should be encrypted const ENCRYPTED_KEYS = [ 'ghlClientSecret', 'ghlAccessToken', 'ghlRefreshToken', 'ghlAgencyApiKey', 'ghlPrivateToken', 'ghlWebhookSecret', 'stripeSecretKey', 'stripeWebhookSecret', 'clickupApiKey', 'claudeApiKey', 'openaiApiKey', 'mcpServerUrl', ]; export class SettingsService { // Get a single setting async get(key: string): Promise { const setting = await prisma.systemSettings.findUnique({ where: { key }, }); if (!setting) return null; return setting.isEncrypted ? decrypt(setting.value) : setting.value; } // Get all settings async getAll(): Promise { const settings = await prisma.systemSettings.findMany(); const result: Record = {}; for (const setting of settings) { result[setting.key] = setting.isEncrypted ? decrypt(setting.value) : setting.value; } return result as SystemSettings; } // Get all settings with sensitive values masked (for display) async getAllMasked(): Promise> { const settings = await prisma.systemSettings.findMany(); const result: Record = {}; for (const setting of settings) { if (setting.isEncrypted) { const decrypted = decrypt(setting.value); result[setting.key] = maskValue(decrypted); } else { result[setting.key] = setting.value; } } return result; } // Set a single setting async set(key: string, value: string, updatedBy?: string): Promise { const isEncrypted = ENCRYPTED_KEYS.includes(key); const storedValue = isEncrypted ? encrypt(value) : value; await prisma.systemSettings.upsert({ where: { key }, update: { value: storedValue, isEncrypted, updatedBy, updatedAt: new Date(), }, create: { key, value: storedValue, isEncrypted, updatedBy, }, }); } // Set multiple settings at once async setMany( settings: Partial, updatedBy?: string ): Promise { const operations = Object.entries(settings).map(([key, value]) => { if (value === undefined || value === null) return null; const isEncrypted = ENCRYPTED_KEYS.includes(key); const storedValue = isEncrypted ? encrypt(String(value)) : String(value); return prisma.systemSettings.upsert({ where: { key }, update: { value: storedValue, isEncrypted, updatedBy, updatedAt: new Date(), }, create: { key, value: storedValue, isEncrypted, updatedBy, }, }); }).filter(Boolean); await prisma.$transaction(operations as any); } // Delete a setting async delete(key: string): Promise { await prisma.systemSettings.delete({ where: { key }, }); } // Test GHL connection with current settings async testGHLConnection(): Promise<{ success: boolean; error?: string; details?: any }> { try { const accessToken = await this.get('ghlAccessToken'); const locationId = await this.get('ghlLocationId'); if (!accessToken) { return { success: false, error: 'Missing GHL Access Token. Please configure OAuth credentials.' }; } if (!locationId) { return { success: false, error: 'Missing GHL Location ID.' }; } // Test v2 API connection by fetching location details const response = await fetch( `https://services.leadconnectorhq.com/locations/${locationId}`, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Version': '2021-07-28', 'Accept': 'application/json', }, } ); if (!response.ok) { const errorText = await response.text(); return { success: false, error: `GHL API returned ${response.status}: ${response.statusText}`, details: errorText }; } const data = await response.json(); return { success: true, details: { locationName: data.location?.name || data.name || 'Connected' } }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } // Test Stripe connection async testStripeConnection(): Promise<{ success: boolean; error?: string }> { try { const secretKey = await this.get('stripeSecretKey'); if (!secretKey) { return { success: false, error: 'Missing Stripe secret key' }; } // We'll implement actual Stripe test later // For now, just check the key format if (!secretKey.startsWith('sk_')) { return { success: false, error: 'Invalid Stripe key format' }; } return { success: true }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } } // Singleton instance export const settingsService = new SettingsService();