Bootstrap initial admin via env and add compose profiles
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
# @SIDE_EFFECT: Starts background scheduler and binds network ports for HTTP/WS traffic.
|
||||
# @DATA_CONTRACT: [HTTP Request | WS Message] -> [HTTP Response | JSON Log Stream]
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# project_root is used for static files mounting
|
||||
@@ -28,6 +29,9 @@ from .dependencies import get_task_manager, get_scheduler_service
|
||||
from .core.encryption_key import ensure_encryption_key
|
||||
from .core.utils.network import NetworkError
|
||||
from .core.logger import logger, belief_scope
|
||||
from .core.database import AuthSessionLocal
|
||||
from .core.auth.security import get_password_hash
|
||||
from .models.auth import User, Role
|
||||
from .api.routes import plugins, tasks, settings, environments, mappings, migration, connections, git, storage, admin, llm, dashboards, datasets, reports, assistant, clean_release, clean_release_v2, profile, health, dataset_review
|
||||
from .api import auth
|
||||
|
||||
@@ -42,6 +46,54 @@ app = FastAPI(
|
||||
)
|
||||
# [/DEF:App:Global]
|
||||
|
||||
# [DEF:ensure_initial_admin_user:Function]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Ensures initial admin user exists when bootstrap env flags are enabled.
|
||||
def ensure_initial_admin_user() -> None:
|
||||
raw_flag = os.getenv("INITIAL_ADMIN_CREATE", "false").strip().lower()
|
||||
if raw_flag not in {"1", "true", "yes", "on"}:
|
||||
return
|
||||
username = os.getenv("INITIAL_ADMIN_USERNAME", "").strip()
|
||||
password = os.getenv("INITIAL_ADMIN_PASSWORD", "").strip()
|
||||
if not username or not password:
|
||||
logger.warning(
|
||||
"INITIAL_ADMIN_CREATE is enabled but INITIAL_ADMIN_USERNAME/INITIAL_ADMIN_PASSWORD is missing; skipping bootstrap."
|
||||
)
|
||||
return
|
||||
|
||||
db = AuthSessionLocal()
|
||||
try:
|
||||
admin_role = db.query(Role).filter(Role.name == "Admin").first()
|
||||
if not admin_role:
|
||||
admin_role = Role(name="Admin", description="System Administrator")
|
||||
db.add(admin_role)
|
||||
db.commit()
|
||||
db.refresh(admin_role)
|
||||
|
||||
existing_user = db.query(User).filter(User.username == username).first()
|
||||
if existing_user:
|
||||
logger.info("Initial admin bootstrap skipped: user '%s' already exists.", username)
|
||||
return
|
||||
|
||||
new_user = User(
|
||||
username=username,
|
||||
email=None,
|
||||
password_hash=get_password_hash(password),
|
||||
auth_source="LOCAL",
|
||||
is_active=True,
|
||||
)
|
||||
new_user.roles.append(admin_role)
|
||||
db.add(new_user)
|
||||
db.commit()
|
||||
logger.info("Initial admin user '%s' created from environment bootstrap.", username)
|
||||
except Exception as exc:
|
||||
db.rollback()
|
||||
logger.error("Failed to bootstrap initial admin user: %s", exc)
|
||||
raise
|
||||
finally:
|
||||
db.close()
|
||||
# [/DEF:ensure_initial_admin_user:Function]
|
||||
|
||||
# [DEF:startup_event:Function]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Handles application startup tasks, such as starting the scheduler.
|
||||
@@ -53,6 +105,7 @@ app = FastAPI(
|
||||
async def startup_event():
|
||||
with belief_scope("startup_event"):
|
||||
ensure_encryption_key()
|
||||
ensure_initial_admin_user()
|
||||
scheduler = get_scheduler_service()
|
||||
scheduler.start()
|
||||
# [/DEF:startup_event:Function]
|
||||
|
||||
35
build.sh
35
build.sh
@@ -7,6 +7,23 @@ cd "$SCRIPT_DIR"
|
||||
|
||||
BACKEND_ENV_FILE="$SCRIPT_DIR/backend/.env"
|
||||
|
||||
PROFILE="${1:-current}"
|
||||
|
||||
case "$PROFILE" in
|
||||
master)
|
||||
PROFILE_ENV_FILE="$SCRIPT_DIR/.env.master"
|
||||
PROJECT_NAME="ss-tools-master"
|
||||
;;
|
||||
current)
|
||||
PROFILE_ENV_FILE="$SCRIPT_DIR/.env.current"
|
||||
PROJECT_NAME="ss-tools-current"
|
||||
;;
|
||||
*)
|
||||
echo "Error: unknown profile '$PROFILE'. Use one of: master, current."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "Error: docker is not installed or not in PATH."
|
||||
exit 1
|
||||
@@ -80,11 +97,23 @@ PY
|
||||
|
||||
ensure_backend_encryption_key
|
||||
|
||||
COMPOSE_ARGS=(-p "$PROJECT_NAME")
|
||||
if [[ -f "$PROFILE_ENV_FILE" ]]; then
|
||||
COMPOSE_ARGS+=(--env-file "$PROFILE_ENV_FILE")
|
||||
else
|
||||
echo "[build] Warning: profile env file not found at $PROFILE_ENV_FILE, using compose defaults."
|
||||
fi
|
||||
|
||||
echo "[build] Profile: $PROFILE (project: $PROJECT_NAME)"
|
||||
if [[ -f "$PROFILE_ENV_FILE" ]]; then
|
||||
echo "[build] Env file: $PROFILE_ENV_FILE"
|
||||
fi
|
||||
|
||||
echo "[1/2] Building project images..."
|
||||
"${COMPOSE_CMD[@]}" build
|
||||
"${COMPOSE_CMD[@]}" "${COMPOSE_ARGS[@]}" build
|
||||
|
||||
echo "[2/2] Starting Docker services..."
|
||||
"${COMPOSE_CMD[@]}" up -d
|
||||
"${COMPOSE_CMD[@]}" "${COMPOSE_ARGS[@]}" up -d
|
||||
|
||||
echo "Done. Services are running."
|
||||
echo "Use '${COMPOSE_CMD[*]} ps' to check status and '${COMPOSE_CMD[*]} logs -f' to stream logs."
|
||||
echo "Use '${COMPOSE_CMD[*]} ${COMPOSE_ARGS[*]} ps' to check status and '${COMPOSE_CMD[*]} ${COMPOSE_ARGS[*]} logs -f' to stream logs."
|
||||
|
||||
@@ -2,7 +2,6 @@ services:
|
||||
db:
|
||||
image: ${POSTGRES_IMAGE:?Set POSTGRES_IMAGE in .env.enterprise-clean}
|
||||
pull_policy: never
|
||||
container_name: ss_tools_db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB:-ss_tools}
|
||||
@@ -21,7 +20,6 @@ services:
|
||||
backend:
|
||||
image: ${BACKEND_IMAGE:?Set BACKEND_IMAGE in .env.enterprise-clean}
|
||||
pull_policy: never
|
||||
container_name: ss_tools_backend
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
@@ -50,7 +48,6 @@ services:
|
||||
frontend:
|
||||
image: ${FRONTEND_IMAGE:?Set FRONTEND_IMAGE in .env.enterprise-clean}
|
||||
pull_policy: never
|
||||
container_name: ss_tools_frontend
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
services:
|
||||
db:
|
||||
image: ${POSTGRES_IMAGE:-postgres:16-alpine}
|
||||
container_name: ss_tools_db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ss_tools
|
||||
@@ -21,7 +20,6 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/backend.Dockerfile
|
||||
container_name: ss_tools_backend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
@@ -34,6 +32,9 @@ services:
|
||||
TASKS_DATABASE_URL: postgresql+psycopg2://postgres:postgres@db:5432/ss_tools
|
||||
AUTH_DATABASE_URL: postgresql+psycopg2://postgres:postgres@db:5432/ss_tools
|
||||
BACKEND_PORT: 8000
|
||||
INITIAL_ADMIN_CREATE: ${INITIAL_ADMIN_CREATE:-false}
|
||||
INITIAL_ADMIN_USERNAME: ${INITIAL_ADMIN_USERNAME:-admin}
|
||||
INITIAL_ADMIN_PASSWORD: ${INITIAL_ADMIN_PASSWORD:-}
|
||||
ports:
|
||||
- "${BACKEND_HOST_PORT:-8001}:8000"
|
||||
volumes:
|
||||
@@ -46,7 +47,6 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/frontend.Dockerfile
|
||||
container_name: ss_tools_frontend
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
Reference in New Issue
Block a user