Source code for regstack.backends.base
"""Backend ABC.
A `Backend` owns the persistence story for one regstack instance: it
hands out the five repository implementations, knows how to install /
migrate its own schema, and how to tear down its connection pool when
the host shuts down. Routers and services never see the backend object
directly — they pull repos off the ``RegStack`` façade, which in turn
holds one backend.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from enum import StrEnum
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from regstack.auth.clock import Clock
from regstack.backends.protocols import (
BlacklistRepoProtocol,
LoginAttemptRepoProtocol,
MfaCodeRepoProtocol,
OAuthIdentityRepoProtocol,
OAuthStateRepoProtocol,
PendingRepoProtocol,
UserRepoProtocol,
)
from regstack.config.schema import RegStackConfig
[docs]
class BackendKind(StrEnum):
SQLITE = "sqlite"
POSTGRES = "postgres"
MONGO = "mongo"
[docs]
class Backend(ABC):
"""A configured persistence backend for one regstack instance."""
kind: BackendKind
def __init__(self, *, config: RegStackConfig, clock: Clock) -> None:
self.config = config
self.clock = clock
# --- Repositories ----------------------------------------------------
# Each backend exposes the five repos as attributes after construction.
# They are typed as protocols (not concrete classes) so consumers
# cannot reach for backend-specific helpers by accident.
users: UserRepoProtocol
pending: PendingRepoProtocol
blacklist: BlacklistRepoProtocol
attempts: LoginAttemptRepoProtocol
mfa_codes: MfaCodeRepoProtocol
oauth_identities: OAuthIdentityRepoProtocol
oauth_states: OAuthStateRepoProtocol
# --- Lifecycle -------------------------------------------------------
[docs]
@abstractmethod
async def install_schema(self) -> None:
"""Create indexes (Mongo) or run migrations (SQL).
Idempotent — safe to call on every app start. Hosts typically
invoke this from a FastAPI ``lifespan`` startup hook.
"""
[docs]
@abstractmethod
async def aclose(self) -> None:
"""Close the underlying connection pool / client."""
# --- Diagnostics -----------------------------------------------------
[docs]
@abstractmethod
async def ping(self) -> None:
"""Cheap connectivity probe. Raises on failure. Used by `regstack doctor`."""