Table des matières
SpringBootFilesUploader
SpringBootFilesUploader est une API REST en Kotlin construite sur Spring Boot 2.6.3. Son nom décrit sa première tâche : recevoir un fichier, en placer les octets dans un compartiment compatible S3 et sa description dans MongoDB, puis le resservir par son identifiant. Le code va plus loin que le nom. La même application liste et contrôle des conteneurs Docker, interroge un serveur Caddy et exécute des commandes par SSH, le tout derrière une authentification JWT à trois rôles. Le dernier commit date de février 2022 et le README tient en une ligne : tout ce qui suit vient du code source.
1. Aperçu
Six contrôleurs composent l’API. Les chemins partent de la racine du serveur ; /auth/** et la documentation de l’API sont publics, tout le reste exige un jeton porteur (SecurityConf.kt).
| Contrôleur | Points d’accès | Rôles dans @PreAuthorize |
|---|---|---|
AuthController (/auth) | POST /signin, POST /signup, POST /signup/admin | publics ; /signup/admin exige ADMIN |
FilesController (/files) | POST /documents (champ multipart document), GET /{id} | envoi : MODERATOR ou ADMIN ; téléchargement : tout rôle |
UserController (/user) | GET /{id}/page, GET /profile, GET /mod, GET /admin | selon le point d’accès |
DockerController (/containers) | GET /ping, GET /list, PUT /create, POST /start/{name}, /stop/{name}, /restart/{name}, /rename/{name}/{newName} | MOD ou ADMIN ; création et renommage ADMIN |
CaddyController (/caddy) | GET /ping | authentifié |
SSHController (/ssh) | POST /cli | ADMIN |
La page OpenAPI est servie par springdoc à /api-docs (application.yml).
2. Cas d’utilisation
API Spring Boot en Kotlin qui range les fichiers téléversés dans MinIO/S3 derrière des rôles JWT, avec des routes Docker et SSH.
- S'inscrire et obtenir un JWT
- Utilisateur de l'APIMinIO S3 et MongoDB
POST /auth/signup enregistre l'utilisateur dans MongoDB avec son mot de passe haché et ses rôles ; POST /auth/signin renvoie un JwtResponse avec le jeton et les rôles (AuthController.kt, JwtUtils.kt).
- Téléverser un document
- Modérateur ou adminMinIO S3 et MongoDB
POST /files/documents (modérateur ou admin) valide l'extension avec DOC_FILE_EXTENSION_REGEX, écrit le fichier dans le bucket S3 sous files/<uuid>/<nom> et note ses métadonnées dans MongoDB.
- Télécharger un fichier stocké
- Utilisateur de l'APIMinIO S3 et MongoDB
GET /files/{id} retrouve la fiche du fichier dans MongoDB, lit l'objet dans S3 et le renvoie en pièce jointe avec son type et sa taille.
- Gérer des conteneurs Docker
- Modérateur ou admin
Les routes /containers listent, créent, démarrent, arrêtent, redémarrent et renomment des conteneurs avec docker-java, sur l'hôte Docker défini dans application.yml (tcp://127.0.0.1:2375).
- Lancer une commande SSH
- Modérateur ou admin
POST /ssh/cli (admin seulement) ouvre une session Apache SSHD avec l'hôte, le port et les identifiants reçus, puis renvoie la sortie de la commande.
2.1. Scénario : Déposer un PDF puis le récupérer
- Démarrer MongoDB sur localhost:27017 et MinIO sur localhost:9000, comme le prévoit application.yml, puis lancer ./gradlew bootRun.
- Envoyer POST /auth/signup avec un nom d'utilisateur, un courriel, un mot de passe et le rôle « mod ».
- Appeler POST /auth/signin et copier le JWT de la réponse.
- Envoyer le PDF à POST /files/documents dans le champ multipart « document » avec le jeton Bearer ; la réponse donne fileId et fileName.
- Appeler GET /files/{fileId} pour télécharger le PDF, ou parcourir les routes dans Swagger UI à /api-docs.
Fichiers consultésbuild.gradle.ktssrc/main/resources/application.ymlsrc/main/kotlin/com/api/project/controller/AuthController.ktsrc/main/kotlin/com/api/project/controller/FilesController.ktsrc/main/kotlin/com/api/project/controller/DockerController.ktsrc/main/kotlin/com/api/project/controller/SSHController.ktsrc/main/kotlin/com/api/project/service/FilesService.ktsrc/main/kotlin/com/api/project/config/security/SecurityConf.kt
3. Parcours d’un envoi
sequenceDiagram
participant C as Client
participant F as Filtre JWT
participant FC as FilesController
participant FS as FilesService
participant S3 as FileS3 (S3 / MinIO)
participant M as FilesMongo (MongoDB)
C->>F: POST /files/documents, jeton Bearer, multipart "document"
F->>FC: utilisateur authentifié avec ses rôles
FC->>FC: @PreAuthorize MODERATOR ou ADMIN, contrôle de l’extension
FC->>FS: addDocument(octets, nom, ext, contentType, taille)
FS->>S3: putObject(compartiment "fus", "files/{uuid}/{nom}")
FS->>M: getNewFileId() = nombre + 1, enregistre FileModel
FS-->>C: 200 {fileId, fileName}
C->>FC: GET /files/{fileId}
FC->>M: getFile(id) → chemin, nom, type, taille
FC->>S3: getObject(chemin)
FC-->>C: octets avec Content-Disposition attachment
AuthTokenFilterlit le jeton porteur,JwtUtilsle valide avec lejwtSecretde la configuration, et les rôles de l’utilisateur viennent de MongoDB.FilesController.addDocumentrefuse une partie manquante et vérifie le nom de fichier avecDOC_FILE_EXTENSION_REGEX: tableurs, documents Word, PDF, images courantes, MP4, MOV, ZIP, MP3, WAV, ISO et RAR. Seule l’extension est vérifiée, pas le contenu. Le contrôle de taille est en commentaire.FilesService.addDocumentconstruit la clé d’objetfiles/{UUID aléatoire}/{nom d’origine}et écrit les octets par l’interfaceFileRepository, implémentée parFileS3avec le SDK AWS.FileS3crée le compartiment au démarrage s’il n’existe pas.- Le service demande ensuite un identifiant à
FilesMongo, qui vaut le nombre de documents de la collection plus un, puis enregistre unFileModel(identifiant, chemin, nom, extension, UUID, type de contenu, taille). - Un téléchargement cherche l’identifiant dans MongoDB, lit l’objet à son chemin enregistré et le renvoie avec le type et la longueur conservés.
Les octets sont écrits avant les métadonnées. Si l’enregistrement MongoDB échoue, l’objet reste dans le compartiment sans rien qui pointe vers lui.
4. Structure du projet
Vérifiée sur l’arborescence du dépôt. Tout se trouve sous src/main/kotlin/com/api/project/, y compris deux fichiers Java.
SpringBootFilesUploader/
├── build.gradle.kts, settings.gradle.kts, gradlew build Gradle en DSL Kotlin (version 0.0.3)
├── Dockerfile image openjdk11 qui lance build/libs/project-0.0.2.jar
├── docker-compose.yml vide
└── src/
├── main/kotlin/com/api/project/
│ ├── Application.kt
│ ├── config/ S3Config, DockerConfig, WebClientConfig, SpringDocOpenAPI, GeneralConfig
│ │ └── security/ SecurityConf (filtre JWT, sessions sans état, chemins publics)
│ ├── constants/ MAX_DOCUMENT_FILE_SIZE, DOC_FILE_EXTENSION_REGEX
│ ├── controller/ Auth, Files, User, Docker, Caddy, SSH (+ un DockerComposeController.java vide)
│ │ └── dto/ FileDTO, DockerDTO, SSHDTO, request/ (connexion, inscription), response/ (JWT, message)
│ ├── exception/ ApiError, BasicException
│ ├── models/ UserModel, RoleModel, FileModel, DockerModel, SSHModel, enum/ (ERole, DockerStatus, FilesStatus)
│ ├── repository/
│ │ ├── file/ FileRepository (interface), FileS3, FilesMongo
│ │ ├── user/ UserRepository, RoleRepository (Spring Data MongoDB)
│ │ ├── docker/ DockerClientAPI, DockerMongo, DockerCompose.java (ébauche)
│ │ ├── caddy/ CaddyMongo
│ │ └── SSHRepository.kt
│ └── service/
│ ├── FilesService, SSHService
│ ├── docker/ DockerService, DockerComposeService
│ ├── caddy/ CaddyAPI, CaddyService, conversion de configuration et modèle JSON
│ └── jwt/ JwtUtils, AuthTokenFilter, AuthEntryPointJwt, UserDetails*
├── main/resources/ application.yml, application-local.yml
└── test/kotlin/.../ApplicationTests.kt un seul test contextLoads()
5. Services externes
L’application s’attend à joindre ces services, avec les valeurs par défaut de application.yml et application-local.yml :
| Service | Valeur par défaut | Utilisé par |
|---|---|---|
| MongoDB | mongodb://localhost:27017/demo | utilisateurs, rôles, métadonnées des fichiers, fiches Docker et Caddy |
| Stockage compatible S3 (MinIO) | http://localhost:9000, compartiment fus, clés minioadmin | octets des fichiers |
| API Docker Engine | tcp://127.0.0.1:2375 | points d’accès /containers par docker-java |
| API d’administration Caddy | localhost:2019 | /caddy/ping et conversion de configuration |
| Serveur Spring Cloud Config | http://localhost:8888 (profil local) | configuration externe |
| Hôtes SSH | fournis dans chaque requête /ssh/cli | SSHService par Apache MINA SSHD |
6. Dépendances
Tirées de build.gradle.kts. Les versions absentes viennent des BOM Spring Boot 2.6.3 et Spring Cloud 2021.0.1.
| Bibliothèque | Version | Rôle |
|---|---|---|
| Starters Spring Boot : web, webflux, security, data-mongodb, validation | 2.6.3 | API HTTP, WebClient réactif, sécurité, dépôts MongoDB, validation des requêtes |
Kotlin JVM, plugin.spring, kotlin-reflect, kotlin-stdlib-jdk8 | 1.6.10 | Langage et intégration Spring, cible Java 11 |
Spring Cloud starter-config, starter-bootstrap | 2021.0.1 | Configuration venant d’un serveur de configuration |
spring-security-oauth2-jose | BOM | Classes JOSE / JWT |
io.jsonwebtoken:jjwt | 0.9.1 | Signature et lecture des jetons d’accès |
com.amazonaws:aws-java-sdk-s3 | 1.12.159 | Client S3 / MinIO pour les octets |
com.github.docker-java:docker-java + transport-httpclient5 | 3.2.13 | Client de l’API Docker Engine |
org.apache.sshd:sshd-core | 2.8.0 | Client SSH pour /ssh/cli |
org.springdoc:springdoc-openapi-ui + -webflux-ui | 1.6.6 | Document OpenAPI et Swagger UI |
com.squareup.okhttp3:okhttp | 4.9.3 | Client HTTP |
com.googlecode.json-simple:json-simple | 1.1.1 | Réponses JSONArray pour la liste des conteneurs |
Tests : spring-boot-starter-test, spring-security-test, io.mockk:mockk 1.12.2, com.ninja-squad:springmockk 3.1.0, mockwebserver d’OkHttp | Outils de test, presque inutilisés |
spring-boot-starter-parent est déclaré comme dépendance implementation alors que c’est un POM parent, et une BOM Testcontainers (1.15.3) est importée sans aucune dépendance Testcontainers.
7. Démarrage
Démarrez MongoDB et MinIO en local (le docker-compose.yml du dépôt est vide), puis lancez l’application avec le profil local, que application.yml active par défaut :
docker run -d -p 27017:27017 mongo
docker run -d -p 9000:9000 -e MINIO_ROOT_USER=minioadmin -e MINIO_ROOT_PASSWORD=minioadmin minio/minio server /data
./gradlew bootRun
Le profil local pointe aussi vers un serveur Spring Cloud Config sur le port 8888. Les rôles sont lus dans la collection de rôles de MongoDB et rien dans le code ne les crée : insérez ROLE_USER, ROLE_MODERATOR et ROLE_ADMIN avant de vous inscrire.
8. Limites
- Rôle de modérateur en libre-service.
POST /auth/signuptransforme un rôle demandé"mod"enROLE_MODERATOR: n’importe qui peut créer un compte autorisé à envoyer des fichiers. - Des noms de rôles qui ne correspondent jamais.
DockerControllervérifiehasRole('MOD'), mais les rôles sontROLE_USER,ROLE_MODERATORetROLE_ADMIN; seuls les administrateurs passent ces contrôles. - Identifiants séquentiels et concurrents.
getNewFileId()renvoie le nombre de documents plus un : deux envois simultanés peuvent obtenir le même identifiant, et une suppression fait entrer le suivant en collision. - Envois sans limite, en mémoire. Les limites multipart sont fixées à 500 000 Mo, le contrôle de taille est en commentaire et
document.bytescharge tout le fichier en mémoire avant l’envoi vers S3. - Exécution de commandes à distance, par conception.
/ssh/cliouvre un shell sur n’importe quel hôte et y exécute la commande donnée pour un administrateur, et/containersparle à un socket TCP Docker sans authentification. - Valeurs par défaut versionnées.
jwtSecret: secret, les clés MinIOminioadmin, un mot de passe du serveur de configuration et une adresse courriel personnelle se trouvent dans les fichiers YAML. - Build et documentation. Le Dockerfile copie
project-0.0.2.jaralors que le build produit la version 0.0.3, le fichier Compose et le README sont vides, et le seul test vérifie que le contexte se charge.
9. Sources
- Dépôt GitHub
build.gradle.ktsFilesController.ktFilesService.ktFileS3.ktSecurityConf.ktAuthController.ktapplication.yml
10. Pipeline CI/CD
Aucun pipeline CI/CD dans le dépôt. Le code se construit et se lance à la main, comme décrit plus haut.
Généré à partir du code au commit 3be3bbc.
11. Schéma de déploiement
Mermaid Schéma de déploiement
flowchart LR
repo[("Dépôt GitHub")]
image["Image Docker<br/>FROM adoptopenjdk/openjdk11:alpine"]
repo -->|"docker build"| image
12. Dépendances externes
25 paquets déclarés (20 à l’exécution, 5 pour le développement) dans build.gradle.kts.
Tableau des paquets 25
| Paquet | Version | Usage | Rôle | Registre |
|---|---|---|---|---|
org.springframework.boot:spring-boot-starter-web | — | exécution | Cadriciel web | central.sonatype.com |
org.springframework.boot:spring-boot-starter-parent:2.6.3 | — | exécution | — | central.sonatype.com |
com.amazonaws:aws-java-sdk-s3:1.12.159 | — | exécution | — | central.sonatype.com |
com.squareup.okhttp3:okhttp:4.9.3 | — | exécution | — | central.sonatype.com |
org.jetbrains.kotlin:kotlin-reflect | — | exécution | — | central.sonatype.com |
org.jetbrains.kotlin:kotlin-stdlib-jdk8 | — | exécution | — | central.sonatype.com |
org.springframework.cloud:spring-cloud-starter-bootstrap | — | exécution | Interface utilisateur | central.sonatype.com |
org.springframework.cloud:spring-cloud-starter-config | — | exécution | — | central.sonatype.com |
org.springframework.boot:spring-boot-starter-validation | — | exécution | — | central.sonatype.com |
org.springframework.boot:spring-boot-starter-data-mongodb | — | exécution | Accès aux données | central.sonatype.com |
org.springframework.boot:spring-boot-starter-security | — | exécution | Authentification et sécurité | central.sonatype.com |
org.springframework.boot:spring-boot-starter-webflux | — | exécution | Cadriciel web | central.sonatype.com |
org.springframework.security:spring-security-oauth2-jose | — | exécution | Authentification et sécurité | central.sonatype.com |
io.jsonwebtoken:jjwt:0.9.1 | — | exécution | — | central.sonatype.com |
com.github.docker-java:docker-java:3.2.13 | — | exécution | — | central.sonatype.com |
com.github.docker-java:docker-java-transport-httpclient5:3.2.13 | — | exécution | — | central.sonatype.com |
org.springdoc:springdoc-openapi-ui:1.6.6 | — | exécution | — | central.sonatype.com |
org.springdoc:springdoc-openapi-webflux-ui:1.6.6 | — | exécution | — | central.sonatype.com |
org.apache.sshd:sshd-core:2.8.0 | — | exécution | — | central.sonatype.com |
com.googlecode.json-simple:json-simple:1.1.1 | — | exécution | — | central.sonatype.com |
org.springframework.boot:spring-boot-starter-test | — | développement | Tests | central.sonatype.com |
org.springframework.security:spring-security-test | — | développement | Tests | central.sonatype.com |
io.mockk:mockk:1.12.2 | — | développement | — | central.sonatype.com |
com.ninja-squad:springmockk:3.1.0 | — | développement | — | central.sonatype.com |
com.squareup.okhttp3:mockwebserver | — | développement | Tests | central.sonatype.com |
13. Glossaire
Dépôt
Les fichiers source, la configuration et l’historique versionné d’un projet.
README
Le document d’entrée du dépôt, qui présente généralement son rôle et son installation.
Dépendance
Une bibliothèque, un outil ou un service nécessaire à la compilation ou à l’exécution d’un composant.
API
Une interface par laquelle des composants logiciels échangent des requêtes et des résultats. Une API n’utilise pas nécessairement HTTP.
HTTP
Un protocole de requête et réponse utilisé par les navigateurs et services web. Une requête comprend une méthode, un chemin, des en-têtes et parfois un corps.
Session
Un état conservé entre plusieurs requêtes, souvent pour suivre un utilisateur connecté. L’authentification et les permissions restent des vérifications distinctes.
Conteneur
Un environnement de processus isolé créé à partir d’une image. Les données persistantes nécessitent un stockage hors de son système de fichiers temporaire.
Docker Compose
Un format de configuration et un outil pour lancer des conteneurs, réseaux et volumes associés. L’ordre de démarrage ne garantit pas qu’un service est prêt.
Volume et montage de dossier
Un volume est un stockage géré par la plateforme de conteneurs ; un montage de dossier expose un chemin de l’hôte dans un conteneur.
Port publié
Un port de l’hôte associé au port d’un service dans un conteneur. Sa publication peut rendre le service accessible hors du réseau des conteneurs.
Interface en ligne de commande
Une interface textuelle où les commandes et arguments choisissent les opérations. Les codes de retour permettent aux scripts de détecter un succès ou un échec.
Compilation
Les étapes qui transforment les sources et dépendances en programme exécutable ou en livrable distribuable.
Mermaid
Un langage de diagrammes textuel. Les nœuds et liens décrivent un modèle ; le diagramme n’exécute pas le projet.
Maillage
Une surface représentée par des sommets et des éléments reliés, souvent des triangles. Sa géométrie est distincte de son matériau ou de sa texture.
Détection continue des collisions
Une recherche de contact pendant le mouvement entre deux états, au lieu de tester seulement ses extrémités.
Backend
Une implémentation derrière une interface commune. Selon le contexte, il peut s’agir d’un service côté serveur ou d’une implémentation graphique, d’entrée ou de stockage.
Base de données
Un système de stockage et d’interrogation de données structurées. Son schéma, ses règles d’accès et sa persistance déterminent comment l’application utilise ces données.
Micrologiciel
Un logiciel exécuté sur un appareil ou microcontrôleur pour contrôler son matériel et ses communications.
14. Références
- antoinebou12. (2026, 5 novembre). SpringBootFilesUploader. [Software repository] github.com.
- antoinebou12. (2026, 5 novembre). SpringBootFilesUploader: README. [Repository documentation] github.com.
- antoinebou12. (2026, 5 novembre). src/main/kotlin/com/api/project/controller/FilesController.kt. [Source file] github.com.
- antoinebou12. (2026, 5 novembre). src/main/kotlin/com/api/project/service/FilesService.kt. [Source file] github.com.
- antoinebou12. (2026, 5 novembre). build.gradle.kts. [Source file] github.com.
- Boucher, A. (2026, 23 octobre). SpringBootFilesUploader : Séparer les octets et les métadonnées. [Project article] antoineboucher.info.
Les sources sans date de publication indiquent la date de leur dernière vérification, le 5 novembre 2026. Les liens des fichiers conservent la révision inspectée lorsqu’elle est disponible.