v0.5.13: AGENT-GUIDE.md — полный гайд для AI-агентов (англ.)

This commit is contained in:
2026-06-03 06:22:35 +03:00
parent 2841e3ac27
commit 22beaa86a3
2 changed files with 260 additions and 0 deletions
+259
View File
@@ -0,0 +1,259 @@
# IP Whitelist App — AI Agent Guide
> For AI coding agents (Copilot, etc.) to understand and continue development.
> Last updated: 2026-06-03 | Version: 0.5.13
## 1. Quick Overview
**Purpose:** Self-service portal for cloud provider customers to manage trusted IPv4 whitelist entries (CIDRs) that are excluded from DDoS filtering.
**Stack:** Node.js + Express + EJS + PostgreSQL
**Auth:** Mock RS256 JWT (dev) / Keycloak OIDC (production)
**Deployment:** PM2 on VM (`5.172.178.213`), HTTP only via `italo.kube5s.ru`
## 2. Architecture
```
Browser → nginx:80 → Express:3001
├── /export (public, no auth)
├── /api/v1/* (Bearer JWT, REST API)
└── /* (UI layer, SSR EJS, session-based)
```
### Two-layer design:
- **API layer** (`src/api/routes/`): REST endpoints, Bearer JWT auth, direct DB access
- **UI layer** (`ui/routes/`): SSR EJS pages, session-based auth, proxies to API via HTTP (`ui/api-client.js`)
### Why two layers?
- API can be used directly by nginx/firewall for whitelist export
- UI provides human-friendly management interface
- Clear separation of concerns
### Key flow:
```
Browser → UI route → api-client.js (HTTP) → API route → PostgreSQL
```
## 3. Project Structure
```
ipwhitelist-app/
├── server.js # Entry point — mounts all routes
├── package.json
├── .env # Config (not in git)
├── .env.example # Config template
├── sql/schema.sql # Database schema
├── src/
│ ├── auth.js # Auth: mock JWT + OIDC/Keycloak
│ ├── config.js # MOCK_USERS + helpers
│ ├── db.js # PostgreSQL pool
│ ├── queries.js # All DB queries (CRUD, export, audit)
│ ├── validators.js # CIDR validation + blocked ranges + aggregation
│ │
│ ├── api/
│ │ ├── index.js # API router mount
│ │ └── routes/
│ │ ├── entries.js # GET/POST/PATCH/DELETE /entries + /entries/export
│ │ └── admin.js # GET /companies, PATCH /limit, GET /audit
│ │
│ ├── middleware/
│ │ ├── csrf.js # CSRF protection (NOT ACTIVE — tokens are empty)
│ │ ├── rateLimit.js # Rate limiters (auth, mutation, export)
│ │ └── session.js # express-session with pg store
│ │
│ └── routes/
│ ├── oidc.js # OIDC/Keycloak routes (inactive in mock mode)
│ └── (old monolithic routes deleted in v0.5.11)
├── ui/
│ ├── index.js # UI middleware: requireToken + resolveUser + mount routers
│ ├── api-client.js # HTTP client to /api/v1/* (from UI layer)
│ └── routes/
│ ├── auth.js # GET/POST /login, POST /login-token, GET /logout
│ ├── entries.js # GET /, POST /add, POST /edit/:id, POST /delete/:id
│ ├── admin.js # GET /admin, /audit (admin-only)
│ └── export.js # GET /export (proxies to API)
├── views/
│ ├── index.ejs # Main page: entries table + add/edit/delete forms
│ ├── ui-login.ejs # Login page (mock + manual clientId entry)
│ ├── admin.ejs # Admin: companies list + limits
│ ├── audit.ejs # Audit log
│ └── error.ejs # 404/500 error page
├── tests/
│ ├── api.js # 121 API integration tests (Bearer JWT)
│ ├── integration.js # 68 UI integration tests (session, EJS)
│ ├── tz-compliance.js # 47 ТЗ compliance tests
│ ├── tz-full-compliance.js # 111 comprehensive ТЗ tests (20 companies × 100 CIDRs)
│ ├── run-tests.js # 68 unit/smoke tests (no DB)
│ └── stress.js # 191 concurrency/stress tests
└── docs/
├── ТЗ.md # Tech spec (Russian)
├── ТЗ-плюс.md # Client clarifications
├── ТЗ-реализация.md # Implementation notes
├── keycloak-auth-reference.md # Keycloak/OIDC reference
├── devops-deploy.md # DevOps deployment guide
└── deploy-keycloak.md # Keycloak setup guide
```
## 4. Auth System
### Mock Mode (default, DEV_MODE=true)
- Local RSA keypair generated at startup
- `POST /login-token` with `clientId` field → mock JWT → session
- Mock users defined in `src/config.js`:
- `admin` (WZ01112) — admin@nubes.ru
- `alfa` (WZ88888) — alfa@example.com
- `multi` (WZ88888, WZ77777) — multi@example.com
- `beta` (WZ77777) — beta@example.com
### OIDC Mode (production, when KC_CLIENT_ID set)
- Authorization Code flow via Keycloak
- Routes in `src/routes/oidc.js` (inactive in mock mode)
- JWKS certificate verification
### UI resolveUser (ui/index.js)
- Decodes JWT from session (no signature check — API validates on each call)
- Extracts `ClientID`, `allClientIds`, `activeClientId`, `companyId`, `companyName`, `email`
- Sets `req.user` for all downstream handlers
### API bearerMiddleware (src/auth.js)
- Verifies Bearer JWT signature (RS256 via JWKS or mock keypair)
- Sets `req.user` via `userFromPayload()`
## 5. Multi-Company
Users can belong to multiple companies via comma-separated `clientId`:
- `ClientID: "WZ88888, WZ77777"` → two companies
- Active company selector in UI (dropdown, `?switchTo=X` or JS navigation)
- API supports `?client_id=<id>` parameter to specify active company
- Default: first company in list (`allClientIds[0]`)
**IMPORTANT:** `req.user.activeClientId` must be synced BOTH in `session.user.activeClientId` AND `req.user.activeClientId` — see `ui/routes/entries.js` switch handler.
## 6. Company Isolation
- Each company is a row in `companies` table (unique by `client_id`)
- Entries are isolated by `company_id` foreign key
- Admin can view/edit any company via `?company=<db_id>`
- User sees only their company's entries
- Multi-company user switches via `?client_id=<clientId>`
## 7. Database
Three tables (see `sql/schema.sql`):
- `companies` — (id, client_id, name, custom_limit, created_at, updated_at)
- `whitelist_entries` — (id, company_id FK, value_cidr, comment, created_by, created_at, updated_at, deleted_at, deleted_by)
- `audit_log` — (id, user_email, company_id, action, old_value, new_value, entry_id, created_at)
Default limit: 15 entries per company (configurable via `DEFAULT_LIMIT` env).
## 8. Key Endpoints
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/export` | None (public) | Aggregated CIDR list, text/plain |
| GET | `/api/v1/entries` | Bearer JWT | List entries for company |
| POST | `/api/v1/entries` | Bearer JWT | Create entry |
| PATCH | `/api/v1/entries/:id` | Bearer JWT | Update entry |
| DELETE | `/api/v1/entries/:id` | Bearer JWT | Soft-delete entry |
| GET | `/api/v1/entries/export` | Bearer JWT | Aggregated CIDR export |
| GET | `/api/v1/companies` | Bearer JWT (admin) | List companies |
| PATCH | `/api/v1/companies/:id/limit` | Bearer JWT (admin) | Set company limit |
| GET | `/api/v1/audit` | Bearer JWT (admin) | Audit log |
| GET | `/login` | None | Login page (UI) |
| POST | `/login` | None | Mock login (form) |
| POST | `/login-token` | None | Manual clientId login |
| GET | `/logout` | Session | Logout |
### Export query params:
- `?view=1` — inline (browser display)
- `?filename=X` — custom filename (default: white-list.txt)
## 9. CIDR Validation Rules
- Only IPv4, no IPv6
- Mask must be /22 to /32 (inclusive)
- Single IP without mask = /32
- 14 blocked ranges (RFC1918, CGNAT, loopback, etc.)
- No duplicates within a company
- No overlaps within a company
- Normalization: host bits zeroed (e.g., 9.9.9.5/24 → 9.9.9.0/24)
- Comment max 255 chars, optional
## 10. Running Tests
```bash
# Unit tests (no DB needed)
node tests/run-tests.js
# API tests (requires DB, DEV_MODE=true)
node tests/api.js
# UI integration tests
node tests/integration.js
# ТЗ compliance
node tests/tz-compliance.js
# Full ТЗ compliance (20 companies × 100 CIDRs)
node tests/tz-full-compliance.js
# Stress/concurrency tests
node tests/stress.js
```
All tests: 347+ PASS, 0 FAIL (v0.5.13)
## 11. Deployment (VM)
```bash
# From local workspace:
rsync -az -e "ssh -i ~/.ssh/naeel_vm_id_ed25519 ..." /home/naeel/ipwhitelist-app/ naeel@5.172.178.213:~/ipwhitelist/ --exclude node_modules --exclude .git --exclude .env
# On VM:
cd ~/ipwhitelist && pm2 restart ipwhitelist --update-env
```
## 12. Common Pitfalls
### 🚨 Rate Limiter
`authLimiter`: 10 requests per 5 minutes. Hits quickly during testing. Reset after 5 min or use different IPs.
### 🚨 Mock Keypair Mismatch
Each `initAuth()` generates a NEW RSA keypair. When testing with supertest, the UI api-client makes HTTP requests to `localhost:3001` (PM2 instance), which has a DIFFERENT keypair → "invalid signature". Fix: test against the running PM2 instance directly, or stop PM2 and use the supertest port.
### 🚨 Company Switch Bug (FIXED v0.5.12)
`resolveUser` runs BEFORE the route handler. When switching companies via `?switchTo=X`, the session was updated but `req.user.activeClientId` still had the old value. Fixed by adding `req.user.activeClientId = switchTo` after session update.
### 🚨 CSRF Not Active
`csrfToken` is always `""` in templates. CSRF middleware is not mounted. Forms work, but there's no CSRF protection. To enable: mount `doubleCsrfProtection` middleware and call `generateCsrfToken()` in GET handlers.
### 🚨 `.env` on VM gets overwritten
Rsync excludes `.env` now, but double-check. If `.env` contains DB credentials, they MUST NOT be overwritten.
### 🚨 Version Must Always Increment
After ANY code change: bump `"version"` in `package.json`. PM2 shows version — user expects it to match.
## 13. OIDC/Keycloak Transition
To enable production auth:
1. DevOps creates client in Keycloak with redirect URI `/callback`
2. Set `.env`: `KC_CLIENT_ID`, `KC_CLIENT_SECRET`, `KC_BASE_URL`, `APP_URL`
3. Set `DEV_MODE=false`
4. `pm2 restart ipwhitelist --update-env`
5. Routes in `src/routes/oidc.js` activate automatically when `KC_CLIENT_ID` is set
See `docs/deploy-keycloak.md` for details.
## 14. Version History
| Version | Date | Key Changes |
|---------|------|-------------|
| 0.5.13 | 2026-06-03 | OIDC routes prepared, .env.example, devops-deploy guide, repo cleanup |
| 0.5.12 | 2026-06-02 | Fixed company switch bug, aggregation, validation, TZ compliance |
| 0.5.11 | 2026-06-02 | Dead code cleanup, new integration tests, /export public |
| 0.5.10 | 2026-06-02 | UI/API split, mock users fixed, 121 API tests passing |
+1
View File
@@ -26,3 +26,4 @@
- `docs/ТЗ-плюс.md` — уточнения от заказчика
- `docs/keycloak-auth-reference.md` — справка по Keycloak/OIDC
- `Files/token.txt` — креды (токены + БД)
- `.github/AGENT-GUIDE.md` — полный гайд для AI-агентов (англ.)