Aggiunge modulo Replay, Garage dev e YouTube a livello società.
Pipeline registrazione/upload con storage S3, archivio web e app, proxy replay per il browser, OAuth YouTube sulla pagina club (Premium Full) e footer legale. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -26,3 +26,24 @@ STRIPE_PREMIUM_FULL_YEARLY_PRICE_ID=
|
||||
# Legacy (equivale a YEARLY se le righe sopra sono vuote)
|
||||
STRIPE_PREMIUM_LIGHT_PRICE_ID=
|
||||
STRIPE_PREMIUM_FULL_PRICE_ID=
|
||||
|
||||
# --- Replay storage (dopo ogni diretta Premium) ---
|
||||
# Dev locale con Docker: Garage parte con `docker compose up` (servizio garage).
|
||||
# Bootstrap una tantum: bash infra/scripts/setup_garage_replays.sh → aggiorna .env con le chiavi.
|
||||
# MODALITÀ A — solo filesystem: lascia REPLAY_STORAGE_ENDPOINT vuoto (volume /recordings/replays/).
|
||||
# MODALITÀ B — Garage/S3: compila tutte le righe sotto (endpoint rete Docker: http://garage:3900).
|
||||
REPLAY_STORAGE_ENDPOINT=http://garage:3900
|
||||
REPLAY_STORAGE_BUCKET=matchlivetv-replays
|
||||
REPLAY_STORAGE_REGION=garage
|
||||
# Credenziali S3: NON le inventi a mano. Le genera Garage con lo script di bootstrap:
|
||||
# cd infra && bash scripts/setup_garage_replays.sh
|
||||
# Lo script crea la chiave "replay-uploader" e scrive Key ID + Secret in infra/.env (non in questo file).
|
||||
# ACCESS_KEY_ID = "Key ID" mostrato da Garage (es. GK6619e25d53883a7bc3f75755)
|
||||
# SECRET_ACCESS_KEY = "Secret key" mostrata UNA SOLA VOLTA alla creazione della chiave
|
||||
REPLAY_STORAGE_ACCESS_KEY_ID=
|
||||
REPLAY_STORAGE_SECRET_ACCESS_KEY=
|
||||
# true = obbligatorio per Garage/MinIO (path-style). Non cambiare se usi Garage.
|
||||
REPLAY_STORAGE_FORCE_PATH_STYLE=true
|
||||
# Opzionale: durata link firmati per play/download (secondi)
|
||||
# REPLAY_PRESIGNED_URL_TTL_SECONDS=7200
|
||||
# REPLAY_DOWNLOAD_URL_TTL_SECONDS=900
|
||||
|
||||
@@ -94,6 +94,13 @@ services:
|
||||
STRIPE_PREMIUM_FULL_YEARLY_PRICE_ID: ${STRIPE_PREMIUM_FULL_YEARLY_PRICE_ID:-${STRIPE_PREMIUM_FULL_PRICE_ID:-}}
|
||||
STRIPE_PREMIUM_LIGHT_PRICE_ID: ${STRIPE_PREMIUM_LIGHT_PRICE_ID:-}
|
||||
STRIPE_PREMIUM_FULL_PRICE_ID: ${STRIPE_PREMIUM_FULL_PRICE_ID:-}
|
||||
RECORDINGS_PATH: /recordings
|
||||
REPLAY_STORAGE_ENDPOINT: ${REPLAY_STORAGE_ENDPOINT:-}
|
||||
REPLAY_STORAGE_BUCKET: ${REPLAY_STORAGE_BUCKET:-matchlivetv-replays}
|
||||
REPLAY_STORAGE_REGION: ${REPLAY_STORAGE_REGION:-garage}
|
||||
REPLAY_STORAGE_ACCESS_KEY_ID: ${REPLAY_STORAGE_ACCESS_KEY_ID:-}
|
||||
REPLAY_STORAGE_SECRET_ACCESS_KEY: ${REPLAY_STORAGE_SECRET_ACCESS_KEY:-}
|
||||
REPLAY_STORAGE_FORCE_PATH_STYLE: ${REPLAY_STORAGE_FORCE_PATH_STYLE:-true}
|
||||
ports:
|
||||
- "3000:3000" # Solo LAN — NPM proxy HTTPS
|
||||
depends_on:
|
||||
@@ -133,9 +140,18 @@ services:
|
||||
YOUTUBE_PLATFORM_REFRESH_TOKEN: ${YOUTUBE_PLATFORM_REFRESH_TOKEN:-}
|
||||
HLS_PUBLIC_URL: ${HLS_PUBLIC_URL:-http://localhost:8888}
|
||||
APP_PUBLIC_URL: ${APP_PUBLIC_URL:-http://localhost:3000}
|
||||
RECORDINGS_PATH: /recordings
|
||||
REPLAY_STORAGE_ENDPOINT: ${REPLAY_STORAGE_ENDPOINT:-}
|
||||
REPLAY_STORAGE_BUCKET: ${REPLAY_STORAGE_BUCKET:-matchlivetv-replays}
|
||||
REPLAY_STORAGE_REGION: ${REPLAY_STORAGE_REGION:-garage}
|
||||
REPLAY_STORAGE_ACCESS_KEY_ID: ${REPLAY_STORAGE_ACCESS_KEY_ID:-}
|
||||
REPLAY_STORAGE_SECRET_ACCESS_KEY: ${REPLAY_STORAGE_SECRET_ACCESS_KEY:-}
|
||||
REPLAY_STORAGE_FORCE_PATH_STYLE: ${REPLAY_STORAGE_FORCE_PATH_STYLE:-true}
|
||||
depends_on:
|
||||
rails:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- recordings:/recordings
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
||||
@@ -81,6 +81,13 @@ services:
|
||||
STRIPE_PREMIUM_FULL_YEARLY_PRICE_ID: ${STRIPE_PREMIUM_FULL_YEARLY_PRICE_ID:-${STRIPE_PREMIUM_FULL_PRICE_ID:-}}
|
||||
STRIPE_PREMIUM_LIGHT_PRICE_ID: ${STRIPE_PREMIUM_LIGHT_PRICE_ID:-}
|
||||
STRIPE_PREMIUM_FULL_PRICE_ID: ${STRIPE_PREMIUM_FULL_PRICE_ID:-}
|
||||
RECORDINGS_PATH: /recordings
|
||||
REPLAY_STORAGE_ENDPOINT: ${REPLAY_STORAGE_ENDPOINT:-http://garage:3900}
|
||||
REPLAY_STORAGE_BUCKET: ${REPLAY_STORAGE_BUCKET:-matchlivetv-replays}
|
||||
REPLAY_STORAGE_REGION: ${REPLAY_STORAGE_REGION:-garage}
|
||||
REPLAY_STORAGE_ACCESS_KEY_ID: ${REPLAY_STORAGE_ACCESS_KEY_ID:-}
|
||||
REPLAY_STORAGE_SECRET_ACCESS_KEY: ${REPLAY_STORAGE_SECRET_ACCESS_KEY:-}
|
||||
REPLAY_STORAGE_FORCE_PATH_STYLE: ${REPLAY_STORAGE_FORCE_PATH_STYLE:-true}
|
||||
ports:
|
||||
- "3000:3000"
|
||||
depends_on:
|
||||
@@ -90,6 +97,8 @@ services:
|
||||
condition: service_healthy
|
||||
mediamtx:
|
||||
condition: service_started
|
||||
garage:
|
||||
condition: service_started
|
||||
volumes:
|
||||
- ../backend:/app
|
||||
- recordings:/recordings
|
||||
@@ -119,13 +128,33 @@ services:
|
||||
JWT_SECRET: ${JWT_SECRET:-matchlivetv_jwt_dev_secret}
|
||||
YOUTUBE_CLIENT_ID: ${YOUTUBE_CLIENT_ID:-}
|
||||
YOUTUBE_CLIENT_SECRET: ${YOUTUBE_CLIENT_SECRET:-}
|
||||
RECORDINGS_PATH: /recordings
|
||||
REPLAY_STORAGE_ENDPOINT: ${REPLAY_STORAGE_ENDPOINT:-http://garage:3900}
|
||||
REPLAY_STORAGE_BUCKET: ${REPLAY_STORAGE_BUCKET:-matchlivetv-replays}
|
||||
REPLAY_STORAGE_REGION: ${REPLAY_STORAGE_REGION:-garage}
|
||||
REPLAY_STORAGE_ACCESS_KEY_ID: ${REPLAY_STORAGE_ACCESS_KEY_ID:-}
|
||||
REPLAY_STORAGE_SECRET_ACCESS_KEY: ${REPLAY_STORAGE_SECRET_ACCESS_KEY:-}
|
||||
REPLAY_STORAGE_FORCE_PATH_STYLE: ${REPLAY_STORAGE_FORCE_PATH_STYLE:-true}
|
||||
depends_on:
|
||||
rails:
|
||||
condition: service_healthy
|
||||
garage:
|
||||
condition: service_started
|
||||
volumes:
|
||||
- ../backend:/app
|
||||
- recordings:/recordings
|
||||
- bundle_cache:/usr/local/bundle
|
||||
|
||||
garage:
|
||||
image: dxflrs/garage:v1.0.1
|
||||
ports:
|
||||
- "3900:3900"
|
||||
volumes:
|
||||
- garage_meta:/var/lib/garage/meta
|
||||
- garage_data:/var/lib/garage/data
|
||||
- ./garage/garage.toml:/etc/garage.toml:ro
|
||||
command: ["/garage", "-c", "/etc/garage.toml", "server"]
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
@@ -140,3 +169,5 @@ volumes:
|
||||
redis_data:
|
||||
recordings:
|
||||
bundle_cache:
|
||||
garage_meta:
|
||||
garage_data:
|
||||
|
||||
26
infra/garage/garage.toml
Normal file
26
infra/garage/garage.toml
Normal file
@@ -0,0 +1,26 @@
|
||||
# Garage single-node dev config for Match Live TV replay storage
|
||||
# Docs: https://garagehq.deuxfleurs.fr/documentation/quick-start/
|
||||
|
||||
metadata_dir = "/var/lib/garage/meta"
|
||||
data_dir = "/var/lib/garage/data"
|
||||
db_engine = "sqlite"
|
||||
|
||||
replication_factor = 1
|
||||
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_public_addr = "garage:3901"
|
||||
# 32 byte hex (64 caratteri) — solo dev locale
|
||||
rpc_secret = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
|
||||
[s3_api]
|
||||
api_bind_addr = "[::]:3900"
|
||||
s3_region = "garage"
|
||||
root_domain = ".s3.garage.localhost"
|
||||
|
||||
[s3_web]
|
||||
bind_addr = "[::]:3902"
|
||||
root_domain = ".web.garage.localhost"
|
||||
|
||||
[admin]
|
||||
api_bind_addr = "[::]:3903"
|
||||
admin_token = "dev-garage-admin-token-change-in-production"
|
||||
143
infra/scripts/setup_garage_replays.sh
Executable file
143
infra/scripts/setup_garage_replays.sh
Executable file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env bash
|
||||
# Bootstrap Garage (S3) per replay Match Live TV — locale o produzione
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
GARAGE_CONTAINER="${GARAGE_CONTAINER:-infra-garage-1}"
|
||||
BUCKET="${REPLAY_STORAGE_BUCKET:-matchlivetv-replays}"
|
||||
KEY_NAME="${REPLAY_STORAGE_KEY_NAME:-replay-uploader}"
|
||||
CREDS_FILE="${ROOT}/garage/dev-credentials.env"
|
||||
ENV_FILE="${ROOT}/.env"
|
||||
|
||||
gexec() {
|
||||
docker exec "$GARAGE_CONTAINER" /garage "$@"
|
||||
}
|
||||
|
||||
wait_garage() {
|
||||
echo "Attendo Garage..."
|
||||
for _ in $(seq 1 30); do
|
||||
if docker exec "$GARAGE_CONTAINER" /garage status >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "Garage non risponde. Avvia: cd $ROOT && docker compose up -d garage"
|
||||
exit 1
|
||||
}
|
||||
|
||||
ensure_layout() {
|
||||
local node_id
|
||||
node_id="$(gexec status 2>/dev/null | awk '/^[0-9a-f]{16,}/{print $1; exit}')"
|
||||
if [ -z "$node_id" ]; then
|
||||
echo "Impossibile leggere node id da garage status"
|
||||
gexec status || true
|
||||
exit 1
|
||||
fi
|
||||
echo "Node id: $node_id"
|
||||
gexec layout assign -z dc1 -c 1G "$node_id" 2>/dev/null || true
|
||||
gexec layout apply --version 1 2>/dev/null || gexec layout apply 2>/dev/null || true
|
||||
}
|
||||
|
||||
ensure_key() {
|
||||
if gexec key info "$KEY_NAME" >/dev/null 2>&1; then
|
||||
echo "Chiave $KEY_NAME già presente"
|
||||
return 0
|
||||
fi
|
||||
echo "Creazione chiave $KEY_NAME..."
|
||||
local out
|
||||
out="$(gexec key create "$KEY_NAME" 2>&1)"
|
||||
echo "$out"
|
||||
KEY_SECRET="$(echo "$out" | awk -F': ' '/Secret key/{print $2; exit}' | tr -d ' ')"
|
||||
if [ -z "$KEY_SECRET" ]; then
|
||||
KEY_SECRET="$(echo "$out" | grep -i secret | awk '{print $NF}' | head -1)"
|
||||
fi
|
||||
export KEY_SECRET
|
||||
}
|
||||
|
||||
ensure_bucket() {
|
||||
echo "Bucket $BUCKET..."
|
||||
gexec bucket create "$BUCKET" 2>/dev/null || true
|
||||
gexec bucket allow \
|
||||
--read --write \
|
||||
--owner \
|
||||
"$BUCKET" \
|
||||
--key "$KEY_NAME" 2>/dev/null || \
|
||||
gexec bucket allow --read --write "$BUCKET" --key "$KEY_NAME" 2>/dev/null || true
|
||||
}
|
||||
|
||||
write_credentials() {
|
||||
local key_id
|
||||
key_id="$(gexec key info "$KEY_NAME" 2>/dev/null | awk -F': ' '/Key ID|key id/{print $2; exit}' | tr -d ' ')"
|
||||
if [ -z "$key_id" ]; then
|
||||
key_id="$(gexec key info "$KEY_NAME" 2>/dev/null | grep -i 'key' | head -1 | awk '{print $NF}')"
|
||||
fi
|
||||
|
||||
mkdir -p "${ROOT}/garage"
|
||||
cat > "$CREDS_FILE" <<EOF
|
||||
# Generato da setup_garage_replays.sh — non committare
|
||||
REPLAY_STORAGE_ENDPOINT=http://garage:3900
|
||||
REPLAY_STORAGE_BUCKET=${BUCKET}
|
||||
REPLAY_STORAGE_REGION=garage
|
||||
REPLAY_STORAGE_ACCESS_KEY_ID=${key_id}
|
||||
REPLAY_STORAGE_SECRET_ACCESS_KEY=${KEY_SECRET:-}
|
||||
REPLAY_STORAGE_FORCE_PATH_STYLE=true
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "=== Credenziali Garage (replay) ==="
|
||||
gexec key info "$KEY_NAME" || true
|
||||
echo "File: $CREDS_FILE"
|
||||
}
|
||||
|
||||
merge_into_env() {
|
||||
local key_id secret
|
||||
key_id="$(grep '^REPLAY_STORAGE_ACCESS_KEY_ID=' "$CREDS_FILE" | cut -d= -f2-)"
|
||||
secret="$(grep '^REPLAY_STORAGE_SECRET_ACCESS_KEY=' "$CREDS_FILE" | cut -d= -f2-)"
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
cp "${ROOT}/.env.example" "$ENV_FILE" 2>/dev/null || touch "$ENV_FILE"
|
||||
fi
|
||||
if grep -q '^REPLAY_STORAGE_ENDPOINT=' "$ENV_FILE" 2>/dev/null; then
|
||||
sed -i 's|^REPLAY_STORAGE_ENDPOINT=.*|REPLAY_STORAGE_ENDPOINT=http://garage:3900|' "$ENV_FILE"
|
||||
sed -i "s|^REPLAY_STORAGE_BUCKET=.*|REPLAY_STORAGE_BUCKET=${BUCKET}|" "$ENV_FILE"
|
||||
sed -i 's|^REPLAY_STORAGE_REGION=.*|REPLAY_STORAGE_REGION=garage|' "$ENV_FILE"
|
||||
sed -i "s|^REPLAY_STORAGE_ACCESS_KEY_ID=.*|REPLAY_STORAGE_ACCESS_KEY_ID=${key_id}|" "$ENV_FILE"
|
||||
sed -i 's|^REPLAY_STORAGE_FORCE_PATH_STYLE=.*|REPLAY_STORAGE_FORCE_PATH_STYLE=true|' "$ENV_FILE"
|
||||
if [ -n "$secret" ]; then
|
||||
if grep -q '^REPLAY_STORAGE_SECRET_ACCESS_KEY=' "$ENV_FILE" 2>/dev/null; then
|
||||
sed -i "s|^REPLAY_STORAGE_SECRET_ACCESS_KEY=.*|REPLAY_STORAGE_SECRET_ACCESS_KEY=${secret}|" "$ENV_FILE"
|
||||
else
|
||||
echo "REPLAY_STORAGE_SECRET_ACCESS_KEY=${secret}" >> "$ENV_FILE"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
cat >> "$ENV_FILE" <<EOF
|
||||
|
||||
# Replay storage — Garage (generato da setup_garage_replays.sh)
|
||||
REPLAY_STORAGE_ENDPOINT=http://garage:3900
|
||||
REPLAY_STORAGE_BUCKET=${BUCKET}
|
||||
REPLAY_STORAGE_REGION=garage
|
||||
REPLAY_STORAGE_ACCESS_KEY_ID=${key_id}
|
||||
REPLAY_STORAGE_SECRET_ACCESS_KEY=${secret}
|
||||
REPLAY_STORAGE_FORCE_PATH_STYLE=true
|
||||
EOF
|
||||
fi
|
||||
echo "Aggiornato $ENV_FILE (Garage replay storage)."
|
||||
}
|
||||
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
if ! docker ps --format '{{.Names}}' | grep -q "^${GARAGE_CONTAINER}$"; then
|
||||
echo "Avvio container garage..."
|
||||
docker compose up -d garage
|
||||
fi
|
||||
wait_garage
|
||||
ensure_layout
|
||||
ensure_key
|
||||
ensure_bucket
|
||||
write_credentials
|
||||
merge_into_env
|
||||
echo ""
|
||||
echo "Riavvia app: docker compose up -d rails sidekiq"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user