diff --git a/.gitignore b/.gitignore
index a4fb4fb..70bcea8 100644
--- a/.gitignore
+++ b/.gitignore
build/
.cache/
+sessions/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f9f8f46..3fffb07 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
include(${CMAKE_SOURCE_DIR}/cmake/CPM.cmake)
CPMAddPackage(Crow
- VERSION 1.2.1
+ VERSION 1.3.0
GITHUB_REPOSITORY CrowCpp/Crow
OPTIONS
"CROW_BUILD_EXAMPLES Off"
diff --git a/src/main.cpp b/src/main.cpp
index ddd3ac1..6f0af7c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
#include "crow/http_response.h"
#include "crow/middlewares/cookie_parser.h"
#include "crow/middlewares/session.h"
+#include "crow/multipart_view.h"
#include "crow/mustache.h"
#include <pqxx/pqxx>
#include <vector>
+#include <fstream>
+#include <cstdlib>
+#include <filesystem>
+#include <time.h>
+
+namespace utils
+{
+ // Katso onko kirjautumistiedot oikein
+ bool logged_in(const std::string& name, const std::string& password) {
+ try
+ {
+ pqxx::connection cx("dbname = postgres user = postgres password = 1234 \
+ hostaddr = 127.0.0.1 port = 5432");
+ if (not cx.is_open())
+ {
+ CROW_LOG_CRITICAL << "Can't open database";
+ return false;
+ }
+ // Connection established
+ CROW_LOG_INFO << "Opened database successfully: " << cx.dbname();
+ // Etsi kayttajan tiedot
+ pqxx::work tx(cx);
+ pqxx::result r = tx.exec(
+ "SELECT * FROM kayttajat_table "
+ "WHERE nimi='" + tx.esc(name) + "'"
+ ); // pqxx:params too difficult
+ tx.commit();
+ if (r.size() != 1) {
+ CROW_LOG_ERROR << "Unexpected SQL query results";
+ return false;
+ }
+ // Tarkista tiedot
+ return r[0][2].as<std::string>() == password;
+ }
+ catch (const std::exception &e)
+ {
+ CROW_LOG_ERROR << e.what();
+ return false;
+ }
+ return true;
+ }
+
+ // Generoi satunnainen tiedosto nimi
+ std::string fileName(const std::string& fileExtension) {
+ const char allowed[] = "abcedfghijklmnopqrstuvwxyz1234567890";
+ srand(time(NULL));
+ std::string r;
+ for (int i = 0; i < 20; ++i) {
+ r += allowed[rand() % (sizeof(allowed) / sizeof(*allowed) )];
+ }
+ r += fileExtension;
+ return r;
+ }
+}
struct Asunto {
std::string osoite;
std::string kuvaPolku;
};
-bool logged_in(const std::string& name, const std::string& password) {
- try
- {
- pqxx::connection cx("dbname = postgres user = postgres password = 1234 \
- hostaddr = 127.0.0.1 port = 5432");
- if (not cx.is_open())
- {
- CROW_LOG_CRITICAL << "Can't open database";
- return false;
- }
- // Connection established
- CROW_LOG_INFO << "Opened database successfully: " << cx.dbname();
- // Etsi kayttajan tiedot
- CROW_LOG_INFO << name;
- pqxx::work tx(cx);
- pqxx::row r = tx.exec(
- "SELECT * FROM kayttajat_table WHERE nimi='$1'",
- pqxx::params{name}
- ).one_row();
- tx.commit();
- // Tarkista tiedot
- return r[2].as<std::string>() == password;
- }
- catch (const std::exception &e)
- {
- CROW_LOG_ERROR << e.what();
- return false;
- }
- return true;
-}
-
int main()
{
// Typedef
- using Session = crow::SessionMiddleware<crow::InMemoryStore>;
+ using Session = crow::SessionMiddleware<crow::FileStore>;
// Crow App
- crow::App<crow::CookieParser, Session> app{Session{
- crow::CookieParser::Cookie("session").max_age(24 * 60 * 60).path("/"),
- AUTA MUA,
- crow::InMemoryStore{}}};
+ crow::App<crow::CookieParser, Session> app{Session{
+ crow::FileStore{"./sessions"}}};
// Asunnot lista
std::vector<Asunto> asunnot;
- CROW_ROUTE(app, "/")([&asunnot](){
+ CROW_ROUTE(app, "/")([&asunnot](){
auto page = crow::mustache::load("index.html");
crow::json::wvalue asunnotJson;
for (int i = 0; i < asunnot.size(); ++i) {
asunnotJson[i]["kuva"] = asunnot.at(i).kuvaPolku;
}
crow::mustache::context ctx({{"asunnot", asunnotJson}});
- return page.render(ctx);
- });
+ return page.render(ctx);
+ });
CROW_ROUTE(app, "/login")
.methods("GET"_method, "POST"_method)([&app](const crow::request& req, crow::response& res){
// Hanki params
std::string reqName = req.get_body_params().get("name");
std::string reqPass = req.get_body_params().get("pass");
- // Aseta session muuttujat
- session.set("name", reqName);
- session.set("pass", reqPass);
- // Kun POST request ohi, redirect admin sivulle
-
+ // Tarkista kirjautuminen
+ if (utils::logged_in(reqName, reqPass)) {
+ CROW_LOG_INFO << "Account: " << reqName << " logged in";
+ // Aseta session muuttujat
+ session.set("name", reqName);
+ // Kun POST request ohi, redirect admin sivulle
+ res.redirect("/admin");
+ res.end();
+ }
+ else { // Kirjautuminen ei onnistunut
+ CROW_LOG_INFO << "Account " << reqName << " failed login";
+ }
}
crow::mustache::context ctx;
auto page = crow::mustache::load("login.html");
- if (session.contains("name") and session.contains("pass")) { // Näytä kirjautumistiedot jos kirjautunut
+ if (session.contains("name")) { // Näytä kirjautumistiedot jos kirjautunut
ctx["name"] = session.get("name", "_HELP_");
- ctx["pass"] = session.get("pass", "_HELP_");
}
res.body = page.render(ctx).body_;
res.end();
.methods("GET"_method, "POST"_method)([&app](const crow::request& req, crow::response& res){
auto& session = app.get_context<Session>(req);
- if (not logged_in(session.get("name","_NO_"), session.get("pass","_NO_"))) {
- // Jos ei kirjauduttu sisään
+ if (session.get("name","_NO_") == "_NO_") {
+ // Jos ei kirjauduttu sisään, potki pois
res.redirect("/");
res.end();
}
-
- if (req.method == "POST"_method) { // Jos POST request
-
- }
-
+ // Jatka jos kirjautunut sisään
+ // Renderöi sivu
auto page = crow::mustache::load("admin.html");
res.body = page.render().body_;
res.end();
});
+ CROW_ROUTE(app, "/asunto")
+ .methods("POST"_method)([&app, &asunnot](const crow::request& req){
+ auto& session = app.get_context<Session>(req);
+ if (session.get("name","_NO_") == "_NO_") {
+ // Jos ei kirjauduttu sisään, potki pois
+ return crow::response(401);
+ }
+ // Jatka jos kirjautunut sisään
+ std::string asuntoOsoite;
+ std::string asuntoKuva;
+ // Uusi asuntola
+ crow::multipart::message_view file_message(req);
+ // File upload
+ for (const auto& part : file_message.part_map) {
+ const auto& part_name = part.first;
+ const auto& part_value = part.second;
+ CROW_LOG_DEBUG << "Part: " << part_name;
+ // Jos teksti
+ if (part_name != "image") {
+ CROW_LOG_DEBUG << "Value: " << part_value.body; // debug
+ asuntoOsoite = part_value.body;
+ continue;
+ }
+ // Jos kuva
+
+ // Extract the file name
+ auto headers_it = part_value.headers.find("Content-Disposition");
+ if (headers_it == part_value.headers.end()) {
+ CROW_LOG_ERROR << "No Content-Disposition found";
+ return crow::response(400);
+ }
+ auto params_it = headers_it->second.params.find("filename");
+ if (params_it == headers_it->second.params.end()) {
+ CROW_LOG_ERROR << "Part with name \"image\" should have a file";
+ return crow::response(400);
+ }
+ const std::filesystem::path reqFile = params_it->second;
+ const std::string outfileName = utils::fileName(reqFile.extension());
+ const std::filesystem::path outfilePath = "static/images/" + outfileName;
+ // Debug info
+ for (const auto& part_header : part_value.headers) {
+ const auto& part_header_name = part_header.first;
+ const auto& part_header_val = part_header.second;
+ CROW_LOG_DEBUG << "Header: " << part_header_name << '=' << part_header_val.value;
+
+ for (const auto& param : part_header_val.params)
+ {
+ const auto& param_key = param.first;
+ const auto& param_val = param.second;
+ CROW_LOG_DEBUG << " Param: " << param_key << ',' << param_val;
+ }
+ }
+ // Write file to hard drive
+ std::ofstream file(outfilePath);
+ if (file.fail()) {
+ CROW_LOG_ERROR << "Cannot write file";
+ return crow::response(500);
+ }
+ file << part_value.body;
+ file.close();
+ CROW_LOG_INFO << "File written to " << outfilePath;
+ // Tietokantaa varten
+ asuntoKuva = outfileName;
+ }
+ // Lisää SQL tietokantaan
+ try
+ {
+ pqxx::connection cx("dbname = postgres user = postgres password = 1234 \
+ hostaddr = 127.0.0.1 port = 5432");
+ if (not cx.is_open())
+ {
+ CROW_LOG_CRITICAL << "Can't open database";
+ return crow::response(500);
+ }
+ // Connection established
+ CROW_LOG_INFO << "Opened database successfully: " << cx.dbname();
+ // Do work
+ pqxx::work tx(cx);
+
+ pqxx::result r = tx.exec(
+ "INSERT INTO public.asuntolat_table (osoite,kuva)"
+ "VALUES ('" + tx.esc(asuntoOsoite) + "','" + tx.esc(asuntoKuva) + "');");
+ CROW_LOG_INFO << "Record added to database";
+ // Valmista ollaan, nyt vaan päivitetään ramissa olevaa listaa
+ const struct Asunto asunto(asuntoOsoite, asuntoKuva);
+ asunnot.push_back(asunto);
+ tx.commit();
+ }
+ catch (const std::exception &e)
+ {
+ CROW_LOG_ERROR << e.what();
+ return crow::response(500);
+ }
+ // Valmis
+ return crow::response(200);
+ });
+
+
try
- {
+ {
pqxx::connection cx("dbname = postgres user = postgres password = 1234 \
- hostaddr = 127.0.0.1 port = 5432");
- if (not cx.is_open())
- {
+ hostaddr = 127.0.0.1 port = 5432");
+ if (not cx.is_open())
+ {
CROW_LOG_CRITICAL << "Can't open database";
- return EXIT_FAILURE;
- }
+ return EXIT_FAILURE;
+ }
// Connection established
CROW_LOG_INFO << "Opened database successfully: " << cx.dbname();
// Do work
//CROW_LOG_INFO << asunto.osoite << ":" << asunto.kuvaPolku << "\n";
asunnot.push_back( asunto );
}
- }
- catch (const std::exception &e)
- {
+ }
+ catch (const std::exception &e)
+ {
CROW_LOG_ERROR << e.what();
- return EXIT_FAILURE;
- }
+ return EXIT_FAILURE;
+ }
- // :D
- app.port(18080).multithreaded().run();
+ // :D
+ //app.loglevel(crow::LogLevel::Debug);
+ app.port(18080).multithreaded().run();
+ return EXIT_SUCCESS;
}
-
diff --git a/templates/admin.html b/templates/admin.html
index 015b650..a593169 100644
--- a/templates/admin.html
+++ b/templates/admin.html
<body>
<div>
Tietoa meistä:
- <form action="/admin" method="POST" accept-charset="utf-8">
+ <form action="/admin" method="post" accept-charset="utf-8">
<textarea name="info" id="info">{{ info }}</textarea>
<input type="submit" value="Muokkaa" />
</form>
</div>
<div>
Lisää asuntola
- <form action="/admin" method="POST" accept-charset="utf-8">
- <label for="name">Asunnon nimi</label> <br>
+ <form action="/asunto" method="post" accept-charset="utf-8" enctype="multipart/form-data">
+ <label for="name">Osoite</label> <br>
<input type="text" name="name" id="name" value=""> <br>
<label for="image">Kuva asunnosta</label> <br>
<input type="file" name="image" id="image" value=""> <br>
diff --git a/templates/login.html b/templates/login.html
index 03de722..b8f081a 100644
--- a/templates/login.html
+++ b/templates/login.html
<title></title>
</head>
<body>
- {{ #name }}
- <p>Kirjauduttu sisään, tervetuloa {{ name }}!</p>
- {{ /name }}
- <form action="/login" method="POST" accept-charset="utf-8">
+ {{#name}}
+ <p>Kirjauduttu sisään, tervetuloa {{name}}!</p>
+ {{/name}}
+ <form action="/login" method="post" accept-charset="utf-8">
<label for="name">Nimi</label><br>
<input type="text" name="name" id="name" value=""><br>
<label for="pass">Salasana</label><br>