Files
enterprise_digital-platform/start-dev.sh
T
ske087 0aefadbfd8 NetworkView: API routing fix, logout button, audit trail, port/notes editor tracking
- Fix frontend API base path (VITE_API_BASE env var, GraphPage hardcoded /api)
- Add logout button to NetworkView sidebar (clears portal SSO)
- Add AuditTrail component: inline change history on all entity pages
- DB migration: add updated_at, last_edited_by to ports table
- DB migration: add notes_last_edited_by, notes_updated_at to all entity tables
- Backend: track actor on port create/update; notes editor on entity PUT
- Frontend: extend types, MarkdownEditor shows last editor, port modal/list show last editor
- Fix port CREATE TABLE definition to include new columns upfront
- Add try/catch in handleSavePort to surface API errors in modal
2026-05-10 23:10:02 +03:00

434 lines
19 KiB
Bash
Executable File

#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# Enterprise Digital Platform — Development Launcher
# Starts all services locally without Docker.
# Usage: ./start-dev.sh (start everything)
# ./start-dev.sh stop (kill all managed processes)
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PIDFILE="$ROOT/.dev-pids"
LOG_DIR="$ROOT/.dev-logs"
PORTAL_PORT=5001
DIGISERVER_PORT=5002
ITASSETS_PORT=5003
SRVMONITOR_PORT=5004
NV_BACKEND_PORT=3001
NGINX_PORT=8080
NV_DIST="$ROOT/NetworkView/frontend/dist"
# ── Colour helpers ─────────────────────────────────────────────────────────────
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
info() { echo -e "${GREEN}[EDP]${NC} $*"; }
warn() { echo -e "${YELLOW}[EDP]${NC} $*"; }
error() { echo -e "${RED}[EDP]${NC} $*" >&2; }
# ── Stop command ──────────────────────────────────────────────────────────────
if [[ "${1:-}" == "stop" ]]; then
if [[ -f "$PIDFILE" ]]; then
info "Stopping all services..."
while IFS= read -r pid; do
[[ -z "$pid" ]] && continue
kill "$pid" 2>/dev/null && info " killed PID $pid" || true
done < "$PIDFILE"
rm -f "$PIDFILE"
fi
# Clear named PID files used by module_manager
for _npid in "$LOG_DIR"/*.pid; do
[[ -f "$_npid" ]] && rm -f "$_npid"
done
# Stop nginx if we started it
NGINX_BIN="$(command -v nginx 2>/dev/null || echo /usr/sbin/nginx)"
_pidfile="$ROOT/.dev-logs/nginx/nginx.pid"
if [[ -f "$_pidfile" ]]; then
kill "$(cat "$_pidfile")" 2>/dev/null || true
fi
info "All services stopped."
exit 0
fi
# ── Prerequisites ─────────────────────────────────────────────────────────────
mkdir -p "$LOG_DIR"
: > "$PIDFILE"
info "Checking prerequisites..."
# nginx
NGINX_BIN="$(command -v nginx 2>/dev/null || echo /usr/sbin/nginx)"
if ! "$NGINX_BIN" -v &>/dev/null 2>&1; then
warn "nginx not found — installing via apt..."
sudo apt-get install -y -qq nginx
NGINX_BIN=/usr/sbin/nginx
fi
# python3
if ! command -v python3 &>/dev/null; then
error "python3 is required but not found. Please install it first."
exit 1
fi
# node / npm
if ! command -v node &>/dev/null; then
error "node is required but not found. Please install it first."
exit 1
fi
# ── Python venvs ──────────────────────────────────────────────────────────────
setup_venv() {
local name="$1" dir="$2"
if [[ ! -f "$dir/.venv/bin/python" ]]; then
info "Creating venv for $name..."
python3 -m venv "$dir/.venv"
"$dir/.venv/bin/pip" install -q -r "$dir/requirements.txt"
fi
}
setup_venv "portal" "$ROOT/portal"
setup_venv "digiserver" "$ROOT/digiserver-v2"
setup_venv "itassets" "$ROOT/IT_asset_management"
setup_venv "srvmonitor" "$ROOT/Server_Monitorizare_v2"
# ── Node dependencies ─────────────────────────────────────────────────────────
if [[ ! -d "$ROOT/NetworkView/backend/node_modules" ]]; then
info "Installing NetworkView backend npm packages..."
npm --prefix "$ROOT/NetworkView/backend" install --silent
fi
if [[ ! -d "$ROOT/NetworkView/frontend/node_modules" ]]; then
info "Installing NetworkView frontend npm packages..."
npm --prefix "$ROOT/NetworkView/frontend" install --silent
fi
# ── Build NetworkView frontend (only when source is newer than dist) ───────────
VITE_CFG="$ROOT/NetworkView/frontend/vite.config.ts"
if [[ ! -f "$NV_DIST/index.html" ]] \
|| find "$ROOT/NetworkView/frontend/src" -newer "$NV_DIST/index.html" -name "*.ts" -o -name "*.tsx" -o -name "*.css" 2>/dev/null | grep -q .; then
info "Building NetworkView frontend..."
VITE_BASE_PATH=/networkview/ \
npm --prefix "$ROOT/NetworkView/frontend" run build --silent
fi
# ── Generate runtime nginx config ────────────────────────────────────────────
NGINX_CONF="$ROOT/nginx/.dev-nginx.conf"
NGINX_LOGS="$ROOT/.dev-logs/nginx"
mkdir -p "$NGINX_LOGS"
info "Writing nginx config → $NGINX_CONF"
cat > "$NGINX_CONF" <<NGINXEOF
user $(id -un) $(id -gn);
pid $LOG_DIR/nginx.pid;
error_log $NGINX_LOGS/error.log warn;
events { worker_connections 1024; }
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
access_log $NGINX_LOGS/access.log;
limit_req_zone \$binary_remote_addr zone=login:10m rate=10r/m;
upstream portal_upstream { server 127.0.0.1:${PORTAL_PORT}; }
upstream digiserver_upstream { server 127.0.0.1:${DIGISERVER_PORT}; }
upstream itassets_upstream { server 127.0.0.1:${ITASSETS_PORT}; }
upstream srvmonitor_upstream { server 127.0.0.1:${SRVMONITOR_PORT}; }
upstream nv_backend_upstream { server 127.0.0.1:${NV_BACKEND_PORT}; }
server {
listen ${NGINX_PORT};
server_name _;
location = /portal-verify {
internal;
proxy_pass http://portal_upstream/api/verify-token;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Cookie \$http_cookie;
proxy_set_header X-Original-URI \$request_uri;
proxy_set_header X-Real-IP \$remote_addr;
}
location = /health {
proxy_pass http://portal_upstream/health;
proxy_set_header Host \$host;
}
location / {
limit_req zone=login burst=20 nodelay;
proxy_pass http://portal_upstream;
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_read_timeout 60s;
}
# DigiServer — player API (no portal auth)
location /digiserver/api/ {
proxy_pass http://digiserver_upstream/api/;
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_set_header X-Script-Name /digiserver;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
client_max_body_size 256M;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
# DigiServer — media downloads (no portal auth)
location /digiserver/static/uploads/ {
proxy_pass http://digiserver_upstream/static/uploads/;
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_set_header X-Script-Name /digiserver;
expires 60d;
add_header Cache-Control "public";
}
# DigiServer — admin UI (portal auth required)
location /digiserver/ {
auth_request /portal-verify;
auth_request_set \$auth_user_id \$upstream_http_x_auth_user_id;
auth_request_set \$auth_username \$upstream_http_x_auth_username;
auth_request_set \$auth_role \$upstream_http_x_auth_role;
proxy_pass http://digiserver_upstream/;
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_set_header X-Script-Name /digiserver;
proxy_set_header X-Auth-User-Id \$auth_user_id;
proxy_set_header X-Auth-Username \$auth_username;
proxy_set_header X-Auth-Role \$auth_role;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_connect_timeout 300s;
client_max_body_size 2048M;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
}
# IT Asset Management (portal auth required)
location /itassets/ {
auth_request /portal-verify;
auth_request_set \$auth_user_id \$upstream_http_x_auth_user_id;
auth_request_set \$auth_username \$upstream_http_x_auth_username;
auth_request_set \$auth_role \$upstream_http_x_auth_role;
proxy_pass http://itassets_upstream/;
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_set_header X-Script-Name /itassets;
proxy_set_header X-Auth-User-Id \$auth_user_id;
proxy_set_header X-Auth-Username \$auth_username;
proxy_set_header X-Auth-Role \$auth_role;
proxy_read_timeout 120s;
client_max_body_size 50M;
}
# Server Monitor — device API (no portal auth — Raspberry Pi clients push directly)
location /srvmonitor/api/ {
proxy_pass http://srvmonitor_upstream/api/;
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_set_header X-Script-Name /srvmonitor;
client_max_body_size 50M;
}
# Server Monitor — web UI (portal auth required)
location /srvmonitor/ {
auth_request /portal-verify;
auth_request_set \$auth_user_id \$upstream_http_x_auth_user_id;
auth_request_set \$auth_username \$upstream_http_x_auth_username;
auth_request_set \$auth_role \$upstream_http_x_auth_role;
proxy_pass http://srvmonitor_upstream/;
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_set_header X-Script-Name /srvmonitor;
proxy_set_header X-Auth-User-Id \$auth_user_id;
proxy_set_header X-Auth-Username \$auth_username;
proxy_set_header X-Auth-Role \$auth_role;
proxy_read_timeout 120s;
client_max_body_size 50M;
}
# NetworkView API (portal auth required)
location /networkview/api/ {
auth_request /portal-verify;
auth_request_set \$auth_user_id \$upstream_http_x_auth_user_id;
auth_request_set \$auth_username \$upstream_http_x_auth_username;
proxy_pass http://nv_backend_upstream/api/;
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_set_header X-User-Id \$auth_user_id;
proxy_set_header X-Username \$auth_username;
}
# NetworkView SPA — served from pre-built dist/
location /networkview/ {
auth_request /portal-verify;
auth_request_set \$auth_user_id \$upstream_http_x_auth_user_id;
auth_request_set \$auth_username \$upstream_http_x_auth_username;
alias ${NV_DIST}/;
try_files \$uri \$uri/ @nv_index;
}
location @nv_index {
auth_request /portal-verify;
root ${NV_DIST};
try_files /index.html =404;
}
error_page 401 = @login_redirect;
location @login_redirect {
return 302 /login?next=\$request_uri;
}
error_page 403 = @access_denied;
location @access_denied {
return 302 /access-denied;
}
}
}
NGINXEOF
"$NGINX_BIN" -t -c "$NGINX_CONF"
# ── Start services ────────────────────────────────────────────────────────────
start_bg() {
local name="$1"; shift
info "Starting $name..."
"$@" >> "$LOG_DIR/${name}.log" 2>&1 &
local pid=$!
echo $pid >> "$PIDFILE"
echo $pid > "$LOG_DIR/${name}.pid" # named PID for module_manager
info " $name PID=$pid — logs: $LOG_DIR/${name}.log"
}
# Check .dev-module-state.json — returns 0 (true) if enabled, 1 (false) if disabled
module_enabled() {
local app_id="$1"
local state_file="$ROOT/.dev-module-state.json"
[[ ! -f "$state_file" ]] && return 0
python3 -c "
import json, sys
try:
d = json.load(open('$state_file'))
sys.exit(0 if d.get('$app_id', True) else 1)
except Exception:
sys.exit(0)
"
}
# Portal (always starts — it manages the other modules)
start_bg "portal" \
env FLASK_ENV=development \
DATA_DIR="$ROOT/portal/data" \
PORT=$PORTAL_PORT \
"$ROOT/portal/.venv/bin/python" "$ROOT/portal/run.py"
# DigiServer
if module_enabled "digiserver"; then
mkdir -p "$ROOT/digiserver-v2/instance" "$ROOT/digiserver-v2/app/static/uploads"
start_bg "digiserver" \
env FLASK_ENV=development \
ADMIN_USERNAME=admin \
ADMIN_PASSWORD=admin123 \
PORTAL_JWT_SECRET=change-this-jwt-secret-in-production \
PORTAL_LOGOUT_URL="http://localhost:${NGINX_PORT}/logout" \
"$ROOT/digiserver-v2/.venv/bin/gunicorn" \
--bind "127.0.0.1:$DIGISERVER_PORT" \
--workers 2 \
--timeout 120 \
--chdir "$ROOT/digiserver-v2" \
wsgi:application
else
warn "digiserver is disabled — skipping"
fi
# IT Asset Management
if module_enabled "itassets"; then
mkdir -p "$ROOT/IT_asset_management/data"
start_bg "itassets" \
env FLASK_ENV=development \
PORT=$ITASSETS_PORT \
SQLALCHEMY_DATABASE_URI="sqlite:///$ROOT/IT_asset_management/data/itassets.db" \
PORTAL_JWT_SECRET=change-this-jwt-secret-in-production \
PORTAL_LOGIN_URL="http://localhost:${NGINX_PORT}/login" \
PORTAL_LOGOUT_URL="http://localhost:${NGINX_PORT}/logout" \
FLASK_APP=run.py \
"$ROOT/IT_asset_management/.venv/bin/python" "$ROOT/IT_asset_management/run.py"
else
warn "itassets is disabled — skipping"
fi
# NetworkView backend
if module_enabled "srvmonitor"; then
mkdir -p "$ROOT/Server_Monitorizare_v2/data"
start_bg "srvmonitor" \
env PORT=$SRVMONITOR_PORT \
FLASK_ENV=development \
PORTAL_LOGIN_URL="http://localhost:${NGINX_PORT}/login" \
"$ROOT/Server_Monitorizare_v2/.venv/bin/python" "$ROOT/Server_Monitorizare_v2/main.py"
else
warn "srvmonitor is disabled — skipping"
fi
if module_enabled "networkview"; then
mkdir -p "$ROOT/NetworkView/data"
start_bg "networkview-backend" \
env PORT=$NV_BACKEND_PORT \
PORTAL_JWT_SECRET=change-this-jwt-secret-in-production \
NODE_ENV=development \
DB_PATH="$ROOT/NetworkView/data/networkview.db" \
node "$ROOT/NetworkView/backend/src/index.js"
else
warn "networkview is disabled — skipping"
fi
# ── Start nginx ────────────────────────────────────────────────────────────────
info "Starting nginx on port $NGINX_PORT..."
"$NGINX_BIN" -c "$NGINX_CONF"
# ── Summary ────────────────────────────────────────────────────────────────────
echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN} Enterprise Digital Platform is running!${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e " Portal (umbrella): http://localhost:${NGINX_PORT}/"
echo -e " DigiServer: http://localhost:${NGINX_PORT}/digiserver/"
echo -e " IT Assets: http://localhost:${NGINX_PORT}/itassets/"
echo -e " Server Monitor: http://localhost:${NGINX_PORT}/srvmonitor/"
echo -e " NetworkView: http://localhost:${NGINX_PORT}/networkview/"
echo ""
echo -e " Login: admin / admin123"
echo ""
echo -e " Logs: $LOG_DIR/"
echo -e " Stop: ./start-dev.sh stop"
echo ""