API REST v1 — Référence
L'API REST gitrust est accessible sous le préfixe /api/v1/. Elle accepte et retourne du JSON. Elle partage les mêmes services que l'interface web SSR.
Documentation interactive : GET /api/docs sur toute instance gitrust.
Authentification
Deux méthodes sont acceptées sur tous les endpoints authentifiés :
Cookie JWT (navigateur) — défini automatiquement par le serveur après login :
Cookie: jwt_token=<JWT>
Bearer PAT (clients API, CI, scripts) — Personal Access Token créé depuis /settings/tokens :
Authorization: Bearer <PAT>
Les PAT supportent des scopes : repo:read, repo:write, issues:read, issues:write, pulls:read, pulls:write, ci:read, ci:write.
Un endpoint qui requiert repo:write rejette un PAT avec seulement repo:read avec 403 Forbidden.
Format des erreurs
Toutes les erreurs retournent ce format JSON :
{ "error": "not_found", "message": "Le dépôt 'alice/myrepo' est introuvable", "status": 404 }
Codes d'erreur standards :
error | HTTP | Cause |
|---|---|---|
unauthorized | 401 | Token absent, expiré ou invalide |
forbidden | 403 | Permissions insuffisantes |
not_found | 404 | Ressource inexistante |
validation_error | 400 | Corps de requête invalide |
conflict | 409 | Contrainte d'unicité violée |
rate_limited | 429 | Trop de requêtes |
internal_error | 500 | Erreur serveur |
Pagination
Tous les endpoints de liste acceptent ?page=N&per_page=N (défaut : page=1, per_page=30, max per_page=100).
Headers de réponse :
X-Total-Count: 142 X-Page: 2 X-Per-Page: 30
Rate limiting
En cas de dépassement : 429 Too Many Requests avec Retry-After: <secondes>.
Endpoints
Authentification
POST /api/v1/auth/login
// Requête { "username": "alice", "password": "SecurePass123!" } // Réponse 200 (sans 2FA) { "access_token": "<JWT>", "refresh_token": "<token>", "token_type": "Bearer", "user": { "id": "...", "username": "alice", "email": "alice@example.com" } } // Réponse 200 (avec 2FA activé) { "requires_2fa": true, "challenge_token": "abc123..." }
POST /api/v1/auth/2fa/verify
// Requête { "challenge_token": "abc123...", "code": "123456" } // Réponse 200 { "access_token": "...", "refresh_token": "...", "user": { ... } }
POST /api/v1/auth/refresh
// Requête { "refresh_token": "<token>" } // Réponse 200 { "access_token": "<nouveau JWT>", "refresh_token": "<nouveau token>" }
GET /api/v1/auth/me
// Réponse 200 { "id": "550e8400-e29b-41d4-a716-446655440000", "username": "alice", "email": "alice@example.com", "email_verified": true, "roles": ["user"] }
Utilisateur courant
GET /api/v1/user
Profil de l'utilisateur authentifié. Identique à GET /api/v1/auth/me.
GET /api/v1/user/repos
Liste les dépôts accessibles par l'utilisateur courant (possédés + partagés).
// Réponse 200 { "items": [ { "id": "...", "owner": "alice", "slug": "myrepo", "description": "Mon premier dépôt", "default_branch": "main", "is_empty": false, "is_public": false, "created_at": "2026-03-25T10:00:00Z" } ], "total": 1, "page": 1, "per_page": 30 }
Dépôts
GET /api/v1/repos/{owner}/{repo}
// Réponse 200 { "id": "...", "owner": "alice", "slug": "myrepo", "description": "Mon dépôt", "default_branch": "main", "is_empty": false, "is_public": true, "created_at": "2026-03-25T10:00:00Z", "updated_at": "2026-04-01T12:00:00Z" }
GET /api/v1/repos/{owner}/{repo}/branches
// Réponse 200 { "items": [ { "name": "main", "is_default": true, "commit_sha": "abc123..." }, { "name": "feat/42-fix", "is_default": false, "commit_sha": "def456..." } ], "total": 2, "page": 1, "per_page": 30 }
GET /api/v1/repos/{owner}/{repo}/commits
Paramètres optionnels : ?ref=main&page=1&per_page=30
// Réponse 200 { "items": [ { "sha": "abc123...", "message": "feat: ajouter la signature HMAC", "author": { "name": "Alice", "email": "alice@example.com" }, "committed_at": "2026-04-01T09:00:00Z" } ], "total": 42, "page": 1, "per_page": 30 }
Issues
GET /api/v1/repos/{owner}/{repo}/issues
Paramètres : ?state=open&page=1&per_page=30
// Réponse 200 { "items": [ { "id": "...", "number": 1, "title": "Bug dans le parser", "state": "open", "author": "alice", "labels": [ { "name": "bug", "color": "#d73a4a", "type": "classification" } ], "comment_count": 3, "created_at": "2026-03-27T08:00:00Z" } ], "total": 5, "page": 1, "per_page": 30 }
POST /api/v1/repos/{owner}/{repo}/issues
// Requête { "title": "Nouveau bug", "body": "Description en Markdown..." } // Réponse 201 { "id": "...", "number": 6, "title": "Nouveau bug", "state": "open", ... }
PATCH /api/v1/repos/{owner}/{repo}/issues/{num}
// Requête (tous les champs optionnels) { "state": "closed" } // Réponse 200 { "id": "...", "number": 1, "state": "closed", ... }
Pull Requests
GET /api/v1/repos/{owner}/{repo}/pulls
Paramètres : ?state=open&page=1&per_page=30
POST /api/v1/repos/{owner}/{repo}/pulls
// Requête { "title": "feat: ajouter la signature HMAC", "body": "Closes #42", "source_branch": "feat/42-hmac", "target_branch": "main" } // Réponse 201 { "id": "...", "number": 3, "state": "open", ... }
POST /api/v1/repos/{owner}/{repo}/pulls/{num}/merge
// Requête { "merge_method": "merge" } // merge_method: "merge" | "squash" | "rebase" // Réponse 200 { "merged": true, "merge_commit_sha": "abc123..." }
CI Pipelines
GET /api/v1/repos/{owner}/{repo}/ci/pipelines
Paramètres : ?page=1&per_page=30
// Réponse 200 { "items": [ { "id": "...", "status": "success", "trigger": "push", "ref": "main", "commit_sha": "abc123...", "duration_ms": 45000, "created_at": "2026-04-01T09:00:00Z" } ], "total": 12, "page": 1, "per_page": 30 }
POST /api/v1/repos/{owner}/{repo}/ci/pipelines/trigger
Déclenche un pipeline manuellement.
// Requête { "ref": "main" } // Réponse 201 { "id": "...", "status": "pending", ... }
GET /api/v1/repos/{owner}/{repo}/ci/pipelines/{id}/logs
// Réponse 200 { "items": [ { "line": 1, "content": "$ cargo build --release", "stream": "stdout", "timestamp": "..." }, { "line": 2, "content": " Compiling gitrust v0.1.0", "stream": "stdout", "timestamp": "..." } ], "total": 248, "page": 1, "per_page": 100 }
Exemples clients
Lister les issues avec curl
curl -s \ -H "Authorization: Bearer ${GITRUST_PAT}" \ "https://demo.gitrust.eu/api/v1/repos/alice/myrepo/issues?state=open" \ | jq '.items[].title'
Créer une issue avec Python
import httpx client = httpx.Client( base_url="https://demo.gitrust.eu", headers={"Authorization": f"Bearer {PAT}"}, ) resp = client.post( "/api/v1/repos/alice/myrepo/issues", json={"title": "Bug trouvé", "body": "Description..."}, ) resp.raise_for_status() print(resp.json()["number"])
Créer une issue avec Rust
use reqwest::Client; use serde_json::json; let client = Client::new(); let resp = client .post("https://demo.gitrust.eu/api/v1/repos/alice/myrepo/issues") .bearer_auth(&pat) .json(&json!({"title": "Bug", "body": "..."})) .send() .await?; println!("Issue #{}", resp.json::<serde_json::Value>().await?["number"]);
GitRust