mirror of
git://f0xx.org/ac/ac-deploy
synced 2026-07-29 02:57:38 +03:00
400 lines
14 KiB
Bash
Executable File
400 lines
14 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}"
|
|
|
|
ensure_ac_ms_media_transcode() {
|
|
_mt="${WS}/ac-ms-media-transcode"
|
|
_origin="${AC_MEDIA_TRANSCODE_ORIGIN:-git://f0xx.org/ac/ac-ms-media-transcode}"
|
|
_branch="${AC_GIT_BRANCH:-next}"
|
|
_seed="${AC_ROOT:-/mnt/repos/ac}/ac-ms-media-transcode"
|
|
if [ -f "${_mt}/assets/pirate_drift.png" ] && [ -x "${_mt}/bin/run.sh" ]; then
|
|
log "ac-ms-media-transcode OK at ${_mt}"
|
|
chmod +x "${_mt}/bin/"*.sh 2>/dev/null || true
|
|
return 0
|
|
fi
|
|
if [ -f "${_seed}/assets/pirate_drift.png" ] && [ -d "${_seed}/.git" ]; then
|
|
log "seed ac-ms-media-transcode from ${_seed} → ${_mt}"
|
|
rm -rf "${_mt}"
|
|
cp -a "${_seed}" "${_mt}"
|
|
chmod +x "${_mt}/bin/"*.sh 2>/dev/null || true
|
|
return 0
|
|
fi
|
|
if [ -d "${_mt}/.git" ]; then
|
|
log "update ac-ms-media-transcode in ${_mt}"
|
|
git -C "${_mt}" fetch origin 2>/dev/null || true
|
|
git -C "${_mt}" checkout "${_branch}" 2>/dev/null || true
|
|
git -C "${_mt}" pull --ff-only origin "${_branch}" 2>/dev/null \
|
|
|| git -C "${_mt}" reset --hard "origin/${_branch}" 2>/dev/null || true
|
|
else
|
|
log "clone ac-ms-media-transcode → ${_mt}"
|
|
rm -rf "${_mt}"
|
|
git clone --branch "${_branch}" "${_origin}" "${_mt}"
|
|
fi
|
|
chmod +x "${_mt}/bin/"*.sh 2>/dev/null || true
|
|
}
|
|
ensure_ac_ms_media_transcode
|
|
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
|
|
|
|
if [ -f "${WS}/ac-be-auth/public/assets/js/two_factor.js" ]; then
|
|
log "sync ac-be-auth two_factor.js"
|
|
mkdir -p "${COMPOSED}/public/assets/js"
|
|
cp -a "${WS}/ac-be-auth/public/assets/js/two_factor.js" "${COMPOSED}/public/assets/js/"
|
|
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_lab_seed_src MediaPipelinesRepository.php
|
|
overlay_lab_seed_src MediaPipelineRunner.php
|
|
|
|
overlay_lab_seed_api() {
|
|
_f="$1"
|
|
if [ -f "${LEGACY}/public/api/${_f}" ]; then
|
|
log "overlay lab-seed api ${_f}"
|
|
mkdir -p "${COMPOSED}/public/api"
|
|
cp -a "${LEGACY}/public/api/${_f}" "${COMPOSED}/public/api/"
|
|
fi
|
|
}
|
|
overlay_lab_seed_api media_pipelines.php
|
|
|
|
overlay_lab_seed_public() {
|
|
_rel="$1"
|
|
if [ -f "${LEGACY}/public/${_rel}" ]; then
|
|
log "overlay lab-seed public/${_rel}"
|
|
mkdir -p "${COMPOSED}/public/$(dirname "$_rel")"
|
|
cp -a "${LEGACY}/public/${_rel}" "${COMPOSED}/public/${_rel}"
|
|
fi
|
|
}
|
|
overlay_lab_seed_public assets/js/media_pipelines.js
|
|
overlay_lab_seed_public index.php
|
|
|
|
overlay_lab_seed_sql() {
|
|
_f="$1"
|
|
if [ -f "${LEGACY}/sql/migrations/${_f}" ]; then
|
|
log "overlay lab-seed sql/migrations/${_f}"
|
|
mkdir -p "${COMPOSED}/sql/migrations"
|
|
cp -a "${LEGACY}/sql/migrations/${_f}" "${COMPOSED}/sql/migrations/"
|
|
fi
|
|
}
|
|
overlay_lab_seed_sql 081_media_pipelines.sql
|
|
overlay_lab_seed_sql 081_media_pipelines.sqlite.sql
|
|
|
|
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-tickets
|
|
overlay_views ac-be-graphs
|
|
overlay_views ac-be-remote-access
|
|
overlay_views ac-be-access
|
|
# ac-be-issues last — its layout.php is the canonical nav shell (other BE repos ship stubs).
|
|
overlay_views ac-be-issues
|
|
|
|
ISSUES_BASE="${APP_BASE_PATH:-/app/androidcast_project/issues}"
|
|
PUBLIC_ORIGIN_CFG="${LAB_PUBLIC_ORIGIN:-https://apps.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',
|
|
],
|
|
],
|
|
],
|
|
'media_pipelines' => [
|
|
'enabled' => true,
|
|
'developers_only' => true,
|
|
'script_root' => '${WS}/ac-ms-media-transcode',
|
|
'storage_root' => '/var/www/ac/media-pipelines',
|
|
'hls_url_prefix' => '/app/androidcast_project/media/hls',
|
|
'hls_filesystem_root' => '/var/www/ac/media-pipelines/hls',
|
|
'logo_path' => '${WS}/ac-ms-media-transcode/assets/pirate_drift.png',
|
|
'ytdlp_enabled' => true,
|
|
],
|
|
];
|
|
PHP
|
|
|
|
MEDIA_ROOT="/var/www/ac/media-pipelines"
|
|
MEDIA_HLS="${MEDIA_ROOT}/hls"
|
|
MT_ROOT="${WS}/ac-ms-media-transcode"
|
|
log "ensure media-pipelines dirs ${MEDIA_ROOT}"
|
|
mkdir -p "${MEDIA_HLS}" "${MEDIA_ROOT}/pipelines"
|
|
chown nginx:nginx "${MEDIA_ROOT}" "${MEDIA_HLS}" 2>/dev/null || true
|
|
if [ -d "${MT_ROOT}/bin" ]; then
|
|
chmod +x "${MT_ROOT}"/bin/*.sh 2>/dev/null || true
|
|
log "media-transcode scripts at ${MT_ROOT} (logo: ${MT_ROOT}/assets/pirate_drift.png)"
|
|
fi
|
|
|
|
BUILD_CFG="${WS}/ac-be-builder/config/config.php"
|
|
if [ -f "${COMPOSED}/config/config.php" ]; then
|
|
log "write ac-be-builder config.php (shared cluster session + DB)"
|
|
cat > "$BUILD_CFG" <<'PHP'
|
|
<?php
|
|
$config = require '/var/www/ac/composed/backend/config/config.php';
|
|
$config['app_name'] = 'Android Cast Builder';
|
|
$config['base_path'] = '/app/androidcast_project/build';
|
|
$config['links'] = [
|
|
'hub' => '/app/androidcast_project/',
|
|
'crashes' => '/app/androidcast_project/issues/',
|
|
'git' => '/app/androidcast_project/git/',
|
|
'tickets' => '/app/androidcast_project/issues/?view=tickets',
|
|
];
|
|
$config['build'] = array_merge([
|
|
'docker_image' => 'androidcast-ci:latest',
|
|
'docker_bin' => '/usr/bin/docker',
|
|
'ci_version' => '00.01.00.1000',
|
|
'repo_root' => '/var/www/ac/workspace',
|
|
'artifacts_root' => '/var/www/ac/broadcast/builds',
|
|
'ota_mount' => '/var/www/localhost/htdocs/apps/app/androidcast_project/ota-artifacts',
|
|
'downloads_mount' => '/var/www/localhost/htdocs/apps/app/androidcast_project/downloads',
|
|
'ota_base_url' => 'https://apps.f0xx.org',
|
|
'runner_script' => '/var/www/ac/workspace/ac-scripts/docker-build-runner.sh',
|
|
'isolate_source' => true,
|
|
'git_clone_depth' => 1,
|
|
'pipeline_config' => '/var/www/ac/workspace/ac-scripts/build.config.yml',
|
|
'default_channels' => ['stable', 'staging', 'dev', 'nightly'],
|
|
'mobile_subdir' => 'ac-mobile-android',
|
|
'scripts_dir' => '/var/www/ac/workspace/ac-scripts',
|
|
'notify' => [
|
|
'admin_email' => 'bestcastr@gmail.com',
|
|
'broadcast_config' => '/var/www/ac/workspace/ac-ms-broadcast/config/config.php',
|
|
],
|
|
], $config['build'] ?? []);
|
|
return $config;
|
|
PHP
|
|
chown nginx:nginx "$BUILD_CFG" 2>/dev/null || true
|
|
fi
|
|
|
|
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}"
|