Security hardening: CORS lockdown, rate limiting middleware con sliding window e eviction IP stale, security headers (CSP, HSTS, X-Frame-Options), session cookie hardening, filename sanitization upload. i18n completion: internazionalizzati barcode.js e csv-export.js con bridge window.BARCODE_I18N/CSV_I18N, aggiornati .po IT/EN con 27 nuove stringhe. Tablet UX: touch target 44px per dispositivi coarse pointer. Test suite: 101 test totali (76 server + 25 client), copertura completa di tutti i router API, autenticazione, ruoli, CRUD, SPC, file upload, security integration. Infrastruttura SQLite async in-memory con fixtures. Fix critici: MissingGreenlet in recipe_service (selectinload eager), route ordering tasks.py, auth_service bcrypt diretto, Measurement.id Integer per SQLite. Documentazione: API.md (riferimento completo 40+ endpoint), DEPLOYMENT.md (guida produzione con Docker/Nginx/SSL), USER_GUIDE.md (manuale utente per ruolo). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
16 KiB
TieMeasureFlow Deployment Guide
This guide covers deploying TieMeasureFlow in development, staging, and production environments.
Table of Contents
- Prerequisites
- Environment Setup
- Database Setup
- Server Deployment
- Client Deployment
- Production Deployment
- Docker Deployment
- SSL/HTTPS
- Backup Strategy
- 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
# Python 3.11+
python3 --version
# MySQL 8.0
mysql --version
# Node.js (optional)
node --version
npm --version
Windows
Download and install:
- Python 3.11+
- MySQL Community Server
- Node.js (optional)
Environment Setup
1. Clone Repository
git clone <repository-url>
cd TieMeasureFlow
2. Create .env File
Copy the example and customize for your environment:
cp .env.example .env
3. Configure .env
Edit .env with your settings:
# =====================================================================
# 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 |
MAX_UPLOAD_SIZE_MB |
int | 50 | Maximum upload file size in MB |
RATE_LIMIT_LOGIN |
int | 5 | Login requests per minute |
RATE_LIMIT_GENERAL |
int | 100 | General requests per minute |
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
mysql -u root -p
-- 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
cd server
pip install -r requirements.txt
alembic upgrade head
This creates all required tables:
users- System usersrecipes- Measurement recipesrecipe_versions- Immutable recipe versionsrecipe_tasks- Tasks within recipesrecipe_subtasks- Subtasks within tasks (individual measurements)measurements- Recorded measurementsaccess_logs- API access audit trailsystem_settings- Configuration key-value pairsrecipe_version_audit- Recipe change history
3. Create Initial Admin User (Optional)
Use the Flask client to create the first admin user, or run:
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
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:
pip install gunicorn
Start server:
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:
pip install waitress
Start server:
cd server
waitress-serve --host=0.0.0.0 --port=8000 main:app
systemd Service (Linux)
Create /etc/systemd/system/tiemeasureflow-api.service:
[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:
sudo systemctl daemon-reload
sudo systemctl enable tiemeasureflow-api.service
sudo systemctl start tiemeasureflow-api.service
sudo systemctl status tiemeasureflow-api.service
View logs:
sudo journalctl -u tiemeasureflow-api -f
Client Deployment
Development Server
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)
cd client
npx tailwindcss -i static/css/input.css -o static/css/tailwind.css --watch
Production Server (Gunicorn)
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:
[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:
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:
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:
# 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
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:
docker build -f server/Dockerfile -t tiemeasureflow-api .
docker run -p 8000:8000 --env-file .env tiemeasureflow-api
Web Client
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:
docker build -f client/Dockerfile -t tiemeasureflow-web .
docker run -p 5000:5000 --env-file .env tiemeasureflow-web
SSL/TLS
Self-Signed Certificate (Development)
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes
Let's Encrypt (Production)
sudo apt install certbot python3-certbot-nginx
sudo certbot certonly --nginx -d yourdomain.com
Configure in .env
SSL_CERTFILE=/etc/ssl/certs/yourdomain.com.crt
SSL_KEYFILE=/etc/ssl/private/yourdomain.com.key
Start Server with SSL
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:
#!/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:
crontab -e
Add line:
0 2 * * * /opt/tiemeasureflow/backup.sh >> /var/log/tiemeasureflow/backup.log 2>&1
Restore from Backup
# 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:
# 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:
# 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:
cd server
pip install -r requirements.txt
File Upload Not Working
/app/server/uploads: Permission denied
Solution:
# 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:
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:
# 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:
[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:
sudo systemctl restart mysql
Application Workers
Increase Gunicorn workers (rule: 2 * CPU_cores + 1):
gunicorn --workers 9 ... # For 4-core system
Nginx Caching
Add to nginx config:
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
# 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
curl http://localhost:8000/api/health
Response:
{
"status": "ok",
"service": "TieMeasureFlow API",
"version": "0.1.0"
}
Database Connection Pooling
Monitor with:
# MySQL connections
mysql -e "SHOW PROCESSLIST;" | grep tiemeasureflow