feat: move rApp secrets to Infisical, add seed script
Remove DATABASE_URL and ADMIN_DIDS from docker-compose.yml (now injected via Infisical entrypoint). Add scripts/seed-infisical.sh to interactively populate 21 module-specific secrets (R2, Immich, Twenty, Discourse, FAL, RunPod, etc.) into the rspace Infisical project. Update Dockerfile to include scripts/ in the image. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5e0f30567a
commit
7850b9d34c
|
|
@ -50,7 +50,8 @@ RUN mkdir -p /data/communities /data/books /data/swag-artifacts /data/files /dat
|
|||
|
||||
# Copy entrypoint for Infisical secret injection
|
||||
COPY entrypoint.sh /app/entrypoint.sh
|
||||
RUN chmod +x /app/entrypoint.sh
|
||||
COPY scripts/ /app/scripts/
|
||||
RUN chmod +x /app/entrypoint.sh /app/scripts/*.sh
|
||||
|
||||
# Set environment
|
||||
ENV NODE_ENV=production
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ services:
|
|||
- 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
|
||||
|
|
@ -38,7 +37,6 @@ services:
|
|||
- IMAP_PORT=993
|
||||
- IMAP_TLS_REJECT_UNAUTHORIZED=false
|
||||
- TWENTY_API_URL=https://rnetwork.online
|
||||
- ADMIN_DIDS=${ADMIN_DIDS}
|
||||
depends_on:
|
||||
rspace-db:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
#!/bin/sh
|
||||
# seed-infisical.sh — Add missing rApp secrets to the rspace Infisical project.
|
||||
# Run from inside the rspace container:
|
||||
# docker exec -it rspace-online /app/scripts/seed-infisical.sh
|
||||
#
|
||||
# Uses INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET, INFISICAL_URL,
|
||||
# INFISICAL_PROJECT_SLUG, and INFISICAL_ENV from the container environment.
|
||||
|
||||
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 "ERROR: INFISICAL_CLIENT_ID and INFISICAL_CLIENT_SECRET must be set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Authenticating with Infisical at ${INFISICAL_URL}..."
|
||||
|
||||
TOKEN=$(bun -e "
|
||||
(async () => {
|
||||
const r = await fetch('${INFISICAL_URL}/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 (!r.accessToken) { console.error('Auth failed:', JSON.stringify(r)); process.exit(1); }
|
||||
console.log(r.accessToken);
|
||||
})();
|
||||
") || { echo "Authentication failed"; exit 1; }
|
||||
|
||||
echo "Authenticated. Fetching existing secrets..."
|
||||
|
||||
EXISTING=$(bun -e "
|
||||
(async () => {
|
||||
const r = await fetch('${INFISICAL_URL}/api/v3/secrets/raw?workspaceSlug=${INFISICAL_PROJECT_SLUG}&environment=${INFISICAL_ENV}&secretPath=/&recursive=true', {
|
||||
headers: { 'Authorization': 'Bearer ${TOKEN}' }
|
||||
}).then(r => r.json());
|
||||
if (!r.secrets) { console.error('Failed to list secrets'); process.exit(1); }
|
||||
console.log(r.secrets.map(s => s.secretKey).join('\n'));
|
||||
})();
|
||||
") || { echo "Failed to list secrets"; exit 1; }
|
||||
|
||||
# Secrets to seed: NAME|MODULE|DESCRIPTION
|
||||
SECRETS="
|
||||
DATABASE_URL|core|Full Postgres connection string (e.g. postgres://rspace:PASS@rspace-db:5432/rspace)
|
||||
ADMIN_DIDS|core|Comma-separated admin DIDs
|
||||
R2_ENDPOINT|rTube|Cloudflare R2 S3 endpoint URL
|
||||
R2_BUCKET|rTube|R2 bucket name (default: rtube-videos)
|
||||
R2_ACCESS_KEY_ID|rTube|R2 API key ID
|
||||
R2_SECRET_ACCESS_KEY|rTube|R2 API secret key
|
||||
R2_PUBLIC_URL|rTube|R2 public CDN URL
|
||||
RPHOTOS_IMMICH_URL|rPhotos|Internal Immich URL (e.g. http://immich-server:2283)
|
||||
RPHOTOS_API_KEY|rPhotos|Immich API key
|
||||
RPHOTOS_IMMICH_PUBLIC_URL|rPhotos|Public Immich URL
|
||||
TWENTY_API_TOKEN|rNetwork|Twenty CRM API token
|
||||
HETZNER_API_TOKEN|rForum|Hetzner Cloud API token (Discourse VM provisioning)
|
||||
CLOUDFLARE_API_TOKEN|rForum|Cloudflare API token (Discourse DNS management)
|
||||
CLOUDFLARE_FORUM_ZONE_ID|rForum|Cloudflare zone ID for Discourse forum DNS
|
||||
DISCOURSE_API_KEY|rForum|Discourse admin API key
|
||||
DISCOURSE_URL|rForum|Discourse instance URL (e.g. https://forum.rforum.online)
|
||||
FAL_KEY|AI/MI|Fal.ai API key
|
||||
RUNPOD_API_KEY|AI/MI|RunPod API key
|
||||
X402_PAY_TO|payments|Payment recipient address
|
||||
MAILCOW_API_KEY|EncryptID|Mailcow admin API key
|
||||
ENCRYPTID_DEMO_SPACES|EncryptID|Comma-separated demo space slugs
|
||||
"
|
||||
|
||||
ADDED=0
|
||||
SKIPPED=0
|
||||
|
||||
echo ""
|
||||
echo "=== Seeding rspace Infisical secrets ==="
|
||||
echo ""
|
||||
|
||||
echo "$SECRETS" | while IFS='|' read -r NAME MODULE DESC; do
|
||||
# Skip blank lines
|
||||
[ -z "$NAME" ] && continue
|
||||
# Trim whitespace
|
||||
NAME=$(echo "$NAME" | xargs)
|
||||
MODULE=$(echo "$MODULE" | xargs)
|
||||
DESC=$(echo "$DESC" | xargs)
|
||||
|
||||
# Check if already exists
|
||||
if echo "$EXISTING" | grep -qx "$NAME"; then
|
||||
echo "[skip] $NAME (already exists)"
|
||||
continue
|
||||
fi
|
||||
|
||||
printf "[%s] %s — %s\n" "$MODULE" "$NAME" "$DESC"
|
||||
printf " Enter value (or press Enter to skip): "
|
||||
read -r VALUE
|
||||
|
||||
if [ -z "$VALUE" ]; then
|
||||
echo " Skipped."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Create the secret via Infisical API
|
||||
RESULT=$(bun -e "
|
||||
(async () => {
|
||||
const r = await fetch('${INFISICAL_URL}/api/v3/secrets/raw/$(echo "$NAME" | sed 's/ /%20/g')', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ${TOKEN}',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
workspaceSlug: '${INFISICAL_PROJECT_SLUG}',
|
||||
environment: '${INFISICAL_ENV}',
|
||||
secretPath: '/',
|
||||
secretValue: $(printf '%s' "$VALUE" | bun -e "process.stdout.write(JSON.stringify(await Bun.stdin.text()))")
|
||||
})
|
||||
});
|
||||
if (r.ok) console.log('OK');
|
||||
else console.log('FAIL: ' + (await r.text()));
|
||||
})();
|
||||
") || RESULT="FAIL: bun error"
|
||||
|
||||
if [ "$RESULT" = "OK" ]; then
|
||||
echo " Added."
|
||||
else
|
||||
echo " ERROR: $RESULT"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Done. Restart the container to pick up new secrets:"
|
||||
echo " docker compose restart rspace"
|
||||
Loading…
Reference in New Issue