feat: Add Dockerization support

This commit is contained in:
danielvici123
2026-06-09 20:13:39 +02:00
parent 520fd4f1e6
commit 0103d2939d
6 changed files with 151 additions and 8 deletions

31
.dockerignore Normal file
View File

@@ -0,0 +1,31 @@
# Rust
target/
**/*.rs.bk
# Git
.git
.gitignore
# Docker
Dockerfile
.dockerignore
# Config and local data
*.json
!config.json.example
!response.json.example
status.json
# IDEs and Editors
.vscode/
.idea/
*.swp
*.swo
# CI/CD
.gitea/
.github/
# OS files
.DS_Store
Thumbs.db

44
Dockerfile Normal file
View File

@@ -0,0 +1,44 @@
# Build stage
FROM rust:alpine AS builder
# Install build dependencies
RUN apk add --no-cache musl-dev
WORKDIR /usr/src/dhl
# 1. Copy only the dependency manifests
COPY Cargo.toml Cargo.lock ./
# 2. Create a dummy source file to build dependencies
RUN mkdir src && echo "fn main() {}" > src/main.rs && \
cargo build --release && \
rm -rf src
# 3. Now copy the real source code
COPY src ./src
# 4. Build the actual application
# We touch the main file to ensure cargo rebuilds it
RUN touch src/main.rs && cargo build --release
# Final stage
FROM alpine:latest
# Set environment variable to signal the app it's running in Docker
ENV DOCKER_CONTAINER=true
# Install runtime dependencies
RUN apk add --no-cache ca-certificates
WORKDIR /app
# Copy the binary from the builder stage
COPY --from=builder /usr/src/dhl/target/release/dhl /usr/local/bin/dhl
# Volume for configuration and response files
VOLUME ["/app"]
# Default port
EXPOSE 3000
CMD ["dhl"]

View File

@@ -99,6 +99,52 @@ cargo run
cargo build --release cargo build --release
``` ```
### Using with Docker
You can run DHL as a Docker container. The application automatically generates a default `config.json` and `response.json` if they are missing in the work directory.
#### Build the Image
```bash
docker build -t dhl .
```
#### Run with Docker CLI
To persist your configuration and response files, mount a local directory to `/app` in the container. The application will create default files in that directory if it is empty.
```bash
# Create a folder for your mock data
mkdir mock-data
# Run the container and mount the folder
docker run -p 3000:3000 -v $(pwd)/mock-data:/app dhl
```
#### Run with Docker Compose
An example `docker-compose.yml` is provided in the root directory:
```yaml
version: '3.8'
services:
dhl:
build: .
ports:
- "3000:3000"
volumes:
- ./mock-data:/app
restart: unless-stopped
```
To start:
```bash
docker-compose up -d
```
**Note:** The default host is set to `0.0.0.0` for Docker compatibility. If you are using a custom `config.json`, ensure the `host` is set to `0.0.0.0`.
### Release Assets ### Release Assets
When downloading a release, you will find: When downloading a release, you will find:

11
docker-compose.yml Normal file
View File

@@ -0,0 +1,11 @@
services:
dhl:
build: .
image: dhl:latest
container_name: dhl
ports:
- "3000:3000"
volumes:
# Mount a local folder to /app to manage config.json and response files
- ./mock-data:/app
restart: unless-stopped

View File

@@ -1,28 +1,28 @@
use serde::Deserialize; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
use std::fs; use std::fs;
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct TlsConfig { pub struct TlsConfig {
pub enabled: bool, pub enabled: bool,
pub cert_path: PathBuf, pub cert_path: PathBuf,
pub key_path: PathBuf, pub key_path: PathBuf,
} }
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct MaskingConfig { pub struct MaskingConfig {
pub enabled: bool, pub enabled: bool,
pub headers: Vec<String>, pub headers: Vec<String>,
} }
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct RouteConfig { pub struct RouteConfig {
pub path: String, pub path: String,
pub response_file: PathBuf, pub response_file: PathBuf,
pub status_code: u16, pub status_code: u16,
} }
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Config { pub struct Config {
pub host: String, pub host: String,
pub port: u16, pub port: u16,
@@ -41,7 +41,11 @@ impl Config {
}) })
} }
Err(_) => { Err(_) => {
Self::default() let default_config = Self::default();
if let Ok(json) = serde_json::to_string_pretty(&default_config) {
let _ = fs::write("config.json", json);
}
default_config
} }
} }
} }
@@ -50,7 +54,7 @@ impl Config {
impl Default for Config { impl Default for Config {
fn default() -> Self { fn default() -> Self {
Self { Self {
host: "127.0.0.1".to_string(), host: "0.0.0.0".to_string(),
port: 3000, port: 3000,
routes: vec![RouteConfig { routes: vec![RouteConfig {
path: "/".to_string(), path: "/".to_string(),

View File

@@ -47,7 +47,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.layer(axum_middleware::from_fn_with_state(state.clone(), logging_middleware)) .layer(axum_middleware::from_fn_with_state(state.clone(), logging_middleware))
.with_state(state); .with_state(state);
let addr: SocketAddr = format!("{}:{}", config.host, config.port).parse()?; let mut host = config.host.clone();
// In Docker, we usually want to bind to 0.0.0.0 to be accessible
if std::env::var("DOCKER_CONTAINER").is_ok() {
host = "0.0.0.0".to_string();
}
let addr: SocketAddr = format!("{}:{}", host, config.port).parse()?;
// 4. Start server // 4. Start server
if config.tls.enabled { if config.tls.enabled {