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
+236
View File
@@ -0,0 +1,236 @@
name: 🚀 Deploy Nanaxe Server
on:
push:
branches: [ main, master ]
paths:
- 'server/**'
- '.github/workflows/**'
pull_request:
branches: [ main, master ]
paths:
- 'server/**'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/nanaxe-api
jobs:
# ==========================================
# Build and Test
# ==========================================
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: 📥 Checkout repository
uses: actions/checkout@v4
- name: 🐳 Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 📦 Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: 🏷️ Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: 🔨 Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ./server/api
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
- name: 🧪 Test Docker image
run: |
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest node --version
# ==========================================
# Deploy to Production
# ==========================================
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
environment:
name: production
url: http://${{ secrets.VPS_HOST }}
steps:
- name: 📥 Checkout repository
uses: actions/checkout@v4
- name: 🔐 Setup SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.VPS_SSH_KEY }}
- name: 🚀 Deploy to VPS
env:
VPS_HOST: ${{ secrets.VPS_HOST }}
VPS_USER: ${{ secrets.VPS_USER }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
run: |
# Add host to known hosts
mkdir -p ~/.ssh
ssh-keyscan -H $VPS_HOST >> ~/.ssh/known_hosts
# Create deployment script
cat > deploy.sh << 'EOF'
#!/bin/bash
set -e
echo "🔄 Starting deployment..."
cd ${{ secrets.DEPLOY_PATH }}
# Backup current state
echo "💾 Creating backup..."
if [ -f docker-compose.yml ]; then
docker compose exec -T postgres pg_dump -U gitea gitea > backup/db-$(date +%Y%m%d-%H%M%S).sql 2>/dev/null || true
fi
# Pull latest changes
echo "📥 Pulling latest code..."
git fetch origin
git reset --hard origin/$(git rev-parse --abbrev-ref HEAD)
# Update environment variables
echo "⚙️ Updating environment..."
cat > .env << 'ENVFILE'
GITEA_DOMAIN=${{ secrets.GITEA_DOMAIN }}
GITEA_TOKEN=${{ secrets.GITEA_TOKEN }}
JWT_SECRET=${{ secrets.JWT_SECRET }}
WEBDAV_USER=${{ secrets.WEBDAV_USER }}
WEBDAV_PASSWORD=${{ secrets.WEBDAV_PASSWORD }}
POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}
API_PORT=8080
ENVFILE
# Update image tag in docker-compose
sed -i "s|image:.*nanaxe-api.*|image: ghcr.io/${{ github.repository }}/nanaxe-api:latest|" docker-compose.prod.yml || true
# Pull and deploy
echo "🐳 Pulling Docker images..."
docker compose -f docker-compose.yml pull
echo "🚀 Starting services..."
docker compose -f docker-compose.yml up -d --remove-orphans
# Health check
echo "🏥 Running health checks..."
sleep 10
# Check API health
if curl -sf http://localhost:8080/health > /dev/null; then
echo "✅ API is healthy"
else
echo "❌ API health check failed"
docker compose logs --tail=50 api
exit 1
fi
# Check Gitea
if curl -sf http://localhost:3000/api/v1/version > /dev/null; then
echo "✅ Gitea is healthy"
else
echo "⚠️ Gitea may still be initializing"
fi
# Cleanup
echo "🧹 Cleaning up..."
docker system prune -f
docker volume prune -f
echo "✅ Deployment completed successfully!"
EOF
chmod +x deploy.sh
# Copy files to server
echo "📤 Copying files to server..."
scp -r server/docker-compose.yml server/nginx $VPS_USER@$VPS_HOST:$DEPLOY_PATH/
scp deploy.sh $VPS_USER@$VPS_HOST:$DEPLOY_PATH/
# Execute deployment
echo "🚀 Executing deployment..."
ssh $VPS_USER@$VPS_HOST "cd $DEPLOY_PATH && bash deploy.sh"
- name: 🔔 Notify on success
if: success()
run: |
echo "✅ Deployment completed successfully!"
echo "🌐 Server: http://${{ secrets.VPS_HOST }}"
- name: 🔔 Notify on failure
if: failure()
run: |
echo "❌ Deployment failed!"
echo "Check logs for details"
# ==========================================
# Rollback (manual trigger)
# ==========================================
rollback:
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch'
environment: production
steps:
- name: 🔐 Setup SSH
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.VPS_SSH_KEY }}
- name: ⏮️ Rollback to previous version
env:
VPS_HOST: ${{ secrets.VPS_HOST }}
VPS_USER: ${{ secrets.VPS_USER }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
run: |
ssh-keyscan -H $VPS_HOST >> ~/.ssh/known_hosts
ssh $VPS_USER@$VPS_HOST << 'EOF'
cd $DEPLOY_PATH
echo "⏮️ Rolling back..."
# Get previous image
PREVIOUS_IMAGE=$(docker images ghcr.io/${{ github.repository }}/nanaxe-api --format "{{.Repository}}:{{.Tag}}" | grep -v latest | head -1)
if [ -n "$PREVIOUS_IMAGE" ]; then
echo "Reverting to: $PREVIOUS_IMAGE"
docker compose stop api
docker compose 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
echo "✅ Rollback completed"
else
echo "❌ No previous image found"
exit 1
fi
EOF
+101
View File
@@ -0,0 +1,101 @@
name: 🔍 PR Checks
on:
pull_request:
branches: [ main, master ]
paths:
- 'server/**'
- '.github/workflows/**'
jobs:
# ==========================================
# Lint and Test
# ==========================================
lint-and-test:
runs-on: ubuntu-latest
steps:
- name: 📥 Checkout repository
uses: actions/checkout@v4
- name: ⬢ Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: server/api/package-lock.json
- name: 📦 Install dependencies
working-directory: ./server/api
run: |
npm ci
- name: 🔍 Lint code
working-directory: ./server/api
run: |
npm run lint || echo "No lint script, skipping"
- name: 🧪 Run tests
working-directory: ./server/api
run: |
npm test || echo "No test script, skipping"
# ==========================================
# Build Test
# ==========================================
build-test:
runs-on: ubuntu-latest
steps:
- name: 📥 Checkout repository
uses: actions/checkout@v4
- name: 🐳 Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 🔨 Build Docker image (test only)
uses: docker/build-push-action@v5
with:
context: ./server/api
push: false
load: true
tags: nanaxe-api:test
cache-from: type=gha
- name: 🧪 Test Docker image
run: |
docker run --rm nanaxe-api:test node --version
docker run --rm nanaxe-api:test npm --version
- name: 🔍 Scan for vulnerabilities
uses: aquasecurity/trivy-action@master
with:
image-ref: nanaxe-api:test
format: 'table'
exit-code: '0'
ignore-unfixed: true
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'
# ==========================================
# Docker Compose Validation
# ==========================================
compose-validation:
runs-on: ubuntu-latest
steps:
- name: 📥 Checkout repository
uses: actions/checkout@v4
- name: 🐳 Validate Docker Compose
working-directory: ./server
run: |
docker compose config
- name: 🔍 Check for secrets
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: main
head: HEAD
extra_args: --debug --only-verified