The API image ran four uvicorn workers, and each one built a full flow controller — four sets of MQTT subscriptions, cron ticks and webhooks. Runs one worker now; scaling out is the worker split, not more processes. Adds a loop-lag watchdog and a deep /utils/health/ that fails when the event loop is wedged or Redis is unreachable, the two failure modes a process-alive check never sees. Autoheal restarts on that signal, behind a compose profile because it mounts the Docker socket. The private user-seeding routes now need an explicit opt-in rather than just ENVIRONMENT=local, so a deployment that kept the default never exposes them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011LF61rxW1FG5YCD2J9YqjY
46 lines
1.0 KiB
Python
46 lines
1.0 KiB
Python
from typing import Any
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from app.api.deps import SessionDep
|
|
from app.core.config import settings
|
|
from app.core.security import get_password_hash
|
|
from app.models import (
|
|
User,
|
|
UserPublic,
|
|
)
|
|
|
|
router = APIRouter(tags=["private"], prefix="/private")
|
|
|
|
|
|
def _require_private_api() -> None:
|
|
if not (settings.ENVIRONMENT == "local" and settings.PRIVATE_API_ENABLED):
|
|
raise HTTPException(status_code=403, detail="Private API is disabled")
|
|
|
|
|
|
class PrivateUserCreate(BaseModel):
|
|
email: str
|
|
password: str
|
|
full_name: str
|
|
is_verified: bool = False
|
|
|
|
|
|
@router.post("/users/", response_model=UserPublic)
|
|
def create_user(user_in: PrivateUserCreate, session: SessionDep) -> Any:
|
|
"""
|
|
Create a new user.
|
|
"""
|
|
_require_private_api()
|
|
|
|
user = User(
|
|
email=user_in.email,
|
|
full_name=user_in.full_name,
|
|
hashed_password=get_password_hash(user_in.password),
|
|
)
|
|
|
|
session.add(user)
|
|
session.commit()
|
|
|
|
return user
|