Files
log/oauth/src/config/app.ts
2025-06-16 20:45:55 +09:00

156 lines
4.6 KiB
TypeScript

// 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();