4de7d78b66
Stations were the headline V2.0.0 feature but had no user-facing
documentation outside the architecture page. Filled the gap across
the three operational docs.
USER_GUIDE.md
- New entries in "Key Concepts": Station and Station assignment.
- New "Recipes you see are filtered by station" subsection in the
MeasurementTec workflow, explaining why the Select Recipe page may
legitimately show fewer recipes than expected and what the
"Stazione non configurata" error means at the operator level.
- New "Station Management" section under Admin Workflow covering:
the mental model, station create/edit/delete, the two-column
recipe-assignment modal, the immutable-code rule, the role of the
ST-DEFAULT seed station, and the tablet deployment cheat sheet.
- Admin role description updated to mention stations.
DEPLOYMENT.md
- Environment Variables Reference: added STATION_CODE row and noted
that an empty value triggers the deliberate fail-fast HTTP 503 on
/measure/select. Updated RATE_LIMIT_GENERAL default (300, per the
V2.0.0 perf change). Clarified UPLOAD_DIR resolves against the
project root.
API.md
- New "Stations" endpoint section listing all eight routes with
request/response examples and the 401/403/404/409 error contract:
GET / POST /stations, GET /stations/{id}, PUT /stations/{id},
DELETE /stations/{id}, GET /stations/{id}/recipes,
GET /stations/by-code/{code}/recipes (the operator-facing one used
by the Flask client), POST /stations/{id}/recipes,
DELETE /stations/{id}/recipes/{recipe_id}.
- TOC updated with the new "Stations" anchor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
821 lines
17 KiB
Markdown
821 lines
17 KiB
Markdown
# TieMeasureFlow Deployment Guide
|
|
|
|
This guide covers deploying TieMeasureFlow in development, staging, and production environments.
|
|
|
|
## Table of Contents
|
|
|
|
1. [Prerequisites](#prerequisites)
|
|
2. [Environment Setup](#environment-setup)
|
|
3. [Database Setup](#database-setup)
|
|
4. [Server Deployment](#server-deployment)
|
|
5. [Client Deployment](#client-deployment)
|
|
6. [Production Deployment](#production-deployment)
|
|
7. [Docker Deployment](#docker-deployment)
|
|
8. [SSL/HTTPS](#ssltls)
|
|
9. [Backup Strategy](#backup-strategy)
|
|
10. [Troubleshooting](#troubleshooting)
|
|
|
|
## Prerequisites
|
|
|
|
### System Requirements
|
|
|
|
- **OS**: Linux, macOS, or Windows
|
|
- **Python**: 3.11 or higher
|
|
- **MySQL**: 8.0 or higher
|
|
- **Node.js**: 16+ (optional, for Tailwind CSS compilation)
|
|
- **Disk Space**: 500 MB minimum
|
|
- **RAM**: 2 GB minimum
|
|
|
|
### Software Installation
|
|
|
|
#### Linux/macOS
|
|
|
|
```bash
|
|
# Python 3.11+
|
|
python3 --version
|
|
|
|
# MySQL 8.0
|
|
mysql --version
|
|
|
|
# Node.js (optional)
|
|
node --version
|
|
npm --version
|
|
```
|
|
|
|
#### Windows
|
|
|
|
Download and install:
|
|
- [Python 3.11+](https://www.python.org/downloads/)
|
|
- [MySQL Community Server](https://dev.mysql.com/downloads/mysql/)
|
|
- [Node.js](https://nodejs.org/) (optional)
|
|
|
|
---
|
|
|
|
## Environment Setup
|
|
|
|
### 1. Clone Repository
|
|
|
|
```bash
|
|
git clone <repository-url>
|
|
cd TieMeasureFlow
|
|
```
|
|
|
|
### 2. Create .env File
|
|
|
|
Copy the example and customize for your environment:
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
### 3. Configure .env
|
|
|
|
Edit `.env` with your settings:
|
|
|
|
```env
|
|
# =====================================================================
|
|
# DATABASE
|
|
# =====================================================================
|
|
DB_HOST=localhost
|
|
DB_PORT=3306
|
|
DB_NAME=tiemeasureflow
|
|
DB_USER=tmflow
|
|
DB_PASSWORD=change_me_in_production
|
|
|
|
# =====================================================================
|
|
# SERVER (FastAPI)
|
|
# =====================================================================
|
|
SERVER_HOST=0.0.0.0
|
|
SERVER_PORT=8000
|
|
SERVER_SECRET_KEY=change-this-to-a-random-secret-key-with-32-chars
|
|
|
|
# =====================================================================
|
|
# CLIENT (Flask)
|
|
# =====================================================================
|
|
CLIENT_HOST=0.0.0.0
|
|
CLIENT_PORT=5000
|
|
|
|
# =====================================================================
|
|
# CORS
|
|
# =====================================================================
|
|
SERVER_CORS_ORIGINS=http://localhost:5000,http://127.0.0.1:5000
|
|
|
|
# =====================================================================
|
|
# FILE UPLOAD
|
|
# =====================================================================
|
|
UPLOAD_DIR=uploads
|
|
MAX_UPLOAD_SIZE_MB=50
|
|
|
|
# =====================================================================
|
|
# RATE LIMITING
|
|
# =====================================================================
|
|
RATE_LIMIT_LOGIN=5
|
|
RATE_LIMIT_GENERAL=100
|
|
|
|
# =====================================================================
|
|
# SSL/HTTPS (Production only)
|
|
# =====================================================================
|
|
SSL_CERTFILE=
|
|
SSL_KEYFILE=
|
|
```
|
|
|
|
### Environment Variables Reference
|
|
|
|
| Variable | Type | Default | Description |
|
|
|----------|------|---------|-------------|
|
|
| `DB_HOST` | string | localhost | MySQL server hostname |
|
|
| `DB_PORT` | int | 3306 | MySQL server port |
|
|
| `DB_NAME` | string | tiemeasureflow | Database name |
|
|
| `DB_USER` | string | tmflow | Database user |
|
|
| `DB_PASSWORD` | string | change_me_in_production | Database password **[CHANGE IN PROD]** |
|
|
| `SERVER_HOST` | string | 0.0.0.0 | API server bind address |
|
|
| `SERVER_PORT` | int | 8000 | API server port |
|
|
| `SERVER_SECRET_KEY` | string | change-this-to... | Secret key for sessions **[CHANGE IN PROD]** |
|
|
| `CLIENT_HOST` | string | 0.0.0.0 | Flask client bind address |
|
|
| `CLIENT_PORT` | int | 5000 | Flask client port |
|
|
| `SERVER_CORS_ORIGINS` | string | http://localhost:5000 | Comma-separated CORS origins |
|
|
| `UPLOAD_DIR` | string | uploads | Directory for file uploads (resolved against the project root) |
|
|
| `MAX_UPLOAD_SIZE_MB` | int | 50 | Maximum upload file size in MB |
|
|
| `RATE_LIMIT_LOGIN` | int | 5 | Login requests per minute, per real client IP |
|
|
| `RATE_LIMIT_GENERAL` | int | 300 | General requests per minute, per real client IP (post-V2.0.0; was 100 in V1.0.x) |
|
|
| `STATION_CODE` | string | (empty) | **Per-tablet** code identifying the station this Flask client serves. Must match a station created in the admin UI. Empty = the client refuses `/measure/select` with HTTP 503 "Stazione non configurata". |
|
|
| `SSL_CERTFILE` | string | (empty) | Path to SSL certificate (production) |
|
|
| `SSL_KEYFILE` | string | (empty) | Path to SSL private key (production) |
|
|
|
|
---
|
|
|
|
## Database Setup
|
|
|
|
### 1. Create MySQL Database and User
|
|
|
|
```bash
|
|
mysql -u root -p
|
|
```
|
|
|
|
```sql
|
|
-- Create database
|
|
CREATE DATABASE tiemeasureflow CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
|
|
-- Create user
|
|
CREATE USER 'tmflow'@'localhost' IDENTIFIED BY 'secure_password_here';
|
|
|
|
-- Grant permissions
|
|
GRANT ALL PRIVILEGES ON tiemeasureflow.* TO 'tmflow'@'localhost';
|
|
FLUSH PRIVILEGES;
|
|
|
|
-- Exit
|
|
EXIT;
|
|
```
|
|
|
|
### 2. Run Database Migrations
|
|
|
|
```bash
|
|
cd server
|
|
pip install -r requirements.txt
|
|
alembic upgrade head
|
|
```
|
|
|
|
This creates all required tables:
|
|
- `users` - System users
|
|
- `recipes` - Measurement recipes
|
|
- `recipe_versions` - Immutable recipe versions
|
|
- `recipe_tasks` - Tasks within recipes
|
|
- `recipe_subtasks` - Subtasks within tasks (individual measurements)
|
|
- `measurements` - Recorded measurements
|
|
- `access_logs` - API access audit trail
|
|
- `system_settings` - Configuration key-value pairs
|
|
- `recipe_version_audit` - Recipe change history
|
|
|
|
### 3. Create Initial Admin User (Optional)
|
|
|
|
Use the Flask client to create the first admin user, or run:
|
|
|
|
```bash
|
|
cd server
|
|
python -c "
|
|
from database import init_db, SessionLocal
|
|
from services.auth_service import create_user
|
|
import asyncio
|
|
|
|
async def init():
|
|
await init_db()
|
|
async with SessionLocal() as db:
|
|
await create_user(
|
|
db,
|
|
username='admin',
|
|
password='change_me_first_login',
|
|
display_name='Admin',
|
|
email='admin@example.com',
|
|
roles=['Maker', 'MeasurementTec', 'Metrologist'],
|
|
is_admin=True
|
|
)
|
|
await db.commit()
|
|
print('Admin user created: admin / change_me_first_login')
|
|
|
|
asyncio.run(init())
|
|
"
|
|
```
|
|
|
|
---
|
|
|
|
## Server Deployment
|
|
|
|
### Development Server
|
|
|
|
```bash
|
|
cd server
|
|
pip install -r requirements.txt
|
|
uvicorn main:app --reload --host 0.0.0.0 --port 8000
|
|
```
|
|
|
|
Runs at `http://0.0.0.0:8000`
|
|
|
|
API Documentation:
|
|
- Swagger UI: `http://localhost:8000/docs`
|
|
- ReDoc: `http://localhost:8000/redoc`
|
|
|
|
### Production Server (Gunicorn + Uvicorn)
|
|
|
|
Install Gunicorn:
|
|
|
|
```bash
|
|
pip install gunicorn
|
|
```
|
|
|
|
Start server:
|
|
|
|
```bash
|
|
cd server
|
|
gunicorn main:app \
|
|
--workers 4 \
|
|
--worker-class uvicorn.workers.UvicornWorker \
|
|
--bind 0.0.0.0:8000 \
|
|
--access-logfile /var/log/tiemeasureflow/access.log \
|
|
--error-logfile /var/log/tiemeasureflow/error.log \
|
|
--log-level info
|
|
```
|
|
|
|
### Production Server (Waitress - Windows)
|
|
|
|
Install Waitress:
|
|
|
|
```bash
|
|
pip install waitress
|
|
```
|
|
|
|
Start server:
|
|
|
|
```bash
|
|
cd server
|
|
waitress-serve --host=0.0.0.0 --port=8000 main:app
|
|
```
|
|
|
|
### systemd Service (Linux)
|
|
|
|
Create `/etc/systemd/system/tiemeasureflow-api.service`:
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=TieMeasureFlow API Server
|
|
After=network.target mysql.service
|
|
|
|
[Service]
|
|
Type=notify
|
|
User=tiemeasureflow
|
|
WorkingDirectory=/opt/tiemeasureflow/server
|
|
Environment="PATH=/opt/tiemeasureflow/venv/bin"
|
|
ExecStart=/opt/tiemeasureflow/venv/bin/gunicorn main:app \
|
|
--workers 4 \
|
|
--worker-class uvicorn.workers.UvicornWorker \
|
|
--bind 0.0.0.0:8000
|
|
|
|
Restart=on-failure
|
|
RestartSec=10
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
Enable and start:
|
|
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable tiemeasureflow-api.service
|
|
sudo systemctl start tiemeasureflow-api.service
|
|
sudo systemctl status tiemeasureflow-api.service
|
|
```
|
|
|
|
View logs:
|
|
|
|
```bash
|
|
sudo journalctl -u tiemeasureflow-api -f
|
|
```
|
|
|
|
---
|
|
|
|
## Client Deployment
|
|
|
|
### Development Server
|
|
|
|
```bash
|
|
cd client
|
|
pip install -r requirements.txt
|
|
flask run --host 0.0.0.0 --port 5000
|
|
```
|
|
|
|
Runs at `http://0.0.0.0:5000`
|
|
|
|
### Compile Tailwind CSS (Optional)
|
|
|
|
```bash
|
|
cd client
|
|
npx tailwindcss -i static/css/input.css -o static/css/tailwind.css --watch
|
|
```
|
|
|
|
### Production Server (Gunicorn)
|
|
|
|
```bash
|
|
pip install gunicorn
|
|
cd client
|
|
gunicorn --workers 4 --bind 0.0.0.0:5000 app:app
|
|
```
|
|
|
|
### systemd Service (Linux)
|
|
|
|
Create `/etc/systemd/system/tiemeasureflow-web.service`:
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=TieMeasureFlow Web Client
|
|
After=network.target tiemeasureflow-api.service
|
|
|
|
[Service]
|
|
Type=notify
|
|
User=tiemeasureflow
|
|
WorkingDirectory=/opt/tiemeasureflow/client
|
|
Environment="PATH=/opt/tiemeasureflow/venv/bin"
|
|
Environment="FLASK_ENV=production"
|
|
ExecStart=/opt/tiemeasureflow/venv/bin/gunicorn \
|
|
--workers 4 \
|
|
--bind 0.0.0.0:5000 \
|
|
app:app
|
|
|
|
Restart=on-failure
|
|
RestartSec=10
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
---
|
|
|
|
## Production Deployment
|
|
|
|
### Security Checklist
|
|
|
|
- [ ] Change all default passwords in `.env`
|
|
- [ ] Generate random `SERVER_SECRET_KEY` (32+ characters)
|
|
- [ ] Set CORS origins to actual client domains
|
|
- [ ] Enable SSL/HTTPS (see SSL/TLS section)
|
|
- [ ] Configure firewall rules
|
|
- [ ] Set up regular database backups
|
|
- [ ] Configure log rotation
|
|
- [ ] Use strong database credentials
|
|
- [ ] Restrict file upload sizes
|
|
- [ ] Keep dependencies updated
|
|
|
|
### Recommended Architecture
|
|
|
|
```
|
|
┌─────────────────┐
|
|
│ Nginx Proxy │ (Port 80/443)
|
|
└────────┬────────┘
|
|
│
|
|
┌────┴────┐
|
|
│ │
|
|
┌───▼──┐ ┌───▼──┐
|
|
│API │ │Web │
|
|
│:8000 │ │:5000 │
|
|
└──────┘ └──────┘
|
|
│ │
|
|
└────┬─────┘
|
|
│
|
|
┌────▼────┐
|
|
│ MySQL │
|
|
│ :3306 │
|
|
└─────────┘
|
|
```
|
|
|
|
### Nginx Configuration
|
|
|
|
Create `/etc/nginx/sites-available/tiemeasureflow`:
|
|
|
|
```nginx
|
|
upstream tiemeasureflow_api {
|
|
server 127.0.0.1:8000;
|
|
}
|
|
|
|
upstream tiemeasureflow_web {
|
|
server 127.0.0.1:5000;
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
server_name yourdomain.com;
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name yourdomain.com;
|
|
|
|
ssl_certificate /etc/ssl/certs/yourdomain.com.crt;
|
|
ssl_certificate_key /etc/ssl/private/yourdomain.com.key;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
|
|
|
client_max_body_size 50M;
|
|
|
|
# API
|
|
location /api/ {
|
|
proxy_pass http://tiemeasureflow_api;
|
|
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;
|
|
}
|
|
|
|
# Web Client
|
|
location / {
|
|
proxy_pass http://tiemeasureflow_web;
|
|
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;
|
|
}
|
|
|
|
# Static files (optional caching)
|
|
location /static/ {
|
|
proxy_pass http://tiemeasureflow_web;
|
|
expires 1d;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
}
|
|
```
|
|
|
|
Enable and test:
|
|
|
|
```bash
|
|
sudo ln -s /etc/nginx/sites-available/tiemeasureflow /etc/nginx/sites-enabled/
|
|
sudo nginx -t
|
|
sudo systemctl restart nginx
|
|
```
|
|
|
|
---
|
|
|
|
## Docker Deployment
|
|
|
|
### Docker Compose (Complete Stack)
|
|
|
|
The project includes a `docker-compose.yml` for easy deployment:
|
|
|
|
```bash
|
|
# Build images
|
|
docker-compose build
|
|
|
|
# Start services
|
|
docker-compose up -d
|
|
|
|
# View logs
|
|
docker-compose logs -f
|
|
|
|
# Stop services
|
|
docker-compose down
|
|
```
|
|
|
|
### Manual Docker Build
|
|
|
|
#### API Server
|
|
|
|
```dockerfile
|
|
FROM python:3.11-slim
|
|
|
|
WORKDIR /app/server
|
|
|
|
COPY server/requirements.txt .
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
|
|
COPY server/ .
|
|
COPY .env ..
|
|
|
|
EXPOSE 8000
|
|
|
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
```
|
|
|
|
Build and run:
|
|
|
|
```bash
|
|
docker build -f server/Dockerfile -t tiemeasureflow-api .
|
|
docker run -p 8000:8000 --env-file .env tiemeasureflow-api
|
|
```
|
|
|
|
#### Web Client
|
|
|
|
```dockerfile
|
|
FROM python:3.11-slim
|
|
|
|
WORKDIR /app/client
|
|
|
|
COPY client/requirements.txt .
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
|
|
COPY client/ .
|
|
COPY .env ..
|
|
|
|
EXPOSE 5000
|
|
|
|
CMD ["gunicorn", "--workers", "4", "--bind", "0.0.0.0:5000", "app:app"]
|
|
```
|
|
|
|
Build and run:
|
|
|
|
```bash
|
|
docker build -f client/Dockerfile -t tiemeasureflow-web .
|
|
docker run -p 5000:5000 --env-file .env tiemeasureflow-web
|
|
```
|
|
|
|
---
|
|
|
|
## SSL/TLS
|
|
|
|
### Self-Signed Certificate (Development)
|
|
|
|
```bash
|
|
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes
|
|
```
|
|
|
|
### Let's Encrypt (Production)
|
|
|
|
```bash
|
|
sudo apt install certbot python3-certbot-nginx
|
|
sudo certbot certonly --nginx -d yourdomain.com
|
|
```
|
|
|
|
### Configure in .env
|
|
|
|
```env
|
|
SSL_CERTFILE=/etc/ssl/certs/yourdomain.com.crt
|
|
SSL_KEYFILE=/etc/ssl/private/yourdomain.com.key
|
|
```
|
|
|
|
### Start Server with SSL
|
|
|
|
```bash
|
|
cd server
|
|
gunicorn main:app \
|
|
--workers 4 \
|
|
--worker-class uvicorn.workers.UvicornWorker \
|
|
--bind 0.0.0.0:8443 \
|
|
--certfile /etc/ssl/certs/yourdomain.com.crt \
|
|
--keyfile /etc/ssl/private/yourdomain.com.key
|
|
```
|
|
|
|
---
|
|
|
|
## Backup Strategy
|
|
|
|
### Daily Database Backups
|
|
|
|
Create backup script `/opt/tiemeasureflow/backup.sh`:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
|
|
BACKUP_DIR="/opt/tiemeasureflow/backups"
|
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
DB_NAME="tiemeasureflow"
|
|
DB_USER="tmflow"
|
|
|
|
mkdir -p $BACKUP_DIR
|
|
|
|
# MySQL dump
|
|
mysqldump -u $DB_USER -p$MYSQL_PASSWORD $DB_NAME \
|
|
| gzip > $BACKUP_DIR/db_$TIMESTAMP.sql.gz
|
|
|
|
# Keep only last 30 days
|
|
find $BACKUP_DIR -name "db_*.sql.gz" -mtime +30 -delete
|
|
|
|
# Upload to S3 (optional)
|
|
# aws s3 cp $BACKUP_DIR/db_$TIMESTAMP.sql.gz s3://your-bucket/backups/
|
|
|
|
echo "Backup completed: $BACKUP_DIR/db_$TIMESTAMP.sql.gz"
|
|
```
|
|
|
|
Schedule with crontab:
|
|
|
|
```bash
|
|
crontab -e
|
|
```
|
|
|
|
Add line:
|
|
|
|
```cron
|
|
0 2 * * * /opt/tiemeasureflow/backup.sh >> /var/log/tiemeasureflow/backup.log 2>&1
|
|
```
|
|
|
|
### Restore from Backup
|
|
|
|
```bash
|
|
# Decompress
|
|
gunzip < backups/db_20250207_020000.sql.gz | \
|
|
mysql -u tmflow -p tiemeasureflow
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Database Connection Failed
|
|
|
|
```
|
|
sqlalchemy.exc.OperationalError: (asyncmy.errors.ProgrammingError) (2003, "Can't connect to MySQL server...")
|
|
```
|
|
|
|
**Check:**
|
|
```bash
|
|
# Verify MySQL is running
|
|
mysql -u tmflow -p -e "SELECT 1"
|
|
|
|
# Check .env variables
|
|
grep DB_ .env
|
|
|
|
# Test connection string
|
|
cd server && python -c "from config import settings; print(settings.database_url)"
|
|
```
|
|
|
|
### Port Already in Use
|
|
|
|
```
|
|
OSError: [Errno 48] Address already in use
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Find process on port 8000
|
|
lsof -i :8000
|
|
kill -9 <PID>
|
|
|
|
# Or use different port
|
|
uvicorn main:app --port 8001
|
|
```
|
|
|
|
### Import Errors
|
|
|
|
```
|
|
ModuleNotFoundError: No module named 'fastapi'
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
cd server
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
### File Upload Not Working
|
|
|
|
```
|
|
/app/server/uploads: Permission denied
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Create uploads directory with correct permissions
|
|
mkdir -p server/uploads
|
|
chmod 755 server/uploads
|
|
```
|
|
|
|
### Migrations Failed
|
|
|
|
```
|
|
alembic.util.exc.CommandError: Can't locate revision identified by...
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
cd server
|
|
# Check migration status
|
|
alembic current
|
|
|
|
# Reset migrations (CAUTION - deletes data)
|
|
alembic downgrade base
|
|
alembic upgrade head
|
|
```
|
|
|
|
### API Key Not Working
|
|
|
|
```
|
|
detail: "Invalid API key"
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Regenerate API key for user (admin endpoint)
|
|
curl -X POST http://localhost:8000/api/users/1/regenerate-key \
|
|
-H "X-API-Key: admin_token"
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Tuning
|
|
|
|
### MySQL
|
|
|
|
Edit `/etc/mysql/mysql.conf.d/mysqld.cnf`:
|
|
|
|
```ini
|
|
[mysqld]
|
|
# Connection pool
|
|
max_connections = 100
|
|
|
|
# Buffer sizes
|
|
innodb_buffer_pool_size = 256M
|
|
innodb_log_file_size = 100M
|
|
|
|
# Query optimization
|
|
query_cache_size = 0
|
|
query_cache_type = 0
|
|
|
|
# Logging
|
|
slow_query_log = 1
|
|
long_query_time = 2
|
|
```
|
|
|
|
Restart:
|
|
```bash
|
|
sudo systemctl restart mysql
|
|
```
|
|
|
|
### Application Workers
|
|
|
|
Increase Gunicorn workers (rule: 2 * CPU_cores + 1):
|
|
|
|
```bash
|
|
gunicorn --workers 9 ... # For 4-core system
|
|
```
|
|
|
|
### Nginx Caching
|
|
|
|
Add to nginx config:
|
|
|
|
```nginx
|
|
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m;
|
|
|
|
location /api/statistics/ {
|
|
proxy_cache api_cache;
|
|
proxy_cache_valid 200 10m;
|
|
add_header X-Cache-Status $upstream_cache_status;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Monitoring & Logging
|
|
|
|
### Application Logs
|
|
|
|
```bash
|
|
# API server
|
|
tail -f /var/log/tiemeasureflow/api_error.log
|
|
|
|
# Web client
|
|
tail -f /var/log/tiemeasureflow/web_error.log
|
|
|
|
# System
|
|
journalctl -u tiemeasureflow-api -f
|
|
```
|
|
|
|
### Health Check Endpoint
|
|
|
|
```bash
|
|
curl http://localhost:8000/api/health
|
|
```
|
|
|
|
Response:
|
|
```json
|
|
{
|
|
"status": "ok",
|
|
"service": "TieMeasureFlow API",
|
|
"version": "0.1.0"
|
|
}
|
|
```
|
|
|
|
### Database Connection Pooling
|
|
|
|
Monitor with:
|
|
|
|
```bash
|
|
# MySQL connections
|
|
mysql -e "SHOW PROCESSLIST;" | grep tiemeasureflow
|
|
```
|