login now works and files are protected

This commit is contained in:
Lukas Forsberg 2026-02-02 22:06:37 +01:00
parent 79b5737bcb
commit fbb54b461e
9 changed files with 149 additions and 105 deletions

View File

@ -2,59 +2,57 @@
<script lang="ts">
import { API_BASE } from '$lib/config';
import { goto } from '$app/navigation';
let user : any = '';
let password : any = '';
let loading : any = false;
let error : any = '';
let error = "";
let Credentials = {
username: "",
password: "",
}
async function handleLogin() {
let error : any = '';
let loading : Boolean = true;
try {
const res = await fetch(`${API_BASE}/api/shadowrun/characters`, {
const res = await fetch(`${API_BASE}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: user,
password: password,
})
credentials: 'include',
body: JSON.stringify(
Credentials )
});
if (res.ok) {
characters = await res.json();
if (!res.ok) {
error = await res.text();
return;
}
alert('Logged in!');
goto("/shadowrun")
} catch (e) {
error = e.message;
} finally {
loading = false;
}
}
</script>
<form class="login" on:submit|preventDefault={handleLogin}>
<form class="login">
<h2>Login</h2>
<label>
User
<input type="text" bind:value={user} required />
<input type="text" bind:value={Credentials.username} required />
</label>
<label>
Password
<input type="password" bind:value={password} required />
<input type="password" bind:value={Credentials.password} required />
</label>
<button on:click={handleLogin}>
Login
</button>
{#if error}
<p class="error">{error}</p>
{/if}
<button disabled={loading}>
{loading ? 'Logging in…' : 'Login'}
</button>
</form>

View File

@ -29,20 +29,21 @@ bool isLoggedIn(const crow::request& req) {
std::optional<std::string> loginUser(const std::string& username, const std::string& password)
{
auto user = getVerifiedUser(username, password);
if (user.has_value()) {
return sessionHandler.createSession(user.value().id);
if (user) {
return sessionHandler.createSession(user->id);
}
return {};
}
void initLogin(crow::SimpleApp& app)
{
// createUser("lukas", "Trollar4928");
//createUser("lukas", "Trollar4928");
CROW_ROUTE(app, "/login").methods("POST"_method)
([](const crow::request& req) {
nlohmann::json body = nlohmann::json::parse(req.body); // parse JSON from HTTP body
if (!body.empty())
if (body.empty())
return crow::response(400, "Invalid JSON");
auto usenameIt = body.find("username");

View File

@ -11,15 +11,27 @@ void initLogin(crow::SimpleApp& app);
bool isLoggedIn(const crow::request& req);
// lambda to be used by endpoint that requiere login
// login_required lambda that works for any handler with arbitrary args
inline auto login_required = [](auto handler){
return [handler](const crow::request& req){
return [handler](auto&&... args) -> crow::response {
// the first argument is always crow::request
const crow::request& req = std::get<0>(std::forward_as_tuple(args...));
if (!isLoggedIn(req)) {
return crow::response(401, "Login required");
return crow::response(401, "Login required");
}
// call original handler with all arguments
auto result = handler(std::forward<decltype(args)>(args)...);
// ensure crow::response return type
if constexpr (std::is_same_v<decltype(result), crow::response>) {
return result;
} else {
return crow::response(result);
}
return handler(req);
};
};
}
#endif // __LOGIN_H__

View File

@ -1,8 +1,11 @@
#include "loginDb.hpp"
#include "databasepool.h"
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <iostream>
extern "C" {
#include "monocypher.h"
}
@ -59,8 +62,14 @@ int createUser(const std::string& username, const std::string& password){
int64_t id;
auto db = dbpool.acquire();
auto user = db->get_optional<User>(
where(c(&User::username) == username)
for (auto &u : db->get_all<login::User>()) {
if (u.username == username){
std::cout << "WTF" << std::endl;
};
}
auto user = db->get_optional<login::User>(
where(c(&login::User::username) == username)
);
if (user.has_value()) {
@ -76,11 +85,16 @@ int createUser(const std::string& username, const std::string& password){
std::optional<User> getUser(const std::string& username){
auto db = dbpool.acquire();
auto user = db->get_optional<User>(
where(c(&User::username) == username)
auto user = db->get_all<login::User>(
where(c(&login::User::username) == username)
);
dbpool.release(db);
return user;
if(user.size() > 0){
return user[0];
}
return {};
}
std::optional<User> getVerifiedUser(const std::string& username, const std::string& password){
@ -88,7 +102,7 @@ std::optional<User> getVerifiedUser(const std::string& username, const std::stri
if (!user.has_value())
return {};
if (verifyUser(user.value(), password)){
if (verifyUser(*user, password)){
return user;
}
return {};

View File

@ -3,7 +3,6 @@
#include <string>
#include <vector>
#include <optional>
#include "json.hpp"
#include "utils.hpp"
namespace login

View File

@ -7,38 +7,12 @@
using namespace std;
std::string read_file(const std::string& path) {
std::ifstream file(path, std::ios::binary);
if (!file) {
throw std::runtime_error("Cannot open file: " + path);
}
std::ostringstream ss;
ss << file.rdbuf();
return ss.str();
}
// Simple MIME type detection
std::string get_mime_type(const std::string& path) {
if (path.ends_with(".html")) return "text/html";
if (path.ends_with(".js")) return "text/javascript";
if (path.ends_with(".css")) return "text/css";
if (path.ends_with(".json")) return "application/json";
if (path.ends_with(".svg")) return "image/svg+xml";
if (path.ends_with(".png")) return "image/png";
if (path.ends_with(".jpg") || path.ends_with(".jpeg")) return "image/jpeg";
if (path.ends_with(".woff")) return "font/woff";
if (path.ends_with(".woff2")) return "font/woff2";
if (path.ends_with(".pdf")) return "application/pdf";
return "application/octet-stream";
}
int main() {
crow::SimpleApp app;
const filesystem::path build_dir = "frontend/build/"; // <-- set your build folder
// Root route
CROW_ROUTE(app, "/")([&]() {
auto data = read_file(build_dir / "index.html");
auto data = utils::read_file(utils::build_dir / "index.html");
return crow::response(200, "text/html", data);
});
@ -63,40 +37,19 @@ int main() {
([&](const std::string& p) {
const filesystem::path assets_dir = "assets/"; // <-- set your build folder
filesystem::path file_path = assets_dir / p;
// If file exists, serve it
if (filesystem::exists(file_path)) {
auto data = read_file(file_path);
auto mime = get_mime_type(file_path.string());
return crow::response(200, mime.c_str(), data);
}
// SPA fallback: serve root index.html for unknown routes
filesystem::path fallback = build_dir / "index.html";
auto data = read_file(fallback);
return crow::response(404, "text/html", data);
return utils::getFile(file_path);
});
// Catch-all route for static files and SPA fallback
CROW_ROUTE(app, "/<path>")
([&](const std::string& p) {
filesystem::path file_path = build_dir / p;
filesystem::path file_path = utils::build_dir / p;
// If path is a directory, serve index.html inside it
if (filesystem::is_directory(file_path))
file_path /= "index.html";
// If file exists, serve it
if (filesystem::exists(file_path)) {
auto data = read_file(file_path);
auto mime = get_mime_type(file_path.string());
return crow::response(200, mime.c_str(), data);
}
// SPA fallback: serve root index.html for unknown routes
filesystem::path fallback = build_dir / "index.html";
auto data = read_file(fallback);
return crow::response(404, "text/html", data);
return utils::getFile(file_path);
});
app.loglevel(crow::LogLevel::INFO);

View File

@ -26,16 +26,26 @@ static std::unordered_map<std::string, std::string> parse_query_string(const std
return params;
}
static crow::response rsp(const std::string& msg){
auto str = format("<div class='alert alert-success'>"
"{} </div>", msg);
return crow::response{str};
}
void initApi(crow::SimpleApp& app){
CROW_ROUTE(app, "/assets/shadowrun/<path>")
([&](const crow::request& req, const std::string& p) {
if (!login::isLoggedIn(req)) {
return crow::response(401, "Login required");
}
const filesystem::path assets_dir = "assets/shadowrun/";
filesystem::path file_path = assets_dir / p;
return utils::getFile(file_path);
});
void initApi(crow::SimpleApp& app)
{
CROW_ROUTE(app, "/api/shadowrun/characters")
([&]() {
([&](const crow::request& req) {
if (!login::isLoggedIn(req)) {
return crow::response(401, "Login required");
}
auto characters = getCharacters();
auto res =
crow::response(200, utils::toJsonArray(characters));
@ -45,6 +55,10 @@ void initApi(crow::SimpleApp& app)
CROW_ROUTE(app, "/api/shadowrun/characters").methods("POST"_method)
([](const crow::request& req) {
if (!login::isLoggedIn(req)) {
return crow::response(401, "Login required");
}
nlohmann::json data = nlohmann::json::parse(req.body); // parse JSON from HTTP body
auto name = data["name"];
int id = createCharacter(name);
@ -60,7 +74,10 @@ void initApi(crow::SimpleApp& app)
});
CROW_ROUTE(app, "/api/shadowrun/characters/<int>")
([&](int id) {
([&](const crow::request& req, int id) {
if (!login::isLoggedIn(req)) {
return crow::response(401, "Login required");
}
auto optCharacter = getChracter(id);
if (!optCharacter.has_value())
return crow::response(404, "Character not found");
@ -71,7 +88,10 @@ void initApi(crow::SimpleApp& app)
});
CROW_ROUTE(app, "/api/shadowrun/characters_data/<int>")
([&](int id) {
([&](const crow::request& req, int id) {
if (!login::isLoggedIn(req)) {
return crow::response(401, "Login required");
}
nlohmann::json j;
const auto characterData = getChracterData(id);
@ -99,6 +119,9 @@ void initApi(crow::SimpleApp& app)
CROW_ROUTE(app, "/api/shadowrun/characters_data/<int>").methods("POST"_method)
([&](const crow::request& req, int id) {
if (!login::isLoggedIn(req)) {
return crow::response(401, "Login required");
}
nlohmann::json j = nlohmann::json::parse(req.body);
for (auto type : magic_enum::enum_values<Type>()) {
@ -110,7 +133,6 @@ void initApi(crow::SimpleApp& app)
auto res = crow::response(200, "Saved Character data");
return res;
//return crow::response(405, ret.error());
});
}

View File

@ -6,6 +6,7 @@
#include <netinet/in.h>
#include <netdb.h>
#include "utils.hpp"
#include "crow/http_response.h"
#include <algorithm>
#include <fstream>
#include <sstream>
@ -141,6 +142,45 @@ string currentTime(){
return ss.str();
}
std::string read_file(const std::string& path) {
std::ifstream file(path, std::ios::binary);
if (!file) {
throw std::runtime_error("Cannot open file: " + path);
}
std::ostringstream ss;
ss << file.rdbuf();
return ss.str();
}
// Simple MIME type detection
std::string get_mime_type(const std::string& path) {
if (path.ends_with(".html")) return "text/html";
if (path.ends_with(".js")) return "text/javascript";
if (path.ends_with(".css")) return "text/css";
if (path.ends_with(".json")) return "application/json";
if (path.ends_with(".svg")) return "image/svg+xml";
if (path.ends_with(".png")) return "image/png";
if (path.ends_with(".jpg") || path.ends_with(".jpeg")) return "image/jpeg";
if (path.ends_with(".woff")) return "font/woff";
if (path.ends_with(".woff2")) return "font/woff2";
if (path.ends_with(".pdf")) return "application/pdf";
return "application/octet-stream";
}
crow::response getFile(const filesystem::path& file_path){
// If file exists, serve it
if (filesystem::exists(file_path)) {
auto data = read_file(file_path);
auto mime = get_mime_type(file_path.string());
return crow::response(200, mime.c_str(), data);
}
// SPA fallback: serve root index.html for unknown routes
filesystem::path fallback = build_dir / "index.html";
auto data = read_file(fallback);
return crow::response(404, "text/html", data);
}
std::expected<nlohmann::json, std::string> parseJson(const std::string& input) {
try {
return nlohmann::json::parse(input);

View File

@ -5,10 +5,13 @@
#include <string>
#include <cstdint>
#include <filesystem>
#include <map>
#include <expected>
#include "json.hpp"
#include "crow.h"
namespace utils {
const std::filesystem::path build_dir = "frontend/build/"; // <-- set your build folder
std::map<std::string, std::string> parseBody(const std::string& body);
std::optional<std::string> getBodyName(const std::string& body);
@ -45,8 +48,10 @@ namespace utils {
catch (const nlohmann::json::exception& e) {
return std::unexpected(e.what());
}
}
std::string read_file(const std::string& path);
crow::response getFile(const std::filesystem::path& file_path);
}
#endif