From 98f30786b775588ebe377dd914a18d19c6979ca4 Mon Sep 17 00:00:00 2001 From: Anton Afanasyeu Date: Tue, 23 Jun 2026 13:10:35 +0200 Subject: [PATCH] cluster0: composed ac backend, /issues/ nginx, postgres semi-prod MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compose lab PHP from ac-workspace overlay; 301 /crashes/ → /issues/; verify on issues path; optional native Postgres on cast01. Co-authored-by: Cursor --- sim/cluster0/cluster.env | 7 +- sim/cluster0/nginx/apps-port80.conf | 58 ++++--- sim/cluster0/populate_lab_setup.sh | 6 + sim/cluster0/scripts/compose-lab-backend.sh | 163 +++++++++++++++++++ sim/cluster0/scripts/deploy-postgres-semi.sh | 32 ++++ sim/cluster0/scripts/validate-ac-repos.sh | 4 +- sim/cluster0/verify_lab_setup.sh | 6 + 7 files changed, 254 insertions(+), 22 deletions(-) create mode 100755 sim/cluster0/scripts/compose-lab-backend.sh create mode 100755 sim/cluster0/scripts/deploy-postgres-semi.sh diff --git a/sim/cluster0/cluster.env b/sim/cluster0/cluster.env index a6fd49b..3b5d6da 100644 --- a/sim/cluster0/cluster.env +++ b/sim/cluster0/cluster.env @@ -37,8 +37,13 @@ URL_SHORTENER_ORIGIN=git://f0xx.org/ac/ac-ms-url-shortener URL_SHORTENER_ROOT=/var/www/url-shortener APP_ROOT=/var/www/localhost/htdocs/apps/app/androidcast_project -APP_BASE_PATH=/app/androidcast_project/crashes +APP_BASE_PATH=/app/androidcast_project/issues +APP_LEGACY_BASE_PATH=/app/androidcast_project/crashes APP_HUB_PATH=/app/androidcast_project +COMPOSE_BACKEND=/var/www/ac/composed/backend +AC_PLATFORM_ROOT=/var/www/ac/platform +COMPOSE_RUNTIME=1 +POSTGRES_SEMI_ENABLE=1 # Set after DNS + FE redirect, then run verify_global_setup.sh PUBLIC_ORIGIN=https://apps.f0xx.org diff --git a/sim/cluster0/nginx/apps-port80.conf b/sim/cluster0/nginx/apps-port80.conf index 5889903..1af3b39 100644 --- a/sim/cluster0/nginx/apps-port80.conf +++ b/sim/cluster0/nginx/apps-port80.conf @@ -34,44 +34,62 @@ server { try_files $uri $uri/ =404; } - location = /app/androidcast_project/crashes { - return 301 /app/androidcast_project/crashes/; - } - location = /app/androidcast_project/graphs { return 301 /app/androidcast_project/graphs/; } - location ^~ /app/androidcast_project/crashes/assets/ { - alias /var/www/localhost/htdocs/apps/app/androidcast_project/android_cast/examples/crash_reporter/backend/public/assets/; + # --- Target paths (SPEC): /issues/ served from composed ac/* backend --- + location = /app/androidcast_project/issues { + return 301 /app/androidcast_project/issues/; } - location ^~ /app/androidcast_project/graphs/assets/ { - alias /var/www/localhost/htdocs/apps/app/androidcast_project/android_cast/examples/crash_reporter/backend/public/assets/; + location ^~ /app/androidcast_project/issues/assets/ { + alias /var/www/ac/composed/backend/public/assets/; } - location = /app/androidcast_project/crashes/api/upload.php { + location = /app/androidcast_project/issues/api/upload.php { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; - fastcgi_param SCRIPT_FILENAME /var/www/localhost/htdocs/apps/app/androidcast_project/android_cast/examples/crash_reporter/backend/public/api/upload.php; - fastcgi_param SCRIPT_NAME /app/androidcast_project/crashes/api/upload.php; + fastcgi_param SCRIPT_FILENAME /var/www/ac/composed/backend/public/api/upload.php; + fastcgi_param SCRIPT_NAME /app/androidcast_project/issues/api/upload.php; fastcgi_param REQUEST_URI $request_uri; client_max_body_size 4m; } + location ^~ /app/androidcast_project/issues/ { + include fastcgi_params; + fastcgi_pass 127.0.0.1:9000; + fastcgi_param SCRIPT_FILENAME /var/www/ac/composed/backend/public/index.php; + fastcgi_param SCRIPT_NAME /app/androidcast_project/issues/index.php; + fastcgi_param REQUEST_URI $request_uri; + client_max_body_size 4m; + } + + # Legacy /crashes/ → 301 to /issues/ (SPEC §7) + location = /app/androidcast_project/crashes { + return 301 /app/androidcast_project/issues/; + } + location ^~ /app/androidcast_project/crashes/ { - include fastcgi_params; - fastcgi_pass 127.0.0.1:9000; - fastcgi_param SCRIPT_FILENAME /var/www/localhost/htdocs/apps/app/androidcast_project/android_cast/examples/crash_reporter/backend/public/index.php; - fastcgi_param SCRIPT_NAME /app/androidcast_project/crashes/index.php; - fastcgi_param REQUEST_URI $request_uri; - client_max_body_size 4m; + rewrite ^/app/androidcast_project/crashes/?(.*)$ /app/androidcast_project/issues/$1 permanent; + } + + location ^~ /app/androidcast_project/crashes/assets/ { + alias /var/www/ac/composed/backend/public/assets/; + } + + location = /app/androidcast_project/crashes/api/upload.php { + return 301 /app/androidcast_project/issues/api/upload.php; + } + + location ^~ /app/androidcast_project/graphs/assets/ { + alias /var/www/ac/composed/backend/public/assets/; } location = /app/androidcast_project/graphs/api/graphs.php { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; - fastcgi_param SCRIPT_FILENAME /var/www/localhost/htdocs/apps/app/androidcast_project/android_cast/examples/crash_reporter/backend/public/api/graphs.php; + fastcgi_param SCRIPT_FILENAME /var/www/ac/composed/backend/public/api/graphs.php; fastcgi_param SCRIPT_NAME /app/androidcast_project/graphs/api/graphs.php; fastcgi_param REQUEST_URI $request_uri; } @@ -79,7 +97,7 @@ server { location = /app/androidcast_project/graphs/api/graph_upload.php { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; - fastcgi_param SCRIPT_FILENAME /var/www/localhost/htdocs/apps/app/androidcast_project/android_cast/examples/crash_reporter/backend/public/api/graph_upload.php; + fastcgi_param SCRIPT_FILENAME /var/www/ac/composed/backend/public/api/graph_upload.php; fastcgi_param SCRIPT_NAME /app/androidcast_project/graphs/api/graph_upload.php; fastcgi_param REQUEST_URI $request_uri; client_max_body_size 4m; @@ -88,7 +106,7 @@ server { location ^~ /app/androidcast_project/graphs/ { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; - fastcgi_param SCRIPT_FILENAME /var/www/localhost/htdocs/apps/app/androidcast_project/android_cast/examples/crash_reporter/backend/public/index.php; + fastcgi_param SCRIPT_FILENAME /var/www/ac/composed/backend/public/index.php; fastcgi_param SCRIPT_NAME /app/androidcast_project/graphs/index.php; fastcgi_param REQUEST_URI $request_uri; client_max_body_size 4m; diff --git a/sim/cluster0/populate_lab_setup.sh b/sim/cluster0/populate_lab_setup.sh index 1876f2c..22edd53 100644 --- a/sim/cluster0/populate_lab_setup.sh +++ b/sim/cluster0/populate_lab_setup.sh @@ -71,6 +71,12 @@ run_phase_app() { run_script "$ROOT/scripts/deploy-app.sh" run_script "$ROOT/scripts/deploy-ac-workspace.sh" run_script "$ROOT/scripts/deploy-ac-url-shortener.sh" + if [ "${COMPOSE_RUNTIME:-0}" = "1" ]; then + run_script "$ROOT/scripts/compose-lab-backend.sh" + fi + if [ "${POSTGRES_SEMI_ENABLE:-0}" = "1" ] && is_primary_node; then + run_script "$ROOT/scripts/deploy-postgres-semi.sh" || journal_warn postgres_semi "install failed (non-fatal)" + fi run_script "$ROOT/scripts/configure-nginx.sh" rc-service php-fpm83 restart 2>/dev/null || true rc-service nginx reload 2>/dev/null || rc-service nginx restart 2>/dev/null || true diff --git a/sim/cluster0/scripts/compose-lab-backend.sh b/sim/cluster0/scripts/compose-lab-backend.sh new file mode 100755 index 0000000..0e4d95b --- /dev/null +++ b/sim/cluster0/scripts/compose-lab-backend.sh @@ -0,0 +1,163 @@ +#!/bin/sh +# Assemble runnable PHP backend from ac-workspace submodules (overlay on legacy seed). +set -eu +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +# shellcheck source=/dev/null +. "$ROOT/scripts/lib/common.sh" +load_cluster_env + +WS="${AC_WORKSPACE_ROOT:-/var/www/ac/workspace}" +LEGACY="${APP_ROOT}/android_cast/examples/crash_reporter/backend" +COMPOSED="${COMPOSE_BACKEND:-/var/www/ac/composed/backend}" +PLATFORM_ROOT="${AC_PLATFORM_ROOT:-/var/www/ac/platform}" +APP_PASS="$(read_cred mariadb_app password)" +DB_HOST="$PRIMARY_DB_HOST" +if is_primary_node; then + DB_HOST=127.0.0.1 +fi + +[ -d "$WS/.git" ] || die "missing ac-workspace at $WS (run deploy-ac-workspace.sh first)" + +overlay_src() { + _repo="$1" + _dir="${WS}/${_repo}/src" + [ -d "$_dir" ] || { log "skip src ${_repo} (no src/)"; return 0; } + log "overlay src ${_repo}" + cp -a "$_dir"/. "${COMPOSED}/src/" +} + +overlay_api() { + _repo="$1" + _dir="${WS}/${_repo}/public/api" + [ -d "$_dir" ] || return 0 + log "overlay api ${_repo}" + mkdir -p "${COMPOSED}/public/api" + cp -a "$_dir"/. "${COMPOSED}/public/api/" +} + +overlay_views() { + _repo="$1" + _dir="${WS}/${_repo}/views" + [ -d "$_dir" ] || return 0 + log "overlay views ${_repo}" + mkdir -p "${COMPOSED}/views" + cp -a "$_dir"/. "${COMPOSED}/views/" +} + +if [ ! -f "${COMPOSED}/public/index.php" ] || [ "${FORCE_COMPOSE_SEED:-0}" = "1" ]; then + [ -d "$LEGACY/public" ] || die "legacy backend missing at $LEGACY — run deploy-app.sh first" + log "seed composed backend from legacy ${LEGACY}" + mkdir -p "$(dirname "$COMPOSED")" + rm -rf "$COMPOSED" + mkdir -p "$COMPOSED" + cp -a "${LEGACY}/." "$COMPOSED/" +fi + +mkdir -p "$PLATFORM_ROOT" +if [ -d "${WS}/ac-platform-php/platform" ]; then + log "sync platform helpers → ${PLATFORM_ROOT}" + cp -a "${WS}/ac-platform-php/platform/." "$PLATFORM_ROOT/" +fi + +if [ -d "${WS}/ac-platform-web/assets" ]; then + log "sync platform-web assets" + mkdir -p "${COMPOSED}/public/assets" + cp -a "${WS}/ac-platform-web/assets/." "${COMPOSED}/public/assets/" + if [ -f "${WS}/ac-platform-web/src/AnalyticsHead.php" ]; then + cp -a "${WS}/ac-platform-web/src/AnalyticsHead.php" "${COMPOSED}/src/" + fi +fi + +overlay_src ac-platform-php +overlay_src ac-ms-identity +overlay_src ac-ms-rbac +overlay_src ac-ms-devices +overlay_src ac-ms-issues +overlay_src ac-ms-tickets +overlay_src ac-ms-graphs +overlay_src ac-ms-remote-access + +overlay_api ac-ms-identity +overlay_api ac-ms-rbac +overlay_api ac-ms-devices +overlay_api ac-ms-issues +overlay_api ac-ms-tickets +overlay_api ac-ms-graphs +overlay_api ac-ms-remote-access + +overlay_views ac-be-auth +overlay_views ac-be-issues +overlay_views ac-be-tickets +overlay_views ac-be-graphs +overlay_views ac-be-remote-access +overlay_views ac-be-access + +ISSUES_BASE="${APP_BASE_PATH:-/app/androidcast_project/issues}" +mkdir -p "${COMPOSED}/config" +log "write composed config.php base_path=${ISSUES_BASE}" +cat > "${COMPOSED}/config/config.php" < 'Android Cast Issues', + 'base_path' => '${ISSUES_BASE}', + 'db' => [ + 'driver' => 'mariadb', + 'sqlite_path' => __DIR__ . '/../data/crashes.sqlite', + 'mysql' => [ + 'host' => '${DB_HOST}', + 'port' => ${PRIMARY_DB_PORT}, + 'socket' => '', + 'database' => 'androidcast_crashes', + 'username' => 'androidcast', + 'password' => '${APP_PASS}', + 'charset' => 'utf8mb4', + ], + ], + 'session_name' => 'ac_crash_sess', + 'session_cookie_path' => '${APP_HUB_PATH}', + 'debug' => true, + 'rbac' => ['default_company_slug' => 'default', 'default_company_id' => 1], + 'analytics' => ['enabled' => false, 'measurement_id' => '', 'debug' => false], + 'remote_access' => [ + 'wg_endpoint' => '', + 'wg_server_public_key' => '', + 'provision_peers' => false, + 'require_wg_tools' => false, + ], + 'auth' => [ + 'encryption_key' => 'lab-cluster-dev-32-char-min-secret!!', + 'max_attempts' => 8, + 'lockout_window_minutes' => 15, + ], + 'url_shortener' => [ + 'enabled' => true, + 'public_base' => '${SHORT_LINKS_PUBLIC_BASE}', + 'db' => [ + 'mysql' => [ + 'host' => '${DB_HOST}', + 'port' => ${PRIMARY_DB_PORT}, + 'socket' => '', + 'database' => 'url_shortener', + 'username' => 'androidcast', + 'password' => '${APP_PASS}', + 'charset' => 'utf8mb4', + ], + ], + ], +]; +PHP + +if command -v composer >/dev/null 2>&1; then + log "composer dump-autoload (optional)" + cat > "${COMPOSED}/composer.json" <<'JSON' +{ + "name": "androidcast/lab-composed-backend", + "require": { "php": ">=8.1" }, + "autoload": { "classmap": ["src/"] } +} +JSON + (cd "$COMPOSED" && composer dump-autoload -o 2>/dev/null) || true +fi + +chown -R nginx:nginx "$(dirname "$COMPOSED")" "$PLATFORM_ROOT" 2>/dev/null || true +log "compose-lab-backend_ok $(host_short) → ${COMPOSED}" diff --git a/sim/cluster0/scripts/deploy-postgres-semi.sh b/sim/cluster0/scripts/deploy-postgres-semi.sh new file mode 100755 index 0000000..fa4a3ab --- /dev/null +++ b/sim/cluster0/scripts/deploy-postgres-semi.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# Optional semi-prod PostgreSQL on cast01 (native Alpine, no Docker). +set -eu +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +# shellcheck source=/dev/null +. "$ROOT/scripts/lib/common.sh" +load_cluster_env + +[ "$(host_short)" = "$CAST01_HOST" ] || { log "postgres semi-prod: cast01 only — skip on $(host_short)"; exit 0; } + +PG_VER="${POSTGRES_SEMI_VERSION:-16}" +DB_NAME="${POSTGRES_SEMI_DB:-androidcast_semi_pg}" + +if ! apk info -e "postgresql${PG_VER}" >/dev/null 2>&1; then + log "install postgresql${PG_VER}" + apk add --no-cache "postgresql${PG_VER}" "postgresql${PG_VER}-contrib" || apk add --no-cache postgresql postgresql-contrib +fi + +if [ ! -d /var/lib/postgresql/data/PG_VERSION ]; then + log "initdb" + mkdir -p /var/lib/postgresql/data + chown postgres:postgres /var/lib/postgresql/data + su postgres -c "initdb -D /var/lib/postgresql/data" +fi + +rc-service postgresql restart 2>/dev/null || rc-service postgresql start +sleep 2 + +su postgres -c "psql -tc \"SELECT 1 FROM pg_database WHERE datname='${DB_NAME}'\" | grep -q 1" \ + || su postgres -c "createdb ${DB_NAME}" + +log "postgres_semi_ok db=${DB_NAME} host=127.0.0.1" diff --git a/sim/cluster0/scripts/validate-ac-repos.sh b/sim/cluster0/scripts/validate-ac-repos.sh index 92ee999..8058585 100755 --- a/sim/cluster0/scripts/validate-ac-repos.sh +++ b/sim/cluster0/scripts/validate-ac-repos.sh @@ -37,8 +37,10 @@ check_dir "ac-workspace" "$AC_ROOT" check_dir "ac-platform-db" "${AC_ROOT}/ac-platform-db/sql/crash_reporter" check_dir "ac-platform-php" "${AC_ROOT}/ac-platform-php/src" check_dir "ac-platform-web" "${AC_ROOT}/ac-platform-web/assets/css" -check_dir "legacy-monolith" "$LEGACY" +check_dir "composed-backend" "${COMPOSE_BACKEND:-/var/www/ac/composed/backend}" +check_file "composed-index" "${COMPOSE_BACKEND:-/var/www/ac/composed/backend}/public/index.php" check_file "legacy-backend" "${LEGACY}/examples/crash_reporter/backend/public/index.php" +check_dir "legacy-monolith" "$LEGACY" check_dir "url-shortener" "${URL_SHORTENER_ROOT:-/var/www/url-shortener}/public" if [ -d "${AC_ROOT}/.git" ]; then diff --git a/sim/cluster0/verify_lab_setup.sh b/sim/cluster0/verify_lab_setup.sh index d28bb24..eb8b051 100644 --- a/sim/cluster0/verify_lab_setup.sh +++ b/sim/cluster0/verify_lab_setup.sh @@ -46,6 +46,12 @@ check_local_node() { DIAG="$(curl -sS "http://127.0.0.1${APP_BASE_PATH}/api/diag.php" 2>/dev/null || true)" echo "$DIAG" | grep -q '"ok":true' || { log "FAIL diag.php"; FAIL=1; } + LEGACY_REDIRECT="$(curl -sS -o /dev/null -w '%{http_code}' "http://127.0.0.1${APP_LEGACY_BASE_PATH:-/app/androidcast_project/crashes}/" 2>/dev/null || echo 000)" + case "$LEGACY_REDIRECT" in + 301|308) log "legacy_redirect=${LEGACY_REDIRECT}" ;; + *) log "WARN legacy_redirect=${LEGACY_REDIRECT} (want 301 from ${APP_LEGACY_BASE_PATH}/)" ;; + esac + if [ -x "$ROOT/scripts/validate-ac-repos.sh" ]; then sh "$ROOT/scripts/validate-ac-repos.sh" || FAIL=1 fi