feat: Add Nanaxe server with Docker, GitHub Actions CI/CD, and auto-deployment

- Add complete server infrastructure with Gitea, API, PostgreSQL
- Implement Obsidian-compatible markdown mindmap converter
- Add Flutter service for server synchronization
- Configure GitHub Actions for automated build and deploy
- Add production docker-compose with health checks
- Include VPS setup and deployment scripts
- Add comprehensive deployment documentation
This commit is contained in:
2026-04-19 22:18:25 +05:00
parent 52aea9ac87
commit f1e0f6b5ef
56 changed files with 8892 additions and 397 deletions
+263
View File
@@ -0,0 +1,263 @@
#!/bin/bash
# ==========================================
# Nanaxe Server Deployment Script
# ==========================================
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
COMPOSE_FILE="docker-compose.prod.yml"
BACKUP_DIR="./backups"
LOG_FILE="./logs/deploy-$(date +%Y%m%d-%H%M%S).log"
# ==========================================
# Helper Functions
# ==========================================
log() {
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
}
error() {
echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
}
success() {
echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
}
warning() {
echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}
# ==========================================
# Pre-deployment Checks
# ==========================================
check_prerequisites() {
log "Checking prerequisites..."
# Check Docker
if ! command -v docker &> /dev/null; then
error "Docker is not installed"
exit 1
fi
# Check Docker Compose
if ! docker compose version &> /dev/null; then
error "Docker Compose is not installed"
exit 1
fi
# Check .env file
if [ ! -f .env ]; then
error ".env file not found. Please create it from .env.example"
exit 1
fi
# Check compose file
if [ ! -f "$COMPOSE_FILE" ]; then
error "Compose file $COMPOSE_FILE not found"
exit 1
fi
success "Prerequisites check passed"
}
# ==========================================
# Backup
# ==========================================
create_backup() {
log "Creating backup..."
mkdir -p "$BACKUP_DIR"
# Backup database
if docker compose ps postgres | grep -q "running"; then
log "Backing up PostgreSQL database..."
docker compose exec -T postgres pg_dump -U gitea gitea > "$BACKUP_DIR/db-$(date +%Y%m%d-%H%M%S).sql" 2>/dev/null || warning "Database backup failed"
fi
# Backup mindmaps
if [ -d "mindmap-data" ]; then
log "Backing up mindmaps..."
tar -czf "$BACKUP_DIR/mindmaps-$(date +%Y%m%d-%H%M%S).tar.gz" mindmap-data/ 2>/dev/null || warning "Mindmaps backup failed"
fi
success "Backup completed"
}
# ==========================================
# Deployment
# ==========================================
deploy() {
log "Starting deployment..."
# Pull latest images
log "Pulling latest images..."
docker compose -f "$COMPOSE_FILE" pull
# Deploy with zero downtime
log "Deploying services..."
docker compose -f "$COMPOSE_FILE" up -d --remove-orphans
success "Deployment completed"
}
# ==========================================
# Health Checks
# ==========================================
health_check() {
log "Running health checks..."
local max_attempts=30
local attempt=1
# Wait for services to start
sleep 10
# Check API
log "Checking API health..."
while [ $attempt -le $max_attempts ]; do
if curl -sf http://localhost:8080/health > /dev/null 2>&1; then
success "API is healthy"
break
fi
if [ $attempt -eq $max_attempts ]; then
error "API health check failed after $max_attempts attempts"
show_logs
exit 1
fi
log "Attempt $attempt/$max_attempts: API not ready yet..."
sleep 5
((attempt++))
done
# Check Gitea
log "Checking Gitea health..."
attempt=1
while [ $attempt -le $max_attempts ]; do
if curl -sf http://localhost:3000/api/healthz > /dev/null 2>&1; then
success "Gitea is healthy"
break
fi
if [ $attempt -eq $max_attempts ]; then
warning "Gitea health check failed (may still be initializing)"
break
fi
log "Attempt $attempt/$max_attempts: Gitea not ready yet..."
sleep 5
((attempt++))
done
success "All health checks passed"
}
# ==========================================
# Cleanup
# ==========================================
cleanup() {
log "Cleaning up..."
# Remove old backups (keep last 7)
if [ -d "$BACKUP_DIR" ]; then
ls -t "$BACKUP_DIR"/*.sql 2>/dev/null | tail -n +8 | xargs -r rm
ls -t "$BACKUP_DIR"/*.tar.gz 2>/dev/null | tail -n +8 | xargs -r rm
fi
# Clean Docker
docker system prune -f
docker volume prune -f
success "Cleanup completed"
}
# ==========================================
# Rollback
# ==========================================
rollback() {
error "Deployment failed! Rolling back..."
# Get previous image
local previous_image=$(docker images ghcr.io/*/nanaxe-api --format "{{.Repository}}:{{.Tag}}" | grep -v latest | head -1)
if [ -n "$previous_image" ]; then
log "Reverting to: $previous_image"
docker compose -f "$COMPOSE_FILE" stop api
docker compose -f "$COMPOSE_FILE" rm -f api
docker run -d --name nanaxe-api-rollback \
--network nanaxe-network \
-v mindmap-data:/app/mindmaps \
-e NODE_ENV=production \
-e PORT=8080 \
-p 8080:8080 \
"$previous_image"
success "Rollback completed"
else
error "No previous image found for rollback"
fi
}
# ==========================================
# Show Logs
# ==========================================
show_logs() {
log "Showing recent logs..."
docker compose -f "$COMPOSE_FILE" logs --tail=50
}
# ==========================================
# Main
# ==========================================
main() {
log "======================================"
log "Nanaxe Server Deployment"
log "======================================"
# Create logs directory
mkdir -p logs
# Run deployment steps
check_prerequisites
create_backup
# Deploy with error handling
if deploy; then
if health_check; then
cleanup
success "======================================"
success "Deployment completed successfully!"
success "======================================"
log "API: http://localhost:8080"
log "Gitea: http://localhost:3000"
else
rollback
exit 1
fi
else
rollback
exit 1
fi
}
# Run main function
main "$@"
+281
View File
@@ -0,0 +1,281 @@
#!/bin/bash
# ==========================================
# VPS Setup Script for Nanaxe Server
# Run this on your fresh VPS
# ==========================================
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "${BLUE}[SETUP]${NC} $1"; }
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
# ==========================================
# Configuration
# ==========================================
DEPLOY_DIR="/opt/nanaxe-server"
REPO_URL="" # Will be set interactively
DOMAIN="" # Will be set interactively
# ==========================================
# System Update
# ==========================================
update_system() {
log "Updating system packages..."
apt update && apt upgrade -y
apt install -y curl wget git nano htop ufw fail2ban
success "System updated"
}
# ==========================================
# Install Docker
# ==========================================
install_docker() {
log "Installing Docker..."
if command -v docker &> /dev/null; then
warning "Docker already installed, skipping"
return
fi
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
rm get-docker.sh
usermod -aG docker $USER
success "Docker installed"
}
# ==========================================
# Setup Firewall
# ==========================================
setup_firewall() {
log "Configuring firewall..."
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 3000/tcp # Gitea
ufw allow 8080/tcp # API
ufw allow 222/tcp # Gitea SSH
ufw --force enable
success "Firewall configured"
}
# ==========================================
# Setup Fail2Ban
# ==========================================
setup_fail2ban() {
log "Configuring Fail2Ban..."
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
EOF
systemctl restart fail2ban
success "Fail2Ban configured"
}
# ==========================================
# Setup Deployment Directory
# ==========================================
setup_deploy_dir() {
log "Setting up deployment directory..."
mkdir -p "$DEPLOY_DIR"
cd "$DEPLOY_DIR"
# Clone repository
if [ -n "$REPO_URL" ]; then
git clone "$REPO_URL" .
fi
# Create necessary directories
mkdir -p backups logs
success "Deployment directory ready"
}
# ==========================================
# Setup SSL with Let's Encrypt
# ==========================================
setup_ssl() {
log "Setting up SSL certificates..."
if [ -z "$DOMAIN" ]; then
warning "No domain specified, skipping SSL setup"
return
fi
apt install -y certbot
# Obtain certificates
certbot certonly --standalone -d "$DOMAIN" --agree-tos --non-interactive --email admin@$DOMAIN || {
warning "SSL setup failed, continuing without SSL"
return
}
# Auto-renewal cron job
(crontab -l 2>/dev/null; echo "0 12 * * * /usr/bin/certbot renew --quiet") | crontab -
success "SSL certificates configured"
}
# ==========================================
# Create Environment File
# ==========================================
setup_env() {
log "Creating environment configuration..."
cd "$DEPLOY_DIR/server"
# Generate secrets
JWT_SECRET=$(openssl rand -base64 32)
POSTGRES_PASSWORD=$(openssl rand -base64 24)
GITEA_SECRET_KEY=$(openssl rand -base64 32)
WEBDAV_PASSWORD=$(openssl rand -base64 16)
cat > .env << EOF
# Domain configuration
GITEA_DOMAIN=${DOMAIN:-localhost}
# Gitea Configuration
GITEA_TOKEN=
GITEA_SECRET_KEY=$GITEA_SECRET_KEY
# Security
JWT_SECRET=$JWT_SECRET
# WebDAV
WEBDAV_USER=admin
WEBDAV_PASSWORD=$WEBDAV_PASSWORD
# Database
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
# API Configuration
API_PORT=8080
EOF
success "Environment file created"
warning "Please edit .env file and add your GITEA_TOKEN after first Gitea setup"
}
# ==========================================
# Create Systemd Service
# ==========================================
setup_systemd() {
log "Creating systemd service..."
cat > /etc/systemd/system/nanaxe-server.service << EOF
[Unit]
Description=Nanaxe Server
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=$DEPLOY_DIR/server
ExecStart=/usr/bin/docker compose -f docker-compose.prod.yml up -d
ExecStop=/usr/bin/docker compose -f docker-compose.prod.yml down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable nanaxe-server.service
success "Systemd service created"
}
# ==========================================
# Interactive Setup
# ==========================================
interactive_setup() {
echo ""
echo "======================================"
echo "Nanaxe Server VPS Setup"
echo "======================================"
echo ""
read -p "Enter your GitHub repository URL (or leave empty to skip clone): " REPO_URL
read -p "Enter your domain name (or leave empty for local setup): " DOMAIN
echo ""
log "Starting setup with:"
log " Repository: ${REPO_URL:-None}"
log " Domain: ${DOMAIN:-localhost}"
echo ""
read -p "Continue? (y/n): " confirm
if [ "$confirm" != "y" ]; then
error "Setup cancelled"
exit 1
fi
}
# ==========================================
# Main
# ==========================================
main() {
interactive_setup
log "Starting VPS setup..."
update_system
install_docker
setup_firewall
setup_fail2ban
setup_deploy_dir
setup_ssl
setup_env
setup_systemd
echo ""
success "======================================"
success "VPS Setup Complete!"
success "======================================"
echo ""
log "Next steps:"
echo " 1. Edit $DEPLOY_DIR/server/.env with your settings"
echo " 2. Run: cd $DEPLOY_DIR/server && docker compose -f docker-compose.prod.yml up -d"
echo " 3. Open Gitea at http://${DOMAIN:-your-server-ip}:3000"
echo " 4. Create admin account and API token"
echo " 5. Add GITEA_TOKEN to .env file"
echo " 6. Restart: systemctl restart nanaxe-server"
echo ""
log "Useful commands:"
echo " - View logs: docker compose -f docker-compose.prod.yml logs -f"
echo " - Restart: systemctl restart nanaxe-server"
echo " - Update: cd $DEPLOY_DIR && git pull && systemctl restart nanaxe-server"
echo ""
}
main "$@"