Move dataset review clarification into the assistant workspace and rework the review page into a chat-centric layout with execution rails. Add session-scoped assistant actions for mappings, semantic fields, and SQL preview generation. Introduce optimistic locking for dataset review mutations, propagate session versions through API responses, and mask imported filter values before assistant exposure. Refresh tests, i18n, and spec artifacts to match the new workflow. BREAKING CHANGE: dataset review mutation endpoints now require the X-Session-Version header, and clarification is no longer handled through ClarificationDialog-based flows
230 lines
6.3 KiB
Bash
Executable File
230 lines
6.3 KiB
Bash
Executable File
#!/bin/bash
|
|
# [DEF:run:Module]
|
|
# @PURPOSE: Utility script for run
|
|
# @COMPLEXITY: 1
|
|
|
|
# Project Launch Script
|
|
# Automates setup and concurrent execution of backend and frontend servers.
|
|
|
|
set -e
|
|
|
|
# Default configuration
|
|
BACKEND_PORT=${BACKEND_PORT:-8000}
|
|
FRONTEND_PORT=${FRONTEND_PORT:-5173}
|
|
SKIP_INSTALL=false
|
|
|
|
# Help message
|
|
show_help() {
|
|
echo "Usage: ./run.sh [options]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --help Show this help message"
|
|
echo " --skip-install Skip dependency checks and installation"
|
|
echo ""
|
|
echo "Environment Variables:"
|
|
echo " BACKEND_PORT Port for the backend server (default: 8000)"
|
|
echo " FRONTEND_PORT Port for the frontend server (default: 5173)"
|
|
}
|
|
|
|
# Parse arguments
|
|
while [[ "$#" -gt 0 ]]; do
|
|
case $1 in
|
|
--help) show_help; exit 0 ;;
|
|
--skip-install) SKIP_INSTALL=true ;;
|
|
*) echo "Unknown parameter passed: $1"; show_help; exit 1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
echo "Starting Project Launch Script..."
|
|
|
|
# Environment validation
|
|
validate_env() {
|
|
echo "Validating environment..."
|
|
|
|
if ! command -v python3 &> /dev/null; then
|
|
echo "Error: python3 is not installed."
|
|
exit 1
|
|
fi
|
|
|
|
if ! python3 -c 'import sys; exit(0) if sys.version_info >= (3, 9) else exit(1)'; then
|
|
PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
|
|
echo "Error: python3 version 3.9 or higher is required. Found $PYTHON_VERSION"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v npm &> /dev/null; then
|
|
echo "Error: npm is not installed."
|
|
exit 1
|
|
fi
|
|
|
|
PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
|
|
echo "Environment validation passed (Python $PYTHON_VERSION, npm $(npm -v))"
|
|
}
|
|
|
|
validate_env
|
|
|
|
# Database connectivity preflight
|
|
check_database() {
|
|
# Keep resolution order aligned with backend/src/core/database.py defaults.
|
|
local DB_URL="${DATABASE_URL:-${POSTGRES_URL:-postgresql+psycopg2://postgres:postgres@localhost:5432/ss_tools}}"
|
|
|
|
# SQLite does not require external service.
|
|
if [[ "$DB_URL" == sqlite* ]]; then
|
|
echo "Database preflight: sqlite detected, skipping PostgreSQL connectivity check."
|
|
return
|
|
fi
|
|
|
|
local DB_HOST DB_PORT
|
|
read -r DB_HOST DB_PORT < <(
|
|
python3 - "$DB_URL" <<'PY'
|
|
import sys
|
|
from urllib.parse import urlparse
|
|
|
|
url = sys.argv[1]
|
|
if "://" not in url:
|
|
print("localhost 5432")
|
|
raise SystemExit(0)
|
|
|
|
# Support SQLAlchemy schemes like postgresql+psycopg2://...
|
|
scheme, rest = url.split("://", 1)
|
|
parsed = urlparse(f"{scheme.split('+', 1)[0]}://{rest}")
|
|
host = parsed.hostname or "localhost"
|
|
port = parsed.port or 5432
|
|
print(f"{host} {port}")
|
|
PY
|
|
)
|
|
|
|
local check_cmd
|
|
check_cmd='import socket,sys; socket.create_connection((sys.argv[1], int(sys.argv[2])), timeout=1).close()'
|
|
|
|
if python3 -c "$check_cmd" "$DB_HOST" "$DB_PORT" >/dev/null 2>&1; then
|
|
echo "Database preflight: reachable at ${DB_HOST}:${DB_PORT}."
|
|
return
|
|
fi
|
|
|
|
echo "Database preflight: cannot connect to ${DB_HOST}:${DB_PORT}."
|
|
|
|
# For local development defaults, attempt to auto-start bundled PostgreSQL.
|
|
if [ "$DB_HOST" = "localhost" ] && [ "$DB_PORT" = "5432" ] && command -v docker >/dev/null 2>&1; then
|
|
if [ -f "docker-compose.yml" ]; then
|
|
echo "Attempting to start local PostgreSQL via docker compose (service: db)..."
|
|
docker compose up -d db || true
|
|
fi
|
|
fi
|
|
|
|
for _ in {1..20}; do
|
|
if python3 -c "$check_cmd" "$DB_HOST" "$DB_PORT" >/dev/null 2>&1; then
|
|
echo "Database preflight: reachable at ${DB_HOST}:${DB_PORT}."
|
|
return
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
echo "Error: PostgreSQL is unavailable at ${DB_HOST}:${DB_PORT}."
|
|
echo "Run: docker compose up -d db"
|
|
echo "Or set DATABASE_URL/POSTGRES_URL to a reachable database."
|
|
exit 1
|
|
}
|
|
|
|
check_database
|
|
|
|
# Backend dependency management
|
|
setup_backend() {
|
|
if [ "$SKIP_INSTALL" = true ]; then
|
|
echo "Skipping backend installation..."
|
|
return
|
|
fi
|
|
|
|
echo "Setting up backend..."
|
|
cd backend
|
|
if [ ! -d ".venv" ]; then
|
|
echo "Creating virtual environment..."
|
|
python3 -m venv .venv
|
|
fi
|
|
|
|
source .venv/bin/activate
|
|
if [ -f "requirements.txt" ]; then
|
|
echo "Installing backend dependencies..."
|
|
pip install -r requirements.txt
|
|
else
|
|
echo "Warning: backend/requirements.txt not found."
|
|
fi
|
|
cd ..
|
|
}
|
|
|
|
# Frontend dependency management
|
|
setup_frontend() {
|
|
if [ "$SKIP_INSTALL" = true ]; then
|
|
echo "Skipping frontend installation..."
|
|
return
|
|
fi
|
|
|
|
echo "Setting up frontend..."
|
|
cd frontend
|
|
if [ ! -d "node_modules" ]; then
|
|
echo "Installing frontend dependencies..."
|
|
npm install
|
|
else
|
|
echo "frontend/node_modules already exists. Skipping npm install."
|
|
fi
|
|
cd ..
|
|
}
|
|
|
|
setup_backend
|
|
setup_frontend
|
|
|
|
# Cleanup function for graceful shutdown
|
|
cleanup() {
|
|
echo ""
|
|
echo "Stopping services..."
|
|
if [ -n "$BACKEND_PID" ]; then
|
|
kill $BACKEND_PID 2>/dev/null || true
|
|
fi
|
|
if [ -n "$FRONTEND_PID" ]; then
|
|
kill $FRONTEND_PID 2>/dev/null || true
|
|
fi
|
|
echo "Services stopped."
|
|
exit 0
|
|
}
|
|
|
|
# Trap SIGINT (Ctrl+C)
|
|
trap cleanup SIGINT
|
|
|
|
# Start Backend
|
|
start_backend() {
|
|
echo -e "\033[0;34m[Backend]\033[0m Starting on port $BACKEND_PORT..."
|
|
cd backend
|
|
local -a uvicorn_env_args=()
|
|
if [ -f ".venv/bin/activate" ]; then
|
|
source .venv/bin/activate
|
|
else
|
|
echo -e "\033[0;31m[Backend]\033[0m Warning: .venv/bin/activate not found. Attempting to run without venv."
|
|
fi
|
|
if [ -f ".env" ]; then
|
|
uvicorn_env_args=(--env-file .env)
|
|
fi
|
|
# Use a subshell to prefix output
|
|
python3 -m uvicorn src.app:app --reload --port "$BACKEND_PORT" "${uvicorn_env_args[@]}" 2>&1 | sed "s/^/$(echo -e '\033[0;34m[Backend]\033[0m ') /" &
|
|
BACKEND_PID=$!
|
|
cd ..
|
|
}
|
|
|
|
# Start Frontend
|
|
start_frontend() {
|
|
echo -e "\033[0;32m[Frontend]\033[0m Starting on port $FRONTEND_PORT..."
|
|
cd frontend
|
|
# Use a subshell to prefix output
|
|
npm run dev -- --port "$FRONTEND_PORT" 2>&1 | sed "s/^/$(echo -e '\033[0;32m[Frontend]\033[0m ') /" &
|
|
FRONTEND_PID=$!
|
|
cd ..
|
|
}
|
|
|
|
start_backend
|
|
start_frontend
|
|
|
|
echo "Services are running. Press Ctrl+C to stop."
|
|
wait
|
|
|
|
# [/DEF:run:Module]
|