diff --git a/pom.xml b/pom.xml
index f3cc02a6cc8c809c1fc2db1cffc0582dff60199a..9512a90d67ad8a303ba673eb634647852ed03a46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,18 +31,28 @@
             <artifactId>jersey-hk2</artifactId>
         </dependency>
 
-        <!-- uncomment this to get JSON support:
-         <dependency>
+        <dependency>
             <groupId>org.glassfish.jersey.media</groupId>
-            <artifactId>jersey-media-json-binding</artifactId>
+            <artifactId>jersey-media-json-jackson</artifactId>
+            <version>${jersey.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.jaxrs</groupId>
+            <artifactId>jackson-jaxrs-json-provider</artifactId>
+            <version>2.12.3</version>
         </dependency>
-        -->
+
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.12</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>commons-net</groupId>
+            <artifactId>commons-net</artifactId>
+            <version>3.8.0</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/src/main/java/fil/sr2/flopbox/AuthFilter.java b/src/main/java/fil/sr2/flopbox/AuthFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce1d73817e9bbebb50d61ec70043e5b1f44fa8e6
--- /dev/null
+++ b/src/main/java/fil/sr2/flopbox/AuthFilter.java
@@ -0,0 +1,76 @@
+package fil.sr2.flopbox;
+
+import java.io.IOException;
+import java.util.Map;
+
+import jakarta.annotation.Priority;
+import jakarta.ws.rs.Priorities;
+import jakarta.ws.rs.core.*;
+import jakarta.ws.rs.container.*;
+import jakarta.ws.rs.ext.Provider;
+
+@Provider
+@Priority(Priorities.AUTHENTICATION)
+public class AuthFilter implements ContainerRequestFilter {
+    private static final Map<String, String> USERS = loadUsers();
+
+    /*
+     * @Override
+     * public void filter(ContainerRequestContext requestContext) throws IOException
+     * {
+     * // Récupération de l'en-tête Authorization (ex.
+     * "Bearer flopbox-secret-token")
+     * String authHeader =
+     * requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
+     * if (authHeader == null || !authHeader.startsWith("Bearer ")) {
+     * requestContext.abortWith(
+     * Response.status(Response.Status.UNAUTHORIZED)
+     * .entity("En-tête Authorization manquant ou invalide")
+     * .build());
+     * return;
+     * }
+     * String token = authHeader.substring("Bearer ".length());
+     * // Vérification du token (dans une implémentation réelle, vérification par
+     * // rapport à une liste de comptes stockés)
+     * if (!"flopbox-secret-token".equals(token)) {
+     * requestContext.abortWith(
+     * Response.status(Response.Status.UNAUTHORIZED)
+     * .entity("Token invalide")
+     * .build());
+     * return;
+     * }
+     * // Si le token est valide, la requête continue.
+     * }
+     */
+
+    @Override
+    public void filter(ContainerRequestContext ctx) throws IOException {
+        String token = extractToken(ctx);
+        if (!isValidToken(token)) {
+            ctx.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
+        }
+    }
+
+    // Method to extract token from the request headers
+    private String extractToken(ContainerRequestContext ctx) {
+        // Extract the "Authorization" header
+        String authorizationHeader = ctx.getHeaderString(HttpHeaders.AUTHORIZATION);
+        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
+            return authorizationHeader.substring(7); // Extract the token after "Bearer "
+        }
+        return null; // No token found
+    }
+
+    private boolean isValidToken(String token) {
+        // You can implement your own validation logic here (e.g., checking against a
+        // database)
+        return USERS.containsKey(token); // Just an example where token is checked against a map
+    }
+
+    private static Map<String, String> loadUsers() {
+        // Implémenter le chargement depuis un fichier (ex. users.properties)
+        return Map.of(
+                "valid-token-1", "User1",
+                "valid-token-2", "User2");
+    }
+}
diff --git a/src/main/java/fil/sr2/flopbox/FTPResource.java b/src/main/java/fil/sr2/flopbox/FTPResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..521c49111feeac211c20b4afaf10a81b0e8bd620
--- /dev/null
+++ b/src/main/java/fil/sr2/flopbox/FTPResource.java
@@ -0,0 +1,211 @@
+package fil.sr2.flopbox;
+
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.SocketException;
+import java.net.URI;
+import java.util.List;
+
+import org.apache.commons.net.ftp.FTP;
+import org.apache.commons.net.ftp.FTPClient;
+
+@Path("/ftps")
+public class FTPResource {
+
+    // GET /ftps - liste des serveurs FTP enregistrés
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response listFTPServers() {
+        List<FTPServerConfig> servers = FTPServerRepository.getInstance().getAllServers();
+        return Response.ok(servers).build();
+    }
+
+    // POST /ftps - enregistre un nouveau serveur FTP
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response addFTPServer(FTPServerConfig config, @Context UriInfo uriInfo) {
+        boolean created = FTPServerRepository.getInstance().addServer(config);
+        if (!created) {
+            return Response.status(Response.Status.CONFLICT)
+                    .entity("Alias déjà utilisé").build();
+        }
+        UriBuilder builder = uriInfo.getAbsolutePathBuilder();
+        builder.path(config.getAlias());
+        return Response.created(builder.build()).entity(config).build();
+    }
+
+    // GET /ftps/{alias}/{path} - récupère le contenu d'un répertoire ou d'un
+    // fichier sur le serveur FTP
+    @GET
+    @Path("/{alias}/{path: .+}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getFTPContent(
+            @PathParam("alias") String alias,
+            @PathParam("path") String path,
+            @HeaderParam("X-FTP-User") String ftpUser,
+            @HeaderParam("X-FTP-Pass") String ftpPass) {
+        FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
+        if (config == null) {
+            return Response.status(Response.Status.NOT_FOUND)
+                    .entity("Alias de serveur FTP non trouvé").build();
+        }
+        FTPClient ftpClient = new FTPClient();
+        try {
+            ftpClient.connect(config.getHost(), config.getPort());
+            boolean login = ftpClient.login(ftpUser, ftpPass);
+            if (!login) {
+                return Response.status(Response.Status.UNAUTHORIZED)
+                        .entity("Échec de l'authentification FTP").build();
+            }
+            // Pour cet exemple, on se place dans le répertoire demandé et on renvoie la
+            // liste des fichiers.
+            ftpClient.changeWorkingDirectory(path);
+            String[] files = ftpClient.listNames();
+            ftpClient.logout();
+            ftpClient.disconnect();
+            return Response.ok(files).build();
+        } catch (IOException ex) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
+                    .entity("Erreur FTP : " + ex.getMessage()).build();
+        }
+    }
+
+    @DELETE
+    @Path("/{alias}")
+    public Response removeFTPServer(@PathParam("alias") String alias) {
+        boolean removed = FTPServerRepository.getInstance().removeServer(alias);
+        return removed ? Response.noContent().build() : Response.status(Response.Status.NOT_FOUND).build();
+    }
+
+    @PUT
+    @Path("/{alias}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response updateFTPServer(@PathParam("alias") String alias, FTPServerConfig newConfig) {
+        boolean updated = FTPServerRepository.getInstance().updateServer(alias, newConfig);
+        return updated ? Response.ok(newConfig).build() : Response.status(Response.Status.NOT_FOUND).build();
+    }
+
+    @GET
+    @Path("/{alias}/{path: .+}/download")
+    @Produces(MediaType.APPLICATION_OCTET_STREAM)
+    public Response downloadFile(
+            @PathParam("alias") String alias,
+            @PathParam("path") String path,
+            @HeaderParam("X-FTP-User") String user,
+            @HeaderParam("X-FTP-Pass") String pass) {
+
+        FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
+        FTPClient ftp = new FTPClient();
+        try {
+            ftp.connect(config.getHost(), config.getPort());
+            ftp.login(user, pass);
+            InputStream is = ftp.retrieveFileStream(path);
+            return Response.ok(is)
+                    .header("Content-Disposition", "attachment; filename=\"" + getFileName(path) + "\"")
+                    .build();
+        } catch (IOException e) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        } finally {
+            try {
+                if (ftp.isConnected()) {
+                    ftp.logout();
+                    ftp.disconnect();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @PUT
+    @Path("/{alias}/{path: .+}/upload")
+    @Consumes(MediaType.APPLICATION_OCTET_STREAM)
+    public Response uploadFile(
+            @PathParam("alias") String alias,
+            @PathParam("path") String path,
+            @HeaderParam("X-FTP-User") String user,
+            @HeaderParam("X-FTP-Pass") String pass,
+            InputStream fileStream) {
+
+        FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
+        FTPClient ftp = new FTPClient();
+        try {
+            ftp.connect(config.getHost(), config.getPort());
+            ftp.login(user, pass);
+            boolean success = ftp.storeFile(path, fileStream);
+            return success ? Response.ok().build() : Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        } catch (IOException e) {
+            return Response.serverError().entity(e.getMessage()).build();
+        } finally {
+            try {
+                if (ftp.isConnected()) {
+                    ftp.logout();
+                    ftp.disconnect();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @POST
+    @Path("/{alias}/{path: .+}/mkdir")
+    public Response createDirectory(
+            @PathParam("alias") String alias,
+            @PathParam("path") String path,
+            @HeaderParam("X-FTP-User") String user,
+            @HeaderParam("X-FTP-Pass") String pass) throws SocketException, IOException {
+
+        FTPClient ftp = new FTPClient();
+        try {
+            FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
+            ftp.connect(config.getHost(), config.getPort());
+            ftp.login(user, pass);
+            return ftp.makeDirectory(path) ? Response.created(URI.create(path)).build()
+                    : Response.status(Response.Status.BAD_REQUEST).build();
+        } finally {
+            try {
+                if (ftp.isConnected()) {
+                    ftp.logout();
+                    ftp.disconnect();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @DELETE
+    @Path("/{alias}/{path: .+}/rmdir")
+    public Response deleteDirectory(
+            @PathParam("alias") String alias,
+            @PathParam("path") String path,
+            @HeaderParam("X-FTP-User") String user,
+            @HeaderParam("X-FTP-Pass") String pass) throws SocketException, IOException {
+
+        FTPClient ftp = new FTPClient();
+        try {
+            FTPServerConfig config = FTPServerRepository.getInstance().getServer(alias);
+            ftp.connect(config.getHost(), config.getPort());
+            ftp.login(user, pass);
+            return ftp.removeDirectory(path) ? Response.noContent().build()
+                    : Response.status(Response.Status.NOT_FOUND).build();
+        } finally {
+            try {
+                if (ftp.isConnected()) {
+                    ftp.logout();
+                    ftp.disconnect();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private String getFileName(String path) {
+        return path.substring(path.lastIndexOf('/') + 1);
+    }
+}
diff --git a/src/main/java/fil/sr2/flopbox/FTPServerConfig.java b/src/main/java/fil/sr2/flopbox/FTPServerConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a1ea89e15748bd8884289d800d7fa0d01219f30
--- /dev/null
+++ b/src/main/java/fil/sr2/flopbox/FTPServerConfig.java
@@ -0,0 +1,41 @@
+package fil.sr2.flopbox;
+
+public class FTPServerConfig {
+    private String alias;
+    private String host;
+    private int port;
+
+    public FTPServerConfig() {
+    }
+
+    public FTPServerConfig(String alias, String host, int port) {
+        this.alias = alias;
+        this.host = host;
+        this.port = port;
+    }
+
+    // Getters et setters
+    public String getAlias() {
+        return alias;
+    }
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+}
diff --git a/src/main/java/fil/sr2/flopbox/FTPServerRepository.java b/src/main/java/fil/sr2/flopbox/FTPServerRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..afe3c0654565d4768129390182904b7b074820bd
--- /dev/null
+++ b/src/main/java/fil/sr2/flopbox/FTPServerRepository.java
@@ -0,0 +1,42 @@
+package fil.sr2.flopbox;
+
+import java.util.*;
+
+public class FTPServerRepository {
+    private static FTPServerRepository instance = new FTPServerRepository();
+    private Map<String, FTPServerConfig> serverMap = new HashMap<>();
+
+    private FTPServerRepository() {
+    }
+
+    public static FTPServerRepository getInstance() {
+        return instance;
+    }
+
+    public List<FTPServerConfig> getAllServers() {
+        return new ArrayList<>(serverMap.values());
+    }
+
+    public FTPServerConfig getServer(String alias) {
+        return serverMap.get(alias);
+    }
+
+    public boolean addServer(FTPServerConfig config) {
+        if (serverMap.containsKey(config.getAlias())) {
+            return false;
+        }
+        serverMap.put(config.getAlias(), config);
+        return true;
+    }
+
+    public boolean removeServer(String alias) {
+        return serverMap.remove(alias) != null;
+    }
+
+    public boolean updateServer(String alias, FTPServerConfig newConfig) {
+        if (!serverMap.containsKey(alias))
+            return false;
+        serverMap.put(alias, newConfig);
+        return true;
+    }
+}
diff --git a/src/main/java/fil/sr2/flopbox/Main.java b/src/main/java/fil/sr2/flopbox/Main.java
index ecede9fa28b61bbc15b373b7fc920f6ee0ac1e8e..4d067676d357a11ab04017ea1f7ddc07843a0632 100644
--- a/src/main/java/fil/sr2/flopbox/Main.java
+++ b/src/main/java/fil/sr2/flopbox/Main.java
@@ -4,41 +4,26 @@ import org.glassfish.grizzly.http.server.HttpServer;
 import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
 import org.glassfish.jersey.server.ResourceConfig;
 
-import java.io.IOException;
 import java.net.URI;
 
-/**
- * Main class.
- *
- */
 public class Main {
-    // Base URI the Grizzly HTTP server will listen on
     public static final String BASE_URI = "http://localhost:8080/";
 
-    /**
-     * Starts Grizzly HTTP server exposing JAX-RS resources defined in this application.
-     * @return Grizzly HTTP server.
-     */
     public static HttpServer startServer() {
-        // create a resource config that scans for JAX-RS resources and providers
-        // in fil.sr2.flopbox package
-        final ResourceConfig rc = new ResourceConfig().packages("fil.sr2.flopbox");
-
-        // create and start a new instance of grizzly http server
-        // exposing the Jersey application at BASE_URI
+        // On configure Jersey pour scanner le package "com.flopbox" (ressources,
+        // filtres, etc.)
+        final ResourceConfig rc = new ResourceConfig().packages("fil.sr2.flopbox")
+                .register(org.glassfish.jersey.jackson.JacksonFeature.class);
         return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
     }
 
-    /**
-     * Main method.
-     * @param args
-     * @throws IOException
-     */
-    public static void main(String[] args) throws IOException {
+    public static void main(String[] args) {
         final HttpServer server = startServer();
-        System.out.println(String.format("Jersey app started with endpoints available at "
-                + "%s%nHit Ctrl-C to stop it...", BASE_URI));
-        System.in.read();
-        server.stop();
+        System.out.println("FlopBox API démarrée sur " + BASE_URI);
+        try {
+            Thread.currentThread().join();
+        } catch (InterruptedException ex) {
+            System.err.println("Serveur interrompu : " + ex.getMessage());
+        }
     }
 }
diff --git a/src/test/java/fil/sr2/flopbox/MyResourceTest.java b/src/test/java/fil/sr2/flopbox/MyResourceTest.javaTEMPO
similarity index 100%
rename from src/test/java/fil/sr2/flopbox/MyResourceTest.java
rename to src/test/java/fil/sr2/flopbox/MyResourceTest.javaTEMPO