Backend: Sanitarized json parser (quotes, \n, \r...) Added a get_post route. Frontend: Basic article management.

This commit is contained in:
Yohan Boujon 2024-03-15 21:55:03 +01:00
parent 36af469f8f
commit 42c487006c
9 changed files with 89 additions and 12 deletions

View file

@ -22,6 +22,8 @@ public:
private: private:
Data _dataBuffer; Data _dataBuffer;
Json _buffer; Json _buffer;
void sanitarize(std::string& str);
}; };
} }

View file

@ -0,0 +1,16 @@
#ifndef _HEADER_ETHERYOBLOG_UTILITY
#define _HEADER_ETHERYOBLOG_UTILITY
#include <string>
inline void str_replace(std::string& str, const std::string& from, const std::string& to) {
if(from.empty())
return;
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
}
#endif //_HEADER_ETHERYOBLOG_UTILITY

View file

@ -1,4 +1,7 @@
#include "json.hpp" #include "json.hpp"
#include "utility.hpp"
#include <algorithm>
#include <iostream>
using namespace Etheryo; using namespace Etheryo;
@ -34,12 +37,13 @@ std::string JsonHandler::to_str()
std::string str = "["; std::string str = "[";
size_t map_size(0), map_index(0), vec_index(0); size_t map_size(0), map_index(0), vec_index(0);
for (const auto& map_data : _buffer) { for (auto& map_data : _buffer) {
vec_index++; vec_index++;
str.push_back('{'); str.push_back('{');
map_size = map_data.size(); map_size = map_data.size();
for (auto it = map_data.begin(); it != map_data.end(); it++) { for (auto it = map_data.begin(); it != map_data.end(); it++) {
map_index++; map_index++;
sanitarize(it->second);
str += ("\"" + it->first + "\": \"" + it->second + (map_index >= map_size ? "\"" : "\",")); str += ("\"" + it->first + "\": \"" + it->second + (map_index >= map_size ? "\"" : "\","));
} }
map_index = 0; map_index = 0;
@ -49,4 +53,11 @@ std::string JsonHandler::to_str()
} }
str.push_back(']'); str.push_back(']');
return str; return str;
}
void JsonHandler::sanitarize(std::string& str)
{
str_replace(str, "\"", "\\\"");
str_replace(str, "\r", "\\r");
str_replace(str, "\n", "\\n");
} }

View file

@ -3,6 +3,7 @@
#include "crow.h" #include "crow.h"
#include <pqxx/pqxx> #include <pqxx/pqxx>
#include "crow/app.h"
#include "crow/http_response.h" #include "crow/http_response.h"
#include "dotenv.hpp" #include "dotenv.hpp"
#include "json.hpp" #include "json.hpp"
@ -10,16 +11,19 @@
pqxx::connection* globalConnection(nullptr); pqxx::connection* globalConnection(nullptr);
crow::response test(void); crow::response test(void);
crow::response getSimplePosts(int limit); crow::response getSimplePosts(int limit);
crow::response getPost(std::string slug);
// Json Handler with a precise data type // Json Handler with a precise data type
Etheryo::JsonHandler category; Etheryo::JsonHandler category;
Etheryo::JsonHandler post_info; Etheryo::JsonHandler post_info;
Etheryo::JsonHandler post;
int main() int main()
{ {
// Init Json Objects // Init Json Objects
category.add({ "id", "slug", "icon", "type_icon" }); category.add({ "id", "slug", "icon", "type_icon" });
post_info.add({"slug","author","title", "date", "picture"}); post_info.add({"slug","author","title", "date", "picture"});
post.add({"name","title","date","picture","body","views","likes","dislikes"});
// Init Postgresql + DotEnv // Init Postgresql + DotEnv
Etheryo::DotEnv dotenvParser; Etheryo::DotEnv dotenvParser;
@ -31,7 +35,7 @@ int main()
crow::SimpleApp app; crow::SimpleApp app;
CROW_ROUTE(app, "/")(test); CROW_ROUTE(app, "/")(test);
CROW_ROUTE(app, "/get_simple_posts/<int>")(getSimplePosts); CROW_ROUTE(app, "/get_simple_posts/<int>")(getSimplePosts);
CROW_ROUTE(app, "/get_post/<string>")(getPost);
app.port(8000).multithreaded().run(); app.port(8000).multithreaded().run();
globalConnection->close(); globalConnection->close();
} }
@ -72,4 +76,28 @@ crow::response getSimplePosts(int limit)
response.add_header("Access-Control-Allow-Origin", "*"); response.add_header("Access-Control-Allow-Origin", "*");
response.add_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); response.add_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
return response; return response;
}
crow::response getPost(std::string slug)
{
post.clear();
pqxx::work worker { *globalConnection };
auto result = worker.query<int, std::string, std::string, std::string, std::string, std::string, int, int, int>
("select p.author_id, a.name, p.title, p.date, p.picture, p.body, p.views, p.likes, p.dislikes from post p join authors a on p.author_id = a.id where p.slug = '"+slug+"';");
for(auto [author_id, name, title, date, picture, body, views, likes, dislikes] : result)
{
post["name"] = name; //"title","date","picture","body","views","likes","dislikes"
post["title"] = title;
post["date"] = date;
post["picture"] = picture;
post["body"] = body;
post["views"] = std::to_string(views);
post["likes"] = std::to_string(likes);
post["dislikes"] = std::to_string(dislikes);
post.push();
}
auto response = crow::response { "application/json", post.to_str() };
response.add_header("Access-Control-Allow-Origin", "*");
response.add_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
return response;
} }

View file

@ -26,13 +26,13 @@
role="button" role="button"
tabindex="0" tabindex="0"
on:click={() => { on:click={() => {
redirectTo(`/article/${mainpost.slug}`); redirectTo(`/articles/${mainpost.slug}`);
}} }}
on:keydown={(event) => handleKeyDown(event, `/article/${mainpost.slug}`)} on:keydown={(event) => handleKeyDown(event, `/articles/${mainpost.slug}`)}
> >
<div> <div>
<h2>{mainpost.title}</h2> <h2>{mainpost.title}</h2>
<div class="center flex justify-center h-60"> <div class="center flex justify-center h-70 margin-bottom ">
<img class="carousel-picture" alt="mainpicture" src={mainpost.picture} /> <img class="carousel-picture" alt="mainpicture" src={mainpost.picture} />
</div> </div>
<div class="flex align-center margin-horizontal-05"> <div class="flex align-center margin-horizontal-05">
@ -54,9 +54,9 @@
role="button" role="button"
tabindex="0" tabindex="0"
on:click={() => { on:click={() => {
redirectTo(`/article/${p.slug}`); redirectTo(`/articles/${p.slug}`);
}} }}
on:keydown={(event) => handleKeyDown(event, `/article/${p.slug}`)} on:keydown={(event) => handleKeyDown(event, `/articles/${p.slug}`)}
> >
<img class="carousel-picture" alt="mainpicture" src={p.picture} /> <img class="carousel-picture" alt="mainpicture" src={p.picture} />
<div class="flex-col align justify-center"> <div class="flex-col align justify-center">

View file

@ -59,7 +59,7 @@ h1 {
height: 100%; height: 100%;
} }
.h-60 { .h-70 {
height: 60%; height: 60%;
} }
@ -84,6 +84,10 @@ h1 {
padding: 0; padding: 0;
} }
.margin-bottom {
margin-bottom: 0.5rem;
}
.margin-horizontal-05 { .margin-horizontal-05 {
margin-top: 0.5rem; margin-top: 0.5rem;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;

View file

@ -34,7 +34,6 @@
width: 90%; width: 90%;
height: auto; height: auto;
object-fit: cover; object-fit: cover;
margin-bottom: 0.5rem;
} }
.carousel-v-minpost-container { .carousel-v-minpost-container {
@ -71,11 +70,12 @@
transition: all .1s ease 0s; transition: all .1s ease 0s;
margin-right: 1rem; margin-right: 1rem;
margin-left: 1rem; margin-left: 1rem;
background-color: var(--color-background);
} }
.carousel-v-minpost img { .carousel-v-minpost img {
min-width: 50%; min-width: 40%;
max-width: 50%; max-width: 40%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
margin: 0; margin: 0;
@ -88,5 +88,6 @@
overflow: hidden; overflow: hidden;
display: -webkit-box; display: -webkit-box;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
-webkit-line-clamp: 1; -webkit-line-clamp: 2;
font-size: 1.4rem;
} }

View file

@ -0,0 +1,10 @@
import { error } from '@sveltejs/kit';
export function load({ params }) {
const slug = params.slug;
return {
status: 0,
slug: slug
};
}

View file

@ -0,0 +1,5 @@
<script>
export let data;
</script>
<p>{data.slug}</p>