# 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/" } ``` 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.