diff --git a/config.h b/config.h index 019c571..c1a6145 100644 --- a/config.h +++ b/config.h @@ -13,3 +13,5 @@ #define CLIENT_SECRET "8ZbKPseob8n1UmLLunPb06MNUKfPGRi1" #define REDIRECT_URI "http://localhost:8082/discordauth" #define BOT_TOKEN "Nzk3NTY1NTkyODM1NDU3MDI0.X_oU1w.fCVL8j58pphoEaU7e3q4yH7cFa4" + +#define COMMUNITY "https://discord.gg/ZMj5ytaQRZ" diff --git a/controllers/views.cc b/controllers/views.cc index 81d34a1..f84e3d5 100644 --- a/controllers/views.cc +++ b/controllers/views.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -11,9 +12,8 @@ #include "views.h" static unordered_map last_votes; -#define isAuthed() getOptional("discord_authed").has_value() #define authenticate(cb) cb(HttpResponse::newRedirectionResponse(OAUTH_URL)); return -#define toStart(cb) cb(HttpResponse::newRedirectionResponse("/")); return +#define toStartPage(cb) cb(HttpResponse::newRedirectionResponse("/")); return #define voteID(uid, bid) std::to_string(uid)+'-'+std::to_string(bid) #define cantVote(vid, ...) {auto _cantvoteres = last_votes.find(vid); auto now = trantor::Date::date(); if (_cantvoteres != last_votes.end() and now < _cantvoteres->second.after(43200)) __VA_ARGS__} @@ -47,20 +47,6 @@ std::string dbEsc(const std::string& src) { } return fres.str(); } -std::string htmlEsc(const std::string& src) { - std::ostringstream fres; - for (const auto &character : src) { - switch (character) { - case '<': fres << "<"; break; - case '>': fres << ">"; break; - case '&': fres << "&"; break; - case '"': fres << """; break; - case '\n': fres << "
"; break; - default: fres << character; - } - } - return fres.str(); -} auto errPage(const std::exception& e) { HttpViewData data; @@ -71,6 +57,28 @@ auto errPage(const std::exception& e) { return resp; } +SessionDataPtr getSessionData(SessionPtr session) { + return session->getOptional("data").value_or(nullptr); +} +void setSessionData(SessionPtr session, SessionDataPtr data) { + session->insert("data", data); +} +void resetSessionData(SessionPtr session) { + session->erase("data"); +} + +HttpViewData HttpViewDataPrep(SessionDataPtr sessionData) { + HttpViewData fres; + fres.insert("sessionData", sessionData); + return fres; +} +HttpViewData HttpViewDataPrep(SessionPtr session) { + return HttpViewDataPrep(getSessionData(session)); +} +HttpViewData HttpViewDataPrep(const HttpRequestPtr& request) { + return HttpViewDataPrep(request->session()); +} + void getUser(uint64_t user_id, std::function callback) { auto discordapi = HttpClient::newHttpClient("https://discord.com"); auto req = HttpRequest::newHttpRequest(); @@ -95,6 +103,18 @@ void getUser(uint64_t user_id, std::function callback auto dbErr = [](const orm::DrogonDbException &) {}; +std::string SessionData::discord_fullname() { + return discord_username+'#'+discord_discriminator; +} + +void LoginFilter::doFilter(const HttpRequestPtr &req, FilterCallback &&fcb, FilterChainCallback &&fccb) { + if (getSessionData(req->session())) { + fccb(); + } else { + authenticate(fcb); + } +} + views::views() { db = drogon::app().getDbClient(); } @@ -106,43 +126,44 @@ void views::start( callback(HttpResponse::newRedirectionResponse("/bots/@all", HttpStatusCode::k301MovedPermanently)); } -void LoginFilter::doFilter(const HttpRequestPtr &req, FilterCallback &&fcb, FilterChainCallback &&fccb) { - if (req->session()->isAuthed()) { - fccb(); - } else { - authenticate(fcb); - } +void views::menu( + const HttpRequestPtr& req, std::function &&callback + ) +{ + auto data = HttpViewDataPrep(req); + data.insert("ref", req->getHeader("Referer")); + + callback(HttpResponse::newHttpViewResponse("menu.csp", data)); } void views::botlist( const HttpRequestPtr& req, std::function &&callback ) { - auto session = req->session(); - auto authed = session->isAuthed(); + auto sessionData = getSessionData(req->session()); auto justMine = req->getPath()=="/bots/@me"; auto modView = req->getPath()=="/bots/@unapproved"; std::string q = "SELECT * FROM bots WHERE "; if (justMine) { - q.append("owner_id = '"+std::to_string(session->get("discord_user_id"))+"'"); + q.append("owner_id = '"+std::to_string(sessionData->discord_id)+"'"); } else { q.append("approved = "+std::string(modView?"false":"true")); } q.append(" ORDER BY votes"); db->execSqlAsync(q, - [justMine, authed, callback] (const orm::Result &rows) { + [justMine, sessionData, callback] (const orm::Result &rows) { std::map bot_list; for (const auto& r : rows) { Bot bot = deserializeBot(r); bot_list[bot.app_id] = bot; } - HttpViewData data; + auto data = HttpViewDataPrep(sessionData); data.insert("modView", false); data.insert("justMine", justMine); - data.insert("authed", authed); + data.insert("authed", bool(sessionData)); data.insert("bots", bot_list); callback(HttpResponse::newHttpViewResponse("botlist.csp", data)); @@ -160,14 +181,17 @@ void views::botdetail( callback(HttpResponse::newNotFoundResponse()); } else { // Bot found - auto session = req->session(); + auto sessionData = getSessionData(req->session()); auto bot = deserializeBot(rows[0]); - HttpViewData data; + auto data = HttpViewDataPrep(sessionData); data.insert("modView", false); data.insert("bot_id", bot.app_id); data.insert("bot", bot); - data.insert("owner", session->isAuthed() and session->get("discord_user_id") == bot.owner_id); - cantVote(voteID(req->session()->get("discord_user_id"), bot_id), { + data.insert("owner", sessionData and sessionData->discord_id == bot.owner_id); + if (not sessionData) { + // Vote button is not grezed out if user isn"t logged in + data.insert("canVote", bot.approved); + } else cantVote(voteID(sessionData->discord_id, bot_id), { data.insert("canVote", false); } else { data.insert("canVote", bot.approved); @@ -180,9 +204,8 @@ void views::botdetail( void views::botvote(const HttpRequestPtr& req, std::function &&callback, uint64_t bot_id) { - auto session = req->session(); // Check if user is able to vote again - auto user_id = session->get("discord_user_id"); + auto user_id = getSessionData(req->session())->discord_id; auto vote_id = voteID(user_id, bot_id); cantVote(vote_id, { callback(HttpResponse::newRedirectionResponse("detail")); @@ -206,10 +229,8 @@ void views::botvote(const HttpRequestPtr& req, std::function &&callback, const std::string& error) { - auto session = req->session(); // Display page - HttpViewData data; - data.insert("owner", session->get("discord_user_fullname")); + auto data = HttpViewDataPrep(req); data.insert("error", error); callback(HttpResponse::newHttpViewResponse("botregister.csp", data)); @@ -226,10 +247,10 @@ void views::botregister_submit(const HttpRequestPtr& req, std::functiongetParameter("app_id")); - short_description = htmlEsc(req->getParameter("short_description")); - long_description = htmlEsc(req->getParameter("long_description")); - support_server = htmlEsc(req->getParameter("support_server")); - prefix = htmlEsc(req->getParameter("prefix")); + short_description = HttpViewData::htmlTranslate(req->getParameter("short_description")); + long_description = HttpViewData::htmlTranslate(req->getParameter("long_description")); + support_server = HttpViewData::htmlTranslate(req->getParameter("support_server")); + prefix = HttpViewData::htmlTranslate(req->getParameter("prefix")); } catch (std::exception& e) { onError(e.what()); } @@ -247,10 +268,12 @@ void views::botregister_submit(const HttpRequestPtr& req, std::functionexecSqlAsync(fmt::format("INSERT INTO bots (name, short_description, long_description, avatar_url, owner, support_server, prefix, owner_id, app_id, votes, approved) " "VALUES('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', 0, 'f')", - dbEsc(botuser["username"].asString()), dbEsc(short_description), dbEsc(long_description), dbEsc(botuser["avatar_url"].asString()), session->get("discord_user_fullname"), dbEsc(support_server), dbEsc(prefix), session->get("discord_user_id"), app_id), + dbEsc(botuser["username"].asString()), dbEsc(short_description), dbEsc(long_description), dbEsc(botuser["avatar_url"].asString()), sessionData->discord_fullname(), dbEsc(support_server), dbEsc(prefix), sessionData->discord_id, app_id), [app_id, callback] (const orm::Result &) { callback(HttpResponse::newRedirectionResponse(std::to_string(app_id)+"/detail")); }, dbErr); @@ -262,7 +285,7 @@ void views::discorddeauth( const HttpRequestPtr& req, std::function &&callback ) { req->session()->clear(); - toStart(callback); + toStartPage(callback); } void views::discordauth( @@ -291,33 +314,33 @@ void views::discordauth( if (response->getStatusCode() == HttpStatusCode::k200OK) { // Auth success auto &data = *response->getJsonObject().get(); + // Prepare session auto session = client_req->session(); - auto access_token = data["access_token"].asString(); - session->clear(); - session->insert("discord_access_token", access_token); + resetSessionData(session); + auto sessionData = std::make_shared(); + sessionData->discord_access_token = data["access_token"].asString(); // Get user data auto req = HttpRequest::newHttpRequest(); req->setPath("/api/v8/users/@me"); req->setMethod(HttpMethod::Get); - req->addHeader("Authorization", "Bearer "+access_token); - discordapi->sendRequest(req, [client_req, callback, session] (ReqResult, const HttpResponsePtr &response) { + req->addHeader("Authorization", "Bearer "+sessionData->discord_access_token); + discordapi->sendRequest(req, [client_req, callback, session, sessionData] (ReqResult, const HttpResponsePtr &response) { if (response->getStatusCode() == HttpStatusCode::k200OK) { // Getting user data success auto &userdata = *response->getJsonObject().get(); - auto fullname = userdata["username"].asString()+'#'+userdata["discriminator"].asString(); - session->insert("discord_authed", true); - session->insert("discord_user_id", std::stoul(userdata["id"].asString())); - session->insert("discord_user_fullname", fullname); + sessionData->discord_id = std::stoul(userdata["id"].asString()); + sessionData->discord_username = userdata["username"].asString(); + sessionData->discord_discriminator = userdata["discriminator"].asString(); + setSessionData(session, sessionData); // Show success page - HttpViewData data; - data.insert("fullname", fullname); + auto data = HttpViewDataPrep(sessionData); callback(HttpResponse::newHttpViewResponse("authsuccess.csp", data)); } else { - toStart(callback); + toStartPage(callback); } }); } else { - toStart(callback); + toStartPage(callback); } }); } diff --git a/controllers/views.h b/controllers/views.h index 327463f..b98ca66 100644 --- a/controllers/views.h +++ b/controllers/views.h @@ -1,4 +1,5 @@ #pragma once +#include #include using namespace std; @@ -10,6 +11,15 @@ struct Bot { bool approved = false; }; +struct SessionData { + uint64_t discord_id; + std::string discord_access_token, + discord_username, discord_discriminator; + bool moderator; + std::string discord_fullname(); +}; +using SessionDataPtr = std::shared_ptr; + class LoginFilter:public drogon::HttpFilter { public: @@ -24,6 +34,7 @@ class views: public drogon::HttpController { public: views(); void start(const HttpRequestPtr&, std::function &&); + void menu(const HttpRequestPtr&, std::function &&); void botlist(const HttpRequestPtr&, std::function &&); void botdetail(const HttpRequestPtr&, std::function &&, uint64_t); void botvote(const HttpRequestPtr&, std::function &&, uint64_t); @@ -42,5 +53,6 @@ public: ADD_METHOD_TO(views::botvote, "/bots/{1}/vote", Get, "LoginFilter"); ADD_METHOD_TO(views::discordauth, "/discordauth?code={1}", Get); ADD_METHOD_TO(views::discorddeauth, "/discorddeauth", Get); + ADD_METHOD_TO(views::menu, "/menu", Get); METHOD_LIST_END }; diff --git a/static/global.css b/static/global.css index d61410d..728eccb 100644 --- a/static/global.css +++ b/static/global.css @@ -4,15 +4,30 @@ body { font-family: sans-serif; } +.header { + position:fixed; + top:0; + width:99%; + background-color:#333333; +} + .container { margin: 20%; margin-top: 0px; margin-bottom: 0px; } + +.maxwidth { + width:100%; +} + @media only screen and (max-width: 600px) { .container { margin: unset; } + .maxwidth { + width:80%; + } } [class*="special-"] { @@ -30,8 +45,8 @@ body { } .special-input { - cursor:text; - text-align:left; + cursor:text; + text-align:left; } .special-button { @@ -48,6 +63,9 @@ body { position:relative; top:1px; } +.noborder { + border:unset; +} .title { font-size: 30px @@ -57,6 +75,10 @@ body { text-align: center; } -.maxwidth { - width:100%; +.content-desktop {display: block;} +.content-mobile {display: none;} + +@media screen and (max-width: 1000px) { +.content-desktop {display: none;} +.content-mobile {display: block;} } diff --git a/views/authsuccess.csp b/views/authsuccess.csp index aa67b73..095d4f2 100644 --- a/views/authsuccess.csp +++ b/views/authsuccess.csp @@ -1,9 +1,10 @@ -<%c++ auto fullname = @@.get("fullname");%> +<%inc#include "controllers/views.h" %> +<%c++ auto sessionData = @@.get("sessionData");%> <%layout global_layout%> Authentication success - DFB
-

Authenticated as: {%fullname%}

+

Authenticated as: {%sessionData->discord_fullname()%}

diff --git a/views/botlist.csp b/views/botlist.csp index 6431261..9e4e31d 100644 --- a/views/botlist.csp +++ b/views/botlist.csp @@ -16,9 +16,6 @@ My bots <%c++ }%> Register bot - Logout - <%c++ } else {%> - Login <%c++ }%>

diff --git a/views/botregister.csp b/views/botregister.csp index 0496c02..9b43e3e 100644 --- a/views/botregister.csp +++ b/views/botregister.csp @@ -1,4 +1,5 @@ -<%c++ auto owner = @@.get("owner");%> +<%inc#include "controllers/views.h" %> +<%c++ auto sessionData = @@.get("sessionData");%> <%c++ auto error = @@.get("error");%> <%layout global_layout%> @@ -8,13 +9,29 @@

Registering a bot

{%error%} -

Client ID

-

Short description

-

Long description

-

Prefix

-

Permanent support server invite code

-

Owner

+

Client ID

+

Short description

+

Long description

+

Prefix

+

Permanent support server invite code

+

Owner




- +
+ diff --git a/views/exception.csp b/views/exception.csp index 174ec3f..a3e2f7a 100644 --- a/views/exception.csp +++ b/views/exception.csp @@ -10,7 +10,7 @@ -

An exception has occured

+

An exception has occured

{%message%}

diff --git a/views/global_layout.csp b/views/global_layout.csp index 449acf5..62688eb 100644 --- a/views/global_layout.csp +++ b/views/global_layout.csp @@ -1,3 +1,8 @@ +<%inc#include "controllers/views.h" +#include "config.h" %> +<%c++ auto sessionData = @@.get("sessionData");%> + + @@ -7,6 +12,37 @@ +
+
+ <%c++ if (sessionData) {%> + + <%c++ }%> +
+ +
+
+

+ <%c++ if (sessionData) {%> + {%sessionData->discord_username%} + Logout + <%c++ } else {%> + Anonymous + Login + <%c++ }%> + Join our Discord +

+
+
+
+
+ +
+
+
+



[[]] diff --git a/views/menu.csp b/views/menu.csp new file mode 100644 index 0000000..352fdab --- /dev/null +++ b/views/menu.csp @@ -0,0 +1,45 @@ +<%inc#include "controllers/views.h" +#include "config.h" %> +<%c++ auto ref = @@.get("ref");%> +<%c++ auto sessionData = @@.get("sessionData");%> + + + + + + + + + + + +
+ + + +
+
+
+

{%std::string(sessionData?sessionData->discord_username:"Anonymous")%}

+ Start
+ <%c++ if (sessionData) {%> + My bots
+ Register bot
+ Logout
+ <%c++ } else {%> + Login
+ <%c++ }%> +
+ Join our Discord +
+ + +