mirror of
git://f0xx.org/ac/ac-deploy
synced 2026-07-29 03:17:55 +03:00
fix(cluster0): Gitea cluster proxy, 2FA seed, hub downloads nginx
Bind Gitea on all interfaces for cast02/03 proxy, fix stale 2FA lab-seed approve flow, add /v0/downloads/ and GITEA_UPSTREAM_HOST, and guard nginx configure against empty templates. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -51,6 +51,8 @@ POSTGRES_SEMI_ENABLE=1
|
||||
# Gitea browse UI on cast01 (primary); mirrors added later
|
||||
GITEA_ENABLE=1
|
||||
GITEA_HTTP_PORT=3000
|
||||
# nginx on all cluster nodes proxies /git/ to primary (cast01) — see apps-port80.conf
|
||||
GITEA_UPSTREAM_HOST=10.7.16.236
|
||||
GITEA_DB_TYPE=sqlite3
|
||||
GITEA_PUBLIC_HOST=acl0.f0xx.org
|
||||
GITEA_ROOT_URL=https://acl0.f0xx.org/app/androidcast_project/git/
|
||||
|
||||
@@ -17,7 +17,8 @@ LFS_START_SERVER = true
|
||||
PROTOCOL = http
|
||||
DOMAIN = @GITEA_DOMAIN@
|
||||
ROOT_URL = @GITEA_ROOT_URL@
|
||||
HTTP_ADDR = 127.0.0.1
|
||||
# Bind on all interfaces so cast02/cast03 nginx can proxy to cast01:3000.
|
||||
HTTP_ADDR = 0.0.0.0
|
||||
HTTP_PORT = @GITEA_HTTP_PORT@
|
||||
APP_DATA_PATH = @GITEA_APP_DATA@
|
||||
STATIC_ROOT_PATH = /usr/share/webapps/gitea
|
||||
|
||||
@@ -1,35 +1,56 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/** 2FA challenge page helpers (QR short link for mobile handoff). */
|
||||
/** 2FA challenge page helpers — cross-device approval QR. */
|
||||
final class AuthTwoFactorPage {
|
||||
/** @return array{short_url?:string,qr_url?:string,long_url:string}|null */
|
||||
/**
|
||||
* Build the QR payload for the 2FA challenge screen.
|
||||
*
|
||||
* @return array{long_url:string,short_url?:string,qr_url?:string,raw_token:string}|null
|
||||
*/
|
||||
public static function qrPayload(): ?array {
|
||||
$uid = Auth::pending2faUserId();
|
||||
if ($uid <= 0) {
|
||||
return null;
|
||||
}
|
||||
$project = Auth::projectBasePath();
|
||||
$longUrl = 'https://apps.f0xx.org' . $project . '/?ref=2fa&uid=' . $uid;
|
||||
$ip = Auth::clientIp();
|
||||
$rawToken = AuthApproval::create($uid, session_id(), $ip);
|
||||
|
||||
$_SESSION['pending_2fa_approval_token'] = $rawToken;
|
||||
|
||||
$origin = rtrim((string) cfg('public_origin', 'https://apps.f0xx.org'), '/');
|
||||
$approveUrl = $origin . $project . '/two-factor/approve?token=' . urlencode($rawToken);
|
||||
|
||||
$payload = [
|
||||
'long_url' => $approveUrl,
|
||||
'raw_token' => $rawToken,
|
||||
'qr_url' => AuthTotp::qrImageUrl($approveUrl),
|
||||
];
|
||||
|
||||
if (!UrlShortenerDatabase::enabled() || !UrlShortenerDatabase::ping()) {
|
||||
return ['long_url' => $longUrl];
|
||||
return $payload;
|
||||
}
|
||||
$bearers = ShortLinksRepository::listBearers();
|
||||
if ($bearers === []) {
|
||||
return ['long_url' => $longUrl];
|
||||
return $payload;
|
||||
}
|
||||
$bearerId = (int) ($bearers[0]['id'] ?? 0);
|
||||
if ($bearerId <= 0) {
|
||||
return ['long_url' => $longUrl];
|
||||
return $payload;
|
||||
}
|
||||
$mint = ShortLinksRepository::createLink($bearerId, $longUrl, 900);
|
||||
$mint = ShortLinksRepository::createLink($bearerId, $approveUrl, 900);
|
||||
if (empty($mint['ok'])) {
|
||||
return ['long_url' => $longUrl];
|
||||
return $payload;
|
||||
}
|
||||
return [
|
||||
'long_url' => $longUrl,
|
||||
'short_url' => (string) ($mint['short_url'] ?? ''),
|
||||
'qr_url' => (string) ($mint['qr_url'] ?? ''),
|
||||
];
|
||||
$shortUrl = (string) ($mint['short_url'] ?? '');
|
||||
$qrUrl = (string) ($mint['qr_url'] ?? '');
|
||||
if ($qrUrl !== '') {
|
||||
$payload['qr_url'] = $qrUrl;
|
||||
}
|
||||
if ($shortUrl !== '') {
|
||||
$payload['short_url'] = $shortUrl;
|
||||
}
|
||||
return $payload;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,13 @@ server {
|
||||
add_header Cache-Control "public, max-age=60";
|
||||
}
|
||||
|
||||
# Hub download artifacts (Session Studio jar, etc.) — populated by deploy-hub-downloads.sh
|
||||
location ^~ /v0/downloads/ {
|
||||
alias /var/www/localhost/htdocs/apps/app/androidcast_project/downloads/;
|
||||
add_header Cache-Control "public, max-age=300";
|
||||
default_type application/octet-stream;
|
||||
}
|
||||
|
||||
# Favicon — served from hub root (shared by all sub-apps via explicit <link> tags)
|
||||
location = /favicon.ico {
|
||||
alias /var/www/localhost/htdocs/apps/app/androidcast_project/favicon.ico;
|
||||
@@ -254,7 +261,8 @@ server {
|
||||
return 301 $public_scheme://$http_host/app/androidcast_project/git/;
|
||||
}
|
||||
location ^~ /app/androidcast_project/git/ {
|
||||
proxy_pass http://127.0.0.1:3000/;
|
||||
# Gitea runs on cast01 only — do not proxy to 127.0.0.1 on cast02/cast03.
|
||||
proxy_pass http://10.7.16.236:3000/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
|
||||
@@ -10,8 +10,13 @@ ensure_shared_mounted
|
||||
GIT_NGINX="/mnt/repos/ac/ac-deploy/sim/cluster0/nginx/apps-port80.conf"
|
||||
CONF_DST="$ROOT/nginx/apps-port80.conf"
|
||||
if [ -f "$GIT_NGINX" ] && [ "$(realpath "$GIT_NGINX" 2>/dev/null)" != "$(realpath "$CONF_DST" 2>/dev/null)" ]; then
|
||||
if [ -s "$GIT_NGINX" ]; then
|
||||
install -D -m 644 "$GIT_NGINX" "$CONF_DST"
|
||||
else
|
||||
log "WARN: skip empty GIT_NGINX at $GIT_NGINX"
|
||||
fi
|
||||
fi
|
||||
[ -s "$CONF_DST" ] || die "missing or empty nginx template at $CONF_DST"
|
||||
install -D -m 644 "$CONF_DST" /etc/nginx/http.d/androidcast.conf
|
||||
rm -f /etc/nginx/http.d/default.conf /etc/nginx/http.d/cluster.conf 2>/dev/null || true
|
||||
nginx -t
|
||||
|
||||
25
sim/cluster0/scripts/deploy-hub-downloads.sh
Normal file
25
sim/cluster0/scripts/deploy-hub-downloads.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# Publish hub download artifacts (Session Studio jar) to BE downloads mount.
|
||||
# APK is served via OTA stable channel (/v0/ota/) — run ci-build-and-publish-ota.sh separately.
|
||||
set -eu
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
# shellcheck source=/dev/null
|
||||
. "$ROOT/scripts/lib/common.sh"
|
||||
load_cluster_env
|
||||
|
||||
DOWNLOADS_ROOT="${APP_ROOT:-/var/www/localhost/htdocs/apps/app/androidcast_project}/downloads"
|
||||
WS="${AC_WORKSPACE_ROOT:-/var/www/ac/workspace}"
|
||||
STUDIO_JAR="${WS}/ac-session-studio/build/libs/ac-session-studio.jar"
|
||||
|
||||
mkdir -p "$DOWNLOADS_ROOT"
|
||||
|
||||
if [ -f "$STUDIO_JAR" ]; then
|
||||
install -m 644 "$STUDIO_JAR" "${DOWNLOADS_ROOT}/ac-session-studio.jar"
|
||||
log "installed Session Studio jar → ${DOWNLOADS_ROOT}/ac-session-studio.jar"
|
||||
else
|
||||
log "WARN: Session Studio jar not built at ${STUDIO_JAR}"
|
||||
log " cd ac-session-studio && ./gradlew shadowJar (or installDist) on builder node"
|
||||
fi
|
||||
|
||||
log "Hub downloads dir: ${DOWNLOADS_ROOT}"
|
||||
log "URL: ${PUBLIC_ORIGIN:-https://apps.f0xx.org}/v0/downloads/ac-session-studio.jar"
|
||||
Reference in New Issue
Block a user