diff --git a/Dockerfile b/Dockerfile index 4782f06..a070599 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,6 +48,10 @@ RUN bun install --production # Create data directories RUN mkdir -p /data/communities /data/books /data/swag-artifacts /data/files /data/splats +# Copy entrypoint for Infisical secret injection +COPY entrypoint.sh /app/entrypoint.sh +RUN chmod +x /app/entrypoint.sh + # Set environment ENV NODE_ENV=production ENV STORAGE_DIR=/data/communities @@ -66,4 +70,5 @@ VOLUME /data/splats EXPOSE 3000 +ENTRYPOINT ["/app/entrypoint.sh"] CMD ["bun", "run", "server/index.ts"] diff --git a/docker-compose.yml b/docker-compose.yml index c5457a0..1e0276b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,33 +18,24 @@ services: - BOOKS_DIR=/data/books - SWAG_ARTIFACTS_DIR=/data/swag-artifacts - PORT=3000 - - INTERNAL_API_KEY=${INTERNAL_API_KEY} + - FILES_DIR=/data/files + - SPLATS_DIR=/data/splats + - INFISICAL_CLIENT_ID=${INFISICAL_CLIENT_ID} + - INFISICAL_CLIENT_SECRET=${INFISICAL_CLIENT_SECRET} + - INFISICAL_PROJECT_SLUG=rspace + - INFISICAL_ENV=prod + - INFISICAL_URL=http://infisical:8080 - DATABASE_URL=postgres://rspace:${POSTGRES_PASSWORD}@rspace-db:5432/rspace - FLOW_SERVICE_URL=http://payment-flow:3010 - FLOW_ID=a79144ec-e6a2-4e30-a42a-6d8237a5953d - FUNNEL_ID=0ff6a9ac-1667-4fc7-9a01-b1620810509f - - FILES_DIR=/data/files - - SPLATS_DIR=/data/splats - - X402_PAY_TO=${X402_PAY_TO:-} - - X402_NETWORK=${X402_NETWORK:-eip155:84532} - - X402_UPLOAD_PRICE=${X402_UPLOAD_PRICE:-0.01} - - X402_FACILITATOR_URL=${X402_FACILITATOR_URL:-https://x402.org/facilitator} - - R2_ENDPOINT=${R2_ENDPOINT} - - R2_BUCKET=${R2_BUCKET:-rtube-videos} - - R2_ACCESS_KEY_ID=${R2_ACCESS_KEY_ID} - - R2_SECRET_ACCESS_KEY=${R2_SECRET_ACCESS_KEY} - - R2_PUBLIC_URL=${R2_PUBLIC_URL} - UMAMI_URL=https://analytics.rspace.online - UMAMI_WEBSITE_ID=292f6ac6-79f8-497b-ba6a-7a51e3b87b9f - MAPS_SYNC_URL=wss://sync.rmaps.online - IMAP_HOST=mail.rmail.online - IMAP_PORT=993 - IMAP_TLS_REJECT_UNAUTHORIZED=false - - HETZNER_API_TOKEN=${HETZNER_API_TOKEN} - - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN} - - CLOUDFLARE_ZONE_ID=${CLOUDFLARE_ZONE_ID} - TWENTY_API_URL=https://rnetwork.online - - TWENTY_API_TOKEN=${TWENTY_API_TOKEN} depends_on: rspace-db: condition: service_healthy diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..1ab3d8b --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# Infisical secret injection entrypoint +# Fetches secrets from Infisical API and injects them as env vars before starting the app. +# Required env vars: INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET +# Optional: INFISICAL_PROJECT_SLUG (default: rspace), INFISICAL_ENV (default: prod), +# INFISICAL_URL (default: http://infisical:8080) + +set -e + +INFISICAL_URL="${INFISICAL_URL:-http://infisical:8080}" +INFISICAL_ENV="${INFISICAL_ENV:-prod}" +INFISICAL_PROJECT_SLUG="${INFISICAL_PROJECT_SLUG:-rspace}" + +if [ -z "$INFISICAL_CLIENT_ID" ] || [ -z "$INFISICAL_CLIENT_SECRET" ]; then + echo "[infisical] No credentials set, starting without secret injection" + exec "$@" +fi + +echo "[infisical] Fetching secrets from ${INFISICAL_PROJECT_SLUG}/${INFISICAL_ENV}..." + +# Use Bun's built-in fetch API for HTTP calls and JSON parsing +EXPORTS=$(bun -e " +(async () => { + try { + const base = process.env.INFISICAL_URL; + const auth = await fetch(base + '/api/v1/auth/universal-auth/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + clientId: process.env.INFISICAL_CLIENT_ID, + clientSecret: process.env.INFISICAL_CLIENT_SECRET + }) + }).then(r => r.json()); + if (!auth.accessToken) { console.error('[infisical] Auth failed'); process.exit(1); } + + const slug = process.env.INFISICAL_PROJECT_SLUG; + const env = process.env.INFISICAL_ENV; + const secrets = await fetch(base + '/api/v3/secrets/raw?workspaceSlug=' + slug + '&environment=' + env + '&secretPath=/&recursive=true', { + headers: { 'Authorization': 'Bearer ' + auth.accessToken } + }).then(r => r.json()); + + if (!secrets.secrets) { console.error('[infisical] No secrets returned'); process.exit(1); } + + for (const s of secrets.secrets) { + const escaped = s.secretValue.replace(/'/g, \"'\\\\''\" ); + console.log('export ' + s.secretKey + \"='\" + escaped + \"'\"); + } + } catch (e) { console.error('[infisical] Error:', e.message); process.exit(1); } +})(); +" 2>&1) || { + echo "[infisical] WARNING: Failed to fetch secrets, starting with existing env vars" + exec "$@" +} + +# Check if we got export statements or error messages +if echo "$EXPORTS" | grep -q "^export "; then + COUNT=$(echo "$EXPORTS" | grep -c "^export ") + eval "$EXPORTS" + echo "[infisical] Injected ${COUNT} secrets" +else + echo "[infisical] WARNING: $EXPORTS" + echo "[infisical] Starting with existing env vars" +fi + +exec "$@"