2026-06-23 23:54:37 +00:00
|
|
|
#!/bin/bash
|
|
|
|
|
set -e
|
|
|
|
|
|
2026-06-25 13:59:51 +00:00
|
|
|
wait_for_postgres() {
|
|
|
|
|
if [ -z "$DATABASE_URL" ]; then
|
|
|
|
|
return 0
|
|
|
|
|
fi
|
|
|
|
|
echo "ctxd: waiting for PostgreSQL (DATABASE_URL is set)..."
|
|
|
|
|
python3 <<'PY'
|
|
|
|
|
import os, sys, time
|
|
|
|
|
|
|
|
|
|
url = os.environ.get("DATABASE_URL", "")
|
|
|
|
|
max_wait = int(os.environ.get("CTXD_PG_WAIT_SECONDS", "120"))
|
|
|
|
|
interval = int(os.environ.get("CTXD_PG_WAIT_INTERVAL", "2"))
|
|
|
|
|
deadline = time.time() + max_wait
|
|
|
|
|
last_err = ""
|
|
|
|
|
attempt = 0
|
|
|
|
|
while time.time() < deadline:
|
|
|
|
|
attempt += 1
|
|
|
|
|
try:
|
|
|
|
|
import psycopg
|
|
|
|
|
conn = psycopg.connect(url, connect_timeout=5)
|
|
|
|
|
conn.close()
|
|
|
|
|
print("ctxd: PostgreSQL is ready")
|
|
|
|
|
sys.exit(0)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
last_err = str(e)
|
|
|
|
|
if attempt == 1 or attempt % 10 == 0:
|
|
|
|
|
print(f"ctxd: postgres not ready (attempt {attempt}): {last_err}", file=sys.stderr)
|
|
|
|
|
time.sleep(interval)
|
|
|
|
|
|
|
|
|
|
print("ctxd: FATAL: PostgreSQL not reachable within wait window.", file=sys.stderr)
|
|
|
|
|
print(f"ctxd: last error: {last_err}", file=sys.stderr)
|
|
|
|
|
print("ctxd:", file=sys.stderr)
|
|
|
|
|
print("ctxd: Fix: start the full stack (postgres + ctxd), e.g.", file=sys.stderr)
|
|
|
|
|
print("ctxd: cd app && docker compose up -d postgres ctxd", file=sys.stderr)
|
|
|
|
|
print("ctxd:", file=sys.stderr)
|
|
|
|
|
print("ctxd: Avoid: docker compose up -d --no-deps ctxd (skips postgres)", file=sys.stderr)
|
|
|
|
|
print("ctxd: If postgres is on another host, fix DATABASE_URL / network.", file=sys.stderr)
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
PY
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wait_for_postgres
|
|
|
|
|
|
2026-06-25 00:20:55 +00:00
|
|
|
# Initialize if needed
|
|
|
|
|
if [ -n "$DATABASE_URL" ]; then
|
|
|
|
|
# PostgreSQL mode — check if schema exists
|
|
|
|
|
NEEDS_INIT=$(python3 -c "
|
|
|
|
|
import psycopg, os, sys
|
|
|
|
|
try:
|
|
|
|
|
conn = psycopg.connect(os.environ['DATABASE_URL'])
|
|
|
|
|
cur = conn.execute(\"SELECT EXISTS (SELECT FROM pg_tables WHERE schemaname='public' AND tablename='users')\")
|
|
|
|
|
if not cur.fetchone()[0]:
|
|
|
|
|
print('yes')
|
|
|
|
|
else:
|
|
|
|
|
print('no')
|
|
|
|
|
except Exception:
|
|
|
|
|
print('yes')
|
|
|
|
|
" 2>/dev/null || echo "yes")
|
|
|
|
|
if [ "$NEEDS_INIT" = "yes" ]; then
|
|
|
|
|
echo "ctxd: initializing PostgreSQL database"
|
|
|
|
|
python3 -m ctxd init --home "$CTXD_HOME"
|
2026-06-25 14:43:58 +00:00
|
|
|
else
|
|
|
|
|
echo "ctxd: running pending migrations"
|
|
|
|
|
python3 -m ctxd.migrate_user_fk_set_null 2>&1 || echo "ctxd: migration warning (non-fatal)"
|
2026-06-25 00:20:55 +00:00
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
# SQLite mode — check for db file
|
|
|
|
|
if [ ! -f "$CTXD_HOME/ctxd.db" ]; then
|
|
|
|
|
echo "ctxd: initializing database at $CTXD_HOME"
|
|
|
|
|
python3 -m ctxd init --home "$CTXD_HOME"
|
|
|
|
|
fi
|
2026-06-23 23:54:37 +00:00
|
|
|
fi
|
|
|
|
|
|
2026-06-25 00:20:55 +00:00
|
|
|
echo "ctxd: starting daemon on ${CTXD_HOST:-0.0.0.0}:${CTXD_PORT:-9091}"
|
2026-06-23 23:54:37 +00:00
|
|
|
exec python3 -m ctxd
|