Table of contents
SpringBootFilesUploader
SpringBootFilesUploader is a Kotlin REST API built on Spring Boot 2.6.3. The name describes its first job: accept a file upload, put the bytes in an S3-compatible bucket and the description in MongoDB, and serve the file back by id. The code goes further than the name. The same application also lists and controls Docker containers, pings a Caddy server and runs commands over SSH, all behind JWT authentication with three roles. The last commit dates from February 2022, and the README is a single line, so everything below comes from the source.
1. Overview
Six controllers make up the API. Paths are relative to the server root; /auth/** and the API docs are public, everything else needs a bearer token (SecurityConf.kt).
| Controller | Endpoints | Roles in @PreAuthorize |
|---|---|---|
AuthController (/auth) | POST /signin, POST /signup, POST /signup/admin | public; /signup/admin needs ADMIN |
FilesController (/files) | POST /documents (multipart field document), GET /{id} | upload: MODERATOR or ADMIN; download: any role |
UserController (/user) | GET /{id}/page, GET /profile, GET /mod, GET /admin | per endpoint |
DockerController (/containers) | GET /ping, GET /list, PUT /create, POST /start/{name}, /stop/{name}, /restart/{name}, /rename/{name}/{newName} | MOD or ADMIN; create and rename ADMIN |
CaddyController (/caddy) | GET /ping | authenticated |
SSHController (/ssh) | POST /cli | ADMIN |
The OpenAPI page is served by springdoc at /api-docs (application.yml).
2. Use cases
Kotlin Spring Boot API that stores uploaded files in MinIO/S3 behind JWT roles, with Docker and SSH admin endpoints.
- Sign up and get a JWT
- API userMinIO S3 and MongoDB
POST /auth/signup saves a user with hashed password and roles in MongoDB; POST /auth/signin returns a JwtResponse with the token and roles (AuthController.kt, JwtUtils.kt).
- Upload a document
- Moderator or adminMinIO S3 and MongoDB
POST /files/documents (moderator or admin) checks the extension against DOC_FILE_EXTENSION_REGEX, writes the bytes to the S3 bucket under files/<uuid>/<name> and records metadata in MongoDB.
- Download a stored file
- API userMinIO S3 and MongoDB
GET /files/{id} looks up the file record in MongoDB, reads the object from S3 and returns it as an attachment with its content type and size.
- Manage Docker containers
- Moderator or admin
The /containers routes list, create, start, stop, restart and rename containers through docker-java against the Docker host set in application.yml (tcp://127.0.0.1:2375).
- Run a remote SSH command
- Moderator or admin
POST /ssh/cli (admin only) opens an Apache SSHD session with the host, port and credentials in the request body and returns the command output.
2.1. Scenario: Store a PDF and fetch it back
- Start MongoDB on localhost:27017 and MinIO on localhost:9000, as configured in application.yml, then run ./gradlew bootRun.
- POST /auth/signup with a username, email, password and the role "mod".
- POST /auth/signin and copy the JWT from the response.
- POST /files/documents with the PDF in the multipart field "document" and the Bearer token; the response returns fileId and fileName.
- GET /files/{fileId} to download the PDF, or browse the routes in Swagger UI at /api-docs.
Files readbuild.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. How an upload works
sequenceDiagram
participant C as Client
participant F as JWT filter
participant FC as FilesController
participant FS as FilesService
participant S3 as FileS3 (S3 / MinIO)
participant M as FilesMongo (MongoDB)
C->>F: POST /files/documents, Bearer token, multipart "document"
F->>FC: authenticated user with roles
FC->>FC: @PreAuthorize MODERATOR or ADMIN, extension regex check
FC->>FS: addDocument(bytes, name, ext, contentType, size)
FS->>S3: putObject(bucket "fus", "files/{uuid}/{name}")
FS->>M: getNewFileId() = count + 1, save FileModel
FS-->>C: 200 {fileId, fileName}
C->>FC: GET /files/{fileId}
FC->>M: getFile(id) → path, name, type, size
FC->>S3: getObject(path)
FC-->>C: bytes with Content-Disposition attachment
AuthTokenFilterreads the bearer token,JwtUtilsvalidates it with thejwtSecretfrom the configuration, and the user’s roles come from MongoDB.FilesController.addDocumentrejects a missing part and checks the filename againstDOC_FILE_EXTENSION_REGEX: spreadsheets, Word documents, PDF, common images, MP4, MOV, ZIP, MP3, WAV, ISO and RAR. Only the extension is checked, not the content. The size check is commented out.FilesService.addDocumentbuilds the object keyfiles/{random UUID}/{original name}and writes the bytes through theFileRepositoryinterface, implemented byFileS3with the AWS SDK.FileS3creates the bucket at startup if it does not exist.- The service then asks
FilesMongofor an id, which is the number of documents in the collection plus one, and saves aFileModel(id, path, name, extension, UUID, content type, size). - A download looks the id up in MongoDB, reads the object from its stored path and returns it with the saved content type and length.
The bytes are written before the metadata. If the MongoDB save fails, the object stays in the bucket with nothing pointing to it.
4. Project structure
Checked against the repository tree. Everything lives under src/main/kotlin/com/api/project/, including two Java files.
SpringBootFilesUploader/
├── build.gradle.kts, settings.gradle.kts, gradlew Gradle Kotlin DSL build (version 0.0.3)
├── Dockerfile openjdk11 image running build/libs/project-0.0.2.jar
├── docker-compose.yml empty
└── src/
├── main/kotlin/com/api/project/
│ ├── Application.kt
│ ├── config/ S3Config, DockerConfig, WebClientConfig, SpringDocOpenAPI, GeneralConfig
│ │ └── security/ SecurityConf (JWT filter, stateless sessions, public paths)
│ ├── constants/ MAX_DOCUMENT_FILE_SIZE, DOC_FILE_EXTENSION_REGEX
│ ├── controller/ Auth, Files, User, Docker, Caddy, SSH (+ an empty DockerComposeController.java)
│ │ └── dto/ FileDTO, DockerDTO, SSHDTO, request/ (login, sign-up), 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 (stub)
│ │ ├── caddy/ CaddyMongo
│ │ └── SSHRepository.kt
│ └── service/
│ ├── FilesService, SSHService
│ ├── docker/ DockerService, DockerComposeService
│ ├── caddy/ CaddyAPI, CaddyService, config mappers and JSON model
│ └── jwt/ JwtUtils, AuthTokenFilter, AuthEntryPointJwt, UserDetails*
├── main/resources/ application.yml, application-local.yml
└── test/kotlin/.../ApplicationTests.kt a single contextLoads() test
5. External services
The application expects these to be reachable, with the defaults from application.yml and application-local.yml:
| Service | Default | Used by |
|---|---|---|
| MongoDB | mongodb://localhost:27017/demo | users, roles, file metadata, Docker and Caddy records |
| S3-compatible storage (MinIO) | http://localhost:9000, bucket fus, minioadmin keys | file bytes |
| Docker Engine API | tcp://127.0.0.1:2375 | /containers endpoints through docker-java |
| Caddy admin API | localhost:2019 | /caddy/ping and config mapping |
| Spring Cloud Config server | http://localhost:8888 (local profile) | external configuration |
| SSH hosts | given in each /ssh/cli request | SSHService through Apache MINA SSHD |
6. Dependencies
From build.gradle.kts. Versions without a number come from the Spring Boot 2.6.3 and Spring Cloud 2021.0.1 BOMs.
| Library | Version | Purpose |
|---|---|---|
| Spring Boot starters: web, webflux, security, data-mongodb, validation | 2.6.3 | HTTP API, reactive WebClient, security, MongoDB repositories, request validation |
Kotlin JVM, plugin.spring, kotlin-reflect, kotlin-stdlib-jdk8 | 1.6.10 | Language and Spring integration, Java 11 target |
Spring Cloud starter-config, starter-bootstrap | 2021.0.1 | Configuration from a config server |
spring-security-oauth2-jose | BOM | JOSE / JWT support classes |
io.jsonwebtoken:jjwt | 0.9.1 | Signing and parsing the access tokens |
com.amazonaws:aws-java-sdk-s3 | 1.12.159 | S3 / MinIO client for file bytes |
com.github.docker-java:docker-java + transport-httpclient5 | 3.2.13 | Docker Engine API client |
org.apache.sshd:sshd-core | 2.8.0 | SSH client for /ssh/cli |
org.springdoc:springdoc-openapi-ui + -webflux-ui | 1.6.6 | OpenAPI document and Swagger UI |
com.squareup.okhttp3:okhttp | 4.9.3 | HTTP client |
com.googlecode.json-simple:json-simple | 1.1.1 | JSONArray responses for container lists |
Test: spring-boot-starter-test, spring-security-test, io.mockk:mockk 1.12.2, com.ninja-squad:springmockk 3.1.0, OkHttp mockwebserver | Test tooling, mostly unused |
spring-boot-starter-parent is declared as an implementation dependency although it is a parent POM, and a Testcontainers BOM (1.15.3) is imported without any Testcontainers dependency.
7. Getting started
Start MongoDB and MinIO locally (the repository’s docker-compose.yml is empty), then run the app with the local profile, which application.yml activates by default:
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
The local profile also points to a Spring Cloud Config server on port 8888. Roles are read from MongoDB’s role collection and nothing in the code creates them, so insert ROLE_USER, ROLE_MODERATOR and ROLE_ADMIN before signing up.
8. Limits
- Self-service moderator role.
POST /auth/signupmaps a requested role"mod"toROLE_MODERATOR, so anyone can create an account allowed to upload files. - Role names that never match.
DockerControllercheckshasRole('MOD'), but the roles areROLE_USER,ROLE_MODERATORandROLE_ADMIN; only admins pass those checks. - Sequential, racy file ids.
getNewFileId()returns the document count plus one: two concurrent uploads can get the same id, and a deletion makes the next id collide. - Unbounded uploads in memory. Multipart limits are set to 500,000 MB, the size check is commented out, and
document.bytesloads the whole file into memory before it is sent to S3. - Remote command execution by design.
/ssh/cliopens a shell on any host and runs the given command for an admin, and/containerstalks to an unauthenticated Docker TCP socket. - Committed defaults.
jwtSecret: secret, MinIOminioadminkeys, a config-server password and a personal email are in the YAML files. - Build and docs. The Dockerfile copies
project-0.0.2.jarwhile the build produces version 0.0.3, the Compose file and README are empty, and the only test checks that the context loads.
9. Sources
- GitHub repository
build.gradle.ktsFilesController.ktFilesService.ktFileS3.ktSecurityConf.ktAuthController.ktapplication.yml
10. CI/CD pipeline
No CI/CD pipeline in the repository. The code is built and run by hand, as described above.
Generated from the source at commit 3be3bbc.
11. Deployment diagram
Mermaid Deployment diagram
flowchart LR
repo[("GitHub repository")]
image["Docker image<br/>FROM adoptopenjdk/openjdk11:alpine"]
repo -->|"docker build"| image
12. External dependencies
25 declared packages (20 runtime, 5 development) in build.gradle.kts.
Package table 25
| Package | Version | Scope | Purpose | Registry |
|---|---|---|---|---|
org.springframework.boot:spring-boot-starter-web | — | runtime | Web framework | central.sonatype.com |
org.springframework.boot:spring-boot-starter-parent:2.6.3 | — | runtime | — | central.sonatype.com |
com.amazonaws:aws-java-sdk-s3:1.12.159 | — | runtime | — | central.sonatype.com |
com.squareup.okhttp3:okhttp:4.9.3 | — | runtime | — | central.sonatype.com |
org.jetbrains.kotlin:kotlin-reflect | — | runtime | — | central.sonatype.com |
org.jetbrains.kotlin:kotlin-stdlib-jdk8 | — | runtime | — | central.sonatype.com |
org.springframework.cloud:spring-cloud-starter-bootstrap | — | runtime | User interface | central.sonatype.com |
org.springframework.cloud:spring-cloud-starter-config | — | runtime | — | central.sonatype.com |
org.springframework.boot:spring-boot-starter-validation | — | runtime | — | central.sonatype.com |
org.springframework.boot:spring-boot-starter-data-mongodb | — | runtime | Database access | central.sonatype.com |
org.springframework.boot:spring-boot-starter-security | — | runtime | Authentication and security | central.sonatype.com |
org.springframework.boot:spring-boot-starter-webflux | — | runtime | Web framework | central.sonatype.com |
org.springframework.security:spring-security-oauth2-jose | — | runtime | Authentication and security | central.sonatype.com |
io.jsonwebtoken:jjwt:0.9.1 | — | runtime | — | central.sonatype.com |
com.github.docker-java:docker-java:3.2.13 | — | runtime | — | central.sonatype.com |
com.github.docker-java:docker-java-transport-httpclient5:3.2.13 | — | runtime | — | central.sonatype.com |
org.springdoc:springdoc-openapi-ui:1.6.6 | — | runtime | — | central.sonatype.com |
org.springdoc:springdoc-openapi-webflux-ui:1.6.6 | — | runtime | — | central.sonatype.com |
org.apache.sshd:sshd-core:2.8.0 | — | runtime | — | central.sonatype.com |
com.googlecode.json-simple:json-simple:1.1.1 | — | runtime | — | central.sonatype.com |
org.springframework.boot:spring-boot-starter-test | — | development | Testing | central.sonatype.com |
org.springframework.security:spring-security-test | — | development | Testing | central.sonatype.com |
io.mockk:mockk:1.12.2 | — | development | — | central.sonatype.com |
com.ninja-squad:springmockk:3.1.0 | — | development | — | central.sonatype.com |
com.squareup.okhttp3:mockwebserver | — | development | Testing | central.sonatype.com |
13. Glossary
Repository
The versioned source files, configuration, and history of a project.
README
The repository’s entry document, usually covering its purpose and initial setup.
Dependency
A library, tool, or service that another component needs to build or run.
API
An interface through which software components exchange requests and results. An API does not have to use HTTP.
HTTP
A request-and-response protocol used by browsers and web services. A method, path, headers, and optional body describe a request.
Session
State kept across related requests, often to track a signed-in user. Authentication and permission checks remain separate concerns.
Container
An isolated process environment created from an image. Persistent data needs storage outside the container’s disposable filesystem.
Docker Compose
A configuration format and tool for starting related containers, networks, and volumes. Startup order alone does not guarantee service readiness.
Volume and bind mount
A volume is storage managed by the container platform; a bind mount exposes a chosen host path inside a container.
Published port
A host port mapped to a service’s container port. Publishing a port can expose that service beyond its container network.
CLI
A text interface in which commands and arguments select operations. Exit codes let scripts detect success or failure.
Build
The steps that turn source files and dependencies into a runnable program or distributable artifact.
Mermaid
A text-based diagram language. Nodes and connections describe a model; the diagram itself does not execute the project.
Backend
An implementation behind a shared interface. Depending on context, it can mean a server-side service or a graphics, input, or storage implementation.
Database
A system for storing and querying structured records. Its schema, access rules, and persistence determine how the application can use that data.
Firmware
Software running on a device or microcontroller to control its hardware and communication.
14. References
- antoinebou12. (2026, November 5). SpringBootFilesUploader. [Software repository] github.com.
- antoinebou12. (2026, November 5). SpringBootFilesUploader: README. [Repository documentation] github.com.
- antoinebou12. (2026, November 5). src/main/kotlin/com/api/project/controller/FilesController.kt. [Source file] github.com.
- antoinebou12. (2026, November 5). src/main/kotlin/com/api/project/service/FilesService.kt. [Source file] github.com.
- antoinebou12. (2026, November 5). build.gradle.kts. [Source file] github.com.
- Boucher, A. (2026, October 23). SpringBootFilesUploader: File bytes and metadata on separate paths. [Project article] antoineboucher.info.
Sources without a publication date show the date they were last checked, November 5, 2026. File links retain the inspected revision where available.