Scoped DB role + own theme colour (port log E17)
- Split database.py into a runtime engine (scoped `kds` DB role, used for all request handling) and a migration engine (privileged `kitchen` role, used only at startup to create KDS's own tables and ALTER kitchen_settings) — KDS previously shared kitchen's full-access DB credential wholesale - Dispose both engines on shutdown (main.py) - Fix theme colour clash: KDS was accidentally seeded with kitchen's teal (#0d9488) instead of its own colour — now #ea580c (orange), regenerated PWA icons to match Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
870027faca
commit
23889315f5
7 changed files with 4446 additions and 47 deletions
|
|
@ -2,25 +2,48 @@ import os
|
|||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
DATABASE_URL = os.getenv(
|
||||
|
||||
def _to_asyncpg(url: str) -> str:
|
||||
if url.startswith("postgresql://"):
|
||||
return url.replace("postgresql://", "postgresql+asyncpg://", 1)
|
||||
return url
|
||||
|
||||
|
||||
# Runtime connection — the scoped `kds` DB role, used for every request via
|
||||
# get_db(). Only has SELECT/INSERT/UPDATE/DELETE on kds_tickets,
|
||||
# kds_course_bumps and the specific kitchen_settings columns KDS needs (see
|
||||
# deploy_kds grants) — it cannot read the rest of kitchen_db.
|
||||
DATABASE_URL = _to_asyncpg(os.getenv(
|
||||
"DATABASE_URL",
|
||||
"postgresql+asyncpg://kitchen:kitchen_secret@localhost:5432/kitchen_gp"
|
||||
"postgresql://kitchen:kitchen_secret@localhost:5432/kitchen_gp"
|
||||
))
|
||||
|
||||
# Migration connection — the privileged `kitchen` DB role. Used ONLY at
|
||||
# startup to create KDS's own tables and ALTER kitchen_settings (adding KDS
|
||||
# columns). Never used for request handling. Falls back to DATABASE_URL for
|
||||
# local/dev convenience, but production must set this separately once the
|
||||
# `kds` role is scoped down (see port log E17/task: KDS DB role split).
|
||||
MIGRATION_DATABASE_URL = _to_asyncpg(
|
||||
os.getenv("MIGRATION_DATABASE_URL") or os.getenv(
|
||||
"DATABASE_URL", "postgresql://kitchen:kitchen_secret@localhost:5432/kitchen_gp"
|
||||
)
|
||||
)
|
||||
|
||||
# Convert standard postgres URL to asyncpg format
|
||||
if DATABASE_URL.startswith("postgresql://"):
|
||||
DATABASE_URL = DATABASE_URL.replace("postgresql://", "postgresql+asyncpg://", 1)
|
||||
# `engine` stays the name existing migration files (`from database import
|
||||
# engine`) already import — no changes needed there. It's bound to the
|
||||
# privileged connection.
|
||||
engine = create_async_engine(MIGRATION_DATABASE_URL, echo=False)
|
||||
|
||||
engine = create_async_engine(
|
||||
runtime_engine = create_async_engine(
|
||||
DATABASE_URL,
|
||||
echo=False,
|
||||
pool_size=10, # Default is 5
|
||||
max_overflow=20, # Default is 10 - allows burst to 30 connections
|
||||
pool_pre_ping=True # Verify connections are alive before use
|
||||
pool_pre_ping=True # Verify connections are alive before use
|
||||
)
|
||||
|
||||
AsyncSessionLocal = async_sessionmaker(
|
||||
engine,
|
||||
runtime_engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from contextlib import asynccontextmanager
|
|||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from database import engine, Base
|
||||
from database import engine, runtime_engine, Base
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
|
|
@ -34,7 +34,9 @@ async def _run(name, coro):
|
|||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# Create any SQLAlchemy-mapped KDS tables (idempotent)
|
||||
# Create any SQLAlchemy-mapped KDS tables (idempotent). Uses the
|
||||
# privileged migration engine (`kitchen` role) — the scoped `kds` role
|
||||
# used for request handling can't CREATE TABLE.
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
|
||||
|
|
@ -51,6 +53,7 @@ async def lifespan(app: FastAPI):
|
|||
|
||||
await stop_signalr_listener()
|
||||
await engine.dispose()
|
||||
await runtime_engine.dispose()
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
|
|
|
|||
4441
frontend/package-lock.json
generated
4441
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.8 KiB |
|
|
@ -20,7 +20,7 @@
|
|||
--kds-sent: #6b7280;
|
||||
|
||||
/* App primary — teal (shared with kitchen for recipe images etc.) */
|
||||
--app-primary: #0d9488;
|
||||
--app-primary: #ea580c; /* kds orange — distinct from kitchen's teal, D5 */
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ export default defineConfig({
|
|||
start_url: '/kds/',
|
||||
scope: '/kds/',
|
||||
display: 'standalone',
|
||||
theme_color: '#0d9488',
|
||||
background_color: '#0d9488',
|
||||
theme_color: '#ea580c',
|
||||
background_color: '#ea580c',
|
||||
icons: [
|
||||
{ src: '/kds/icons/icon-192.png', sizes: '192x192', type: 'image/png' },
|
||||
{ src: '/kds/icons/icon-512.png', sizes: '512x512', type: 'image/png' },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue