mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 18:01:09 +02:00
feat(deploy): add production deployment configuration for manacore.ai
Complete production setup with: - docker-compose.yml with all recommended apps (auth, chat, todo, calendar, clock, dashboard) - Caddyfile for automatic HTTPS via Let's Encrypt - PostgreSQL backup/restore scripts with daily retention - Environment template with secure defaults - Comprehensive deployment guide Apps deployed: - auth.manacore.ai (Authentication) - app.manacore.ai (Dashboard) - chat.manacore.ai / chat-api.manacore.ai - todo.manacore.ai / todo-api.manacore.ai - calendar.manacore.ai / calendar-api.manacore.ai - clock.manacore.ai / clock-api.manacore.ai
This commit is contained in:
parent
9afae2efd2
commit
22a58eecef
7 changed files with 1313 additions and 0 deletions
78
docker/production/.env.template
Normal file
78
docker/production/.env.template
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# ManaCore Production Environment Variables
|
||||
# Copy this file to .env and fill in the values:
|
||||
# cp .env.template .env
|
||||
# nano .env
|
||||
#
|
||||
# IMPORTANT: Never commit the .env file to git!
|
||||
|
||||
# ============================================
|
||||
# Docker Registry
|
||||
# ============================================
|
||||
DOCKER_REGISTRY=ghcr.io/memo-2023
|
||||
|
||||
# ============================================
|
||||
# PostgreSQL
|
||||
# ============================================
|
||||
POSTGRES_DB=manacore
|
||||
POSTGRES_USER=postgres
|
||||
# Generate with: openssl rand -base64 32
|
||||
POSTGRES_PASSWORD=CHANGE_ME_STRONG_PASSWORD
|
||||
|
||||
# ============================================
|
||||
# Redis
|
||||
# ============================================
|
||||
# Generate with: openssl rand -base64 32
|
||||
REDIS_PASSWORD=CHANGE_ME_STRONG_PASSWORD
|
||||
|
||||
# ============================================
|
||||
# JWT Configuration
|
||||
# ============================================
|
||||
# Generate secret with: openssl rand -base64 64
|
||||
JWT_SECRET=CHANGE_ME_STRONG_SECRET
|
||||
|
||||
# EdDSA Key Pair (Ed25519)
|
||||
# Generate with: node -e "const { generateKeyPairSync } = require('crypto'); const { publicKey, privateKey } = generateKeyPairSync('ed25519'); console.log('PUBLIC:', publicKey.export({type:'spki',format:'pem'}).toString().replace(/\\n/g,'\\\\n')); console.log('PRIVATE:', privateKey.export({type:'pkcs8',format:'pem'}).toString().replace(/\\n/g,'\\\\n'));"
|
||||
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nYOUR_PUBLIC_KEY_HERE\n-----END PUBLIC KEY-----"
|
||||
JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nYOUR_PRIVATE_KEY_HERE\n-----END PRIVATE KEY-----"
|
||||
|
||||
JWT_ACCESS_TOKEN_EXPIRY=15m
|
||||
JWT_REFRESH_TOKEN_EXPIRY=7d
|
||||
|
||||
# ============================================
|
||||
# Service Versions (optional, defaults to latest)
|
||||
# ============================================
|
||||
AUTH_VERSION=latest
|
||||
CHAT_VERSION=latest
|
||||
CHAT_WEB_VERSION=latest
|
||||
TODO_VERSION=latest
|
||||
TODO_WEB_VERSION=latest
|
||||
CALENDAR_VERSION=latest
|
||||
CALENDAR_WEB_VERSION=latest
|
||||
CLOCK_VERSION=latest
|
||||
CLOCK_WEB_VERSION=latest
|
||||
MANACORE_WEB_VERSION=latest
|
||||
|
||||
# ============================================
|
||||
# Azure OpenAI (for Chat)
|
||||
# ============================================
|
||||
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
|
||||
AZURE_OPENAI_API_KEY=your-api-key
|
||||
AZURE_OPENAI_API_VERSION=2024-12-01-preview
|
||||
|
||||
# ============================================
|
||||
# Credits System
|
||||
# ============================================
|
||||
CREDITS_SIGNUP_BONUS=100
|
||||
CREDITS_DAILY_FREE=5
|
||||
|
||||
# ============================================
|
||||
# OAuth (optional)
|
||||
# ============================================
|
||||
# GOOGLE_CLIENT_ID=
|
||||
# GOOGLE_CLIENT_SECRET=
|
||||
|
||||
# ============================================
|
||||
# Stripe (optional)
|
||||
# ============================================
|
||||
# STRIPE_SECRET_KEY=sk_live_...
|
||||
# STRIPE_WEBHOOK_SECRET=whsec_...
|
||||
8
docker/production/.gitignore
vendored
Normal file
8
docker/production/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Never commit production secrets
|
||||
.env
|
||||
*.env.local
|
||||
|
||||
# Backup files (stored on server, not in git)
|
||||
backups/
|
||||
*.sql
|
||||
*.sql.gz
|
||||
169
docker/production/Caddyfile
Normal file
169
docker/production/Caddyfile
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# ManaCore Production Reverse Proxy
|
||||
# Domain: manacore.ai
|
||||
#
|
||||
# Features:
|
||||
# - Automatic HTTPS via Let's Encrypt
|
||||
# - HTTP/2 and HTTP/3 support
|
||||
# - Gzip compression
|
||||
# - Security headers
|
||||
# - Health check endpoints
|
||||
#
|
||||
# Reload: docker exec manacore-caddy caddy reload --config /etc/caddy/Caddyfile
|
||||
|
||||
# Global options
|
||||
{
|
||||
email admin@manacore.ai
|
||||
# Uncomment for staging/testing Let's Encrypt (higher rate limits)
|
||||
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
}
|
||||
|
||||
# Common security headers snippet
|
||||
(security_headers) {
|
||||
header {
|
||||
# Security headers
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
# Remove server identification
|
||||
-Server
|
||||
}
|
||||
}
|
||||
|
||||
# Common compression snippet
|
||||
(compression) {
|
||||
encode gzip zstd
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Auth Service
|
||||
# ============================================
|
||||
|
||||
auth.manacore.ai {
|
||||
import security_headers
|
||||
import compression
|
||||
|
||||
reverse_proxy mana-core-auth:3001 {
|
||||
health_uri /api/v1/health
|
||||
health_interval 30s
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Main Dashboard
|
||||
# ============================================
|
||||
|
||||
app.manacore.ai {
|
||||
import security_headers
|
||||
import compression
|
||||
|
||||
reverse_proxy manacore-web:5173 {
|
||||
health_uri /health
|
||||
health_interval 30s
|
||||
}
|
||||
}
|
||||
|
||||
# Redirect root domain to app
|
||||
manacore.ai {
|
||||
redir https://app.manacore.ai{uri} permanent
|
||||
}
|
||||
|
||||
www.manacore.ai {
|
||||
redir https://app.manacore.ai{uri} permanent
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Chat App
|
||||
# ============================================
|
||||
|
||||
chat.manacore.ai {
|
||||
import security_headers
|
||||
import compression
|
||||
|
||||
reverse_proxy chat-web:3000 {
|
||||
health_uri /health
|
||||
health_interval 30s
|
||||
}
|
||||
}
|
||||
|
||||
chat-api.manacore.ai {
|
||||
import security_headers
|
||||
import compression
|
||||
|
||||
reverse_proxy chat-backend:3002 {
|
||||
health_uri /api/v1/health
|
||||
health_interval 30s
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Todo App
|
||||
# ============================================
|
||||
|
||||
todo.manacore.ai {
|
||||
import security_headers
|
||||
import compression
|
||||
|
||||
reverse_proxy todo-web:5188 {
|
||||
health_uri /health
|
||||
health_interval 30s
|
||||
}
|
||||
}
|
||||
|
||||
todo-api.manacore.ai {
|
||||
import security_headers
|
||||
import compression
|
||||
|
||||
reverse_proxy todo-backend:3018 {
|
||||
health_uri /api/v1/health
|
||||
health_interval 30s
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Calendar App
|
||||
# ============================================
|
||||
|
||||
calendar.manacore.ai {
|
||||
import security_headers
|
||||
import compression
|
||||
|
||||
reverse_proxy calendar-web:5186 {
|
||||
health_uri /health
|
||||
health_interval 30s
|
||||
}
|
||||
}
|
||||
|
||||
calendar-api.manacore.ai {
|
||||
import security_headers
|
||||
import compression
|
||||
|
||||
reverse_proxy calendar-backend:3016 {
|
||||
health_uri /api/v1/health
|
||||
health_interval 30s
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Clock App
|
||||
# ============================================
|
||||
|
||||
clock.manacore.ai {
|
||||
import security_headers
|
||||
import compression
|
||||
|
||||
reverse_proxy clock-web:5187 {
|
||||
health_uri /health
|
||||
health_interval 30s
|
||||
}
|
||||
}
|
||||
|
||||
clock-api.manacore.ai {
|
||||
import security_headers
|
||||
import compression
|
||||
|
||||
reverse_proxy clock-backend:3017 {
|
||||
health_uri /api/v1/health
|
||||
health_interval 30s
|
||||
}
|
||||
}
|
||||
346
docker/production/DEPLOY.md
Normal file
346
docker/production/DEPLOY.md
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
# ManaCore Production Deployment Guide
|
||||
|
||||
One-Server-Setup for manacore.ai with automated HTTPS and daily backups.
|
||||
|
||||
## Quick Overview
|
||||
|
||||
| Domain | Service | Port |
|
||||
|--------|---------|------|
|
||||
| auth.manacore.ai | Auth Service | 3001 |
|
||||
| app.manacore.ai | Dashboard | 5173 |
|
||||
| chat.manacore.ai | Chat Web | 3000 |
|
||||
| chat-api.manacore.ai | Chat Backend | 3002 |
|
||||
| todo.manacore.ai | Todo Web | 5188 |
|
||||
| todo-api.manacore.ai | Todo Backend | 3018 |
|
||||
| calendar.manacore.ai | Calendar Web | 5186 |
|
||||
| calendar-api.manacore.ai | Calendar Backend | 3016 |
|
||||
| clock.manacore.ai | Clock Web | 5187 |
|
||||
| clock-api.manacore.ai | Clock Backend | 3017 |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### 1. Server Requirements (Hetzner CAX21 recommended)
|
||||
|
||||
- 4 vCPU, 8GB RAM, 80GB SSD (~8€/month)
|
||||
- Ubuntu 22.04 or Debian 12
|
||||
- Docker + Docker Compose v2
|
||||
|
||||
### 2. DNS Configuration
|
||||
|
||||
Add these A records pointing to your server IP:
|
||||
|
||||
```
|
||||
manacore.ai → YOUR_SERVER_IP
|
||||
www.manacore.ai → YOUR_SERVER_IP
|
||||
app.manacore.ai → YOUR_SERVER_IP
|
||||
auth.manacore.ai → YOUR_SERVER_IP
|
||||
chat.manacore.ai → YOUR_SERVER_IP
|
||||
chat-api.manacore.ai → YOUR_SERVER_IP
|
||||
todo.manacore.ai → YOUR_SERVER_IP
|
||||
todo-api.manacore.ai → YOUR_SERVER_IP
|
||||
calendar.manacore.ai → YOUR_SERVER_IP
|
||||
calendar-api.manacore.ai → YOUR_SERVER_IP
|
||||
clock.manacore.ai → YOUR_SERVER_IP
|
||||
clock-api.manacore.ai → YOUR_SERVER_IP
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Server Setup (First Time)
|
||||
|
||||
### Step 1: Install Docker
|
||||
|
||||
```bash
|
||||
# SSH into server
|
||||
ssh root@YOUR_SERVER_IP
|
||||
|
||||
# Install Docker
|
||||
curl -fsSL https://get.docker.com | sh
|
||||
|
||||
# Add user to docker group (optional)
|
||||
usermod -aG docker $USER
|
||||
|
||||
# Verify installation
|
||||
docker --version
|
||||
docker compose version
|
||||
```
|
||||
|
||||
### Step 2: Create Project Directory
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/manacore
|
||||
cd /opt/manacore
|
||||
```
|
||||
|
||||
### Step 3: Copy Files to Server
|
||||
|
||||
From your local machine:
|
||||
|
||||
```bash
|
||||
# Copy production files
|
||||
scp docker/production/docker-compose.yml root@YOUR_SERVER_IP:/opt/manacore/
|
||||
scp docker/production/Caddyfile root@YOUR_SERVER_IP:/opt/manacore/
|
||||
scp docker/production/.env.template root@YOUR_SERVER_IP:/opt/manacore/
|
||||
scp docker/production/backup.sh root@YOUR_SERVER_IP:/opt/manacore/
|
||||
scp docker/production/restore.sh root@YOUR_SERVER_IP:/opt/manacore/
|
||||
```
|
||||
|
||||
### Step 4: Configure Environment
|
||||
|
||||
```bash
|
||||
cd /opt/manacore
|
||||
|
||||
# Copy template
|
||||
cp .env.template .env
|
||||
|
||||
# Generate secure passwords
|
||||
echo "POSTGRES_PASSWORD=$(openssl rand -base64 32)" >> .env.generated
|
||||
echo "REDIS_PASSWORD=$(openssl rand -base64 32)" >> .env.generated
|
||||
echo "JWT_SECRET=$(openssl rand -base64 64)" >> .env.generated
|
||||
|
||||
# Edit .env with your values
|
||||
nano .env
|
||||
```
|
||||
|
||||
### Step 5: Generate JWT Keys
|
||||
|
||||
```bash
|
||||
# Generate Ed25519 key pair
|
||||
node -e "
|
||||
const { generateKeyPairSync } = require('crypto');
|
||||
const { publicKey, privateKey } = generateKeyPairSync('ed25519');
|
||||
console.log('JWT_PUBLIC_KEY=\"' + publicKey.export({type:'spki',format:'pem'}).toString().replace(/\n/g,'\\\\n') + '\"');
|
||||
console.log('JWT_PRIVATE_KEY=\"' + privateKey.export({type:'pkcs8',format:'pem'}).toString().replace(/\n/g,'\\\\n') + '\"');
|
||||
"
|
||||
```
|
||||
|
||||
Copy the output to your `.env` file.
|
||||
|
||||
### Step 6: Create Backup Directory
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/manacore/backups
|
||||
chmod +x backup.sh restore.sh
|
||||
```
|
||||
|
||||
### Step 7: Login to Docker Registry
|
||||
|
||||
```bash
|
||||
# Create GitHub token with read:packages permission
|
||||
docker login ghcr.io -u YOUR_GITHUB_USERNAME
|
||||
# Enter your GitHub Personal Access Token
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
### Initial Deployment
|
||||
|
||||
```bash
|
||||
cd /opt/manacore
|
||||
|
||||
# Pull all images
|
||||
docker compose pull
|
||||
|
||||
# Start all services
|
||||
docker compose up -d
|
||||
|
||||
# Watch logs
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
### Verify Services
|
||||
|
||||
```bash
|
||||
# Check all containers are running
|
||||
docker compose ps
|
||||
|
||||
# Test health endpoints
|
||||
curl -s http://localhost:3001/api/v1/health # Auth
|
||||
curl -s http://localhost:3002/api/v1/health # Chat Backend
|
||||
curl -s http://localhost:3018/api/v1/health # Todo Backend
|
||||
curl -s http://localhost:3016/api/v1/health # Calendar Backend
|
||||
curl -s http://localhost:3017/api/v1/health # Clock Backend
|
||||
```
|
||||
|
||||
### Initialize Databases
|
||||
|
||||
The databases are created automatically on first start, but you may need to run migrations:
|
||||
|
||||
```bash
|
||||
# Create databases if not exist
|
||||
docker exec manacore-postgres psql -U postgres -c "CREATE DATABASE manacore_auth;"
|
||||
docker exec manacore-postgres psql -U postgres -c "CREATE DATABASE chat;"
|
||||
docker exec manacore-postgres psql -U postgres -c "CREATE DATABASE todo;"
|
||||
docker exec manacore-postgres psql -U postgres -c "CREATE DATABASE calendar;"
|
||||
docker exec manacore-postgres psql -U postgres -c "CREATE DATABASE clock;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Daily Operations
|
||||
|
||||
### Update Services
|
||||
|
||||
```bash
|
||||
cd /opt/manacore
|
||||
|
||||
# Pull latest images
|
||||
docker compose pull
|
||||
|
||||
# Restart with new images (zero-downtime for stateless services)
|
||||
docker compose up -d
|
||||
|
||||
# Or restart specific service
|
||||
docker compose pull chat-backend
|
||||
docker compose up -d chat-backend
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
docker compose logs -f
|
||||
|
||||
# Specific service
|
||||
docker compose logs -f chat-backend
|
||||
|
||||
# Last 100 lines
|
||||
docker compose logs --tail=100 mana-core-auth
|
||||
```
|
||||
|
||||
### Backup
|
||||
|
||||
```bash
|
||||
# Manual backup
|
||||
./backup.sh
|
||||
|
||||
# Backup specific database
|
||||
./backup.sh chat
|
||||
|
||||
# List backups
|
||||
ls -lh backups/
|
||||
```
|
||||
|
||||
### Restore
|
||||
|
||||
```bash
|
||||
# Restore from backup (CAUTION: overwrites data!)
|
||||
./restore.sh chat backups/chat_20250116_030000.sql.gz
|
||||
```
|
||||
|
||||
### Setup Daily Backups (Cron)
|
||||
|
||||
```bash
|
||||
crontab -e
|
||||
|
||||
# Add this line (backup at 3 AM daily)
|
||||
0 3 * * * /opt/manacore/backup.sh >> /var/log/manacore-backup.log 2>&1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Service won't start
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
docker compose logs SERVICE_NAME
|
||||
|
||||
# Check container status
|
||||
docker inspect SERVICE_NAME
|
||||
|
||||
# Restart service
|
||||
docker compose restart SERVICE_NAME
|
||||
```
|
||||
|
||||
### Database connection issues
|
||||
|
||||
```bash
|
||||
# Test database connection
|
||||
docker exec manacore-postgres psql -U postgres -c "\l"
|
||||
|
||||
# Check if database exists
|
||||
docker exec manacore-postgres psql -U postgres -c "\c chat"
|
||||
```
|
||||
|
||||
### Certificate issues (HTTPS)
|
||||
|
||||
```bash
|
||||
# Caddy automatically obtains certificates
|
||||
# Check Caddy logs for errors
|
||||
docker compose logs caddy
|
||||
|
||||
# Force certificate renewal
|
||||
docker exec manacore-caddy caddy reload --config /etc/caddy/Caddyfile
|
||||
```
|
||||
|
||||
### Out of disk space
|
||||
|
||||
```bash
|
||||
# Check disk usage
|
||||
df -h
|
||||
|
||||
# Clean up Docker
|
||||
docker system prune -a --volumes
|
||||
|
||||
# Clean old backups
|
||||
find /opt/manacore/backups -name "*.sql.gz" -mtime +7 -delete
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
The GitHub Actions workflow can be updated to deploy automatically:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/cd-production.yml
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Deploy to Production
|
||||
uses: appleboy/ssh-action@v1
|
||||
with:
|
||||
host: ${{ secrets.PROD_SERVER_IP }}
|
||||
username: root
|
||||
key: ${{ secrets.PROD_SSH_KEY }}
|
||||
script: |
|
||||
cd /opt/manacore
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resource Usage
|
||||
|
||||
Approximate resource usage per service:
|
||||
|
||||
| Service | CPU | Memory |
|
||||
|---------|-----|--------|
|
||||
| PostgreSQL | 0.5 | 512MB |
|
||||
| Redis | 0.1 | 256MB |
|
||||
| Caddy | 0.1 | 64MB |
|
||||
| mana-core-auth | 0.5-1 | 256-512MB |
|
||||
| chat-backend | 1-2 | 512MB-1GB |
|
||||
| chat-web | 0.2 | 128MB |
|
||||
| Other backends | 0.2-0.5 | 128-256MB |
|
||||
| Other webs | 0.2 | 128MB |
|
||||
|
||||
**Total**: ~2-4 vCPU, ~3-4GB RAM
|
||||
|
||||
Recommended server: **Hetzner CAX21** (4 vCPU ARM, 8GB RAM) for ~8€/month
|
||||
|
||||
---
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- [ ] Strong passwords in `.env` (use `openssl rand -base64 32`)
|
||||
- [ ] Firewall: Only ports 80, 443, 22 open
|
||||
- [ ] SSH key authentication (disable password login)
|
||||
- [ ] Regular backups (daily cron)
|
||||
- [ ] Keep Docker and OS updated
|
||||
- [ ] Monitor logs for anomalies
|
||||
99
docker/production/backup.sh
Executable file
99
docker/production/backup.sh
Executable file
|
|
@ -0,0 +1,99 @@
|
|||
#!/bin/bash
|
||||
# ManaCore PostgreSQL Backup Script
|
||||
#
|
||||
# Usage:
|
||||
# ./backup.sh # Backup all databases
|
||||
# ./backup.sh chat # Backup specific database
|
||||
#
|
||||
# Setup cron for daily backups:
|
||||
# crontab -e
|
||||
# 0 3 * * * /opt/manacore/backup.sh >> /var/log/manacore-backup.log 2>&1
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
BACKUP_DIR="${BACKUP_DIR:-./backups}"
|
||||
CONTAINER_NAME="${CONTAINER_NAME:-manacore-postgres}"
|
||||
POSTGRES_USER="${POSTGRES_USER:-postgres}"
|
||||
RETENTION_DAYS="${RETENTION_DAYS:-7}"
|
||||
|
||||
# Databases to backup (add more as needed)
|
||||
DATABASES=("manacore_auth" "chat" "todo" "calendar" "clock")
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
# Create backup directory if not exists
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Timestamp for backup files
|
||||
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
|
||||
|
||||
backup_database() {
|
||||
local db_name=$1
|
||||
local backup_file="${BACKUP_DIR}/${db_name}_${TIMESTAMP}.sql.gz"
|
||||
|
||||
log_info "Backing up database: $db_name"
|
||||
|
||||
if docker exec "$CONTAINER_NAME" pg_dump -U "$POSTGRES_USER" "$db_name" 2>/dev/null | gzip > "$backup_file"; then
|
||||
local size=$(du -h "$backup_file" | cut -f1)
|
||||
log_info "Created: $backup_file ($size)"
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to backup $db_name"
|
||||
rm -f "$backup_file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_old_backups() {
|
||||
log_info "Cleaning up backups older than $RETENTION_DAYS days..."
|
||||
find "$BACKUP_DIR" -name "*.sql.gz" -type f -mtime +$RETENTION_DAYS -delete
|
||||
local count=$(find "$BACKUP_DIR" -name "*.sql.gz" -type f | wc -l)
|
||||
log_info "Remaining backups: $count"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
log_info "=== ManaCore Backup Started ==="
|
||||
|
||||
# Check if specific database requested
|
||||
if [ $# -eq 1 ]; then
|
||||
backup_database "$1"
|
||||
else
|
||||
# Backup all databases
|
||||
failed=0
|
||||
for db in "${DATABASES[@]}"; do
|
||||
if ! backup_database "$db"; then
|
||||
((failed++))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $failed -gt 0 ]; then
|
||||
log_warn "$failed database(s) failed to backup"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Cleanup old backups
|
||||
cleanup_old_backups
|
||||
|
||||
log_info "=== ManaCore Backup Completed ==="
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
log_info "Backup summary:"
|
||||
ls -lh "$BACKUP_DIR"/*.sql.gz 2>/dev/null | tail -10 || log_warn "No backups found"
|
||||
531
docker/production/docker-compose.yml
Normal file
531
docker/production/docker-compose.yml
Normal file
|
|
@ -0,0 +1,531 @@
|
|||
# ManaCore Production Stack
|
||||
# Domain: manacore.ai
|
||||
#
|
||||
# Apps included:
|
||||
# - mana-core-auth (auth.manacore.ai)
|
||||
# - chat (chat.manacore.ai + chat-api.manacore.ai)
|
||||
# - todo (todo.manacore.ai + todo-api.manacore.ai)
|
||||
# - calendar (calendar.manacore.ai + calendar-api.manacore.ai)
|
||||
# - clock (clock.manacore.ai + clock-api.manacore.ai)
|
||||
# - manacore-web (app.manacore.ai)
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.yml up -d
|
||||
# docker compose -f docker-compose.yml logs -f
|
||||
# docker compose -f docker-compose.yml down
|
||||
|
||||
services:
|
||||
# ============================================
|
||||
# Infrastructure Services
|
||||
# ============================================
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: manacore-postgres
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-manacore}
|
||||
POSTGRES_USER: ${POSTGRES_USER:-postgres}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./backups:/backups
|
||||
ports:
|
||||
- "127.0.0.1:5432:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "50m"
|
||||
max-file: "5"
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: manacore-redis
|
||||
restart: always
|
||||
command: redis-server --requirepass ${REDIS_PASSWORD:?REDIS_PASSWORD is required} --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
ports:
|
||||
- "127.0.0.1:6379:6379"
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
|
||||
# ============================================
|
||||
# Reverse Proxy (Caddy - Auto HTTPS)
|
||||
# ============================================
|
||||
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
container_name: manacore-caddy
|
||||
restart: always
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "443:443/udp" # HTTP/3
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
|
||||
# ============================================
|
||||
# Auth Service
|
||||
# ============================================
|
||||
|
||||
mana-core-auth:
|
||||
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/mana-core-auth:${AUTH_VERSION:-latest}
|
||||
container_name: mana-core-auth
|
||||
restart: always
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3001
|
||||
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/manacore_auth
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||
JWT_SECRET: ${JWT_SECRET}
|
||||
JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY}
|
||||
JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY}
|
||||
JWT_ACCESS_TOKEN_EXPIRY: ${JWT_ACCESS_TOKEN_EXPIRY:-15m}
|
||||
JWT_REFRESH_TOKEN_EXPIRY: ${JWT_REFRESH_TOKEN_EXPIRY:-7d}
|
||||
CORS_ORIGINS: https://app.manacore.ai,https://chat.manacore.ai,https://todo.manacore.ai,https://calendar.manacore.ai,https://clock.manacore.ai
|
||||
CREDITS_SIGNUP_BONUS: ${CREDITS_SIGNUP_BONUS:-100}
|
||||
CREDITS_DAILY_FREE: ${CREDITS_DAILY_FREE:-5}
|
||||
# OAuth (optional)
|
||||
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
|
||||
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-}
|
||||
# Stripe (optional)
|
||||
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-}
|
||||
STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-}
|
||||
ports:
|
||||
- "127.0.0.1:3001:3001"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/api/v1/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "50m"
|
||||
max-file: "5"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 512M
|
||||
|
||||
# ============================================
|
||||
# Chat App
|
||||
# ============================================
|
||||
|
||||
chat-backend:
|
||||
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/chat-backend:${CHAT_VERSION:-latest}
|
||||
container_name: chat-backend
|
||||
restart: always
|
||||
depends_on:
|
||||
mana-core-auth:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3002
|
||||
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/chat
|
||||
MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||
AZURE_OPENAI_ENDPOINT: ${AZURE_OPENAI_ENDPOINT}
|
||||
AZURE_OPENAI_API_KEY: ${AZURE_OPENAI_API_KEY}
|
||||
AZURE_OPENAI_API_VERSION: ${AZURE_OPENAI_API_VERSION:-2024-12-01-preview}
|
||||
CORS_ORIGINS: https://chat.manacore.ai,https://app.manacore.ai
|
||||
ports:
|
||||
- "127.0.0.1:3002:3002"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3002/api/v1/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "50m"
|
||||
max-file: "5"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 1G
|
||||
|
||||
chat-web:
|
||||
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/chat-web:${CHAT_WEB_VERSION:-latest}
|
||||
container_name: chat-web
|
||||
restart: always
|
||||
depends_on:
|
||||
chat-backend:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
# Server-side URLs (Docker internal)
|
||||
PUBLIC_BACKEND_URL: http://chat-backend:3002
|
||||
PUBLIC_MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||
# Client-side URLs (browser)
|
||||
PUBLIC_BACKEND_URL_CLIENT: https://chat-api.manacore.ai
|
||||
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: https://auth.manacore.ai
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
|
||||
# ============================================
|
||||
# ManaCore Dashboard
|
||||
# ============================================
|
||||
|
||||
manacore-web:
|
||||
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/manacore-web:${MANACORE_WEB_VERSION:-latest}
|
||||
container_name: manacore-web
|
||||
restart: always
|
||||
depends_on:
|
||||
mana-core-auth:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 5173
|
||||
# Auth
|
||||
PUBLIC_MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: https://auth.manacore.ai
|
||||
# Backend URLs for dashboard widgets
|
||||
PUBLIC_TODO_API_URL: http://todo-backend:3018
|
||||
PUBLIC_TODO_API_URL_CLIENT: https://todo-api.manacore.ai
|
||||
PUBLIC_CALENDAR_API_URL: http://calendar-backend:3016
|
||||
PUBLIC_CALENDAR_API_URL_CLIENT: https://calendar-api.manacore.ai
|
||||
PUBLIC_CLOCK_API_URL: http://clock-backend:3017
|
||||
PUBLIC_CLOCK_API_URL_CLIENT: https://clock-api.manacore.ai
|
||||
PUBLIC_CHAT_API_URL: http://chat-backend:3002
|
||||
PUBLIC_CHAT_API_URL_CLIENT: https://chat-api.manacore.ai
|
||||
ports:
|
||||
- "127.0.0.1:5173:5173"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5173/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
|
||||
# ============================================
|
||||
# Todo App
|
||||
# ============================================
|
||||
|
||||
todo-backend:
|
||||
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/todo-backend:${TODO_VERSION:-latest}
|
||||
container_name: todo-backend
|
||||
restart: always
|
||||
depends_on:
|
||||
mana-core-auth:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3018
|
||||
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/todo
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_USER: ${POSTGRES_USER:-postgres}
|
||||
MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||
CORS_ORIGINS: https://todo.manacore.ai,https://app.manacore.ai
|
||||
ports:
|
||||
- "127.0.0.1:3018:3018"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3018/api/v1/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
|
||||
todo-web:
|
||||
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/todo-web:${TODO_WEB_VERSION:-latest}
|
||||
container_name: todo-web
|
||||
restart: always
|
||||
depends_on:
|
||||
todo-backend:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 5188
|
||||
PUBLIC_BACKEND_URL: http://todo-backend:3018
|
||||
PUBLIC_MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||
PUBLIC_BACKEND_URL_CLIENT: https://todo-api.manacore.ai
|
||||
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: https://auth.manacore.ai
|
||||
ports:
|
||||
- "127.0.0.1:5188:5188"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5188/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
|
||||
# ============================================
|
||||
# Calendar App
|
||||
# ============================================
|
||||
|
||||
calendar-backend:
|
||||
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/calendar-backend:${CALENDAR_VERSION:-latest}
|
||||
container_name: calendar-backend
|
||||
restart: always
|
||||
depends_on:
|
||||
mana-core-auth:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3016
|
||||
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/calendar
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_USER: ${POSTGRES_USER:-postgres}
|
||||
MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||
CORS_ORIGINS: https://calendar.manacore.ai,https://app.manacore.ai
|
||||
ports:
|
||||
- "127.0.0.1:3016:3016"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3016/api/v1/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
|
||||
calendar-web:
|
||||
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/calendar-web:${CALENDAR_WEB_VERSION:-latest}
|
||||
container_name: calendar-web
|
||||
restart: always
|
||||
depends_on:
|
||||
calendar-backend:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 5186
|
||||
PUBLIC_BACKEND_URL: http://calendar-backend:3016
|
||||
PUBLIC_MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||
PUBLIC_BACKEND_URL_CLIENT: https://calendar-api.manacore.ai
|
||||
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: https://auth.manacore.ai
|
||||
ports:
|
||||
- "127.0.0.1:5186:5186"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5186/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
|
||||
# ============================================
|
||||
# Clock App
|
||||
# ============================================
|
||||
|
||||
clock-backend:
|
||||
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/clock-backend:${CLOCK_VERSION:-latest}
|
||||
container_name: clock-backend
|
||||
restart: always
|
||||
depends_on:
|
||||
mana-core-auth:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3017
|
||||
DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD}@postgres:5432/clock
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_USER: ${POSTGRES_USER:-postgres}
|
||||
MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||
CORS_ORIGINS: https://clock.manacore.ai,https://app.manacore.ai
|
||||
ports:
|
||||
- "127.0.0.1:3017:3017"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3017/api/v1/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
|
||||
clock-web:
|
||||
image: ${DOCKER_REGISTRY:-ghcr.io/memo-2023}/clock-web:${CLOCK_WEB_VERSION:-latest}
|
||||
container_name: clock-web
|
||||
restart: always
|
||||
depends_on:
|
||||
clock-backend:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 5187
|
||||
PUBLIC_BACKEND_URL: http://clock-backend:3017
|
||||
PUBLIC_MANA_CORE_AUTH_URL: http://mana-core-auth:3001
|
||||
PUBLIC_BACKEND_URL_CLIENT: https://clock-api.manacore.ai
|
||||
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: https://auth.manacore.ai
|
||||
ports:
|
||||
- "127.0.0.1:5187:5187"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5187/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- manacore-network
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
|
||||
# ============================================
|
||||
# Networks
|
||||
# ============================================
|
||||
|
||||
networks:
|
||||
manacore-network:
|
||||
driver: bridge
|
||||
name: manacore-production
|
||||
|
||||
# ============================================
|
||||
# Volumes
|
||||
# ============================================
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
name: manacore-postgres-prod
|
||||
redis_data:
|
||||
name: manacore-redis-prod
|
||||
caddy_data:
|
||||
name: manacore-caddy-data
|
||||
caddy_config:
|
||||
name: manacore-caddy-config
|
||||
82
docker/production/restore.sh
Executable file
82
docker/production/restore.sh
Executable file
|
|
@ -0,0 +1,82 @@
|
|||
#!/bin/bash
|
||||
# ManaCore PostgreSQL Restore Script
|
||||
#
|
||||
# Usage:
|
||||
# ./restore.sh chat backups/chat_20250116_030000.sql.gz
|
||||
#
|
||||
# WARNING: This will OVERWRITE the existing database!
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
CONTAINER_NAME="${CONTAINER_NAME:-manacore-postgres}"
|
||||
POSTGRES_USER="${POSTGRES_USER:-postgres}"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
# Validate arguments
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Usage: $0 <database_name> <backup_file.sql.gz>"
|
||||
echo "Example: $0 chat backups/chat_20250116_030000.sql.gz"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DB_NAME=$1
|
||||
BACKUP_FILE=$2
|
||||
|
||||
# Check if backup file exists
|
||||
if [ ! -f "$BACKUP_FILE" ]; then
|
||||
log_error "Backup file not found: $BACKUP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Confirm restoration
|
||||
echo ""
|
||||
log_warn "WARNING: This will OVERWRITE the database '$DB_NAME'!"
|
||||
log_warn "Backup file: $BACKUP_FILE"
|
||||
echo ""
|
||||
read -p "Are you sure you want to continue? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
log_info "Restore cancelled."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log_info "=== ManaCore Restore Started ==="
|
||||
|
||||
# Create backup of current state before restore
|
||||
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
|
||||
CURRENT_BACKUP="./backups/${DB_NAME}_pre_restore_${TIMESTAMP}.sql.gz"
|
||||
|
||||
log_info "Creating backup of current state..."
|
||||
docker exec "$CONTAINER_NAME" pg_dump -U "$POSTGRES_USER" "$DB_NAME" 2>/dev/null | gzip > "$CURRENT_BACKUP" || true
|
||||
log_info "Current state backed up to: $CURRENT_BACKUP"
|
||||
|
||||
# Drop and recreate database
|
||||
log_info "Dropping and recreating database: $DB_NAME"
|
||||
docker exec "$CONTAINER_NAME" psql -U "$POSTGRES_USER" -c "DROP DATABASE IF EXISTS $DB_NAME;"
|
||||
docker exec "$CONTAINER_NAME" psql -U "$POSTGRES_USER" -c "CREATE DATABASE $DB_NAME;"
|
||||
|
||||
# Restore from backup
|
||||
log_info "Restoring from backup..."
|
||||
gunzip -c "$BACKUP_FILE" | docker exec -i "$CONTAINER_NAME" psql -U "$POSTGRES_USER" "$DB_NAME"
|
||||
|
||||
log_info "=== ManaCore Restore Completed ==="
|
||||
log_info "Database '$DB_NAME' has been restored from $BACKUP_FILE"
|
||||
log_info "Previous state saved to: $CURRENT_BACKUP"
|
||||
Loading…
Add table
Add a link
Reference in a new issue