# Platform Admin Dashboard Product-agnostic admin console for the ByteLyst platform. Connects **directly** to Azure Cosmos DB via the `@azure/cosmos` SDK — no intermediate API server. Set `PRODUCT_ID` in `.env.local` to deploy for any product (e.g. `lysnrai`, `chronomind`, `nomgap`, `mindlyst`). --- ## Tech Stack | Layer | Technology | | ------------- | ------------------------------------------------ | | **Framework** | Next.js 16 (App Router, React 19) | | **Styling** | TailwindCSS v4 + shadcn/ui (New York style) | | **Database** | Azure Cosmos DB (NoSQL, Serverless) — direct SDK | | **Auth** | JWT (jose) + bcrypt (bcryptjs), server-side only | | **Charts** | Recharts | | **Icons** | Lucide React | | **Language** | TypeScript 5 | ## Getting Started ```bash npm install cp .env.local.example .env.local # Fill in real Azure credentials npm run dev # http://localhost:3001 npm run check # TypeScript + ESLint npm run build # Production build ``` ### Seed the Database Creates all Cosmos containers + default admin/viewer users: ```bash curl -X POST "http://localhost:3001/api/seed?secret=" ``` ### Default Logins | Email | Password | Role | | -------------------- | ----------- | ----------- | | `admin@example.com` | `Admin123!` | Super Admin | | `viewer@example.com` | `viewer123` | Viewer | ## Environment Variables Copy `.env.local.example` → `.env.local`: | Variable | Required | Description | | ------------------------- | -------- | ---------------------------------------- | | `COSMOS_ENDPOINT` | Yes | Cosmos DB endpoint URL | | `COSMOS_KEY` | Yes | Cosmos DB primary key | | `COSMOS_DATABASE` | No | Database name (set per product) | | `COSMOS_REGION` | No | Region (default: `westus2`) | | `JWT_SECRET` | Yes | Shared JWT signing secret | | `SEED_SECRET` | Yes | Secret for POST /api/seed | | `AZURE_KEYVAULT_URL` | No | Azure Key Vault URL | | `AZURE_SPEECH_KEY` | No | Speech service key (for future features) | | `AZURE_SPEECH_REGION` | No | Speech region | | `AZURE_OPENAI_ENDPOINT` | No | OpenAI endpoint | | `AZURE_OPENAI_KEY` | No | OpenAI key | | `AZURE_OPENAI_DEPLOYMENT` | No | Model deployment name | ## Pages | Route | Description | | ---------------- | ---------------------------------------------------------------- | | `/` | Dashboard overview — KPIs, charts, recent activity | | `/users` | User management — search, filter, detail dialogs | | `/subscriptions` | Plan management — pricing, features, create plans | | `/tokens` | API token management — create, revoke, scopes | | `/usage` | Usage analytics — token/request charts, model costs | | `/audit` | Audit log — admin actions, security events | | `/settings` | Platform config — kill switch, Azure, rate limits, notifications | | `/login` | Authentication page | ## API Routes All routes are in `src/app/api/`: | Endpoint | Method | Description | | --------------------------- | -------------- | ------------------------------------------ | | `/api/auth/login` | POST | Authenticate user (email + password → JWT) | | `/api/auth/me` | GET | Get current user from Bearer token | | `/api/users` | GET/POST | List/create users | | `/api/users/[id]` | GET/PUT/DELETE | User CRUD by ID | | `/api/tokens` | GET/POST | List/create API tokens | | `/api/usage` | GET | Usage analytics | | `/api/audit` | GET | Audit log entries | | `/api/dashboard/stats` | GET | Dashboard KPI statistics | | `/api/settings/kill-switch` | GET/PUT | Platform kill switch (read/toggle) | | `/api/seed` | POST | Initialize containers + seed default users | ## Architecture ``` src/ ├── app/ │ ├── (dashboard)/ # Protected routes (sidebar + auth guard) │ │ ├── layout.tsx # Dashboard shell (sidebar, error boundary) │ │ ├── loading.tsx # Skeleton loading state │ │ ├── page.tsx # Dashboard overview │ │ ├── users/page.tsx │ │ ├── subscriptions/page.tsx │ │ ├── tokens/page.tsx │ │ ├── usage/page.tsx │ │ ├── audit/page.tsx │ │ └── settings/page.tsx # Includes kill switch toggle │ ├── api/ # Next.js API routes → direct Cosmos DB │ │ ├── auth/login/route.ts │ │ ├── auth/me/route.ts │ │ ├── users/route.ts │ │ ├── users/[id]/route.ts │ │ ├── tokens/route.ts │ │ ├── usage/route.ts │ │ ├── audit/route.ts │ │ ├── dashboard/stats/route.ts │ │ ├── settings/kill-switch/route.ts │ │ └── seed/route.ts │ ├── login/page.tsx # Public login page │ ├── layout.tsx # Root layout (providers) │ └── providers.tsx # Auth + Theme providers ├── components/ │ ├── ui/ # 16 shadcn/ui primitives (avatar, badge, button, card, etc.) │ ├── sidebar-nav.tsx # Responsive sidebar (mobile hamburger) │ ├── auth-guard.tsx # Route protection → redirect to /login │ └── error-boundary.tsx # Catch page crashes gracefully └── lib/ ├── cosmos.ts # Cosmos DB client singleton + container registry ├── auth-server.ts # JWT create/verify + bcrypt (server-side only) ├── auth-context.tsx # Client auth provider (localStorage tokens) ├── theme-context.tsx # Dark/light/system mode ├── api.ts # Client-side API helper functions ├── mock-data.ts # Mock data + types (fallback when Cosmos unavailable) ├── utils.ts # cn() tailwind merge helper └── repositories/ # Direct Cosmos DB CRUD ├── users.ts # getUserById, getUserByEmail, createUser, updateUser ├── tokens.ts # API token CRUD ├── audit.ts # Audit log read/write └── usage.ts # Usage statistics ``` ## Cosmos DB Integration The dashboard connects **directly** to Cosmos DB — no intermediate backend server. **Pattern:** ``` Page Component → fetch("/api/...") → API Route → Repository → Cosmos SDK → Azure ``` **Key files:** - `lib/cosmos.ts` — singleton `CosmosClient`, `getContainer(name)`, `initializeAllContainers()` - `lib/repositories/*.ts` — CRUD functions per container - `lib/auth-server.ts` — `authenticateUser()`, `createAccessToken()`, `verifyToken()` **Container definitions** (in `cosmos.ts`): | Container | Partition Key | TTL | | ------------- | ------------- | ------- | | `users` | `/id` | — | | `licenses` | `/userId` | — | | `transcripts` | `/userId` | — | | `usage_daily` | `/userId` | 1 year | | `settings` | `/userId` | — | | `audit_log` | `/category` | 90 days | | `api_tokens` | `/userId` | — | | `devices` | `/userId` | — | ## Kill Switch The Settings page has a prominent kill switch card at the top: - **Green** = platform active, **Red** = platform disabled - Toggle writes to `settings` container: `{ id: "kill_switch", userId: "system", enabled: bool }` - All apps (desktop, mobile, web) read this document to check platform status ## Features - **Direct Cosmos DB** — no middleware, API routes call SDK directly - **JWT auth** — bcrypt password hashing, HS256 JWT tokens - **Auth guard** — redirects to `/login` if not authenticated - **Mock fallback** — pages fall back to mock data if Cosmos is unavailable - **Responsive** — sidebar collapses to hamburger on mobile - **Dark mode** — toggle in sidebar footer, persisted to localStorage - **Error boundary** — catches page crashes gracefully - **Kill switch** — global platform toggle on Settings page ## Docker ```bash docker build -t platform-admin . docker run -p 3001:3000 --env-file .env.local platform-admin # Or with docker-compose docker compose up -d ``` ## Development Notes - **Port**: runs on 3001 in dev (3000 may conflict with Docker) - **shadcn/ui**: components are in `src/components/ui/`, added via `npx shadcn@latest add ` - **Adding a new API route**: create `src/app/api//route.ts`, import from `@/lib/repositories/*` - **Adding a new page**: create `src/app/(dashboard)//page.tsx` — auto-protected by auth guard