Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9769c1c
Merge branch 'master' into feature/#36-configure-environment-variable…
aasheptunov Mar 18, 2026
b7b64cf
feat(app-config): #36: adding an app-config layer to work with enviro…
aasheptunov Mar 18, 2026
25bbb49
chore(devcontainer): #36: adding the --env-file argument to the devco…
aasheptunov Mar 19, 2026
2ffab6d
docs(readme): #36: adding information to the README about the automat…
aasheptunov Mar 19, 2026
378dec3
refactor(environment): #36: convert `app-config` into a singleton cla…
aasheptunov Mar 20, 2026
6944e7f
Merge branch 'master' into feature/#36-configure-environment-variable…
aasheptunov Mar 20, 2026
eab4313
docs(readme): #36: add the necessary step to create the .env file in …
aasheptunov Mar 20, 2026
8244c77
fix(workflows): #36: fixing a pipeline error related to the missing r…
aasheptunov Mar 20, 2026
e8de3d1
refactor(environment): #36: add error handling to app-config, add log…
aasheptunov Mar 23, 2026
221c5f4
ci(workflows): #36: add a step to create the .env file so that the de…
aasheptunov Mar 23, 2026
7242eaa
docs(readme): #36: reverted the automatic change, as it was not neces…
aasheptunov Mar 23, 2026
84dcbb9
fix(environment): #36: fixed a logic error in the error logging, as t…
aasheptunov Mar 23, 2026
69b984f
Merge branch 'feature/#36-configure-environment-variable-settings-glo…
aasheptunov Mar 23, 2026
67814bb
refactor(environment): #36: removal of an unused variable, since the …
aasheptunov Mar 23, 2026
c531bd7
docs(logging): #36: editing the comment to make it clearer
aasheptunov Mar 23, 2026
0125b78
feat(environment): #36: add setters for variables, move the set logic…
aasheptunov Mar 25, 2026
828e43c
feat(environment): #36: add a log message that appears when an error …
aasheptunov Mar 25, 2026
ef7a052
Merge branch 'master' into feature/#36-configure-environment-variable…
aasheptunov Mar 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
"remoteUser": "root",
"runArgs": [
// This is necessary to avoid having to manually export environment variables from
// the .env file in every terminal session.
"--env-file",
"${localWorkspaceFolder}/.env"
],
"customizations": {
"vscode": {
"extensions": [
Expand Down
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
API_HOST=tobemodified
API_PORT=tobemodified
API_LOG_LEVEL=tobemodified # Possible values: TRACE (0), DEBUG (1), INFO (2), WARN (3) or ERROR (4)
API_NUMBER_OF_THREADS=tobemodified # Cannot be less than 1

POSTGRES_HOST=tobemodified
POSTGRES_PORT=tobemodified
POSTGRES_DB=tobemodified
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ project(${PROJECT_NAME} CXX)
file(GLOB_RECURSE sources CONFIGURE_DEPENDS
${CMAKE_SOURCE_DIR}/src/*/*.cpp
${CMAKE_SOURCE_DIR}/src/data/models/odb-gen/*.cxx
${CMAKE_SOURCE_DIR}/src/utils/**/*.cpp
)

set(PROJECT_OBJECTS ${PROJECT_NAME}_lib)
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ For development purposes use a devcontainer named `developing`.
- Microsoft VS Code
- VS Code should also have the "Dev Containers" extension installed. To check it, open "View: Extensions" with `Ctrl + Shift + X` or as shown in the screenshot below:
<p style="text-align: center;"><img src="docs/images/vscodeExtensions.png" alt="vscodeExtensions" width="400"/></p>
- Before running the container, create a .env file in the project root and specify the environment variables in it, just as you did in .env.example. Otherwise, running the devcontainer will result in an error.
- Make sure Docker daemon is running before opening the dev container (`Ctrl + Shift + P` -> "Reopen in container" or click here + "Reopen in container")
<p style="text-align: center;"><img src="docs/images/vscodeContainer.png" alt="vscodeContainer" width="400"/></p>

Expand All @@ -39,7 +40,7 @@ When dependencies are built, use the command `conan build . -pr:h profiles/to-do

#### Before launching web server:
- Run the database container via docker compose command `docker compose up -d` from workspace.
- Import environment variables declared in the `.env` file while in the dev container and using the command `export $(grep -v '^#' .env | xargs)`. If the file containing the environment variables is named something other than `.env`, you should modify the command to specify the correct name.
- Import the environment variables defined in the .env file in the project root. The environment variables were automatically exported from .env when the container was built. If the contents of the .env file have been modified after the container was built, run the command `export $(grep -v '^#' .env | xargs)`, but keep in mind that the environment variables will only be visible in the terminal session where the command was executed.

To launch the executable, click Launch in the CMake extension.
<p style="text-align: center;"><img src="docs/images/cmakeLaunch.png" alt="cmakeLaunch" width="400"/></p>
Expand Down Expand Up @@ -103,4 +104,4 @@ The alembic tool is used to work with migrations. To work with it, you need to m

Alternatively, you can use make targets.
- To create a migration, run the command `make create-migration name=<name-of-migration>`, where `name` is the name of the migration. You can also use `make create-migration`, in which case the migration will be named after the current date and time.
- To apply the migrations, run the `make apply-migrations` command.
- To apply the migrations, run the `make apply-migrations` command.
17 changes: 10 additions & 7 deletions src/data/db_connection.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "db_connection.h"
#include "../utils/app-config/app-config.h"

#include <cstdlib>
#include <stdexcept>
Expand All @@ -14,15 +15,17 @@ std::shared_ptr<odb::database> DbConnection::get()
flag,
[]()
{
const std::string user = std::getenv("POSTGRES_USER");
const std::string password = std::getenv("POSTGRES_PASSWORD");
const std::string db_name = std::getenv("POSTGRES_DB");
const std::string host = std::getenv("POSTGRES_HOST");
const std::string port = std::getenv("POSTGRES_PORT");
auto& config = AppConfig::GetInstance();

std::string conninfo = "host=" + host + " port=" + port + " dbname=" + db_name + " user=" + user + " password=" + password;
const std::string host = config.getDatabaseHost();
const std::string port = config.getDatabasePort();
const std::string user = config.getDatabaseUser();
const std::string password = config.getDatabasePassword();
const std::string dbname = config.getDatabaseName();

instance_ = std::shared_ptr<odb::database>(new odb::pgsql::database(conninfo));
std::string conn = "host=" + host + " port=" + port + " dbname=" + dbname + " user=" + user + " password=" + password;

instance_ = std::shared_ptr<odb::database>(new odb::pgsql::database(conn));
}
);

Expand Down
8 changes: 7 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
#include "utils/app-config/app-config.h"
#include <drogon/HttpAppFramework.h>

int main()
{
drogon::app().addListener("127.0.0.1", 8080).setLogLevel(trantor::Logger::kInfo).setThreadNum(1);
// We need to set the logging level right away; otherwise, the default level
// will be used until .setLogLevel is called
drogon::app().setLogLevel(AppConfig::getApiLogLevel());

auto& config = AppConfig::GetInstance();

drogon::app().addListener(config.getApiHost(), config.getApiPort()).setThreadNum(config.getApiNumThreads());
drogon::app().run();

return 0;
Expand Down
161 changes: 161 additions & 0 deletions src/utils/app-config/app-config.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#include "app-config.h"

AppConfig::AppConfig()
{
setApiHost(getEnv("API_HOST", "0.0.0.0"));
setApiPort(getEnvInt("API_PORT", 80));
setApiNumThreads(getEnvInt("API_NUMBER_OF_THREADS", 1));
setDatabaseHost(getEnv("POSTGRES_HOST", "0.0.0.0"));
setDatabasePort(getEnv("POSTGRES_PORT", "5432"));
setDatabaseName(getEnv("POSTGRES_DB", "to-dos-api-cpp-db"));
setDatabaseUser(getEnv("POSTGRES_USER", "postgres"));
setDatabasePassword(getEnv("POSTGRES_PASSWORD", "password"));
}

AppConfig& AppConfig::GetInstance()
{
static AppConfig instance;
return instance;
}

std::string AppConfig::getEnv(std::string name, std::string defaultValue)
{
char* value = std::getenv(name.c_str());

if (value)
{
return std::string(value);
}
else
{
LOG_WARN << "Failed to get the value of the " << name << " environment variable; the default value will be used: " << defaultValue;
return defaultValue;
}
}

uint32_t AppConfig::getEnvInt(std::string name, uint32_t defaultValue)
{
char* value = std::getenv(name.c_str());

if (!value)
return defaultValue;

try
{
auto result = std::stoi(value);
auto limit = std::numeric_limits<uint32_t>::max();

if (result < 0 || result > limit)
throw std::overflow_error(
"error: an attempt to write a " + name + " value which is larger than what can be stored in a " + std::to_string(limit)
);

return static_cast<uint32_t>(result);
}
catch (const std::exception& e)
{
LOG_ERROR << e.what();
return defaultValue;
}
}

trantor::Logger::LogLevel AppConfig::parseLogLevel(const std::string& level)
{
// kTrace < kDebug < kInfo < kWarn < kError
if (level == "TRACE")
return trantor::Logger::kTrace;
else if (level == "DEBUG")
return trantor::Logger::kDebug;
else if (level == "INFO")
return trantor::Logger::kInfo;
else if (level == "WARN")
return trantor::Logger::kWarn;
else
return trantor::Logger::kError;
}

void AppConfig::setApiHost(std::string apiHost)
{
apiHost_ = apiHost;
LOG_DEBUG << "Update value: apiHost_=" << apiHost_;
};

void AppConfig::setApiPort(uint32_t apiPort)
{
apiPort_ = apiPort;
LOG_DEBUG << "Update value: apiPort_=" << apiPort_;
};

void AppConfig::setApiNumThreads(uint32_t apiNumThreads)
{
uint32_t numberOfThreads = apiNumThreads;

if (numberOfThreads > 0)
{
apiNumThreads_ = numberOfThreads;
}
else
{
apiNumThreads_ = 1;
LOG_ERROR << "The number of allocated threads cannot be less than 1";
}

LOG_DEBUG << "Update value: apiNumThreads_=" << apiNumThreads_;
};

void AppConfig::setDatabaseHost(std::string databaseHost)
{
databaseHost_ = databaseHost;
LOG_DEBUG << "Update value: databaseHost_=" << databaseHost_;
};

void AppConfig::setDatabasePort(std::string databasePort)
{
databasePort_ = databasePort;
LOG_DEBUG << "Update value: databasePort_=" << databasePort_;
};

void AppConfig::setDatabaseName(std::string databaseName)
{
databaseName_ = databaseName;
LOG_DEBUG << "Update value: databaseName_=" << databaseName_;
};

void AppConfig::setDatabaseUser(std::string databaseUser)
{
databaseUser_ = databaseUser;
LOG_DEBUG << "Update value: databaseUser_=" << databaseUser_;
};

void AppConfig::setDatabasePassword(std::string databasePassword)
{
databasePassword_ = databasePassword;
LOG_DEBUG << "Update value: databasePassword_=" << databasePassword_;
};

const std::string& AppConfig::getApiHost() const
{ return apiHost_; }

const uint32_t& AppConfig::getApiPort() const
{ return apiPort_; }

const uint32_t& AppConfig::getApiNumThreads() const
{ return apiNumThreads_; }

const trantor::Logger::LogLevel AppConfig::getApiLogLevel()
{ return parseLogLevel(getEnv("API_LOG_LEVEL", "INFO")); }

const std::string& AppConfig::getDatabaseHost() const
{ return databaseHost_; }

const std::string& AppConfig::getDatabasePort() const
{ return databasePort_; }

const std::string& AppConfig::getDatabaseName() const
{ return databaseName_; }

const std::string& AppConfig::getDatabaseUser() const
{ return databaseUser_; }

const std::string& AppConfig::getDatabasePassword() const
{ return databasePassword_; }
Loading
Loading