mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2026-04-08 14:56:49 +00:00
feat: Livekit - Selfhost video room solution (#946)
* feat: Livekit bare no recording nor pipeline * feat: full livekit pipeline * fix: caddy hatchet with livekit * fix: caddy livekit * fix: hatchet tls * fix: agg to webm for no padding * fix: reflector user id on participants and duration fix * fix: better docs and internal review fixes * fix: remove video files livekit
This commit is contained in:
committed by
GitHub
parent
b570d202dc
commit
bc8338fa4f
@@ -26,6 +26,12 @@
|
||||
# (If omitted, configure an external OpenAI-compatible LLM in server/.env)
|
||||
#
|
||||
# Optional flags:
|
||||
# --livekit Enable LiveKit self-hosted video platform (generates credentials,
|
||||
# starts livekit-server + livekit-egress containers)
|
||||
# --ip IP Set the server's IP address for all URLs. Implies --caddy
|
||||
# (self-signed HTTPS, required for browser mic/camera access).
|
||||
# Mutually exclusive with --domain. Use for LAN or cloud VM access.
|
||||
# On Linux, IP is auto-detected; on macOS, use --ip to specify it.
|
||||
# --garage Use Garage for local S3-compatible storage
|
||||
# --caddy Enable Caddy reverse proxy with auto-SSL
|
||||
# --domain DOMAIN Use a real domain for Caddy (enables Let's Encrypt auto-HTTPS)
|
||||
@@ -42,10 +48,10 @@
|
||||
# --build Build backend and frontend images from source instead of pulling
|
||||
#
|
||||
# Examples:
|
||||
# ./scripts/setup-selfhosted.sh --gpu --ollama-gpu --garage --caddy
|
||||
# ./scripts/setup-selfhosted.sh --gpu --ollama-gpu --garage --caddy --domain reflector.example.com
|
||||
# ./scripts/setup-selfhosted.sh --cpu --ollama-cpu --garage --caddy
|
||||
# ./scripts/setup-selfhosted.sh --hosted --garage --caddy
|
||||
# ./scripts/setup-selfhosted.sh --gpu --ollama-gpu --livekit --garage --caddy
|
||||
# ./scripts/setup-selfhosted.sh --gpu --ollama-gpu --livekit --garage --caddy --domain reflector.example.com
|
||||
# ./scripts/setup-selfhosted.sh --cpu --ollama-cpu --livekit --garage --caddy
|
||||
# ./scripts/setup-selfhosted.sh --hosted --livekit --garage --caddy
|
||||
# ./scripts/setup-selfhosted.sh --cpu --padding modal --garage --caddy
|
||||
# ./scripts/setup-selfhosted.sh --gpu --translation passthrough --garage --caddy
|
||||
# ./scripts/setup-selfhosted.sh --cpu --diarization modal --translation modal --garage
|
||||
@@ -58,9 +64,11 @@
|
||||
# Config memory: after a successful run, flags are saved to data/.selfhosted-last-args.
|
||||
# Re-running with no arguments replays the saved configuration automatically.
|
||||
#
|
||||
# The script auto-detects Daily.co (DAILY_API_KEY) and Whereby (WHEREBY_API_KEY)
|
||||
# from server/.env. If Daily.co is configured, Hatchet workflow services are
|
||||
# started automatically for multitrack recording processing.
|
||||
# The script auto-detects Daily.co (DAILY_API_KEY), Whereby (WHEREBY_API_KEY),
|
||||
# and LiveKit (LIVEKIT_API_KEY) from server/.env.
|
||||
# - Daily.co: enables Hatchet workflow services for multitrack recording processing.
|
||||
# - LiveKit: enables livekit-server + livekit-egress containers (self-hosted,
|
||||
# generates livekit.yaml and egress.yaml configs automatically).
|
||||
#
|
||||
# Idempotent — safe to re-run at any time.
|
||||
#
|
||||
@@ -207,8 +215,10 @@ fi
|
||||
MODEL_MODE="" # gpu or cpu (required, mutually exclusive)
|
||||
OLLAMA_MODE="" # ollama-gpu or ollama-cpu (optional)
|
||||
USE_GARAGE=false
|
||||
USE_LIVEKIT=false
|
||||
USE_CADDY=false
|
||||
CUSTOM_DOMAIN="" # optional domain for Let's Encrypt HTTPS
|
||||
CUSTOM_IP="" # optional --ip override (mutually exclusive with --caddy)
|
||||
BUILD_IMAGES=false # build backend/frontend from source
|
||||
ADMIN_PASSWORD="" # optional admin password for password auth
|
||||
CUSTOM_CA="" # --custom-ca: path to dir or CA cert file
|
||||
@@ -261,7 +271,16 @@ for i in "${!ARGS[@]}"; do
|
||||
OLLAMA_MODEL="${ARGS[$next_i]}"
|
||||
SKIP_NEXT=true ;;
|
||||
--garage) USE_GARAGE=true ;;
|
||||
--livekit) USE_LIVEKIT=true ;;
|
||||
--caddy) USE_CADDY=true ;;
|
||||
--ip)
|
||||
next_i=$((i + 1))
|
||||
if [[ $next_i -ge ${#ARGS[@]} ]] || [[ "${ARGS[$next_i]}" == --* ]]; then
|
||||
err "--ip requires an IP address (e.g. --ip 192.168.0.100)"
|
||||
exit 1
|
||||
fi
|
||||
CUSTOM_IP="${ARGS[$next_i]}"
|
||||
SKIP_NEXT=true ;;
|
||||
--build) BUILD_IMAGES=true ;;
|
||||
--password)
|
||||
next_i=$((i + 1))
|
||||
@@ -356,6 +375,16 @@ for i in "${!ARGS[@]}"; do
|
||||
esac
|
||||
done
|
||||
|
||||
# --- Validate flag combinations ---
|
||||
if [[ -n "$CUSTOM_IP" ]] && [[ -n "$CUSTOM_DOMAIN" ]]; then
|
||||
err "--ip and --domain are mutually exclusive. Use --ip for IP-based access, or --domain for domain-based access."
|
||||
exit 1
|
||||
fi
|
||||
# --ip implies --caddy (browsers require HTTPS for mic/camera access on non-localhost)
|
||||
if [[ -n "$CUSTOM_IP" ]]; then
|
||||
USE_CADDY=true
|
||||
fi
|
||||
|
||||
# --- Save CLI args for config memory (re-run without flags) ---
|
||||
if [[ $# -gt 0 ]]; then
|
||||
mkdir -p "$ROOT_DIR/data"
|
||||
@@ -505,6 +534,112 @@ if [[ "$HAS_OVERRIDES" == "true" ]]; then
|
||||
MODE_DISPLAY="$MODE_DISPLAY (overrides: transcript=$EFF_TRANSCRIPT, diarization=$EFF_DIARIZATION, translation=$EFF_TRANSLATION, padding=$EFF_PADDING, mixdown=$EFF_MIXDOWN)"
|
||||
fi
|
||||
|
||||
# =========================================================
|
||||
# LiveKit config generation helper
|
||||
# =========================================================
|
||||
_generate_livekit_config() {
|
||||
local lk_key lk_secret lk_url
|
||||
lk_key=$(env_get "$SERVER_ENV" "LIVEKIT_API_KEY" || true)
|
||||
lk_secret=$(env_get "$SERVER_ENV" "LIVEKIT_API_SECRET" || true)
|
||||
lk_url=$(env_get "$SERVER_ENV" "LIVEKIT_URL" || true)
|
||||
|
||||
if [[ -z "$lk_key" ]] || [[ -z "$lk_secret" ]]; then
|
||||
warn "LIVEKIT_API_KEY or LIVEKIT_API_SECRET not set — generating random credentials"
|
||||
lk_key="reflector_$(openssl rand -hex 8)"
|
||||
lk_secret="$(openssl rand -hex 32)"
|
||||
env_set "$SERVER_ENV" "LIVEKIT_API_KEY" "$lk_key"
|
||||
env_set "$SERVER_ENV" "LIVEKIT_API_SECRET" "$lk_secret"
|
||||
env_set "$SERVER_ENV" "LIVEKIT_URL" "ws://livekit-server:7880"
|
||||
ok "Generated LiveKit API credentials"
|
||||
fi
|
||||
|
||||
# Set internal URL for server->livekit communication
|
||||
if ! env_has_key "$SERVER_ENV" "LIVEKIT_URL" || [[ -z "$(env_get "$SERVER_ENV" "LIVEKIT_URL" || true)" ]]; then
|
||||
env_set "$SERVER_ENV" "LIVEKIT_URL" "ws://livekit-server:7880"
|
||||
fi
|
||||
|
||||
# Set public URL based on deployment mode.
|
||||
# When Caddy is enabled (HTTPS), LiveKit WebSocket is proxied through Caddy
|
||||
# at /lk-ws to avoid mixed-content blocking (browsers block ws:// on https:// pages).
|
||||
# When no Caddy, browsers connect directly to LiveKit on port 7880.
|
||||
local public_lk_url
|
||||
if [[ "$USE_CADDY" == "true" ]]; then
|
||||
if [[ -n "$CUSTOM_DOMAIN" ]]; then
|
||||
public_lk_url="wss://${CUSTOM_DOMAIN}/lk-ws"
|
||||
elif [[ -n "$PRIMARY_IP" ]]; then
|
||||
public_lk_url="wss://${PRIMARY_IP}/lk-ws"
|
||||
else
|
||||
public_lk_url="wss://localhost/lk-ws"
|
||||
fi
|
||||
else
|
||||
if [[ -n "$PRIMARY_IP" ]]; then
|
||||
public_lk_url="ws://${PRIMARY_IP}:7880"
|
||||
else
|
||||
public_lk_url="ws://localhost:7880"
|
||||
fi
|
||||
fi
|
||||
env_set "$SERVER_ENV" "LIVEKIT_PUBLIC_URL" "$public_lk_url"
|
||||
env_set "$SERVER_ENV" "DEFAULT_VIDEO_PLATFORM" "livekit"
|
||||
|
||||
# LiveKit storage: always sync from transcript storage config.
|
||||
# Endpoint URL must match (changes between Caddy/no-Caddy runs).
|
||||
local ts_bucket ts_region ts_key ts_secret ts_endpoint
|
||||
ts_bucket=$(env_get "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_BUCKET_NAME" 2>/dev/null || echo "reflector-bucket")
|
||||
ts_region=$(env_get "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_REGION" 2>/dev/null || echo "us-east-1")
|
||||
ts_key=$(env_get "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID" 2>/dev/null || true)
|
||||
ts_secret=$(env_get "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY" 2>/dev/null || true)
|
||||
ts_endpoint=$(env_get "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_ENDPOINT_URL" 2>/dev/null || true)
|
||||
env_set "$SERVER_ENV" "LIVEKIT_STORAGE_AWS_BUCKET_NAME" "$ts_bucket"
|
||||
env_set "$SERVER_ENV" "LIVEKIT_STORAGE_AWS_REGION" "$ts_region"
|
||||
[[ -n "$ts_key" ]] && env_set "$SERVER_ENV" "LIVEKIT_STORAGE_AWS_ACCESS_KEY_ID" "$ts_key"
|
||||
[[ -n "$ts_secret" ]] && env_set "$SERVER_ENV" "LIVEKIT_STORAGE_AWS_SECRET_ACCESS_KEY" "$ts_secret"
|
||||
[[ -n "$ts_endpoint" ]] && env_set "$SERVER_ENV" "LIVEKIT_STORAGE_AWS_ENDPOINT_URL" "$ts_endpoint"
|
||||
if [[ -z "$ts_key" ]] || [[ -z "$ts_secret" ]]; then
|
||||
warn "LiveKit storage: S3 credentials not found — Track Egress recording will fail!"
|
||||
warn "Configure LIVEKIT_STORAGE_AWS_ACCESS_KEY_ID and LIVEKIT_STORAGE_AWS_SECRET_ACCESS_KEY in server/.env"
|
||||
warn "Or run with --garage to auto-configure local S3 storage"
|
||||
else
|
||||
ok "LiveKit storage: synced from transcript storage config"
|
||||
fi
|
||||
|
||||
# Generate livekit.yaml
|
||||
cat > "$ROOT_DIR/livekit.yaml" << LKEOF
|
||||
port: 7880
|
||||
rtc:
|
||||
tcp_port: 7881
|
||||
port_range_start: 44200
|
||||
port_range_end: 44300
|
||||
redis:
|
||||
address: redis:6379
|
||||
keys:
|
||||
${lk_key}: ${lk_secret}
|
||||
webhook:
|
||||
urls:
|
||||
- http://server:1250/v1/livekit/webhook
|
||||
api_key: ${lk_key}
|
||||
logging:
|
||||
level: info
|
||||
room:
|
||||
empty_timeout: 300
|
||||
max_participants: 0
|
||||
LKEOF
|
||||
ok "Generated livekit.yaml"
|
||||
|
||||
# Generate egress.yaml (Track Egress only — no composite video)
|
||||
cat > "$ROOT_DIR/egress.yaml" << EGEOF
|
||||
api_key: ${lk_key}
|
||||
api_secret: ${lk_secret}
|
||||
ws_url: ws://livekit-server:7880
|
||||
redis:
|
||||
address: redis:6379
|
||||
health_port: 7082
|
||||
log_level: info
|
||||
session_limits:
|
||||
file_output_max_duration: 4h
|
||||
EGEOF
|
||||
ok "Generated egress.yaml"
|
||||
}
|
||||
|
||||
# =========================================================
|
||||
# Step 0: Prerequisites
|
||||
# =========================================================
|
||||
@@ -737,13 +872,23 @@ step_server_env() {
|
||||
fi
|
||||
else
|
||||
if [[ -n "$PRIMARY_IP" ]]; then
|
||||
server_base_url="http://$PRIMARY_IP"
|
||||
server_base_url="http://$PRIMARY_IP:1250"
|
||||
else
|
||||
server_base_url="http://localhost:1250"
|
||||
fi
|
||||
fi
|
||||
env_set "$SERVER_ENV" "BASE_URL" "$server_base_url"
|
||||
env_set "$SERVER_ENV" "CORS_ORIGIN" "$server_base_url"
|
||||
# CORS: allow the frontend origin (port 3000, not the API port)
|
||||
local cors_origin="${server_base_url}"
|
||||
if [[ "$USE_CADDY" != "true" ]]; then
|
||||
# Without Caddy, frontend is on port 3000, API on 1250
|
||||
cors_origin="${server_base_url/:1250/:3000}"
|
||||
# Safety: if substitution didn't change anything, construct explicitly
|
||||
if [[ "$cors_origin" == "$server_base_url" ]] && [[ -n "$PRIMARY_IP" ]]; then
|
||||
cors_origin="http://${PRIMARY_IP}:3000"
|
||||
fi
|
||||
fi
|
||||
env_set "$SERVER_ENV" "CORS_ORIGIN" "$cors_origin"
|
||||
|
||||
# WebRTC: advertise host IP in ICE candidates so browsers can reach the server
|
||||
if [[ -n "$PRIMARY_IP" ]]; then
|
||||
@@ -951,8 +1096,21 @@ step_server_env() {
|
||||
# Hatchet is always required (file, live, and multitrack pipelines all use it)
|
||||
env_set "$SERVER_ENV" "HATCHET_CLIENT_SERVER_URL" "http://hatchet:8888"
|
||||
env_set "$SERVER_ENV" "HATCHET_CLIENT_HOST_PORT" "hatchet:7077"
|
||||
env_set "$SERVER_ENV" "HATCHET_CLIENT_TLS_STRATEGY" "none"
|
||||
ok "Hatchet connectivity configured (workflow engine for processing pipelines)"
|
||||
|
||||
# BIND_HOST controls whether server/web ports are exposed on all interfaces
|
||||
local root_env="$ROOT_DIR/.env"
|
||||
touch "$root_env"
|
||||
if [[ "$USE_CADDY" == "true" ]]; then
|
||||
# With Caddy, services stay on localhost (Caddy is the public entry point)
|
||||
env_set "$root_env" "BIND_HOST" "127.0.0.1"
|
||||
elif [[ -n "$PRIMARY_IP" ]]; then
|
||||
# Without Caddy + detected IP, expose on all interfaces for direct access
|
||||
env_set "$root_env" "BIND_HOST" "0.0.0.0"
|
||||
ok "BIND_HOST=0.0.0.0 (ports exposed for direct access)"
|
||||
fi
|
||||
|
||||
ok "server/.env ready"
|
||||
}
|
||||
|
||||
@@ -980,18 +1138,26 @@ step_www_env() {
|
||||
base_url="https://localhost"
|
||||
fi
|
||||
else
|
||||
# No Caddy — user's proxy handles SSL. Use http for now, they'll override.
|
||||
# No Caddy — clients connect directly to services on their ports.
|
||||
if [[ -n "$PRIMARY_IP" ]]; then
|
||||
base_url="http://$PRIMARY_IP"
|
||||
base_url="http://$PRIMARY_IP:3000"
|
||||
else
|
||||
base_url="http://localhost"
|
||||
base_url="http://localhost:3000"
|
||||
fi
|
||||
fi
|
||||
|
||||
# API_URL: with Caddy, same origin (443 proxies both); without Caddy, API is on port 1250
|
||||
local api_url="$base_url"
|
||||
if [[ "$USE_CADDY" != "true" ]]; then
|
||||
api_url="${base_url/:3000/:1250}"
|
||||
# fallback if no port substitution happened (e.g. localhost without port)
|
||||
[[ "$api_url" == "$base_url" ]] && api_url="${base_url}:1250"
|
||||
fi
|
||||
|
||||
env_set "$WWW_ENV" "SITE_URL" "$base_url"
|
||||
env_set "$WWW_ENV" "NEXTAUTH_URL" "$base_url"
|
||||
env_set "$WWW_ENV" "NEXTAUTH_SECRET" "$NEXTAUTH_SECRET"
|
||||
env_set "$WWW_ENV" "API_URL" "$base_url"
|
||||
env_set "$WWW_ENV" "API_URL" "$api_url"
|
||||
env_set "$WWW_ENV" "WEBSOCKET_URL" "auto"
|
||||
env_set "$WWW_ENV" "SERVER_API_URL" "http://server:1250"
|
||||
env_set "$WWW_ENV" "KV_URL" "redis://redis:6379"
|
||||
@@ -1014,14 +1180,17 @@ step_www_env() {
|
||||
fi
|
||||
|
||||
# Enable rooms if any video platform is configured in server/.env
|
||||
local _daily_key="" _whereby_key=""
|
||||
local _daily_key="" _whereby_key="" _livekit_key=""
|
||||
if env_has_key "$SERVER_ENV" "DAILY_API_KEY"; then
|
||||
_daily_key=$(env_get "$SERVER_ENV" "DAILY_API_KEY")
|
||||
fi
|
||||
if env_has_key "$SERVER_ENV" "WHEREBY_API_KEY"; then
|
||||
_whereby_key=$(env_get "$SERVER_ENV" "WHEREBY_API_KEY")
|
||||
fi
|
||||
if [[ -n "$_daily_key" ]] || [[ -n "$_whereby_key" ]]; then
|
||||
if env_has_key "$SERVER_ENV" "LIVEKIT_API_KEY"; then
|
||||
_livekit_key=$(env_get "$SERVER_ENV" "LIVEKIT_API_KEY")
|
||||
fi
|
||||
if [[ -n "$_daily_key" ]] || [[ -n "$_whereby_key" ]] || [[ -n "$_livekit_key" ]]; then
|
||||
env_set "$WWW_ENV" "FEATURE_ROOMS" "true"
|
||||
ok "Rooms feature enabled (video platform configured)"
|
||||
fi
|
||||
@@ -1110,7 +1279,13 @@ step_garage() {
|
||||
|
||||
# Write S3 credentials to server/.env
|
||||
env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_BACKEND" "aws"
|
||||
env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_ENDPOINT_URL" "http://garage:3900"
|
||||
# Endpoint URL: use public IP when no Caddy so presigned URLs work in the browser.
|
||||
# With Caddy, internal hostname is fine (Caddy proxies or browser never sees presigned URLs directly).
|
||||
if [[ "$USE_CADDY" != "true" ]] && [[ -n "$PRIMARY_IP" ]]; then
|
||||
env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_ENDPOINT_URL" "http://${PRIMARY_IP}:3900"
|
||||
else
|
||||
env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_ENDPOINT_URL" "http://garage:3900"
|
||||
fi
|
||||
env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_BUCKET_NAME" "reflector-media"
|
||||
env_set "$SERVER_ENV" "TRANSCRIPT_STORAGE_AWS_REGION" "garage"
|
||||
if [[ "$created_key" == "true" ]]; then
|
||||
@@ -1188,6 +1363,22 @@ step_caddyfile() {
|
||||
rm -rf "$caddyfile"
|
||||
fi
|
||||
|
||||
# LiveKit reverse proxy snippet (inserted into Caddyfile when --livekit is active)
|
||||
# LiveKit reverse proxy snippet (inserted into Caddyfile when --livekit is active).
|
||||
# Strips /lk-ws prefix so LiveKit server sees requests at its root /.
|
||||
local lk_proxy_block=""
|
||||
if [[ "$LIVEKIT_DETECTED" == "true" ]]; then
|
||||
lk_proxy_block="
|
||||
handle_path /lk-ws/* {
|
||||
reverse_proxy livekit-server:7880
|
||||
}
|
||||
handle_path /lk-ws {
|
||||
reverse_proxy livekit-server:7880
|
||||
}"
|
||||
fi
|
||||
|
||||
local hatchet_proxy_block=""
|
||||
|
||||
if [[ -n "$TLS_CERT_PATH" ]] && [[ -n "$CUSTOM_DOMAIN" ]]; then
|
||||
# Custom domain with user-provided TLS certificate (from --custom-ca directory)
|
||||
cat > "$caddyfile" << CADDYEOF
|
||||
@@ -1199,7 +1390,7 @@ $CUSTOM_DOMAIN {
|
||||
}
|
||||
handle /health {
|
||||
reverse_proxy server:1250
|
||||
}
|
||||
}${lk_proxy_block}${hatchet_proxy_block}
|
||||
handle {
|
||||
reverse_proxy web:3000
|
||||
}
|
||||
@@ -1216,7 +1407,7 @@ $CUSTOM_DOMAIN {
|
||||
}
|
||||
handle /health {
|
||||
reverse_proxy server:1250
|
||||
}
|
||||
}${lk_proxy_block}${hatchet_proxy_block}
|
||||
handle {
|
||||
reverse_proxy web:3000
|
||||
}
|
||||
@@ -1225,17 +1416,19 @@ CADDYEOF
|
||||
ok "Created Caddyfile for $CUSTOM_DOMAIN (Let's Encrypt auto-HTTPS)"
|
||||
elif [[ -n "$PRIMARY_IP" ]]; then
|
||||
# No domain, IP only: catch-all :443 with self-signed cert
|
||||
# (IP connections don't send SNI, so we can't match by address)
|
||||
# on_demand generates certs dynamically for any hostname/IP on first request
|
||||
cat > "$caddyfile" << CADDYEOF
|
||||
# Generated by setup-selfhosted.sh — self-signed cert for IP access
|
||||
:443 {
|
||||
tls internal
|
||||
tls internal {
|
||||
on_demand
|
||||
}
|
||||
handle /v1/* {
|
||||
reverse_proxy server:1250
|
||||
}
|
||||
handle /health {
|
||||
reverse_proxy server:1250
|
||||
}
|
||||
}${lk_proxy_block}${hatchet_proxy_block}
|
||||
handle {
|
||||
reverse_proxy web:3000
|
||||
}
|
||||
@@ -1249,21 +1442,8 @@ CADDYEOF
|
||||
ok "Caddyfile already exists"
|
||||
fi
|
||||
|
||||
# Add Hatchet dashboard route if Daily.co is detected
|
||||
if [[ "$DAILY_DETECTED" == "true" ]]; then
|
||||
if ! grep -q "hatchet" "$caddyfile" 2>/dev/null; then
|
||||
cat >> "$caddyfile" << CADDYEOF
|
||||
|
||||
# Hatchet workflow dashboard (Daily.co multitrack processing)
|
||||
:8888 {
|
||||
tls internal
|
||||
reverse_proxy hatchet:8888
|
||||
}
|
||||
CADDYEOF
|
||||
ok "Added Hatchet dashboard route to Caddyfile (port 8888)"
|
||||
else
|
||||
ok "Hatchet dashboard route already in Caddyfile"
|
||||
fi
|
||||
if [[ "$DAILY_DETECTED" == "true" ]] || [[ "$LIVEKIT_DETECTED" == "true" ]]; then
|
||||
ok "Hatchet dashboard available at port 8888"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1467,7 +1647,7 @@ step_health() {
|
||||
info "Waiting for Hatchet workflow engine..."
|
||||
local hatchet_ok=false
|
||||
for i in $(seq 1 60); do
|
||||
if curl -sf http://localhost:8888/api/live > /dev/null 2>&1; then
|
||||
if compose_cmd exec -T hatchet curl -sf http://localhost:8888/api/live > /dev/null 2>&1; then
|
||||
hatchet_ok=true
|
||||
break
|
||||
fi
|
||||
@@ -1515,7 +1695,7 @@ step_hatchet_token() {
|
||||
# Wait for hatchet to be healthy
|
||||
local hatchet_ok=false
|
||||
for i in $(seq 1 60); do
|
||||
if curl -sf http://localhost:8888/api/live > /dev/null 2>&1; then
|
||||
if compose_cmd exec -T hatchet curl -sf http://localhost:8888/api/live > /dev/null 2>&1; then
|
||||
hatchet_ok=true
|
||||
break
|
||||
fi
|
||||
@@ -1586,12 +1766,19 @@ main() {
|
||||
[[ "$BUILD_IMAGES" == "true" ]] && echo " Build: from source"
|
||||
echo ""
|
||||
|
||||
# Detect primary IP
|
||||
PRIMARY_IP=""
|
||||
if [[ "$OS" == "Linux" ]]; then
|
||||
PRIMARY_IP=$(hostname -I 2>/dev/null | awk '{print $1}' || true)
|
||||
if [[ "$PRIMARY_IP" == "127."* ]] || [[ -z "$PRIMARY_IP" ]]; then
|
||||
PRIMARY_IP=$(ip -4 route get 1 2>/dev/null | sed -n 's/.*src \([0-9.]*\).*/\1/p' || true)
|
||||
# Detect primary IP (--ip overrides auto-detection)
|
||||
if [[ -n "$CUSTOM_IP" ]]; then
|
||||
PRIMARY_IP="$CUSTOM_IP"
|
||||
ok "Using provided IP: $PRIMARY_IP"
|
||||
else
|
||||
PRIMARY_IP=""
|
||||
if [[ "$OS" == "Linux" ]]; then
|
||||
PRIMARY_IP=$(hostname -I 2>/dev/null | awk '{print $1}' || true)
|
||||
if [[ "$PRIMARY_IP" == "127."* ]] || [[ -z "$PRIMARY_IP" ]]; then
|
||||
PRIMARY_IP=$(ip -4 route get 1 2>/dev/null | sed -n 's/.*src \([0-9.]*\).*/\1/p' || true)
|
||||
fi
|
||||
elif [[ "$OS" == "Darwin" ]]; then
|
||||
PRIMARY_IP=$(detect_lan_ip)
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1621,14 +1808,21 @@ main() {
|
||||
# Auto-detect video platforms from server/.env (after step_server_env so file exists)
|
||||
DAILY_DETECTED=false
|
||||
WHEREBY_DETECTED=false
|
||||
LIVEKIT_DETECTED=false
|
||||
if env_has_key "$SERVER_ENV" "DAILY_API_KEY" && [[ -n "$(env_get "$SERVER_ENV" "DAILY_API_KEY")" ]]; then
|
||||
DAILY_DETECTED=true
|
||||
fi
|
||||
if env_has_key "$SERVER_ENV" "WHEREBY_API_KEY" && [[ -n "$(env_get "$SERVER_ENV" "WHEREBY_API_KEY")" ]]; then
|
||||
WHEREBY_DETECTED=true
|
||||
fi
|
||||
# LiveKit: enabled via --livekit flag OR pre-existing LIVEKIT_API_KEY in env
|
||||
if [[ "$USE_LIVEKIT" == "true" ]]; then
|
||||
LIVEKIT_DETECTED=true
|
||||
elif env_has_key "$SERVER_ENV" "LIVEKIT_API_KEY" && [[ -n "$(env_get "$SERVER_ENV" "LIVEKIT_API_KEY")" ]]; then
|
||||
LIVEKIT_DETECTED=true
|
||||
fi
|
||||
ANY_PLATFORM_DETECTED=false
|
||||
[[ "$DAILY_DETECTED" == "true" || "$WHEREBY_DETECTED" == "true" ]] && ANY_PLATFORM_DETECTED=true
|
||||
[[ "$DAILY_DETECTED" == "true" || "$WHEREBY_DETECTED" == "true" || "$LIVEKIT_DETECTED" == "true" ]] && ANY_PLATFORM_DETECTED=true
|
||||
|
||||
# Conditional profile activation for Daily.co
|
||||
if [[ "$DAILY_DETECTED" == "true" ]]; then
|
||||
@@ -1636,6 +1830,13 @@ main() {
|
||||
ok "Daily.co detected — enabling Hatchet workflow services"
|
||||
fi
|
||||
|
||||
# Conditional profile activation for LiveKit
|
||||
if [[ "$LIVEKIT_DETECTED" == "true" ]]; then
|
||||
COMPOSE_PROFILES+=("livekit")
|
||||
_generate_livekit_config
|
||||
ok "LiveKit enabled — livekit-server + livekit-egress"
|
||||
fi
|
||||
|
||||
# Generate .env.hatchet for hatchet dashboard config (always needed)
|
||||
local hatchet_server_url hatchet_cookie_domain
|
||||
if [[ -n "$CUSTOM_DOMAIN" ]]; then
|
||||
@@ -1683,10 +1884,12 @@ EOF
|
||||
echo " App: https://localhost (accept self-signed cert in browser)"
|
||||
echo " API: https://localhost/v1/"
|
||||
fi
|
||||
elif [[ -n "$PRIMARY_IP" ]]; then
|
||||
echo " App: http://$PRIMARY_IP:3000"
|
||||
echo " API: http://$PRIMARY_IP:1250"
|
||||
else
|
||||
echo " No Caddy — point your reverse proxy at:"
|
||||
echo " Frontend: web:3000 (or localhost:3000 from host)"
|
||||
echo " API: server:1250 (or localhost:1250 from host)"
|
||||
echo " App: http://localhost:3000"
|
||||
echo " API: http://localhost:1250"
|
||||
fi
|
||||
echo ""
|
||||
if [[ "$HAS_OVERRIDES" == "true" ]]; then
|
||||
@@ -1702,6 +1905,7 @@ EOF
|
||||
[[ "$USES_OLLAMA" != "true" ]] && echo " LLM: External (configure in server/.env)"
|
||||
[[ "$DAILY_DETECTED" == "true" ]] && echo " Video: Daily.co (live rooms + multitrack processing via Hatchet)"
|
||||
[[ "$WHEREBY_DETECTED" == "true" ]] && echo " Video: Whereby (live rooms)"
|
||||
[[ "$LIVEKIT_DETECTED" == "true" ]] && echo " Video: LiveKit (self-hosted, live rooms + track egress)"
|
||||
[[ "$ANY_PLATFORM_DETECTED" != "true" ]] && echo " Video: None (rooms disabled)"
|
||||
if [[ "$USE_CUSTOM_CA" == "true" ]]; then
|
||||
echo " CA: Custom (certs/ca.crt)"
|
||||
|
||||
Reference in New Issue
Block a user