feat(admin): crée un ticket Forgejo depuis un message de contact (#81) #125
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/forgejo-issue-from-contact-message"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Depuis
/admin/messages(issue #80), un admin peut créer une issue Forgejo à partir d'un message utilisateur en un geste : un bouton ouvre une modale pré-remplie (titre + corps éditables), l'admin ajuste et confirme, l'app crée l'issue via l'API Forgejo et mémorise le lien sur le message (#N, le bouton disparaît). Première intégration Forgejo d'apps/api.Dépend de #80 (mergé). 1 PR.
Contenu
Modèle — delta sur
ContactMessage:forgejoIssueNumber/forgejoIssueUrlnullables (migration20260908130000_contact_message_forgejo_link, aucun backfill).Config (
config/env.ts+.env.example+ci.yml) —FORGEJO_BASE_URL(.url()),FORGEJO_TOKEN(secret),FORGEJO_REPO(owner/repo), toutes.optional().ForgejoService.isConfigured()les vérifie ensemble ; sans les trois, le bouton est masqué et l'endpoint répond503(même posture fail-closed queADMIN_JWT_SECRET).ForgejoService(lib/forgejo/forgejo.service.ts, singletonforgejo) —createIssue({ title, body })→POST {BASE}/api/v1/repos/{REPO}/issues, en-têteAuthorization: token <pat>(schéma PAT Forgejo), lit{ number, html_url }. Viafetchglobal (commemobile.service.ts). Erreur réseau / non-2xx / JSON invalide ⇒HttpError 502 FORGEJO_REQUEST_FAILED.admin-contact(issue #80, étendu) —POST /admin/api/contact/:id/forgejo-issue(createForgejoIssueSchema) : ordre503→404 CONTACT_MESSAGE_NOT_FOUND→409 CONTACT_MESSAGE_ALREADY_LINKED→ appel Forgejo → écriture BDD. Un échec Forgejo (502) ne touche pas la BDD — pas d'état partiel. Renvoie leContactMessageAdminViewà jour.GET /admin/api/contact/forgejo/status→{ enabled }.Contrat partagé —
createForgejoIssueSchema(title 1–255 = limite Forgejo, body 1–60000),ContactMessageAdminView+forgejoIssueNumber/forgejoIssueUrl,ForgejoIntegrationStatus,ErrorCode:FORGEJO_NOT_CONFIGURED=5001,FORGEJO_REQUEST_FAILED=5002,CONTACT_MESSAGE_ALREADY_LINKED=4026,CONTACT_MESSAGE_NOT_FOUND=4055 + labels danserrors.*.Front —
MessagesPagesondeforgejo/statusau montage ; colonne « Ticket » : lien#N(target="_blank") si lié, bouton « Créer un ticket » sienabled, note « intégration Forgejo non configurée » sinon.CreateForgejoIssueModal(via le primitifDialog) + helpers pursmessages.ts(buildIssueTitle=[Bug] <1ʳᵉ ligne>,buildIssueBody= texte + bloc---contexte + pied). Aucun label auto (décision #81). Erreurs viaErrorMessageService, jamaiserr.message.Tests
apps/api/test/forgejo.service.test.ts—globalThis.fetchréassigné : URL / en-têtetoken/ corps{title, body};502sur non-2xx et sur erreur réseau.apps/api/test/admin-contact.test.ts(étendu) — status ;503(env absent) ; nominal (stocke le lien, renvoie la vue) ;404;409;502+ ligne intacte ;400titre vide.apps/web/cypress/e2e/admin-messages.cy.ts(étendu) — modale pré-remplie,#Naprès création, pas de bouton sienabled:false(+ note), lien affiché pour un message déjà lié.Vérifié localement
pnpm lint✅ ·pnpm build(tsc api + web, vite) ✅ · Cypressadmin-messages+admin-corrections+admin-layout: 11/11 ✅. Mocha → CI (pas de Postgres local).Closes #81.
🤖 Generated with Claude Code
Depuis /admin/messages, un admin peut créer une issue Forgejo à partir d'un message utilisateur : bouton « Créer un ticket » → modale pré-remplie éditable → l'issue est créée via l'API Forgejo et le lien (#N) est mémorisé sur le message. Première intégration Forgejo d'apps/api. - Modèle : `forgejoIssueNumber` / `forgejoIssueUrl` sur `ContactMessage` (migration `20260908130000_contact_message_forgejo_link`, 2 colonnes nullables, aucun backfill). - Config : `FORGEJO_BASE_URL` / `FORGEJO_TOKEN` / `FORGEJO_REPO`, toutes `.optional()` — **fail-closed** (même posture que `ADMIN_JWT_SECRET`) : bouton masqué + endpoint `503` tant que les trois ne sont pas là. Documentées dans `.env.example` ; valeurs de test dans `ci.yml`. - `ForgejoService` (`lib/forgejo/forgejo.service.ts`, singleton `forgejo`) : `isConfigured()` + `createIssue({ title, body })` → `POST .../issues`, en-tête `Authorization: token <pat>`, lit `{ number, html_url }` ; erreur réseau / non-2xx / JSON invalide ⇒ `HttpError 502 FORGEJO_REQUEST_FAILED`. - `admin-contact` : `POST /admin/api/contact/:id/forgejo-issue` (`createForgejoIssueFromMessage` — ordre 503 → 404 → 409 → Forgejo → écriture BDD ; un échec Forgejo ne touche pas la BDD) et `GET /admin/api/contact/forgejo/status`. - Contrat partagé : `createForgejoIssueSchema` (title 1–255, body 1–60000), `ContactMessageAdminView` étendu, `ForgejoIntegrationStatus`, nouveaux `ErrorCode` (`FORGEJO_NOT_CONFIGURED`=5001, `FORGEJO_REQUEST_FAILED`=5002, `CONTACT_MESSAGE_ALREADY_LINKED`=4026, `CONTACT_MESSAGE_NOT_FOUND`=4055) + labels i18n. - Front : `MessagesPage` — sonde `forgejo/status` au montage, colonne « Ticket » (lien `#N` si lié, bouton sinon, « non configurée » sinon), `CreateForgejoIssueModal` + helpers purs `messages.ts` (`buildIssueTitle` / `buildIssueBody`). - Tests : `forgejo.service.test.ts` (stub `globalThis.fetch`), `admin-contact.test.ts` étendu (nominal / 503 / 404 / 409 / 502 + ligne intacte / 400), `admin-messages.cy.ts` étendu (modale pré-remplie, lien après création, masquage si non configuré). - Docs : `backend-architecture.md`, `batch-cooking-modele.md`. Closes #81. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>