2023-04-03 08:57:51 +00:00
|
|
|
#include <cstdio>
|
|
|
|
#include <httplib/httplib.h>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
|
|
|
|
#include "config.h"
|
2023-04-04 17:01:50 +00:00
|
|
|
#include "pixivclient.h"
|
2023-04-03 15:27:07 +00:00
|
|
|
#include "servehelper.h"
|
2023-04-03 15:10:08 +00:00
|
|
|
#include "routes/routes.h"
|
2023-06-08 16:02:36 +00:00
|
|
|
#include "hiredis_wrapper.h"
|
2023-04-03 08:57:51 +00:00
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
|
|
|
if (argc != 2) {
|
|
|
|
fprintf(stderr, "Usage: %s <path/to/config/file.json>\n", argc > 0 ? argv[0] : "pixwhile");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
Config config;
|
|
|
|
try {
|
|
|
|
config = load_config(argv[1]);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
fprintf(stderr, "Failed to load config: %s\n", e.what());
|
|
|
|
return 1;
|
|
|
|
}
|
2023-06-08 16:02:36 +00:00
|
|
|
std::optional<Redis> redis;
|
|
|
|
if (config.redis_config) {
|
|
|
|
if (const IPConnection* ip_connection = std::get_if<IPConnection>(&config.redis_config->connection_method)) {
|
|
|
|
redis.emplace(ip_connection->address, ip_connection->port);
|
|
|
|
} else if (const UnixConnection* unix_connection = std::get_if<UnixConnection>(&config.redis_config->connection_method)) {
|
|
|
|
redis.emplace(unix_connection->unix);
|
|
|
|
}
|
|
|
|
if (config.redis_config->username && config.redis_config->password) {
|
|
|
|
redis->auth(*config.redis_config->username, *config.redis_config->password);
|
|
|
|
} else if (config.redis_config->password) {
|
|
|
|
redis->auth(*config.redis_config->password);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PixivClient pixiv_client(redis ? &*redis : nullptr);
|
2023-04-03 08:57:51 +00:00
|
|
|
httplib::Server server;
|
2023-06-08 16:02:36 +00:00
|
|
|
|
2023-04-03 09:32:26 +00:00
|
|
|
server.Get("/", [&](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
home_route(req, res, config);
|
|
|
|
});
|
2023-04-04 11:09:55 +00:00
|
|
|
server.Get("/style\\.css", css_route);
|
2023-04-03 08:57:51 +00:00
|
|
|
|
2023-04-05 16:36:20 +00:00
|
|
|
server.Get("/users/(\\d+)", [&](const httplib::Request& req, httplib::Response& res) {
|
2023-04-09 15:33:14 +00:00
|
|
|
serve_redirect(req, res, config, get_origin(req, config) + "/users/" + req.matches.str(1) + "/illustrations");
|
2023-04-05 16:36:20 +00:00
|
|
|
});
|
2023-04-07 15:06:31 +00:00
|
|
|
server.Get("/users/(\\d+)/illustrations", [&](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
user_illustrations_route(req, res, config, pixiv_client);
|
|
|
|
});
|
2023-04-09 13:31:50 +00:00
|
|
|
server.Get("/artworks/(\\d+)", [&](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
artworks_route(req, res, config, pixiv_client);
|
|
|
|
});
|
2023-05-01 16:40:33 +00:00
|
|
|
server.Get("/tags/([^/]+)", [&](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
serve_redirect(req, res, config, get_origin(req, config) + "/tags/" + req.matches.str(1) + "/illustrations");
|
|
|
|
});
|
|
|
|
server.Get("/tags/([^/]+)/illustrations", [&](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
tags_route(req, res, config, pixiv_client);
|
|
|
|
});
|
2023-04-05 16:36:20 +00:00
|
|
|
|
2023-04-04 11:09:55 +00:00
|
|
|
server.Get("/member\\.php", [&](const httplib::Request& req, httplib::Response& res) {
|
2023-04-04 08:33:48 +00:00
|
|
|
std::string id = req.get_param_value("id");
|
|
|
|
if (id.empty() || id.find_first_not_of("1234567890") != std::string::npos) {
|
|
|
|
res.status = 400;
|
|
|
|
serve_error(req, res, config, "400: Bad Request", "Missing or invalid user ID");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
serve_redirect(req, res, config, get_origin(req, config) + "/users/" + id);
|
|
|
|
});
|
2023-04-04 11:09:55 +00:00
|
|
|
server.Get("/member_illust\\.php", [&](const httplib::Request& req, httplib::Response& res) {
|
2023-04-04 08:33:48 +00:00
|
|
|
std::string illust_id = req.get_param_value("illust_id");
|
|
|
|
if (illust_id.empty() || illust_id.find_first_not_of("1234567890") != std::string::npos) {
|
|
|
|
res.status = 400;
|
|
|
|
serve_error(req, res, config, "400: Bad Request", "Missing or invalid illust ID");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
serve_redirect(req, res, config, get_origin(req, config) + "/artworks/" + illust_id);
|
|
|
|
});
|
2023-04-28 10:32:01 +00:00
|
|
|
server.Get("/search", [&](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
std::string q = req.get_param_value("q");
|
|
|
|
if (q.empty()) {
|
|
|
|
res.status = 400;
|
|
|
|
serve_error(req, res, config, "400: Bad Request", "Missing or empty search query");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
serve_redirect(req, res, config, get_origin(req, config) + "/tags/" + blankie::murl::escape(std::move(q)));
|
|
|
|
});
|
2023-04-04 08:33:48 +00:00
|
|
|
|
2023-06-07 09:16:03 +00:00
|
|
|
server.Get("/guess_extension/i.pximg.net(/.+)", [&](const httplib::Request& req, httplib::Response& res) {
|
2023-06-08 16:02:36 +00:00
|
|
|
guess_extension_route(req, res, config, pixiv_client, redis ? &*redis : nullptr);
|
2023-06-07 09:16:03 +00:00
|
|
|
});
|
|
|
|
|
2023-04-03 15:43:40 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
server.Get("/debug/exception/known", [](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
throw std::runtime_error("awoo");
|
|
|
|
});
|
|
|
|
server.Get("/debug/exception/unknown", [](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
throw "cope";
|
|
|
|
});
|
|
|
|
#endif
|
2023-04-03 15:27:07 +00:00
|
|
|
server.Get(".*", [&](const httplib::Request& req, httplib::Response& res) {
|
|
|
|
res.status = 404;
|
|
|
|
serve_error(req, res, config, "404: Page not found");
|
|
|
|
});
|
2023-04-03 15:43:40 +00:00
|
|
|
server.set_exception_handler([&](const httplib::Request& req, httplib::Response& res, std::exception_ptr ep) {
|
|
|
|
res.status = 500;
|
|
|
|
try {
|
|
|
|
std::rethrow_exception(ep);
|
|
|
|
} catch (const std::exception& e) {
|
2023-04-04 08:37:10 +00:00
|
|
|
fprintf(stderr, "Exception thrown on %s: %s\n", req.path.c_str(), e.what());
|
2023-04-03 15:43:40 +00:00
|
|
|
serve_error(req, res, config, "500: Internal server error", std::nullopt, e.what());
|
|
|
|
} catch (...) {
|
2023-04-04 08:37:10 +00:00
|
|
|
fprintf(stderr, "Exception thrown on %s: Unknown exception\n", req.path.c_str());
|
2023-04-03 15:43:40 +00:00
|
|
|
serve_error(req, res, config, "500: Internal server error", std::nullopt, "Unknown exception");
|
|
|
|
}
|
|
|
|
});
|
2023-04-03 15:27:07 +00:00
|
|
|
|
2023-04-03 08:57:51 +00:00
|
|
|
if (config.bind_port != 0) {
|
|
|
|
if (!server.bind_to_port(config.bind_host, config.bind_port)) {
|
|
|
|
fprintf(stderr, "Failed to bind to %s:%d\n", config.bind_host.c_str(), config.bind_port);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int port = server.bind_to_any_port(config.bind_host);
|
|
|
|
if (port == -1) {
|
|
|
|
fprintf(stderr, "Failed to bind to %s:<any>\n", config.bind_host.c_str());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
config.bind_port = port;
|
|
|
|
}
|
|
|
|
printf("Listening on %s:%d...\n", config.bind_host.c_str(), config.bind_port);
|
|
|
|
|
|
|
|
if (!server.listen_after_bind()) {
|
|
|
|
fprintf(stderr, "Failed to listen on %s:%d\n", config.bind_host.c_str(), config.bind_port);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|