Added: - Groq AI provider (free, fast with llama-3.3-70b-versatile) - Timezone setting (22 timezones) - Journal context: include previous journals (3/7/14/30 days) - Test connection button for AI providers - Per-provider settings (API key, model, base URL remembered) - Detailed task logging (full prompts and responses) - Tasks page with expandable details - Progress modal with steps and AI output details Fixed: - Groq API endpoint (https://api.groq.com/openai/v1/chat/completions) - Ollama baseUrl leaking to other providers - Database schema references - Proper Prisma migrations (data-safe) Changed: - Default AI: OpenAI → Groq - Project renamed: TotalRecall → DearDiary - Strict anti-hallucination prompt - Docker uses prisma migrate deploy (non-destructive)
101 lines
3.1 KiB
TypeScript
101 lines
3.1 KiB
TypeScript
import { Hono } from 'hono';
|
|
import { HonoEnv } from '../lib/types';
|
|
|
|
export const settingsRoutes = new Hono<HonoEnv>();
|
|
|
|
settingsRoutes.get('/', async (c) => {
|
|
const userId = c.get('userId');
|
|
const prisma = c.get('prisma');
|
|
|
|
const settings = await prisma.settings.findUnique({
|
|
where: { userId },
|
|
});
|
|
|
|
if (!settings) {
|
|
const newSettings = await prisma.settings.create({
|
|
data: { userId },
|
|
});
|
|
return c.json({ data: newSettings, error: null });
|
|
}
|
|
|
|
return c.json({ data: settings, error: null });
|
|
});
|
|
|
|
settingsRoutes.put('/', async (c) => {
|
|
const userId = c.get('userId');
|
|
const prisma = c.get('prisma');
|
|
|
|
const body = await c.req.json();
|
|
const { aiProvider, aiApiKey, aiModel, aiBaseUrl, journalPrompt, language } = body;
|
|
|
|
const data: Record<string, unknown> = {};
|
|
if (aiProvider !== undefined) data.aiProvider = aiProvider;
|
|
if (aiApiKey !== undefined) data.aiApiKey = aiApiKey;
|
|
if (aiModel !== undefined) data.aiModel = aiModel;
|
|
if (aiBaseUrl !== undefined) data.aiBaseUrl = aiBaseUrl;
|
|
if (journalPrompt !== undefined) data.journalPrompt = journalPrompt;
|
|
if (language !== undefined) data.language = language;
|
|
|
|
const settings = await prisma.settings.upsert({
|
|
where: { userId },
|
|
create: { userId, ...data },
|
|
update: data,
|
|
});
|
|
|
|
return c.json({ data: settings, error: null });
|
|
});
|
|
|
|
settingsRoutes.post('/validate-key', async (c) => {
|
|
const body = await c.req.json();
|
|
const { provider, apiKey, baseUrl } = body;
|
|
|
|
if (!provider || !apiKey) {
|
|
return c.json({ data: null, error: { code: 'VALIDATION_ERROR', message: 'provider and apiKey are required' } }, 400);
|
|
}
|
|
|
|
const { createAIProvider } = await import('../services/ai/provider');
|
|
|
|
try {
|
|
const aiProvider = createAIProvider({
|
|
provider,
|
|
apiKey,
|
|
baseUrl,
|
|
});
|
|
const valid = await aiProvider.validate?.();
|
|
return c.json({ data: { valid: true }, error: null });
|
|
} catch {
|
|
return c.json({ data: { valid: false }, error: null });
|
|
}
|
|
});
|
|
|
|
settingsRoutes.post('/test', async (c) => {
|
|
const body = await c.req.json();
|
|
const { provider, apiKey, model, baseUrl } = body;
|
|
|
|
if (!provider) {
|
|
return c.json({ data: null, error: { code: 'VALIDATION_ERROR', message: 'provider is required' } }, 400);
|
|
}
|
|
|
|
const { createAIProvider } = await import('../services/ai/provider');
|
|
|
|
try {
|
|
const aiProvider = createAIProvider({
|
|
provider,
|
|
apiKey: apiKey || '',
|
|
model: model || undefined,
|
|
baseUrl: baseUrl || undefined,
|
|
});
|
|
|
|
const result = await aiProvider.generate('Say "OK" if you can read this.', 'You are a test assistant. Respond with just "OK".');
|
|
|
|
if (result.toLowerCase().includes('ok')) {
|
|
return c.json({ data: { valid: true, message: 'Connection successful!' }, error: null });
|
|
} else {
|
|
return c.json({ data: { valid: false }, error: { code: 'TEST_FAILED', message: 'Model responded but with unexpected output' } });
|
|
}
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : 'Connection failed';
|
|
return c.json({ data: { valid: false }, error: { code: 'TEST_FAILED', message } });
|
|
}
|
|
});
|