#include #include #include #include "config.h" #include "pixivclient.h" #include "servehelper.h" #include "routes/routes.h" #include "hiredis_wrapper.h" int main(int argc, char** argv) { if (argc != 2) { fprintf(stderr, "Usage: %s \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; } std::optional redis; if (config.redis_config) { if (const IPConnection* ip_connection = std::get_if(&config.redis_config->connection_method)) { redis.emplace(ip_connection->address, ip_connection->port); } else if (const UnixConnection* unix_connection = std::get_if(&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); httplib::Server server; server.Get("/", [&](const httplib::Request& req, httplib::Response& res) { home_route(req, res, config); }); server.Get("/style\\.css", css_route); server.Get("/users/(\\d+)", [&](const httplib::Request& req, httplib::Response& res) { serve_redirect(req, res, config, get_origin(req, config) + "/users/" + req.matches.str(1) + "/illustrations"); }); server.Get("/users/(\\d+)/illustrations", [&](const httplib::Request& req, httplib::Response& res) { user_illustrations_route(req, res, config, pixiv_client); }); server.Get("/artworks/(\\d+)", [&](const httplib::Request& req, httplib::Response& res) { artworks_route(req, res, config, pixiv_client); }); 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); }); server.Get("/member\\.php", [&](const httplib::Request& req, httplib::Response& res) { 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); }); server.Get("/member_illust\\.php", [&](const httplib::Request& req, httplib::Response& res) { 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); }); 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))); }); server.Get("/guess_extension/i.pximg.net(/.+)", [&](const httplib::Request& req, httplib::Response& res) { guess_extension_route(req, res, config, pixiv_client, redis ? &*redis : nullptr); }); #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 server.Get(".*", [&](const httplib::Request& req, httplib::Response& res) { res.status = 404; serve_error(req, res, config, "404: Page not found"); }); 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) { fprintf(stderr, "Exception thrown on %s: %s\n", req.path.c_str(), e.what()); serve_error(req, res, config, "500: Internal server error", std::nullopt, e.what()); } catch (...) { fprintf(stderr, "Exception thrown on %s: Unknown exception\n", req.path.c_str()); serve_error(req, res, config, "500: Internal server error", std::nullopt, "Unknown exception"); } }); 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:\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; } }