configured app to run on custom port with apache reverse proxy, wrote deploy.sh script
This commit is contained in:
parent
490d560306
commit
c66d498d04
11 changed files with 277 additions and 29 deletions
|
|
@ -215,7 +215,7 @@ events {
|
|||
|
||||
http {
|
||||
upstream backend {
|
||||
server backend:8000;
|
||||
server backend:8048;
|
||||
}
|
||||
|
||||
upstream frontend {
|
||||
|
|
@ -405,7 +405,7 @@ docker-compose -f docker-compose.prod.yml exec backend python -c "from app.datab
|
|||
docker-compose -f docker-compose.prod.yml logs frontend
|
||||
|
||||
# Check if backend is accessible
|
||||
curl http://backend:8000/health
|
||||
curl http://backend:8048/health
|
||||
```
|
||||
|
||||
### SSL certificate issues
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
4. Configure:
|
||||
- Name: "APAC Ops Bot"
|
||||
- Supported account types: "Accounts in this organizational directory only"
|
||||
- Redirect URI: `http://localhost:8000/api/v1/auth/msal/callback`
|
||||
- Redirect URI: `http://localhost:8048/api/v1/auth/msal/callback`
|
||||
5. After registration, note down:
|
||||
- **Application (client) ID**
|
||||
- **Directory (tenant) ID**
|
||||
|
|
@ -81,9 +81,9 @@ docker-compose logs postgres
|
|||
docker-compose up --build backend
|
||||
```
|
||||
|
||||
The backend will be available at: **http://localhost:8000**
|
||||
- API Docs: http://localhost:8000/docs
|
||||
- Health Check: http://localhost:8000/health
|
||||
The backend will be available at: **http://localhost:8048**
|
||||
- API Docs: http://localhost:8048/docs
|
||||
- Health Check: http://localhost:8048/health
|
||||
|
||||
### Step 4: Run Database Migrations
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ The frontend will be available at: **http://localhost:3000**
|
|||
|
||||
```bash
|
||||
# Health check
|
||||
curl http://localhost:8000/health
|
||||
curl http://localhost:8048/health
|
||||
|
||||
# Expected response:
|
||||
# {"status":"healthy","app":"Seapac Ops Bot","environment":"development"}
|
||||
|
|
@ -184,7 +184,7 @@ apac-ops-bot/
|
|||
**Solution:**
|
||||
```bash
|
||||
# Check what's using the port
|
||||
lsof -i :8000 # or :5432, :6379
|
||||
lsof -i :8048 # or :5432, :6379
|
||||
|
||||
# Stop existing containers
|
||||
docker-compose down
|
||||
|
|
|
|||
12
README.md
12
README.md
|
|
@ -68,7 +68,7 @@ cp .env.example .env
|
|||
**Required Frontend environment variables:**
|
||||
- `REACT_APP_AZURE_CLIENT_ID` - Same as backend Azure client ID
|
||||
- `REACT_APP_AZURE_TENANT_ID` - Same as backend Azure tenant ID
|
||||
- `REACT_APP_API_URL` - Backend API URL (default: http://localhost:8000/api/v1)
|
||||
- `REACT_APP_API_URL` - Backend API URL (default: /apac-ops-bot-back/api/v1)
|
||||
|
||||
### 3. Start services with Docker Compose
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ docker-compose up --build
|
|||
This will start:
|
||||
- PostgreSQL (port 5432)
|
||||
- Redis (port 6379)
|
||||
- Backend API (port 8000)
|
||||
- Backend API (port 8048)
|
||||
- Frontend (port 3000)
|
||||
|
||||
**First run:** The backend will automatically create database tables on startup.
|
||||
|
|
@ -88,8 +88,8 @@ This will start:
|
|||
### 4. Access the application
|
||||
|
||||
- **Frontend Application:** http://localhost:3000
|
||||
- **API Documentation:** http://localhost:8000/docs
|
||||
- **Health Check:** http://localhost:8000/health
|
||||
- **API Documentation:** http://localhost:8048/docs
|
||||
- **Health Check:** http://localhost:8048/health
|
||||
|
||||
## Development
|
||||
|
||||
|
|
@ -185,8 +185,8 @@ apac-ops-bot/
|
|||
## API Documentation
|
||||
|
||||
Once the backend is running, access the interactive API documentation at:
|
||||
- Swagger UI: http://localhost:8000/docs
|
||||
- ReDoc: http://localhost:8000/redoc
|
||||
- Swagger UI: http://localhost:8048/docs
|
||||
- ReDoc: http://localhost:8048/redoc
|
||||
|
||||
## Security
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ DATABASE_URL=postgresql+asyncpg://apac_ops_bot:password@localhost:5432/apac_ops_
|
|||
AZURE_TENANT_ID=your-tenant-id
|
||||
AZURE_CLIENT_ID=your-client-id
|
||||
AZURE_CLIENT_SECRET=your-client-secret
|
||||
AZURE_REDIRECT_URI=http://localhost:8000/api/v1/auth/msal/callback
|
||||
AZURE_REDIRECT_URI=http://localhost:8048/api/v1/auth/msal/callback
|
||||
|
||||
# OpenAI Responses API
|
||||
OPENAI_API_KEY=your-openai-api-key-here
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ RUN pip install --no-cache-dir -r requirements-dev.txt
|
|||
COPY . .
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8000
|
||||
EXPOSE 8048
|
||||
|
||||
# Development command (with hot reload)
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8048", "--reload"]
|
||||
|
||||
# Stage 3: Production environment
|
||||
FROM base as production
|
||||
|
|
@ -47,7 +47,7 @@ RUN useradd -m -u 1000 appuser && \
|
|||
USER appuser
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8000
|
||||
EXPOSE 8048
|
||||
|
||||
# Production command with multiple workers
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8048", "--workers", "4"]
|
||||
|
|
|
|||
|
|
@ -78,6 +78,6 @@ if __name__ == "__main__":
|
|||
uvicorn.run(
|
||||
"app.main:app",
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
port=8048,
|
||||
reload=settings.DEBUG,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -78,13 +78,13 @@ def test_settings_azure_ad_configuration():
|
|||
AZURE_TENANT_ID="test-tenant-id",
|
||||
AZURE_CLIENT_ID="test-client-id",
|
||||
AZURE_CLIENT_SECRET="test-client-secret",
|
||||
AZURE_REDIRECT_URI="http://localhost:8000/api/v1/auth/callback",
|
||||
AZURE_REDIRECT_URI="http://localhost:8048/api/v1/auth/callback",
|
||||
)
|
||||
|
||||
assert settings.AZURE_TENANT_ID == "test-tenant-id"
|
||||
assert settings.AZURE_CLIENT_ID == "test-client-id"
|
||||
assert settings.AZURE_CLIENT_SECRET == "test-client-secret"
|
||||
assert settings.AZURE_REDIRECT_URI == "http://localhost:8000/api/v1/auth/callback"
|
||||
assert settings.AZURE_REDIRECT_URI == "http://localhost:8048/api/v1/auth/callback"
|
||||
|
||||
|
||||
def test_settings_redis_url():
|
||||
|
|
|
|||
244
deploy.sh
Executable file
244
deploy.sh
Executable file
|
|
@ -0,0 +1,244 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# APAC Ops Bot Deployment Script
|
||||
# Idempotent deployment for Ubuntu server at /opt/apac-ops-bot/
|
||||
#
|
||||
# Usage: sudo ./deploy.sh
|
||||
#
|
||||
# Note: Run 'git pull origin main' manually before running this script
|
||||
# (root user typically doesn't have SSH keys for git)
|
||||
|
||||
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
|
||||
|
||||
# Logging functions
|
||||
log_info() {
|
||||
echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] ${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] ${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] ${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] ${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# Error handler
|
||||
error_exit() {
|
||||
log_error "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
FRONTEND_DEPLOY_PATH="/var/www/html/apac-ops-bot"
|
||||
BACKEND_PORT=8048
|
||||
HEALTH_CHECK_RETRIES=30
|
||||
HEALTH_CHECK_INTERVAL=2
|
||||
|
||||
log_info "Starting APAC Ops Bot deployment..."
|
||||
log_info "Working directory: $SCRIPT_DIR"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Pre-flight checks
|
||||
# -----------------------------------------------------------------------------
|
||||
log_info "Running pre-flight checks..."
|
||||
|
||||
# Check if running as root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
error_exit "This script must be run as root (use sudo)"
|
||||
fi
|
||||
|
||||
# Check Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
error_exit "Docker is not installed"
|
||||
fi
|
||||
log_info "Docker found: $(docker --version)"
|
||||
|
||||
# Check docker-compose (try both v1 and v2 syntax)
|
||||
if command -v docker-compose &> /dev/null; then
|
||||
DOCKER_COMPOSE="docker-compose"
|
||||
elif docker compose version &> /dev/null; then
|
||||
DOCKER_COMPOSE="docker compose"
|
||||
else
|
||||
error_exit "docker-compose is not installed"
|
||||
fi
|
||||
log_info "Docker Compose found: $($DOCKER_COMPOSE version --short 2>/dev/null || $DOCKER_COMPOSE version)"
|
||||
|
||||
# Check Node.js
|
||||
if ! command -v node &> /dev/null; then
|
||||
error_exit "Node.js is not installed"
|
||||
fi
|
||||
NODE_VERSION=$(node --version)
|
||||
log_info "Node.js found: $NODE_VERSION"
|
||||
|
||||
# Check npm
|
||||
if ! command -v npm &> /dev/null; then
|
||||
error_exit "npm is not installed"
|
||||
fi
|
||||
log_info "npm found: $(npm --version)"
|
||||
|
||||
# Check .env files
|
||||
if [[ ! -f "$SCRIPT_DIR/backend/.env" ]]; then
|
||||
error_exit "Backend .env file not found at $SCRIPT_DIR/backend/.env"
|
||||
fi
|
||||
log_info "Backend .env file found"
|
||||
|
||||
if [[ ! -f "$SCRIPT_DIR/frontend/.env" ]]; then
|
||||
log_warn "Frontend .env file not found at $SCRIPT_DIR/frontend/.env (may use defaults)"
|
||||
fi
|
||||
|
||||
# Verify docker-compose.yml exists
|
||||
if [[ ! -f "$SCRIPT_DIR/docker-compose.yml" ]]; then
|
||||
error_exit "docker-compose.yml not found"
|
||||
fi
|
||||
|
||||
log_success "Pre-flight checks passed"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build Docker containers
|
||||
# -----------------------------------------------------------------------------
|
||||
log_info "Building Docker containers..."
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
$DOCKER_COMPOSE build --pull || error_exit "Docker build failed"
|
||||
|
||||
log_success "Docker containers built"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Restart Docker services
|
||||
# -----------------------------------------------------------------------------
|
||||
log_info "Starting Docker services..."
|
||||
|
||||
$DOCKER_COMPOSE up -d || error_exit "Failed to start Docker services"
|
||||
|
||||
# Wait for postgres to be healthy
|
||||
log_info "Waiting for PostgreSQL to be ready..."
|
||||
POSTGRES_READY=false
|
||||
for i in $(seq 1 $HEALTH_CHECK_RETRIES); do
|
||||
if $DOCKER_COMPOSE exec -T postgres pg_isready -U apac_ops_bot &> /dev/null; then
|
||||
POSTGRES_READY=true
|
||||
break
|
||||
fi
|
||||
log_info "Waiting for PostgreSQL... (attempt $i/$HEALTH_CHECK_RETRIES)"
|
||||
sleep $HEALTH_CHECK_INTERVAL
|
||||
done
|
||||
|
||||
if [[ "$POSTGRES_READY" != "true" ]]; then
|
||||
error_exit "PostgreSQL failed to become ready"
|
||||
fi
|
||||
|
||||
log_success "Docker services started and PostgreSQL is ready"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Run database migrations
|
||||
# -----------------------------------------------------------------------------
|
||||
log_info "Running database migrations..."
|
||||
|
||||
$DOCKER_COMPOSE exec -T backend alembic upgrade head || error_exit "Database migrations failed"
|
||||
|
||||
log_success "Database migrations completed"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Build frontend
|
||||
# -----------------------------------------------------------------------------
|
||||
log_info "Building frontend..."
|
||||
cd "$SCRIPT_DIR/frontend"
|
||||
|
||||
# Clean install dependencies
|
||||
log_info "Installing frontend dependencies..."
|
||||
npm ci || error_exit "npm ci failed"
|
||||
|
||||
# Build production bundle
|
||||
log_info "Creating production build..."
|
||||
npm run build || error_exit "Frontend build failed"
|
||||
|
||||
if [[ ! -d "$SCRIPT_DIR/frontend/build" ]]; then
|
||||
error_exit "Frontend build directory not found"
|
||||
fi
|
||||
|
||||
log_success "Frontend built successfully"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Deploy frontend to Apache
|
||||
# -----------------------------------------------------------------------------
|
||||
log_info "Deploying frontend to Apache..."
|
||||
|
||||
# Create deployment directory if it doesn't exist
|
||||
mkdir -p "$FRONTEND_DEPLOY_PATH"
|
||||
|
||||
# Clear existing files
|
||||
rm -rf "${FRONTEND_DEPLOY_PATH:?}"/*
|
||||
|
||||
# Copy new build
|
||||
cp -r "$SCRIPT_DIR/frontend/build/"* "$FRONTEND_DEPLOY_PATH/"
|
||||
|
||||
# Set proper ownership
|
||||
chown -R www-data:www-data "$FRONTEND_DEPLOY_PATH"
|
||||
|
||||
# Verify index.html exists
|
||||
if [[ ! -f "$FRONTEND_DEPLOY_PATH/index.html" ]]; then
|
||||
error_exit "Frontend deployment verification failed - index.html not found"
|
||||
fi
|
||||
|
||||
log_success "Frontend deployed to $FRONTEND_DEPLOY_PATH"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Verification
|
||||
# -----------------------------------------------------------------------------
|
||||
log_info "Running verification checks..."
|
||||
|
||||
# Wait for backend to be ready
|
||||
log_info "Waiting for backend health check..."
|
||||
BACKEND_READY=false
|
||||
for i in $(seq 1 $HEALTH_CHECK_RETRIES); do
|
||||
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$BACKEND_PORT/health" 2>/dev/null || echo "000")
|
||||
if [[ "$HTTP_STATUS" == "200" ]]; then
|
||||
BACKEND_READY=true
|
||||
break
|
||||
fi
|
||||
log_info "Waiting for backend... (attempt $i/$HEALTH_CHECK_RETRIES, status: $HTTP_STATUS)"
|
||||
sleep $HEALTH_CHECK_INTERVAL
|
||||
done
|
||||
|
||||
if [[ "$BACKEND_READY" != "true" ]]; then
|
||||
log_warn "Backend health check failed - service may still be starting"
|
||||
else
|
||||
log_success "Backend health check passed"
|
||||
fi
|
||||
|
||||
# Verify Docker containers are running
|
||||
log_info "Docker container status:"
|
||||
cd "$SCRIPT_DIR"
|
||||
$DOCKER_COMPOSE ps
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Summary
|
||||
# -----------------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "=============================================="
|
||||
COMMIT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||
COMMIT_MSG=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "unknown")
|
||||
|
||||
if [[ "$BACKEND_READY" == "true" ]]; then
|
||||
log_success "Deployment completed successfully!"
|
||||
else
|
||||
log_warn "Deployment completed with warnings (backend may still be starting)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Deployed commit: $COMMIT_HASH - $COMMIT_MSG"
|
||||
log_info "Frontend path: $FRONTEND_DEPLOY_PATH"
|
||||
log_info "Backend URL: http://localhost:$BACKEND_PORT"
|
||||
echo "=============================================="
|
||||
|
|
@ -44,11 +44,11 @@ services:
|
|||
dockerfile: Dockerfile
|
||||
target: development
|
||||
container_name: apac_ops_bot_backend
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8048 --reload
|
||||
volumes:
|
||||
- ./backend:/app
|
||||
ports:
|
||||
- "8000:8000"
|
||||
- "8048:8048"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
environment:
|
||||
|
|
@ -78,8 +78,8 @@ services:
|
|||
env_file:
|
||||
- ./frontend/.env
|
||||
environment:
|
||||
- REACT_APP_API_URL=http://83.151.203.105:8000/api/v1
|
||||
- REACT_APP_WS_URL=ws://83.151.203.105:8000/ws
|
||||
- REACT_APP_API_URL=/apac-ops-bot-back/api/v1
|
||||
- REACT_APP_WS_URL=/apac-ops-bot-back/ws
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
# API Configuration
|
||||
REACT_APP_API_URL=http://localhost:8000/api/v1
|
||||
REACT_APP_WS_URL=ws://localhost:8000/ws
|
||||
# For production with Apache reverse proxy:
|
||||
REACT_APP_API_URL=/apac-ops-bot-back/api/v1
|
||||
REACT_APP_WS_URL=/apac-ops-bot-back/ws
|
||||
# For local development without proxy:
|
||||
# REACT_APP_API_URL=http://localhost:8048/api/v1
|
||||
# REACT_APP_WS_URL=ws://localhost:8048/ws
|
||||
|
||||
# Azure AD / MSAL Configuration
|
||||
REACT_APP_AZURE_CLIENT_ID=your-client-id-here
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
|
||||
|
||||
const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000/api/v1';
|
||||
const API_URL = process.env.REACT_APP_API_URL || '/apac-ops-bot-back/api/v1';
|
||||
|
||||
// Create axios instance
|
||||
const apiClient: AxiosInstance = axios.create({
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue