Add primitive user page
This commit is contained in:
parent
1e4b221dd7
commit
aecde91ad1
|
@ -24,7 +24,7 @@ add_link_options(${FLAGS})
|
||||||
|
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} main.cpp misc.cpp config.cpp servehelper.cpp pixivclient.cpp blankie/serializer.cpp blankie/escape.cpp
|
add_executable(${PROJECT_NAME} main.cpp misc.cpp config.cpp servehelper.cpp pixivclient.cpp blankie/serializer.cpp blankie/escape.cpp
|
||||||
routes/home.cpp routes/css.cpp)
|
routes/home.cpp routes/css.cpp routes/users/common.cpp routes/users/users.cpp)
|
||||||
set_target_properties(${PROJECT_NAME}
|
set_target_properties(${PROJECT_NAME}
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
CXX_STANDARD 20
|
CXX_STANDARD 20
|
||||||
|
|
15
main.cpp
15
main.cpp
|
@ -28,6 +28,10 @@ int main(int argc, char** argv) {
|
||||||
});
|
});
|
||||||
server.Get("/style\\.css", css_route);
|
server.Get("/style\\.css", css_route);
|
||||||
|
|
||||||
|
server.Get("/users/(\\d+)", [&](const httplib::Request& req, httplib::Response& res) {
|
||||||
|
users_route(req, res, config, pixiv_client);
|
||||||
|
});
|
||||||
|
|
||||||
server.Get("/member\\.php", [&](const httplib::Request& req, httplib::Response& res) {
|
server.Get("/member\\.php", [&](const httplib::Request& req, httplib::Response& res) {
|
||||||
std::string id = req.get_param_value("id");
|
std::string id = req.get_param_value("id");
|
||||||
if (id.empty() || id.find_first_not_of("1234567890") != std::string::npos) {
|
if (id.empty() || id.find_first_not_of("1234567890") != std::string::npos) {
|
||||||
|
@ -54,17 +58,6 @@ int main(int argc, char** argv) {
|
||||||
server.Get("/debug/exception/unknown", [](const httplib::Request& req, httplib::Response& res) {
|
server.Get("/debug/exception/unknown", [](const httplib::Request& req, httplib::Response& res) {
|
||||||
throw "cope";
|
throw "cope";
|
||||||
});
|
});
|
||||||
// TODO stop it
|
|
||||||
server.Get("/debug/getuser/good", [&](const httplib::Request& req, httplib::Response& res) {
|
|
||||||
User user = pixiv_client.get_user(2583663);
|
|
||||||
printf("%s\n", user.username.c_str());
|
|
||||||
res.set_content(user.username, "text/plain");
|
|
||||||
});
|
|
||||||
server.Get("/debug/getuser/bad", [&](const httplib::Request& req, httplib::Response& res) {
|
|
||||||
User user = pixiv_client.get_user(1);
|
|
||||||
printf("%s\n", user.username.c_str());
|
|
||||||
res.set_content(user.username, "text/plain");
|
|
||||||
});
|
|
||||||
#endif
|
#endif
|
||||||
server.Get(".*", [&](const httplib::Request& req, httplib::Response& res) {
|
server.Get(".*", [&](const httplib::Request& req, httplib::Response& res) {
|
||||||
res.status = 404;
|
res.status = 404;
|
||||||
|
|
|
@ -38,7 +38,7 @@ void from_json(const nlohmann::json& j, User& user) {
|
||||||
j.at("user_name").get_to(user.display_name);
|
j.at("user_name").get_to(user.display_name);
|
||||||
user.user_id = to_ull(j.at("user_id").get_ref<const nlohmann::json::string_t&>());
|
user.user_id = to_ull(j.at("user_id").get_ref<const nlohmann::json::string_t&>());
|
||||||
|
|
||||||
if (j.contains("cover_image")) {
|
if (j.contains("cover_image") && j["cover_image"].is_object()) {
|
||||||
nlohmann::json cover_image = j["cover_image"];
|
nlohmann::json cover_image = j["cover_image"];
|
||||||
std::string c_720x360 = cover_image.at("profile_cover_image").at("720x360").get<std::string>();
|
std::string c_720x360 = cover_image.at("profile_cover_image").at("720x360").get<std::string>();
|
||||||
std::optional<std::string> original = get_original_cover_image(c_720x360);
|
std::optional<std::string> original = get_original_cover_image(c_720x360);
|
||||||
|
@ -59,9 +59,9 @@ void from_json(const nlohmann::json& j, User& user) {
|
||||||
|
|
||||||
std::string user_webpage = j.at("user_webpage").get<std::string>();
|
std::string user_webpage = j.at("user_webpage").get<std::string>();
|
||||||
if (!user_webpage.empty()) {
|
if (!user_webpage.empty()) {
|
||||||
user.links.push_back({"webpage", std::move(user_webpage)});
|
user.links.push_back({"Webpage", std::move(user_webpage)});
|
||||||
}
|
}
|
||||||
auto add_social_as_needed = [&](const char* key) {
|
auto add_social_as_needed = [&](const char* key, const char* public_name) {
|
||||||
nlohmann::json social = j.at("social");
|
nlohmann::json social = j.at("social");
|
||||||
if (!social.is_object()) {
|
if (!social.is_object()) {
|
||||||
return;
|
return;
|
||||||
|
@ -71,14 +71,14 @@ void from_json(const nlohmann::json& j, User& user) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string url = social[key].at("url").get<std::string>();
|
std::string url = social[key].at("url").get<std::string>();
|
||||||
user.links.push_back({key, std::move(url)});
|
user.links.push_back({public_name, std::move(url)});
|
||||||
};
|
};
|
||||||
add_social_as_needed("twitter");
|
add_social_as_needed("twitter", "Twitter");
|
||||||
add_social_as_needed("instagram");
|
add_social_as_needed("instagram", "Instagram");
|
||||||
add_social_as_needed("tumblr");
|
add_social_as_needed("tumblr", "Tumblr");
|
||||||
add_social_as_needed("facebook");
|
add_social_as_needed("facebook", "Facebook");
|
||||||
add_social_as_needed("circlems");
|
add_social_as_needed("circlems", "Circle.ms");
|
||||||
add_social_as_needed("pawoo");
|
add_social_as_needed("pawoo", "Pawoo");
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::regex c1920x960_cover_image_thumbnail_regex(
|
static std::regex c1920x960_cover_image_thumbnail_regex(
|
||||||
|
|
|
@ -31,6 +31,24 @@ void css_route(const httplib::Request& req, httplib::Response& res) {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cover {
|
||||||
|
object-fit: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 50vh;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
.profilepicture {
|
||||||
|
object-fit: cover;
|
||||||
|
width: 5em;
|
||||||
|
height: 5em;
|
||||||
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
.usermetadata {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: var(--error-background-color);
|
background-color: var(--error-background-color);
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
#include <httplib/httplib.h>
|
#include <httplib/httplib.h>
|
||||||
|
|
||||||
struct Config; // forward declaration from config.h
|
struct Config; // forward declaration from ../config.h
|
||||||
|
class PixivClient; // forward declaration from ../pixivclient.h
|
||||||
|
|
||||||
void home_route(const httplib::Request& req, httplib::Response& res, const Config& config);
|
void home_route(const httplib::Request& req, httplib::Response& res, const Config& config);
|
||||||
void css_route(const httplib::Request& req, httplib::Response& res);
|
void css_route(const httplib::Request& req, httplib::Response& res);
|
||||||
|
void users_route(const httplib::Request& req, httplib::Response& res, const Config& config, PixivClient& pixiv_client);
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include "common.h"
|
||||||
|
#include "../../config.h"
|
||||||
|
#include "../../servehelper.h"
|
||||||
|
#include "../../pixivclient.h"
|
||||||
|
|
||||||
|
static inline Element generate_user_links(const User& user);
|
||||||
|
static std::string thumbnail_or_original(const Images& images);
|
||||||
|
static std::string original_or_thumbnail(const Images& images);
|
||||||
|
|
||||||
|
Element generate_user_header(const User& user, const Config& config) {
|
||||||
|
auto proxy_url = [&](std::string url) {
|
||||||
|
return config.image_proxy_url + remove_origin(std::move(url));
|
||||||
|
};
|
||||||
|
|
||||||
|
Element header("header");
|
||||||
|
if (user.cover_images) {
|
||||||
|
header.nodes.push_back(Element("a", {{"href", proxy_url(original_or_thumbnail(*user.cover_images))}}, {
|
||||||
|
Element("img", {{"class", "cover"}, {"src", proxy_url(thumbnail_or_original(*user.cover_images))}}, {})
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
header.nodes.push_back(Element("div", {{"class", "usermetadata"}}, {
|
||||||
|
Element("a", {{"href", proxy_url(original_or_thumbnail(user.profile_pictures))}}, {
|
||||||
|
Element("img", {{"class", "profilepicture"}, {"src", proxy_url(thumbnail_or_original(user.profile_pictures))}}, {})
|
||||||
|
}),
|
||||||
|
Element("div", {
|
||||||
|
Element("p", {Element("b", {user.display_name}), " (@", user.username, ")"}),
|
||||||
|
generate_user_links(user)
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Element generate_user_links(const User& user) {
|
||||||
|
Element p("p");
|
||||||
|
for (const auto &[name, url] : user.links) {
|
||||||
|
if (!p.nodes.empty()) {
|
||||||
|
p.nodes.push_back(", ");
|
||||||
|
}
|
||||||
|
p.nodes.push_back(Element("a", {{"href", url}}, {name}));
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string thumbnail_or_original(const Images& images) {
|
||||||
|
if (!images.thumbnails.empty()) {
|
||||||
|
return images.thumbnails.back();
|
||||||
|
}
|
||||||
|
return images.original.value_or("");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string original_or_thumbnail(const Images& images) {
|
||||||
|
if (images.original) {
|
||||||
|
return *images.original;
|
||||||
|
}
|
||||||
|
if (!images.thumbnails.empty()) {
|
||||||
|
return images.thumbnails.back();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../blankie/serializer.h"
|
||||||
|
struct User; // forward declaration from ../../pixivclient.h
|
||||||
|
struct Config; // forward declaration from ../../config.h
|
||||||
|
|
||||||
|
using Element = blankie::html::Element;
|
||||||
|
|
||||||
|
Element generate_user_header(const User& user, const Config& config);
|
|
@ -0,0 +1,41 @@
|
||||||
|
#include "../routes.h"
|
||||||
|
#include "../../servehelper.h"
|
||||||
|
#include "../../pixivclient.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
static inline uint64_t to_ull(const std::string& str);
|
||||||
|
|
||||||
|
void users_route(const httplib::Request& req, httplib::Response& res, const Config& config, PixivClient& pixiv_client) {
|
||||||
|
uint64_t user_id = to_ull(req.matches[1].str());
|
||||||
|
User user;
|
||||||
|
try {
|
||||||
|
user = pixiv_client.get_user(user_id);
|
||||||
|
} catch (const PixivException& e) {
|
||||||
|
if (e.status == 404) {
|
||||||
|
res.status = 404;
|
||||||
|
serve_error(req, res, config, "404: User not found", e.what());
|
||||||
|
} else {
|
||||||
|
res.status = 500;
|
||||||
|
serve_error(req, res, config, "500: Internal server error", "Failed to fetch user information", e.what());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
res.status = 500;
|
||||||
|
serve_error(req, res, config, "500: Internal server error", "Failed to fetch user information", e.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
serve(req, res, config, user.display_name + " (@" + user.username + ')', generate_user_header(user, config));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t to_ull(const std::string& str) {
|
||||||
|
char* endptr;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
uint64_t res = strtoull(str.c_str(), &endptr, 10);
|
||||||
|
if (res == ULLONG_MAX && errno == ERANGE) {
|
||||||
|
throw std::overflow_error(str + " is too big");
|
||||||
|
} else if (endptr[0] != '\0') {
|
||||||
|
throw std::invalid_argument(str + " contains trailing text or is not an integer");
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -3,11 +3,6 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "servehelper.h"
|
#include "servehelper.h"
|
||||||
|
|
||||||
static std::regex image_proxy_regex(
|
|
||||||
"(https?://)?" // optional scheme
|
|
||||||
"(?:.+?@)?" // optional username and pass
|
|
||||||
"([^/]+(?::\\d+)?)" // host
|
|
||||||
"(?:/.*)?$");
|
|
||||||
static inline std::string get_image_proxy_origin(const std::string& url);
|
static inline std::string get_image_proxy_origin(const std::string& url);
|
||||||
|
|
||||||
void serve(const httplib::Request& req, httplib::Response& res, const Config& config, std::string title, Element element) {
|
void serve(const httplib::Request& req, httplib::Response& res, const Config& config, std::string title, Element element) {
|
||||||
|
@ -79,7 +74,25 @@ std::string get_origin(const httplib::Request& req, const Config& config) {
|
||||||
return origin;
|
return origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::regex remove_origin_regex(
|
||||||
|
"(?:https?://)?" // optional schema
|
||||||
|
"(?:.+?@)?" // optional username and pass
|
||||||
|
"(?:[^/]+[.:][^/]+(?:\\d+)?)" // host
|
||||||
|
"(/.*)");
|
||||||
|
std::string remove_origin(const std::string& url) {
|
||||||
|
std::smatch sm;
|
||||||
|
if (!std::regex_match(url, sm, remove_origin_regex)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return sm[1].str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static std::regex image_proxy_regex(
|
||||||
|
"(https?://)?" // optional scheme
|
||||||
|
"(?:.+?@)?" // optional username and pass
|
||||||
|
"([^/]+(?::\\d+)?)" // host
|
||||||
|
"(?:/.*)?$");
|
||||||
static inline std::string get_image_proxy_origin(const std::string& url) {
|
static inline std::string get_image_proxy_origin(const std::string& url) {
|
||||||
std::smatch sm;
|
std::smatch sm;
|
||||||
if (!std::regex_match(url, sm, image_proxy_regex)) {
|
if (!std::regex_match(url, sm, image_proxy_regex)) {
|
||||||
|
|
|
@ -13,3 +13,4 @@ void serve_error(const httplib::Request& req, httplib::Response& res, const Conf
|
||||||
std::string title, std::optional<std::string> subtitle = std::nullopt, std::optional<std::string> info = std::nullopt);
|
std::string title, std::optional<std::string> subtitle = std::nullopt, std::optional<std::string> info = std::nullopt);
|
||||||
void serve_redirect(const httplib::Request& req, httplib::Response& res, const Config& config, std::string url);
|
void serve_redirect(const httplib::Request& req, httplib::Response& res, const Config& config, std::string url);
|
||||||
std::string get_origin(const httplib::Request& req, const Config& config);
|
std::string get_origin(const httplib::Request& req, const Config& config);
|
||||||
|
std::string remove_origin(const std::string& url);
|
||||||
|
|
Loading…
Reference in New Issue