Update setup script to improve environment variable management and secrets handling.

This commit is contained in:
Nikita
2025-12-24 19:13:20 -08:00
parent 3141241626
commit bc84274304

263
flow.toml
View File

@@ -20,7 +20,7 @@ EXAMPLE_FILE="$WEB_DIR/.env.example"
echo "=== Linsa Setup ===" echo "=== Linsa Setup ==="
echo "" echo ""
# 1. Create .env from template if needed # 1. Create .env from template
if [ ! -f "$ENV_FILE" ]; then if [ ! -f "$ENV_FILE" ]; then
cp "$EXAMPLE_FILE" "$ENV_FILE" cp "$EXAMPLE_FILE" "$ENV_FILE"
echo " Created $ENV_FILE from template" echo " Created $ENV_FILE from template"
@@ -28,7 +28,27 @@ else
echo " $ENV_FILE exists" echo " $ENV_FILE exists"
fi fi
# 2. Generate secrets and set defaults # 2. Pull secrets from 1focus (API keys, etc)
echo ""
echo "Pulling secrets from 1focus..."
RESPONSE=$(curl -s "https://1f-worker.nikiv.workers.dev/api/v1/env/linsa" 2>/dev/null || echo "{}")
if echo "$RESPONSE" | jq -e '.env' > /dev/null 2>&1; then
echo "$RESPONSE" | jq -r '.env | to_entries | .[] | "(.key)=(.value)"' | while read line; do
key=$(echo "$line" | cut -d= -f1)
value=$(echo "$line" | cut -d= -f2-)
if grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
sed -i '' "s|^${key}=.*|${key}=${value}|" "$ENV_FILE"
else
echo "${key}=${value}" >> "$ENV_FILE"
fi
echo " $key"
done
else
echo " (1focus unavailable, using defaults)"
fi
# 3. Generate local secrets
node - <<'NODE' node - <<'NODE'
const fs = require("fs") const fs = require("fs")
const path = require("path") const path = require("path")
@@ -61,70 +81,63 @@ ensureKey("APP_BASE_URL", "http://localhost:5613")
fs.writeFileSync(envPath, text) fs.writeFileSync(envPath, text)
NODE NODE
# 3. Install dependencies # 4. Install dependencies
echo "" echo ""
echo "Installing dependencies..." echo "Installing dependencies..."
pnpm install pnpm install
# 4. Check DATABASE_URL # 5. Database setup
echo "" echo ""
DATABASE_URL=$(grep -E "^DATABASE_URL=" "$ENV_FILE" 2>/dev/null | cut -d'=' -f2- || true) DATABASE_URL=$(grep -E "^DATABASE_URL=" "$ENV_FILE" 2>/dev/null | cut -d'=' -f2- || true)
if [ -z "$DATABASE_URL" ] || [ "$DATABASE_URL" = "" ] || [[ "$DATABASE_URL" == *"user:password"* ]]; then if [ -z "$DATABASE_URL" ] || [[ "$DATABASE_URL" == *"user:password"* ]]; then
echo "=== Database Setup ===" echo "=== Database Options ==="
echo "" echo ""
echo "You need a Neon Postgres database." echo " 1. Local Docker (recommended for dev)"
echo "Get your connection string from: https://console.neon.tech" echo " 2. Neon Postgres (cloud)"
echo "" echo ""
read -p "Paste your Neon DATABASE_URL (or press Enter to skip): " NEW_DB_URL read -p "Choose [1/2] or press Enter for local: " DB_CHOICE
if [ -n "$NEW_DB_URL" ]; then if [ "$DB_CHOICE" = "2" ]; then
# Update .env with the new DATABASE_URL echo ""
if grep -q "^DATABASE_URL=" "$ENV_FILE"; then echo "Get your connection string from: https://console.neon.tech"
read -p "Paste DATABASE_URL: " NEW_DB_URL
if [ -n "$NEW_DB_URL" ]; then
sed -i '' "s|^DATABASE_URL=.*|DATABASE_URL=$NEW_DB_URL|" "$ENV_FILE" sed -i '' "s|^DATABASE_URL=.*|DATABASE_URL=$NEW_DB_URL|" "$ENV_FILE"
else DATABASE_URL="$NEW_DB_URL"
echo "DATABASE_URL=$NEW_DB_URL" >> "$ENV_FILE" echo " DATABASE_URL saved"
fi fi
DATABASE_URL="$NEW_DB_URL" else
echo " DATABASE_URL saved" echo "Using local Docker database"
echo "Run 'f local-services' to start PostgreSQL + Electric"
fi fi
fi fi
# 5. Push schema to database if DATABASE_URL is set # 6. Push schema if using cloud DB
if [ -n "$DATABASE_URL" ] && [ "$DATABASE_URL" != "" ] && [[ "$DATABASE_URL" != *"user:password"* ]]; then if [ -n "$DATABASE_URL" ] && [[ "$DATABASE_URL" != *"user:password"* ]] && [[ "$DATABASE_URL" != *"localtest.me"* ]]; then
echo "" echo ""
echo "Pushing schema to database..." echo "Pushing schema to database..."
cd "$WEB_DIR" cd "$WEB_DIR"
pnpm drizzle-kit push --force 2>&1 | tail -5 pnpm drizzle-kit push --force 2>&1 | tail -3
echo " Database schema ready" echo " Database schema ready"
cd "$ROOT" cd "$ROOT"
fi fi
# 6. Summary # 7. Summary
echo "" echo ""
echo "=== Setup Complete ===" echo "=== Setup Complete ==="
echo "" echo ""
# Check what's configured DB_SET=$(grep -E "^DATABASE_URL=.+" "$ENV_FILE" 2>/dev/null | grep -v "user:password" | wc -l | tr -d ' ')
DB_SET=$(grep -E "^DATABASE_URL=.+" "$ENV_FILE" 2>/dev/null | grep -v "DATABASE_URL=$" | grep -v "user:password" | wc -l | tr -d ' ')
AI_SET=$(grep -E "^OPENROUTER_API_KEY=.+" "$ENV_FILE" 2>/dev/null | grep -v "OPENROUTER_API_KEY=$" | wc -l | tr -d ' ') AI_SET=$(grep -E "^OPENROUTER_API_KEY=.+" "$ENV_FILE" 2>/dev/null | grep -v "OPENROUTER_API_KEY=$" | wc -l | tr -d ' ')
if [ "$DB_SET" = "1" ]; then [ "$DB_SET" = "1" ] && echo " Database" || echo " Database (run 'f local-services' or add DATABASE_URL)"
echo " Database: Connected" [ "$AI_SET" = "1" ] && echo " AI Chat" || echo " AI Chat (add OPENROUTER_API_KEY for responses)"
else
echo " Database: Not configured (add DATABASE_URL to packages/web/.env)"
fi
if [ "$AI_SET" = "1" ]; then
echo " AI Chat: Configured"
else
echo " AI Chat: Not configured (add OPENROUTER_API_KEY for AI responses)"
fi
echo "" echo ""
echo "Run 'f dev' to start the web server on http://localhost:5613" echo "Next: Run 'f dev' to start on http://localhost:5613"
""" """
description = "Set up Linsa: create .env, install deps, push schema to Neon." description = "Set up Linsa: pull secrets from 1focus, install deps, configure database."
dependencies = ["node", "pnpm"] dependencies = ["node", "pnpm"]
shortcuts = ["s"] shortcuts = ["s"]
@@ -1803,162 +1816,122 @@ dependencies = ["node", "pnpm"]
shortcuts = ["stc", "stripe-check"] shortcuts = ["stc", "stripe-check"]
# ============================================================================= # =============================================================================
# Environment Management # Environment Management (via 1focus)
# ============================================================================= # =============================================================================
[[tasks]] [[tasks]]
name = "env-status" name = "env-pull"
description = "Show local .env vs wrangler secrets status" description = "Pull env vars from 1focus to .env (for contributors)"
command = ''' command = '''
set -euo pipefail set -euo pipefail
echo "=== Environment Status ==="
echo ""
cd packages/web cd packages/web
echo "Local .env:" echo "=== Pulling env from 1focus ==="
if [ -f .env ]; then echo ""
cat .env | grep -v "^#" | grep -v "^$" | cut -d= -f1 | sort | while read key; do
echo " ✓ $key" # Fetch from 1focus
done RESPONSE=$(curl -s "https://1f-worker.nikiv.workers.dev/api/v1/env/linsa")
else
echo " ✗ No .env file" if ! echo "$RESPONSE" | jq -e '.env' > /dev/null 2>&1; then
echo "Failed to fetch env from 1focus"
exit 1
fi
# Create .env from example first
if [ ! -f .env ]; then
cp .env.example .env 2>/dev/null || touch .env
fi
# Update with 1focus values (keeping local DATABASE_URL etc)
echo "$RESPONSE" | jq -r '.env | to_entries | .[] | "\(.key)=\(.value)"' | while read line; do
key=$(echo "$line" | cut -d= -f1)
value=$(echo "$line" | cut -d= -f2-)
# Update existing or append
if grep -q "^${key}=" .env 2>/dev/null; then
sed -i "" "s|^${key}=.*|${key}=${value}|" .env
else
echo "${key}=${value}" >> .env
fi
echo " $key"
done
# Generate auth secret if missing
if ! grep -q "^BETTER_AUTH_SECRET=" .env || grep -q "your-strong-secret" .env; then
AUTH_SECRET=$(openssl rand -hex 32)
if grep -q "^BETTER_AUTH_SECRET=" .env; then
sed -i "" "s|^BETTER_AUTH_SECRET=.*|BETTER_AUTH_SECRET=${AUTH_SECRET}|" .env
else
echo "BETTER_AUTH_SECRET=${AUTH_SECRET}" >> .env
fi
echo " BETTER_AUTH_SECRET (generated)"
fi fi
echo "" echo ""
echo "Wrangler Secrets (production):" echo "Done! Run 'f dev' to start."
pnpm exec wrangler secret list 2>&1 | grep '"name"' | sed 's/.*"name": "\([^"]*\)".*/ ✓ \1/' || echo " (none or error)"
echo ""
echo "Commands:"
echo " f env-push - Push .env to wrangler secrets"
echo " f env-pull - Pull wrangler secrets to .env"
echo " f env-set KEY value - Set single var"
''' '''
shortcuts = ["envs"] shortcuts = ["env", "envp"]
[[tasks]] [[tasks]]
name = "env-push" name = "env-push"
description = "Push local .env to wrangler secrets (production)" description = "Push local secrets to 1focus (maintainers only)"
command = ''' command = '''
set -euo pipefail set -euo pipefail
cd packages/web cd packages/web
if [ ! -f .env ]; then if [ ! -f .env ]; then
echo "No .env file found" echo "No .env file"
exit 1 exit 1
fi fi
echo "Pushing .env to wrangler secrets..." echo "Pushing secrets to 1focus..."
echo ""
# Read .env and push each secret # Build JSON from .env
VARS="{"
FIRST=true
while IFS='=' read -r key value || [ -n "$key" ]; do while IFS='=' read -r key value || [ -n "$key" ]; do
# Skip comments and empty lines
[[ "$key" =~ ^#.*$ ]] && continue [[ "$key" =~ ^#.*$ ]] && continue
[[ -z "$key" ]] && continue [[ -z "$key" ]] && continue
# Skip VITE_ vars (those are build-time, not secrets)
[[ "$key" =~ ^VITE_ ]] && continue [[ "$key" =~ ^VITE_ ]] && continue
[[ "$key" == "DATABASE_URL" ]] && continue
[[ "$key" == "ELECTRIC_URL" ]] && continue
[[ "$key" == "BETTER_AUTH_SECRET" ]] && continue
[[ "$key" == "APP_BASE_URL" ]] && continue
# Skip local-only vars
[[ "$key" == "DATABASE_URL" ]] && continue # Use Hyperdrive in prod
[[ "$key" == "PROD_DATABASE_URL" ]] && continue
[[ "$key" == "ELECTRIC_URL" ]] && continue # Local Electric
# Remove quotes from value
value="${value%\"}" value="${value%\"}"
value="${value#\"}" value="${value#\"}"
echo "Setting $key..." if [ "$FIRST" = true ]; then FIRST=false; else VARS+=","; fi
echo "$value" | pnpm exec wrangler secret put "$key" 2>/dev/null || echo " (failed)" value=$(echo "$value" | sed 's/\\/\\\\/g; s/"/\\"/g')
VARS+="\"$key\":\"$value\""
done < .env done < .env
VARS+="}"
curl -s -X POST "https://1f-worker.nikiv.workers.dev/api/v1/env/linsa" \
-H "Content-Type: application/json" \
-d "{\"vars\": $VARS}" | jq .
echo "" echo ""
echo "Done. Run 'f env-status' to verify." echo "Done!"
''' '''
dependencies = ["pnpm"] shortcuts = ["envs"]
shortcuts = ["envp"]
[[tasks]] [[tasks]]
name = "env-set" name = "env-show"
description = "Set a wrangler secret (usage: f env-set KEY value)" description = "Show env vars stored in 1focus"
command = ''' command = '''
set -euo pipefail curl -s "https://1f-worker.nikiv.workers.dev/api/v1/env/linsa" | jq .
KEY="${1:-}"
VALUE="${2:-}"
if [ -z "$KEY" ]; then
echo "Usage: f env-set KEY value"
exit 1
fi
cd packages/web
echo "Setting $KEY..."
echo "$VALUE" | pnpm exec wrangler secret put "$KEY"
echo "Done."
''' '''
dependencies = ["pnpm"] shortcuts = ["env1f"]
shortcuts = ["envset"]
[[tasks]]
name = "env-local"
description = "Create local .env from .env.example with defaults"
command = '''
set -euo pipefail
cd packages/web
if [ -f .env ]; then
echo ".env already exists. Delete it first to regenerate."
exit 0
fi
cp .env.example .env
# Generate random auth secret
AUTH_SECRET=$(openssl rand -hex 32)
sed -i '' "s/your-strong-secret-at-least-32-chars/$AUTH_SECRET/" .env
echo "Created .env with:"
echo " - Random BETTER_AUTH_SECRET"
echo " - Local PostgreSQL (needs docker)"
echo " - Local Electric (needs docker)"
echo ""
echo "Next steps:"
echo " 1. Add your OPENROUTER_API_KEY"
echo " 2. Run 'f local-services' for postgres + electric"
echo " 3. Run 'f dev'"
'''
shortcuts = ["envl"]
[[tasks]] [[tasks]]
name = "secrets-list" name = "secrets-list"
description = "List all wrangler secrets" description = "List wrangler secrets (for production)"
command = ''' command = '''
cd packages/web cd packages/web
pnpm exec wrangler secret list 2>&1 | jq -r '.[].name' 2>/dev/null | sort || pnpm exec wrangler secret list pnpm exec wrangler secret list 2>&1 | grep '"name"' | sed 's/.*"name": "\([^"]*\)".*/ ✓ \1/' | sort
''' '''
dependencies = ["pnpm"] dependencies = ["pnpm"]
shortcuts = ["sec"] shortcuts = ["sec"]
[[tasks]]
name = "secrets-delete"
description = "Delete a wrangler secret (usage: f secrets-delete KEY)"
command = '''
KEY="${1:-}"
if [ -z "$KEY" ]; then
echo "Usage: f secrets-delete KEY"
exit 1
fi
cd packages/web
pnpm exec wrangler secret delete "$KEY"
'''
dependencies = ["pnpm"]
shortcuts = ["secd"]