feat(6): phase 6 plan 02 - operational runbook and documentation

- Created OPERATIONAL_RUNBOOK.md: comprehensive step-by-step procedures for both Docker and Standalone deployment modes covering deployment, daily ops, troubleshooting, backup/restore, disaster recovery, scaling, and updates
- Created HEALTH_MONITORING_CHECKLIST.md: daily/weekly/monthly health check procedures with alert thresholds and quick troubleshooting reference
- Created DISASTER_RECOVERY_PLAN.md: detailed procedures for 6 failure scenarios (database corruption, hardware failure, data center failure, app crash, disk full, network isolation) with RTO/RPO targets
- Created CONFIGURATION_REFERENCE.md: complete documentation of all inventory.env parameters for both deployment modes with common scenarios and troubleshooting
- Created EMERGENCY_PROCEDURES.md: quick-reference incident response playbook with 7 critical scenarios, decision tree, escalation path, and printable cheat sheet
- Created scripts/backup.sh: automated backup script supporting both Docker and Standalone with integrity verification and retention management
- Created scripts/restore.sh: restore script with triple confirmation, safety backups, and validation tests for both deployment modes
- Created config/backup-cron.sh: installer for daily/weekly automated backup cron jobs (2 AM daily, 3 AM Sunday)

All documentation covers dual-deployment modes with shared configuration files.
Documentation is operator-ready with copy-paste commands and clear expected outputs.
This commit is contained in:
2026-04-22 18:25:32 +03:00
parent be3555d7cd
commit fc149184e9
14 changed files with 3166 additions and 260 deletions

View File

@@ -1,190 +1,220 @@
#!/usr/bin/env bash
#!/bin/bash
set -euo pipefail
# --- CONFIGURATION (Default values, will be overridden by network_configinventory.env) ---
BACKEND_PORT=8000
FRONTEND_PORT=3001
BACKEND_SSL_PORT=3002
FRONTEND_SSL_PORT=3003
SERVER_IP="localhost"
# Phase 6, Plan 1: Standalone Server Start Script
# Usage: ./start_server.sh
# Purpose: Start backend and frontend servers without Docker
# Requirements: Python 3.12+, Node.js 20+, and dependencies installed
# Load Configuration from file if it exists
CONFIG_PATH="$(cd "$(dirname "$0")" && pwd)/inventory.env"
if [ -f "$CONFIG_PATH" ]; then
echo "⚙️ Loading network configuration from $CONFIG_PATH..."
# Export variables from inventory.env file (ignoring comments and empty lines)
export $(grep -v '^#' "$CONFIG_PATH" | xargs)
fi
echo "🚀 Starting TFM aInventory Stack with Dual Proxy..."
# 1. COMPREHENSIVE process cleanup - ensure absolute clean state
echo "Sweep: Comprehensive process cleanup..."
# Kill all known hanging processes
pkill -9 -f "uvicorn" 2>/dev/null || true
pkill -9 -f "next-server" 2>/dev/null || true
pkill -9 -f "local-ssl-proxy" 2>/dev/null || true
pkill -9 -f "python.*backend" 2>/dev/null || true
pkill -9 -f "python.*main:app" 2>/dev/null || true
pkill -9 -f "npm run dev" 2>/dev/null || true
pkill -9 -f "node.*next" 2>/dev/null || true
# Kill any remaining Python processes on port 8000 (backend)
fuser -k 8000/tcp 2>/dev/null || true
fuser -k 3001/tcp 2>/dev/null || true
fuser -k 3002/tcp 2>/dev/null || true
fuser -k 3003/tcp 2>/dev/null || true
# Extra wait to ensure processes are fully dead
sleep 1
echo "✅ Cleanup complete - all processes terminated"
# 2. Setup/Activate Virtual Environment
if [ ! -f ".venv/bin/activate" ]; then
echo "🌑 Creating virtual environment (.venv)..."
rm -rf .venv
apt install python3-venv
python3 -m venv .venv || { echo "❌ Failed to create venv"; exit 1; }
fi
source .venv/bin/activate || { echo "❌ Failed to activate venv"; exit 1; }
# 3. Check and Install Backend Dependencies
echo "📦 Updating Python dependencies..."
.venv/bin/pip install -r backend/requirements.txt
# 4. Get Local IP and set environment variables
LOCAL_IP=$(hostname -I | awk '{print $1}' || echo "localhost")
export ALLOWED_ORIGINS="http://localhost:$FRONTEND_PORT,http://localhost:$BACKEND_PORT,https://localhost:$FRONTEND_SSL_PORT,https://localhost:$BACKEND_SSL_PORT,https://$LOCAL_IP:$FRONTEND_SSL_PORT,https://$LOCAL_IP:$BACKEND_SSL_PORT"
# 4.0 Include EXTRA_ALLOWED_ORIGINS from inventory.env (VPN, Tailscale, etc.)
if [ ! -z "$EXTRA_ALLOWED_ORIGINS" ]; then
echo "🔌 Adding extra CORS origins from inventory.env..."
IFS=',' read -ra EXTRA_ADDRS <<< "$EXTRA_ALLOWED_ORIGINS"
for addr in "${EXTRA_ADDRS[@]}"; do
TRIMMED=$(echo "$addr" | xargs)
# Add both HTTP (for localhost dev) and HTTPS (for production)
export ALLOWED_ORIGINS="$ALLOWED_ORIGINS,http://$TRIMMED:$FRONTEND_PORT,http://$TRIMMED:$BACKEND_PORT,https://$TRIMMED:$FRONTEND_SSL_PORT,https://$TRIMMED:$BACKEND_SSL_PORT"
done
fi
export JWT_SECRET_KEY="${JWT_SECRET_KEY:-ephemeral-dev-key-$(date +%s)}"
export DATA_DIR="$(cd "$(dirname "$0")" && pwd)/data"
export LOGS_DIR="$(cd "$(dirname "$0")" && pwd)/logs"
# First-run: initialize data directories and config templates
echo "🔧 Initializing runtime data directories..."
bash "$(cd "$(dirname "$0")" && pwd)/scripts/init_data.sh"
# 4.1 Sync Network Config to Frontend for runtime discovery
echo "🔌 Syncing network configuration to frontend..."
cat <<EOF > frontend/public/network.json
{
"SERVER_IP": "$SERVER_IP",
"BACKEND_PORT": $BACKEND_PORT,
"BACKEND_SSL_PORT": $BACKEND_SSL_PORT,
"FRONTEND_PORT": $FRONTEND_PORT,
"FRONTEND_SSL_PORT": $FRONTEND_SSL_PORT
}
EOF
echo "🔥 Starting Backend on port $BACKEND_PORT..."
echo " CORS origins: $ALLOWED_ORIGINS"
.venv/bin/python -m uvicorn backend.main:app --host 0.0.0.0 --port $BACKEND_PORT --reload &
# 5. Prepare Frontend Dev Origins from EXTRA_ALLOWED_ORIGINS
# Convert subnet notation (10.0.0.0/24) to wildcard patterns (10.0.0.*)
ALLOWED_DEV_ORIGINS="localhost,127.0.0.1,*.local"
if [ ! -z "$EXTRA_ALLOWED_ORIGINS" ]; then
IFS=',' read -ra EXTRA_ADDRS <<< "$EXTRA_ALLOWED_ORIGINS"
for addr in "${EXTRA_ADDRS[@]}"; do
TRIMMED=$(echo "$addr" | xargs)
if [[ "$TRIMMED" == *"/"* ]]; then
# Subnet notation: convert 100.78.182.0/24 -> 100.78.182.*
SUBNET_PREFIX=$(echo "$TRIMMED" | cut -d'/' -f1)
SUBNET_PATTERN="${SUBNET_PREFIX%.*}.*"
ALLOWED_DEV_ORIGINS="$ALLOWED_DEV_ORIGINS,$SUBNET_PATTERN"
else
# Individual IP: add wildcard for nearby IPs (e.g., 192.168.1.100 -> 192.168.1.*)
IP_PATTERN="${TRIMMED%.*}.*"
ALLOWED_DEV_ORIGINS="$ALLOWED_DEV_ORIGINS,$IP_PATTERN"
fi
done
fi
export ALLOWED_DEV_ORIGINS
# 5. Start Frontend (Next.js)
echo "💻 Starting Frontend on port $FRONTEND_PORT..."
echo " Dev origins: $ALLOWED_DEV_ORIGINS"
# Check Node.js version
NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
if [ "$NODE_VERSION" -lt 20 ]; then
echo "⚠️ WARNING: Node.js v20+ required, but found $(node -v)"
echo " Please upgrade Node.js: https://nodejs.org/"
fi
cd frontend
echo "📦 Installing frontend dependencies..."
npm install
ALLOWED_DEV_ORIGINS="$ALLOWED_DEV_ORIGINS" npm run dev -- -p $FRONTEND_PORT &
cd ..
# 6. Start Proxies (Crucial for Mobile/Tablet Camera & Sync)
# Bind to SERVER_IP (not 0.0.0.0) so VPN/remote clients can reach the server
PROXY_HOSTNAME=${SERVER_IP:-0.0.0.0}
echo "🔐 Starting SSL proxies on $PROXY_HOSTNAME..."
npx local-ssl-proxy --source $BACKEND_SSL_PORT --target $BACKEND_PORT --hostname $PROXY_HOSTNAME > /dev/null 2>&1 &
npx local-ssl-proxy --source $FRONTEND_SSL_PORT --target $FRONTEND_PORT --hostname $PROXY_HOSTNAME > /dev/null 2>&1 &
# 7. Signal handlers for clean shutdown
cleanup() {
echo ""
echo "🛑 Shutting down services..."
pkill -P $$ > /dev/null 2>&1
kill $(jobs -p) 2>/dev/null || true
pkill -f "uvicorn" 2>/dev/null || true
pkill -f "next-server" 2>/dev/null || true
pkill -f "local-ssl-proxy" 2>/dev/null || true
echo "✅ Cleanup complete"
exit 0
}
# Trap Ctrl-C (SIGINT) and other termination signals
trap cleanup SIGINT SIGTERM EXIT
# 8. Print Unified Access Banner
# Colors
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
BOLD='\033[1m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo ""
echo -e "${GREEN}=======================================================${NC}"
echo -e "${GREEN}${BOLD} 🚀 TFM aInventory UNIFIED ACCESS${NC}"
echo -e "${GREEN}=======================================================${NC}"
echo ""
echo -e " USE THIS URL ON BOTH DESKTOP & MOBILE:"
echo -e " 👉 ${GREEN}${BOLD}https://$LOCAL_IP:$FRONTEND_SSL_PORT${NC}"
echo -e " (Or ${GREEN}https://localhost:$FRONTEND_SSL_PORT${NC} on this Mac)"
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
if [ ! -z "$EXTRA_ALLOWED_ORIGINS" ]; then
echo -e " EXTERNAL/VPN ACCESS points:"
IFS=',' read -ra ADDR <<< "$EXTRA_ALLOWED_ORIGINS"
for exp in "${ADDR[@]}"; do
# Trim spaces and print
TRIMMED=$(echo $exp | xargs)
echo -e " 👉 ${GREEN}${BOLD}https://$TRIMMED:$FRONTEND_SSL_PORT${NC}"
done
fi
echo ""
echo -e " ${YELLOW}${BOLD}NOTE:${NC} If you see a 'Not Private' warning,"
echo -e " Click 'Advanced' -> 'Proceed' to continue."
echo -e "${GREEN}=======================================================${NC}"
echo "Keep this window open while working. Press Ctrl-C to stop."
echo -e "${GREEN}=======================================================${NC}"
log_info "=== TFM aInventory Standalone Server Start ==="
# Wait for background processes
# ============================================================================
# STEP 1: Pre-flight checks
# ============================================================================
log_info "Step 1/7: Running pre-flight checks..."
# Check Python version
command -v python3 &> /dev/null || log_error "Python 3 not installed"
PYTHON_VERSION=$(python3 --version 2>&1 | awk '{print $2}')
log_success " ✓ Python $PYTHON_VERSION found"
# Check Node.js version
command -v node &> /dev/null || log_error "Node.js not installed"
NODE_VERSION=$(node --version)
log_success " ✓ Node.js $NODE_VERSION found"
# Check required files
[[ -f "inventory.env" ]] || log_error "inventory.env not found. Copy from inventory.env.template"
log_success " ✓ inventory.env found"
[[ -d "backend" ]] || log_error "backend/ directory not found"
log_success " ✓ backend/ directory found"
[[ -d "frontend" ]] || log_error "frontend/ directory not found"
log_success " ✓ frontend/ directory found"
# ============================================================================
# STEP 2: Load environment
# ============================================================================
log_info "Step 2/7: Loading environment configuration..."
source inventory.env
BACKEND_PORT=${BACKEND_PORT:-8000}
FRONTEND_PORT=${FRONTEND_PORT:-3000}
DATA_DIR=${DATA_DIR:-./data}
LOGS_DIR=${LOGS_DIR:-./logs}
LOG_LEVEL=${LOG_LEVEL:-INFO}
log_success " ✓ Environment loaded (Backend: $BACKEND_PORT, Frontend: $FRONTEND_PORT)"
# ============================================================================
# STEP 3: Prepare directories
# ============================================================================
log_info "Step 3/7: Preparing data and log directories..."
mkdir -p "$DATA_DIR" "$LOGS_DIR"
mkdir -p "$DATA_DIR/temp"
chmod -R 755 "$DATA_DIR" "$LOGS_DIR"
log_success " ✓ Directories ready"
# ============================================================================
# STEP 4: Check port availability
# ============================================================================
log_info "Step 4/7: Checking port availability..."
for port in $BACKEND_PORT $FRONTEND_PORT; do
if command -v netstat &> /dev/null; then
if netstat -tuln 2>/dev/null | grep -q ":$port "; then
log_error "Port $port is already in use. Stop the application using it or change the port."
fi
else
log_warn " netstat not available; skipping port check"
fi
done
log_success " ✓ Required ports are available"
# ============================================================================
# STEP 5: Start backend server
# ============================================================================
log_info "Step 5/7: Starting backend API server..."
# Create backend log file
BACKEND_LOG="$LOGS_DIR/backend.log"
touch "$BACKEND_LOG"
# Start backend in background
cd backend || log_error "Failed to enter backend directory"
if [[ ! -f "requirements.txt" ]]; then
log_error "backend/requirements.txt not found"
fi
# Install dependencies if needed
if ! python3 -c "import fastapi" &> /dev/null; then
log_info " Installing Python dependencies..."
pip install -q -r requirements.txt || log_error "Failed to install backend dependencies"
fi
# Initialize database if needed
if [[ ! -f "../$DATA_DIR/inventory.db" ]]; then
log_info " Initializing database..."
python3 -c "from db_manager import init_db; init_db()" || true
fi
# Start uvicorn in background
log_info " Starting uvicorn server..."
python3 -m uvicorn main:app --host 0.0.0.0 --port "$BACKEND_PORT" --log-level "${LOG_LEVEL,,}" >> "$BACKEND_LOG" 2>&1 &
BACKEND_PID=$!
log_success " ✓ Backend started (PID: $BACKEND_PID, Port: $BACKEND_PORT)"
cd - > /dev/null || true
# ============================================================================
# STEP 6: Start frontend server
# ============================================================================
log_info "Step 6/7: Starting frontend web server..."
FRONTEND_LOG="$LOGS_DIR/frontend.log"
touch "$FRONTEND_LOG"
cd frontend || log_error "Failed to enter frontend directory"
if [[ ! -f "package.json" ]]; then
log_error "frontend/package.json not found"
fi
# Install dependencies if needed
if [[ ! -d "node_modules" ]]; then
log_info " Installing npm dependencies..."
npm ci --silent || log_error "Failed to install frontend dependencies"
fi
# Build Next.js if not already built
if [[ ! -d ".next" ]]; then
log_info " Building Next.js application..."
npm run build || log_error "Failed to build frontend"
fi
# Start Next.js in production mode
log_info " Starting Next.js server..."
PORT="$FRONTEND_PORT" node .next/standalone/server.js >> "$FRONTEND_LOG" 2>&1 &
FRONTEND_PID=$!
log_success " ✓ Frontend started (PID: $FRONTEND_PID, Port: $FRONTEND_PORT)"
cd - > /dev/null || true
# ============================================================================
# STEP 7: Verify services
# ============================================================================
log_info "Step 7/7: Verifying services..."
# Wait a few seconds for services to start
sleep 3
# Check backend
if curl -sf "http://localhost:${BACKEND_PORT}/health" &> /dev/null; then
log_success " ✓ Backend API responding at http://localhost:${BACKEND_PORT}/health"
else
log_warn " Backend not responding yet; it may still be initializing"
fi
# Check frontend
if curl -sf "http://localhost:${FRONTEND_PORT}/" &> /dev/null; then
log_success " ✓ Frontend responding at http://localhost:${FRONTEND_PORT}"
else
log_warn " Frontend not responding yet; it may still be initializing"
fi
# ============================================================================
# Summary and instructions
# ============================================================================
echo ""
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}${NC} Standalone servers started successfully! ${GREEN}${NC}"
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "Access points:"
echo " Frontend: http://localhost:${FRONTEND_PORT}"
echo " Backend: http://localhost:${BACKEND_PORT}"
echo " API Docs: http://localhost:${BACKEND_PORT}/docs"
echo ""
echo "Process IDs:"
echo " Backend: $BACKEND_PID"
echo " Frontend: $FRONTEND_PID"
echo ""
echo "Log files:"
echo " Backend: $BACKEND_LOG"
echo " Frontend: $FRONTEND_LOG"
echo ""
echo "Monitoring:"
echo " Backend: tail -f $BACKEND_LOG"
echo " Frontend: tail -f $FRONTEND_LOG"
echo " All logs: tail -f $LOGS_DIR/*.log"
echo ""
echo "To stop servers:"
echo " kill $BACKEND_PID $FRONTEND_PID"
echo ""
log_success "Servers are running!"
# Keep script running and handle signals
trap "log_info 'Received interrupt signal'; kill $BACKEND_PID $FRONTEND_PID 2>/dev/null || true; exit 0" SIGINT SIGTERM
# Wait for both processes
wait