just some thoughts
This commit is contained in:
parent
03d7614673
commit
f0d205dee7
@ -13,14 +13,37 @@ include(GNUInstallDirs)
|
|||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
set(PICA_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif()
|
||||||
|
message("Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
|
set(COMPILE_OPTIONS -fno-sized-deallocation)
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL Release)
|
||||||
|
list(APPEND COMPILE_OPTIONS -O3)
|
||||||
|
elseif (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
|
list(APPEND COMPILE_OPTIONS -g)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wall)
|
||||||
|
list(APPEND COMPILE_OPTIONS -Wextra)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(COMPILE_OPTIONS_STRING "")
|
||||||
|
foreach(element IN LISTS COMPILE_OPTIONS)
|
||||||
|
if(NOT COMPILE_OPTIONS_STRING STREQUAL "")
|
||||||
|
set(COMPILE_OPTIONS_STRING "${COMPILE_OPTIONS_STRING} ")
|
||||||
|
endif()
|
||||||
|
set(COMPILE_OPTIONS_STRING "${COMPILE_OPTIONS_STRING}${element}")
|
||||||
|
endforeach()
|
||||||
|
message("Compile options: " ${COMPILE_OPTIONS_STRING})
|
||||||
|
|
||||||
find_package(nlohmann_json REQUIRED)
|
find_package(nlohmann_json REQUIRED)
|
||||||
find_package(FCGI REQUIRED)
|
find_package(FCGI REQUIRED)
|
||||||
|
|
||||||
add_executable(pica main.cpp)
|
add_executable(${PROJECT_NAME} main.cpp)
|
||||||
target_include_directories(pica PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_include_directories(pica PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS})
|
||||||
|
|
||||||
add_subdirectory(server)
|
add_subdirectory(server)
|
||||||
add_subdirectory(request)
|
add_subdirectory(request)
|
||||||
@ -31,10 +54,10 @@ add_subdirectory(utils)
|
|||||||
|
|
||||||
configure_file(config.h.in config.h @ONLY)
|
configure_file(config.h.in config.h @ONLY)
|
||||||
|
|
||||||
target_link_libraries(pica PRIVATE
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
FCGI::FCGI
|
FCGI::FCGI
|
||||||
FCGI::FCGI++
|
FCGI::FCGI++
|
||||||
nlohmann_json::nlohmann_json
|
nlohmann_json::nlohmann_json
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS pica RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
@ -7,3 +7,4 @@
|
|||||||
#define BIN_DIR "@CMAKE_INSTALL_BINDIR@"
|
#define BIN_DIR "@CMAKE_INSTALL_BINDIR@"
|
||||||
|
|
||||||
#define PROJECT_NAME "@PROJECT_NAME@"
|
#define PROJECT_NAME "@PROJECT_NAME@"
|
||||||
|
#define PROJECT_VERSION "@PROJECT_VERSION@"
|
||||||
|
@ -6,7 +6,7 @@ set(SOURCES
|
|||||||
dbinterface.cpp
|
dbinterface.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(pica PRIVATE ${SOURCES})
|
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
|
||||||
|
|
||||||
add_subdirectory(mysql)
|
add_subdirectory(mysql)
|
||||||
add_subdirectory(migrations)
|
add_subdirectory(migrations)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
set(MIGRATIONS migrations)
|
set(MIGRATIONS migrations)
|
||||||
configure_file(m0.sql ${PICA_BIN_DIR}/${CMAKE_INSTALL_DATADIR}/${MIGRATIONS}/m0.sql COPYONLY)
|
configure_file(m0.sql ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/${MIGRATIONS}/m0.sql COPYONLY)
|
||||||
|
|
||||||
install(
|
install(
|
||||||
FILES
|
FILES
|
||||||
|
@ -1,6 +1,36 @@
|
|||||||
|
--creating system table
|
||||||
CREATE TABLE IF NOT EXISTS system (
|
CREATE TABLE IF NOT EXISTS system (
|
||||||
`key` VARCHAR(32) PRIMARY KEY,
|
`key` VARCHAR(32) PRIMARY KEY,
|
||||||
`value` TEXT
|
`value` TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
--creating roles table
|
||||||
|
CREATE TABLE IF NOT EXISTS roles (
|
||||||
|
`id` INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`name` VARCHAR(256) UNIQUE NOT NULL,
|
||||||
|
`color` INTEGER UNSIGNED DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
--creating accounts table
|
||||||
|
CREATE TABLE IF NOT EXISTS accounts (
|
||||||
|
`id` INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
`login` VARCHAR(256) UNIQUE NOT NULL,
|
||||||
|
`nick` VARCHAR(256),
|
||||||
|
`type` INTEGER UNSIGNED NOT NULL,
|
||||||
|
`password` VARCHAR(64),
|
||||||
|
`salt` VARCHAR(32),
|
||||||
|
`role` INTEGER UNSIGNED NOT NULL,
|
||||||
|
`created` TIMESTAMP DEFAULT UTC_TIMESTAMP(),
|
||||||
|
|
||||||
|
FOREIGN KEY (role) REFERENCES roles(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
--creating defailt roles
|
||||||
|
INSERT IGNORE INTO roles (`name`)
|
||||||
|
VALUES ('root');
|
||||||
|
|
||||||
|
--inserting initial version
|
||||||
INSERT INTO system (`key`, `value`) VALUES ('version', '0');
|
INSERT INTO system (`key`, `value`) VALUES ('version', '0');
|
||||||
|
|
||||||
|
--recording initial time
|
||||||
|
INSERT INTO system (`key`, `value`) VALUES ('created', UTC_TIMESTAMP());
|
||||||
|
@ -10,6 +10,6 @@ set(SOURCES
|
|||||||
|
|
||||||
find_package(MariaDB REQUIRED)
|
find_package(MariaDB REQUIRED)
|
||||||
|
|
||||||
target_sources(pica PRIVATE ${SOURCES})
|
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
|
||||||
|
|
||||||
target_link_libraries(pica PRIVATE MariaDB::client)
|
target_link_libraries(${PROJECT_NAME} PRIVATE MariaDB::client)
|
||||||
|
@ -116,6 +116,14 @@ void MySQL::executeFile(const std::filesystem::path& relativePath) {
|
|||||||
std::ifstream inputFile(path);
|
std::ifstream inputFile(path);
|
||||||
std::string query;
|
std::string query;
|
||||||
while (std::getline(inputFile, query, ';')) {
|
while (std::getline(inputFile, query, ';')) {
|
||||||
|
std::optional<std::string> comment = getComment(query);
|
||||||
|
while (comment) {
|
||||||
|
std::cout << '\t' << comment.value() << std::endl;
|
||||||
|
comment = getComment(query);
|
||||||
|
}
|
||||||
|
if (query.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
int result = mysql_query(con, query.c_str());
|
int result = mysql_query(con, query.c_str());
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
int errcode = mysql_errno(con);
|
int errcode = mysql_errno(con);
|
||||||
@ -162,15 +170,49 @@ void MySQL::migrate(uint8_t targetVersion) {
|
|||||||
uint8_t currentVersion = getVersion();
|
uint8_t currentVersion = getVersion();
|
||||||
|
|
||||||
while (currentVersion < targetVersion) {
|
while (currentVersion < targetVersion) {
|
||||||
|
if (currentVersion == 255)
|
||||||
|
throw std::runtime_error("Maximum possible database version reached");
|
||||||
|
|
||||||
|
uint8_t nextVersion = currentVersion + 1;
|
||||||
std::string fileName = "migrations/m" + std::to_string(currentVersion) + ".sql";
|
std::string fileName = "migrations/m" + std::to_string(currentVersion) + ".sql";
|
||||||
std::cout << "Performing migration "
|
std::cout << "Performing migration "
|
||||||
<< std::to_string(currentVersion)
|
<< std::to_string(currentVersion)
|
||||||
<< " -> "
|
<< " -> "
|
||||||
<< std::to_string(++currentVersion)
|
<< std::to_string(nextVersion)
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
executeFile(fileName);
|
executeFile(fileName);
|
||||||
setVersion(currentVersion);
|
setVersion(nextVersion);
|
||||||
|
currentVersion = nextVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Database is now on actual version " << std::to_string(targetVersion) << std::endl;
|
std::cout << "Database is now on actual version " << std::to_string(targetVersion) << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> MySQL::getComment(std::string& string) {
|
||||||
|
ltrim(string);
|
||||||
|
if (string.length() < 2)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (string[0] == '-') {
|
||||||
|
if (string[1] == '-') {
|
||||||
|
string.erase(0, 2);
|
||||||
|
std::string::size_type eol = string.find('\n');
|
||||||
|
return extract(string, 0, eol);
|
||||||
|
}
|
||||||
|
} else if (string[0] == '/') {
|
||||||
|
if (string[1] == '*') {
|
||||||
|
string.erase(0, 2);
|
||||||
|
std::string::size_type end = 0;
|
||||||
|
do {
|
||||||
|
end = string.find(end, '*');
|
||||||
|
} while (end != std::string::npos && end < string.size() - 1 && string[end + 1] == '/');
|
||||||
|
if (end < string.size() - 1)
|
||||||
|
end = std::string::npos;
|
||||||
|
|
||||||
|
return extract(string, 0, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <mysql.h>
|
#include <mysql.h>
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void executeFile(const std::filesystem::path& relativePath);
|
void executeFile(const std::filesystem::path& relativePath);
|
||||||
|
static std::optional<std::string> getComment(std::string& string);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MYSQL connection;
|
MYSQL connection;
|
||||||
|
@ -6,4 +6,4 @@ set(SOURCES
|
|||||||
response.cpp
|
response.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(pica PRIVATE ${SOURCES})
|
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
|
||||||
|
@ -8,4 +8,4 @@ set(SOURCES
|
|||||||
router.cpp
|
router.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(pica PRIVATE ${SOURCES})
|
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
|
||||||
|
@ -20,17 +20,7 @@ Server::Server():
|
|||||||
db->setCredentials("pica", "pica");
|
db->setCredentials("pica", "pica");
|
||||||
db->setDatabase("pica");
|
db->setDatabase("pica");
|
||||||
|
|
||||||
bool connected = false;
|
|
||||||
try {
|
|
||||||
db->connect("/run/mysqld/mysqld.sock");
|
db->connect("/run/mysqld/mysqld.sock");
|
||||||
connected = true;
|
|
||||||
std::cout << "Successfully connected to the database" << std::endl;
|
|
||||||
|
|
||||||
} catch (const std::runtime_error& e) {
|
|
||||||
std::cerr << "Couldn't connect to the database: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connected)
|
|
||||||
db->migrate(currentDbVesion);
|
db->migrate(currentDbVesion);
|
||||||
|
|
||||||
router.addRoute("info", Server::info);
|
router.addRoute("info", Server::info);
|
||||||
@ -74,7 +64,7 @@ void Server::handleRequest(std::unique_ptr<Request> request) {
|
|||||||
try {
|
try {
|
||||||
std::string path = request->getPath(serverName.value());
|
std::string path = request->getPath(serverName.value());
|
||||||
router.route(path.data(), std::move(request), this);
|
router.route(path.data(), std::move(request), this);
|
||||||
} catch (const std::exception e) {
|
} catch (const std::exception& e) {
|
||||||
Response error(Response::Status::internalError);
|
Response error(Response::Status::internalError);
|
||||||
error.setBody(std::string(e.what()));
|
error.setBody(std::string(e.what()));
|
||||||
error.replyTo(*request.get());
|
error.replyTo(*request.get());
|
||||||
@ -82,7 +72,7 @@ void Server::handleRequest(std::unique_ptr<Request> request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Server::printEnvironment(Request* request, Server* server) {
|
bool Server::printEnvironment(Request* request, Server* server) {
|
||||||
(void)server;
|
UNUSED(server);
|
||||||
nlohmann::json body = nlohmann::json::object();
|
nlohmann::json body = nlohmann::json::object();
|
||||||
request->printEnvironment(body);
|
request->printEnvironment(body);
|
||||||
|
|
||||||
@ -94,11 +84,11 @@ bool Server::printEnvironment(Request* request, Server* server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Server::info(Request* request, Server* server) {
|
bool Server::info(Request* request, Server* server) {
|
||||||
(void)server;
|
UNUSED(server);
|
||||||
Response res;
|
Response res;
|
||||||
nlohmann::json body = nlohmann::json::object();
|
nlohmann::json body = nlohmann::json::object();
|
||||||
body["type"] = "Pica";
|
body["type"] = PROJECT_NAME;
|
||||||
body["version"] = "0.0.1";
|
body["version"] = PROJECT_VERSION;
|
||||||
|
|
||||||
res.setBody(body);
|
res.setBody(body);
|
||||||
res.replyTo(*request);
|
res.replyTo(*request);
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#include "response/response.h"
|
#include "response/response.h"
|
||||||
#include "router.h"
|
#include "router.h"
|
||||||
#include "database/dbinterface.h"
|
#include "database/dbinterface.h"
|
||||||
|
#include "utils/helpers.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
public:
|
public:
|
||||||
|
@ -8,4 +8,4 @@ set(SOURCES
|
|||||||
ostream.cpp
|
ostream.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_sources(pica PRIVATE ${SOURCES})
|
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
|
||||||
|
@ -4,12 +4,18 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
#include "iostream"
|
#include "iostream"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
static bool installed = false;
|
static bool installed = false;
|
||||||
static std::filesystem::path sPath;
|
static std::filesystem::path sPath;
|
||||||
|
|
||||||
|
bool isSpace(char ch){
|
||||||
|
return std::isspace(static_cast<unsigned char>(ch));
|
||||||
|
}
|
||||||
|
|
||||||
void setAbsoluteSharedPath () {
|
void setAbsoluteSharedPath () {
|
||||||
installed = true;
|
installed = true;
|
||||||
sPath = FULL_DATA_DIR "/" PROJECT_NAME; // should be something like /usr/share/pica or /local/usr/share/pica
|
sPath = FULL_DATA_DIR "/" PROJECT_NAME; // should be something like /usr/share/pica or /local/usr/share/pica
|
||||||
@ -36,7 +42,7 @@ void initPaths(const char* programPath) {
|
|||||||
if (endsWith(parent.string(), BIN_DIR)) { //this is the case when the program is installed somewhere but not system root
|
if (endsWith(parent.string(), BIN_DIR)) { //this is the case when the program is installed somewhere but not system root
|
||||||
std::filesystem::path bin(BIN_DIR); //so it will read from something like ../share/pica/ relative to the binary
|
std::filesystem::path bin(BIN_DIR); //so it will read from something like ../share/pica/ relative to the binary
|
||||||
for (const auto& hop : bin) {
|
for (const auto& hop : bin) {
|
||||||
(void)hop; //I do this just to make as many ups as many members are in bin
|
UNUSED(hop); //I do this just to make as many ups as many members are in bin
|
||||||
parent = parent.parent_path();
|
parent = parent.parent_path();
|
||||||
}
|
}
|
||||||
sPath = parent / DATA_DIR / PROJECT_NAME;
|
sPath = parent / DATA_DIR / PROJECT_NAME;
|
||||||
@ -59,3 +65,40 @@ bool endsWith(const std::string& string, const std::string& query) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ltrim(std::string& string) {
|
||||||
|
string.erase(
|
||||||
|
string.begin(),
|
||||||
|
std::find_if(
|
||||||
|
string.begin(),
|
||||||
|
string.end(),
|
||||||
|
std::not_fn(isSpace)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rtrim(std::string& string) {
|
||||||
|
string.erase(
|
||||||
|
std::find_if(
|
||||||
|
string.rbegin(),
|
||||||
|
string.rend(),
|
||||||
|
std::not_fn(isSpace)
|
||||||
|
).base(),
|
||||||
|
string.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void trim(std::string& string) {
|
||||||
|
ltrim(string);
|
||||||
|
rtrim(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string extract(std::string& string, std::string::size_type begin, std::string::size_type end) {
|
||||||
|
std::string result = string.substr(begin, end);
|
||||||
|
if (end == std::string::npos)
|
||||||
|
string.erase(begin, end);
|
||||||
|
else
|
||||||
|
string.erase(begin, result.length() + 1);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -6,7 +6,12 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#define UNUSED(variable) (void)variable
|
||||||
|
|
||||||
void initPaths(const char* programPath);
|
void initPaths(const char* programPath);
|
||||||
const std::filesystem::path& sharedPath();
|
const std::filesystem::path& sharedPath();
|
||||||
bool endsWith(const std::string& string, const std::string& query);
|
bool endsWith(const std::string& string, const std::string& query);
|
||||||
|
void ltrim(std::string& string);
|
||||||
|
void rtrim(std::string& string);
|
||||||
|
void trim(std::string& string);
|
||||||
|
std::string extract(std::string& string, std::string::size_type begin, std::string::size_type end);
|
||||||
|
Loading…
Reference in New Issue
Block a user