feat: brain service — self-contained second brain knowledge manager
Full backend service with: - FastAPI REST API with CRUD, search, reprocess endpoints - PostgreSQL + pgvector for items and semantic search - Redis + RQ for background job processing - Meilisearch for fast keyword/filter search - Browserless/Chrome for JS rendering and screenshots - OpenAI structured output for AI classification - Local file storage with S3-ready abstraction - Gateway auth via X-Gateway-User-Id header - Own docker-compose stack (6 containers) Classification: fixed folders (Home/Family/Work/Travel/Knowledge/Faith/Projects) and fixed tags (28 predefined). AI assigns exactly 1 folder, 2-3 tags, title, summary, and confidence score per item. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,17 +2,35 @@ import type { Handle } from '@sveltejs/kit';
|
||||
import { env } from '$env/dynamic/private';
|
||||
|
||||
const gatewayUrl = env.GATEWAY_URL || 'http://localhost:8100';
|
||||
const devAutoLogin = ['1', 'true', 'yes', 'on'].includes((env.DEV_AUTO_LOGIN || '').toLowerCase());
|
||||
const immichUrl = env.IMMICH_URL || '';
|
||||
const immichApiKey = env.IMMICH_API_KEY || '';
|
||||
const karakeepUrl = env.KARAKEEP_URL || '';
|
||||
const karakeepApiKey = env.KARAKEEP_API_KEY || '';
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
function shouldUseDevAutoLogin(): boolean {
|
||||
if (!devAutoLogin) return false;
|
||||
const host = event.url.host.toLowerCase();
|
||||
return host.includes(':4174') || host.startsWith('test.');
|
||||
}
|
||||
|
||||
function normalizeSetCookieForHttp(value: string): string {
|
||||
// The gateway issues Secure cookies by default. On local HTTP dev hosts
|
||||
// like 192.168.x.x or localhost, browsers drop those cookies entirely.
|
||||
// Relax only at the frontend proxy boundary when the current app URL is HTTP.
|
||||
if (event.url.protocol !== 'http:') return value;
|
||||
return value.replace(/;\s*Secure/gi, '');
|
||||
}
|
||||
|
||||
async function isAuthenticated(request: Request): Promise<boolean> {
|
||||
const cookie = request.headers.get('cookie') || '';
|
||||
if (!cookie.includes('platform_session=')) return false;
|
||||
if (!cookie.includes('platform_session=') && !shouldUseDevAutoLogin()) return false;
|
||||
try {
|
||||
const res = await fetch(`${gatewayUrl}/api/auth/me`, { headers: { cookie } });
|
||||
const headers: Record<string, string> = {};
|
||||
if (cookie) headers.cookie = cookie;
|
||||
if (shouldUseDevAutoLogin()) headers['X-Dev-Auto-Login'] = '1';
|
||||
const res = await fetch(`${gatewayUrl}/api/auth/me`, { headers });
|
||||
if (!res.ok) return false;
|
||||
const data = await res.json();
|
||||
return data.authenticated === true;
|
||||
@@ -187,6 +205,9 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
headers.set(key, value);
|
||||
}
|
||||
}
|
||||
if (shouldUseDevAutoLogin()) {
|
||||
headers.set('X-Dev-Auto-Login', '1');
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(targetUrl, {
|
||||
@@ -200,7 +221,11 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
// Forward set-cookie headers from gateway
|
||||
const responseHeaders = new Headers();
|
||||
for (const [key, value] of response.headers.entries()) {
|
||||
responseHeaders.append(key, value);
|
||||
if (key.toLowerCase() === 'set-cookie') {
|
||||
responseHeaders.append(key, normalizeSetCookieForHttp(value));
|
||||
} else {
|
||||
responseHeaders.append(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(response.body, {
|
||||
|
||||
Reference in New Issue
Block a user