Update admin theme/layout and refresh README details.

Align the project baseline with the latest admin interface styling and layout structure while documenting setup and usage updates in README.
This commit is contained in:
vlad
2026-07-14 17:12:28 +03:00
commit 86cc3fa541
278 changed files with 19416 additions and 0 deletions
+101
View File
@@ -0,0 +1,101 @@
"""Docker entrypoint: wait for DB, reconcile Alembic state, migrate, start API."""
from __future__ import annotations
import subprocess
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from alembic import command
from alembic.config import Config
from sqlalchemy import create_engine, inspect, text
from app.core.install_secrets import ensure_install_secrets
from app.core.config import settings
INITIAL_REVISION = "20260711_0001"
SCHEMA_TABLES = (
"content_pages",
"email_verification_tokens",
"password_reset_tokens",
"refresh_tokens",
"user_profiles",
"users",
)
def wait_for_database(max_attempts: int = 30, delay_seconds: float = 1.0):
engine = create_engine(settings.database_url)
last_error: Exception | None = None
for _ in range(max_attempts):
try:
with engine.connect() as connection:
connection.execute(text("SELECT 1"))
return engine
except Exception as exc:
last_error = exc
time.sleep(delay_seconds)
detail = str(last_error or "unknown error")
hint = ""
if "password authentication failed" in detail or "does not exist" in detail:
hint = (
"\n\nPostgres credentials in install.env do not match the existing database volume "
"(common if docker compose ran before bootstrap_install.py).\n"
"Local dev fix — deletes Docker DB data:\n"
" docker compose --profile docker-web down -v\n"
" docker compose --profile docker-web up -d --build\n"
"See docs/secrets-recovery.md"
)
raise RuntimeError(f"Database is unavailable: {detail}{hint}") from last_error
def current_revision(engine) -> str | None:
inspector = inspect(engine)
if "alembic_version" not in inspector.get_table_names():
return None
with engine.connect() as connection:
return connection.execute(text("SELECT version_num FROM alembic_version")).scalar()
def _reset_schema(engine) -> None:
cascade = " CASCADE" if engine.dialect.name == "postgresql" else ""
with engine.begin() as connection:
for table in SCHEMA_TABLES:
connection.execute(text(f'DROP TABLE IF EXISTS "{table}"{cascade}'))
connection.execute(text(f'DROP TABLE IF EXISTS "alembic_version"{cascade}'))
def run_migrations(engine) -> None:
config = Config("alembic.ini")
tables = set(inspect(engine).get_table_names())
revision = current_revision(engine)
schema_tables = set(SCHEMA_TABLES)
existing_schema = tables & schema_tables
if existing_schema:
if schema_tables.issubset(tables):
if revision is None:
command.stamp(config, INITIAL_REVISION)
else:
_reset_schema(engine)
command.upgrade(config, "head")
def main() -> None:
ensure_install_secrets()
engine = wait_for_database()
run_migrations(engine)
subprocess.run(
[sys.executable, "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"],
check=True,
)
if __name__ == "__main__":
main()