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:
@@ -0,0 +1,7 @@
|
|||||||
|
build/
|
||||||
|
.git/
|
||||||
|
.vscode/
|
||||||
|
.cache/
|
||||||
|
uploads/
|
||||||
|
.env
|
||||||
|
.env.example
|
||||||
+37
@@ -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"]
|
||||||
@@ -131,6 +131,20 @@ ctest --test-dir build --output-on-failure
|
|||||||
The `RequestLogger` plugin logs every response as method, path, status and
|
The `RequestLogger` plugin logs every response as method, path, status and
|
||||||
client address.
|
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
|
## Current Endpoints
|
||||||
|
|
||||||
- `GET /api/v1/health`: process health check, no external dependency.
|
- `GET /api/v1/health`: process health check, no external dependency.
|
||||||
|
|||||||
+67
-9
@@ -6,33 +6,91 @@ namespace visione::cors
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::string allowedOrigin()
|
std::vector<std::string> allowedOrigins()
|
||||||
{
|
{
|
||||||
return drogon::app()
|
auto cfg = drogon::app().getCustomConfig();
|
||||||
.getCustomConfig()["cors"]
|
std::vector<std::string> origins;
|
||||||
.get("allowed_origin", "http://localhost:5173")
|
|
||||||
.asString();
|
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)
|
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,
|
void addHeaders(const drogon::HttpRequestPtr &request,
|
||||||
const drogon::HttpResponsePtr &response)
|
const drogon::HttpResponsePtr &response)
|
||||||
{
|
{
|
||||||
if (!isAllowed(request))
|
auto origin = request->getHeader("Origin");
|
||||||
{
|
if (origin.empty())
|
||||||
return;
|
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-Credentials", "true");
|
||||||
response->addHeader("Access-Control-Allow-Methods",
|
response->addHeader("Access-Control-Allow-Methods",
|
||||||
"GET,POST,PATCH,DELETE,OPTIONS");
|
"GET,POST,PATCH,DELETE,OPTIONS");
|
||||||
response->addHeader("Access-Control-Allow-Headers",
|
response->addHeader("Access-Control-Allow-Headers",
|
||||||
"Authorization,Content-Type");
|
"Authorization,Content-Type");
|
||||||
|
response->addHeader("Vary", "Origin");
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -63,7 +63,7 @@
|
|||||||
"spec_path": "build/openapi/visione.openapi.json"
|
"spec_path": "build/openapi/visione.openapi.json"
|
||||||
},
|
},
|
||||||
"cors": {
|
"cors": {
|
||||||
"allowed_origin": "http://localhost:5173"
|
"allowed_origin": ["http://localhost:3000", "http://localhost:5137"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -5,16 +5,46 @@
|
|||||||
#include "repositories/MigrationRunner.h"
|
#include "repositories/MigrationRunner.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||||
(void)RequestLogger::classTypeName();
|
(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;
|
Json::Value requestLoggerConfig;
|
||||||
requestLoggerConfig["include_query"] = true;
|
requestLoggerConfig["include_query"] = true;
|
||||||
|
|
||||||
drogon::app().loadConfigFile("config.json");
|
|
||||||
drogon::app().addPlugin("RequestLogger", {}, requestLoggerConfig);
|
drogon::app().addPlugin("RequestLogger", {}, requestLoggerConfig);
|
||||||
visione::cors::configure();
|
visione::cors::configure();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user