cavecomm/src/main.cpp

1027 lines
54 KiB
C++

#include <vector>
#include <string>
#include <thread>
#include <pqxx/pqxx>
#include "crow.h"
#include "crow/middlewares/cookie_parser.h"
#include "database.cpp"
#include "utilities.cpp"
#include "regularTaskExecution.cpp"
#include "templateConstCollection.cpp"
using namespace std;
using namespace TemplateConstCollection;
int main(int argc, char *argv[]) {
Utilities::config configuration;
if (argc > 1)
configuration.configPath = argv[1];
if (configuration.readConfigFile()) {
cout << "ERROR: Unable to read configuration file: " << configuration.configPath << endl;
cout << "Aborting Startup!" << endl;
return 1;
}
// Create app with Middleware
crow::App<crow::CookieParser> app;
// starts thread that executes modules
thread threadRegularExecution(RegularTaskExecution::regularExecution, configuration);
/*
* Freelancer Profile listing for customers
*/
CROW_ROUTE(app, "/").methods("POST"_method, "GET"_method)
([&, configuration](const crow::request& request) {
int selectedPage = 1;
if (!request.url_params.keys().empty() && request.url_params.get("page") != nullptr) {
string selectedPageString = request.url_params.get("page");
if (!selectedPageString.empty())
selectedPage = stoi(selectedPageString);
}
auto page = crow::mustache::load(TEMPLATE_CUSTOMER_INDEX_FREELANCER_LISTING);
crow::mustache::context ctx(Utilities::getFreelancerListing(configuration, selectedPage));
auto& cookieCtx = app.get_context<crow::CookieParser>(request);
if (Utilities::checkCookieLoginState(configuration, cookieCtx))
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
if (configuration.itemsPerPage > 0) {
ctx[MUSTACHE_PAGINATION] = true;
vector<int> pages = Utilities::getFreelancerIndexPagination(configuration);
ctx[MUSTACHE_PAGINATION_NUMBERS] = pages;
if (selectedPage <= 1)
ctx[MUSTACHE_PAGINATION_PREVIOUS] = 1;
else
ctx[MUSTACHE_PAGINATION_PREVIOUS] = selectedPage - 1;
if (selectedPage >= pages.size())
ctx[MUSTACHE_PAGINATION_NEXT] = pages.size();
else
ctx[MUSTACHE_PAGINATION_NEXT] = selectedPage + 1;
}
return page.render(ctx);
});
/*
* Freelancer Profile listing for customers
*/
CROW_ROUTE(app, "/@<string>")
([configuration](string alias) {
crow::mustache::context ctx(Utilities::getAlias(configuration, alias));
auto page = crow::mustache::load(TEMPLATE_ALIAS_REDIRECT);
return page.render(ctx);
});
/*
* Freelancer Profile Page for customers
*/
CROW_ROUTE(app, "/customer/<string>").methods("POST"_method)
([configuration](const crow::request& postRequest, string freelancerName ) {
//Parses the request body and extracts the specified value
int freelancerID = stoi(Utilities::extractSingleValueFromRequestBody(postRequest.body.c_str(), "freelancerID"));
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatements(databaseConnection, {
ID_SELECT_FREELANCER,
ID_SELECT_FREELANCER_TEMPLATES
});
pqxx::result resultFreelancer = Database::executePreparedStatement_SELECT_FREELANCER(databaseConnection, freelancerID);
//error handling if invalid ID was delivered
string freelancerHTML = TEMPLATE_CUSTOMER_FREELANCER_LISTING_NOT_FOUND;
crow::json::wvalue resultJsonFreelancerTemplate;
if (!resultFreelancer.empty()){
freelancerHTML = TEMPLATE_CUSTOMER_FREELANCER_LISTING;
pqxx::result resultFreelancerTemplates = Database::executePreparedStatement_SELECT_FREELANCER_TEMPLATES(databaseConnection, freelancerID);
resultJsonFreelancerTemplate = Database::convertResultToJSON(resultFreelancerTemplates, "templates");
}
auto page = crow::mustache::load(freelancerHTML);
crow::mustache::context ctx(resultJsonFreelancerTemplate);
if (!resultFreelancer.empty()){
/*
* puts freelancer data into context
*/
for (int i = 0; i < resultFreelancer.columns(); ++i) {
string freelancerColumn = resultFreelancer.column_name(i);
ctx["freelancer" + freelancerColumn] = resultFreelancer.at(0).at(i).c_str();
}
}
ctx["freelancerid"] = freelancerID;
ctx["selectedfreelancername"] = freelancerName;
return page.render(ctx);
});
/*
* Page representing a freelancers Template
*/
CROW_ROUTE(app, "/customer/<string>/template/<string>").methods("POST"_method)
([configuration](const crow::request& postRequest, string freelancerName, string templateName ) {
int templateID = stoi(Utilities::extractSingleValueFromRequestBody(postRequest.body.c_str(), "templateID"));
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatements(databaseConnection, {
ID_SELECT_TEMPLATE,
ID_SELECT_FREELANCER_COMMISSION_STATE
});
pqxx::result resultTemplate = Database::executePreparedStatement_SELECT_TEMPLATE(databaseConnection, templateID);
crow::json::wvalue resultJsonTemplate = Database::convertResultRowToJSON(resultTemplate);
auto page = crow::mustache::load(TEMPLATE_CUSTOMER_FREELANCER_TEMPLATE);
crow::mustache::context ctx(resultJsonTemplate);
bool commissionState = false;
if (!resultTemplate.empty()) {
//use freelancerID based on SQL_STATEMENT_SELECT_TEMPLATE
pqxx::result resultCommissionState = Database::executePreparedStatement_SELECT_FREELANCER_COMMISSION_STATE(databaseConnection, stoi(resultTemplate.at(0).at(2).c_str()));
//the commission-state
if (resultCommissionState.empty() || stoi(resultCommissionState.at(0).at(0).c_str()) == 1)
commissionState = true;
}
ctx[MUSTACHE_ERROR_COMMISSIONS_CLOSED] = commissionState;
return page.render(ctx);
});
/*
* Page for entry of request data
*/
CROW_ROUTE(app, "/customer/<string>/template/<string>/request").methods("POST"_method)
([configuration](const crow::request& postRequest, string freelancerName, string templateName ) {
int templateID = stoi(Utilities::extractSingleValueFromRequestBody(postRequest.body.c_str(), "templateID"));
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatement(databaseConnection, ID_SELECT_TEMPLATE);
pqxx::result resultTemplate = Database::executePreparedStatement_SELECT_TEMPLATE(databaseConnection, templateID);
crow::json::wvalue resultJsonTemplate = Database::convertResultRowToJSON(resultTemplate);
auto page = crow::mustache::load(TEMPLATE_CUSTOMER_FREELANCER_TEMPLATE_REQUEST);
crow::mustache::context ctx(resultJsonTemplate);
return page.render(ctx);
});
/*
* executes the creation of a new request.
*/
CROW_ROUTE(app, "/customer/<string>/template/<string>/request/fulfilment").methods("POST"_method)
([configuration](const crow::request& postRequest, string freelancerName, string templateName ) {
pqxx::connection databaseConnection(configuration.databaseConnectionString);
string templateHTML = TEMPLATE_CUSTOMER_FREELANCER_TEMPLATE_REQUEST_FULFILMENT_ERROR;
Database::requestsItem newRequest;
crow::mustache::context ctx;
ctx["freelancername"] = freelancerName;
ctx["templatename"] = templateName;
string postRequestBody = postRequest.body.c_str();
Utilities::decodeString(postRequestBody);
vector<string> splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&');
bool requestFillCompletion = false;
for (const string& item : splitPostRequestBody) {
vector<string> splitItem = Utilities::splitStringIntoVector(item, '=');
if (splitItem.at(0) == "customername")
newRequest.customerName = splitItem.at(1);
if (splitItem.at(0) == "email")
newRequest.customerEmailAddress = splitItem.at(1);
if (splitItem.at(0) == "customercontactdetails")
newRequest.customerContactDetails = splitItem.at(1);
if (splitItem.at(0) == "requestdescription")
newRequest.requestDescription = splitItem.at(1);
if (splitItem.at(0) == "templateID")
newRequest.templateID = stoi(splitItem.at(1));
}
if (newRequest.customerName != "" && (newRequest.customerEmailAddress != "" || newRequest.customerContactDetails != "") && newRequest.requestDescription != "" )
requestFillCompletion = true;
ctx["templateid"] = newRequest.templateID;
Database::prepareStatements(databaseConnection, {
ID_SELECT_TEMPLATE_FLAT,
ID_SELECT_FREELANCER_COMMISSION_STATE,
ID_INSERT_ITEM_IN_REQUESTS,
ID_SELECT_FREELANCER_EMAIL
});
pqxx::result resultTemplate = Database::executePreparedStatement_SELECT_TEMPLATE_FLAT(databaseConnection, newRequest.templateID);
if (!resultTemplate.empty() && requestFillCompletion) {
for (int i = 0; i < resultTemplate.columns(); ++i) {
//freelancerid
newRequest.freelancerID = stoi(resultTemplate.at(0).at(0).c_str());
//currencypreference
newRequest.currencyPreference = resultTemplate.at(0).at(1).c_str();
//priceupfront
newRequest.priceUpFront = resultTemplate.at(0).at(2).c_str();
//priceondeliver
newRequest.priceOnDeliver = resultTemplate.at(0).at(3).c_str();
}
pqxx::result resultCommissionState = Database::executePreparedStatement_SELECT_FREELANCER_COMMISSION_STATE(databaseConnection, newRequest.freelancerID);
//the commission-state
if (resultCommissionState.empty() || stoi(resultCommissionState.at(0).at(0).c_str()) == 1) {
ctx[MUSTACHE_ERROR_COMMISSIONS_CLOSED] = true;
}
else {
int resultInsertOperation = Database::executePreparedStatement_INSERT_ITEM_IN_REQUESTS(databaseConnection, newRequest);
if (resultInsertOperation == 0) {
templateHTML = TEMPLATE_CUSTOMER_FREELANCER_TEMPLATE_REQUEST_FULFILMENT;
pqxx::result resultEmailAddress = Database::executePreparedStatement_SELECT_FREELANCER_EMAIL(databaseConnection, newRequest.freelancerID);
if (!resultEmailAddress.empty())
Utilities::sendEmail(configuration, resultEmailAddress.at(0).at(0).c_str(), "NEW REQUEST", newRequest.toJSONString());
}
else {
ctx[MUSTACHE_ERROR_UNABLE_TO_CREATE_REQUEST] = true;
}
}
}
else {
if (requestFillCompletion)
ctx[MUSTACHE_ERROR_TEMPLATE_NOT_FOUND] = true;
else
ctx[MUSTACHE_REQUEST_NOT_FILLED] = true;
}
auto page = crow::mustache::load(templateHTML);
return page.render(ctx);
});
/*
* redirect to /freelancer/login
*/
CROW_ROUTE(app, "/login")
([](crow::response& res) {
res.redirect("/freelancer/login");
res.end();
});
/*
* redirect to /freelancer/logout
*/
CROW_ROUTE(app, "/logout")
([](crow::response& res) {
res.redirect("/freelancer/logout");
res.end();
});
/*
* Logs out a freelancer by replacing validation key and expiring cookies
*/
CROW_ROUTE(app, "/freelancer/logout")
([&, configuration](const crow::request& getRequest, crow::response& res) {
auto& ctx = app.get_context<crow::CookieParser>(getRequest);
if (Utilities::checkCookieLoginState(configuration, ctx)) {
std::string freelancerEmail = ctx.get_cookie("freelancerEmail");
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatement(databaseConnection, ID_UPDATE_LOGIN_VALIDATION_KEY);
Database::executePreparedStatement_UPDATE_LOGIN_VALIDATION_KEY(databaseConnection, "EXPIRED", freelancerEmail);
ctx.set_cookie(COOKIE_LOGIN_KEY, Utilities::generateExpiredCookie());
ctx.set_cookie(COOKIE_FREELANCER_EMAIL, Utilities::generateExpiredCookie());
}
res.redirect("/");
res.end();
});
/*
* Page for freelancer to log in
*/
CROW_ROUTE(app, "/freelancer/login")
([&,configuration](const crow::request& getRequest ) {
auto& ctx = app.get_context<crow::CookieParser>(getRequest);
string templateHTML = TEMPLATE_FREELANCER_LOGIN;
if (Utilities::checkCookieLoginState(configuration, ctx))
templateHTML = TEMPLATE_FREELANCER_REDIRECT_PROFILE;
auto page = crow::mustache::load(templateHTML);
return page.render();
});
/*
* Page for freelancer to request a password reset email
*/
CROW_ROUTE(app, "/freelancer/login/passwordreset")
([]() {
auto page = crow::mustache::load(TEMPLATE_FREELANCER_LOGIN_PASSWORD_RESET);
return page.render();
});
/*
* Page for freelancer password reset fulfilment
*/
CROW_ROUTE(app, "/freelancer/login/passwordreset/fulfilment")
([&, configuration](const crow::request& postRequest ) {
crow::mustache::context ctx;
string postRequestBody = postRequest.body;
Utilities::decodeString(postRequestBody);
vector<string> splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&');
string email;
for (const string& item : splitPostRequestBody) {
vector<string> splitItem = Utilities::splitStringIntoVector(item, '=');
if (splitItem.at(0) == "freelanceremail")
email = splitItem.at(1);
}
if (!email.empty()) {
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatements(databaseConnection, {
ID_SELECT_CHECK_EMAIL_EXISTS,
ID_SELECT_CHECK_FREELANCER_RESET_KEY,
ID_DELETE_FREELANCER_RESET_KEY,
ID_INSERT_FREELANCER_RESET_KEY
});
pqxx::result checkFreelancerExists = Database::executePreparedStatement_SELECT_CHECK_EMAIL_EXISTS(databaseConnection, email);
int checkFreelancerExistsExtracted = stoi(checkFreelancerExists.at(0).at(0).c_str());
if (checkFreelancerExistsExtracted == 1) {
std::string passwordResetKey = Utilities::generateRandomHashValueSHA256();
pqxx::result checkFreelancerResetKeyExists = Database::executePreparedStatement_SELECT_CHECK_FREELANCER_RESET_KEY(databaseConnection, email);
int checkFreelancerResetKeyExistsExtracted = stoi(checkFreelancerResetKeyExists.at(0).at(0).c_str());
if (checkFreelancerResetKeyExistsExtracted > 0)
Database::executePreparedStatement_DELETE_FREELANCER_RESET_KEY(databaseConnection, email);
Database::executePreparedStatement_INSERT_FREELANCER_RESET_KEY(databaseConnection, email, passwordResetKey);
if (Utilities::sendPasswordResetEmail(configuration, email, passwordResetKey) == 0)
ctx["passwordresetmailsent"] = true;
}
ctx["resetemail"] = email;
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_LOGIN_PASSWORD_RESET_FULFILMENT);
return page.render(ctx);
});
/*
* Page for freelancer to reset a password from a requested PasswordReset
*/
CROW_ROUTE(app, "/passwordreset/<string>")
([&, configuration](string passwordResetKey) {
crow::mustache::context ctx;
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatement(databaseConnection, ID_SELECT_FREELANCER_EMAIL_FROM_PASSWORD_RESET_KEY);
pqxx::result freelancerEmail = Database::executePreparedStatement_SELECT_FREELANCER_EMAIL_FROM_PASSWORD_RESET_KEY(databaseConnection, passwordResetKey);
if (!freelancerEmail.empty()) {
ctx["freelanceremail"] = freelancerEmail.at(0).at(0).c_str();
ctx["passwordresetkey"] = passwordResetKey;
}
else {
ctx[MUSTACHE_PASSWORD_RESET_DOES_NOT_EXIST] = true;
}
auto page = crow::mustache::load(TEMPLATE_PASSWORD_RESET);
return page.render(ctx);
});
/*
* Page for fulfillment of password reset
*/
CROW_ROUTE(app, "/passwordreset/<string>/fulfilment").methods("POST"_method)
([&, configuration](const crow::request& postRequest, string passwordResetKey) {
crow::mustache::context ctx;
string postRequestBody = postRequest.body;
Utilities::decodeString(postRequestBody);
vector<string> splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&');
string password, passwordConfirmation;
for (const string& item : splitPostRequestBody) {
vector<string> splitItem = Utilities::splitStringIntoVector(item, '=');
if (splitItem.at(0) == "freelancerpassword")
password = splitItem.at(1);
if (splitItem.at(0) == "freelancerpasswordconfirmation")
passwordConfirmation = splitItem.at(1);
}
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatements(databaseConnection, {
ID_SELECT_FREELANCER_EMAIL_FROM_PASSWORD_RESET_KEY,
ID_SELECT_CHECK_FREELANCER_RESET_KEY_EXPIRED,
ID_DELETE_FREELANCER_RESET_KEY,
ID_UPDATE_FREELANCER_PASSWORD_HASH
});
pqxx::result freelancerEmail = Database::executePreparedStatement_SELECT_FREELANCER_EMAIL_FROM_PASSWORD_RESET_KEY(databaseConnection, passwordResetKey);
if (!freelancerEmail.empty() && !password.empty() && password.compare(passwordConfirmation) != 0) {
string email = freelancerEmail.at(0).at(0).c_str();
pqxx::result keyExpiration = Database::executePreparedStatement_SELECT_CHECK_FREELANCER_RESET_KEY_EXPIRED(databaseConnection, email);
if (stoi(keyExpiration.at(0).at(0).c_str()) == 0) {
Database::executePreparedStatement_DELETE_FREELANCER_RESET_KEY(databaseConnection, email);
string pwsalt = Utilities::generateSalt();
string pwhash = Utilities::hashPassword(pwsalt, password);
int errorLevel = Database::executePreparedStatement_UPDATE_FREELANCER_PASSWORD_HASH(databaseConnection, pwhash, pwsalt, email);
if (errorLevel == 0) {
ctx[MUSTACHE_RESET_SUCCESS] = true;
ctx["freelanceremail"] = email;
}
else {
ctx[MUSTACHE_RESET_ERROR] = true;
if (errorLevel == 1)
ctx[MUSTACHE_RESET_ERROR_QUERY] = true;
if (errorLevel == 2)
ctx[MUSTACHE_RESET_ERROR_CRITICAL] = true;
}
}
else {
ctx[MUSTACHE_PASSWORD_RESET_EXPIRED] = true;
}
}
else {
if (password.empty())
ctx[MUSTACHE_PASSWORD_EMPTY] = true;
if (freelancerEmail.empty())
ctx[MUSTACHE_PASSWORD_RESET_DOES_NOT_EXIST] = true;
if (password.compare(passwordConfirmation) != 0)
ctx[MUSTACHE_PASSWORD_RESET_PASS_CONFIRMATION] = true;
}
auto page = crow::mustache::load(TEMPLATE_PASSWORD_RESET_FULFILMENT);
return page.render(ctx);
});
/*
* Page for freelancer to log in fulfillment
*/
CROW_ROUTE(app, "/freelancer/login/fulfilment").methods("POST"_method)
([&, configuration](const crow::request& postRequest ) {
crow::mustache::context ctx;
string postRequestBody = postRequest.body;
Utilities::decodeString(postRequestBody);
vector<string> splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&');
string email, password;
bool stayLoggedIn;
for (const string& item : splitPostRequestBody) {
vector<string> splitItem = Utilities::splitStringIntoVector(item, '=');
if (splitItem.at(0) == "freelanceremail")
email = splitItem.at(1);
if (splitItem.at(0) == "freelancerpassword")
password = splitItem.at(1);
if (splitItem.at(0) == "stayloggedin")
stayLoggedIn = !splitItem.at(1).empty(); //if checkbox not set result is empty ie stay logged in is false, if it is set result is "on" ie not empty ie stay logged in is true
}
//check if login data is complete
if (!email.empty() && !password.empty()){
//check if freelancer exists
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatements(databaseConnection, {
ID_SELECT_CHECK_EMAIL_EXISTS,
ID_SELECT_FREELANCER_SALT,
ID_SELECT_CHECK_HASH_VALID,
ID_UPDATE_LOGIN_VALIDATION_KEY,
ID_SELECT_CHECK_LOGIN_LOCK_OUT,
ID_SELECT_GET_LOGIN_LOCK_OUT_MINUTES
});
pqxx::result checkloginLockedOut = Database::executePreparedStatement_SELECT_CHECK_LOGIN_LOCK_OUT(databaseConnection, email);
string checkloginLockedOutExtracted = "f";
if (!checkloginLockedOut.empty())
checkloginLockedOutExtracted = checkloginLockedOut.at(0).at(0).c_str();
if (checkloginLockedOutExtracted != "t") {
pqxx::result checkFreelancerExists = Database::executePreparedStatement_SELECT_CHECK_EMAIL_EXISTS(databaseConnection, email);
int checkFreelancerExistsExtracted = stoi(checkFreelancerExists.at(0).at(0).c_str());
if (checkFreelancerExistsExtracted == 1) {
pqxx::result freelancerSalt = Database::executePreparedStatement_SELECT_FREELANCER_SALT(databaseConnection, email);
string salt = freelancerSalt.at(0).at(0).c_str();
string hash = Utilities::hashPassword(salt, password);
pqxx::result checkFreelancerHash = Database::executePreparedStatement_SELECT_CHECK_HASH_VALID(databaseConnection, email, hash);
int checkFreelancerHashExtracted = stoi(checkFreelancerHash.at(0).at(0).c_str());
if (checkFreelancerHashExtracted == 1) {
//create secureCookie
auto& cookieCtx = app.get_context<crow::CookieParser>(postRequest);
std::string loginKeyValue = Utilities::generateRandomHashValueSHA512();
Database::executePreparedStatement_UPDATE_LOGIN_VALIDATION_KEY(databaseConnection, loginKeyValue, email);
std::string loginKeyCookieValue = Utilities::generateSecureCookieLoginKeyValue(loginKeyValue, stayLoggedIn);
std::string freelancerEmailCookieValue = Utilities::generateSecureCookieFreelancerEmailValue(email, stayLoggedIn);
cookieCtx.set_cookie(COOKIE_LOGIN_KEY, loginKeyCookieValue);
cookieCtx.set_cookie(COOKIE_FREELANCER_EMAIL,freelancerEmailCookieValue);
ctx[MUSTACHE_LOGIN_SUCCESS] = true;
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
}
else {
ctx[MUSTACHE_LOGIN_ERROR] = true;
ctx[MUSTACHE_LOGIN_ERROR_LOGIN_DATA_INVALID] = true;
Utilities::loginLockOutIncrement(configuration, databaseConnection, email);
}
}
else {
ctx[MUSTACHE_LOGIN_ERROR] = true;
ctx[MUSTACHE_LOGIN_ERROR_LOGIN_DATA_INVALID] = true;
}
}
else {
pqxx::result loginLockOutInMinutes = Database::executePreparedStatement_SELECT_GET_LOGIN_LOCK_OUT_MINUTES(databaseConnection, email);
ctx[MUSTACHE_LOGIN_ERROR] = true;
ctx[MUSTACHE_LOGIN_ERROR_LOCKED_OUT] = true;
ctx[MUSTACHE_LOGIN_ERROR_LOCKED_OUT_MINUTES] = loginLockOutInMinutes.at(0).at(0).c_str();
}
}
else {
ctx[MUSTACHE_LOGIN_ERROR] = true;
ctx[MUSTACHE_LOGIN_ERROR_EMAIL_PASS_NOT_FILLED] = true;
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_LOGIN_FULFILMENT);
return page.render(ctx);
});
/*
* Page for freelancer to sign up
*/
CROW_ROUTE(app, "/freelancer/signup")
([&, configuration](const crow::request& getRequest ) {
auto& ctx = app.get_context<crow::CookieParser>(getRequest);
string templateHTML = TEMPLATE_FREELANCER_SIGN_UP;
if (Utilities::checkCookieLoginState(configuration, ctx))
templateHTML = TEMPLATE_FREELANCER_REDIRECT_PROFILE;
auto page = crow::mustache::load(templateHTML);
return page.render();
});
/*
* Page for freelancer to sign up fulfillment
*/
CROW_ROUTE(app, "/freelancer/signup/fulfilment").methods("POST"_method)
([configuration](const crow::request& postRequest ) {
crow::mustache::context ctx;
string postRequestBody = postRequest.body;
Utilities::decodeString(postRequestBody);
vector<string> splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&');
string name, email, password, passwordConfirmation;
bool requestFillCompletion = false;
for (const string& item : splitPostRequestBody) {
vector<string> splitItem = Utilities::splitStringIntoVector(item, '=');
if (splitItem.at(0) == "freelancername")
name = splitItem.at(1);
if (splitItem.at(0) == "freelanceremail")
email = splitItem.at(1);
if (splitItem.at(0) == "freelancerpassword")
password = splitItem.at(1);
if (splitItem.at(0) == "freelancerpasswordconfirmation")
passwordConfirmation = splitItem.at(1);
}
//check if signup data is complete
if (!email.empty() && !name.empty() && !password.empty() && password.compare(passwordConfirmation) != 0)
requestFillCompletion = true;
if (requestFillCompletion) {
//check if email address hasn't been registered yet
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatements(databaseConnection, {
ID_SELECT_CHECK_EMAIL_EXISTS,
ID_INSERT_NEW_FREELANCER
});
pqxx::result checkResult = Database::executePreparedStatement_SELECT_CHECK_EMAIL_EXISTS(databaseConnection, email);
int checkResultExtracted = stoi(checkResult.at(0).at(0).c_str());
if (checkResultExtracted == 0) {
string pwsalt, pwhash;
pwsalt = Utilities::generateSalt();
pwhash = Utilities::hashPassword(pwsalt, password);
int errorLevel = Database::executePreparedStatement_INSERT_NEW_FREELANCER(databaseConnection, email, name, pwsalt, pwhash);
if (errorLevel == 0) {
ctx[MUSTACHE_REGISTRATION_SUCCESS] = true;
ctx["freelancername"] = name;
ctx["freelanceremail"] = email;
}
else {
ctx[MUSTACHE_REGISTRATION_ERROR] = true;
if (errorLevel == 1)
ctx[MUSTACHE_REGISTRATION_ERROR_QUERY] = true;
if (errorLevel == 2)
ctx[MUSTACHE_REGISTRATION_ERROR_CRITICAL] = true;
}
}
else {
ctx[MUSTACHE_REGISTRATION_ERROR] = true;
ctx["freelanceremail"] = email;
ctx[MUSTACHE_REGISTRATION_ERROR_EMAIL_ALREADY_IN_USE] = true;
}
}
else {
ctx[MUSTACHE_REGISTRATION_ERROR] = true;
if (password.compare(passwordConfirmation) != 0)
ctx[MUSTACHE_REGISTRATION_ERROR_PASS_CONFIRMATION] = true;
ctx[MUSTACHE_REGISTRATION_ERROR_EMAIL_NAME_PASS_NOT_FILLED] = true;
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_SIGN_UP_FULFILMENT);
return page.render(ctx);
});
/*
* Freelancer Profile page
*/
CROW_ROUTE(app, "/freelancer/profile")
([&, configuration](const crow::request& getRequest ) {
auto& cookieCtx = app.get_context<crow::CookieParser>(getRequest);
crow::mustache::context ctx;
string templateHTML = TEMPLATE_FREELANCER_PROFILE;
if (Utilities::checkCookieLoginState(configuration, cookieCtx))
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
auto page = crow::mustache::load(templateHTML);
return page.render(ctx);
});
/*
* Page for freelancer to create/select Templates
*/
CROW_ROUTE(app, "/freelancer/templateManagement")
([&, configuration](const crow::request& getRequest ) {
auto& cookieCtx = app.get_context<crow::CookieParser>(getRequest);
crow::mustache::context ctx;
if (Utilities::checkCookieLoginState(configuration, cookieCtx)) {
ctx = Utilities::getFreelancerTemplates(configuration, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL));
ctx["freelanceremail"] = cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL);
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_TEMPLATE_MANAGEMENT);
return page.render(ctx);
});
/*
* Page for freelancer to create new Template
*/
CROW_ROUTE(app, "/freelancer/templateManagement/template/new")
([&, configuration](const crow::request& getRequest ) {
auto& cookieCtx = app.get_context<crow::CookieParser>(getRequest);
crow::mustache::context ctx;
if (Utilities::checkCookieLoginState(configuration, cookieCtx)) {
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_TEMPLATE_MANAGEMENT_CREATE_NEW);
return page.render(ctx);
});
/*
* Page for freelancer to create new Template fulfilment
*/
CROW_ROUTE(app, "/freelancer/templateManagement/template/new/fulfilment").methods("POST"_method)
([&, configuration](const crow::request& postRequest ) {
auto& cookieCtx = app.get_context<crow::CookieParser>(postRequest);
crow::mustache::context ctx;
if (Utilities::checkCookieLoginState(configuration, cookieCtx)) {
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
Utilities::templateItem newTemplate;
string postRequestBody = postRequest.body;
Utilities::decodeString(postRequestBody);
newTemplate.parseRequestBodyIntoItem(postRequestBody);
newTemplate.outputItem();
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatement(databaseConnection, ID_INSERT_FREELANCER_TEMPLATE);
int errorLevel = Database::executePreparedStatement_INSERT_FREELANCER_TEMPLATE(
databaseConnection, newTemplate.name, newTemplate.content, newTemplate.contactdata, newTemplate.contactinformation,
newTemplate.currencypreference, newTemplate.priceupfront, newTemplate.priceondeliver, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL));
if (errorLevel == 0) {
ctx["templatename"] = newTemplate.name;
ctx["contactdata"] = newTemplate.contactdata;
ctx["contactinformation"] = newTemplate.contactinformation;
ctx["currencypreference"] = newTemplate.currencypreference;
ctx["priceupfront"] = newTemplate.priceupfront;
ctx["priceondeliver"] = newTemplate.priceondeliver;
ctx["content"] = newTemplate.content;
}
else {
ctx[MUSTACHE_FREELANCER_TEMPLATE_CREATION_ERROR] = true;
}
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_TEMPLATE_MANAGEMENT_CREATE_NEW_FULFILMENT);
return page.render(ctx);
});
/*
* Page for freelancer to view/delete/edit a Template
*/
CROW_ROUTE(app, "/freelancer/templateManagement/template/<string>").methods("POST"_method)
([&, configuration](const crow::request& postRequest, string templateName ) {
auto& cookieCtx = app.get_context<crow::CookieParser>(postRequest);
string postRequestBody = postRequest.body;
Utilities::decodeString(postRequestBody);
vector<string> splitRequest = Utilities::splitStringIntoVector(postRequestBody, '=');
vector<string> splitValues = Utilities::splitStringIntoVector(splitRequest.at(1), '|');
bool cookieLoginStateValid = Utilities::checkCookieLoginState(configuration, cookieCtx);
pqxx::connection databaseConnection(configuration.databaseConnectionString);
crow::json::wvalue resultJsonTemplate;
/*
* only selects a template if the login state is valid, it is possible to get the template of another user loaded since they are by design public
* but that should be a non issue since any operation that changes data will be performed with explicit ownership validation
*/
if (cookieLoginStateValid) {
Database::prepareStatement(databaseConnection, ID_SELECT_TEMPLATE);
pqxx::result resultTemplate = Database::executePreparedStatement_SELECT_TEMPLATE(databaseConnection, stoi(splitValues.at(1)));
resultJsonTemplate = Database::convertResultRowToJSON(resultTemplate);
}
crow::mustache::context ctx(resultJsonTemplate);
if (cookieLoginStateValid) {
string templateOperation = splitValues.at(0);
if (templateOperation == MUSTACHE_FREELANCER_TEMPLATE_OPERATION_VIEW) {
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_VIEW] = true;
} else if (templateOperation == MUSTACHE_FREELANCER_TEMPLATE_OPERATION_EDIT) {
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_EDIT] = true;
} else if (templateOperation == MUSTACHE_FREELANCER_TEMPLATE_OPERATION_DELETE) {
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_DELETE] = true;
} else {
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_ERROR_NO_TEMPLATE] = true;
}
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_TEMPLATE_MANAGEMENT_FULFILMENT_STAGE_CONFIRMATION);
return page.render(ctx);
});
/*
* Execute Template Operation
*/
CROW_ROUTE(app, "/freelancer/templateManagement/fulfilment/<string>").methods("POST"_method)
([&, configuration](const crow::request& postRequest, string operation ) {
auto& cookieCtx = app.get_context<crow::CookieParser>(postRequest);
crow::mustache::context ctx;
bool operationEdit = false;
bool operationDelete = false;
bool error = false;
if (operation == MUSTACHE_FREELANCER_TEMPLATE_OPERATION_EDIT) {
operationEdit = true;
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_EDIT] = true;
}
else if (operation == MUSTACHE_FREELANCER_TEMPLATE_OPERATION_DELETE) {
operationDelete = true;
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_DELETE] = true;
}
else {
error = true;
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_ERROR] = true;
}
if (!error) {
if (Utilities::checkCookieLoginState(configuration, cookieCtx)) {
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
string freelancerEmail = cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL);
string postRequestBody = postRequest.body;
Utilities::decodeString(postRequestBody);
int templateid;
vector<string> splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&');
for (const string& item : splitPostRequestBody) {
vector<string> splitItem = Utilities::splitStringIntoVector(item, '=');
if (splitItem[0] == "templateid") {
templateid = stoi(splitItem[1]);
break;
}
}
pqxx::connection databaseConnection(configuration.databaseConnectionString);
if (operationEdit) {
Utilities::templateItem toEditTemplate;
Database::prepareStatement(databaseConnection, ID_UPDATE_EDIT_FREELANCER_TEMPLATE);
toEditTemplate.parseRequestBodyIntoItem(postRequestBody);
toEditTemplate.outputItem();
bool updateSuccess = Database::executePreparedStatement_UPDATE_EDIT_FREELANCER_TEMPLATE(
databaseConnection, toEditTemplate.name, toEditTemplate.content, toEditTemplate.contactdata, toEditTemplate.contactinformation,
toEditTemplate.currencypreference, toEditTemplate.priceupfront, toEditTemplate.priceondeliver, templateid, freelancerEmail);
if (updateSuccess) {
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_COMPLETE] = true;
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_EDIT] = true;
}
else {
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_ERROR] = true;
}
} else if (operationDelete) {
Database::prepareStatement(databaseConnection, ID_DELETE_FREELANCER_TEMPLATE);
Database::executePreparedStatement_DELETE_FREELANCER_TEMPLATE(databaseConnection, templateid, freelancerEmail);
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_COMPLETE] = true;
ctx[MUSTACHE_FREELANCER_TEMPLATE_OPERATION_FULFILMENT_DELETE] = true;
}
}
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_TEMPLATE_MANAGEMENT_FULFILMENT_STAGE_EXECUTION);
return page.render(ctx);
});
/*
* Page for freelancer to create/delete Alias
*/
CROW_ROUTE(app, "/freelancer/aliasManagement")
([&, configuration](const crow::request& getRequest ) {
auto& cookieCtx = app.get_context<crow::CookieParser>(getRequest);
crow::mustache::context ctx;
if (Utilities::checkCookieLoginState(configuration, cookieCtx)) {
ctx = Utilities::getFreelancerAlias(configuration, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL));
ctx["freelanceremail"] = cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL);
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_ALIAS_MANAGEMENT);
return page.render(ctx);
});
/*
* Execute Alias deletion
*/
CROW_ROUTE(app, "/freelancer/aliasManagement/delete").methods("POST"_method)
([&, configuration](const crow::request& postRequest) {
auto& cookieCtx = app.get_context<crow::CookieParser>(postRequest);
crow::mustache::context ctx;
if (Utilities::checkCookieLoginState(configuration, cookieCtx)) {
string postRequestBody = postRequest.body;
Utilities::decodeString(postRequestBody);
string aliasName;
vector<string> splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&');
for (const string& item : splitPostRequestBody) {
vector<string> splitItem = Utilities::splitStringIntoVector(item, '=');
if (splitItem.at(0) == "alias")
aliasName = splitItem.at(1);
}
Utilities::deleteFreelancerAlias(configuration, aliasName, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL));
ctx["freelanceremail"] = cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL);
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_ALIAS_MANAGEMENT_DELETE);
return page.render(ctx);
});
/*
* Page for freelancer to create alias
*/
CROW_ROUTE(app, "/freelancer/aliasManagement/new")
([&, configuration](const crow::request& getRequest ) {
auto& cookieCtx = app.get_context<crow::CookieParser>(getRequest);
crow::mustache::context ctx;
if (Utilities::checkCookieLoginState(configuration, cookieCtx)) {
ctx = Utilities::getFreelancerTemplates(configuration, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL));
ctx["freelanceremail"] = cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL);
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_ALIAS_MANAGEMENT_NEW);
return page.render(ctx);
});
/*
* Execute alias creation
*/
CROW_ROUTE(app, "/freelancer/aliasManagement/new/fulfilment").methods("POST"_method)
([&, configuration](const crow::request& postRequest) {
auto& cookieCtx = app.get_context<crow::CookieParser>(postRequest);
crow::mustache::context ctx;
if (Utilities::checkCookieLoginState(configuration, cookieCtx)) {
bool error = false;
ctx[MUSTACHE_COOKIE_LOGGED_IN] = true;
string postRequestBody = postRequest.body;
Utilities::decodeString(postRequestBody);
vector<string> splitPostRequestBody = Utilities::splitStringIntoVector(postRequestBody, '&');
string aliasname, templateNameID, templateName;
for (const string& item : splitPostRequestBody) {
vector<string> splitItem = Utilities::splitStringIntoVector(item, '=');
if (splitItem.at(0) == "freelanceralias")
aliasname = splitItem.at(1);
if (splitItem.at(0) == "aliasroute")
templateNameID = splitItem.at(1);
}
pqxx::connection databaseConnection(configuration.databaseConnectionString);
Database::prepareStatements(databaseConnection, {
ID_SELECT_CHECK_FREELANCER_ALIAS,
ID_INSERT_FREELANCER_ALIAS,
ID_SELECT_FREELANCER_ID,
ID_SELECT_FREELANCER_NAME
});
bool aliasAlreadyUsed = false;
if (aliasname.empty()) {
error = true;
ctx[MUSTACHE_FREELANCER_ALIAS_CREATION_ERROR] = true;
ctx[MUSTACHE_FREELANCER_ALIAS_CREATION_ERROR_UNNAMED] = true;
} else {
ctx["aliasname"] = aliasname;
aliasAlreadyUsed = Database::executePreparedStatement_SELECT_CHECK_FREELANCER_ALIAS(databaseConnection, aliasname);
}
if (aliasAlreadyUsed) {
error = true;
ctx[MUSTACHE_FREELANCER_ALIAS_CREATION_ERROR] = true;
ctx[MUSTACHE_FREELANCER_ALIAS_CREATION_ERROR_DUPLICATE] = true;
}
if (!error) {
string route, routeparameter, routevalue;
pqxx::result freelancerNameResult = Database::executePreparedStatement_SELECT_FREELANCER_NAME(databaseConnection, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL));
if (templateNameID.empty()) {
routeparameter = "freelancerID";
pqxx::result freelancerIDResult = Database::executePreparedStatement_SELECT_FREELANCER_ID(databaseConnection, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL));
routevalue = freelancerIDResult.at(0).at(0).c_str();
route = Utilities::generateAliasRouteFreelancer(freelancerNameResult.at(0).at(0).c_str());
} else {
routeparameter = "templateID";
vector<string> splitValues = Utilities::splitStringIntoVector(templateNameID, '|');
routevalue = splitValues.at(1);
route = Utilities::generateAliasRouteFreelancerTemplate(freelancerNameResult.at(0).at(0).c_str(), splitValues.at(0));
}
int errorLevel = Database::executePreparedStatement_INSERT_FREELANCER_ALIAS(databaseConnection, aliasname, cookieCtx.get_cookie(COOKIE_FREELANCER_EMAIL), route, routeparameter, routevalue);
ctx["aliasname"] = aliasname;
if (errorLevel != 0)
ctx[MUSTACHE_FREELANCER_ALIAS_CREATION_ERROR] = true;
}
}
auto page = crow::mustache::load(TEMPLATE_FREELANCER_ALIAS_MANAGEMENT_NEW_FULFILMENT);
return page.render(ctx);
});
/*CROW_ROUTE(app, "/getEntry/<int>")
([databaseURI](int entry) {
pqxx::connection databaseConnection(databaseURI);
pqxx::result result = Database::executePreparedStatement_SELECT_ITEM_BY_ID(databaseConnection, entry);
auto page = crow::mustache::load("test.html");
crow::json::wvalue resultJson = Database::convertResultRowToJSON(result);
crow::mustache::context ctx(resultJson);
return page.render(ctx);
});
CROW_ROUTE(app, "/testRequest")
([databaseURI]() {
Database::requestsItem item;
item.customerEmailAddress = "test@testmail.com";
item.freelancer = "michael";
item.templateName = "coding";
item.currencyPreference = "XMR";
item.priceUpFront = "0.36";
item.priceOnDeliver = "0.36";
item.requestDescription = "This is a test";
item.accepted = stoi("0");
item.upFrontInvoiceID = "12424242424";
item.onDeliverInvoiceID = "329532532532";
item.upFrontPaid = stoi("0");
item.onDeliverPaid = stoi("0");
pqxx::connection databaseConnection(databaseURI);
Database::executePreparedStatement_INSERT_ITEM_IN_REQUESTS(databaseConnection, item);
return crow::response(200);
});
CROW_ROUTE(app, "/newRequest")
.methods("POST"_method)
([databaseURI](const crow::request &req) {
auto jsonBody = crow::json::load(req.body);
if (!jsonBody)
return crow::response(crow::status::BAD_REQUEST);
* Example CURL to insert into DB:
* Price must be delivered as a string to avoid unnecessary conversion to double which would lead to the addition to a database of the value 0.359999999999999999999999999 instead of 0.36.
* curl -H "Content-Type: application/json" -H "Accept: application/json" -d '{"customerEmailAddress":"2@testmail.com","freelancer":"nick","templateName":"coding","currencyPreference":"XMR","priceUpFront":"0.36","priceOnDeliver":"0.36","requestDescription":"This is a test","accepted":false,"upFrontInvoiceID":"12424242424","onDeliverInvoiceID":"329532532532","upFrontPaid":false,"onDeliverPaid":false}' http://0.0.0.0:18080/newRequest
Database::requestsItem item;
item.insertJsonIntoItem(jsonBody);
pqxx::connection databaseConnection(databaseURI);
Database::executePreparedStatement_INSERT_ITEM_IN_REQUESTS(databaseConnection, item);
return crow::response(200);
});
*/
//set the port, set the app to run on multiple threads, and run the app
app.ssl_file(configuration.sslCrtPath, configuration.sslKeyPath);
app.port(18080).multithreaded().run();
}