package fr.titionfire.ffsaf.rest; import fr.titionfire.ffsaf.domain.service.MembreService; import fr.titionfire.ffsaf.rest.data.SimpleMembre; import fr.titionfire.ffsaf.rest.from.FullMemberForm; import fr.titionfire.ffsaf.utils.Pair; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.unchecked.Unchecked; import jakarta.annotation.security.RolesAllowed; import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import org.apache.commons.io.FileUtils; import org.apache.tika.mime.MimeTypeException; import org.apache.tika.mime.MimeTypes; import org.eclipse.microprofile.config.inject.ConfigProperty; import java.io.*; import java.net.URI; import java.net.URISyntaxException; import java.net.URLConnection; import java.nio.file.Files; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; @Path("api/member") public class CombEndpoints { @Inject MembreService membreService; @ConfigProperty(name = "upload_dir") String media; @GET @Path("/all") @RolesAllowed("federation_admin") @Produces(MediaType.APPLICATION_JSON) public Uni> getAll() { return membreService.getAll().map(membreModels -> membreModels.stream().map(SimpleMembre::fromModel).toList()); } @GET @Path("{id}") @RolesAllowed("federation_admin") @Produces(MediaType.APPLICATION_JSON) public Uni getById(@PathParam("id") long id) { return membreService.getById(id).map(SimpleMembre::fromModel); } @POST @Path("{id}") @RolesAllowed("federation_admin") @Produces(MediaType.TEXT_PLAIN) @Consumes(MediaType.MULTIPART_FORM_DATA) public Uni setAdminMembre(@PathParam("id") long id, FullMemberForm input) { Future future = CompletableFuture.supplyAsync(() -> { try (InputStream is = new BufferedInputStream(new ByteArrayInputStream(input.getPhoto_data()))) { String mimeType = URLConnection.guessContentTypeFromStream(is); String extension = MimeTypes.getDefaultMimeTypes().forName(mimeType).getExtension(); FileUtils.writeByteArrayToFile(new File(media, "ppMembre/" + input.getId() + extension), input.getPhoto_data()); return "OK"; } catch (IOException | MimeTypeException e) { return e.getMessage(); } }); if (input.getPhoto_data().length > 0) { return membreService.update(id, input) .invoke(Unchecked.consumer(out -> { if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out); })) .chain(() -> Uni.createFrom().future(future)) .invoke(Unchecked.consumer(out -> { if (!out.equals("OK")) throw new InternalError("Fail to get MimeType " + out); })); } else { return membreService.update(id, input) .invoke(Unchecked.consumer(out -> { if (!out.equals("OK")) throw new InternalError("Fail to update data: " + out); })); } } @GET @Path("{id}/photo") @RolesAllowed("federation_admin") public Uni getPhoto(@PathParam("id") long id) throws URISyntaxException { Future> future = CompletableFuture.supplyAsync(() -> { FilenameFilter filter = (directory, filename) -> filename.startsWith(String.valueOf(id)); File[] files = new File(media, "ppMembre").listFiles(filter); if (files != null && files.length > 0) { File file = files[0]; try { byte[] data = Files.readAllBytes(file.toPath()); return new Pair<>(file, data); } catch (IOException ignored) { } } return null; }); URI uri = new URI("https://mdbcdn.b-cdn.net/img/Photos/new-templates/bootstrap-chat/ava2.webp"); return Uni.createFrom().future(future) .map(filePair -> { if (filePair == null) return Response.temporaryRedirect(uri).build(); String mimeType = URLConnection.guessContentTypeFromName(filePair.getKey().getName()); Response.ResponseBuilder resp = Response.ok(filePair.getValue()); resp.type(MediaType.APPLICATION_OCTET_STREAM); resp.header(HttpHeaders.CONTENT_LENGTH, filePair.getValue().length); resp.header(HttpHeaders.CONTENT_TYPE, mimeType); resp.header(HttpHeaders.CONTENT_DISPOSITION, "inline; "); return resp.build(); }); } }