# Project: CMS API for SvelteKit Marketing Site ## Context I have a public marketing website built with SvelteKit (frontend) and Python (backend). I need a secure, read/write JSON API that: - Serves as the data layer between a CMS admin interface and the SvelteKit frontend - Allows authenticated CMS users to create, update, and delete content - Allows the SvelteKit frontend to read content (public, no auth required for reads) ## Tech Stack - **API**: Python (FastAPI) - **Database**: PostgreSQL via SQLAlchemy (async) + Alembic for migrations - **Auth**: JWT bearer tokens for write operations (CMS admin). Read endpoints are public. - **Validation**: Pydantic v2 models for all request/response schemas - **Frontend consumer**: SvelteKit `load()` functions calling the API server-side ## Requirements ### Data Models Scaffold these content types (all with `id`, `created_at`, `updated_at`, `published` boolean, `slug` unique index): - **Page** — `title`, `slug`, `body` (rich text/HTML string), `meta_title`, `meta_description`, `og_image_url` - **BlogPost** — `title`, `slug`, `excerpt`, `body`, `author`, `featured_image_url`, `tags` (array of strings) - **SiteSettings** — singleton row: `site_name`, `tagline`, `logo_url`, `footer_text`, `social_links` (JSON object) ### API Endpoints All under `/api/v1/`: **Public (no auth):** - `GET /pages` — list published pages - `GET /pages/{slug}` — single published page by slug - `GET /posts` — list published posts (pagination: `?page=1&per_page=10`, ordered by `created_at` desc) - `GET /posts/{slug}` — single published post by slug - `GET /settings` — site settings singleton **Protected (JWT required):** - `POST / PUT / DELETE` for pages and posts - `PUT /settings` - `POST /auth/login` — accepts `email` + `password`, returns JWT access + refresh tokens - `POST /auth/refresh` — rotate refresh token ### Security - Passwords hashed with bcrypt - JWT access tokens: 15 min expiry. Refresh tokens: 7 day expiry, stored server-side, revocable. - All write endpoints behind `Depends(get_current_user)` FastAPI dependency - CORS: allow the SvelteKit origin only (configurable via env var `ALLOWED_ORIGINS`) - Rate limiting on `/auth/*` endpoints (e.g. slowapi, 5 req/min per IP) - Input sanitisation: strip/reject dangerous HTML in body fields (bleach or nh3) ### Project Structure ``` backend/ ├── app/ │ ├── main.py # FastAPI app, CORS, lifespan │ ├── config.py # pydantic-settings BaseSettings from .env │ ├── database.py # async engine, sessionmaker, get_db dependency │ ├── models/ # SQLAlchemy ORM models │ ├── schemas/ # Pydantic request/response schemas │ ├── routers/ # pages.py, posts.py, settings.py, auth.py │ ├── services/ # business logic layer │ ├── auth/ # JWT creation, verification, password hashing │ └── middleware/ # rate limiting, request logging ├── alembic/ # migrations ├── alembic.ini ├── requirements.txt ├── .env.example └── Dockerfile ``` ### Additional - Include a `seed.py` script that creates a default admin user and sample content - Add a `Dockerfile` and `docker-compose.yml` (API + Postgres) - Include a `.env.example` with all required env vars documented - Write basic pytest tests for auth flow and one CRUD resource - Add OpenAPI tags and descriptions so the `/docs` page is well-organised ## Constraints - Python 3.12+ - Do NOT use any ORM magic for auth — keep JWT logic explicit and auditable - All database calls must be async - Return proper HTTP status codes (201 on create, 204 on delete, 404/422/401/403 as appropriate) - Pagination responses must include `total`, `page`, `per_page`, `total_pages` metadata