Files
ttrpg-initiative-tracker/scripts/dev-start.sh
T
david raistrick 055895875a Dev-only bulk delete all campaigns; deleteCollection SQL bulk + gates
Feature: debug button to wipe all campaigns/encounters/logs in dev builds.
Previously bulk delete fetched all logs per campaign, client-filtered,
batchWrite — 30s+/campaign. Now SQL bulk DELETE, no fetch.

Server (server/db.js, server/index.js):
- deleteCollection(collPath, {where}) — SQL DELETE FROM docs WHERE parent=?,
  optional where-filter. Broadcasts deletions to WS subscribers.
- DELETE /api/collection endpoint
- Gate: ALLOW_DEV_ENDPOINTS=1 env OR createServer({allowDevEndpoints:true})
- createServer accepts allowDevEndpoints param (tests bypass env)

Storage (src/storage/server.js, src/storage/firebase.js):
- deleteCollection(path, whereField, whereValue) both adapters
- Firebase: fetch matching + batch-delete (firestore no bulk), 500-chunk
- Gate: throws if NODE_ENV not development/test
- Contract-tested both backends

App (src/App.js):
- deleteCampaignCascade refactored (reusable, no try/catch split)
- handleDeleteAllCampaigns: Promise.all per campaign, deleteCollection for
  encounters (no fetch), deleteCollection logs once globally, parallel
- Button dev-gated (NODE_ENV), confirm modal, hidden when no campaigns

Mock fixes (surfaced by new tests):
- firebase firestore mock: added where() export, getDocs applies constraints
  (was returning all docs ignoring query constraints — pre-existing gap)

Tests:
- contract: deleteCollection (bulk, where-filter, empty) both backends
- server-contract: live deleteCollection (bulk, where, 403 gate)
- runStorageContract via makeStorage({allowDevEndpoints:true})

Safety (3 layers):
- UI button hidden in prod (NODE_ENV gate)
- storage method throws in prod (NODE_ENV gate)
- HTTP endpoint 403 in prod (env/param gate)
2026-07-06 19:06:52 -04:00

55 lines
1.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Start local dev stack: node backend (sqlite) + react frontend, server storage mode.
# Usage: ./scripts/dev-start.sh
# Stop: ./scripts/dev-stop.sh
set -euo pipefail
cd "$(dirname "$0")/.."
mkdir -p tmp data
# kill anything on the ports (zombies)
for port in 3999 4001; do
pids=$(lsof -ti :$port 2>/dev/null || true)
if [ -n "$pids" ]; then
echo "port $port in use by: $pids — leaving as-is."
echo " (run ./scripts/dev-stop.sh first to restart clean)"
fi
done
# backend: better-sqlite3, :4001
if ! lsof -ti :4001 >/dev/null 2>&1; then
echo "starting backend :4001..."
DB_PATH=$(pwd)/data/tracker.sqlite PORT=4001 ALLOW_DEV_ENDPOINTS=1 \
nohup npm run server:dev > tmp/server.log 2>&1 &
echo $! > tmp/server.pid
else
echo "backend already on :4001"
fi
# frontend: server storage, :3999
if ! lsof -ti :3999 >/dev/null 2>&1; then
echo "starting frontend :3999..."
REACT_APP_STORAGE=server \
REACT_APP_BACKEND_URL=http://127.0.0.1:4001 \
REACT_APP_BACKEND_REALTIME_URL=ws://127.0.0.1:4001/ws \
BROWSER=none PORT=3999 \
nohup npm start > tmp/fe.log 2>&1 &
echo $! > tmp/fe.pid
else
echo "frontend already on :3999"
fi
# wait for ports to listen
echo "waiting for ports..."
for port in 4001 3999; do
for i in {1..30}; do
lsof -ti :$port >/dev/null 2>&1 && break
sleep 1
done
done
echo ""
echo "backend : http://127.0.0.1:4001 (curl http://127.0.0.1:4001/health)"
echo "frontend : http://127.0.0.1:3999 (admin / player /display)"
echo "logs : tmp/server.log tmp/fe.log"
echo "stop : ./scripts/dev-stop.sh"