feat(main): main
This commit is contained in:
92
core/services/bootstrap.py
Normal file
92
core/services/bootstrap.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
Bootstrap service — idempotent startup initialisation.
|
||||
|
||||
Ensures a default media library exists as soon as the first user is available.
|
||||
Called from three places:
|
||||
1. AppConfig.ready() – every server start (DB already populated)
|
||||
2. post_migrate signal – after `manage.py migrate` runs
|
||||
3. auth.setup_admin endpoint – immediately after the first user is created
|
||||
"""
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_LIBRARY_NAME = "Default Library"
|
||||
|
||||
# One-shot flag: after the first request triggers the bootstrap, disconnect
|
||||
# the signal so we don't repeat the DB check on every subsequent request.
|
||||
_startup_bootstrap_done = False
|
||||
|
||||
|
||||
def ensure_default_library():
|
||||
"""
|
||||
Create a default media library if none exists yet.
|
||||
|
||||
Idempotent — safe to call multiple times; does nothing when a library
|
||||
already exists. Returns the newly-created Library, or None if no action
|
||||
was taken (either a library already exists, or no users exist yet to be
|
||||
the owner).
|
||||
"""
|
||||
from core.models import Library, AppUser
|
||||
|
||||
if Library.objects.exists():
|
||||
return None # Already bootstrapped
|
||||
|
||||
# Need an owner — prefer the first superuser, fall back to any user.
|
||||
owner = (
|
||||
AppUser.objects.filter(is_superuser=True).order_by("date_joined").first()
|
||||
or AppUser.objects.order_by("date_joined").first()
|
||||
)
|
||||
if owner is None:
|
||||
# No users yet — setup_admin will call us again once the first user exists.
|
||||
logger.debug("ensure_default_library: no users yet, skipping.")
|
||||
return None
|
||||
|
||||
library = Library.objects.create(
|
||||
owner_user=owner,
|
||||
name=DEFAULT_LIBRARY_NAME,
|
||||
visibility="public",
|
||||
description=(
|
||||
"Default media library. "
|
||||
"Add media sources here to start building your channels."
|
||||
),
|
||||
)
|
||||
logger.info(
|
||||
"Bootstrap: created default library '%s' (id=%d) owned by '%s'.",
|
||||
library.name,
|
||||
library.id,
|
||||
owner.username,
|
||||
)
|
||||
return library
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Signal handlers — wired up in CoreConfig.ready()
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _on_post_migrate(sender, **kwargs):
|
||||
"""Run bootstrap after every successful migration."""
|
||||
try:
|
||||
ensure_default_library()
|
||||
except Exception as exc: # pragma: no cover
|
||||
logger.warning("ensure_default_library failed in post_migrate: %s", exc)
|
||||
|
||||
|
||||
def _on_first_request(sender, **kwargs):
|
||||
"""
|
||||
One-shot: run bootstrap on the very first HTTP request after server start.
|
||||
Disconnects itself immediately so subsequent requests pay zero overhead.
|
||||
"""
|
||||
global _startup_bootstrap_done
|
||||
if _startup_bootstrap_done:
|
||||
return
|
||||
_startup_bootstrap_done = True
|
||||
|
||||
from django.core.signals import request_started
|
||||
request_started.disconnect(_on_first_request)
|
||||
|
||||
try:
|
||||
ensure_default_library()
|
||||
except Exception as exc: # pragma: no cover
|
||||
logger.warning("ensure_default_library failed on first request: %s", exc)
|
||||
|
||||
Reference in New Issue
Block a user