158 lines
4.7 KiB
TypeScript
158 lines
4.7 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 adminHandle = import.meta.env.VITE_ADMIN_HANDLE || 'ai.syui.ai';
|
||
const aiHandle = import.meta.env.VITE_AI_HANDLE || 'ai.syui.ai';
|
||
|
||
// DIDsはハンドルから実行時に解決される(フォールバック用のみ保持)
|
||
const adminDid = import.meta.env.VITE_ADMIN_DID || 'did:plc:uqzpqmrjnptsxezjx4xuh2mn';
|
||
const aiDid = import.meta.env.VITE_AI_DID || 'did:plc:6qyecktefllvenje24fcxnie';
|
||
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 || 'gemma3:4b';
|
||
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(); |