Files
Froidefond Julien 0f5094575a docs: add AGENTS.md per module and unify ports to 70XX
- Add CLAUDE.md at root and AGENTS.md in apps/api, apps/indexer,
  apps/backoffice, crates/parsers with module-specific guidelines
- Unify all service ports to 70XX (no more internal/external split):
  API 7080, Indexer 7081, Backoffice 7082
- Update docker-compose.yml, Dockerfiles, config.rs defaults,
  .env.example, backoffice routes, bench.sh, smoke.sh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 13:57:39 +01:00

2.9 KiB

apps/api — REST API (axum)

Service HTTP sur le port 7080. Voir AGENTS.md racine pour les conventions globales.

Structure des fichiers

Fichier Rôle
main.rs Routes, initialisation AppState, Semaphore concurrent_renders
state.rs AppState (pool, caches, métriques), load_concurrent_renders
auth.rs Middlewares require_admin / require_read, authentification tokens
error.rs ApiError avec constructeurs bad_request, not_found, internal, etc.
books.rs CRUD livres, thumbnails
pages.rs Rendu page + double cache (mémoire LRU + disque)
libraries.rs CRUD bibliothèques, déclenchement scans
index_jobs.rs Suivi jobs, SSE streaming progression
thumbnails.rs Rebuild/regénération thumbnails
tokens.rs Gestion tokens API (create/revoke)
settings.rs Paramètres applicatifs (stockés en DB, clé limits)
openapi.rs Doc OpenAPI via utoipa, accessible sur /swagger-ui

Patterns clés

Handler type

async fn my_handler(
    State(state): State<AppState>,
    Path(id): Path<Uuid>,
) -> Result<Json<MyDto>, ApiError> {
    // ...
}

Erreurs API

// Constructeurs disponibles dans error.rs
ApiError::bad_request("message")
ApiError::not_found("resource not found")
ApiError::internal("unexpected error")
ApiError::unauthorized("missing token")
ApiError::forbidden("admin required")

// Conversion auto depuis sqlx::Error et std::io::Error

Authentification

  • Bootstrap token : comparaison directe (API_BOOTSTRAP_TOKEN), scope Admin
  • Tokens DB : format stl_<prefix>_<secret>, hash argon2 en DB, scope admin ou read
  • Middleware require_admin → routes admin ; require_read → routes lecture

OpenAPI (utoipa)

#[utoipa::path(get, path = "/books/{id}", ...)]
async fn get_book(...) { }
// Ajouter le handler dans openapi.rs (ApiDoc)

Cache pages (pages.rs)

  • Cache mémoire : LRU 512 entrées (AppState.page_cache)
  • Cache disque : IMAGE_CACHE_DIR (défaut /tmp/stripstream-image-cache), clé SHA256
  • Concurrence limitée par AppState.page_render_limit (Semaphore, configurable en DB)
  • spawn_blocking pour le rendu image (CPU-bound)

Paramètre concurrent_renders

Stocké en DB : SELECT value FROM app_settings WHERE key = 'limits' → JSON {"concurrent_renders": N}. Chargé au démarrage dans load_concurrent_renders.

Gotchas

  • LIBRARIES_ROOT_PATH : les abs_path en DB commencent par /libraries/. Appeler remap_libraries_path() avant tout accès fichier.
  • Rate limit lecture : middleware read_rate_limit sur les routes read (100 req/5s par défaut).
  • Métriques : /metrics expose requests_total, page_cache_hits, page_cache_misses (atomics dans AppState.metrics).
  • Swagger : accessible sur /swagger-ui, spec JSON sur /openapi.json.