"""The blueprints that ship with the product. Nothing in the catalog is active. It is a shelf an admin takes from: adding a blueprint copies it into an ordinary template row, which the customer then owns and edits. The catalog never touches that row again. """ from typing import Annotated from fastapi import Depends from sqlalchemy.ext.asyncio import AsyncSession from app.api.templates.blueprints import parse_or_422, taken_config_ids from app.api.templates.routing import editor_router from app.api.templates.schemas import CatalogDetail, CatalogSummary, TemplateDetail from app.api.templates.view import detail from app.db import get_db from app.errors import ApiError from app.template_catalog import catalog_for_locale, get_catalog_entry from app.template_import import upsert_template router = editor_router() @router.get("/catalog") async def list_catalog( db: Annotated[AsyncSession, Depends(get_db)], ) -> list[CatalogSummary]: """The blueprints shipped with Pablan, each marked with whether this instance has already added it.""" added = await taken_config_ids(db) return [ CatalogSummary( id=entry.id, name=entry.name, description=entry.description, sections=entry.sections, added=entry.id in added, ) for entry in catalog_for_locale() ] @router.get("/catalog/{catalog_id}") async def get_catalog_blueprint(catalog_id: str) -> CatalogDetail: """Read a blueprint before adding it — the whole point of "view" is that an admin can see its structure before committing to it.""" entry = get_catalog_entry(catalog_id) if entry is None: raise ApiError(404, "Blueprint not found.", "not_found") return CatalogDetail( id=entry.id, name=entry.name, description=entry.description, sections=entry.sections, added=False, yaml=entry.source, ) @router.post("/catalog/{catalog_id}") async def add_from_catalog( catalog_id: str, db: Annotated[AsyncSession, Depends(get_db)], ) -> TemplateDetail: """Copy a blueprint into this instance. The result is an ordinary template row: editable, and never touched by the catalog again.""" entry = get_catalog_entry(catalog_id) if entry is None: raise ApiError(404, "Blueprint not found.", "not_found") if entry.id in await taken_config_ids(db): raise ApiError( 409, "This template has already been added — edit or duplicate it instead.", "already_added", ) row, _created = await upsert_template(db, parse_or_422(entry.source)) await db.commit() return detail(row)