Files
Nexus.Reader/run-stage.sh
T
Antigravity c94e8f0acb feat(creator): overhaul Creator flow, editor duplication, and staging setup (#83)
This pull request completely overhauls the Creator editor flow, resolves the editor duplication race condition, aligns layout/styling themes in light and dark mode, and adds Docker staging setups.

### Key Changes
1. **Creator Flow Polish**: Redesigned the editor canvas to prevent double scrolling by delegating overflow to the editor canvas layer, updated styles to a premium aesthetic.
2. **Race Condition Prevention**: Resolved Crepe editor duplication when loading or switching chapters by tracking state via shared window maps (`window.editorCache`, `window.editorStates`) and checking `_lastInitializedEditorId` synchronously in Blazor.
3. **Theme Synchronization**: Integrated explicit theme initialization (`ThemeService.InitializeAsync()`) and anchored CSS isolation selectors to correctly sync with Light (Soft Sepia) and Deep Dark theme preferences.
4. **Staging Automation**: Created staging docker configurations with `--nexus-only` flag to allow iterative development without resetting PG/Neo4j database containers.

---------

Co-authored-by: Marek Jasiński <jasins.marek@gmail.com>
Reviewed-on: #83
Co-authored-by: Antigravity <antigravity@google.com>
Co-committed-by: Antigravity <antigravity@google.com>
2026-06-15 17:15:42 +00:00

155 lines
6.3 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# -------------------------------------------------------------
# Staging Deploy & Orchestration Helper for NexusReader
# -------------------------------------------------------------
set -e
NEXUS_ONLY=false
STOP=false
for arg in "$@"; do
case $arg in
--nexus-only|-n)
NEXUS_ONLY=true
;;
--stop|-s)
STOP=true
;;
esac
done
ENV_FILE=".env.stage"
TEMPLATE_FILE=".env.stage.template"
COMPOSE_FILE="docker-compose.stage.yml"
if [ "$STOP" = true ]; then
echo "🛑 Stopping staging environment..."
if [ ! -f "$ENV_FILE" ] && [ -f "$TEMPLATE_FILE" ]; then
cp "$TEMPLATE_FILE" "$ENV_FILE"
fi
if [ "$NEXUS_ONLY" = true ]; then
echo "🧹 Stopping and removing only the web (nexus) container..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop web || true
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" rm -f web || true
else
echo "🧹 Stopping all containers..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" down --remove-orphans || true
docker compose down --remove-orphans 2>/dev/null || true
fi
echo "✅ Staging environment stopped."
exit 0
fi
echo "🏁 Starting staging environment orchestration..."
if [ "$NEXUS_ONLY" = true ]; then
echo "️ Mode: --nexus-only (only the web/nexus application container will be modified)"
fi
# 1. Create .env.stage if it doesn't exist
if [ ! -f "$ENV_FILE" ]; then
if [ -f "$TEMPLATE_FILE" ]; then
echo "📄 Creating $ENV_FILE from $TEMPLATE_FILE..."
cp "$TEMPLATE_FILE" "$ENV_FILE"
else
echo "❌ Error: Template file $TEMPLATE_FILE not found."
exit 1
fi
fi
# 2. Check and generate secure random passwords for placeholders
if grep -q "CHANGE_ME_TO_STRONG_PASSWORD" "$ENV_FILE"; then
echo "🔐 Generating secure random passwords in $ENV_FILE..."
PG_PASS=$(openssl rand -hex 16)
NEO_PASS=$(openssl rand -hex 16)
# Use standard sed compatible with Linux
sed -i "s/POSTGRES_PASSWORD=CHANGE_ME_TO_STRONG_PASSWORD/POSTGRES_PASSWORD=$PG_PASS/g" "$ENV_FILE"
sed -i "s/NEO4J_PASSWORD=CHANGE_ME_TO_STRONG_PASSWORD/NEO4J_PASSWORD=$NEO_PASS/g" "$ENV_FILE"
fi
if grep -q "CHANGE_ME_TO_SECURE_ADMIN_PASSWORD" "$ENV_FILE"; then
echo "🔐 Generating secure admin seed password in $ENV_FILE..."
ADMIN_PASS=$(openssl rand -hex 16)
sed -i "s/NEXUS_ADMIN_PASSWORD=CHANGE_ME_TO_SECURE_ADMIN_PASSWORD/NEXUS_ADMIN_PASSWORD=$ADMIN_PASS/g" "$ENV_FILE"
fi
if grep -q "^QDRANT_API_KEY=$" "$ENV_FILE" || grep -q "^QDRANT_API_KEY=[[:space:]]*$" "$ENV_FILE"; then
echo "🔐 Generating secure random Qdrant API key in $ENV_FILE..."
QD_KEY=$(openssl rand -hex 16)
sed -i "s/^QDRANT_API_KEY=.*/QDRANT_API_KEY=$QD_KEY/g" "$ENV_FILE"
fi
# Load staging variables for local execution context (needed for ports/migrations)
# Clean up carriage returns just in case
POSTGRES_USER=$(grep "^POSTGRES_USER=" "$ENV_FILE" | cut -d'=' -f2- | tr -d '\r')
POSTGRES_PASSWORD=$(grep "^POSTGRES_PASSWORD=" "$ENV_FILE" | cut -d'=' -f2- | tr -d '\r')
POSTGRES_DB=$(grep "^POSTGRES_DB=" "$ENV_FILE" | cut -d'=' -f2- | tr -d '\r')
POSTGRES_PORT=$(grep "^POSTGRES_PORT=" "$ENV_FILE" | cut -d'=' -f2- | tr -d '\r')
WEB_PORT=$(grep "^WEB_PORT=" "$ENV_FILE" | cut -d'=' -f2- | tr -d '\r')
QDRANT_HTTP_PORT=$(grep "^QDRANT_HTTP_PORT=" "$ENV_FILE" | cut -d'=' -f2- | tr -d '\r')
NEO4J_HTTP_PORT=$(grep "^NEO4J_HTTP_PORT=" "$ENV_FILE" | cut -d'=' -f2- | tr -d '\r')
# Fallbacks in case env parsing is empty
POSTGRES_PORT=${POSTGRES_PORT:-5438}
WEB_PORT=${WEB_PORT:-5080}
# 3. Stop any conflicting Docker Compose environments
if [ "$NEXUS_ONLY" = true ]; then
echo "🧹 Stopping and removing only the web (nexus) container..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop web || true
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" rm -f web || true
else
echo "🧹 Stopping existing containers..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" down --remove-orphans || true
docker compose down --remove-orphans 2>/dev/null || true
fi
# 4. Build and start containers
if [ "$NEXUS_ONLY" = true ]; then
echo "🚀 Building and restarting only the web (nexus) container..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --build web
else
echo "🚀 Building and starting staging containers..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --build
fi
# 5. Wait for Database to be healthy
echo "⏳ Waiting for database (nexus-db-stage) to become healthy..."
MAX_ATTEMPTS=30
attempt=0
until [ "$(docker inspect --format='{{json .State.Health.Status}}' nexus-db-stage 2>/dev/null)" == "\"healthy\"" ]; do
sleep 2
attempt=$((attempt + 1))
if [ $attempt -ge $MAX_ATTEMPTS ]; then
echo "❌ Timeout: Database container never became healthy."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs db
exit 1
fi
done
echo "✅ Database is healthy!"
# 6. Apply Entity Framework migrations
echo "🔄 Applying EF Core migrations to staging database on port $POSTGRES_PORT..."
export ConnectionStrings__PostgresConnection="Host=127.0.0.1;Port=$POSTGRES_PORT;Database=$POSTGRES_DB;Username=$POSTGRES_USER;Password=$POSTGRES_PASSWORD"
dotnet ef database update --project src/NexusReader.Data --startup-project src/NexusReader.Web --no-build
# 7. Wait for Web Application to respond
echo "⏳ Waiting for Web Application to start on http://localhost:$WEB_PORT/health..."
MAX_WEB_ATTEMPTS=30
web_attempt=0
until curl -s -f "http://localhost:$WEB_PORT/health" >/dev/null; do
sleep 2
web_attempt=$((web_attempt + 1))
if [ $web_attempt -ge $MAX_WEB_ATTEMPTS ]; then
echo "⚠️ Warning: Web app is not responding yet on http://localhost:$WEB_PORT/health, but let's check logs..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs web
break
fi
done
echo "🎉 Staging environment is ready!"
echo "--------------------------------------------------------"
echo "🌐 Web Application: http://localhost:$WEB_PORT"
echo "🗄️ PostgreSQL Port: $POSTGRES_PORT"
echo "🔎 Neo4j Console: http://localhost:$NEO4J_HTTP_PORT"
echo "📊 Qdrant Service: http://localhost:$QDRANT_HTTP_PORT"
echo "--------------------------------------------------------"