import { prisma } from "./prisma"; // Settings keys and their env var fallbacks const SETTINGS_KEYS = { // Postiz POSTIZ_URL: { envVar: "POSTIZ_URL", label: "Postiz URL", placeholder: "http://localhost:5000" }, POSTIZ_API_KEY: { envVar: "POSTIZ_API_KEY", label: "Postiz API Key", placeholder: "your-postiz-api-key", secret: true }, // Tavily (Research) TAVILY_API_KEY: { envVar: "TAVILY_API_KEY", label: "Tavily API Key", placeholder: "tvly-...", secret: true }, // Google Gemini (NanoBanana image generation) GEMINI_API_KEY: { envVar: "GEMINI_API_KEY", label: "Google Gemini API Key", placeholder: "AIza...", secret: true }, // Nextdoor NEXTDOOR_API_TOKEN: { envVar: "NEXTDOOR_API_TOKEN", label: "Nextdoor API Token", placeholder: "your-nextdoor-token", secret: true }, NEXTDOOR_ADVERTISER_ID: { envVar: "NEXTDOOR_ADVERTISER_ID", label: "Nextdoor Advertiser ID", placeholder: "your-advertiser-id" }, } as const; export type SettingKey = keyof typeof SETTINGS_KEYS; export const SETTINGS_CONFIG = SETTINGS_KEYS; // Grouped for UI export const SETTINGS_GROUPS = [ { name: "Postiz", description: "Self-hosted social media scheduling. Handles Instagram and TikTok publishing.", docsUrl: "https://postiz.com", keys: ["POSTIZ_URL", "POSTIZ_API_KEY"] as SettingKey[], }, { name: "Tavily", description: "AI-powered web research. Used by the Trend Scout and Research agents. Free tier: 1,000 searches/month.", docsUrl: "https://tavily.com", keys: ["TAVILY_API_KEY"] as SettingKey[], }, { name: "Gemini", description: "Google Gemini powers NanoBanana MCP for AI image generation in static ads. ~$0.04-0.13/image.", docsUrl: "https://aistudio.google.com/apikey", keys: ["GEMINI_API_KEY"] as SettingKey[], }, { name: "Nextdoor", description: "Direct Nextdoor Ads API integration for local advertising.", docsUrl: "https://developer.nextdoor.com", keys: ["NEXTDOOR_API_TOKEN", "NEXTDOOR_ADVERTISER_ID"] as SettingKey[], }, ]; /** * Get a setting value. Checks DB first, falls back to env var. */ export async function getSetting(key: SettingKey): Promise { try { const setting = await prisma.setting.findUnique({ where: { key } }); if (setting?.value) return setting.value; } catch { // DB not available, fall through to env } const config = SETTINGS_KEYS[key]; return process.env[config.envVar] || ""; } /** * Get all settings as a map. */ export async function getAllSettings(): Promise> { const result: Record = {}; for (const [key, config] of Object.entries(SETTINGS_KEYS)) { try { const setting = await prisma.setting.findUnique({ where: { key } }); result[key] = setting?.value || process.env[config.envVar] || ""; } catch { result[key] = process.env[config.envVar] || ""; } } return result; } /** * Save a setting to the database. */ export async function saveSetting(key: SettingKey, value: string) { await prisma.setting.upsert({ where: { key }, update: { value }, create: { key, value }, }); } /** * Check connectivity status for each integration. */ export async function checkIntegrationStatus(): Promise> { const settings = await getAllSettings(); const status: Record = {}; // Postiz if (settings.POSTIZ_URL && settings.POSTIZ_API_KEY) { try { const res = await fetch(`${settings.POSTIZ_URL}/public/v1/integrations`, { headers: { Authorization: `Bearer ${settings.POSTIZ_API_KEY}` }, signal: AbortSignal.timeout(5000), }); status.postiz = { connected: res.ok }; if (!res.ok) status.postiz.error = `HTTP ${res.status}`; } catch (e) { status.postiz = { connected: false, error: e instanceof Error ? e.message : "Connection failed" }; } } else { status.postiz = { connected: false, error: "Not configured" }; } // Tavily if (settings.TAVILY_API_KEY) { status.tavily = { connected: true }; // No ping endpoint, just check if key exists } else { status.tavily = { connected: false, error: "Not configured" }; } // Gemini if (settings.GEMINI_API_KEY) { status.gemini = { connected: true }; } else { status.gemini = { connected: false, error: "Not configured" }; } // Nextdoor if (settings.NEXTDOOR_API_TOKEN && settings.NEXTDOOR_ADVERTISER_ID) { status.nextdoor = { connected: true }; } else { status.nextdoor = { connected: false, error: "Not configured" }; } return status; }