Skip to content
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.

Backend APIAPIDocker
Kotlin 1.6Spring Boot 2.6Spring SecurityJWTMongoDBS3 / MinIOdocker-javaApache MINA SSHDspringdocGradle
  • Updated

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).

ControllerEndpointsRoles in @PreAuthorize
AuthController (/auth)POST /signin, POST /signup, POST /signup/adminpublic; /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 /adminper 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 /pingauthenticated
SSHController (/ssh)POST /cliADMIN

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.

Use-case diagram for SpringBootFilesUploader: Sign up and get a JWT; Upload a document; Download a stored file; Manage Docker containers; Run a remote SSH commandSpringBootFilesUploaderSign up and get a JWTUpload a documentDownload a stored fileManage Docker containersRun a remote SSH commandAPI userModerator or admin«system»MinIO S3 and MongoDB
People on the left, external systems on the right; each oval is a use case backed by the files cited below.
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

  1. Start MongoDB on localhost:27017 and MinIO on localhost:9000, as configured in application.yml, then run ./gradlew bootRun.
  2. POST /auth/signup with a username, email, password and the role "mod".
  3. POST /auth/signin and copy the JWT from the response.
  4. POST /files/documents with the PDF in the multipart field "document" and the Bearer token; the response returns fileId and fileName.
  5. 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
  1. AuthTokenFilter reads the bearer token, JwtUtils validates it with the jwtSecret from the configuration, and the user’s roles come from MongoDB.
  2. FilesController.addDocument rejects a missing part and checks the filename against DOC_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.
  3. FilesService.addDocument builds the object key files/{random UUID}/{original name} and writes the bytes through the FileRepository interface, implemented by FileS3 with the AWS SDK. FileS3 creates the bucket at startup if it does not exist.
  4. The service then asks FilesMongo for an id, which is the number of documents in the collection plus one, and saves a FileModel (id, path, name, extension, UUID, content type, size).
  5. 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:

ServiceDefaultUsed by
MongoDBmongodb://localhost:27017/demousers, roles, file metadata, Docker and Caddy records
S3-compatible storage (MinIO)http://localhost:9000, bucket fus, minioadmin keysfile bytes
Docker Engine APItcp://127.0.0.1:2375/containers endpoints through docker-java
Caddy admin APIlocalhost:2019/caddy/ping and config mapping
Spring Cloud Config serverhttp://localhost:8888 (local profile)external configuration
SSH hostsgiven in each /ssh/cli requestSSHService 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.

LibraryVersionPurpose
Spring Boot starters: web, webflux, security, data-mongodb, validation2.6.3HTTP API, reactive WebClient, security, MongoDB repositories, request validation
Kotlin JVM, plugin.spring, kotlin-reflect, kotlin-stdlib-jdk81.6.10Language and Spring integration, Java 11 target
Spring Cloud starter-config, starter-bootstrap2021.0.1Configuration from a config server
spring-security-oauth2-joseBOMJOSE / JWT support classes
io.jsonwebtoken:jjwt0.9.1Signing and parsing the access tokens
com.amazonaws:aws-java-sdk-s31.12.159S3 / MinIO client for file bytes
com.github.docker-java:docker-java + transport-httpclient53.2.13Docker Engine API client
org.apache.sshd:sshd-core2.8.0SSH client for /ssh/cli
org.springdoc:springdoc-openapi-ui + -webflux-ui1.6.6OpenAPI document and Swagger UI
com.squareup.okhttp3:okhttp4.9.3HTTP client
com.googlecode.json-simple:json-simple1.1.1JSONArray 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 mockwebserverTest 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/signup maps a requested role "mod" to ROLE_MODERATOR, so anyone can create an account allowed to upload files.
  • Role names that never match. DockerController checks hasRole('MOD'), but the roles are ROLE_USER, ROLE_MODERATOR and ROLE_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.bytes loads the whole file into memory before it is sent to S3.
  • Remote command execution by design. /ssh/cli opens a shell on any host and runs the given command for an admin, and /containers talks to an unauthenticated Docker TCP socket.
  • Committed defaults. jwtSecret: secret, MinIO minioadmin keys, a config-server password and a personal email are in the YAML files.
  • Build and docs. The Dockerfile copies project-0.0.2.jar while 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

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
External dependencies
PackageVersionScopePurposeRegistry
org.springframework.boot:spring-boot-starter-web—runtimeWeb frameworkcentral.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—runtimeUser interfacecentral.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—runtimeDatabase accesscentral.sonatype.com
org.springframework.boot:spring-boot-starter-security—runtimeAuthentication and securitycentral.sonatype.com
org.springframework.boot:spring-boot-starter-webflux—runtimeWeb frameworkcentral.sonatype.com
org.springframework.security:spring-security-oauth2-jose—runtimeAuthentication and securitycentral.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—developmentTestingcentral.sonatype.com
org.springframework.security:spring-security-test—developmentTestingcentral.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—developmentTestingcentral.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

  1. antoinebou12. (2026, November 5). SpringBootFilesUploader. [Software repository] github.com.
  2. antoinebou12. (2026, November 5). SpringBootFilesUploader: README. [Repository documentation] github.com.
  3. antoinebou12. (2026, November 5). src/main/kotlin/com/api/project/controller/FilesController.kt. [Source file] github.com.
  4. antoinebou12. (2026, November 5). src/main/kotlin/com/api/project/service/FilesService.kt. [Source file] github.com.
  5. antoinebou12. (2026, November 5). build.gradle.kts. [Source file] github.com.
  6. 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.