Initial portal commit: landing + 9 AI-powered apps

Apps:
- dwg-rooms: extract room numbers from DWG/DXF
- dwg-counting: count symbols in PDF drawings (OpenCV template matching)
- contract-check: review PDF contracts against a checklist (Claude vision + Tesseract OCR fallback)
- email-drafter: bullet notes → polished Czech/English business emails
- invoice-extractor: PDF/image invoice → structured data → Excel
- translator: Czech-first translator across 19 languages with tone control
- vv-check: find inconsistent unit prices across VV sheets in one workbook
- vv-compare: diff original vs new VV files (changes / added / removed)
- feature-request: portal users submit ideas + sample files

Infrastructure:
- LiteLLM gateway with per-app virtual keys + budgets
- Langfuse observability
- Geist font, shared theme, cross-subdomain back link + theme sync via cookie/URL
- Caddy reverse proxy on *.klas.chat
This commit is contained in:
Ondřej Glaser
2026-05-13 15:25:04 +02:00
commit 48cef99257
139 changed files with 20171 additions and 0 deletions

78
README.md Normal file
View File

@@ -0,0 +1,78 @@
# AI Portal
Internal landing page that fronts a stack of AI tools (chat + workflow apps),
all routed through company API keys with per-user budgets and observability.
See `AI_PORTAL_HANDOFF.md` for full architecture and rationale.
## Phase 0 — what's running
- **Landing page** (Next.js 16 + Auth.js v5) — this repo, `landing/`
- **Open WebUI** — already running on host (`127.0.0.1:8080`)
- **Dify stack** — already running (`docker_default` network, nginx on `:443/:8090`)
- **LiteLLM** — already running on `localai` network (`:4000`)
- **Langfuse** — not yet (compose stub left in `docker-compose.yml`)
## Local dev
```bash
cd landing
cp ../.env.example ../.env # then edit secrets
pnpm install
pnpm dev # http://localhost:3000
```
Set at minimum:
- `AUTH_SECRET``openssl rand -base64 32`
- `DEV_PORTAL_PASSWORD` — any string; gates the dev login
## Production-style run (Docker)
From the `portal/` directory:
```bash
docker compose up -d --build
```
The landing service binds to `127.0.0.1:3010`. Caddy on the host fronts it at
`https://ai.klas.chat`.
## Auth
Two providers, both wired through Auth.js v5:
1. **Microsoft Entra ID** — production. Activated automatically when
`AUTH_MICROSOFT_ENTRA_ID_ID` and `AUTH_MICROSOFT_ENTRA_ID_SECRET` are set.
2. **Dev Credentials** — single shared password from `DEV_PORTAL_PASSWORD`.
Used only while Entra is not provisioned. Both can coexist; the login page
shows whichever is configured.
All routes except `/login` and `/api/auth/*` are gated by `proxy.ts`.
## Adding a new tile
Edit `landing/src/data/apps.json` and add an entry:
```json
{
"id": "my-app",
"title": "My App",
"description": "What it does in one line.",
"category": "Documents",
"icon": "FileText",
"accent": "blue",
"href": "https://dify.klas.chat/app/<id>"
}
```
Icon names come from [lucide-react](https://lucide.dev). Accents:
`violet`, `blue`, `cyan`, `emerald`, `amber`, `rose`. Use `"href": "#"` to
mark a tile as "Coming soon".
## What's next (post Phase 0)
1. Wire Open WebUI to use LiteLLM as its OpenAI-compatible endpoint.
2. Configure Dify model providers to point at LiteLLM (so spend is attributed).
3. Stand up Langfuse (uncomment in `docker-compose.yml`).
4. Map per-user identity → LiteLLM virtual keys.
5. Provision Microsoft Entra app registration; flip prod auth on.