Merge pull request 'feature/dockerfile' (#1) from feature/dockerfile into main

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-06-10 21:35:39 +00:00 Unverified
7 changed files with 221 additions and 12 deletions
+7
View File
@@ -0,0 +1,7 @@
build/
.git/
.vscode/
.cache/
uploads/
.env
.env.example
+37
View File
@@ -0,0 +1,37 @@
# --- Builder Stage ---
FROM ubuntu:22.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake ninja-build git pkg-config \
libssl-dev zlib1g-dev \
libmariadb-dev libmariadb-dev-compat \
libsqlite3-dev python3 libjsoncpp-dev uuid-dev ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN rm -rf /usr/include/mysql && ln -s /usr/include/mariadb /usr/include/mysql
WORKDIR /app
COPY . /app
RUN cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \
&& cmake --build build
# --- Runtime Stage ---
FROM ubuntu:22.04 AS runtime
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
libssl3 ca-certificates libstdc++6 libmariadb3 libjsoncpp25 libuuid1 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /app/build/visione-backend /app/visione-backend
COPY --from=builder /app/build/openapi /app/build/openapi
COPY --from=builder /app/config.json /app/config.json
COPY --from=builder /app/migrations /app/migrations
EXPOSE 5555
ENTRYPOINT ["/app/visione-backend"]
+14
View File
@@ -131,6 +131,20 @@ ctest --test-dir build --output-on-failure
The `RequestLogger` plugin logs every response as method, path, status and
client address.
## Dokploy
Use `docker-compose.dev.yml` as the Dokploy compose path for a self-contained
backend deployment with MySQL and a local SMTP catcher. Configure the public
domain in Dokploy against service `backend` and container port `5555`.
The runtime image includes `config.json`; override environment-specific values
with Dokploy environment variables. The SMTP catcher service keeps the internal
name `mailhog` for compatibility, but uses the multi-architecture Mailpit image
to avoid `exec format error` failures on ARM servers.
Set `MYSQL_PASSWORD`, `MYSQL_ROOT_PASSWORD` and any other environment-specific
values in Dokploy environment variables.
## Current Endpoints
- `GET /api/v1/health`: process health check, no external dependency.
+67 -9
View File
@@ -6,33 +6,91 @@ namespace visione::cors
{
namespace
{
std::string allowedOrigin()
std::vector<std::string> allowedOrigins()
{
return drogon::app()
.getCustomConfig()["cors"]
.get("allowed_origin", "http://localhost:5173")
.asString();
auto cfg = drogon::app().getCustomConfig();
std::vector<std::string> origins;
if (cfg.isMember("cors") && cfg["cors"].isObject())
{
auto &c = cfg["cors"];
if (c.isMember("allowed_origin"))
{
auto &v = c["allowed_origin"];
if (v.isArray())
{
for (const auto &el : v)
{
if (el.isString())
origins.emplace_back(el.asString());
}
}
else if (v.isString())
{
origins.emplace_back(v.asString());
}
}
}
if (origins.empty())
origins.emplace_back("http://localhost:5173");
return origins;
}
bool isAllowed(const drogon::HttpRequestPtr &request)
{
return request->getHeader("Origin") == allowedOrigin();
auto origin = request->getHeader("Origin");
if (origin.empty())
return false;
auto origins = allowedOrigins();
for (const auto &o : origins)
{
if (o == "*" || o == origin)
return true;
}
return false;
}
void addHeaders(const drogon::HttpRequestPtr &request,
const drogon::HttpResponsePtr &response)
{
if (!isAllowed(request))
{
auto origin = request->getHeader("Origin");
if (origin.empty())
return;
auto origins = allowedOrigins();
bool allowAll = false;
bool matched = false;
for (const auto &o : origins)
{
if (o == "*")
{
allowAll = true;
break;
}
if (o == origin)
{
matched = true;
break;
}
}
response->addHeader("Access-Control-Allow-Origin", allowedOrigin());
if (!allowAll && !matched)
return;
if (allowAll)
response->addHeader("Access-Control-Allow-Origin", "*");
else
response->addHeader("Access-Control-Allow-Origin", origin);
response->addHeader("Access-Control-Allow-Credentials", "true");
response->addHeader("Access-Control-Allow-Methods",
"GET,POST,PATCH,DELETE,OPTIONS");
response->addHeader("Access-Control-Allow-Headers",
"Authorization,Content-Type");
response->addHeader("Vary", "Origin");
}
} // namespace
+2 -2
View File
@@ -63,7 +63,7 @@
"spec_path": "build/openapi/visione.openapi.json"
},
"cors": {
"allowed_origin": "http://localhost:5173"
"allowed_origin": ["http://localhost:3000", "http://localhost:5137"]
}
}
}
}
+63
View File
@@ -0,0 +1,63 @@
services:
backend:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
volumes:
- uploads-data:/app/uploads
environment:
DB_HOST: ${DB_HOST:-mysql}
DB_PORT: ${DB_PORT:-3306}
DB_USER: ${MYSQL_USER:-visione}
DB_PASSWORD: ${MYSQL_PASSWORD:-visione}
DB_NAME: ${MYSQL_DATABASE:-visione}
SMTP_HOST: ${SMTP_HOST:-mailhog}
SMTP_PORT: ${SMTP_PORT:-1025}
expose:
- "5555"
networks:
- visione
- dokploy-network
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:-root}"
MYSQL_DATABASE: "${MYSQL_DATABASE:-visione}"
MYSQL_USER: "${MYSQL_USER:-visione}"
MYSQL_PASSWORD: "${MYSQL_PASSWORD:-visione}"
expose:
- "3306"
volumes:
- mysql-data:/var/lib/mysql
networks:
- visione
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -u$${MYSQL_USER} -p$${MYSQL_PASSWORD} --silent"]
interval: 5s
timeout: 5s
retries: 20
start_period: 20s
mailhog:
image: axllent/mailpit:latest
restart: unless-stopped
expose:
- "1025"
- "8025"
networks:
- visione
volumes:
mysql-data:
uploads-data:
networks:
visione:
dokploy-network:
external: true
+31 -1
View File
@@ -5,16 +5,46 @@
#include "repositories/MigrationRunner.h"
#include <cstdio>
#include <fstream>
#include <cstdlib>
int main()
{
setvbuf(stdout, nullptr, _IONBF, 0);
(void)RequestLogger::classTypeName();
Json::Value config;
std::ifstream configFile("config.json");
if (configFile.is_open())
{
configFile >> config;
if (const char* envHost = std::getenv("DB_HOST"))
config["db_clients"][0]["host"] = envHost;
if (const char* envPort = std::getenv("DB_PORT"))
config["db_clients"][0]["port"] = std::stoi(envPort);
if (const char* envUser = std::getenv("DB_USER"))
config["db_clients"][0]["user"] = envUser;
if (const char* envPass = std::getenv("DB_PASSWORD"))
config["db_clients"][0]["passwd"] = envPass;
if (const char* envDb = std::getenv("DB_NAME"))
config["db_clients"][0]["dbname"] = envDb;
if (const char* envSmtpHost = std::getenv("SMTP_HOST"))
config["custom_config"]["mail"]["smtp_host"] = envSmtpHost;
if (const char* envSmtpPort = std::getenv("SMTP_PORT"))
config["custom_config"]["mail"]["smtp_port"] = std::stoi(envSmtpPort);
drogon::app().loadConfigJson(config);
}
else
{
drogon::app().loadConfigFile("config.json");
}
Json::Value requestLoggerConfig;
requestLoggerConfig["include_query"] = true;
drogon::app().loadConfigFile("config.json");
drogon::app().addPlugin("RequestLogger", {}, requestLoggerConfig);
visione::cors::configure();