- Add Cloudflare Turnstile to login, register, and password reset forms - Switch AI provider to LiteLLM, transcription to OpenAI Whisper - Change domain to scribe.pedshub.com - Fix PPTX export: add tables, bold/italic, numbered lists, code blocks, blockquotes - Fix announcement banner close button (CSP was blocking inline onclick) - Fix auth middleware: empty Bearer token now falls through to cookie auth - Fix audio backups: only save on transcription failure, stop auto-deleting on success - Soften AI correction injection to prevent model hallucination from correction history - Fix LiteLLM TTS model name handling (no incorrect openai/ prefix) - Expand AI instructions textarea in Learning Hub CMS - Update README for v6 with all features and providers - Add comprehensive docs/: architecture, API reference, database schema, authentication, AI providers, speech, learning hub, configuration, deployment
189 lines
4.6 KiB
Markdown
189 lines
4.6 KiB
Markdown
# Deployment Guide
|
|
|
|
## Requirements
|
|
|
|
- Docker and Docker Compose
|
|
- PostgreSQL 16 with pgvector extension (included in pgvector/pgvector:pg16 image)
|
|
- Reverse proxy (Nginx, Caddy, or Traefik) for HTTPS termination
|
|
- At least one AI provider configured
|
|
|
|
## Docker Deployment
|
|
|
|
### 1. Configure Environment
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
Required settings:
|
|
|
|
```env
|
|
AI_PROVIDER=litellm # or bedrock, azure, vertex, openrouter
|
|
JWT_SECRET=<64-char random> # openssl rand -hex 32
|
|
DB_PASSWORD=<strong password>
|
|
APP_URL=https://your-domain.com # used for CORS, emails, verification links
|
|
```
|
|
|
|
### 2. Build and Start
|
|
|
|
```bash
|
|
docker compose up -d --build
|
|
```
|
|
|
|
This starts two containers:
|
|
- `pediatric-ai-scribe` -- Node.js app on port 3552 (mapped to container port 3000)
|
|
- `pedscribe-db` -- PostgreSQL 16 with pgvector
|
|
|
|
### 3. Verify
|
|
|
|
```bash
|
|
docker compose ps
|
|
curl http://localhost:3552/api/health
|
|
```
|
|
|
|
### 4. First User
|
|
|
|
Navigate to `https://your-domain.com` and register. The first user is automatically promoted to admin.
|
|
|
|
## Reverse Proxy
|
|
|
|
The app binds to `127.0.0.1:3552` by default. You need a reverse proxy for HTTPS.
|
|
|
|
### Nginx
|
|
|
|
```nginx
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name scribe.example.com;
|
|
|
|
ssl_certificate /etc/ssl/certs/scribe.example.com.pem;
|
|
ssl_certificate_key /etc/ssl/private/scribe.example.com.key;
|
|
|
|
client_max_body_size 100M;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3552;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Caddy
|
|
|
|
```
|
|
scribe.example.com {
|
|
reverse_proxy localhost:3552
|
|
}
|
|
```
|
|
|
|
### Trust Proxy
|
|
|
|
If you see `X-Forwarded-For` warnings in logs, add `trust proxy` to Express. The app currently runs behind a local reverse proxy, so rate limiting uses the direct connection IP.
|
|
|
|
## Volumes
|
|
|
|
| Volume | Purpose | Backup Priority |
|
|
|--------|---------|----------------|
|
|
| `pgdata` | PostgreSQL data (all user data, settings, content) | Critical |
|
|
| `scribe-logs` | Application log files (YYYY-MM-DD.log) | Low |
|
|
|
|
### Backup PostgreSQL
|
|
|
|
```bash
|
|
docker exec pedscribe-db pg_dump -U pedscribe pedscribe > backup.sql
|
|
```
|
|
|
|
### Restore
|
|
|
|
```bash
|
|
cat backup.sql | docker exec -i pedscribe-db psql -U pedscribe pedscribe
|
|
```
|
|
|
|
## Updating
|
|
|
|
```bash
|
|
git pull
|
|
docker compose build --no-cache
|
|
docker compose up -d
|
|
```
|
|
|
|
Database migrations run automatically on startup (CREATE TABLE IF NOT EXISTS, ALTER TABLE ADD COLUMN IF NOT EXISTS patterns).
|
|
|
|
## Health Check
|
|
|
|
The container includes a built-in health check:
|
|
|
|
```
|
|
wget --spider -q http://localhost:3000/api/health
|
|
```
|
|
|
|
Runs every 30 seconds with a 20-second start period. Docker marks the container as healthy/unhealthy automatically.
|
|
|
|
## Resource Requirements
|
|
|
|
- Memory: 256MB minimum, 512MB recommended
|
|
- Disk: ~200MB for Docker image (includes self-hosted Whisper WASM models)
|
|
- PostgreSQL: depends on usage (audio backups use BYTEA storage, auto-deleted after 24h)
|
|
|
|
## Environment-Specific Notes
|
|
|
|
### Production Checklist
|
|
|
|
- Set a strong `JWT_SECRET` (64+ characters)
|
|
- Set a strong `DB_PASSWORD`
|
|
- Set `APP_URL` to your actual domain (required for CORS, email links)
|
|
- Configure SMTP for email verification and password reset
|
|
- Use a HIPAA-eligible AI provider if handling PHI (Bedrock, Azure, Vertex)
|
|
- Enable Cloudflare Turnstile for bot protection
|
|
- Set up regular PostgreSQL backups
|
|
- Configure OIDC/SSO for enterprise environments
|
|
|
|
### Development
|
|
|
|
```bash
|
|
npm install
|
|
cp .env.example .env
|
|
# Start PostgreSQL separately or use docker compose for just the DB:
|
|
docker compose up -d postgres
|
|
node server.js
|
|
```
|
|
|
|
The app runs on port 3000 by default. Without `APP_URL` set, CORS allows all origins.
|
|
|
|
## Ports
|
|
|
|
| Service | Internal | External (default) |
|
|
|---------|----------|--------------------|
|
|
| Node.js app | 3000 | 127.0.0.1:3552 |
|
|
| PostgreSQL | 5432 | Not exposed |
|
|
|
|
To change the external port, edit `docker-compose.yml`:
|
|
|
|
```yaml
|
|
ports:
|
|
- "127.0.0.1:YOUR_PORT:3000"
|
|
```
|
|
|
|
## Logs
|
|
|
|
Application logs are written to:
|
|
- Console (visible via `docker compose logs`)
|
|
- `/app/data/logs/YYYY-MM-DD.log` inside the container (mapped to `scribe-logs` volume)
|
|
- Database tables: `audit_log`, `api_log`, `access_log`
|
|
|
|
View logs:
|
|
|
|
```bash
|
|
docker compose logs -f pediatric-scribe
|
|
docker compose logs --since=1h pediatric-scribe
|
|
```
|
|
|
|
## Auto-Cleanup
|
|
|
|
The application automatically cleans up expired data:
|
|
- Saved encounters: deleted after 7 days (configurable via `site.auto_delete_days`)
|
|
- Audio backups: deleted after 24 hours
|
|
- Cleanup runs hourly and 10 seconds after startup
|