You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.0 KiB
6.0 KiB
AI Weekly Synth
Overview
AI Weekly Synth is a self-hosted web application that generates AI-powered weekly news syntheses. Users configure their topics, categories, and preferred LLM provider, then the app searches the web, validates sources, and produces structured summaries.
Architecture
- Backend: Rust (Axum) —
backend/ - Frontend: SolidJS + Tailwind CSS v4 —
frontend/ - Database: PostgreSQL (via sqlx with runtime-checked queries)
- Deployment: Docker only (
docker-compose.yml)
Project Structure
ai_synth/
├── backend/ Rust/Axum backend
│ ├── src/
│ │ ├── main.rs Entry point, CLI (serve, create-admin)
│ │ ├── router.rs All API routes + middleware stack
│ │ ├── handlers/ HTTP handlers (auth, settings, sources, syntheses, admin, etc.)
│ │ ├── services/ Business logic (auth, email, encryption, scraper, LLM providers, synthesis pipeline)
│ │ ├── db/ Database queries (sqlx)
│ │ ├── models/ Data types + validation
│ │ ├── middleware/ Auth session extraction, CSRF check
│ │ └── util/ Token generation, hashing
│ ├── migrations/ SQL migrations (9 files)
│ ├── tests/ Integration tests (require Postgres)
│ ├── Cargo.toml
│ └── Dockerfile Multi-stage build
├── frontend/ SolidJS frontend
│ ├── src/
│ │ ├── App.tsx Router, layouts, route guards
│ │ ├── pages/ Login, Register, Home, Settings, Sources, GenerateSynthesis, SynthesisDetail, admin/*
│ │ ├── components/ Navbar, Layout, AdminLayout, Turnstile, ApiKeyManager, ui/*
│ │ ├── api/ API clients (auth, settings, sources, syntheses, admin, config, apiKeys)
│ │ ├── contexts/ AuthContext (session-based)
│ │ ├── i18n/ French translations (i18n-ready for future languages)
│ │ └── utils/ SSE client, date formatting, provider info
│ ├── package.json
│ └── vite.config.ts SolidJS + Tailwind + dev proxy
├── docs/ Analysis reports + implementation plans
├── docker-compose.yml App + Postgres
├── .env.example All required env vars documented
└── CLAUDE.md This file
Key Features
- Authentication: Email + magic link (passwordless), Cloudflare Turnstile captcha, 30-day session cookies
- LLM Providers: Google Gemini, OpenAI, Anthropic — users bring their own API keys
- Generation Pipeline: 2-pass (search with web grounding → scrape/validate URLs → rewrite summaries), adaptive per provider
- Admin Module: Provider/model curation, rate limit config, user management
- Security: AES-256-GCM encryption for API keys at rest, SSRF prevention in scraper, CSRF via X-Requested-With, HttpOnly/SameSite cookies
- Export: Email via Resend, PDF, Markdown
- Real-time: SSE for generation progress streaming
Running Locally
Docker (production-like)
cp .env.example .env # Fill in values
docker compose up
Development
# Backend (requires Postgres running)
cd backend && cargo run -- serve
# Frontend (proxies /api to backend)
cd frontend && npm install && npm run dev
CLI
# Create first admin user
cd backend && cargo run -- create-admin admin@example.com
Testing
# Backend unit tests (no Postgres needed)
cd backend && cargo test --lib
# Backend integration tests (requires Postgres)
TEST_DATABASE_URL=postgres://user:pass@localhost:5432/postgres cargo test
# Frontend unit tests
cd frontend && npx vitest run
# Frontend type check
cd frontend && npx tsc --noEmit
API Endpoints
Public
POST /api/v1/auth/register— create account + magic linkPOST /api/v1/auth/login— request magic linkGET /api/v1/auth/verify— verify token (email click)POST /api/v1/auth/verify— verify token (frontend API)GET /api/v1/health— health check
Authenticated
GET/PUT /api/v1/settings— user settingsGET/POST/DELETE /api/v1/sources— sources CRUD + bulk/CSV import/exportGET/DELETE /api/v1/syntheses/:id— syntheses CRUDPOST /api/v1/syntheses/generate— trigger async generationGET /api/v1/syntheses/generate/:job_id/progress— SSE progress streamPOST /api/v1/syntheses/:id/send-email— email synthesisGET /api/v1/syntheses/:id/export/markdown— Markdown downloadGET /api/v1/syntheses/:id/export/pdf— PDF downloadGET/POST/DELETE /api/v1/user/api-keys— LLM API key managementGET /api/v1/config/providers— available providers/models
Admin Only
GET/POST/PUT/DELETE /api/v1/admin/providers— provider/model configGET/PUT /api/v1/admin/rate-limits— rate limit configGET /api/v1/admin/users— user listPUT /api/v1/admin/users/:id/role— role management
Database (27 migrations)
Tables: users, sessions, magic_link_tokens, user_settings, sources, syntheses, admin_providers, admin_rate_limits, user_api_keys, audit_log
Environment Variables
See .env.example for the complete list. Key ones:
DATABASE_URL— Postgres connection stringMASTER_ENCRYPTION_KEY— 64 hex chars for AES-256-GCMSESSION_SECRET— at least 64 charsRESEND_API_KEY— for email sendingTURNSTILE_SECRET_KEY/TURNSTILE_SITE_KEY— captchaAPP_URL— public URL (for CORS, magic links, cookies)
Design Decisions
- Idiomatic Rust (learning project) — no unwrap() in production code
- Users bring their own LLM API keys (encrypted at rest)
- Admin curates available providers/models, users select from the list
- Single-tenant self-hosted (one instance per deployment)
- i18n-ready (French only for now, all strings in
frontend/src/i18n/fr.ts) - Adaptive generation pipeline: skips scrape+rewrite when native web grounding is sufficient