Update deploy script and .env.example for PostgreSQL
- deploy.sh: wait for postgres healthcheck before app healthcheck - deploy.sh: add one-time JSON→PostgreSQL migration prompt (step 13) with migration marker file to avoid re-prompting on future deploys - deploy.sh: update summary to show both app and DB container names - deploy.sh: check POSTGRES_PASSWORD in required keys - .env.example: add POSTGRES_PASSWORD, ADMIN_EMAILS, emergency access vars Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8da149b84e
commit
1a1bc97bfc
2 changed files with 78 additions and 28 deletions
16
.env.example
16
.env.example
|
|
@ -12,8 +12,17 @@ AZURE_CLIENT_ID=9079054c-9620-4757-a256-23413042f1ef
|
|||
AZURE_REDIRECT_URI=https://ai-sandbox.oliver.solutions/ac-helper/
|
||||
|
||||
# ── Admin bootstrap ───────────────────────────────────────────────────────────
|
||||
# First login with this email automatically receives the admin role
|
||||
# First login with these emails automatically receives the admin role (comma-separated)
|
||||
ADMIN_EMAIL=
|
||||
ADMIN_EMAILS=
|
||||
|
||||
# ── Emergency access (bypass SSO) ────────────────────────────────────────────
|
||||
# Set EMERGENCY_TOKEN to a long random string to allow token-based login when
|
||||
# Azure AD / 2FA is unavailable. Leave blank to disable entirely.
|
||||
# Generate with: python3 -c "import secrets; print(secrets.token_hex(32))"
|
||||
EMERGENCY_TOKEN=
|
||||
EMERGENCY_USER_EMAIL=
|
||||
EMERGENCY_USER_NAME=Emergency Access
|
||||
|
||||
# ── OpenAI ────────────────────────────────────────────────────────────────────
|
||||
OPENAI_API_KEY=
|
||||
|
|
@ -51,6 +60,11 @@ ENABLE_COST_ESTIMATION=true
|
|||
MAX_PROCESSING_COST_USD=10.00
|
||||
MAX_CONCURRENT_JOBS=5
|
||||
|
||||
# ── PostgreSQL ────────────────────────────────────────────────────────────────
|
||||
# Password for the ac-tool DB user. Change before deploying.
|
||||
# Generate with: python3 -c "import secrets; print(secrets.token_hex(24))"
|
||||
POSTGRES_PASSWORD=
|
||||
|
||||
# ── Security ──────────────────────────────────────────────────────────────────
|
||||
# Generate with: python3 -c "import secrets; print(secrets.token_hex(32))"
|
||||
SESSION_SECRET=
|
||||
|
|
|
|||
90
deploy.sh
90
deploy.sh
|
|
@ -10,6 +10,7 @@ WEB_USER="www-data"
|
|||
ENV_FILE="$APP_DIR/.env"
|
||||
ENV_EXAMPLE="$APP_DIR/.env.example"
|
||||
CONTAINER_NAME="ac-tool"
|
||||
DB_CONTAINER_NAME="ac-tool-db"
|
||||
FE_BUILD_TAG="ac-tool-fe-extract"
|
||||
|
||||
# ── Colours ───────────────────────────────────────────────────────────────────
|
||||
|
|
@ -47,28 +48,28 @@ if [[ ! -f "$ENV_FILE" ]]; then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
# Load .env into the current shell so we can read APP_PORT
|
||||
# Load .env into the current shell so we can read APP_PORT etc.
|
||||
set -o allexport
|
||||
# shellcheck disable=SC1090
|
||||
source "$ENV_FILE"
|
||||
set +o allexport
|
||||
|
||||
APP_PORT="${APP_PORT:-8100}"
|
||||
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-}"
|
||||
|
||||
# Warn about blank required keys
|
||||
MISSING=""
|
||||
for KEY in GEMINI_API_KEY ADMIN_EMAIL SESSION_SECRET; do
|
||||
for KEY in GEMINI_API_KEY ADMIN_EMAIL SESSION_SECRET POSTGRES_PASSWORD; do
|
||||
VAL="${!KEY:-}"
|
||||
[[ -z "$VAL" ]] && MISSING="$MISSING $KEY"
|
||||
done
|
||||
[[ -n "$MISSING" ]] && warn "These required keys are empty in .env:$MISSING"
|
||||
[[ -n "$MISSING" ]] && warn "These keys are empty in .env:$MISSING"
|
||||
|
||||
ok ".env loaded (APP_PORT=$APP_PORT)"
|
||||
|
||||
# ── 4. Port check ─────────────────────────────────────────────────────────────
|
||||
log "Checking port $APP_PORT..."
|
||||
if ss -tlnp | grep -q ":${APP_PORT} "; then
|
||||
# Port is in use — check if it's our own container
|
||||
if docker ps --format '{{.Names}} {{.Ports}}' | grep -q "${CONTAINER_NAME}.*${APP_PORT}"; then
|
||||
warn "Port $APP_PORT already used by our container (will be restarted)"
|
||||
else
|
||||
|
|
@ -93,7 +94,7 @@ else
|
|||
ok "Updated to $(git rev-parse --short HEAD)"
|
||||
fi
|
||||
|
||||
# ── 6. Docker build (with cache, refresh base images) ─────────────────────────
|
||||
# ── 6. Docker build ───────────────────────────────────────────────────────────
|
||||
log "Building Docker image..."
|
||||
docker compose build --pull
|
||||
ok "Docker image built"
|
||||
|
|
@ -103,10 +104,7 @@ log "Building and extracting frontend..."
|
|||
TMPDIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TMPDIR"; docker rmi "$FE_BUILD_TAG" >/dev/null 2>&1 || true' EXIT
|
||||
|
||||
# Build only the Node.js frontend-builder stage (no Python/LibreOffice — fast)
|
||||
docker build --target frontend-builder --tag "$FE_BUILD_TAG" "$APP_DIR"
|
||||
|
||||
# Copy dist/ out of the image via a throwaway container
|
||||
EXTRACT_CONTAINER=$(docker create "$FE_BUILD_TAG")
|
||||
docker cp "$EXTRACT_CONTAINER:/app/frontend/dist/." "$TMPDIR/"
|
||||
docker rm "$EXTRACT_CONTAINER" >/dev/null
|
||||
|
|
@ -117,20 +115,32 @@ ok "Frontend built: $FILE_COUNT files"
|
|||
# ── 8. Deploy frontend static files ───────────────────────────────────────────
|
||||
log "Deploying frontend to $WEB_DIR..."
|
||||
mkdir -p "$WEB_DIR"
|
||||
# Wipe old files (preserve directory itself so Apache doesn't break mid-deploy)
|
||||
find "${WEB_DIR}" -mindepth 1 -delete
|
||||
cp -r "$TMPDIR/." "$WEB_DIR/"
|
||||
chown -R "$WEB_USER:$WEB_USER" "$WEB_DIR"
|
||||
chmod -R 755 "$WEB_DIR"
|
||||
ok "Frontend deployed to $WEB_DIR"
|
||||
|
||||
# ── 9. Restart container ──────────────────────────────────────────────────────
|
||||
log "Restarting application container..."
|
||||
# ── 9. Restart containers ─────────────────────────────────────────────────────
|
||||
log "Restarting containers (app + postgres)..."
|
||||
docker compose down --remove-orphans 2>/dev/null || true
|
||||
docker compose up -d
|
||||
ok "Container started"
|
||||
ok "Containers started"
|
||||
|
||||
# ── 10. Health check ──────────────────────────────────────────────────────────
|
||||
# ── 10. Wait for PostgreSQL ────────────────────────────────────────────────────
|
||||
log "Waiting for PostgreSQL to be ready..."
|
||||
for i in $(seq 1 30); do
|
||||
if docker exec "$DB_CONTAINER_NAME" pg_isready -U achelper -d achelper >/dev/null 2>&1; then
|
||||
ok "PostgreSQL is ready"
|
||||
break
|
||||
fi
|
||||
if [[ $i -eq 30 ]]; then
|
||||
die "PostgreSQL did not become ready after 60s. Check logs: docker logs $DB_CONTAINER_NAME"
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# ── 11. Health check ──────────────────────────────────────────────────────────
|
||||
log "Waiting for application to be healthy..."
|
||||
HEALTH_URL="http://localhost:${APP_PORT}/health"
|
||||
for i in $(seq 1 30); do
|
||||
|
|
@ -144,18 +154,43 @@ for i in $(seq 1 30); do
|
|||
sleep 2
|
||||
done
|
||||
|
||||
# ── 11. First-run initialisation ──────────────────────────────────────────────
|
||||
# ── 12. Data directory ────────────────────────────────────────────────────────
|
||||
DATA_DIR="$APP_DIR/data"
|
||||
if [[ ! -f "$DATA_DIR/users.json" ]]; then
|
||||
log "First run — initialising data directory..."
|
||||
mkdir -p "$DATA_DIR/uploads" "$DATA_DIR/outputs" "$DATA_DIR/sheets"
|
||||
# Trigger the app's startup seeding (dropdowns.json + users.json are created
|
||||
# automatically on the first API request if they don't exist)
|
||||
curl -sf "http://localhost:${APP_PORT}/api/dropdowns/categories" >/dev/null 2>&1 || true
|
||||
ok "Data directory initialised at $DATA_DIR"
|
||||
mkdir -p "$DATA_DIR/uploads" "$DATA_DIR/outputs"
|
||||
ok "Data directories ready at $DATA_DIR"
|
||||
|
||||
# ── 13. JSON → PostgreSQL migration (first deploy after adding Postgres) ───────
|
||||
#
|
||||
# If old JSON data files exist, offer to run the one-time migration script
|
||||
# that imports users, clients, dropdowns, sheets, and export templates into DB.
|
||||
#
|
||||
JSON_MIGRATION_MARKER="$DATA_DIR/.pg_migrated"
|
||||
if [[ ! -f "$JSON_MIGRATION_MARKER" ]]; then
|
||||
HAS_JSON=false
|
||||
for f in "$DATA_DIR/users.json" "$DATA_DIR/sheets_metadata.json" "$DATA_DIR/clients.json"; do
|
||||
[[ -f "$f" ]] && HAS_JSON=true && break
|
||||
done
|
||||
|
||||
if [[ "$HAS_JSON" == "true" ]]; then
|
||||
echo ""
|
||||
warn "Old JSON data files detected. Run the one-time migration to import them into PostgreSQL?"
|
||||
warn " yes — migrate now (recommended)"
|
||||
warn " no — skip (data already in DB or you'll migrate manually)"
|
||||
read -r -p " Migrate now? [yes/no]: " MIGRATE_ANSWER </dev/tty
|
||||
if [[ "${MIGRATE_ANSWER,,}" == "yes" ]]; then
|
||||
log "Running JSON → PostgreSQL migration..."
|
||||
docker exec "$CONTAINER_NAME" python -m server.db.migrate_json \
|
||||
&& touch "$JSON_MIGRATION_MARKER" \
|
||||
&& ok "Migration complete. Marker written to $JSON_MIGRATION_MARKER" \
|
||||
|| warn "Migration reported errors — check logs above. Re-run manually with: docker exec $CONTAINER_NAME python -m server.db.migrate_json"
|
||||
else
|
||||
warn "Skipped. To migrate manually: docker exec $CONTAINER_NAME python -m server.db.migrate_json"
|
||||
warn "To suppress this prompt in future deployments, create: $JSON_MIGRATION_MARKER"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── 12. Apache config reminder ────────────────────────────────────────────────
|
||||
# ── 14. Apache config reminder ────────────────────────────────────────────────
|
||||
if ! grep -rq "ac-helper" /etc/apache2/sites-enabled/ 2>/dev/null; then
|
||||
echo ""
|
||||
hr
|
||||
|
|
@ -191,9 +226,10 @@ fi
|
|||
# ── Summary ───────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
ok "Deployment complete!"
|
||||
echo " Container: docker logs -f $CONTAINER_NAME"
|
||||
echo " Health: $HEALTH_URL"
|
||||
echo " Frontend: $WEB_DIR ($FILE_COUNT files)"
|
||||
echo " Data: $DATA_DIR"
|
||||
echo " Commit: $(git -C "$APP_DIR" rev-parse --short HEAD)"
|
||||
echo " App container: docker logs -f $CONTAINER_NAME"
|
||||
echo " DB container: docker logs -f $DB_CONTAINER_NAME"
|
||||
echo " Health: $HEALTH_URL"
|
||||
echo " Frontend: $WEB_DIR ($FILE_COUNT files)"
|
||||
echo " Data: $DATA_DIR"
|
||||
echo " Commit: $(git -C "$APP_DIR" rev-parse --short HEAD)"
|
||||
echo ""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue