# api-python-001 Réécriture en Python (FastAPI + SQLite) de [afuh/rick-and-morty-api](https://github.com/afuh/rick-and-morty-api) (Node.js/Express + MongoDB) — https://api-python-001.tips-of-mine.com ## Correspondance avec l'original | Original (Node.js) | Ici (Python) | |---|---| | `server.js` (Express) | `app/main.py` (FastAPI) | | `routes/` + `handlers/` | `app/routers/` (+ `common.py` : pagination, filtres, ids multiples) | | `models/` (Mongoose / MongoDB) | `app/models.py` (SQLAlchemy / SQLite) | | `utils/helpers.js` (messages) | `app/errors.py` | | lecture seule | + POST / PUT, DELETE protégé par `X-API-Key` | | `images/` servies sur `/api/character/avatar/N.jpeg` | idem, fichiers envoyés par upload dans `data/avatars` | | — | site web de consultation (`/`, Jinja2, sans JavaScript) | | `graphql/` | chantier suivant | ## Endpoints | Méthode | Chemin | |---|---| | GET | `/api` — index des ressources | | GET | `/api/{character,location,episode}` — 20 par page, `?page=N` + filtres | | GET | `/api/{ressource}/1`, `/1,2,3`, `/[1,2,3]` | | POST | `/api/{ressource}` — `id` et `created` facultatifs (conservés si fournis) | | PUT | `/api/{ressource}/{id}` — remplacement complet (`id`/`created` du corps ignorés) | | DELETE | `/api/{ressource}/{id}` — en-tête `X-API-Key` | | PUT | `/api/character/{id}/avatar` — corps brut JPEG/PNG (2 Mo max), `X-API-Key` ; met à jour `image` | | GET | `/api/character/avatar/{id}.jpeg` (ou `.png`) | | GET | `/health`, `/hello`, `/docs` | Filtres (partiels, insensibles à la casse) : character `name status species type gender`, location `name type dimension`, episode `name episode`. ## Format et relations Réponses identiques à l'original, plus un champ `modified` (date de dernière modification, égale à `created` tant que l'objet n'a pas été modifié) ; une ressource lue en GET peut être renvoyée telle quelle en POST. Les URL (`origin`, `location`, `episode`, `residents`) sont décodées sur `/api//`, quel que soit l'hôte. - `character.episode` ↔ `episode.characters` : une seule relation. Le champ `characters` d'un épisode en entrée est ignoré. - `location.residents` : liste saisie telle quelle (ordre conservé), indépendante de `character.location`. **Ordre d'import (n8n)** : locations → episodes → characters → avatars. ## Site web `/` accueil, `/characters`, `/locations`, `/episodes` (listes paginées + filtres) et leurs fiches `//`. Rendu serveur sans JavaScript, contenu échappé et en-tête CSP strict (les données sont saisissables via l'API). ## CI/CD `.gitea/workflows/build.yml` : tests (étape `test` du Dockerfile) puis push Harbor `${HARBOR_REGISTRY}/api-python-001/api-python-001` — `main` → `latest` + `sha-xxx`, tag `v1.2.3` → `1.2.3`, `1.2`. ## Déploiement SLDOKP03 ```bash mkdir -p /opt/api-python-001/data && cd /opt/api-python-001 # copier docker-compose.yml et .env.example -> .env chown 10001:10001 data docker compose pull && docker compose up -d ```