# syntax=docker/dockerfile:1 FROM lbjlaq/antigravity-manager:latest USER root RUN apt-get update && apt-get install -y --no-install-recommends curl jq && rm -rf /var/lib/apt/lists/* ENV DATA_DIR=/root/.antigravity_tools ENV SYNC_INTERVAL=150 EXPOSE 8045 COPY --chmod=755 <<'SCRIPT' /entrypoint.sh #!/bin/bash set -e DATA_DIR="${DATA_DIR:-/root/.antigravity_tools}" SYNC_INTERVAL="${SYNC_INTERVAL:-30}" LAST_SYNC="/tmp/.last_sync" SUPABASE_PREFIX="antigravity_tools" # ── Helpers ────────────────────────────────────────────────────── supabase_list() { local prefix="$1" response files folders response=$(curl -s -X POST \ "${SUPABASE_URL}/storage/v1/object/list/${SUPABASE_BUCKET}" \ -H "Authorization: Bearer ${SUPABASE_KEY}" \ -H "Content-Type: application/json" \ -d "{\"prefix\":\"${prefix}\",\"limit\":1000}" 2>/dev/null) files=$(echo "$response" | jq -r '.[]? | select(.id != null) | .name' 2>/dev/null) folders=$(echo "$response" | jq -r '.[]? | select(.id == null) | .name' 2>/dev/null) for f in $files; do [ -n "$f" ] && echo "${prefix}${f}"; done for d in $folders; do [ -n "$d" ] && supabase_list "${prefix}${d}/"; done } supabase_download() { local remote_path="$1" local_path="$2" mkdir -p "$(dirname "$local_path")" curl -s -o "$local_path" \ "${SUPABASE_URL}/storage/v1/object/${SUPABASE_BUCKET}/${remote_path}" \ -H "Authorization: Bearer ${SUPABASE_KEY}" } supabase_upload() { local local_path="$1" remote_path="$2" curl -s -X POST \ "${SUPABASE_URL}/storage/v1/object/${SUPABASE_BUCKET}/${remote_path}" \ -H "Authorization: Bearer ${SUPABASE_KEY}" \ -H "Content-Type: application/octet-stream" \ -H "x-upsert: true" \ --data-binary "@${local_path}" > /dev/null 2>&1 } supabase_has_data() { local r r=$(curl -s -X POST \ "${SUPABASE_URL}/storage/v1/object/list/${SUPABASE_BUCKET}" \ -H "Authorization: Bearer ${SUPABASE_KEY}" \ -H "Content-Type: application/json" \ -d "{\"prefix\":\"${SUPABASE_PREFIX}/\",\"limit\":1}" 2>/dev/null) [ "$(echo "$r" | jq 'length' 2>/dev/null)" != "0" ] } # ── Pull: Download all files from Supabase ────────────────────── pull_from_supabase() { echo "[Sync] Pulling from Supabase..." local count=0 supabase_list "${SUPABASE_PREFIX}/" | while read -r remote_path; do local rel="${remote_path#${SUPABASE_PREFIX}/}" local local_path="$DATA_DIR/$rel" supabase_download "$remote_path" "$local_path" echo "[Sync] <- $rel" count=$((count + 1)) done touch "$LAST_SYNC" echo "[Sync] Pull complete!" } # ── Push: Upload entire directory to Supabase ─────────────────── push_all() { echo "[Sync] Pushing entire directory..." find "$DATA_DIR" -type f | while read -r filepath; do local rel="${filepath#$DATA_DIR/}" supabase_upload "$filepath" "${SUPABASE_PREFIX}/${rel}" echo "[Sync] -> $rel" done touch "$LAST_SYNC" echo "[Sync] Push complete at $(date '+%H:%M:%S')" } # ── Push changed: Only upload files modified since last sync ──── push_changed() { find "$DATA_DIR" -type f -newer "$LAST_SYNC" 2>/dev/null | while read -r filepath; do local rel="${filepath#$DATA_DIR/}" supabase_upload "$filepath" "${SUPABASE_PREFIX}/${rel}" echo "[Sync] -> $rel" done touch "$LAST_SYNC" } # ── Main ───────────────────────────────────────────────────────── SYNC="" [ -n "$SUPABASE_URL" ] && [ -n "$SUPABASE_KEY" ] && [ -n "$SUPABASE_BUCKET" ] && SYNC="1" # Step 1: Pull existing data from Supabase if [ -n "$SYNC" ]; then echo "[Sync] Checking Supabase for existing data..." if supabase_has_data; then pull_from_supabase else echo "[Sync] Empty bucket — first run." fi fi # Step 2: Start the app /app/antigravity-tools --headless & APP_PID=$! # Step 3: First run — wait for data dir, then push initial state if [ -n "$SYNC" ] && [ ! -f "$LAST_SYNC" ]; then echo "[Sync] Waiting for app to create data..." while [ ! -d "$DATA_DIR" ]; do sleep 2; done sleep 5 push_all fi # Step 4: Background sync — push changed files every N seconds if [ -n "$SYNC" ]; then ( while kill -0 $APP_PID 2>/dev/null; do sleep "$SYNC_INTERVAL" push_changed || true done ) & SYNC_PID=$! fi # Step 5: Final push on shutdown cleanup() { echo "[Sync] Shutting down — final push..." [ -n "$SYNC" ] && [ -d "$DATA_DIR" ] && push_all kill $APP_PID 2>/dev/null [ -n "$SYNC_PID" ] && kill $SYNC_PID 2>/dev/null wait $APP_PID 2>/dev/null exit 0 } trap cleanup SIGTERM SIGINT wait $APP_PID SCRIPT ENTRYPOINT ["/entrypoint.sh"]