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>
This commit was merged in pull request #83.
This commit is contained in:
Executable
+154
@@ -0,0 +1,154 @@
|
||||
#!/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 "--------------------------------------------------------"
|
||||
Reference in New Issue
Block a user