#!/bin/sh # Startup diagnostics for Coolify / Docker Compose deploys. # Logs to stdout (Coolify log viewer) and appends to /app/logs/startup.log # (bind-mounted volume — survives container prune on the host). set -u LOG_DIR=/app/logs LOG_FILE="$LOG_DIR/startup.log" SERVICE="${ASKIMUT_SERVICE:-unknown}" mkdir -p "$LOG_DIR" 2>/dev/null || true # Bind mounts are often root-owned on first create; ensure node can write. if [ "$(id -u)" = "0" ]; then chown -R node:node "$LOG_DIR" 2>/dev/null || true fi log() { line="$*" printf '%s\n' "$line" printf '%s\n' "$line" >>"$LOG_FILE" 2>/dev/null || true } check_set() { var=$1 eval "val=\${$var:-}" if [ -z "$val" ]; then log "ERROR: $var is unset" return 1 fi log "OK: $var is set" return 0 } missing=0 log "=== askimut startup $(date -Iseconds) service=$SERVICE ===" check_set TURSO_DATABASE_URL || missing=1 check_set TURSO_AUTH_TOKEN || missing=1 if [ "$SERVICE" = "web" ]; then check_set PUBLIC_URL || missing=1 log "PUBLIC_URL=$PUBLIC_URL" log "HOST=${HOST:-0.0.0.0} PORT=${PORT:-3000}" case "$PUBLIC_URL" in http://127.0.0.1:*|http://localhost:*|http://[[]::1]:*) ;; http://*) log "ERROR: PUBLIC_URL must use https:// for OAuth (AT Protocol rejects http on public hostnames)" log "ERROR: set PUBLIC_URL to your Coolify HTTPS URL, e.g. https://your-domain.example.com" missing=1 ;; esac fi if [ "$SERVICE" = "ingester" ]; then log "JETSTREAM_URL=${JETSTREAM_URL:-}" fi if [ "$missing" -ne 0 ]; then log "FATAL: missing required env — container will exit" exit 1 fi run_migrations() { log "Running database migrations..." if [ "$(id -u)" = "0" ]; then runuser -u node -- node .output/server/migrate.mjs code=$? else node .output/server/migrate.mjs code=$? fi if [ "$code" -ne 0 ]; then log "FATAL: migrations failed with code $code" else log "Migrations complete" fi return "$code" } run_publish_lexicons() { require_creds=${1:-0} if [ -z "${LEXICON_AUTHORITY_IDENTIFIER:-}" ] || [ -z "${LEXICON_AUTHORITY_PASSWORD:-}" ]; then if [ "$require_creds" = "1" ]; then log "FATAL: LEXICON_AUTHORITY_IDENTIFIER and LEXICON_AUTHORITY_PASSWORD required" return 1 fi log "SKIP: lexicon publish (set LEXICON_AUTHORITY_IDENTIFIER + LEXICON_AUTHORITY_PASSWORD; also needs _lexicon.askimut.at DNS)" return 0 fi log "Publishing lexicons to the network..." if [ "$(id -u)" = "0" ]; then runuser -u node -- node .output/server/publish-lexicons.mjs code=$? else node .output/server/publish-lexicons.mjs code=$? fi if [ "$code" -ne 0 ]; then log "FATAL: lexicon publish failed with code $code" else log "Lexicon publication complete" fi return "$code" } # Manual one-shot: docker compose run --rm -e ASKIMUT_SERVICE=migrate web if [ "$SERVICE" = "migrate" ]; then run_migrations exit $? fi if [ "$SERVICE" = "publish-lexicons" ]; then run_publish_lexicons 1 exit $? fi # Web runs migrations before serving. Avoid a separate one-shot Compose service: # Coolify treats exited migrate containers as an unhealthy stack and returns 502 # for the proxied domain even with exclude_from_hc (coollabsio/coolify#6591). if [ "$SERVICE" = "web" ]; then run_migrations || exit $? run_publish_lexicons 0 || exit $? fi log "exec: $*" if [ "$(id -u)" = "0" ]; then # Drop to node for the app process; capture exit code for the log file. runuser -u node -- "$@" code=$? else set +e "$@" code=$? fi if [ "$code" -ne 0 ]; then log "FATAL: process exited with code $code" fi exit "$code"