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

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 link
  • POST /api/v1/auth/login — request magic link
  • GET /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 settings
  • GET/POST/DELETE /api/v1/sources — sources CRUD + bulk/CSV import/export
  • GET/DELETE /api/v1/syntheses/:id — syntheses CRUD
  • POST /api/v1/syntheses/generate — trigger async generation
  • GET /api/v1/syntheses/generate/:job_id/progress — SSE progress stream
  • POST /api/v1/syntheses/:id/send-email — email synthesis
  • GET /api/v1/syntheses/:id/export/markdown — Markdown download
  • GET /api/v1/syntheses/:id/export/pdf — PDF download
  • GET/POST/DELETE /api/v1/user/api-keys — LLM API key management
  • GET /api/v1/config/providers — available providers/models

Admin Only

  • GET/POST/PUT/DELETE /api/v1/admin/providers — provider/model config
  • GET/PUT /api/v1/admin/rate-limits — rate limit config
  • GET /api/v1/admin/users — user list
  • PUT /api/v1/admin/users/:id/role — role management

Database (17 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 string
  • MASTER_ENCRYPTION_KEY — 64 hex chars for AES-256-GCM
  • SESSION_SECRET — at least 64 chars
  • RESEND_API_KEY — for email sending
  • TURNSTILE_SECRET_KEY / TURNSTILE_SITE_KEY — captcha
  • APP_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