#!/bin/sh set -e PREFIX="[nocodb-init]" # ─── Utilities ──────────────────────────────────────────────────────────────── log() { echo "$PREFIX $1"; } fail() { echo "$PREFIX ERROR: $1" >&2; exit 1; } api() { # api METHOD /path [data] METHOD="$1"; ENDPOINT="$2"; DATA="$3" if [ -n "$DATA" ]; then curl -sf -X "$METHOD" "${NOCODB_URL}${ENDPOINT}" \ -H "Content-Type: application/json" \ -H "xc-auth: ${TOKEN}" \ -d "$DATA" else curl -sf -X "$METHOD" "${NOCODB_URL}${ENDPOINT}" \ -H "Content-Type: application/json" \ -H "xc-auth: ${TOKEN}" fi } # ─── Install dependencies ───────────────────────────────────────────────────── apk add --no-cache curl jq >/dev/null 2>&1 log "Dependencies installed" # ─── Step 1: Authenticate ───────────────────────────────────────────────────── log "Authenticating with NocoDB..." TOKEN="" for i in $(seq 1 15); do RESPONSE=$(curl -sf -X POST "${NOCODB_URL}/api/v1/auth/user/signin" \ -H "Content-Type: application/json" \ -d "{\"email\":\"${NC_ADMIN_EMAIL}\",\"password\":\"${NC_ADMIN_PASSWORD}\"}" 2>/dev/null) || true if [ -n "$RESPONSE" ]; then TOKEN=$(echo "$RESPONSE" | jq -r '.token // empty') if [ -n "$TOKEN" ]; then break fi fi log " Waiting for NocoDB to be ready... (attempt $i/15)" sleep 3 done if [ -z "$TOKEN" ]; then fail "Could not authenticate with NocoDB after 15 attempts" fi log "Authenticated" # ─── Step 2: Check for existing base ────────────────────────────────────────── BASE_NAME="Changemaker Platform" INTEGRATION_NAME="Changemaker PostgreSQL" log "Checking for existing base..." BASES=$(api GET "/api/v2/meta/bases/") EXISTING_BASE_ID=$(echo "$BASES" | jq -r --arg name "$BASE_NAME" '.list[] | select(.title == $name) | .id // empty' 2>/dev/null) if [ -n "$EXISTING_BASE_ID" ]; then # Base exists — check if it has tables (fully synced) TABLES=$(api GET "/api/v2/meta/bases/${EXISTING_BASE_ID}/tables") TABLE_COUNT=$(echo "$TABLES" | jq -r '.list | length' 2>/dev/null) if [ "$TABLE_COUNT" -gt 0 ] 2>/dev/null; then log "Base '${BASE_NAME}' already exists with ${TABLE_COUNT} tables. Nothing to do." exit 0 fi # Check if an integration-linked source already exists (sync may still be in progress) # NocoDB auto-creates a default source (fk_integration_id=null, is_local=true) — ignore it SOURCES=$(api GET "/api/v2/meta/bases/${EXISTING_BASE_ID}/sources" 2>/dev/null) || true HAS_REAL_SOURCE=$(echo "$SOURCES" | jq -r '[.list[] | select(.fk_integration_id != null)] | length' 2>/dev/null) if [ "$HAS_REAL_SOURCE" -gt 0 ] 2>/dev/null; then log "Base has an integration source — table sync in progress, waiting..." for i in $(seq 1 30); do TABLES=$(api GET "/api/v2/meta/bases/${EXISTING_BASE_ID}/tables" 2>/dev/null) || true TABLE_COUNT=$(echo "$TABLES" | jq -r '.list | length' 2>/dev/null) if [ "$TABLE_COUNT" -gt 0 ] 2>/dev/null; then log "Table sync complete: ${TABLE_COUNT} tables discovered" exit 0 fi sleep 3 done log "Source exists, tables will appear eventually. Nothing more to do." exit 0 fi log "Base exists but has no integration source — will add one" fi # ─── Step 3: Create or reuse integration ────────────────────────────────────── log "Checking for existing integration..." INTEGRATIONS=$(api GET "/api/v2/meta/integrations") INTEGRATION_ID=$(echo "$INTEGRATIONS" | jq -r --arg name "$INTEGRATION_NAME" '.list[] | select(.title == $name) | .id // empty' 2>/dev/null) if [ -z "$INTEGRATION_ID" ]; then log "Creating integration '${INTEGRATION_NAME}'..." INTEGRATION_PAYLOAD=$(cat </dev/null log "Source added — table discovery started" # ─── Step 5: Wait for table sync ───────────────────────────────────────────── log "Waiting for table discovery..." for i in $(seq 1 30); do TABLES=$(api GET "/api/v2/meta/bases/${BASE_ID}/tables" 2>/dev/null) || true TABLE_COUNT=$(echo "$TABLES" | jq -r '.list | length' 2>/dev/null) if [ "$TABLE_COUNT" -gt 0 ] 2>/dev/null; then log "Table sync complete: ${TABLE_COUNT} tables discovered" break fi if [ "$i" = "30" ]; then log "Table sync still in progress after 90s — tables will appear eventually" break fi sleep 3 done # ─── Step 6: Clean up default empty base ────────────────────────────────────── log "Checking for default empty base..." # Re-fetch bases list (may have changed since step 2) BASES=$(api GET "/api/v2/meta/bases/") DEFAULT_BASE_ID=$(echo "$BASES" | jq -r '.list[] | select(.title == "Base") | .id // empty' 2>/dev/null) if [ -n "$DEFAULT_BASE_ID" ] && [ "$DEFAULT_BASE_ID" != "$BASE_ID" ]; then log "Deleting default empty base..." api DELETE "/api/v2/meta/bases/${DEFAULT_BASE_ID}" >/dev/null 2>&1 || true log "Default base cleaned up" fi log "Done"