initial commit
This commit is contained in:
18
deploy/fluentgerman.service
Normal file
18
deploy/fluentgerman.service
Normal file
@@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=FluentGerman.ai — Personalized LLM Language Learning
|
||||
After=network.target postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=fluentgerman
|
||||
Group=fluentgerman
|
||||
WorkingDirectory=/opt/fluentgerman/backend
|
||||
Environment="PATH=/opt/fluentgerman/backend/venv/bin"
|
||||
ExecStart=/opt/fluentgerman/backend/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8999 --workers 2
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
38
deploy/nginx.conf
Normal file
38
deploy/nginx.conf
Normal file
@@ -0,0 +1,38 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _; # Replace with your domain
|
||||
|
||||
# Frontend static files
|
||||
location / {
|
||||
root /opt/fluentgerman/frontend;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# API proxy
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
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;
|
||||
|
||||
# SSE support (for chat streaming)
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
||||
|
||||
# Gzip
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript;
|
||||
gzip_min_length 256;
|
||||
|
||||
# Upload size (for voice audio)
|
||||
client_max_body_size 25M;
|
||||
}
|
||||
78
deploy/nginx.conf.example
Normal file
78
deploy/nginx.conf.example
Normal file
@@ -0,0 +1,78 @@
|
||||
# FluentGerman.ai — Nginx Configuration Template
|
||||
# Copy to /etc/nginx/sites-available/fluentgerman and adjust values marked with <...>
|
||||
#
|
||||
# Setup steps:
|
||||
# 1. sudo cp deploy/nginx.conf.example /etc/nginx/sites-available/fluentgerman
|
||||
# 2. Edit the file: replace <YOUR_DOMAIN> and <APP_PORT>
|
||||
# 3. sudo ln -sf /etc/nginx/sites-available/fluentgerman /etc/nginx/sites-enabled/
|
||||
# 4. sudo nginx -t && sudo systemctl reload nginx
|
||||
# 5. Install SSL: sudo certbot --nginx -d <YOUR_DOMAIN>
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name fluentgerman.mydomain.io; # ← Replace with your subdomain
|
||||
|
||||
# Redirect HTTP → HTTPS (uncomment after certbot setup)
|
||||
# return 301 https://$host$request_uri;
|
||||
|
||||
# ── Frontend static files ──────────────────────────────────────
|
||||
root /opt/fluentgerman/frontend;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# ── API reverse proxy ──────────────────────────────────────────
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:8999; # ← Must match APP_PORT in .env
|
||||
proxy_http_version 1.1;
|
||||
|
||||
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;
|
||||
proxy_set_header Connection '';
|
||||
|
||||
# SSE support (critical for chat streaming)
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 300s;
|
||||
chunked_transfer_encoding on;
|
||||
}
|
||||
|
||||
# ── Security headers ───────────────────────────────────────────
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy strict-origin-when-cross-origin always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# ── Performance ────────────────────────────────────────────────
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml;
|
||||
gzip_min_length 256;
|
||||
gzip_vary on;
|
||||
|
||||
# Upload size limit (voice audio files)
|
||||
client_max_body_size 25M;
|
||||
|
||||
# Static file caching
|
||||
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
}
|
||||
|
||||
# ── HTTPS block (auto-generated by certbot, shown for reference) ───
|
||||
# server {
|
||||
# listen 443 ssl http2;
|
||||
# server_name fluentgerman.mydomain.io;
|
||||
#
|
||||
# ssl_certificate /etc/letsencrypt/live/fluentgerman.mydomain.io/fullchain.pem;
|
||||
# ssl_certificate_key /etc/letsencrypt/live/fluentgerman.mydomain.io/privkey.pem;
|
||||
# include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
# ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
#
|
||||
# # ... (same location blocks as above)
|
||||
# }
|
||||
76
deploy/setup.sh
Normal file
76
deploy/setup.sh
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
# FluentGerman.ai — Debian deployment setup script
|
||||
# Run as root or with sudo
|
||||
|
||||
set -e
|
||||
|
||||
APP_NAME="fluentgerman"
|
||||
APP_DIR="/opt/$APP_NAME"
|
||||
APP_USER="fluentgerman"
|
||||
DB_NAME="fluentgerman"
|
||||
DB_USER="fluentgerman"
|
||||
|
||||
echo "=== FluentGerman.ai — Deployment Setup ==="
|
||||
|
||||
# 1. System user
|
||||
if ! id "$APP_USER" &>/dev/null; then
|
||||
useradd --system --no-create-home --shell /bin/false "$APP_USER"
|
||||
echo "✓ Created system user: $APP_USER"
|
||||
fi
|
||||
|
||||
# 2. Install Python if needed
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq python3 python3-venv python3-pip > /dev/null
|
||||
echo "✓ Python installed"
|
||||
|
||||
# 3. PostgreSQL database setup
|
||||
sudo -u postgres psql -tc "SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'" | grep -q 1 || \
|
||||
sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD 'CHANGE_ME';"
|
||||
|
||||
sudo -u postgres psql -tc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'" | grep -q 1 || \
|
||||
sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;"
|
||||
|
||||
echo "✓ PostgreSQL database ready"
|
||||
|
||||
# 4. Application directory
|
||||
mkdir -p "$APP_DIR"
|
||||
cp -r backend/* "$APP_DIR/backend/"
|
||||
cp -r frontend/* "$APP_DIR/frontend/"
|
||||
chown -R "$APP_USER:$APP_USER" "$APP_DIR"
|
||||
echo "✓ Files deployed to $APP_DIR"
|
||||
|
||||
# 5. Python virtual environment
|
||||
cd "$APP_DIR/backend"
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install --quiet -r requirements.txt
|
||||
deactivate
|
||||
echo "✓ Python venv created"
|
||||
|
||||
# 6. Environment file
|
||||
if [ ! -f "$APP_DIR/backend/.env" ]; then
|
||||
cp "$APP_DIR/backend/.env.example" "$APP_DIR/backend/.env"
|
||||
# Generate random secret key
|
||||
SECRET=$(python3 -c "import secrets; print(secrets.token_urlsafe(48))")
|
||||
sed -i "s/generate-a-strong-random-key-here/$SECRET/" "$APP_DIR/backend/.env"
|
||||
echo "⚠ Created .env from template — EDIT $APP_DIR/backend/.env with your API keys and passwords!"
|
||||
fi
|
||||
|
||||
# 7. Systemd service
|
||||
cp deploy/fluentgerman.service /etc/systemd/system/
|
||||
systemctl daemon-reload
|
||||
systemctl enable "$APP_NAME"
|
||||
systemctl start "$APP_NAME"
|
||||
echo "✓ Systemd service active"
|
||||
|
||||
# 8. Nginx config
|
||||
cp deploy/nginx.conf /etc/nginx/sites-available/$APP_NAME
|
||||
ln -sf /etc/nginx/sites-available/$APP_NAME /etc/nginx/sites-enabled/
|
||||
nginx -t && systemctl reload nginx
|
||||
echo "✓ Nginx configured"
|
||||
|
||||
echo ""
|
||||
echo "=== Deployment complete! ==="
|
||||
echo "1. Edit /opt/$APP_NAME/backend/.env with your settings"
|
||||
echo "2. Restart: systemctl restart $APP_NAME"
|
||||
echo "3. Access: http://your-server-domain"
|
||||
Reference in New Issue
Block a user