207 lines
9.7 KiB
Markdown
207 lines
9.7 KiB
Markdown
# 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=<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 <component>`
|
|
- **Adding a new API route**: create `src/app/api/<name>/route.ts`, import from `@/lib/repositories/*`
|
|
- **Adding a new page**: create `src/app/(dashboard)/<name>/page.tsx` — auto-protected by auth guard
|