#!/bin/sh # Proof of Work (proof-of-work) — StartOS 0.4 container entrypoint. # # Responsibilities (in order): # 1. Ensure the persistent /data directory exists. # 2. Seed /data on truly-fresh first boot: # a. If /app/seed/data/app.db is present (v1.0.0:1 cutover image), # copy it into /data. This is how an operator migrating from # the legacy `workout-log` package preserves their full history. # b. Otherwise fall back to the empty-schema fallback DB at # /app/prisma/data/app.db (already seeded with the curated # exercise library at build time). # Both branches are gated on /data/app.db being absent AND # /data/.seeded being absent — never overwrites live data. # 3. Run idempotent compat ALTERs for columns added after older snapshots. # No-ops on hosts whose schema is already current. # 4. Ensure the curated exercise library is present for every user. New # maintainer-shipped exercises appear on every boot (INSERT OR IGNORE # keyed on (userId, name); never overwrites a user's own exercises). # 5. Exec the Next.js standalone server as PID 1 (under dumb-init). # # Every branch logs to stderr so it is visible in StartOS -> Logs. set -eu DATA_DIR="${WORKOUT_DATA_DIR:-/data}" DB_PATH="${WORKOUT_DB_PATH:-$DATA_DIR/app.db}" FALLBACK_SEED_DB_PATH="${WORKOUT_FALLBACK_SEED_DB_PATH:-/app/prisma/data/app.db}" BAKED_SEED_DB_PATH="${WORKOUT_BAKED_SEED_DB_PATH:-/app/seed/data/app.db}" LIBRARY_JSON_PATH="${WORKOUT_LIBRARY_JSON_PATH:-/app/prisma/exercises.seed.json}" log() { # write to stderr so StartOS log viewer surfaces it immediately printf '[entrypoint] %s\n' "$*" 1>&2 } mkdir -p "$DATA_DIR" # ----------------------------------------------------------------------------- # Step 1 — first-boot seeding. NEVER overwrites an existing app.db. # Both branches are skipped on every restart of an installed host because # /data/app.db already exists. # ----------------------------------------------------------------------------- if [ ! -f "$DB_PATH" ] && [ ! -f "$DATA_DIR/.seeded" ]; then if [ -f "$BAKED_SEED_DB_PATH" ]; then log "no $DB_PATH and no .seeded marker; copying baked cutover seed from $BAKED_SEED_DB_PATH" cp "$BAKED_SEED_DB_PATH" "$DB_PATH" date -u +"seeded from baked cutover snapshot at %Y-%m-%dT%H:%M:%SZ" > "$DATA_DIR/.seeded" elif [ -f "$FALLBACK_SEED_DB_PATH" ]; then log "no $DB_PATH and no baked seed; copying empty-schema fallback from $FALLBACK_SEED_DB_PATH" cp "$FALLBACK_SEED_DB_PATH" "$DB_PATH" date -u +"seeded from empty-schema fallback at %Y-%m-%dT%H:%M:%SZ" > "$DATA_DIR/.seeded" else log "no $DB_PATH, no baked seed, no fallback DB; creating empty $DB_PATH" touch "$DB_PATH" fi else log "$DB_PATH already present; live data is the source of truth" if [ -f "$DATA_DIR/.seeded" ]; then log "found .seeded: $(cat "$DATA_DIR/.seeded")" fi fi # ----------------------------------------------------------------------------- # Step 2 — idempotent compat ALTERs (safety net for older snapshots). # No-ops on hosts whose schema is already current. # ----------------------------------------------------------------------------- if command -v sqlite3 >/dev/null 2>&1 && [ -f "$DB_PATH" ]; then if ! sqlite3 "$DB_PATH" "PRAGMA table_info('SetLog');" 2>/dev/null | grep -q "|customMetrics|"; then log "adding missing column SetLog.customMetrics" sqlite3 "$DB_PATH" "ALTER TABLE SetLog ADD COLUMN customMetrics TEXT;" fi if ! sqlite3 "$DB_PATH" "PRAGMA table_info('Workout');" 2>/dev/null | grep -q "|deletedAt|"; then log "adding missing column Workout.deletedAt" sqlite3 "$DB_PATH" "ALTER TABLE Workout ADD COLUMN deletedAt DATETIME;" fi fi # ----------------------------------------------------------------------------- # Step 3 — ensure curated exercise library for every user (multi-user-aware). # New entries shipped in /app/prisma/exercises.seed.json appear on every boot. # `INSERT OR IGNORE` keyed on (userId, name) so we never overwrite a user's # own custom exercises. Designed to be additive only — exercises removed from # the curated JSON are not deleted from existing installs (users may have # logged sets against them). # ----------------------------------------------------------------------------- if [ -f "$LIBRARY_JSON_PATH" ] && [ -f "$DB_PATH" ]; then log "ensuring curated exercise library is present for every user" node /app/prisma/ensureExerciseLibrary.cjs \ --db "$DB_PATH" \ --json "$LIBRARY_JSON_PATH" \ || log "WARNING: ensureExerciseLibrary failed; continuing boot" else log "skipping library ensure (json or db not found)" fi # ----------------------------------------------------------------------------- # Step 4 — launch the app. # ----------------------------------------------------------------------------- export DATABASE_URL="file:$DB_PATH" export NODE_ENV="${NODE_ENV:-production}" export HOSTNAME="${HOSTNAME:-0.0.0.0}" export PORT="${PORT:-3000}" log "launching Next.js on :${PORT} with DATABASE_URL=file:${DB_PATH}" exec node /app/server.js