1
0
mirror of git://f0xx.org/ac/ac-deploy synced 2026-07-29 09:18:53 +03:00
Files
ac-deploy/sim/cluster0/scripts/compose-lab-backend.sh
Anton Afanasyev 4d2db29b8b Lab cluster: deploy WG/RSSH on cast01 + compose remote_access from secrets.
Add deploy-remote-access-lab.sh, wire WG/RSSH into compose-lab-backend.php,
and run RA deploy during populate app phase on primary.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 22:57:28 +02:00

266 lines
9.2 KiB
Bash
Executable File

#!/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}"
LAB_SEED="${ROOT}/lab-seeds/backend"
LEGACY="$LAB_SEED"
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 "backend seed missing at $LEGACY (sync ac-deploy lab-seeds to NFS /shared/cluster)"
log "seed composed backend from ${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
# Lab-seed PHP not yet split into ac-ms-* repos — NFS seed stays authoritative after overlays.
overlay_lab_seed_src() {
_f="$1"
if [ -f "${LEGACY}/src/${_f}" ]; then
log "overlay lab-seed src ${_f}"
cp -a "${LEGACY}/src/${_f}" "${COMPOSED}/src/"
fi
}
overlay_lab_seed_src ShortLinksRepository.php
overlay_lab_seed_src UrlShortenerDatabase.php
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}"
PUBLIC_ORIGIN_CFG="${LAB_PUBLIC_ORIGIN:-https://acl0.f0xx.org}"
load_secrets_lab
AUTH_KEY="${AUTH_ENCRYPTION_KEY:-lab-cluster-dev-32-char-min-secret!!}"
_MSMTP_PASS="$(read_secret MSMTP_APP_PASSWORD 2>/dev/null || true)"
[ -n "$_MSMTP_PASS" ] || _MSMTP_PASS="${MSMTP_APP_PASSWORD:-}"
MAIL_TRANSPORT="${MAIL_TRANSPORT:-sendmail}"
_mail_from_secret="$(read_secret MAIL_FROM 2>/dev/null || true)"
[ -n "$_mail_from_secret" ] || _mail_from_secret="${MAIL_FROM:-}"
MAIL_REPLY="${MAIL_REPLY_TO:-bestcastr@gmail.com}"
SMTP_HOST="${SMTP_HOST:-127.0.0.1}"
SMTP_PORT="${SMTP_PORT:-587}"
SMTP_ENC="${SMTP_ENCRYPTION:-tls}"
SMTP_USER="${SMTP_USER:-${MSMTP_USER:-}}"
SMTP_PASS="${SMTP_PASS:-}"
if [ -n "$_MSMTP_PASS" ]; then
MAIL_TRANSPORT=smtp
SMTP_HOST=smtp.gmail.com
SMTP_USER="${MSMTP_USER:-bestcastr@gmail.com}"
SMTP_PASS="$_MSMTP_PASS"
fi
MAIL_FROM="$(normalize_mail_from "$_mail_from_secret" "$SMTP_USER")"
[ -n "$MAIL_FROM" ] || MAIL_FROM="Android Cast Lab <${SMTP_USER:-bestcastr@gmail.com}>"
unset _MSMTP_PASS _mail_from_secret
MAIL_FROM_PHP="$(printf '%s' "$MAIL_FROM" | sed "s/'/\\\\'/g")"
AUTH_BEARER_ID="$(read_secret URL_SHORTENER_AUTH_BEARER_ID 2>/dev/null || true)"
[ -n "$AUTH_BEARER_ID" ] || AUTH_BEARER_ID="${URL_SHORTENER_AUTH_BEARER_ID:-0}"
API_BEARER_TOKEN="$(read_secret URL_SHORTENER_API_BEARER_TOKEN 2>/dev/null || true)"
API_BEARER_PHP=""
if [ -n "$API_BEARER_TOKEN" ]; then
API_BEARER_PHP="$(printf '%s' "$API_BEARER_TOKEN" | sed "s/'/\\\\'/g")"
fi
WG_ENDPOINT_CFG="${WG_ENDPOINT:-}"
WG_SERVER_PUBLIC_KEY_CFG="${WG_SERVER_PUBLIC_KEY:-}"
WG_PROVISION="${WG_PROVISION_PEERS:-0}"
if [ -f /etc/wireguard/keys/server.publickey ] && [ -z "$WG_SERVER_PUBLIC_KEY_CFG" ]; then
WG_SERVER_PUBLIC_KEY_CFG="$(cat /etc/wireguard/keys/server.publickey)"
fi
if [ -z "$WG_ENDPOINT_CFG" ] && [ -n "$WG_SERVER_PUBLIC_KEY_CFG" ]; then
WG_ENDPOINT_CFG="${CAST01_HOST:-cast01}.${CAST_DOMAIN:-intra.raptor.org}:51820"
WG_PROVISION=1
fi
WG_PROVISION_PHP=false
RSSH_PROVISION_PHP=false
[ "$WG_PROVISION" = "1" ] && WG_PROVISION_PHP=true
RSSH_BASTION_HOST_CFG="${RSSH_BASTION_HOST:-}"
RSSH_BASTION_PORT_CFG="${RSSH_BASTION_PORT:-22}"
if [ -z "$RSSH_BASTION_HOST_CFG" ] && [ -n "$WG_SERVER_PUBLIC_KEY_CFG" ]; then
RSSH_BASTION_HOST_CFG="${CAST01_HOST:-cast01}.${CAST_DOMAIN:-intra.raptor.org}"
fi
RSSH_PROVISION="${RSSH_PROVISION_USERS:-0}"
if [ -n "$RSSH_BASTION_HOST_CFG" ]; then
RSSH_PROVISION=1
fi
[ "$RSSH_PROVISION" = "1" ] && RSSH_PROVISION_PHP=true
URL_SHORTENER_API_BASE_CFG="${URL_SHORTENER_API_BASE:-${SHORT_LINKS_PUBLIC_BASE}}"
URL_SHORTENER_API_HOST_CFG="${URL_SHORTENER_API_HOST:-}"
mkdir -p "${COMPOSED}/config"
log "write composed config.php base_path=${ISSUES_BASE}"
cat > "${COMPOSED}/config/config.php" <<PHP
<?php
return [
'app_name' => 'Android Cast Issues',
'base_path' => '${ISSUES_BASE}',
'public_origin' => '${PUBLIC_ORIGIN_CFG}',
'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_ENDPOINT_CFG}',
'wg_server_public_key' => '${WG_SERVER_PUBLIC_KEY_CFG}',
'wg_interface' => 'wg0',
'wg_server_allowed_ips' => '172.200.2.1/32',
'wg_client_ip_prefix' => '172.200.2.',
'wg_client_ip_min' => 10,
'wg_client_ip_max' => 250,
'wg_set_prefix' => 'sudo -n ',
'provision_peers' => ${WG_PROVISION_PHP:-false},
'require_wg_tools' => ${WG_PROVISION_PHP:-false},
'session_ttl_s' => 3600,
'min_poll_interval_s' => 45,
'rssh' => [
'bastion_host' => '${RSSH_BASTION_HOST_CFG}',
'bastion_port' => ${RSSH_BASTION_PORT_CFG:-22},
'local_forward_port' => 8022,
'remote_port_min' => 18000,
'remote_port_max' => 18999,
'provision_users' => ${RSSH_PROVISION_PHP:-false},
'bastion_user_script' => __DIR__ . '/../scripts/rssh_bastion_user.sh',
],
],
'auth' => [
'encryption_key' => '${AUTH_KEY}',
'max_attempts' => 8,
'lockout_window_minutes' => 15,
],
'mail' => [
'transport' => '${MAIL_TRANSPORT}',
'from' => '${MAIL_FROM_PHP}',
'reply_to' => '${MAIL_REPLY}',
'smtp' => [
'host' => '${SMTP_HOST}',
'port' => ${SMTP_PORT},
'encryption' => '${SMTP_ENC}',
'username' => '${SMTP_USER}',
'password' => '${SMTP_PASS}',
],
],
'url_shortener' => [
'enabled' => true,
'public_base' => '${SHORT_LINKS_PUBLIC_BASE}',
'api_base' => '${URL_SHORTENER_API_BASE_CFG}',
'api_host' => '${URL_SHORTENER_API_HOST_CFG}',
'api_bearer_token' => '${API_BEARER_PHP}',
'auth_bearer_id' => ${AUTH_BEARER_ID:-0},
'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}"