This commit is contained in:
seniorswe
2025-09-22 20:53:33 -04:00
committed by seniorswe
parent 065689be13
commit 5f19e5a185
4 changed files with 81 additions and 2 deletions

View File

@@ -121,9 +121,40 @@ Defaults
- Backend: http://localhost:5001
- Web: http://localhost:3000 (set NEXT_PUBLIC_SERVER_URL accordingly)
## Docker
- Compose up: `docker compose up --build`
- Services: backend (`:5001`), web (`:3000`), redis (`:6379`)
- Secrets: set `JWT_SECRET_KEY`, `TOKEN_ENCRYPTION_KEY`, `MEM_ENCRYPTION_KEY` via env/secret manager (avoid checking into git)
- Override backend envs: `docker compose run -e KEY=value backend ...`
- Reset volumes/logs: `docker compose down -v`
Smoke checks
- Liveness: `curl -s http://localhost:5001/platform/monitor/liveness``{ "status": "alive" }`
- Readiness: `curl -s http://localhost:5001/platform/monitor/readiness``{ status: "ready", ... }`
- Auth login: `curl -s -c cookies.txt -H 'Content-Type: application/json' -d '{"email":"admin@localhost","password":"password1"}' http://localhost:5001/platform/authorization`
- Auth status: `curl -s -b cookies.txt http://localhost:5001/platform/authorization/status`
- One-liner: `BASE_URL=http://localhost:5001 STARTUP_ADMIN_EMAIL=admin@localhost STARTUP_ADMIN_PASSWORD=password1 bash scripts/smoke.sh`
Production notes
- Use Redis in production (`MEM_OR_EXTERNAL=REDIS`) for distributed rate limiting.
- In memory-only mode, run a single worker: `THREADS=1`.
- Optional: set `LOG_FORMAT=json` for structured logs.
Production security defaults
- Set `CORS_STRICT=true` and explicitly whitelist your origins via `ALLOWED_ORIGINS`.
- Enable `HTTPS_ONLY=true` and `HTTPS_ENABLED=true` so cookies are Secure and CSRF validation is enforced.
Quick go-live checklist
- Start stack: `docker compose up --build`
- Verify health:
- `curl -s http://localhost:5001/platform/monitor/liveness``{ "status": "alive" }`
- `curl -s http://localhost:5001/platform/monitor/readiness``{ status: "ready", ... }`
- Smoke auth:
- `curl -s -c cookies.txt -H 'Content-Type: application/json' -d '{"email":"admin@localhost","password":"password1"}' http://localhost:5001/platform/authorization`
- `curl -s -b cookies.txt http://localhost:5001/platform/authorization/status`
- Web: Ensure `web-client/.env.local` has `NEXT_PUBLIC_SERVER_URL=http://localhost:5001`, then `npm run build && npm start` (or use compose service `web`).
Optional: run `bash scripts/smoke.sh` (uses `BASE_URL`, `STARTUP_ADMIN_EMAIL`, `STARTUP_ADMIN_PASSWORD`).
```
## Web UI

View File

@@ -9,7 +9,7 @@ Recommended production defaults (see `.env`):
- HTTPS_ONLY=true — set `Secure` flag on cookies
- HTTPS_ENABLED=true — enforce CSRF double-submit for cookie auth
- CORS_STRICT=true — disallow wildcard origins; whitelist your domains via `ALLOWED_ORIGINS`
- HTTPS_ONLY=true and HTTPS_ENABLED=true — set Secure cookies and enforce CSRF double-submit
- LOG_FORMAT=json — optional JSON log output for production log pipelines
- MAX_BODY_SIZE_BYTES=1048576 — reject requests with Content-Length above 1 MB
- STRICT_RESPONSE_ENVELOPE=true — platform APIs return consistent envelopes
@@ -56,6 +56,7 @@ Core variables:
3. Set ALLOWED_ORIGINS to your web client domains; set ALLOW_CREDENTIALS=true only when needed.
4. Provision Redis (recommended) and MongoDB (optional in memory-only mode). In memory mode, enable encryption key for dumps and consider TOKEN_ENCRYPTION_KEY for API keys.
5. Rotate JWT_SECRET_KEY periodically; plan for key rotation and token invalidation.
6. Memory-only mode requires a single worker (THREADS=1); multiple workers will have divergent in-memory state.
## Runbooks

View File

@@ -46,6 +46,7 @@ from utils.database import database
import multiprocessing
import logging
import json
import re
import os
import sys
@@ -241,7 +242,22 @@ log_file_handler = RotatingFileHandler(
backupCount=5,
encoding="utf-8"
)
log_file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
if os.getenv("LOG_FORMAT", "plain").lower() == "json":
class JSONFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str:
payload = {
"time": self.formatTime(record, "%Y-%m-%dT%H:%M:%S"),
"name": record.name,
"level": record.levelname,
"message": record.getMessage(),
}
try:
return json.dumps(payload, ensure_ascii=False)
except Exception:
return f'{payload}'
log_file_handler.setFormatter(JSONFormatter())
else:
log_file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
# Configure all doorman loggers to use the same handler and prevent propagation
def configure_logger(logger_name):
@@ -435,6 +451,12 @@ def run():
max_threads = multiprocessing.cpu_count()
env_threads = int(os.getenv("THREADS", max_threads))
num_threads = min(env_threads, max_threads)
try:
if database.memory_only and num_threads != 1:
gateway_logger.info("Memory-only mode detected; forcing single worker to avoid divergent state")
num_threads = 1
except Exception:
pass
gateway_logger.info(f"Started doorman with {num_threads} threads on port {server_port}")
uvicorn.run(
"doorman:doorman",

25
scripts/smoke.sh Normal file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${BASE_URL:-http://localhost:5001}"
EMAIL="${STARTUP_ADMIN_EMAIL:-admin@localhost}"
PASSWORD="${STARTUP_ADMIN_PASSWORD:-password1}"
echo "[1/4] Checking liveness..."
curl -sfS "$BASE_URL/platform/monitor/liveness" | grep -q '"alive"' && echo "OK" || { echo "Liveness failed"; exit 1; }
echo "[2/4] Checking readiness..."
curl -sfS "$BASE_URL/platform/monitor/readiness" | grep -q '"ready"' && echo "OK" || { echo "Readiness degraded"; exit 1; }
echo "[3/4] Logging in..."
COOKIE_JAR="$(mktemp)"
trap 'rm -f "$COOKIE_JAR"' EXIT
curl -sfS -c "$COOKIE_JAR" -H 'Content-Type: application/json' \
-d "{\"email\":\"$EMAIL\",\"password\":\"$PASSWORD\"}" \
"$BASE_URL/platform/authorization" > /dev/null && echo "OK" || { echo "Login failed"; exit 1; }
echo "[4/4] Verifying token status..."
curl -sfS -b "$COOKIE_JAR" "$BASE_URL/platform/authorization/status" | grep -q '"Token is valid"' && echo "OK" || { echo "Token status check failed"; exit 1; }
echo "Smoke checks passed."