feat(live): add live.transquinnftw.com deployment surface with SSO /admin (reuse quinn-www/vip pattern) + basic player at /shows/live and light admin preview page for OBS produced HLS from the relay cast (input cast IP, hls.js player).
Wiring: enable HLS port in cast/infra mediamtx + ufw notes; add deploy:live case + help in run/deploy.sh; update live deploy script. Ties the quinn.cast relay (on-demand DO) to the VIP shows live feature (fanout to live.transquinnftw.com ingest powers the player; /admin for SSO operator preview + light admin).
This commit is contained in:
parent
ec98112267
commit
0da0e1233c
12 changed files with 382 additions and 1 deletions
|
|
@ -26,6 +26,13 @@ rtspAddress: :8554
|
|||
webrtc: yes
|
||||
webrtcAddress: :8889
|
||||
|
||||
# HLS for easy preview (used by live admin /admin and /shows/live player).
|
||||
# URL example: http://<droplet>:8888/hls/live/produced/index.m3u8
|
||||
hls: yes
|
||||
hlsAddress: :8888
|
||||
hlsAllowOrigin: '*'
|
||||
hlsTrustedProxies: []
|
||||
|
||||
# Control API (used by healthchecks and optionally controller for stats).
|
||||
api: yes
|
||||
apiAddress: :9997
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ ufw default allow outgoing || true
|
|||
ufw allow 22/tcp || true
|
||||
ufw allow 8080/tcp || true # LLM controller UI (gate with passphrase; put real TLS/domain in front soon)
|
||||
ufw allow 8890/udp || true # SRT ingest (public by design; hotel push targets this)
|
||||
ufw allow 8888/tcp || true # HLS preview (for live.transquinnftw.com/admin + shows; operator or proxied)
|
||||
ufw allow 1935/tcp || true # RTMP (local produced from OBS; also optional public ingest)
|
||||
ufw allow 4455/tcp || true # obs-websocket (internal to compose; do not expose publicly)
|
||||
ufw allow 8554/tcp || true # rtsp (optional)
|
||||
|
|
|
|||
33
deployments/@domains/live.transquinnftw.com/deploy.sh
Normal file
33
deployments/@domains/live.transquinnftw.com/deploy.sh
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env bash
|
||||
# Deploy for live.transquinnftw.com (VIP live shows player + /admin preview).
|
||||
# Light static surface. Run via ./run deploy:live or directly with --from-local.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
||||
|
||||
REMOTE="${REMOTE:-quinn-vps}"
|
||||
WWW_ROOT="/var/www/live.transquinnftw.com/public"
|
||||
NGINX_SITE="live.transquinnftw.com"
|
||||
|
||||
echo "==> Deploying live.transquinnftw.com static to ${REMOTE}"
|
||||
|
||||
# Sync the static assets (player + admin preview)
|
||||
ssh "$REMOTE" "mkdir -p ${WWW_ROOT} ${WWW_ROOT}/admin ${WWW_ROOT}/shows"
|
||||
rsync -a --delete \
|
||||
--exclude '.git' \
|
||||
"${SCRIPT_DIR}/public/" \
|
||||
"${REMOTE}:${WWW_ROOT}/"
|
||||
|
||||
# Deploy nginx vhost (if present)
|
||||
if [[ -f "${SCRIPT_DIR}/nginx/prod.conf" ]]; then
|
||||
echo "==> Syncing nginx vhost"
|
||||
ssh "$REMOTE" "mkdir -p /etc/nginx/sites-available"
|
||||
scp "${SCRIPT_DIR}/nginx/prod.conf" "${REMOTE}:/etc/nginx/sites-available/${NGINX_SITE}"
|
||||
ssh "$REMOTE" "ln -sf /etc/nginx/sites-available/${NGINX_SITE} /etc/nginx/sites-enabled/${NGINX_SITE}"
|
||||
ssh "$REMOTE" "nginx -t && systemctl reload nginx || true"
|
||||
fi
|
||||
|
||||
echo "==> Done. Test: curl -I https://live.transquinnftw.com/ and https://live.transquinnftw.com/admin (SSO)"
|
||||
echo "==> Player demo: https://live.transquinnftw.com/shows/live (add ?src=... for manual HLS test)"
|
||||
108
deployments/@domains/live.transquinnftw.com/nginx/prod.conf
Normal file
108
deployments/@domains/live.transquinnftw.com/nginx/prod.conf
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# live.transquinnftw.com - VIP live shows player + operator admin
|
||||
#
|
||||
# This domain powers the VIP live streaming feature (vip.transquinnftw.com/shows/live,list).
|
||||
# Public: /shows/live (player for authorized VIP clients), /shows (list?).
|
||||
# Operator: /admin (light admin + preview of the OBS produced stream from the active relay).
|
||||
#
|
||||
# The high-bitrate feed is produced on a quinn.cast DO relay droplet (hotel SRT thin -> DO OBS encode)
|
||||
# and fanned out (including to rtmp://live.transquinnftw.com/app/<show-key>) so the platform ingest
|
||||
# can serve it here.
|
||||
#
|
||||
# SSO: reuse the quinn-www / quinn-vip pattern.
|
||||
# - /admin is edge-gated with auth_request to quinn.sso (operator only).
|
||||
# - Public player uses client-side VIP token (like the vip portal) or show-specific access.
|
||||
# - The admin preview can pull HLS from the relay's mediamtx (e.g. http://<cast-ip>:8888/hls/live/produced/index.m3u8)
|
||||
# or from the platform ingest HLS once wired.
|
||||
#
|
||||
# CSP strict, private where needed, noindex for admin.
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name live.transquinnftw.com;
|
||||
|
||||
return 301 https://live.transquinnftw.com$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name live.transquinnftw.com;
|
||||
|
||||
# SSL — shared SAN cert with transquinnftw.com
|
||||
ssl_certificate /etc/letsencrypt/live/transquinnftw.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/transquinnftw.com/privkey.pem;
|
||||
ssl_trusted_certificate /etc/letsencrypt/live/transquinnftw.com/chain.pem;
|
||||
|
||||
# Mozilla Intermediate
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSLLIVE:10m;
|
||||
ssl_session_tickets off;
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
|
||||
root /var/www/live.transquinnftw.com/public;
|
||||
index index.html;
|
||||
|
||||
client_body_timeout 10s;
|
||||
client_header_timeout 10s;
|
||||
|
||||
# Security headers
|
||||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' https://my.transquinnftw.com; media-src 'self' blob: http: https:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests" always;
|
||||
add_header X-Frame-Options "DENY" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "no-referrer" always;
|
||||
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||
|
||||
# Simple static for player (can be enhanced to SPA later)
|
||||
location /shows {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
try_files $uri $uri/ /shows/index.html;
|
||||
}
|
||||
|
||||
location = /shows/live {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
try_files /shows/live/index.html /shows/index.html =404;
|
||||
}
|
||||
|
||||
# SSO gate for /admin (exact reuse of quinn-www / quinn-vip pattern)
|
||||
location = /_sso_verify {
|
||||
internal;
|
||||
proxy_pass http://127.0.0.1:3025/auth/validate;
|
||||
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-Forwarded-Host $host;
|
||||
}
|
||||
location @sso_redirect {
|
||||
return 302 https://sso.transquinnftw.com/login?redirect=https://live.transquinnftw.com$request_uri;
|
||||
}
|
||||
|
||||
# /admin : light admin + OBS stream preview (SSO required)
|
||||
# The page (static for now, or future SPA shell) can:
|
||||
# - Input cast IP or relay ID (from active VIP show)
|
||||
# - Preview the OBS output via HLS from the relay mediamtx (http://<ip>:8888/hls/live/produced/index.m3u8)
|
||||
# or platform ingest HLS.
|
||||
# - Light controls/status (future: call relay controller API for scenes, start/stop if exposed).
|
||||
location ^~ /admin {
|
||||
auth_request /_sso_verify;
|
||||
error_page 401 = @sso_redirect;
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
try_files /admin/index.html /index.html =404;
|
||||
}
|
||||
|
||||
# SPA / static fallback for other paths (player etc.)
|
||||
location / {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
access_log /var/log/nginx/live.transquinnftw.com.access.log;
|
||||
error_log /var/log/nginx/live.transquinnftw.com.error.log;
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>live / admin — VIP shows (SSO)</title>
|
||||
<style>
|
||||
:root { --bg: #0a0a0f; --fg: #e8e0d0; --accent: #c9a26b; --card: #12121a; --border: #2a2a38; }
|
||||
body { margin: 0; font-family: system-ui, sans-serif; background: var(--bg); color: var(--fg); }
|
||||
header { padding: 12px 24px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }
|
||||
.brand { font-weight: 700; letter-spacing: -.02em; }
|
||||
main { padding: 24px; max-width: 1200px; margin: 0 auto; }
|
||||
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
|
||||
.card { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 16px; }
|
||||
h1 { font-size: 1.1rem; margin: 0 0 12px; }
|
||||
label { display: block; font-size: .75rem; opacity: .7; margin-bottom: 4px; }
|
||||
input { width: 100%; padding: 8px 10px; background: #0a0a0f; border: 1px solid var(--border); border-radius: 6px; color: inherit; font-family: ui-monospace, monospace; }
|
||||
button { padding: 8px 14px; background: var(--accent); color: #111; border: none; border-radius: 6px; font-weight: 600; cursor: pointer; }
|
||||
button:disabled { opacity: .5; cursor: not-allowed; }
|
||||
video { width: 100%; background: #000; border-radius: 8px; }
|
||||
.status { font-size: .8rem; padding: 4px 8px; border-radius: 4px; background: #1a1a22; }
|
||||
.note { font-size: .7rem; opacity: .6; }
|
||||
.row { display: flex; gap: 8px; align-items: flex-end; margin-bottom: 12px; }
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="brand">live.transquinnftw.com / admin</div>
|
||||
<div class="note">SSO protected • light admin + OBS preview (VIP shows)</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="grid">
|
||||
<!-- Controls / Status -->
|
||||
<div class="card">
|
||||
<h1>Relay Preview</h1>
|
||||
<div class="row">
|
||||
<div style="flex:1">
|
||||
<label>Cast droplet IP (from active relay / provision)</label>
|
||||
<input id="castIp" placeholder="203.0.113.77" value="">
|
||||
</div>
|
||||
<button onclick="loadPreview()">Load / Refresh Preview</button>
|
||||
</div>
|
||||
|
||||
<div style="margin: 12px 0;">
|
||||
<div class="status" id="status">Enter cast IP and load preview. HLS from relay mediamtx (produced feed).</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Preview (OBS produced output from the cast)</label>
|
||||
<video id="preview" controls playsinline></video>
|
||||
<div class="note">HLS URL: http://<ip>:8888/hls/live/produced/index.m3u8 (enable hls in cast mediamtx if needed)</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:12px; font-size:.75rem; opacity:.7;">
|
||||
Light admin: use the relay controller UI on the cast (http://<ip>:8080) or the quinn.cast LLM chat for full control (scenes, start/stop, destinations including vip-live).
|
||||
Status can be pulled from the relay controller /api/status when exposed.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Light admin notes / future -->
|
||||
<div class="card">
|
||||
<h1>Light Admin</h1>
|
||||
<p style="font-size:.85rem; opacity:.8;">This is the operator view for the active VIP live show relay.</p>
|
||||
<ul style="font-size:.8rem; line-height:1.5; opacity:.8;">
|
||||
<li>Preview the clean OBS output (scenes + overlays + mix) before/while fanning to the VIP ingest.</li>
|
||||
<li>Active relay details (cast IP, status) come from the VIP show booking or relay controller.</li>
|
||||
<li>For full control (LLM commands, add "vip-live" destination with show key): use the relay controller on the cast or the broadcast API.</li>
|
||||
<li>The fanout from this relay powers the player at /shows/live for authorized VIP clients.</li>
|
||||
</ul>
|
||||
<div class="note">SSO via quinn.sso (same as vip.transquinnftw.com/admin and transquinnftw.com protected routes).</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
let hls = null;
|
||||
const video = document.getElementById('preview');
|
||||
const statusEl = document.getElementById('status');
|
||||
|
||||
function loadPreview() {
|
||||
const ip = document.getElementById('castIp').value.trim();
|
||||
if (!ip) {
|
||||
statusEl.textContent = 'Enter a cast droplet public IP.';
|
||||
return;
|
||||
}
|
||||
const hlsUrl = `http://${ip}:8888/hls/live/produced/index.m3u8`;
|
||||
statusEl.textContent = `Loading preview from ${hlsUrl} ...`;
|
||||
|
||||
if (hls) {
|
||||
hls.destroy();
|
||||
hls = null;
|
||||
}
|
||||
if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
// native HLS (Safari)
|
||||
video.src = hlsUrl;
|
||||
} else if (Hls.isSupported()) {
|
||||
hls = new Hls({ enableWorker: true, lowLatencyMode: true });
|
||||
hls.loadSource(hlsUrl);
|
||||
hls.attachMedia(video);
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
statusEl.textContent = 'Preview live (HLS manifest parsed).';
|
||||
video.play().catch(() => {});
|
||||
});
|
||||
hls.on(Hls.Events.ERROR, (e, data) => {
|
||||
console.warn('HLS error', data);
|
||||
statusEl.textContent = 'Preview error (check cast mediamtx + HLS enabled + firewall).';
|
||||
});
|
||||
} else {
|
||||
statusEl.textContent = 'HLS not supported in this browser.';
|
||||
return;
|
||||
}
|
||||
|
||||
// basic health ping to the relay controller (optional, if reachable; CORS may block)
|
||||
fetch(`http://${ip}:8080/health`, { mode: 'no-cors' }).catch(() => {});
|
||||
}
|
||||
|
||||
// Keyboard hint
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === '/' && document.activeElement.tagName === 'BODY') {
|
||||
e.preventDefault();
|
||||
document.getElementById('castIp').focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-hint
|
||||
setTimeout(() => {
|
||||
const ip = document.getElementById('castIp');
|
||||
if (!ip.value) ip.placeholder = 'e.g. 203.0.113.77 (the active cast from provision)';
|
||||
}, 800);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<!doctype html>
|
||||
<html><head><meta charset="utf-8"><title>live.transquinnftw.com</title></head>
|
||||
<body style="font-family:system-ui;background:#0a0a0f;color:#e8e0d0;padding:40px">
|
||||
<h1>live.transquinnftw.com</h1>
|
||||
<p>VIP live shows: <a href="/shows/live">/shows/live</a></p>
|
||||
<p>Admin (SSO): <a href="/admin">/admin</a></p>
|
||||
</body></html>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>VIP Shows</title>
|
||||
<style>body{font-family:system-ui,sans-serif;background:#0a0a0f;color:#e8e0d0;padding:40px}</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>VIP Shows</h1>
|
||||
<p>Live: <a href="/shows/live">/shows/live</a> (player for active authorized show).</p>
|
||||
<p>List and admin preview at <a href="/admin">/admin</a> (SSO operator only).</p>
|
||||
<p>The live feed is produced via the quinn.cast relay (hotel thin SRT → DO OBS → fanout to platform ingest at live.transquinnftw.com and/or external RTMPs).</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>VIP Show — Live</title>
|
||||
<style>
|
||||
body { margin:0; background:#0a0a0f; color:#e8e0d0; font-family:system-ui,sans-serif; }
|
||||
.player { max-width: 1280px; margin: 40px auto; padding: 0 20px; }
|
||||
video { width:100%; background:#000; border-radius:8px; }
|
||||
.meta { opacity:.7; font-size:.85rem; margin: 12px 0; }
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="player">
|
||||
<h1 style="font-size:1.4rem; margin-bottom:8px;">VIP Show — Live</h1>
|
||||
<div class="meta">Authorized clients only. Stream from the active relay (high-bitrate from DO, thin contribution from performer location).</div>
|
||||
<video id="player" controls playsinline autoplay></video>
|
||||
<div class="meta" id="info">HLS source will be set by the show system or query param (?src=...).</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const video = document.getElementById('player');
|
||||
const info = document.getElementById('info');
|
||||
|
||||
// In real: the show key or token determines the stream path on the ingest.
|
||||
// For demo: support ?src=http://.../index.m3u8 or default to a produced preview if known.
|
||||
const params = new URLSearchParams(location.search);
|
||||
let src = params.get('src') || params.get('hls');
|
||||
|
||||
if (!src) {
|
||||
// Fallback demo note (in real the shows backend injects the correct ingest HLS URL for this show)
|
||||
info.textContent = 'No ?src= provided. In production the /shows/live page for an active show will embed the correct HLS from the platform ingest (fed by the relay fanout to rtmp://live.transquinnftw.com/...).';
|
||||
// You can manually test by appending ?src=http://<cast-ip>:8888/hls/live/produced/index.m3u8
|
||||
}
|
||||
|
||||
function load(srcUrl) {
|
||||
info.textContent = `Loading ${srcUrl} ...`;
|
||||
if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
video.src = srcUrl;
|
||||
} else if (Hls.isSupported()) {
|
||||
const hls = new Hls({ enableWorker: true });
|
||||
hls.loadSource(srcUrl);
|
||||
hls.attachMedia(video);
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||
info.textContent = 'Live (manifest parsed).';
|
||||
video.play().catch(() => {});
|
||||
});
|
||||
} else {
|
||||
info.textContent = 'HLS playback not supported here.';
|
||||
}
|
||||
}
|
||||
|
||||
if (src) {
|
||||
load(src);
|
||||
}
|
||||
|
||||
// Allow manual override in the demo
|
||||
window.setLiveSrc = (u) => load(u);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -27,6 +27,7 @@ services:
|
|||
- "8890:8890/udp" # SRT ingest from hotel (main path)
|
||||
- "1935:1935" # RTMP (for local produced feed + optional ingest)
|
||||
- "8554:8554" # RTSP
|
||||
- "8888:8888" # HLS (for /admin preview and /shows/live player on live.transquinnftw.com)
|
||||
- "8889:8889" # WebRTC (future)
|
||||
- "9997:9997" # API (controller can poll if needed)
|
||||
volumes:
|
||||
|
|
|
|||
|
|
@ -183,4 +183,4 @@ echo " 3. From laptop: ./run deploy:cast --from-local (will install nginx sit
|
|||
echo " 4. After deploy: ssh root@... journalctl -u quinn-cast -f"
|
||||
echo " 5. Open https://cast.transquinnftw.com/?p=<your-passphrase> from anywhere (or via mesh)."
|
||||
echo ""
|
||||
echo "Security: firewall only 22/tcp + 80/tcp + 443/tcp + 8890/udp . Use ufw or doctl."
|
||||
echo "Security: firewall only 22/tcp + 80/tcp + 443/tcp + 8890/udp + 8888/tcp (HLS preview) . Use ufw or doctl."
|
||||
|
|
|
|||
1
run
1
run
|
|
@ -34,6 +34,7 @@
|
|||
# ./run deploy:ai Deploy quinn.ai dashboard to VPS (--direct only)
|
||||
# ./run deploy:ai-worker Deploy ai inference worker to black (--direct only)
|
||||
# ./run deploy:cast Deploy quinn.cast (broadcast relay) to dedicated droplet via provision-stream (--from-local only)
|
||||
# ./run deploy:live Deploy live.transquinnftw.com (VIP shows player + SSO /admin with OBS preview) (--from-local only)
|
||||
# ./run deploy:att Deploy adulttherapytour.com + SEO bait to vps-0 (--from-local only)
|
||||
# ./run deploy:cocotte Deploy cocotte.maison (+ defensive cocottehouse.com via defensive-coms) to vps-0 (--from-local only)
|
||||
# ./run deploy:sansonnet Deploy sansonnet.maison (+ defensive maisonsansonnet.com via defensive-coms) to vps-0 (--from-local only)
|
||||
|
|
|
|||
|
|
@ -216,6 +216,17 @@ case "$COMMAND" in
|
|||
fi
|
||||
;;
|
||||
|
||||
deploy:live)
|
||||
if [[ "$FROM_LOCAL" == "true" ]]; then
|
||||
echo "[direct] Deploying live.transquinnftw.com (VIP shows player + /admin)..."
|
||||
bash "$ROOT_DIR/deployments/@domains/live.transquinnftw.com/deploy.sh" "$@"
|
||||
else
|
||||
echo "ERROR: No CI for live yet. Use --from-local." >&2
|
||||
echo " ./run deploy:live --from-local" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
deploy:api)
|
||||
if [[ "$FROM_LOCAL" == "true" ]]; then
|
||||
echo "[direct] Deploying quinn.api to production..."
|
||||
|
|
@ -294,6 +305,7 @@ case "$COMMAND" in
|
|||
echo " ./run deploy:ai Deploy quinn.ai dashboard to VPS (--from-local only)"
|
||||
echo " ./run deploy:ai-worker Deploy ai inference worker to black (--from-local only)"
|
||||
echo " ./run deploy:cast Deploy quinn.cast (broadcast relay) to dedicated droplet via provision-stream (--from-local only)"
|
||||
echo " ./run deploy:live Deploy live.transquinnftw.com (VIP shows + /admin SSO preview) (--from-local only)"
|
||||
echo " ./run deploy:api Deploy quinn.api data API to VPS (--from-local only)"
|
||||
echo " ./run deploy:att Deploy adulttherapytour.com + SEO bait to vps-0 (--from-local only)"
|
||||
echo " ./run deploy:cocotte Deploy cocotte.maison to vps-0 (--from-local only)"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue