Passa al contenuto principale

Deployment Guide — Virtual Stores System

Version: 1.0 | Last updated: 2026-03-29


Architecture Overview

Internet
|
v
[Cloudflare DNS] -- *.store.inallweb.com -> VPS IP
|
v
[Traefik Reverse Proxy] -- TLS (Let's Encrypt, DNS challenge)
|
+-- /api/* -> Backend (FastAPI, port 8000)
+-- /admin/* -> Admin Panel (React+Vite, port 5173)
+-- /* -> Storefront (Next.js, port 3000)

Infrastructure:

  • Hosting: Hetzner Cloud VPS
  • DNS: Cloudflare (wildcard *.store.inallweb.com)
  • Containers: Docker + Docker Compose
  • Reverse Proxy: Traefik v2 with Let's Encrypt
  • Database: PostgreSQL 15
  • Object Storage: MinIO (S3-compatible)
  • Cache: Redis
  • CI/CD: GitLab CI (7 stages)
  • IaC: Terraform (Hetzner + Cloudflare) + Ansible

Prerequisites

Before deploying, ensure you have:

Credentials & API Keys

ItemEnvironment VariableRequired
Hetzner API tokenHCLOUD_TOKENYes
Cloudflare API token (Zone.DNS permissions)CLOUDFLARE_API_TOKENYes
SSH key pairConfigured in HetznerYes
Stripe LIVE secret keySTRIPE_SECRET_KEYYes
Stripe LIVE public keySTRIPE_PUBLIC_KEYYes
Stripe webhook secretSTRIPE_WEBHOOK_SECRETYes
Stripe Connect webhook secretSTRIPE_CONNECT_WEBHOOK_SECRETYes
Moloni client IDMOLONI_CLIENT_IDFor invoicing
Moloni client secretMOLONI_CLIENT_SECRETFor invoicing
Zeptomail API keyZEPTOMAIL_API_KEYFor emails
MinIO credentialsMINIO_ACCESS_KEY, MINIO_SECRET_KEYYes
PostgreSQL credentialsDATABASE_URLYes
JWT secret keyJWT_SECRET_KEYYes

DNS Configuration

RecordTypeValue
store.inallweb.comAVPS IP address
*.store.inallweb.comAVPS IP address

Cloudflare must be in DNS-only mode (grey cloud) for Let's Encrypt DNS challenge, or use Cloudflare API token for automatic certificate management.

Email DNS Records (Zeptomail)

RecordTypePurpose
SPFTXTAuthorize Zeptomail to send
DKIMTXTEmail signing
DMARCTXTEmail policy

Step 1: Provision Infrastructure

Terraform

The Terraform configuration provisions:

  • Hetzner Cloud VPS (Ubuntu)
  • Cloudflare DNS records (A record + wildcard)
cd stores-deployment/terraform/
terraform init
terraform plan
terraform apply

Output: VPS IP address.

Ansible Bootstrap

After the VPS is provisioned, run the bootstrap playbook:

cd stores-deployment/ansible/
ansible-playbook -i inventories/prod bootstrap-vps.yml

This installs:

  • Docker + Docker Compose
  • UFW firewall (ports 22, 80, 443)
  • Swap space
  • System updates

Step 2: Deploy Application Stack

Docker Compose

The production stack includes:

ServiceImagePort
stores-backendFastAPI8000
stores-adminReact+Vite (nginx)80
stores-storefrontNext.js3000
stores-postgresPostgreSQL 155432
stores-redisRedis 76379
stores-minioMinIO9000/9001
stores-traefikTraefik v280/443

Deploy

cd stores-deployment/ansible/
ansible-playbook -i inventories/prod deploy-stack.yml

Or manually:

cd stores-management/
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Traefik Configuration

Traefik is configured with:

  • Network: stores-edge (independent from other systems)
  • Container: stores-traefik
  • Certificates: Let's Encrypt via Cloudflare DNS challenge
  • Wildcard: *.store.inallweb.com for multi-tenant support

Step 3: Database Setup

Initial Schema

The backend automatically creates tables on startup via SQLAlchemy:

# Or manually trigger schema creation
docker exec stores-backend python -c "from src.core.database import engine, Base; Base.metadata.create_all(engine)"

Seed Data

The seed system provisions:

  • Plan definitions (Starter, Business, Professional, Enterprise)
  • Addon definitions (32 addons)
  • Plan-addon inclusions
  • Templates
docker exec stores-backend python -m src.seeds.run_all

Database Backup

# Manual backup
docker exec stores-postgres pg_dump -U stores_user stores_db > backup_$(date +%Y%m%d).sql

# Restore
docker exec -i stores-postgres psql -U stores_user stores_db < backup_20260329.sql

Step 4: Post-Deploy Verification

Health Check

curl https://store.inallweb.com/api/health
# Expected: {"status": "healthy", ...}

Register Stripe Webhook

  1. Go to Stripe Dashboard > Developers > Webhooks
  2. Add endpoint: https://store.inallweb.com/api/webhooks/stripe
  3. Select events:
    • payment_intent.succeeded
    • payment_intent.payment_failed
    • charge.refunded
    • charge.dispute.created
    • charge.dispute.closed
    • account.updated
  4. Copy the webhook signing secret to STRIPE_WEBHOOK_SECRET

End-to-End Verification

  1. Create a test tenant via sysadmin API
  2. Create a test product
  3. Place a test order
  4. Verify payment via Stripe webhook
  5. Check that email confirmation is sent
  6. Verify Moloni invoice creation (if configured)

Step 5: Tenant Provisioning

Create First Tenant

curl -X POST https://store.inallweb.com/api/sysadmin/tenants \
-H "Authorization: Bearer $SYSADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "DefenseOps Airsoft",
"slug": "defenseopsairsoft",
"email": "admin@defenseops.pt",
"plan": "business",
"template_id": "tactical"
}'

Response includes admin_email and admin_temp_password.

WooCommerce Migration

For migrating from WooCommerce:

curl -X POST https://store.inallweb.com/api/migration/woocommerce/import \
-H "Authorization: Bearer $SYSADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"wc_url": "https://old-store.example.com",
"wc_consumer_key": "ck_...",
"wc_consumer_secret": "cs_..."
}'

Environment Variables Reference

Required

# Database
DATABASE_URL=postgresql://user:pass@host:5432/stores_db

# Auth
JWT_SECRET_KEY=<random-256-bit-key>
JWT_ALGORITHM=HS256
JWT_EXPIRE_MINUTES=1440

# Stripe
STRIPE_SECRET_KEY=sk_live_...
STRIPE_PUBLIC_KEY=pk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_CONNECT_WEBHOOK_SECRET=whsec_...

# MinIO
MINIO_ENDPOINT=minio:9000
MINIO_ACCESS_KEY=...
MINIO_SECRET_KEY=...
MINIO_BUCKET=stores
MINIO_USE_SSL=false

# Redis
REDIS_URL=redis://redis:6379/0

Optional

# Email (Zeptomail)
ZEPTOMAIL_API_KEY=...
ZEPTOMAIL_FROM_EMAIL=noreply@store.inallweb.com
ZEPTOMAIL_FROM_NAME=Loja Online

# Moloni (fiscal invoicing)
MOLONI_CLIENT_ID=...
MOLONI_CLIENT_SECRET=...

# CDN
CDN_BASE_URL=https://cdn.store.inallweb.com

# E2E Testing
SKIP_RATE_LIMIT_FOR_E2E=false

CI/CD Pipeline (GitLab)

The pipeline has 7 stages:

StageDescription
lintESLint, Ruff, type checking
testpytest (backend), unit tests (frontend)
buildDocker image build
e2ePlaywright E2E tests
pushPush images to GitLab Container Registry
deployAnsible deploy to target environment
verifyHealth check and smoke tests

Environments

EnvironmentBranchURL
devany feature branch*.dev.store.inallweb.com
uatdevelop*.uat.store.inallweb.com
stagingstaging*.staging.store.inallweb.com
productionmain*.store.inallweb.com

Rollback Procedure

Quick Rollback (Blue-Green)

# Activate the other deployment slot
./release-activate.sh prod manifest.json store.inallweb.com <other-slot>

Manual Rollback

# 1. Identify the last working image tag
docker images | grep stores-backend

# 2. Update docker-compose with the previous tag
# 3. Redeploy
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# 4. Verify
curl https://store.inallweb.com/api/health

Database Rollback

For database schema changes, always use reversible migrations:

# Restore from backup
docker exec -i stores-postgres psql -U stores_user stores_db < backup_YYYYMMDD.sql

Monitoring

Logs

# All services
docker compose logs -f

# Specific service
docker compose logs -f stores-backend

# Last 100 lines
docker compose logs --tail=100 stores-backend

Resource Usage

docker stats

Disk Usage

# Docker disk usage
docker system df

# MinIO storage
docker exec stores-minio mc ls local/stores/

Security Checklist

  • All secrets in environment variables (never hardcoded)
  • UFW firewall enabled (ports 22, 80, 443 only)
  • HTTPS enforced via Traefik (automatic redirect)
  • JWT tokens with expiration
  • Password hashing with bcrypt
  • Rate limiting enabled (disable for E2E with SKIP_RATE_LIMIT_FOR_E2E)
  • CORS configured for allowed origins
  • Stripe webhook signature verification enabled
  • Row-level security via tenant_id on all queries
  • Audit logging enabled for admin actions
  • GDPR compliance: data export and account deletion endpoints