Files
log/oauth/src/config/app.ts
2025-06-17 17:43:03 +09:00

158 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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