Build [v.1.3.6]
This commit is contained in:
@@ -1,259 +1,155 @@
|
||||
# CURRENT AI WORKING SESSION — IN PROGRESS
|
||||
# CURRENT AI WORKING SESSION — HANDOVER
|
||||
|
||||
**Active AI:** Claude (Haiku 4.5)
|
||||
**Active AI:** Gemini (Antigravity)
|
||||
**Last Updated:** 2026-04-11
|
||||
**Current Version:** v1.3.5
|
||||
**Current Version:** v1.3.5 (pending bump to v1.3.6)
|
||||
**Branch:** dev
|
||||
|
||||
---
|
||||
|
||||
## 🔧 CURRENT ISSUE — CORS Configuration Clarification
|
||||
## STATUS: 🟢 STABLE — READY FOR VERSION SAVE
|
||||
|
||||
### Two Deployment Methods
|
||||
The project supports two distinct deployment approaches:
|
||||
|
||||
#### 1. **Docker Compose** (Production / Containerized)
|
||||
- Uses `docker-compose.yml` with Caddy reverse proxy
|
||||
- Backend container on internal port 8000 → exposed via Caddy on 3002
|
||||
- Frontend container on internal port 3000 → exposed via Caddy on 3003
|
||||
- **Configuration:** `ALLOWED_ORIGINS` environment variable in docker-compose.yml
|
||||
- **Current setting:** `http://localhost:3000,http://localhost:3002`
|
||||
- **For remote deployment:** Must update ALLOWED_ORIGINS to include actual domain (e.g., `https://192.168.84.140:3003`)
|
||||
|
||||
#### 2. **start_server.sh** (Development / Native)
|
||||
- Runs Backend (uvicorn) + Frontend (Next.js) + local-ssl-proxy directly
|
||||
- Backend on port 8000 → SSL proxy on 3002
|
||||
- Frontend on port 3001 → SSL proxy on 3003
|
||||
- **Issue Fixed:** Script now automatically detects local IP and exports ALLOWED_ORIGINS
|
||||
- **Auto-configured for:** localhost + detected IP address on both HTTP and HTTPS
|
||||
|
||||
### Fix Applied (Commit Pending)
|
||||
Updated `start_server.sh` to:
|
||||
1. Detect local IP address (en0/en1 interface)
|
||||
2. Export ALLOWED_ORIGINS with all applicable origins:
|
||||
- HTTP localhost on dev ports (3000/3001)
|
||||
- HTTPS localhost on proxy ports (3002/3003)
|
||||
- HTTPS on detected IP address on proxy ports
|
||||
3. Display CORS configuration when starting backend
|
||||
4. Export JWT_SECRET_KEY if not already set
|
||||
|
||||
**Result:** Frontend at `https://<LOCAL_IP>:3003` can now access backend at `https://<LOCAL_IP>:3002` without CORS blocks.
|
||||
|
||||
### English Language Compliance
|
||||
Fixed Romanian comments in docker-compose.yml:
|
||||
- `"personalizează pentru producție"` → `"customize for production"`
|
||||
- `"GENEREAZĂ O VALOARE SIGURĂ"` → `"GENERATE A SECURE VALUE"`
|
||||
All UI/UX refinements from this session have been applied. The login loop (from previous Claude session) is confirmed fixed. The scanner has been fully redesigned. Documentation is up to date.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 PREVIOUS SESSION — ALL TASKS FINALIZED + ENGLISH COMPLIANCE VERIFIED
|
||||
## WHAT WAS DONE THIS SESSION
|
||||
|
||||
### Final Status
|
||||
### 1. Scanner UI Redesign (`frontend/components/Scanner.tsx`)
|
||||
- **Layout**: Moved all controls OUT of the camera viewport overlay. Camera feed is now 100% unobstructed.
|
||||
- **Automated OCR**: Removed manual "OCR SCAN" button. OCR now runs automatically on a 4-second cycle.
|
||||
- **Visual Countdown**: Added a countdown display (4, 3, 2, 1, Scan) with a progress bar so the user can see the next scan timing.
|
||||
- **Scan-line animation**: Now only active when `isStarted && !isSelecting && !paused`.
|
||||
- **Typography**: Removed all `uppercase` and `tracking-widest` styles per `AI_RULES.md` Section 3.
|
||||
- **Silent failures**: Auto-OCR no longer shows toast errors if no text is found (silently retries on next cycle).
|
||||
|
||||
✅ **Security Audit** — complete (12 vulnerabilities, 6 direct patches)
|
||||
✅ **[C-01] JWT Bearer Auth** — complete (backend + frontend)
|
||||
✅ **[H-02] Rate Limiting** — complete (10 req/min on /items/extract-label)
|
||||
✅ **[M-01] CORS Configuration** — complete (ALLOWED_ORIGINS from env)
|
||||
✅ **[L-01] Token Expiry** — complete (8h JWT expiration)
|
||||
✅ **STRICT ENGLISH POLICY** — complete (all code, comments, docstrings translated; no Co-Authored-By signatures)
|
||||
### 2. Item Type Datalist (`AIOnboarding.tsx`, `page.tsx`, `inventory/page.tsx`)
|
||||
- Added a searchable `<datalist>` to the Item Type field across all relevant forms.
|
||||
- Dynamically populated with existing unique types from the DB, while still allowing manual free-text input.
|
||||
|
||||
### Session Commits
|
||||
|
||||
| Hash | Description |
|
||||
|------|-------------|
|
||||
| `247ea454` | security: audit + 6 patches (C-02, C-03, H-01, H-03, M-01, M-03) |
|
||||
| `9b6adad6` | feat: JWT Bearer auth on all routers |
|
||||
| `e9ada004` | docs: update SESSION_STATE JWT complete |
|
||||
| `e6ca33f2` | feat: frontend JWT, rate limiting, CORS |
|
||||
| `2574726f` | docs: final SESSION_STATE all tasks complete |
|
||||
### 3. `save-version` Automation Command
|
||||
- Created `scripts/save_version.py` — increments patch version in `VERSION.json`, commits all changes, creates a snapshot branch `v.X.Y.Z`, and generates the production ZIP via `./export_prod.sh`.
|
||||
- Registered as an official AI Command Shortcut in `AI_RULES.md` Section 6.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Complete Implementations
|
||||
## WHAT THE NEXT AI MUST DO
|
||||
|
||||
### Backend
|
||||
|
||||
#### JWT Authentication [C-01]
|
||||
- ✅ `backend/auth.py` — JWT creation, validation, role checking
|
||||
- ✅ `/users/login` — returns `TokenResponse` (access_token, user_id, role, 8h expiry)
|
||||
- ✅ All routers — `Depends(get_current_user)` enforcement
|
||||
- ✅ Admin-only endpoints — `Depends(get_current_admin)`
|
||||
- ✅ user_id — extracted from JWT token, NOT from request body [M-02]
|
||||
|
||||
#### Rate Limiting [H-02]
|
||||
- ✅ `slowapi` integrated in requirements.txt
|
||||
- ✅ `/items/extract-label` — `@limiter.limit("10/minute")` per IP
|
||||
- ✅ Protection against spam on AI endpoint
|
||||
|
||||
#### CORS Configuration [M-01]
|
||||
- ✅ `ALLOWED_ORIGINS` from environment variable (fallback: localhost:3000, localhost:3002)
|
||||
- ✅ Specific HTTP methods: GET, POST, PUT, DELETE, OPTIONS
|
||||
- ✅ allow_credentials=True (valid with specific origins)
|
||||
|
||||
#### Direct Patches
|
||||
- ✅ C-02 — Removed passwordless bypass
|
||||
- ✅ C-03 — Admin seed with random password
|
||||
- ✅ H-01 — LDAP injection fix (escape_filter_chars)
|
||||
- ✅ H-03 — MIME validation + 10MB upload limit
|
||||
- ✅ M-03 — LDAP logging via log.debug()
|
||||
|
||||
### Frontend
|
||||
|
||||
#### JWT Handling [L-01]
|
||||
- ✅ `frontend/lib/auth.ts` — saveToken, getToken, getAuthHeader, clearAuth
|
||||
- ✅ Token storage — localStorage (inventory_token + inventory_user)
|
||||
- ✅ Login page — saves TokenResponse from /users/login
|
||||
- ✅ Token attachment — Axios interceptor adds `Authorization: Bearer <token>` to all requests
|
||||
|
||||
#### Token Expiry [L-01]
|
||||
- ✅ 401 Unauthorized → clearAuth() + redirect /login
|
||||
- ✅ Interceptor on axiosInstance
|
||||
- ✅ Auto-logout on expiration
|
||||
|
||||
### Deployment
|
||||
|
||||
#### docker-compose.yml
|
||||
- ✅ Backend environment variables: DATA_DIR, LOGS_DIR, ALLOWED_ORIGINS, JWT_SECRET_KEY
|
||||
- ✅ Comments for production configuration
|
||||
- ✅ Fallback values for development
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
| Variable | Dev Default | Production Required | Purpose |
|
||||
|----------|-------------|-------------------|---------|
|
||||
| ALLOWED_ORIGINS | localhost:3000, localhost:3002 | SET REQUIRED | CORS allowed origins |
|
||||
| JWT_SECRET_KEY | ephemeral (random) | SET REQUIRED | JWT signing key |
|
||||
| DATA_DIR | /app/data | - | SQLite database location |
|
||||
| LOGS_DIR | /app/logs | - | Application logs location |
|
||||
1. Run `save-version` to finalize version `1.3.6` if not already done.
|
||||
2. Monitor TypeScript warnings as the `Item` interface in `frontend/lib/db.ts` may be missing fields.
|
||||
3. If the Item Type datalist grows too large, consider a dedicated "Category/Type Management" settings page.
|
||||
4. Periodically review `dev_docs/SECURITY_REPORT.md`.
|
||||
|
||||
---
|
||||
|
||||
## 📊 Vulnerability Status
|
||||
## SYSTEM STATE
|
||||
|
||||
| ID | Severity | Description | Status |
|
||||
|----|----------|-------------|--------|
|
||||
| C-01 | 🔴 CRITICAL | Zero authentication on API | ✅ PATCHED (JWT) |
|
||||
| C-02 | 🔴 CRITICAL | Passwordless user bypass | ✅ PATCHED |
|
||||
| C-03 | 🔴 CRITICAL | Default "admin" password | ✅ PATCHED |
|
||||
| C-04 | 🔴 CRITICAL | Gemini API key exposed | ✅ SAFE (never in git) |
|
||||
| H-01 | 🟠 HIGH | LDAP injection | ✅ PATCHED |
|
||||
| H-02 | 🟠 HIGH | Missing rate limit | ✅ PATCHED |
|
||||
| H-03 | 🟠 HIGH | File upload validation | ✅ PATCHED |
|
||||
| H-04 | 🟠 HIGH | Admin endpoints exposed | ✅ PATCHED (JWT) |
|
||||
| M-01 | 🟡 MEDIUM | CORS wildcard | ✅ PATCHED |
|
||||
| M-02 | 🟡 MEDIUM | user_id from body | ✅ PATCHED |
|
||||
| M-03 | 🟡 MEDIUM | Debug LDAP logging | ✅ PATCHED |
|
||||
| L-01 | 🔵 LOW | Token expiry | ✅ PATCHED |
|
||||
**Active database:** `<project_root>/data/inventory.db`
|
||||
**LDAP config:** `backend/config/ldap_config.json`
|
||||
```json
|
||||
{"ldap_enabled": true, "server_uri": "ldap://192.168.84.107:3890", "base_dn": "dc=example,dc=com", "user_template": "cn={username},ou=people,dc=example,dc=com", "groups_dn": "ou=groups", "use_tls": false, "role_mappings": [{"group": "inventory_admins", "role": "admin"}, {"group": "inventory_users", "role": "user"}]}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Production Deployment Checklist
|
||||
|
||||
**CRITICAL — Set these environment variables before deploy:**
|
||||
|
||||
- [ ] **JWT_SECRET_KEY** — Generate with `openssl rand -hex 32` (store in secrets manager)
|
||||
- [ ] **ALLOWED_ORIGINS** — Set to actual production domain(s), e.g., `https://inventory.example.com`
|
||||
- [ ] **TLS/HTTPS** — Caddy proxy configured with valid SSL certificate
|
||||
- [ ] **Database backup** — SQLite data/ mapped to persistent volume
|
||||
- [ ] **Logs rotation** — logs/ mapped and monitored
|
||||
|
||||
**RECOMMENDED:**
|
||||
|
||||
- [ ] Set log level to WARNING (not DEBUG)
|
||||
- [ ] Configure rate limiting at reverse proxy level (nginx/Cloudflare) for DDoS protection
|
||||
- [ ] Monitor alert on 401 spikes (token expiry issues)
|
||||
- [ ] Periodic JWT_SECRET_KEY rotation (quarterly minimum)
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Local Testing
|
||||
|
||||
### 1. Start Development Stack
|
||||
**How to start:**
|
||||
```bash
|
||||
docker-compose up --build
|
||||
./start_server.sh
|
||||
```
|
||||
- Frontend: `https://<LOCAL_IP>:3003`
|
||||
- Backend: `http://localhost:8000` direct
|
||||
|
||||
### 2. Test Login
|
||||
```bash
|
||||
curl -X POST http://localhost:8000/users/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username": "Admin", "password": "<initial_password_from_logs>"}'
|
||||
```
|
||||
**Environment variables set by start_server.sh:**
|
||||
- `ALLOWED_ORIGINS` — auto-detected from local IP
|
||||
- `DATA_DIR` — absolute path to `<project_root>/data/`
|
||||
- `LOGS_DIR` — absolute path to `<project_root>/logs/`
|
||||
- `JWT_SECRET_KEY` — ephemeral per-run if not set externally
|
||||
|
||||
### 3. Test Protected Endpoint with Token
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <access_token>" \
|
||||
http://localhost:8000/items/
|
||||
```
|
||||
|
||||
### 4. Test 401 Unauthorized (invalid token)
|
||||
```bash
|
||||
curl -H "Authorization: Bearer invalid_token" \
|
||||
http://localhost:8000/items/
|
||||
# Expected: 401 Unauthorized
|
||||
```
|
||||
|
||||
### 5. Test Rate Limiting (extract-label endpoint)
|
||||
```bash
|
||||
# 11 requests rapid fire — 11th should fail with 429
|
||||
for i in {1..15}; do
|
||||
curl -X POST -F "file=@image.jpg" \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
http://localhost:8000/items/extract-label
|
||||
sleep 0.1
|
||||
done
|
||||
```
|
||||
This session applied the 3 frontend fixes for the login redirect loop. The server was NOT restarted and the login flow was NOT tested. The next AI must test and confirm — or continue debugging if the loop persists.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Validations Completed
|
||||
## WHAT WAS DONE THIS SESSION
|
||||
|
||||
- ✅ Backend fully secured with JWT
|
||||
- ✅ Frontend token handling complete
|
||||
- ✅ Rate limiting on AI endpoint
|
||||
- ✅ CORS configurable via environment
|
||||
- ✅ All routers have authentication enforcement
|
||||
- ✅ Token expiry with automatic login redirect
|
||||
- ✅ Admin role checks functional
|
||||
- ✅ LDAP injection protection
|
||||
- ✅ File upload validation
|
||||
- ✅ Audit logging with user_id from token
|
||||
### 1. `frontend/lib/api.ts` — Lazy baseURL (Fix 1)
|
||||
|
||||
axiosInstance no longer receives baseURL at creation. `getBackendUrl()` is now called inside the request interceptor, at request time, so SSR can no longer lock it to `http://localhost:8000`.
|
||||
|
||||
```typescript
|
||||
// BEFORE (broken):
|
||||
const axiosInstance = axios.create({ baseURL: getBackendUrl() });
|
||||
|
||||
// AFTER (fixed):
|
||||
const axiosInstance = axios.create({});
|
||||
axiosInstance.interceptors.request.use((config) => {
|
||||
if (!config.baseURL) config.baseURL = getBackendUrl();
|
||||
const token = getToken();
|
||||
if (token) config.headers.Authorization = `Bearer ${token}`;
|
||||
return config;
|
||||
});
|
||||
```
|
||||
|
||||
### 2. `frontend/lib/api.ts` — 401 interceptor guard (Fix 2)
|
||||
|
||||
```typescript
|
||||
// BEFORE (broken — infinite loop):
|
||||
if (typeof window !== 'undefined') {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
// AFTER (fixed):
|
||||
if (typeof window !== 'undefined' && !window.location.pathname.includes('/login')) {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
```
|
||||
|
||||
### 3. `frontend/app/page.tsx` — Token guard on both useEffect hooks (Fix 3)
|
||||
|
||||
Added `if (!localStorage.getItem('inventory_token')) { window.location.href = '/login'; return; }` at the top of BOTH useEffect hooks — the first one (calls `getCategories`) and the second one (calls `loadInventory`).
|
||||
|
||||
### 4. Debug cleanup
|
||||
- `frontend/lib/auth.ts` — removed `console.log('[Auth] saveToken...')` statements
|
||||
- `frontend/app/login/page.tsx` — removed `console.log('[LoginPage]...')` statements + unused `memo` import
|
||||
|
||||
---
|
||||
|
||||
## 🎓 Key Learnings
|
||||
## WHAT THE NEXT AI MUST DO
|
||||
|
||||
1. **CORS with credentials=True** — requires specific origins, NOT wildcards
|
||||
2. **Rate limiting** — essential on expensive endpoints (AI processing)
|
||||
3. **JWT expiration** — 8h balances security vs. UX
|
||||
4. **user_id from token** — eliminates operation spoofing
|
||||
5. **Debug logging** — careful with PII leaks (LDAP DNs)
|
||||
1. Restart the server: `./start_server.sh`
|
||||
2. Open `https://192.168.84.140:3003/login` in browser
|
||||
3. Log in with LDAP user `bede`
|
||||
4. Verify: main page loads and STAYS — no redirect back to `/login`
|
||||
5. Verify: `localStorage.getItem('inventory_token')` is non-null after login
|
||||
6. If loop persists: check browser Network tab for which API endpoint returns 401 first — there may be other API calls in child components (`PageShell`, `Scanner`, etc.) that fire before the token guard
|
||||
|
||||
---
|
||||
|
||||
## 📝 Production Environment Variables Example
|
||||
## KNOWN PRE-EXISTING ISSUES (not introduced by this session)
|
||||
|
||||
TypeScript errors in `frontend/app/page.tsx`:
|
||||
- Line 241: `Property 'serial_number' does not exist on type 'Item'`
|
||||
- Lines 598-599: `Property 'type' does not exist on type 'Partial<Item>'`
|
||||
|
||||
These are separate bugs — `Item` type in `frontend/lib/db.ts` is missing fields that the UI uses. Fix separately from the login issue.
|
||||
|
||||
---
|
||||
|
||||
## SYSTEM STATE
|
||||
|
||||
**Active database:** `<project_root>/data/inventory.db`
|
||||
**LDAP config:** `backend/config/ldap_config.json`
|
||||
```json
|
||||
{"ldap_enabled": true, "server_uri": "ldap://192.168.84.107:3890", "base_dn": "dc=example,dc=com", "user_template": "cn={username},ou=people,dc=example,dc=com", "groups_dn": "ou=groups", "use_tls": false, "role_mappings": [{"group": "inventory_admins", "role": "admin"}, {"group": "inventory_users", "role": "user"}]}
|
||||
```
|
||||
|
||||
**How to start:**
|
||||
```bash
|
||||
# Set these before `docker-compose up`
|
||||
export JWT_SECRET_KEY="$(openssl rand -hex 32)"
|
||||
export ALLOWED_ORIGINS="https://inventory.example.com,https://api.example.com"
|
||||
|
||||
docker-compose up -d
|
||||
./start_server.sh
|
||||
```
|
||||
- Frontend: `https://<LOCAL_IP>:3003`
|
||||
- Backend: `https://<LOCAL_IP>:3002` (also `http://localhost:8000` direct)
|
||||
|
||||
Or in `.env.production` (NOT in git):
|
||||
```
|
||||
JWT_SECRET_KEY=abcdef1234567890...
|
||||
ALLOWED_ORIGINS=https://inventory.example.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Status:** 🚀 PRODUCTION-READY (with environment configuration required)
|
||||
|
||||
**Future Scope (OUT OF SCOPE):**
|
||||
- Email verification for new users
|
||||
- 2FA/TOTP support
|
||||
- OAuth2 federation (GitHub/Google/Azure)
|
||||
- API key management for service accounts
|
||||
- Audit log export/archival to cold storage
|
||||
**Environment variables set by start_server.sh:**
|
||||
- `ALLOWED_ORIGINS` — auto-detected from local IP (includes both HTTP and HTTPS variants)
|
||||
- `DATA_DIR` — absolute path to `<project_root>/data/`
|
||||
- `LOGS_DIR` — absolute path to `<project_root>/logs/`
|
||||
- `JWT_SECRET_KEY` — ephemeral per-run if not set externally (means tokens invalidated on restart)
|
||||
|
||||
Reference in New Issue
Block a user