feat: Add Linux deployment scripts and server logs view
- Add deployment/linux/ with deploy.sh for deploying from Claude-Agent LXC to Windows server - Add ServerLogsView.vue for viewing server logs from frontend - Add shared/routes/system.py for system health endpoints - Update CLAUDE.md with quick deploy instructions - Improve Windows deployment scripts (ROA2WEB-Console.ps1) - Fix OCR service validation and worker pool improvements - Update environment config examples - Various script permission and startup fixes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
381
deployment/linux/deploy.sh
Executable file
381
deployment/linux/deploy.sh
Executable file
@@ -0,0 +1,381 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# ROA2WEB Linux Deployment Script
|
||||
# Builds and deploys to Windows IIS server via SSH/SCP
|
||||
#
|
||||
# Usage:
|
||||
# ./deploy.sh # Full deploy (frontend + backend)
|
||||
# ./deploy.sh frontend # Frontend only
|
||||
# ./deploy.sh backend # Backend only
|
||||
# ./deploy.sh test # Test SSH connection only
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
# =============================================================================
|
||||
# CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# SSH Configuration (matches deploy-config.json)
|
||||
SSH_HOST="roa2web-prod" # Uses ~/.ssh/config
|
||||
REMOTE_PATH="C:/Temp"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# =============================================================================
|
||||
# HELPER FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
log_step() {
|
||||
echo -e "\n${CYAN}[*] $1${NC}" >&2
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN} [OK] $1${NC}" >&2
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED} [ERROR] $1${NC}" >&2
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW} [WARN] $1${NC}" >&2
|
||||
}
|
||||
|
||||
log_info() {
|
||||
echo -e " [INFO] $1" >&2
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# SSH CONNECTION TEST
|
||||
# =============================================================================
|
||||
|
||||
test_ssh_connection() {
|
||||
log_step "Testing SSH connection to $SSH_HOST..."
|
||||
|
||||
if ssh -o ConnectTimeout=10 "$SSH_HOST" "echo 'Connection successful'" 2>/dev/null; then
|
||||
log_success "SSH connection working"
|
||||
return 0
|
||||
else
|
||||
log_error "SSH connection failed"
|
||||
echo ""
|
||||
echo "Please ensure:"
|
||||
echo " 1. SSH key is configured in ~/.ssh/config"
|
||||
echo " 2. Public key is added to server's authorized_keys"
|
||||
echo " 3. Server is reachable on port 22122"
|
||||
echo ""
|
||||
echo "SSH config should contain:"
|
||||
echo " Host roa2web-prod"
|
||||
echo " HostName 10.0.20.36"
|
||||
echo " Port 22122"
|
||||
echo " User Administrator"
|
||||
echo " IdentityFile ~/.ssh/roa2web_deploy"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# BUILD FRONTEND
|
||||
# =============================================================================
|
||||
|
||||
build_frontend() {
|
||||
log_step "Building frontend..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Check Node.js
|
||||
if ! command -v node &> /dev/null; then
|
||||
log_error "Node.js not found. Please install Node.js 16+"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NODE_VERSION=$(node --version)
|
||||
log_info "Node.js: $NODE_VERSION"
|
||||
|
||||
# Check if node_modules exists
|
||||
if [ ! -d "node_modules" ]; then
|
||||
log_step "Installing npm dependencies..."
|
||||
npm install
|
||||
fi
|
||||
|
||||
# Build production
|
||||
log_step "Running production build..."
|
||||
NODE_ENV=production npm run build
|
||||
|
||||
if [ ! -d "dist" ]; then
|
||||
log_error "Build failed: dist/ directory not created"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Count files
|
||||
FILE_COUNT=$(find dist -type f | wc -l)
|
||||
TOTAL_SIZE=$(du -sh dist | cut -f1)
|
||||
log_success "Build completed: $FILE_COUNT files ($TOTAL_SIZE)"
|
||||
|
||||
# Verify web.config
|
||||
if [ -f "dist/web.config" ]; then
|
||||
log_success "web.config present in build"
|
||||
else
|
||||
log_warning "web.config NOT found in build"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# CREATE DEPLOYMENT PACKAGE
|
||||
# =============================================================================
|
||||
|
||||
create_package() {
|
||||
local COMPONENT=$1
|
||||
local TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||
local PACKAGE_DIR="$PROJECT_ROOT/deploy-package-$TIMESTAMP"
|
||||
|
||||
log_step "Creating deployment package..."
|
||||
log_info "Package: $PACKAGE_DIR"
|
||||
|
||||
mkdir -p "$PACKAGE_DIR"
|
||||
|
||||
# Frontend
|
||||
if [ "$COMPONENT" = "all" ] || [ "$COMPONENT" = "frontend" ]; then
|
||||
log_step "Packaging frontend..."
|
||||
mkdir -p "$PACKAGE_DIR/frontend"
|
||||
cp -r "$PROJECT_ROOT/dist/"* "$PACKAGE_DIR/frontend/"
|
||||
log_success "Frontend packaged"
|
||||
fi
|
||||
|
||||
# Backend
|
||||
if [ "$COMPONENT" = "all" ] || [ "$COMPONENT" = "backend" ]; then
|
||||
log_step "Packaging backend..."
|
||||
mkdir -p "$PACKAGE_DIR/backend"
|
||||
|
||||
# Copy backend files (excluding venv, __pycache__, logs, .env)
|
||||
rsync -av --progress \
|
||||
--exclude 'venv' \
|
||||
--exclude '__pycache__' \
|
||||
--exclude '*.pyc' \
|
||||
--exclude '*.pyo' \
|
||||
--exclude '.pytest_cache' \
|
||||
--exclude 'logs' \
|
||||
--exclude '.env' \
|
||||
--exclude '.env.local' \
|
||||
--exclude '*.log' \
|
||||
"$PROJECT_ROOT/backend/" "$PACKAGE_DIR/backend/"
|
||||
|
||||
# Copy .env.example if exists
|
||||
if [ -f "$PROJECT_ROOT/backend/.env.example" ]; then
|
||||
cp "$PROJECT_ROOT/backend/.env.example" "$PACKAGE_DIR/backend/"
|
||||
fi
|
||||
|
||||
log_success "Backend packaged"
|
||||
fi
|
||||
|
||||
# Shared modules
|
||||
if [ "$COMPONENT" = "all" ] || [ "$COMPONENT" = "backend" ]; then
|
||||
log_step "Packaging shared modules..."
|
||||
mkdir -p "$PACKAGE_DIR/shared"
|
||||
|
||||
rsync -av --progress \
|
||||
--exclude '__pycache__' \
|
||||
--exclude '*.pyc' \
|
||||
--exclude 'tests' \
|
||||
"$PROJECT_ROOT/shared/" "$PACKAGE_DIR/shared/"
|
||||
|
||||
log_success "Shared modules packaged"
|
||||
fi
|
||||
|
||||
# Config templates
|
||||
log_step "Packaging config templates..."
|
||||
if [ -d "$PROJECT_ROOT/deployment/config" ]; then
|
||||
mkdir -p "$PACKAGE_DIR/config"
|
||||
cp -r "$PROJECT_ROOT/deployment/config/"* "$PACKAGE_DIR/config/" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Deployment scripts
|
||||
log_step "Packaging deployment scripts..."
|
||||
mkdir -p "$PACKAGE_DIR/scripts"
|
||||
|
||||
SCRIPTS=(
|
||||
"ROA2WEB-Console.ps1"
|
||||
"Install-ROA2WEB.ps1"
|
||||
"Check-And-Deploy.ps1"
|
||||
)
|
||||
|
||||
for script in "${SCRIPTS[@]}"; do
|
||||
if [ -f "$PROJECT_ROOT/deployment/windows/scripts/$script" ]; then
|
||||
cp "$PROJECT_ROOT/deployment/windows/scripts/$script" "$PACKAGE_DIR/scripts/"
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "Deployment scripts packaged"
|
||||
|
||||
# Create README
|
||||
cat > "$PACKAGE_DIR/README.txt" << EOF
|
||||
================================================================================
|
||||
ROA2WEB DEPLOYMENT PACKAGE
|
||||
Generated: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
From: Linux/LXC deployment script
|
||||
================================================================================
|
||||
|
||||
CONTENTS:
|
||||
---------
|
||||
backend/ Unified FastAPI backend
|
||||
frontend/ Vue.js SPA (production build)
|
||||
shared/ Shared Python modules
|
||||
config/ Configuration templates
|
||||
scripts/ PowerShell deployment scripts
|
||||
|
||||
DEPLOYMENT:
|
||||
-----------
|
||||
Server will auto-deploy within 5 minutes (Check-And-Deploy.ps1 scheduled task)
|
||||
|
||||
Or manually:
|
||||
cd scripts
|
||||
.\ROA2WEB-Console.ps1 -NonInteractive -Action DeployAll
|
||||
|
||||
================================================================================
|
||||
EOF
|
||||
|
||||
# Calculate package size
|
||||
PACKAGE_SIZE=$(du -sh "$PACKAGE_DIR" | cut -f1)
|
||||
FILE_COUNT=$(find "$PACKAGE_DIR" -type f | wc -l)
|
||||
|
||||
log_success "Package created: $FILE_COUNT files ($PACKAGE_SIZE)"
|
||||
|
||||
# Return path via global variable (avoid stdout pollution from rsync)
|
||||
CREATED_PACKAGE_DIR="$PACKAGE_DIR"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# TRANSFER TO SERVER
|
||||
# =============================================================================
|
||||
|
||||
transfer_to_server() {
|
||||
local PACKAGE_DIR=$1
|
||||
local TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||
local REMOTE_DEPLOY_DIR="deploy-$TIMESTAMP"
|
||||
local REMOTE_FULL_PATH="C:\\Temp\\$REMOTE_DEPLOY_DIR"
|
||||
|
||||
log_step "Transferring to server..."
|
||||
log_info "Remote path: C:\\Temp\\$REMOTE_DEPLOY_DIR"
|
||||
|
||||
# Create remote directory
|
||||
log_step "Creating remote directory..."
|
||||
ssh "$SSH_HOST" "New-Item -ItemType Directory -Path 'C:\\Temp\\$REMOTE_DEPLOY_DIR' -Force"
|
||||
log_success "Remote directory created"
|
||||
|
||||
# Transfer files one directory at a time (Bitvise SCP compatibility)
|
||||
log_step "Uploading files via SCP..."
|
||||
|
||||
for dir in "$PACKAGE_DIR"/*/; do
|
||||
if [ -d "$dir" ]; then
|
||||
local dirname=$(basename "$dir")
|
||||
log_info "Uploading $dirname/..."
|
||||
scp -r "$dir" "roa2web-prod:C:\\Temp\\$REMOTE_DEPLOY_DIR\\$dirname"
|
||||
fi
|
||||
done
|
||||
|
||||
# Upload root files (README, etc.)
|
||||
for file in "$PACKAGE_DIR"/*; do
|
||||
if [ -f "$file" ]; then
|
||||
local filename=$(basename "$file")
|
||||
scp "$file" "roa2web-prod:C:\\Temp\\$REMOTE_DEPLOY_DIR\\$filename"
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "Transfer completed"
|
||||
|
||||
# Verify transfer
|
||||
log_step "Verifying transfer..."
|
||||
REMOTE_FILE_COUNT=$(ssh "$SSH_HOST" "(Get-ChildItem -Path 'C:\\Temp\\$REMOTE_DEPLOY_DIR' -Recurse -File).Count")
|
||||
log_success "Remote files: $REMOTE_FILE_COUNT"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo -e "${GREEN} DEPLOYMENT PACKAGE UPLOADED${NC}"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo " Remote path: C:\\Temp\\$REMOTE_DEPLOY_DIR"
|
||||
echo ""
|
||||
echo " Server will auto-deploy within 5 minutes."
|
||||
echo " Or manually deploy:"
|
||||
echo " ssh $SSH_HOST"
|
||||
echo " cd C:\\Temp\\$REMOTE_DEPLOY_DIR\\scripts"
|
||||
echo " .\\ROA2WEB-Console.ps1"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# CLEANUP
|
||||
# =============================================================================
|
||||
|
||||
cleanup_local_package() {
|
||||
local PACKAGE_DIR=$1
|
||||
|
||||
log_step "Cleaning up local package..."
|
||||
rm -rf "$PACKAGE_DIR"
|
||||
log_success "Local package removed"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MAIN
|
||||
# =============================================================================
|
||||
|
||||
main() {
|
||||
local COMPONENT=${1:-all}
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " ROA2WEB Linux Deployment Script"
|
||||
echo " Component: $COMPONENT"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# Handle test command
|
||||
if [ "$COMPONENT" = "test" ]; then
|
||||
test_ssh_connection
|
||||
exit $?
|
||||
fi
|
||||
|
||||
# Validate component
|
||||
case $COMPONENT in
|
||||
all|frontend|backend)
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [all|frontend|backend|test]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Test SSH connection first
|
||||
if ! test_ssh_connection; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build frontend if needed
|
||||
if [ "$COMPONENT" = "all" ] || [ "$COMPONENT" = "frontend" ]; then
|
||||
build_frontend
|
||||
fi
|
||||
|
||||
# Create package (sets CREATED_PACKAGE_DIR global variable)
|
||||
create_package "$COMPONENT"
|
||||
|
||||
# Transfer to server
|
||||
transfer_to_server "$CREATED_PACKAGE_DIR"
|
||||
|
||||
# Cleanup
|
||||
cleanup_local_package "$CREATED_PACKAGE_DIR"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Deployment completed successfully!${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run main with all arguments
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user