Architecture modifiée pour Frontend-Centric Authentication: 1. **Suppression des dépendances OIDC** - quarkus-oidc → quarkus-smallrye-jwt - quarkus-keycloak-authorization → quarkus-smallrye-jwt-build - Le backend ne gère plus l'authentification OAuth 2. **Configuration JWT simple** - Validation des tokens JWT envoyés par le frontend - mp.jwt.verify.publickey.location (JWKS de Keycloak) - mp.jwt.verify.issuer (Keycloak realm) - Authentification via Authorization: Bearer header 3. **Suppression configurations OIDC** - application.properties: Suppression %dev.quarkus.oidc.* - application.properties: Suppression %prod.quarkus.oidc.* - application-prod.properties: Remplacement par mp.jwt.* - Logging: io.quarkus.oidc → io.quarkus.smallrye.jwt 4. **Sécurité simplifiée** - quarkus.security.auth.proactive=false - @Authenticated sur les endpoints - CORS configuré pour le frontend - Endpoints publics: /q/*, /openapi, /swagger-ui/* Flux d'authentification: 1️⃣ Frontend → Keycloak (OAuth login) 2️⃣ Frontend ← Keycloak (access_token) 3️⃣ Frontend → Backend (Authorization: Bearer token) 4️⃣ Backend valide le token JWT (signature + issuer) 5️⃣ Backend → Frontend (données API) Avantages: ✅ Pas de secret backend à gérer ✅ Pas de client btpxpress-backend dans Keycloak ✅ Séparation claire frontend/backend ✅ Backend devient une API REST stateless ✅ Tokens gérés par le frontend (localStorage/sessionStorage) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
195 lines
8.2 KiB
Java
195 lines
8.2 KiB
Java
package dev.lions.btpxpress.adapter.http;
|
|
|
|
import dev.lions.btpxpress.application.service.FournisseurService;
|
|
import dev.lions.btpxpress.domain.core.entity.Fournisseur;
|
|
import jakarta.inject.Inject;
|
|
import jakarta.validation.Valid;
|
|
import jakarta.ws.rs.*;
|
|
import jakarta.ws.rs.core.MediaType;
|
|
import jakarta.ws.rs.core.Response;
|
|
import org.eclipse.microprofile.openapi.annotations.Operation;
|
|
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
|
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
|
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
|
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
|
|
/**
|
|
* Resource REST pour la gestion des fournisseurs BTP
|
|
* Architecture 2025 : API complète pour la gestion des fournisseurs
|
|
*/
|
|
@Path("/api/v1/fournisseurs")
|
|
@Produces(MediaType.APPLICATION_JSON)
|
|
@Consumes(MediaType.APPLICATION_JSON)
|
|
@Tag(name = "Fournisseurs", description = "Gestion des fournisseurs BTP")
|
|
public class FournisseurResource {
|
|
|
|
@Inject
|
|
FournisseurService fournisseurService;
|
|
|
|
// === ENDPOINTS DE LECTURE - ARCHITECTURE 2025 ===
|
|
|
|
@GET
|
|
@Operation(summary = "Récupère tous les fournisseurs")
|
|
@APIResponse(responseCode = "200", description = "Liste des fournisseurs")
|
|
public Response getAllFournisseurs(
|
|
@QueryParam("page") @DefaultValue("0") int page,
|
|
@QueryParam("size") @DefaultValue("10") int size,
|
|
@QueryParam("search") String search) {
|
|
try {
|
|
List<Fournisseur> fournisseurs;
|
|
if (search != null && !search.trim().isEmpty()) {
|
|
fournisseurs = fournisseurService.searchFournisseurs(search);
|
|
} else {
|
|
fournisseurs = fournisseurService.getAllFournisseurs(page, size);
|
|
}
|
|
return Response.ok(fournisseurs).build();
|
|
} catch (Exception e) {
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity(Map.of("error", "Erreur lors de la récupération des fournisseurs"))
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/{id}")
|
|
@Operation(summary = "Récupère un fournisseur par ID")
|
|
@APIResponse(responseCode = "200", description = "Fournisseur trouvé")
|
|
@APIResponse(responseCode = "404", description = "Fournisseur non trouvé")
|
|
public Response getFournisseurById(
|
|
@Parameter(description = "ID du fournisseur") @PathParam("id") UUID id) {
|
|
try {
|
|
Fournisseur fournisseur = fournisseurService.getFournisseurById(id);
|
|
return Response.ok(fournisseur).build();
|
|
} catch (Exception e) {
|
|
return Response.status(Response.Status.NOT_FOUND)
|
|
.entity(Map.of("error", "Fournisseur non trouvé"))
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/search")
|
|
@Operation(summary = "Recherche des fournisseurs")
|
|
@APIResponse(responseCode = "200", description = "Résultats de la recherche")
|
|
public Response searchFournisseurs(@QueryParam("q") String query) {
|
|
try {
|
|
List<Fournisseur> fournisseurs = fournisseurService.searchFournisseurs(query);
|
|
return Response.ok(fournisseurs).build();
|
|
} catch (Exception e) {
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity(Map.of("error", "Erreur lors de la recherche"))
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/stats")
|
|
@Operation(summary = "Récupère les statistiques des fournisseurs")
|
|
@APIResponse(responseCode = "200", description = "Statistiques des fournisseurs")
|
|
public Response getFournisseurStats() {
|
|
try {
|
|
Map<String, Object> stats = fournisseurService.getFournisseurStats();
|
|
return Response.ok(stats).build();
|
|
} catch (Exception e) {
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity(Map.of("error", "Erreur lors du calcul des statistiques"))
|
|
.build();
|
|
}
|
|
}
|
|
|
|
// === ENDPOINTS DE CRÉATION - ARCHITECTURE 2025 ===
|
|
|
|
@POST
|
|
@Operation(summary = "Crée un nouveau fournisseur")
|
|
@APIResponse(responseCode = "201", description = "Fournisseur créé avec succès")
|
|
@APIResponse(responseCode = "400", description = "Données invalides")
|
|
@APIResponse(responseCode = "409", description = "Conflit - fournisseur existant")
|
|
public Response createFournisseur(@Valid Fournisseur fournisseur) {
|
|
try {
|
|
Fournisseur created = fournisseurService.createFournisseur(fournisseur);
|
|
return Response.status(Response.Status.CREATED)
|
|
.entity(created)
|
|
.build();
|
|
} catch (Exception e) {
|
|
return Response.status(Response.Status.BAD_REQUEST)
|
|
.entity(Map.of("error", "Erreur lors de la création du fournisseur"))
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@PUT
|
|
@Path("/{id}")
|
|
@Operation(summary = "Met à jour un fournisseur existant")
|
|
@APIResponse(responseCode = "200", description = "Fournisseur mis à jour avec succès")
|
|
@APIResponse(responseCode = "400", description = "Données invalides")
|
|
@APIResponse(responseCode = "404", description = "Fournisseur non trouvé")
|
|
public Response updateFournisseur(
|
|
@Parameter(description = "ID du fournisseur") @PathParam("id") UUID id,
|
|
@Valid Fournisseur fournisseur) {
|
|
try {
|
|
Fournisseur updated = fournisseurService.updateFournisseur(id, fournisseur);
|
|
return Response.ok(updated).build();
|
|
} catch (Exception e) {
|
|
return Response.status(Response.Status.NOT_FOUND)
|
|
.entity(Map.of("error", "Fournisseur non trouvé"))
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@DELETE
|
|
@Path("/{id}")
|
|
@Operation(summary = "Supprime un fournisseur")
|
|
@APIResponse(responseCode = "204", description = "Fournisseur supprimé avec succès")
|
|
@APIResponse(responseCode = "404", description = "Fournisseur non trouvé")
|
|
public Response deleteFournisseur(
|
|
@Parameter(description = "ID du fournisseur") @PathParam("id") UUID id) {
|
|
try {
|
|
fournisseurService.deleteFournisseur(id);
|
|
return Response.noContent().build();
|
|
} catch (Exception e) {
|
|
return Response.status(Response.Status.NOT_FOUND)
|
|
.entity(Map.of("error", "Fournisseur non trouvé"))
|
|
.build();
|
|
}
|
|
}
|
|
|
|
// === ENDPOINTS DE MODIFICATION - ARCHITECTURE 2025 ===
|
|
|
|
@PUT
|
|
@Path("/{id}/activate")
|
|
@Operation(summary = "Active un fournisseur")
|
|
@APIResponse(responseCode = "200", description = "Fournisseur activé avec succès")
|
|
@APIResponse(responseCode = "404", description = "Fournisseur non trouvé")
|
|
public Response activateFournisseur(
|
|
@Parameter(description = "ID du fournisseur") @PathParam("id") UUID id) {
|
|
try {
|
|
fournisseurService.activateFournisseur(id);
|
|
return Response.ok(Map.of("message", "Fournisseur activé avec succès")).build();
|
|
} catch (Exception e) {
|
|
return Response.status(Response.Status.NOT_FOUND)
|
|
.entity(Map.of("error", "Fournisseur non trouvé"))
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@PUT
|
|
@Path("/{id}/deactivate")
|
|
@Operation(summary = "Désactive un fournisseur")
|
|
@APIResponse(responseCode = "200", description = "Fournisseur désactivé avec succès")
|
|
@APIResponse(responseCode = "404", description = "Fournisseur non trouvé")
|
|
public Response deactivateFournisseur(
|
|
@Parameter(description = "ID du fournisseur") @PathParam("id") UUID id) {
|
|
try {
|
|
fournisseurService.deactivateFournisseur(id);
|
|
return Response.ok(Map.of("message", "Fournisseur désactivé avec succès")).build();
|
|
} catch (Exception e) {
|
|
return Response.status(Response.Status.NOT_FOUND)
|
|
.entity(Map.of("error", "Fournisseur non trouvé"))
|
|
.build();
|
|
}
|
|
}
|
|
}
|