// Application configuration export interface AppConfig { adminDid: string; adminHandle: string; aiDid: string; aiHandle: string; aiDisplayName: string; aiAvatar: string; aiDescription: string; collections: { base: string; // Base collection like "ai.syui.log" }; host: string; rkey?: string; // Current post rkey if on post page aiEnabled: boolean; aiAskAi: boolean; aiProvider: string; aiModel: string; aiHost: string; aiSystemPrompt: string; allowedHandles: string[]; // Handles allowed for OAuth authentication atprotoPds: string; // Configured PDS for admin/ai handles // Legacy - prefer per-user PDS detection bskyPublicApi: string; atprotoApi: string; } // Collection name builders (similar to Rust implementation) export function getCollectionNames(base: string) { if (!base) { // Fallback to default base = 'ai.syui.log'; } const collections = { comment: base, user: `${base}.user`, chat: `${base}.chat`, chatLang: `${base}.chat.lang`, chatComment: `${base}.chat.comment`, }; return collections; } // Generate collection names from host // Format: ${reg}.${name}.${sub} // Example: log.syui.ai -> ai.syui.log function generateBaseCollectionFromHost(host: string): string { try { // Remove protocol if present const cleanHost = host.replace(/^https?:\/\//, ''); // Split host into parts const parts = cleanHost.split('.'); if (parts.length < 2) { throw new Error('Invalid host format'); } // Reverse the parts for collection naming // log.syui.ai -> ai.syui.log const reversedParts = parts.reverse(); const result = reversedParts.join('.'); return result; } catch (error) { // Fallback to default return 'ai.syui.log'; } } // Extract rkey from current URL // /posts/xxx -> xxx (remove .html if present) function extractRkeyFromUrl(): string | undefined { const pathname = window.location.pathname; const match = pathname.match(/\/posts\/([^/]+)\/?$/); if (match) { // Remove .html extension if present return match[1].replace(/\.html$/, ''); } return undefined; } // Get application configuration from environment variables export function getAppConfig(): AppConfig { const host = import.meta.env.VITE_APP_HOST || 'https://log.syui.ai'; const adminDid = import.meta.env.VITE_ADMIN_DID || 'did:plc:uqzpqmrjnptsxezjx4xuh2mn'; const adminHandle = import.meta.env.VITE_ADMIN_HANDLE || 'syui.ai'; const aiDid = import.meta.env.VITE_AI_DID || 'did:plc:4hqjfn7m6n5hno3doamuhgef'; const aiHandle = import.meta.env.VITE_AI_HANDLE || 'yui.syui.ai'; const aiDisplayName = import.meta.env.VITE_AI_DISPLAY_NAME || 'ai'; const aiAvatar = import.meta.env.VITE_AI_AVATAR || ''; const aiDescription = import.meta.env.VITE_AI_DESCRIPTION || ''; // Priority: Environment variables > Auto-generated from host const autoGeneratedBase = generateBaseCollectionFromHost(host); let baseCollection = import.meta.env.VITE_OAUTH_COLLECTION || autoGeneratedBase; // Ensure base collection is never undefined if (!baseCollection) { baseCollection = 'ai.syui.log'; } const collections = { base: baseCollection, }; const rkey = extractRkeyFromUrl(); // AI configuration const aiEnabled = import.meta.env.VITE_AI_ENABLED === 'true'; const aiAskAi = import.meta.env.VITE_AI_ASK_AI === 'true'; const aiProvider = import.meta.env.VITE_AI_PROVIDER || 'ollama'; const aiModel = import.meta.env.VITE_AI_MODEL || 'gemma2:2b'; const aiHost = import.meta.env.VITE_AI_HOST || 'https://ollama.syui.ai'; const aiSystemPrompt = import.meta.env.VITE_AI_SYSTEM_PROMPT || 'You are a helpful AI assistant trained on this blog\'s content.'; const atprotoPds = import.meta.env.VITE_ATPROTO_PDS || 'syu.is'; const bskyPublicApi = import.meta.env.VITE_BSKY_PUBLIC_API || 'https://public.api.bsky.app'; const atprotoApi = import.meta.env.VITE_ATPROTO_API || 'https://bsky.social'; // Parse allowed handles list const allowedHandlesStr = import.meta.env.VITE_ATPROTO_HANDLE_LIST || '[]'; let allowedHandles: string[] = []; try { allowedHandles = JSON.parse(allowedHandlesStr); } catch { // If parsing fails, allow all handles (empty array means no restriction) allowedHandles = []; } return { adminDid, adminHandle, aiDid, aiHandle, aiDisplayName, aiAvatar, aiDescription, collections, host, rkey, aiEnabled, aiAskAi, aiProvider, aiModel, aiHost, aiSystemPrompt, allowedHandles, atprotoPds, bskyPublicApi, atprotoApi }; } // Export singleton instance export const appConfig = getAppConfig();