social-reporting-tool/v2/deploy/setup-v2.sh
DJP b89e8b511e Add V2: multi-team social-reporting platform with manifest-gated linking
V2 lives entirely under v2/ and is built around three asks the team raised
about V1: per-video assets sometimes drifted onto the wrong trend, hashtag
scrapes returned junk that wasn't filterable per-client, and there was no
multi-user model behind Microsoft SSO.

Highlights:
- Stable TikTok numeric-id key for every per-video asset; URL form drift is
  logged loudly to drift_log.jsonl and never silently nulls assets. Stage 5
  manifest hard-gates Stage 6 if any selected video is missing any required
  asset; --drop-failing auto-backfills from the next-best recipe candidates.
- Per-brief engagement floor (min_likes / min_plays / min_stl_pct), applied
  at Apify scrape time and re-validated locally; spend_log.json records
  raw_returned vs kept_after_floor per scrape.
- Users + teams + memberships with owner/admin/editor/viewer roles; SSO
  upserts a user keyed on Azure oid, auto-creates a personal team, and a
  super-admin is bootstrapped via BOOTSTRAP_SUPER_ADMIN_EMAIL on first
  sign-in. Phase A integration test: 16/16 pass.
- 10-stage TS pipeline (brief → seed → scrape1 → select → scrape2 →
  validate → analyse → insights → trends → qa → build) wired through one
  CLI; each stage idempotent + resumable from disk via .state sentinels.
  §4.5 rubrics shipped under prompts/ and loaded into Claude calls.
- React 18 + Vite + TS + Tailwind operator SPA: brief intake form,
  team management, super-admin user list, help/FAQ ported from V1.
- Separate Docker Compose project (name: social-reporting-v2, port 3457,
  Postgres 5437) with deploy/setup-v2.sh, deploy-v2.sh, rollback-to-v1.sh
  scripts that take over V1's /social-reports URL and let us roll back.

Verification: 62 unit tests pass (auth/session, ids extractor with full URL
fixture, engagement floor, recipes, manifest, linking-fix, MoM compare).
Live smoke run on a Dove brief: 1400 raw → 253 kept (82% culled) → 21
fully-bundled videos → 25 editorial trends across 8 brief-driven categories,
with drift=0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 17:39:07 -04:00

91 lines
4 KiB
Bash
Executable file

#!/bin/bash
set -euo pipefail
# ═══════════════════════════════════════════════════════
# Social Reporting V2 — Server Setup (one-time)
# Target: optical-dev.oliver.solutions
# Cuts over from V1 at the same URL. V1 source kept on disk for rollback.
# ═══════════════════════════════════════════════════════
REPO_URL="${REPO_URL:-}"
BACKEND_DIR_V2="/opt/social-reporting-v2"
BACKEND_DIR_V1="/opt/social-reporting"
APACHE_CONF="/etc/apache2/conf-available/social-reports.conf"
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; NC='\033[0m'
log() { echo -e "${GREEN}[+]${NC} $1"; }
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
err() { echo -e "${RED}[x]${NC} $1"; exit 1; }
[[ -z "$REPO_URL" ]] && err "REPO_URL not set"
command -v docker >/dev/null || err "Docker not installed"
command -v apache2ctl >/dev/null || err "Apache not installed"
# ─── Clone or pull V2 source ───
if [[ -d "$BACKEND_DIR_V2/.git" ]]; then
log "Updating V2 repo at $BACKEND_DIR_V2..."
cd "$BACKEND_DIR_V2" && git remote set-url origin "$REPO_URL" && git pull origin main
else
log "Cloning V2 repo to $BACKEND_DIR_V2..."
sudo mkdir -p "$BACKEND_DIR_V2"
sudo chown "$(whoami):$(whoami)" "$BACKEND_DIR_V2"
git clone "$REPO_URL" "$BACKEND_DIR_V2"
fi
cd "$BACKEND_DIR_V2"
# ─── .env template ───
if [[ ! -f "$BACKEND_DIR_V2/v2/.env" ]]; then
warn "v2/.env not found — copying template"
cp v2/.env.example v2/.env
SS=$(openssl rand -hex 32)
sed -i "s/^SESSION_SECRET=$/SESSION_SECRET=${SS}/" v2/.env
warn "Edit $BACKEND_DIR_V2/v2/.env: APIFY_TOKEN, ANTHROPIC_API_KEY, AZURE_*, BOOTSTRAP_SUPER_ADMIN_EMAIL"
fi
# ─── Cutover from V1 (graceful) ───
warn "About to cut over the /social-reports URL from V1 → V2."
warn "V1 source remains at $BACKEND_DIR_V1 (untouched). Rollback: deploy/rollback-to-v1.sh"
read -r -p "Proceed? [y/N] " ans
[[ "$ans" != "y" && "$ans" != "Y" ]] && err "Aborted"
if [[ -d "$BACKEND_DIR_V1" ]]; then
log "Stopping V1 stack..."
cd "$BACKEND_DIR_V1"
docker compose -p social-listening down || warn "V1 was not running"
cd "$BACKEND_DIR_V2"
fi
# ─── Apache: swap conf to V2 ───
log "Backing up old Apache conf and installing V2..."
[[ -f "$APACHE_CONF" ]] && sudo cp "$APACHE_CONF" "${APACHE_CONF}.v1.bak.$(date +%s)"
sudo cp "$BACKEND_DIR_V2/v2/deploy/apache-social-reports-v2.conf" "$APACHE_CONF"
for mod in proxy proxy_http headers rewrite; do
apache2ctl -M 2>/dev/null | grep -q "${mod}_module" || sudo a2enmod "$mod"
done
sudo a2enconf social-reports >/dev/null 2>&1 || true
sudo apache2ctl configtest || err "Apache config test failed"
# ─── Build & start V2 ───
log "Building & starting V2 stack..."
cd "$BACKEND_DIR_V2"
docker compose -f v2/docker-compose.v2.yml -f v2/docker-compose.v2.prod.yml up -d --build
log "Waiting for V2 backend (port 3457)..."
for i in {1..30}; do
curl -sf http://127.0.0.1:3457/api/health >/dev/null 2>&1 && { log "V2 healthy"; break; }
[ "$i" -eq 30 ] && err "V2 not responding — check: docker compose -p social-reporting-v2 logs app-v2"
sleep 2
done
log "Reloading Apache..."
sudo systemctl reload apache2
echo ""
echo "════════════════════════════════════════════════════"
echo -e " ${GREEN}V2 deployed!${NC}"
echo " URL: https://optical-dev.oliver.solutions/social-reports/"
echo " Backend: http://127.0.0.1:3457 (Docker)"
echo " V2 dir: $BACKEND_DIR_V2"
echo " V1 dir: $BACKEND_DIR_V1 (kept for rollback)"
echo " Rollback: bash $BACKEND_DIR_V2/v2/deploy/rollback-to-v1.sh"
echo "════════════════════════════════════════════════════"