Initial commit: Full project structure
- Backend: NestJS with Docker - Frontend: Next.js with Docker - Nginx configuration for reverse proxy - PostgreSQL setup - Docker compose for orchestration - Development environment configuration
This commit is contained in:
22
infrastructure/.env.example
Normal file
22
infrastructure/.env.example
Normal file
@@ -0,0 +1,22 @@
|
||||
# Docker Hub Configuration
|
||||
DOCKER_USERNAME=yourdockerhub
|
||||
TAG=v1.0.0
|
||||
|
||||
# Domain Configuration
|
||||
DOMAIN=dyolink.com
|
||||
|
||||
# Database Environment (create database.env from this)
|
||||
# POSTGRES_USER=dyolink_user
|
||||
# POSTGRES_PASSWORD=CHANGE_THIS_IN_PRODUCTION
|
||||
# POSTGRES_DB=dyolink_db
|
||||
|
||||
# Backend Environment (create backend.env from this)
|
||||
# NODE_ENV=production
|
||||
# JWT_SECRET=CHANGE_THIS_TO_STRONG_SECRET_32_CHARS
|
||||
# DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
|
||||
# CORS_ORIGIN=https://dyolink.com
|
||||
|
||||
# Frontend Environment (create frontend.env from this)
|
||||
# NEXT_PUBLIC_API_URL=/api
|
||||
# NEXT_PUBLIC_APP_NAME=Dyolink
|
||||
# NEXT_PUBLIC_APP_URL=https://dyolink.com
|
||||
21
infrastructure/database/Dockerfile
Normal file
21
infrastructure/database/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM postgres:15-alpine
|
||||
|
||||
# Set timezone
|
||||
ENV TZ=UTC
|
||||
|
||||
# Copy initialization script
|
||||
COPY init.sql /docker-entrypoint-initdb.d/
|
||||
|
||||
# Copy backup script
|
||||
COPY backup.sh /usr/local/bin/backup.sh
|
||||
RUN chmod +x /usr/local/bin/backup.sh
|
||||
|
||||
# Create backup directory
|
||||
RUN mkdir -p /backups && chown postgres:postgres /backups
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
||||
CMD pg_isready -U ${POSTGRES_USER} || exit 1
|
||||
|
||||
# Run as non-root
|
||||
USER postgres
|
||||
8
infrastructure/database/init.sql
Normal file
8
infrastructure/database/init.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
-- This file is optional with Prisma
|
||||
-- Prisma will create all tables via migrations
|
||||
|
||||
-- Only add extensions that Prisma can't create
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- That's it! No tables needed here.
|
||||
118
infrastructure/docker-compose.prod.yml
Normal file
118
infrastructure/docker-compose.prod.yml
Normal file
@@ -0,0 +1,118 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: dyolink_db_prod
|
||||
env_file:
|
||||
- database.env
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_USER}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=${POSTGRES_DB:-dyolink_db}
|
||||
- TZ=UTC
|
||||
ports:
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- postgres_data_prod:/var/lib/postgresql/data
|
||||
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
||||
- ./database/backups:/backups
|
||||
networks:
|
||||
- dyolink_network
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
backend:
|
||||
image: ${DOCKER_USERNAME}/dyolink-backend:${TAG:-latest}
|
||||
container_name: dyolink_backend_prod
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- backend.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- TZ=UTC
|
||||
- PORT=3000
|
||||
ports:
|
||||
- "4001:3000"
|
||||
networks:
|
||||
- dyolink_network
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/health', (r) => {if(r.statusCode!==200)process.exit(1)})"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
frontend:
|
||||
image: ${DOCKER_USERNAME}/dyolink-frontend:${TAG:-latest}
|
||||
container_name: dyolink_frontend_prod
|
||||
depends_on:
|
||||
- backend
|
||||
env_file:
|
||||
- frontend.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- TZ=UTC
|
||||
- PORT=3000
|
||||
ports:
|
||||
- "4000:3000"
|
||||
networks:
|
||||
- dyolink_network
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000', (r) => {if(r.statusCode!==200)process.exit(1)})"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: dyolink_nginx_prod
|
||||
depends_on:
|
||||
- backend
|
||||
- frontend
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ./ssl:/etc/nginx/ssl:ro
|
||||
- ./logs/nginx:/var/log/nginx
|
||||
networks:
|
||||
- dyolink_network
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
networks:
|
||||
dyolink_network:
|
||||
driver: bridge
|
||||
name: dyolink_network
|
||||
|
||||
volumes:
|
||||
postgres_data_prod:
|
||||
name: dyolink_postgres_data_prod
|
||||
92
infrastructure/docker-compose.yml
Normal file
92
infrastructure/docker-compose.yml
Normal file
@@ -0,0 +1,92 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: dyolink_db_container
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: 1234
|
||||
POSTGRES_DB: dyolink_db
|
||||
ports:
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- postgres_data_dev:/var/lib/postgresql/data
|
||||
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
||||
- ./database/backups:/backups
|
||||
networks:
|
||||
- dyolink_network
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
backend:
|
||||
build: ../backend
|
||||
container_name: dyolink_backend_container
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- ../backend/.env
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://postgres:1234@postgres:5432/dyolink_db
|
||||
- FRONTEND_URL=http://frontend:3000
|
||||
- PORT=3000
|
||||
ports:
|
||||
- "4001:3000"
|
||||
volumes:
|
||||
- ../backend:/app:rw
|
||||
- /app/node_modules
|
||||
networks:
|
||||
- dyolink_network
|
||||
command: npm run start:dev
|
||||
restart: unless-stopped
|
||||
|
||||
frontend:
|
||||
build: ../frontend
|
||||
container_name: dyolink_frontend_container
|
||||
depends_on:
|
||||
- backend
|
||||
env_file:
|
||||
- ../frontend/.env.local
|
||||
environment:
|
||||
- NEXT_PUBLIC_API_URL=http://localhost:4001/api
|
||||
- NEXT_PUBLIC_APP_URL=http://localhost:4000
|
||||
- PORT=3000
|
||||
ports:
|
||||
- "4000:3000"
|
||||
volumes:
|
||||
- ../frontend:/app:rw
|
||||
- /app/node_modules
|
||||
- /app/.next
|
||||
networks:
|
||||
- dyolink_network
|
||||
command: npm run dev
|
||||
restart: unless-stopped
|
||||
|
||||
nginx:
|
||||
build: ./nginx
|
||||
container_name: dyolink_nginx_container
|
||||
depends_on:
|
||||
- backend
|
||||
- frontend
|
||||
ports:
|
||||
- "8080:80"
|
||||
- "8443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ./ssl:/etc/nginx/ssl:ro
|
||||
- ./logs/nginx:/var/log/nginx
|
||||
networks:
|
||||
- dyolink_network
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
dyolink_network:
|
||||
driver: bridge
|
||||
name: dyolink_network
|
||||
|
||||
volumes:
|
||||
postgres_data_dev:
|
||||
name: dyolink_postgres_data_dev
|
||||
19
infrastructure/nginx/Dockerfile
Normal file
19
infrastructure/nginx/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
# Remove default configuration
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy custom configuration
|
||||
COPY nginx.conf /etc/nginx/conf.d/
|
||||
|
||||
# Create log directory
|
||||
RUN mkdir -p /var/log/nginx && \
|
||||
chown -R nginx:nginx /var/log/nginx && \
|
||||
chmod -R 755 /var/log/nginx
|
||||
|
||||
# Switch to non-root user
|
||||
USER nginx
|
||||
|
||||
EXPOSE 80 443
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
103
infrastructure/nginx/nginx.conf
Normal file
103
infrastructure/nginx/nginx.conf
Normal file
@@ -0,0 +1,103 @@
|
||||
# Upstream servers using Docker service names (internal network)
|
||||
upstream dyolink_backend {
|
||||
server backend:3000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
upstream dyolink_frontend {
|
||||
server frontend:3000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# HTTP Server (redirects to HTTPS)
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name dyolink.com www.dyolink.com;
|
||||
|
||||
# Redirect all HTTP to HTTPS
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
# HTTPS Server
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name dyolink.com www.dyolink.com;
|
||||
|
||||
# SSL Configuration - Replace with your actual certificates
|
||||
ssl_certificate /etc/nginx/ssl/dyolink.com.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/dyolink.com.key;
|
||||
|
||||
# SSL Security
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_proxied expired no-cache no-store private auth;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
|
||||
# Client settings
|
||||
client_max_body_size 50M;
|
||||
client_body_timeout 12;
|
||||
client_header_timeout 12;
|
||||
keepalive_timeout 15;
|
||||
send_timeout 10;
|
||||
|
||||
# Frontend (Next.js)
|
||||
location / {
|
||||
proxy_pass http://dyolink_frontend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_read_timeout 300;
|
||||
proxy_connect_timeout 300;
|
||||
}
|
||||
|
||||
# Backend API
|
||||
location /api {
|
||||
proxy_pass http://dyolink_backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_read_timeout 300;
|
||||
proxy_connect_timeout 300;
|
||||
}
|
||||
|
||||
# Health check endpoint (no logging)
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# Deny access to hidden files
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
}
|
||||
24
infrastructure/scripts/backup.sh
Normal file
24
infrastructure/scripts/backup.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
BACKUP_DIR="/backups"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_NAME="dyolink_backup_$TIMESTAMP"
|
||||
BACKUP_FILE="$BACKUP_DIR/$BACKUP_NAME.sql.gz"
|
||||
|
||||
echo "[$(date)] Starting database backup: $BACKUP_NAME"
|
||||
|
||||
# Perform backup
|
||||
pg_dumpall -U $POSTGRES_USER | gzip > $BACKUP_FILE
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
BACKUP_SIZE=$(du -h $BACKUP_FILE | cut -f1)
|
||||
echo "[$(date)] ✅ Backup completed: $BACKUP_NAME ($BACKUP_SIZE)"
|
||||
|
||||
# Keep only last 7 days of backups
|
||||
find $BACKUP_DIR -name "dyolink_backup_*.sql.gz" -mtime +7 -delete
|
||||
echo "[$(date)] Cleaned up backups older than 7 days"
|
||||
else
|
||||
echo "[$(date)] ❌ Backup failed!"
|
||||
exit 1
|
||||
fi
|
||||
105
infrastructure/scripts/build-and-push.sh
Normal file
105
infrastructure/scripts/build-and-push.sh
Normal file
@@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Dyolink - Build & Push ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
|
||||
# Load environment
|
||||
if [ -f .env ]; then
|
||||
source .env
|
||||
echo -e "${GREEN}✓ Loaded .env${NC}"
|
||||
fi
|
||||
|
||||
# Get version
|
||||
read -p "Enter version tag (e.g., v1.0.0, default: latest): " TAG
|
||||
TAG=${TAG:-latest}
|
||||
echo -e "${YELLOW}Using tag: ${TAG}${NC}"
|
||||
|
||||
# Get Docker Hub username
|
||||
read -p "Docker Hub username [${DOCKER_USERNAME:-yourdockerhub}]: " USERNAME
|
||||
USERNAME=${USERNAME:-${DOCKER_USERNAME:-yourdockerhub}}
|
||||
|
||||
# Check Docker
|
||||
if ! docker info > /dev/null 2>&1; then
|
||||
echo -e "${RED}✗ Docker is not running!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Login
|
||||
echo -e "${YELLOW}Logging into Docker Hub...${NC}"
|
||||
docker login --username $USERNAME
|
||||
|
||||
echo -e "\n${GREEN}=== Building Images ===${NC}"
|
||||
|
||||
# Build backend
|
||||
echo -e "${YELLOW}Building backend...${NC}"
|
||||
docker build -t $USERNAME/dyolink-backend:$TAG -t $USERNAME/dyolink-backend:latest ../backend
|
||||
echo -e "${GREEN}✓ Backend built${NC}"
|
||||
|
||||
# Build frontend
|
||||
echo -e "${YELLOW}Building frontend...${NC}"
|
||||
docker build -t $USERNAME/dyolink-frontend:$TAG -t $USERNAME/dyolink-frontend:latest ../frontend
|
||||
echo -e "${GREEN}✓ Frontend built${NC}"
|
||||
|
||||
echo -e "\n${GREEN}=== Pushing to Docker Hub ===${NC}"
|
||||
|
||||
# Push backend
|
||||
echo -e "${YELLOW}Pushing backend...${NC}"
|
||||
docker push $USERNAME/dyolink-backend:$TAG
|
||||
docker push $USERNAME/dyolink-backend:latest
|
||||
echo -e "${GREEN}✓ Backend pushed${NC}"
|
||||
|
||||
# Push frontend
|
||||
echo -e "${YELLOW}Pushing frontend...${NC}"
|
||||
docker push $USERNAME/dyolink-frontend:$TAG
|
||||
docker push $USERNAME/dyolink-frontend:latest
|
||||
echo -e "${GREEN}✓ Frontend pushed${NC}"
|
||||
|
||||
# Create deployment package
|
||||
echo -e "\n${YELLOW}Creating deployment package...${NC}"
|
||||
DEPLOY_DIR="dyolink-deploy-$TAG"
|
||||
mkdir -p $DEPLOY_DIR
|
||||
cp docker-compose.prod.yml $DEPLOY_DIR/
|
||||
cp -r nginx/nginx.conf $DEPLOY_DIR/
|
||||
cp database/init.sql $DEPLOY_DIR/
|
||||
cp scripts/deploy.sh $DEPLOY_DIR/
|
||||
cp .env.example $DEPLOY_DIR/
|
||||
|
||||
# Create env templates
|
||||
echo "# Database" > $DEPLOY_DIR/database.env.example
|
||||
echo "POSTGRES_USER=dyolink_user" >> $DEPLOY_DIR/database.env.example
|
||||
echo "POSTGRES_PASSWORD=CHANGE_ME" >> $DEPLOY_DIR/database.env.example
|
||||
echo "POSTGRES_DB=dyolink_db" >> $DEPLOY_DIR/database.env.example
|
||||
|
||||
echo "# Backend" > $DEPLOY_DIR/backend.env.example
|
||||
echo "NODE_ENV=production" >> $DEPLOY_DIR/backend.env.example
|
||||
echo "JWT_SECRET=CHANGE_ME_32_CHARS_MINIMUM" >> $DEPLOY_DIR/backend.env.example
|
||||
echo "DATABASE_URL=postgresql://\${POSTGRES_USER}:\${POSTGRES_PASSWORD}@postgres:5432/\${POSTGRES_DB}" >> $DEPLOY_DIR/backend.env.example
|
||||
echo "CORS_ORIGIN=https://dyolink.com" >> $DEPLOY_DIR/backend.env.example
|
||||
echo "FRONTEND_URL=https://dyolink.com" >> $DEPLOY_DIR/backend.env.example
|
||||
|
||||
echo "# Frontend" > $DEPLOY_DIR/frontend.env.example
|
||||
echo "NEXT_PUBLIC_API_URL=/api" >> $DEPLOY_DIR/frontend.env.example
|
||||
echo "NEXT_PUBLIC_APP_NAME=Dyolink" >> $DEPLOY_DIR/frontend.env.example
|
||||
echo "NEXT_PUBLIC_APP_URL=https://dyolink.com" >> $DEPLOY_DIR/frontend.env.example
|
||||
|
||||
# Package
|
||||
tar -czf $DEPLOY_DIR.tar.gz $DEPLOY_DIR/
|
||||
rm -rf $DEPLOY_DIR
|
||||
echo -e "${GREEN}✓ Created: ${DEPLOY_DIR}.tar.gz${NC}"
|
||||
|
||||
echo -e "\n${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Build Complete! ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
echo -e "${GREEN}Pushed:${NC}"
|
||||
echo -e " • ${USERNAME}/dyolink-backend:${TAG}"
|
||||
echo -e " • ${USERNAME}/dyolink-frontend:${TAG}"
|
||||
echo -e "\n${YELLOW}Next: scp ${DEPLOY_DIR}.tar.gz user@server:/tmp/${NC}"
|
||||
68
infrastructure/scripts/deploy.sh
Normal file
68
infrastructure/scripts/deploy.sh
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Dyolink - Deploy Tool ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
|
||||
# Load environment
|
||||
if [ -f .env ]; then
|
||||
source .env
|
||||
echo -e "${GREEN}✓ Loaded .env${NC}"
|
||||
fi
|
||||
|
||||
# Check for env files
|
||||
ENV_FILES=("database.env" "backend.env" "frontend.env")
|
||||
for env_file in "${ENV_FILES[@]}"; do
|
||||
if [ ! -f "$env_file" ]; then
|
||||
echo -e "${YELLOW}⚠ $env_file not found, creating from example${NC}"
|
||||
if [ -f "${env_file}.example" ]; then
|
||||
cp "${env_file}.example" "$env_file"
|
||||
echo -e "${RED}❕ Edit $env_file with production values!${NC}"
|
||||
read -p "Continue? (y/n): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Create directories
|
||||
mkdir -p logs/nginx database/backups ssl
|
||||
|
||||
# Get version
|
||||
read -p "Enter version to deploy (default: ${TAG:-latest}): " DEPLOY_TAG
|
||||
DEPLOY_TAG=${DEPLOY_TAG:-${TAG:-latest}}
|
||||
export TAG=$DEPLOY_TAG
|
||||
export DOCKER_USERNAME=${DOCKER_USERNAME:-yourdockerhub}
|
||||
|
||||
# Pull images
|
||||
echo -e "${YELLOW}Pulling images from Docker Hub...${NC}"
|
||||
docker-compose -f docker-compose.prod.yml pull
|
||||
|
||||
# Stop old
|
||||
echo -e "${YELLOW}Stopping old containers...${NC}"
|
||||
docker-compose -f docker-compose.prod.yml down
|
||||
|
||||
# Start new
|
||||
echo -e "${YELLOW}Starting containers...${NC}"
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# Wait for health
|
||||
echo -e "${YELLOW}Waiting for services...${NC}"
|
||||
sleep 10
|
||||
|
||||
# Show status
|
||||
echo -e "\n${GREEN}=== Status ===${NC}"
|
||||
docker-compose -f docker-compose.prod.yml ps
|
||||
|
||||
echo -e "\n${BLUE}╔════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Deployment Complete! ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════╝${NC}"
|
||||
32
infrastructure/scripts/logs.sh
Normal file
32
infrastructure/scripts/logs.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo "=== Dyolink Health Monitor ==="
|
||||
echo "Time: $(date)"
|
||||
echo ""
|
||||
|
||||
# Check containers
|
||||
echo "Container Status:"
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Health}}" | grep dyolink
|
||||
|
||||
# Check resources
|
||||
echo -e "\nResource Usage:"
|
||||
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" | grep dyolink
|
||||
|
||||
# Check disk
|
||||
DISK=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
|
||||
if [ $DISK -gt 80 ]; then
|
||||
echo -e "\n${RED}⚠ WARNING: Disk usage at ${DISK}%${NC}"
|
||||
else
|
||||
echo -e "\n${GREEN}✓ Disk usage: ${DISK}%${NC}"
|
||||
fi
|
||||
|
||||
# Check API
|
||||
echo -e "\nAPI Health:"
|
||||
curl -s -o /dev/null -w "Backend API: %{http_code}\n" http://localhost:3001/api/health || echo "Backend API: DOWN"
|
||||
curl -s -o /dev/null -w "Frontend: %{http_code}\n" http://localhost:3002 || echo "Frontend: DOWN"
|
||||
127
infrastructure/scripts/manage.sh
Normal file
127
infrastructure/scripts/manage.sh
Normal file
@@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
COMPOSE_FILE="docker-compose.prod.yml"
|
||||
|
||||
show_help() {
|
||||
echo -e "${BLUE}Dyolink Management Tool${NC}"
|
||||
echo ""
|
||||
echo "Usage: $0 {start|stop|restart|logs|status|backup|update|cleanup|monitor}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " start - Start all services"
|
||||
echo " stop - Stop all services"
|
||||
echo " restart - Restart all services"
|
||||
echo " logs - Show logs (add -f to follow)"
|
||||
echo " status - Show container status"
|
||||
echo " backup - Backup database"
|
||||
echo " update - Pull and restart with latest images"
|
||||
echo " cleanup - Clean old containers/images"
|
||||
echo " monitor - Show health status"
|
||||
}
|
||||
|
||||
check_compose() {
|
||||
if [ ! -f "$COMPOSE_FILE" ]; then
|
||||
echo -e "${RED}Error: $COMPOSE_FILE not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
check_compose
|
||||
echo -e "${GREEN}Starting services...${NC}"
|
||||
docker-compose -f $COMPOSE_FILE up -d
|
||||
;;
|
||||
stop)
|
||||
check_compose
|
||||
echo -e "${YELLOW}Stopping services...${NC}"
|
||||
docker-compose -f $COMPOSE_FILE down
|
||||
;;
|
||||
restart)
|
||||
check_compose
|
||||
echo -e "${YELLOW}Restarting services...${NC}"
|
||||
docker-compose -f $COMPOSE_FILE restart
|
||||
;;
|
||||
logs)
|
||||
check_compose
|
||||
echo -e "${GREEN}Showing logs...${NC}"
|
||||
docker-compose -f $COMPOSE_FILE logs ${@:2}
|
||||
;;
|
||||
status)
|
||||
check_compose
|
||||
echo -e "${GREEN}Container Status:${NC}"
|
||||
docker-compose -f $COMPOSE_FILE ps
|
||||
echo -e "\n${GREEN}Running Containers:${NC}"
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep dyolink
|
||||
;;
|
||||
backup)
|
||||
check_compose
|
||||
echo -e "${GREEN}Backing up database...${NC}"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_FILE="dyolink_backup_$TIMESTAMP.sql.gz"
|
||||
docker-compose -f $COMPOSE_FILE exec -T postgres pg_dumpall -U postgres | gzip > $BACKUP_FILE
|
||||
if [ $? -eq 0 ]; then
|
||||
SIZE=$(du -h $BACKUP_FILE | cut -f1)
|
||||
echo -e "${GREEN}✓ Backup saved: $BACKUP_FILE ($SIZE)${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Backup failed${NC}"
|
||||
fi
|
||||
;;
|
||||
update)
|
||||
check_compose
|
||||
echo -e "${GREEN}Pulling latest images...${NC}"
|
||||
docker-compose -f $COMPOSE_FILE pull
|
||||
docker-compose -f $COMPOSE_FILE up -d
|
||||
echo -e "${GREEN}✓ Update complete!${NC}"
|
||||
;;
|
||||
cleanup)
|
||||
echo -e "${YELLOW}Cleaning up Docker resources...${NC}"
|
||||
docker container prune -f
|
||||
docker image prune -f
|
||||
docker volume prune -f
|
||||
docker network prune -f
|
||||
echo -e "${GREEN}✓ Cleanup complete!${NC}"
|
||||
;;
|
||||
monitor)
|
||||
check_compose
|
||||
echo -e "${GREEN}Health Monitoring:${NC}\n"
|
||||
|
||||
SERVICES=("postgres" "backend" "frontend" "nginx")
|
||||
ALL_HEALTHY=true
|
||||
|
||||
for SERVICE in "${SERVICES[@]}"; do
|
||||
STATUS=$(docker-compose -f $COMPOSE_FILE ps $SERVICE 2>/dev/null | tail -1)
|
||||
if echo "$STATUS" | grep -q "Up"; then
|
||||
echo -e " ${GREEN}✓${NC} $SERVICE: Running"
|
||||
else
|
||||
echo -e " ${RED}✗${NC} $SERVICE: Not running"
|
||||
ALL_HEALTHY=false
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
DISK=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
|
||||
if [ $DISK -gt 80 ]; then
|
||||
echo -e "${RED}⚠ Disk usage: ${DISK}%${NC}"
|
||||
ALL_HEALTHY=false
|
||||
else
|
||||
echo -e "${GREEN}✓ Disk usage: ${DISK}%${NC}"
|
||||
fi
|
||||
|
||||
if [ "$ALL_HEALTHY" = true ]; then
|
||||
echo -e "\n${GREEN}✅ All systems operational${NC}"
|
||||
else
|
||||
echo -e "\n${YELLOW}⚠ Issues detected${NC}"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
32
infrastructure/scripts/monitor.sh
Normal file
32
infrastructure/scripts/monitor.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo "=== Dyolink Health Monitor ==="
|
||||
echo "Time: $(date)"
|
||||
echo ""
|
||||
|
||||
# Check containers
|
||||
echo "Container Status:"
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Health}}" | grep dyolink
|
||||
|
||||
# Check resources
|
||||
echo -e "\nResource Usage:"
|
||||
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" | grep dyolink
|
||||
|
||||
# Check disk
|
||||
DISK=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
|
||||
if [ $DISK -gt 80 ]; then
|
||||
echo -e "\n${RED}⚠ WARNING: Disk usage at ${DISK}%${NC}"
|
||||
else
|
||||
echo -e "\n${GREEN}✓ Disk usage: ${DISK}%${NC}"
|
||||
fi
|
||||
|
||||
# Check API
|
||||
echo -e "\nAPI Health:"
|
||||
curl -s -o /dev/null -w "Backend API: %{http_code}\n" http://localhost:3001/api/health || echo "Backend API: DOWN"
|
||||
curl -s -o /dev/null -w "Frontend: %{http_code}\n" http://localhost:3002 || echo "Frontend: DOWN"
|
||||
Reference in New Issue
Block a user