Skip to content

Commit 5f81a99

Browse files
jiajunagentclaude
andcommitted
feat: 部署脚本——Oracle Cloud Free Tier + Nginx + SSL
3 个脚本: 1. setup-server.sh — 系统初始化(Docker/Git/防火墙/Swap) 2. deploy.sh — 一键部署(.env/build/start/admin/invite codes) 3. setup-nginx.sh — Nginx 反向代理 + Let's Encrypt SSL Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6cd291e commit 5f81a99

4 files changed

Lines changed: 374 additions & 0 deletions

File tree

deploy/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# MetaboFlow Deployment Guide
2+
3+
## Prerequisites
4+
5+
- Oracle Cloud Free Tier account (or any Ubuntu 22.04 server with ≥8GB RAM)
6+
- Domain name pointing to server IP (e.g., ponytech.dev)
7+
- SSH access to server
8+
9+
## Quick Deploy (3 steps)
10+
11+
### Step 1: Setup server (run as root)
12+
13+
```bash
14+
ssh ubuntu@<SERVER_IP>
15+
sudo bash
16+
curl -fsSL https://raw.githubusercontent.com/ponytech-dev/MetaboFlow/main/deploy/setup-server.sh | bash
17+
```
18+
19+
### Step 2: Deploy MetaboFlow (run as ubuntu)
20+
21+
```bash
22+
cd /opt/metaboflow
23+
git clone https://github.com/ponytech-dev/MetaboFlow.git .
24+
export METABOFLOW_DOMAIN=ponytech.dev
25+
export ADMIN_EMAIL=jiajunagent@gmail.com
26+
bash deploy/deploy.sh
27+
```
28+
29+
First build takes 15-30 minutes (R packages compilation).
30+
31+
### Step 3: Setup Nginx + SSL (run as root)
32+
33+
```bash
34+
export METABOFLOW_DOMAIN=ponytech.dev
35+
export ADMIN_EMAIL=jiajunagent@gmail.com
36+
sudo bash deploy/setup-nginx.sh
37+
```
38+
39+
## Access
40+
41+
- Frontend: https://ponytech.dev
42+
- API docs: https://ponytech.dev/docs
43+
- Admin: jiajunagent@gmail.com / MetaboFlow2026! (change after first login)
44+
45+
## Server Requirements
46+
47+
| Scale | RAM | CPU | Disk | Monthly Cost |
48+
|-------|-----|-----|------|-------------|
49+
| 5 labs | 8GB | 4 cores | 100GB | Free (Oracle) |
50+
| 10 labs | 16-24GB | 4 cores | 200GB | Free (Oracle) |
51+
| 20+ labs | 32GB | 8 cores | 500GB | ~$40/mo |
52+
53+
## Maintenance
54+
55+
```bash
56+
# Update to latest version
57+
cd /opt/metaboflow
58+
git pull
59+
docker compose build --parallel
60+
docker compose up -d
61+
62+
# View logs
63+
docker compose logs -f backend
64+
docker compose logs -f xcms-worker
65+
66+
# Check service health
67+
docker compose ps
68+
```

deploy/deploy.sh

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/bin/bash
2+
# MetaboFlow Deployment Script
3+
# Run from the MetaboFlow repo root: bash deploy/deploy.sh
4+
set -euo pipefail
5+
6+
DOMAIN="${METABOFLOW_DOMAIN:-ponytech.dev}"
7+
ADMIN_EMAIL="${ADMIN_EMAIL:-jiajunagent@gmail.com}"
8+
SECRET_KEY=$(openssl rand -hex 32)
9+
10+
echo "=== MetaboFlow Deployment ==="
11+
echo "Domain: $DOMAIN"
12+
echo "Admin email: $ADMIN_EMAIL"
13+
14+
# 1. Create production .env
15+
echo ">>> Step 1: Create .env"
16+
cat > .env << EOF
17+
# MetaboFlow Production Environment
18+
FRONTEND_PORT=3005
19+
METABOFLOW_DATABASE_URL=postgresql://metaboflow:metaboflow@postgres:5432/metaboflow
20+
METABOFLOW_REDIS_URL=redis://redis:6379/0
21+
METABOFLOW_CELERY_BROKER_URL=redis://redis:6379/0
22+
METABOFLOW_CELERY_RESULT_BACKEND=redis://redis:6379/1
23+
METABOFLOW_SECRET_KEY=${SECRET_KEY}
24+
METABOFLOW_CORS_ORIGINS=["http://localhost:3005","http://${DOMAIN}","https://${DOMAIN}"]
25+
METABOFLOW_XCMS_WORKER_URL=http://xcms-worker:8001
26+
METABOFLOW_STATS_WORKER_URL=http://stats-worker:8002
27+
METABOFLOW_CHART_SERVICE_URL=http://chart-service:8005
28+
METABOFLOW_ANNOT_WORKER_URL=http://annot-worker:8006
29+
METABOFLOW_SIRIUS_WORKER_URL=http://sirius-worker:8007
30+
METABOFLOW_CHART_R_WORKER_URL=http://chart-r-worker:8008
31+
METABOFLOW_REPORT_WORKER_URL=http://report-worker:8009
32+
POSTGRES_USER=metaboflow
33+
POSTGRES_PASSWORD=metaboflow
34+
POSTGRES_DB=metaboflow
35+
EOF
36+
echo ".env created (SECRET_KEY generated)"
37+
38+
# 2. Build all images
39+
echo ">>> Step 2: Build Docker images (this takes 15-30 minutes first time)"
40+
docker compose build --parallel 2>&1 | tail -20
41+
42+
# 3. Start all services
43+
echo ">>> Step 3: Start services"
44+
docker compose up -d
45+
46+
# 4. Wait for services to be healthy
47+
echo ">>> Step 4: Waiting for services..."
48+
for i in $(seq 1 60); do
49+
healthy=$(docker compose ps --format json 2>/dev/null | python3 -c "
50+
import sys, json
51+
lines = sys.stdin.read().strip().split('\n')
52+
total = len(lines)
53+
ok = sum(1 for l in lines if 'healthy' in l or 'running' in l.lower())
54+
print(f'{ok}/{total}')
55+
" 2>/dev/null || echo "?/?")
56+
echo " [$i/60] Services: $healthy"
57+
if docker compose ps | grep -q "unhealthy\|starting"; then
58+
sleep 10
59+
else
60+
break
61+
fi
62+
done
63+
64+
# 5. Create admin user + invite codes
65+
echo ">>> Step 5: Create admin user"
66+
docker compose exec -T backend uv run python3 -c "
67+
from app.db.base import SessionLocal, init_db
68+
from app.db.models import User, InviteCode
69+
from app.services.auth_service import hash_password
70+
import uuid, secrets
71+
from datetime import datetime, timedelta, timezone
72+
73+
init_db()
74+
session = SessionLocal()
75+
76+
# Admin user
77+
existing = session.query(User).filter_by(email='${ADMIN_EMAIL}').first()
78+
if not existing:
79+
admin = User(id=str(uuid.uuid4()), email='${ADMIN_EMAIL}',
80+
password_hash=hash_password('MetaboFlow2026!'),
81+
is_admin=True, created_at=datetime.now(timezone.utc))
82+
session.add(admin)
83+
print(f'Admin created: ${ADMIN_EMAIL} / MetaboFlow2026!')
84+
else:
85+
print('Admin already exists')
86+
87+
# Generate 10 invite codes
88+
codes = []
89+
for i in range(10):
90+
code = secrets.token_urlsafe(16)
91+
invite = InviteCode(id=str(uuid.uuid4()), code=code,
92+
expires_at=datetime.now(timezone.utc) + timedelta(days=90),
93+
created_at=datetime.now(timezone.utc))
94+
session.add(invite)
95+
codes.append(code)
96+
97+
session.commit()
98+
session.close()
99+
100+
print(f'\n10 invite codes (valid 90 days):')
101+
for i, c in enumerate(codes, 1):
102+
print(f' {i:2d}. {c}')
103+
"
104+
105+
# 6. Test health
106+
echo ""
107+
echo ">>> Step 6: Health check"
108+
echo -n "Backend: "
109+
curl -s http://localhost:8000/health | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])" 2>/dev/null || echo "FAIL"
110+
echo -n "Frontend: "
111+
curl -s -o /dev/null -w "%{http_code}" http://localhost:3005/ 2>/dev/null || echo "FAIL"
112+
113+
echo ""
114+
echo "=============================================="
115+
echo " MetaboFlow deployed successfully!"
116+
echo "=============================================="
117+
echo ""
118+
echo " Frontend: http://${DOMAIN}:3005"
119+
echo " API docs: http://${DOMAIN}:8000/docs"
120+
echo ""
121+
echo " Admin login:"
122+
echo " Email: ${ADMIN_EMAIL}"
123+
echo " Password: MetaboFlow2026!"
124+
echo ""
125+
echo " Change admin password after first login!"
126+
echo ""
127+
echo " Next: setup Nginx reverse proxy + SSL"
128+
echo " bash deploy/setup-nginx.sh"
129+
echo "=============================================="

deploy/setup-nginx.sh

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/bin/bash
2+
# Setup Nginx reverse proxy + Let's Encrypt SSL for MetaboFlow
3+
# Run as root or with sudo
4+
set -euo pipefail
5+
6+
DOMAIN="${METABOFLOW_DOMAIN:-ponytech.dev}"
7+
ADMIN_EMAIL="${ADMIN_EMAIL:-jiajunagent@gmail.com}"
8+
9+
echo "=== Nginx + SSL Setup ==="
10+
echo "Domain: $DOMAIN"
11+
12+
# 1. Install Nginx + Certbot
13+
echo ">>> Step 1: Install Nginx + Certbot"
14+
apt-get install -y nginx certbot python3-certbot-nginx
15+
16+
# 2. Create Nginx config
17+
echo ">>> Step 2: Configure Nginx"
18+
cat > /etc/nginx/sites-available/metaboflow << NGINX
19+
server {
20+
listen 80;
21+
server_name ${DOMAIN};
22+
23+
# Frontend
24+
location / {
25+
proxy_pass http://127.0.0.1:3005;
26+
proxy_http_version 1.1;
27+
proxy_set_header Upgrade \$http_upgrade;
28+
proxy_set_header Connection 'upgrade';
29+
proxy_set_header Host \$host;
30+
proxy_set_header X-Real-IP \$remote_addr;
31+
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
32+
proxy_set_header X-Forwarded-Proto \$scheme;
33+
proxy_cache_bypass \$http_upgrade;
34+
}
35+
36+
# Backend API
37+
location /api/ {
38+
proxy_pass http://127.0.0.1:8000/api/;
39+
proxy_http_version 1.1;
40+
proxy_set_header Host \$host;
41+
proxy_set_header X-Real-IP \$remote_addr;
42+
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
43+
proxy_set_header X-Forwarded-Proto \$scheme;
44+
proxy_read_timeout 3600; # Long timeout for analysis pipelines
45+
}
46+
47+
# Backend health + docs
48+
location /health {
49+
proxy_pass http://127.0.0.1:8000/health;
50+
}
51+
location /docs {
52+
proxy_pass http://127.0.0.1:8000/docs;
53+
}
54+
location /openapi.json {
55+
proxy_pass http://127.0.0.1:8000/openapi.json;
56+
}
57+
58+
# SSE (Server-Sent Events) — needs special proxy config
59+
location ~ /api/v1/analyses/.*/progress/stream {
60+
proxy_pass http://127.0.0.1:8000;
61+
proxy_http_version 1.1;
62+
proxy_set_header Connection '';
63+
proxy_set_header Cache-Control 'no-cache';
64+
proxy_set_header X-Accel-Buffering 'no';
65+
proxy_buffering off;
66+
chunked_transfer_encoding off;
67+
proxy_read_timeout 86400;
68+
}
69+
70+
# File upload size (mzML files can be 200MB+)
71+
client_max_body_size 500M;
72+
}
73+
NGINX
74+
75+
ln -sf /etc/nginx/sites-available/metaboflow /etc/nginx/sites-enabled/
76+
rm -f /etc/nginx/sites-enabled/default
77+
78+
# Test and reload
79+
nginx -t && systemctl reload nginx
80+
81+
# 3. SSL Certificate
82+
echo ">>> Step 3: SSL Certificate (Let's Encrypt)"
83+
echo "Make sure DNS for ${DOMAIN} points to this server's IP first!"
84+
echo ""
85+
read -p "DNS configured? (y/n) " -n 1 -r
86+
echo ""
87+
if [[ $REPLY =~ ^[Yy]$ ]]; then
88+
certbot --nginx -d "${DOMAIN}" --non-interactive --agree-tos -m "${ADMIN_EMAIL}"
89+
echo "SSL certificate installed!"
90+
echo ""
91+
echo "MetaboFlow is now available at:"
92+
echo " https://${DOMAIN}"
93+
else
94+
echo "Skipping SSL. Configure DNS first, then run:"
95+
echo " certbot --nginx -d ${DOMAIN} --agree-tos -m ${ADMIN_EMAIL}"
96+
echo ""
97+
echo "MetaboFlow is available at:"
98+
echo " http://${DOMAIN}"
99+
fi
100+
101+
# 4. Auto-renewal
102+
echo ">>> Step 4: Certbot auto-renewal"
103+
systemctl enable certbot.timer
104+
echo "SSL auto-renewal enabled"

deploy/setup-server.sh

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/bin/bash
2+
# MetaboFlow Server Setup Script
3+
# Target: Oracle Cloud Free Tier (ARM/aarch64, Ubuntu 22.04)
4+
# Run as root or with sudo
5+
set -euo pipefail
6+
7+
echo "=== MetaboFlow Server Setup ==="
8+
echo "Target: Oracle Cloud Free Tier (4 ARM cores, 24GB RAM, 200GB disk)"
9+
10+
# 1. System update
11+
echo ">>> Step 1: System update"
12+
apt-get update && apt-get upgrade -y
13+
14+
# 2. Install Docker
15+
echo ">>> Step 2: Install Docker"
16+
if ! command -v docker &>/dev/null; then
17+
curl -fsSL https://get.docker.com | sh
18+
systemctl enable docker
19+
systemctl start docker
20+
usermod -aG docker ubuntu # default Oracle Cloud user
21+
echo "Docker installed. You may need to re-login for group changes."
22+
else
23+
echo "Docker already installed"
24+
fi
25+
26+
# 3. Install Docker Compose plugin
27+
echo ">>> Step 3: Install Docker Compose"
28+
if ! docker compose version &>/dev/null; then
29+
apt-get install -y docker-compose-plugin
30+
else
31+
echo "Docker Compose already installed"
32+
fi
33+
34+
# 4. Install git
35+
echo ">>> Step 4: Install Git"
36+
apt-get install -y git
37+
38+
# 5. Configure firewall (Oracle Cloud uses iptables by default)
39+
echo ">>> Step 5: Configure firewall"
40+
# Open ports: 80 (HTTP), 443 (HTTPS), 8000 (API), 3005 (frontend)
41+
iptables -I INPUT -p tcp --dport 80 -j ACCEPT
42+
iptables -I INPUT -p tcp --dport 443 -j ACCEPT
43+
iptables -I INPUT -p tcp --dport 8000 -j ACCEPT
44+
iptables -I INPUT -p tcp --dport 3005 -j ACCEPT
45+
# Save rules
46+
apt-get install -y iptables-persistent
47+
netfilter-persistent save
48+
49+
# 6. Setup swap (helpful for R package compilation)
50+
echo ">>> Step 6: Setup swap (4GB)"
51+
if [ ! -f /swapfile ]; then
52+
fallocate -l 4G /swapfile
53+
chmod 600 /swapfile
54+
mkswap /swapfile
55+
swapon /swapfile
56+
echo '/swapfile none swap sw 0 0' >> /etc/fstab
57+
echo "Swap configured"
58+
else
59+
echo "Swap already exists"
60+
fi
61+
62+
# 7. Create app directory
63+
echo ">>> Step 7: Create app directory"
64+
mkdir -p /opt/metaboflow
65+
chown ubuntu:ubuntu /opt/metaboflow
66+
67+
echo ""
68+
echo "=== Server setup complete ==="
69+
echo "Next steps:"
70+
echo " 1. SSH as 'ubuntu' user"
71+
echo " 2. cd /opt/metaboflow"
72+
echo " 3. git clone https://github.com/ponytech-dev/MetaboFlow.git ."
73+
echo " 4. bash deploy/deploy.sh"

0 commit comments

Comments
 (0)