Diff between a6da4f5d08ca752d6d3c2a1a25d84a819b60f2c7 and 01a91bb4e54f40c96af293066b99c3a4a99c1410

Changed Files

File Additions Deletions Status
.gitignore +1 -0 modified
CMakeLists.txt +1 -1 modified
src/main.cpp +191 -67 modified
templates/admin.html +3 -3 modified
templates/login.html +4 -4 modified

Full Patch

diff --git a/.gitignore b/.gitignore
index a4fb4fb..70bcea8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 build/
 .cache/
+sessions/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f9f8f46..3fffb07 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,7 +7,7 @@ find_package(ZLIB REQUIRED)
 
 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
@@ -2,60 +2,82 @@
 #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) {
@@ -63,8 +85,8 @@ int main()
 			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){
@@ -79,17 +101,23 @@ int main()
 			// 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();
@@ -99,30 +127,125 @@ int main()
 		.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
@@ -139,14 +262,15 @@ int main()
 			//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
@@ -8,15 +8,15 @@
 	<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
@@ -6,10 +6,10 @@
 		<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>