80 lines
3.8 KiB
Markdown
80 lines
3.8 KiB
Markdown
|
|
# 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
|