[BACKEND] Initialize Go backend structure and dependencies (#23)
This commit is contained in:
61
scripts/db-bootstrap.sh
Normal file
61
scripts/db-bootstrap.sh
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Chat Switchboard - Database Bootstrap
|
||||
# ============================================
|
||||
# Idempotent: safe to run on every CI build.
|
||||
# Creates the app role and database if they
|
||||
# don't exist. Runs as the Postgres superuser.
|
||||
#
|
||||
# Required env vars:
|
||||
# PGHOST, PGPORT, PGUSER, PGPASSWORD — admin connection
|
||||
# APP_USER, APP_PASSWORD — app-level credentials
|
||||
# DB_NAME — target database name
|
||||
# ============================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Database bootstrap: ${DB_NAME}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# ── 1. Create role if not exists ─────────────
|
||||
ROLE_EXISTS=$(psql -tAc "SELECT 1 FROM pg_roles WHERE rolname = '${APP_USER}';" postgres)
|
||||
|
||||
if [[ "${ROLE_EXISTS}" == "1" ]]; then
|
||||
echo "✓ Role '${APP_USER}' already exists"
|
||||
# Update password in case it changed
|
||||
psql -c "ALTER ROLE ${APP_USER} WITH PASSWORD '${APP_PASSWORD}';" postgres
|
||||
echo "✓ Password synced"
|
||||
else
|
||||
psql -c "CREATE ROLE ${APP_USER} WITH LOGIN PASSWORD '${APP_PASSWORD}';" postgres
|
||||
echo "✓ Role '${APP_USER}' created"
|
||||
fi
|
||||
|
||||
# ── 2. Create database if not exists ─────────
|
||||
DB_EXISTS=$(psql -tAc "SELECT 1 FROM pg_database WHERE datname = '${DB_NAME}';" postgres)
|
||||
|
||||
if [[ "${DB_EXISTS}" == "1" ]]; then
|
||||
echo "✓ Database '${DB_NAME}' already exists"
|
||||
else
|
||||
psql -c "CREATE DATABASE ${DB_NAME} OWNER ${APP_USER};" postgres
|
||||
echo "✓ Database '${DB_NAME}' created"
|
||||
fi
|
||||
|
||||
# ── 3. Ensure extensions (requires superuser) ─
|
||||
psql -d "${DB_NAME}" <<'SQL'
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
CREATE EXTENSION IF NOT EXISTS "vector";
|
||||
SQL
|
||||
echo "✓ Extensions verified (uuid-ossp, pgcrypto)"
|
||||
|
||||
# ── 4. Grant privileges ─────────────────────
|
||||
psql -d "${DB_NAME}" -c "GRANT ALL PRIVILEGES ON DATABASE ${DB_NAME} TO ${APP_USER};"
|
||||
psql -d "${DB_NAME}" -c "GRANT ALL PRIVILEGES ON SCHEMA public TO ${APP_USER};"
|
||||
psql -d "${DB_NAME}" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${APP_USER};"
|
||||
psql -d "${DB_NAME}" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${APP_USER};"
|
||||
echo "✓ Privileges granted"
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Bootstrap complete: ${DB_NAME}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
91
scripts/db-migrate.sh
Normal file
91
scripts/db-migrate.sh
Normal file
@@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Chat Switchboard - Database Migration Runner
|
||||
# ============================================
|
||||
# Tracks applied migrations in schema_migrations.
|
||||
# In dev (DB_WIPE=true): drops all tables, re-runs
|
||||
# all migrations for a clean slate every PR build.
|
||||
#
|
||||
# Required env vars:
|
||||
# PGHOST, PGPORT, PGUSER, PGPASSWORD — app-level connection
|
||||
# DB_NAME — target database
|
||||
# DB_WIPE — "true" to wipe first (dev only)
|
||||
# ============================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MIGRATIONS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../migrations" && pwd)"
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Migration runner: ${DB_NAME}"
|
||||
echo " Wipe mode: ${DB_WIPE}"
|
||||
echo " Migrations: ${MIGRATIONS_DIR}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
export PGDATABASE="${DB_NAME}"
|
||||
|
||||
# ── Dev wipe: drop everything and start fresh ─
|
||||
if [[ "${DB_WIPE}" == "true" ]]; then
|
||||
echo "⚠ Dev wipe: dropping all objects..."
|
||||
psql <<'SQL'
|
||||
DO $$ DECLARE
|
||||
r RECORD;
|
||||
BEGIN
|
||||
-- Drop all views
|
||||
FOR r IN (SELECT viewname FROM pg_views WHERE schemaname = 'public') LOOP
|
||||
EXECUTE 'DROP VIEW IF EXISTS public.' || quote_ident(r.viewname) || ' CASCADE';
|
||||
END LOOP;
|
||||
-- Drop all tables (including schema_migrations for clean slate)
|
||||
FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP
|
||||
EXECUTE 'DROP TABLE IF EXISTS public.' || quote_ident(r.tablename) || ' CASCADE';
|
||||
END LOOP;
|
||||
-- Drop all functions (skip extension-owned like uuid_nil etc)
|
||||
FOR r IN (SELECT ns.nspname || '.' || p.proname || '(' || pg_get_function_identity_arguments(p.oid) || ')' AS func
|
||||
FROM pg_proc p JOIN pg_namespace ns ON p.pronamespace = ns.oid
|
||||
WHERE ns.nspname = 'public'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pg_depend d
|
||||
WHERE d.objid = p.oid AND d.deptype = 'e'
|
||||
)) LOOP
|
||||
EXECUTE 'DROP FUNCTION IF EXISTS ' || r.func || ' CASCADE';
|
||||
END LOOP;
|
||||
END $$;
|
||||
SQL
|
||||
echo "✓ All public schema objects dropped"
|
||||
fi
|
||||
|
||||
# ── Ensure tracking table exists ──────────────
|
||||
psql <<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
version VARCHAR(255) PRIMARY KEY,
|
||||
applied_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
SQL
|
||||
|
||||
# ── Apply pending migrations ──────────────────
|
||||
APPLIED=0
|
||||
SKIPPED=0
|
||||
|
||||
for migration in "${MIGRATIONS_DIR}"/*.sql; do
|
||||
[[ -f "${migration}" ]] || continue
|
||||
|
||||
VERSION=$(basename "${migration}")
|
||||
|
||||
ALREADY=$(psql -tAc "SELECT 1 FROM schema_migrations WHERE version = '${VERSION}';")
|
||||
|
||||
if [[ "${ALREADY}" == "1" ]]; then
|
||||
echo " skip: ${VERSION} (already applied)"
|
||||
SKIPPED=$((SKIPPED + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
echo " apply: ${VERSION}..."
|
||||
psql -v ON_ERROR_STOP=1 -f "${migration}"
|
||||
psql -c "INSERT INTO schema_migrations (version) VALUES ('${VERSION}');"
|
||||
APPLIED=$((APPLIED + 1))
|
||||
echo " ✓ ${VERSION} applied"
|
||||
done
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Migrations complete: ${APPLIED} applied, ${SKIPPED} skipped"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
28
scripts/docker-entrypoint.sh
Normal file
28
scripts/docker-entrypoint.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Chat Switchboard - Backend Launcher
|
||||
# ============================================
|
||||
# Runs as an nginx entrypoint.d hook.
|
||||
# Starts the Go backend in the background
|
||||
# before nginx begins accepting connections.
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔀 Starting Chat Switchboard backend..."
|
||||
|
||||
# Launch Go backend in background
|
||||
/usr/local/bin/switchboard &
|
||||
BACKEND_PID=$!
|
||||
|
||||
# Wait for backend to be ready (max 10s)
|
||||
for i in $(seq 1 20); do
|
||||
if wget -q --spider http://localhost:${PORT:-8080}/health 2>/dev/null; then
|
||||
echo "✅ Backend ready (PID ${BACKEND_PID})"
|
||||
exit 0
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
echo "❌ Backend failed to start within 10s"
|
||||
exit 1
|
||||
Reference in New Issue
Block a user