diff options
-rw-r--r-- | public/style.css | 9 | ||||
-rw-r--r-- | server.js | 229 | ||||
-rw-r--r-- | views/account_change_about.pug (renamed from views/change_about.pug) | 2 | ||||
-rw-r--r-- | views/account_change_mail.pug (renamed from views/change_mail.pug) | 6 | ||||
-rw-r--r-- | views/account_change_name.pug (renamed from views/change_name.pug) | 6 | ||||
-rw-r--r-- | views/account_change_password.pug (renamed from views/change_password.pug) | 2 | ||||
-rw-r--r-- | views/account_delete.pug (renamed from views/delete_account.pug) | 2 | ||||
-rw-r--r-- | views/account_forgot_password.pug (renamed from views/forgot_password.pug) | 2 | ||||
-rw-r--r-- | views/account_mail_verify.pug | 32 | ||||
-rw-r--r-- | views/account_reset_password.pug (renamed from views/reset_password.pug) | 2 | ||||
-rw-r--r-- | views/account_webhook.pug (renamed from views/webhook.pug) | 4 | ||||
-rw-r--r-- | views/contacts_search.pug (renamed from views/search_user.pug) | 0 | ||||
-rw-r--r-- | views/create_index.pug (renamed from views/create-index.pug) | 0 | ||||
-rw-r--r-- | views/create_title.pug (renamed from views/create.pug) | 0 | ||||
-rw-r--r-- | views/head.pug | 33 | ||||
-rw-r--r-- | views/login.pug | 4 | ||||
-rw-r--r-- | views/profile.pug | 65 | ||||
-rw-r--r-- | views/signup.pug | 2 | ||||
-rw-r--r-- | views/stats_title.pug (renamed from views/game_stats.pug) | 2 | ||||
-rw-r--r-- | views/stats_user.pug (renamed from views/user_stats.pug) | 0 | ||||
-rw-r--r-- | views/title.pug (renamed from views/info.pug) | 0 | ||||
-rw-r--r-- | views/tm_seed.pug | 6 | ||||
-rw-r--r-- | views/user.pug | 34 | ||||
-rw-r--r-- | views/verify_mail.pug | 21 |
24 files changed, 255 insertions, 208 deletions
diff --git a/public/style.css b/public/style.css index bbbcbbd..7c0c07b 100644 --- a/public/style.css +++ b/public/style.css @@ -226,6 +226,15 @@ div.logo img { border: var(--thin-border); } +p.box { + white-space: pre-wrap; + font-style: italic; + padding: 8px 12px; + border: var(--thin-border); + box-shadow: var(--drop-shadow); + background-color: var(--color-text); +} + /* TABLES */ table { @@ -340,6 +340,12 @@ function format_minutes(mins) { return mins + " minutes" } +function is_valid_password(password) { + if (password.length < 4 || password.length > 100) + return false + return true +} + function is_valid_email(email) { return REGEX_MAIL.test(email) } @@ -372,6 +378,14 @@ function hash_password(password, salt) { return hash.digest("hex") } +function verify_password(user, password) { + var user_login = SQL_SELECT_LOGIN.get(user.user_id) + var hash = hash_password(password, user_login.salt) + if (hash !== user_login.password) + return false + return true +} + /* * ALTCHA ANTI-BOT SIGNUP */ @@ -462,6 +476,7 @@ const SQL_SELECT_USER_DYNAMIC = SQL("select * from user_dynamic_view where user_ const SQL_SELECT_USER_ID = SQL("SELECT user_id FROM users WHERE name=?").pluck() const SQL_SELECT_USER_BY_SEARCH = SQL("select name, atime from users left join user_last_seen using(user_id) where name like ? order by name") +const SQL_SELECT_USER_ABOUT = SQL("SELECT about FROM user_about WHERE user_id=?").pluck() const SQL_SELECT_USER_NOTIFY = SQL("SELECT notify FROM users WHERE user_id=?").pluck() const SQL_SELECT_USER_VERIFIED = SQL("SELECT is_verified FROM users WHERE user_id=?").pluck() const SQL_UPDATE_USER_NOTIFY = SQL("UPDATE users SET notify=? WHERE user_id=?") @@ -495,7 +510,7 @@ app.use(function (req, res, next) { let user_id = login_sql_select.get(sid) if (user_id) { login_touch(res, sid) - req.user = SQL_SELECT_USER_DYNAMIC.get(user_id) + req.user = res.locals.user = SQL_SELECT_USER_DYNAMIC.get(user_id) SQL_UPDATE_USER_LAST_SEEN.run(user_id, ip) if (req.user.is_banned) return res.status(403).send("") @@ -524,15 +539,15 @@ function must_be_administrator(req, res, next) { } app.get("/", function (req, res) { - res.render("index.pug", { user: req.user }) + res.render("index.pug") }) app.get("/create", function (req, res) { - res.render("create-index.pug", { user: req.user }) + res.render("create_index.pug") }) app.get("/about", function (req, res) { - res.render("about.pug", { user: req.user }) + res.render("about.pug") }) app.post("/logout", function (req, res) { @@ -599,89 +614,67 @@ app.post("/signup", must_pass_altcha, function (req, res) { res.redirect("/profile") }) -function create_and_mail_verification_token(user) { - if (!SQL_FIND_TOKEN.get(user.user_id)) - mail_verification_token(user, SQL_CREATE_TOKEN.get(user.user_id)) -} - -app.get("/verify-mail", must_be_logged_in, function (req, res) { +app.get("/account/mail/verify", must_be_logged_in, function (req, res) { if (SQL_SELECT_USER_VERIFIED.get(req.user.user_id)) return res.redirect("/profile") - create_and_mail_verification_token(req.user) - res.render("verify_mail.pug", { user: req.user }) + var sent_token = SQL_FIND_TOKEN.get(req.user.user_id) + var input_token = req.query.token + res.render("account_mail_verify.pug", { input_token, sent_token }) }) -app.get("/verify-mail/:token", must_be_logged_in, function (req, res) { - if (SQL_SELECT_USER_VERIFIED.get(req.user.user_id)) - return res.redirect("/profile") - res.render("verify_mail.pug", { user: req.user, token: req.params.token }) +app.post("/account/mail/verify-send", must_be_logged_in, function (req, res) { + if (!SQL_FIND_TOKEN.get(req.user.user_id)) + mail_verification_token(req.user, SQL_CREATE_TOKEN.get(req.user.user_id)) + res.redirect("/account/mail/verify") }) -app.post("/verify-mail", must_be_logged_in, function (req, res) { +app.post("/account/mail/verify", must_be_logged_in, function (req, res) { if (SQL_VERIFY_TOKEN.get(req.user.user_id, req.body.token)) { SQL_UPDATE_USER_VERIFIED.run(1, req.user.user_id) res.redirect("/profile") } else { - create_and_mail_verification_token(req.user) - res.render("verify_mail.pug", { user: req.user, flash: "Invalid or expired token!" }) + var sent_token = SQL_FIND_TOKEN.get(req.user.user_id) + res.render("account_mail_verify.pug", { sent_token, flash: "Invalid or expired token!" }) } }) -app.get("/forgot-password", function (req, res) { +app.get("/account/forgot-password", function (req, res) { if (req.user) return res.redirect("/") - res.render("forgot_password.pug") + res.render("account_forgot_password.pug") }) -app.post("/forgot-password", must_pass_altcha, function (req, res) { +app.post("/account/forgot-password", must_pass_altcha, function (req, res) { let mail = req.body.mail let user = SQL_SELECT_LOGIN_BY_MAIL.get(mail) if (user) { - let token = SQL_FIND_TOKEN.get(user.user_id) - if (!token) { - token = SQL_CREATE_TOKEN.get(user.user_id) - mail_password_reset_token(user, token) - } - return res.redirect("/reset-password/" + mail) + var token = SQL_CREATE_TOKEN.get(user.user_id) + mail_password_reset_token(user, token) + return res.redirect("/account/reset-password?mail=" + mail) } - res.render("forgot_password.pug", { flash: "User not found." }) + res.render("account_forgot_password.pug", { flash: "User not found." }) }) -app.get("/reset-password", function (req, res) { +app.get("/account/reset-password", function (req, res) { if (req.user) return res.redirect("/") - res.render("reset_password.pug", { mail: "", token: "" }) + var mail = req.query.mail + var token = req.query.token + res.render("account_reset_password.pug", { mail, token }) }) -app.get("/reset-password/:mail", function (req, res) { - if (req.user) - return res.redirect("/") - let mail = req.params.mail - res.render("reset_password.pug", { mail: mail, token: "" }) -}) - -app.get("/reset-password/:mail/:token", function (req, res) { - if (req.user) - return res.redirect("/") - let mail = req.params.mail - let token = req.params.token - res.render("reset_password.pug", { mail: mail, token: token }) -}) - -app.post("/reset-password", must_pass_altcha, function (req, res) { +app.post("/account/reset-password", must_pass_altcha, function (req, res) { let mail = req.body.mail let token = req.body.token let password = req.body.password function err(msg) { - res.render("reset_password.pug", { mail: mail, token: token, flash: msg }) + res.render("account_reset_password.pug", { mail: mail, token: token, flash: msg }) } let user = SQL_SELECT_LOGIN_BY_MAIL.get(mail) if (!user) return err("User not found.") - if (password.length < 4) - return err("Password is too short!") - if (password.length > 100) - return err("Password is too long!") + if (!is_valid_password(password)) + return err("New password is invalid!") if (!SQL_VERIFY_TOKEN.get(user.user_id, token)) return err("Invalid or expired token!") let salt = crypto.randomBytes(32).toString("hex") @@ -692,22 +685,19 @@ app.post("/reset-password", must_pass_altcha, function (req, res) { return res.redirect("/profile") }) -app.get("/change-password", must_be_logged_in, function (req, res) { - res.render("change_password.pug", { user: req.user }) +app.get("/account/change-password", must_be_logged_in, function (req, res) { + res.render("account_change_password.pug") }) -app.post("/change-password", must_be_logged_in, function (req, res) { +app.post("/account/change-password", must_be_logged_in, function (req, res) { let oldpass = req.body.password let newpass = req.body.newpass // Get full user record including password and salt let user = SQL_SELECT_LOGIN.get(req.user.user_id) - if (newpass.length < 4) - return res.render("change_password.pug", { user: req.user, flash: "Password is too short!" }) - if (newpass.length > 100) - return res.render("change_password.pug", { user: req.user, flash: "Password is too long!" }) - let oldhash = hash_password(oldpass, user.salt) - if (oldhash !== user.password) - return res.render("change_password.pug", { user: req.user, flash: "Wrong password!" }) + if (!is_valid_password(newpass)) + return res.render("account_change_password.pug", { flash: "New password is invalid!" }) + if (!verify_password(req.user, oldpass)) + return res.render("account_change_password.pug", { flash: "Wrong password!" }) let salt = crypto.randomBytes(32).toString("hex") let hash = hash_password(newpass, salt) SQL_UPDATE_USER_PASSWORD.run(user.user_id, hash, salt) @@ -726,17 +716,17 @@ function may_delete_account(user_id) { return true } -app.get("/delete-account", must_be_logged_in, function (req, res) { +app.get("/account/delete", must_be_logged_in, function (req, res) { if (!may_delete_account(req.user.user_id)) return res.status(401).send("You may not delete your account while you have unfinished games.") - res.render("delete_account.pug", { user: req.user }) + res.render("account_delete.pug") }) const SQL_SELECT_GAME_ROLE_FOR_DELETED_USER = SQL(` select game_id, role from players where user_id = ? and game_id in (select game_id from games where status <= 1) `) -app.post("/delete-account", must_be_logged_in, function (req, res) { +app.post("/account/delete", must_be_logged_in, function (req, res) { if (!may_delete_account(req.user.user_id)) res.status(401).send("You may not delete your account while you have unfinished games.") @@ -745,7 +735,7 @@ app.post("/delete-account", must_be_logged_in, function (req, res) { let user = SQL_SELECT_LOGIN.get(req.user.user_id) let hash = hash_password(password, user.salt) if (hash !== user.password) - return res.render("delete_account.pug", { user: req.user, flash: "Wrong password!" }) + return res.render("account_delete.pug", { flash: "Wrong password!" }) let list = SQL_SELECT_GAME_ROLE_FOR_DELETED_USER.all(req.user.user_id) for (let item of list) @@ -771,27 +761,27 @@ app.get("/admin/unban-user/:who", must_be_administrator, function (req, res) { * USER PROFILE */ -app.get("/subscribe", must_be_logged_in, function (req, res) { +app.get("/account/mail/subscribe", must_be_logged_in, function (req, res) { SQL_UPDATE_USER_NOTIFY.run(1, req.user.user_id) res.redirect("/profile") }) -app.get("/unsubscribe", must_be_logged_in, function (req, res) { +app.get("/account/mail/unsubscribe", must_be_logged_in, function (req, res) { SQL_UPDATE_USER_NOTIFY.run(0, req.user.user_id) res.redirect("/profile") }) -app.get("/webhook", must_be_logged_in, function (req, res) { +app.get("/account/webhook", must_be_logged_in, function (req, res) { let webhook = SQL_SELECT_WEBHOOK.get(req.user.user_id) - res.render("webhook.pug", { user: req.user, webhook: webhook }) + res.render("account_webhook.pug", { webhook: webhook }) }) -app.post("/api/webhook/delete", must_be_logged_in, function (req, res) { +app.post("/account/webhook/delete", must_be_logged_in, function (req, res) { SQL_DELETE_WEBHOOK.run(req.user.user_id) - res.redirect("/webhook") + res.redirect("/account/webhook") }) -app.post("/api/webhook/update", must_be_logged_in, function (req, res) { +app.post("/account/webhook/update", must_be_logged_in, function (req, res) { let url = req.body.url let prefix = req.body.prefix let format = req.body.format @@ -799,45 +789,50 @@ app.post("/api/webhook/update", must_be_logged_in, function (req, res) { const webhook = SQL_SELECT_WEBHOOK_SEND.get(req.user.user_id) if (webhook) send_webhook(req.user.user_id, webhook, "Test message!", 0) - res.setHeader("refresh", "3; url=/webhook") + res.setHeader("refresh", "3; url=/account/webhook") res.send("Testing Webhook. Please wait...") }) -app.get("/change-name", must_be_logged_in, function (req, res) { - res.render("change_name.pug", { user: req.user }) +app.get("/account/change-name", must_be_logged_in, function (req, res) { + res.render("account_change_name.pug") }) -app.post("/change-name", must_be_logged_in, function (req, res) { +app.post("/account/change-name", must_be_logged_in, function (req, res) { let newname = clean_user_name(req.body.newname) if (!is_valid_user_name(newname)) - return res.render("change_name.pug", { user: req.user, flash: "Invalid user name!" }) + return res.render("account_change_name.pug", { flash: "Invalid user name!" }) if (SQL_EXISTS_USER_NAME.get(newname)) - return res.render("change_name.pug", { user: req.user, flash: "That name is already taken!" }) + return res.render("account_change_name.pug", { flash: "That name is already taken!" }) + if (!verify_password(req.user, req.body.password)) + return res.render("account_change_name.pug", { flash: "Wrong password!" }) SQL_UPDATE_USER_NAME.run(newname, req.user.user_id) return res.redirect("/profile") }) -app.get("/change-mail", must_be_logged_in, function (req, res) { - res.render("change_mail.pug", { user: req.user }) +app.get("/account/change-mail", must_be_logged_in, function (req, res) { + res.render("account_change_mail.pug") }) -app.post("/change-mail", must_be_logged_in, function (req, res) { +app.post("/account/change-mail", must_be_logged_in, function (req, res) { let newmail = req.body.newmail if (!is_valid_email(newmail) || is_forbidden_mail(newmail)) - return res.render("change_mail.pug", { user: req.user, flash: "Invalid mail address!" }) + return res.render("account_change_mail.pug", { flash: "Invalid mail address!" }) if (SQL_EXISTS_USER_MAIL.get(newmail)) - return res.render("change_mail.pug", { user: req.user, flash: "That mail address is already taken!" }) + return res.render("account_change_mail.pug", { flash: "That mail address is already taken!" }) + if (!verify_password(req.user, req.body.password)) + return res.render("account_change_mail.pug", { flash: "Wrong password!" }) SQL_UPDATE_USER_MAIL.run(newmail, req.user.user_id) SQL_UPDATE_USER_VERIFIED.run(0, req.user.user_id) + SQL_UPDATE_USER_NOTIFY.run(0, req.user.user_id) return res.redirect("/profile") }) -app.get("/change-about", must_be_logged_in, function (req, res) { - let about = SQL_SELECT_USER_PROFILE.get(req.user.name).about - res.render("change_about.pug", { user: req.user, about: about || "" }) +app.get("/account/change-about", must_be_logged_in, function (req, res) { + let about = SQL_SELECT_USER_ABOUT.get(req.user.user_id) + res.render("account_change_about.pug", { about }) }) -app.post("/change-about", must_be_logged_in, function (req, res) { +app.post("/account/change-about", must_be_logged_in, function (req, res) { SQL_UPDATE_USER_ABOUT.run(req.user.user_id, req.body.about) return res.redirect("/profile") }) @@ -916,13 +911,13 @@ app.get("/contacts/search", must_be_logged_in, function (req, res) { if (!q.includes("%")) q = "%" + q + "%" let results = SQL_SELECT_USER_BY_SEARCH.all(q) - res.render("search_user.pug", { + res.render("contacts_search.pug", { user: req.user, search: req.query.q, results }) } else { - res.render("search_user.pug", { + res.render("contacts_search.pug", { user: req.user, search: null, results: null, @@ -1255,7 +1250,7 @@ app.get("/forum/search", must_be_logged_in, function (req, res) { results = FORUM_SEARCH.all('"' + search.replaceAll('"', '""') + '"') } } - res.render("forum_search.pug", { user: req.user, search, results }) + res.render("forum_search.pug", { search, results }) }) /* @@ -1694,10 +1689,14 @@ function annotate_games(list, user_id, unread, unseen) { } app.get("/profile", must_be_logged_in, function (req, res) { - req.user.notify = SQL_SELECT_USER_NOTIFY.get(req.user.user_id) - req.user.is_verified = SQL_SELECT_USER_VERIFIED.get(req.user.user_id) - req.user.webhook = SQL_SELECT_WEBHOOK.get(req.user.user_id) - res.render("profile.pug", { user: req.user }) + var who = SQL_SELECT_USER_PROFILE.get(req.user.name) + var mail = { + notify: SQL_SELECT_USER_NOTIFY.get(req.user.user_id), + is_verified: SQL_SELECT_USER_VERIFIED.get(req.user.user_id) + } + var webhook = SQL_SELECT_WEBHOOK.get(req.user.user_id) + var ratings = SQL_USER_RATINGS.all(req.user.user_id) + res.render("profile.pug", { who, mail, webhook, ratings }) }) app.get("/games", function (_req, res) { @@ -1725,7 +1724,7 @@ app.get("/games/active", must_be_logged_in, function (req, res) { let active_pools = TM_POOL_LIST_USER_ACTIVE.all(user_id) let finished_pools = TM_POOL_LIST_USER_RECENT_FINISHED.all(user_id) - res.render("games_active.pug", { user: req.user, who: req.user, games, seeds, active_pools, finished_pools }) + res.render("games_active.pug", { who: req.user, games, seeds, active_pools, finished_pools }) }) app.get("/tm/active", must_be_logged_in, function (req, res) { @@ -1733,7 +1732,7 @@ app.get("/tm/active", must_be_logged_in, function (req, res) { let seeds = TM_SEED_LIST_USER.all(user_id) let active_pools = TM_POOL_LIST_USER_ACTIVE.all(user_id) let finished_pools = TM_POOL_LIST_USER_RECENT_FINISHED.all(user_id) - res.render("tm_active.pug", { user: req.user, who: req.user, seeds, active_pools, finished_pools }) + res.render("tm_active.pug", { who: req.user, seeds, active_pools, finished_pools }) }) app.get("/games/finished", must_be_logged_in, function (req, res) { @@ -1741,12 +1740,12 @@ app.get("/games/finished", must_be_logged_in, function (req, res) { let unread = SQL_SELECT_UNREAD_CHAT_GAMES.all(req.user.user_id) let unseen = SQL_SELECT_UNSEEN_GAME_LIST.all(req.user.user_id) annotate_games(games, req.user.user_id, unread, unseen) - res.render("games_finished.pug", { user: req.user, who: req.user, games }) + res.render("games_finished.pug", { who: req.user, games }) }) app.get("/tm/finished", must_be_logged_in, function (req, res) { let pools = TM_POOL_LIST_USER_ALL_FINISHED.all(req.user.user_id) - res.render("tm_finished.pug", { user: req.user, who: req.user, pools }) + res.render("tm_finished.pug", { who: req.user, pools }) }) app.get("/games/finished/:who_name", function (req, res) { @@ -1754,7 +1753,7 @@ app.get("/games/finished/:who_name", function (req, res) { if (who) { let games = QUERY_LIST_FINISHED_GAMES_OF_USER.all({ user_id: who.user_id }) annotate_games(games, 0, null, null) - res.render("games_finished.pug", { user: req.user, who, games }) + res.render("games_finished.pug", { who, games }) } else { return res.status(404).send("Invalid user name.") } @@ -1764,7 +1763,7 @@ app.get("/tm/finished/:who_name", function (req, res) { let who = SQL_SELECT_USER_BY_NAME.get(req.params.who_name) if (who) { let pools = TM_POOL_LIST_USER_ALL_FINISHED.all(who.user_id) - res.render("tm_finished.pug", { user: req.user, who, pools }) + res.render("tm_finished.pug", { who, pools }) } else { return res.status(404).send("Invalid user name.") } @@ -1826,7 +1825,7 @@ function get_title_page(req, res, title_id) { let active_pools = TM_POOL_LIST_TITLE_ACTIVE.all(title_id) let finished_pools = TM_POOL_LIST_TITLE_FINISHED.all(title_id) - res.render("info.pug", { + res.render("title.pug", { user: req.user, title: title, open_games, @@ -1847,7 +1846,7 @@ app.get("/create/:title_id", function (req, res) { let title = TITLE_TABLE[title_id] if (!title) return res.status(404).send("Invalid title.") - res.render("create.pug", { + res.render("create_title.pug", { user: req.user, title: title, limit: req.user ? check_create_game_limit(req.user) : null, @@ -2479,7 +2478,7 @@ function mail_password_reset_token(user, token) { let subject = "Password reset request" let body = "Your password reset token is: " + token + "\n\n" + - SITE_URL + "/reset-password/" + user.mail + "/" + token + "\n" + SITE_URL + "/account/reset-password?mail=" + user.mail + "&token=" + token + "\n" console.log("SENT MAIL:", mail_addr(user), subject) mailer.sendMail({ from: MAIL_FROM, to: mail_addr(user), subject: subject, text: body }, mail_callback) } @@ -2490,7 +2489,7 @@ function mail_verification_token(user, token) { let subject = "Verify mail address" let body = "Your mail verification token is: " + token + "\n\n" + - SITE_URL + "/verify-mail/" + token + "\n" + SITE_URL + "/account/mail/verify?token=" + token + "\n" console.log("SENT MAIL:", mail_addr(user), subject) mailer.sendMail({ from: MAIL_FROM, to: mail_addr(user), subject: subject, text: body }, mail_callback) } @@ -3159,7 +3158,7 @@ const TM_SELECT_SEED_READY_MINI_CUP = SQL(` app.get("/tm/list", function (req, res) { let seeds = TM_SEED_LIST_ALL.all(req.user ? req.user.user_id : 0) - res.render("tm_list.pug", { user: req.user, seeds }) + res.render("tm_list.pug", { seeds }) }) app.get("/tm/seed/:seed_name", function (req, res) { @@ -3188,7 +3187,7 @@ app.get("/tm/seed/:seed_name", function (req, res) { may_register = true } - res.render("tm_seed.pug", { user: req.user, error, may_register, seed, queues, active_pools, finished_pools }) + res.render("tm_seed.pug", { error, may_register, seed, queues, active_pools, finished_pools }) }) app.get("/tm/pool/:pool_name", function (req, res) { @@ -3206,10 +3205,10 @@ app.get("/tm/pool/:pool_name", function (req, res) { players = TM_SELECT_PLAYERS_MP.all(pool_id) let games = TM_SELECT_GAMES.all(pool_id) let games_by_round = object_group_by(games, "round") - res.render("tm_pool.pug", { user: req.user, seed, pool, roles, players, games, games_by_round }) + res.render("tm_pool.pug", { seed, pool, roles, players, games, games_by_round }) }) -app.post("/api/tm/register/:seed_id/:level", must_be_logged_in, function (req, res) { +app.post("/tm/register/:seed_id/:level", must_be_logged_in, function (req, res) { let seed_id = req.params.seed_id | 0 let level = req.params.level | 0 let user_id = req.user.user_id @@ -3225,7 +3224,7 @@ app.post("/api/tm/register/:seed_id/:level", must_be_logged_in, function (req, r return res.redirect(req.headers.referer) }) -app.post("/api/tm/withdraw/:seed_id/:level", must_be_logged_in, function (req, res) { +app.post("/tm/withdraw/:seed_id/:level", must_be_logged_in, function (req, res) { let seed_id = req.params.seed_id | 0 let level = req.params.level | 0 let user_id = req.user.user_id @@ -3233,7 +3232,7 @@ app.post("/api/tm/withdraw/:seed_id/:level", must_be_logged_in, function (req, r return res.redirect(req.headers.referer) }) -app.post("/api/tm/start/:seed_id/:level", must_be_administrator, function (req, res) { +app.post("/tm/start/:seed_id/:level", must_be_administrator, function (req, res) { let seed_id = req.params.seed_id | 0 let level = req.params.level | 0 start_tournament_seed(seed_id, level) @@ -4240,23 +4239,23 @@ const SQL_GAME_RATINGS = SQL(` limit 50 `) -app.get("/user-stats/:who_name", must_be_administrator, function (req, res) { +app.get("/stats/user/:who_name", must_be_administrator, function (req, res) { let who = SQL_SELECT_USER_BY_NAME.get(req.params.who_name) if (who) { let stats = SQL_USER_STATS.all(who.user_id, who.user_id) let ratings = SQL_USER_RATINGS.all(who.user_id) - res.render("user_stats.pug", { user: req.user, who, stats, ratings }) + res.render("stats_user.pug", { who, stats, ratings }) } else { return res.status(404).send("Invalid user name.") } }) -app.get("/game-stats/:title_id", must_be_administrator, function (req, res) { +app.get("/stats/title/:title_id", must_be_administrator, function (req, res) { let title_id = req.params.title_id if (title_id in TITLE_TABLE) { let title_name = TITLE_NAME[title_id] let ratings = SQL_GAME_RATINGS.all(title_id) - res.render("game_stats.pug", { user: req.user, title_name, ratings }) + res.render("stats_title.pug", { title_name, ratings }) } else { return res.status(404).send("Invalid title.") } diff --git a/views/change_about.pug b/views/account_change_about.pug index b5e2c84..7a0531e 100644 --- a/views/change_about.pug +++ b/views/account_change_about.pug @@ -10,7 +10,7 @@ html include header article h1 Change profile text - form(method="post" action="/change-about") + form(method="post") p Name: #{user.name} p Mail: #{user.mail} p diff --git a/views/change_mail.pug b/views/account_change_mail.pug index d244e98..4d0605a 100644 --- a/views/change_mail.pug +++ b/views/account_change_mail.pug @@ -11,7 +11,7 @@ html if flash p.error= flash - form(method="post" action="/change-mail") + form(method="post") p Name: #{user.name} p Mail: #{user.mail} p @@ -19,4 +19,8 @@ html br input(type="text" name="newmail" required) p + label Password: + br + input(type="password" name="password" required) + p button(type="submit") Change mail diff --git a/views/change_name.pug b/views/account_change_name.pug index 03763ac..b1845ab 100644 --- a/views/change_name.pug +++ b/views/account_change_name.pug @@ -11,7 +11,7 @@ html if flash p.error= flash - form(method="post" action="/change-name") + form(method="post") p Name: #{user.name} p Mail: #{user.mail} p @@ -19,4 +19,8 @@ html br input(type="text" name="newname" required) p + label Password: + br + input(type="password" name="password" required) + p button(type="submit") Change name diff --git a/views/change_password.pug b/views/account_change_password.pug index 022a67c..ca8e973 100644 --- a/views/change_password.pug +++ b/views/account_change_password.pug @@ -11,7 +11,7 @@ html if flash p.error= flash - form(method="post" action="/change-password") + form(method="post") p Name: #{user.name} p Mail: #{user.mail} p diff --git a/views/delete_account.pug b/views/account_delete.pug index e16e1b6..54c3bc4 100644 --- a/views/delete_account.pug +++ b/views/account_delete.pug @@ -11,7 +11,7 @@ html if flash p.error= flash - form(method="post" action="/delete-account") + form(method="post") p Name: #{user.name} p Mail: #{user.mail} p diff --git a/views/forgot_password.pug b/views/account_forgot_password.pug index 935cae1..0913857 100644 --- a/views/forgot_password.pug +++ b/views/account_forgot_password.pug @@ -15,7 +15,7 @@ html if user p You're already logged in! else - form(method="post" action="/forgot-password") + form(method="post") p label Mail: br diff --git a/views/account_mail_verify.pug b/views/account_mail_verify.pug new file mode 100644 index 0000000..fddf2fa --- /dev/null +++ b/views/account_mail_verify.pug @@ -0,0 +1,32 @@ +//- vim:ts=4:sw=4: +doctype html +html + head + include head + title Verify mail + body + include header + article + h1 Verify mail + if flash + p.error= flash + + p Your mail address is <code>#{user.mail}</code> + + p If the above address is wrong, you can <a href="/account/change-mail">change it</a>! + + if sent_token + p Your token is in the mail! + else + form(method="post" action="/account/mail/verify-send") + p + button(type="submit") Send token + + form(method="post") + p + label Enter your mail verification token here: + br + input(type="text" name="token" size=32 value=token style="font-family:monospace" required) + p + button(type="submit") Verify + diff --git a/views/reset_password.pug b/views/account_reset_password.pug index 2fda418..8707ebc 100644 --- a/views/reset_password.pug +++ b/views/account_reset_password.pug @@ -13,7 +13,7 @@ html p You should have received a password reset token in your mail. - form(method="post" action="/reset-password") + form(method="post") p label Mail: br diff --git a/views/webhook.pug b/views/account_webhook.pug index 20d20ac..b9817ef 100644 --- a/views/webhook.pug +++ b/views/account_webhook.pug @@ -14,7 +14,7 @@ html(lang="en") - var format = webhook && webhook.format || "" - var prefix = webhook && webhook.prefix || "" - form(action="/api/webhook/update" method="post") + form(action="/account/webhook/update" method="post") if webhook && webhook.error p.error ERROR: #{webhook.error} p Webhook URL: @@ -34,7 +34,7 @@ html(lang="en") button(type="submit") Create if webhook - form(action="/api/webhook/delete" method="post") + form(action="/account/webhook/delete" method="post") button(type="submit") Delete h2 Discord Notifications diff --git a/views/search_user.pug b/views/contacts_search.pug index 308cc41..308cc41 100644 --- a/views/search_user.pug +++ b/views/contacts_search.pug diff --git a/views/create-index.pug b/views/create_index.pug index c508be7..c508be7 100644 --- a/views/create-index.pug +++ b/views/create_index.pug diff --git a/views/create.pug b/views/create_title.pug index c4c5c2a..c4c5c2a 100644 --- a/views/create.pug +++ b/views/create_title.pug diff --git a/views/head.pug b/views/head.pug index eec1299..da73d4e 100644 --- a/views/head.pug +++ b/views/head.pug @@ -212,3 +212,36 @@ mixin tourlist(seeds, pools, fin) +poollist(pools, "Active", TM_ICON_ACTIVE) div +poollist(fin, "Finished", TM_ICON_FINISHED) + +mixin userstats(who, ratings) + if (who.move_time_mean !== null) + h3 Response time + div Average response time: #{format_minutes(who.move_time_mean)} + if (who.move_time_q2 !== null) + div Median response time: #{format_minutes(who.move_time_q2)} + if (who.move_time_q1 !== null && who.move_time_q2 !== null) + div Middle half of response times: #{format_minutes(who.move_time_q1)} to #{format_minutes(who.move_time_q3)} + + h3 Timeouts + div Total number of timeouts: #{who.timeout_total} + div Games completed since last timeout: #{who.games_since_timeout} + + if ratings.length > 0 + h3 Most played games + table + thead + tr + th Title + th Count + th Last played + if user && user.user_id === 1 + th Elo + tbody + each row in ratings + tr + td + a.black(href="/" + row.title_id)= row.title_name + td.r= row.count + td.r= row.last + if user && user.user_id === 1 + td.r= row.rating diff --git a/views/login.pug b/views/login.pug index 9a44b44..ec8be48 100644 --- a/views/login.pug +++ b/views/login.pug @@ -19,7 +19,7 @@ html p You need to be logged in to view this page! p Log in below, or <a href="/signup">sign up</a> for a free account. - form(method="post" action="/login") + form(method="post") input(type="hidden" name="redirect" value=redirect) p label Name or mail: @@ -33,4 +33,4 @@ html p button(type="submit") Login p - a(href="/forgot-password") Forgot password + a(href="/account/forgot-password") Forgot password diff --git a/views/profile.pug b/views/profile.pug index 4ecf289..fc9a1d3 100644 --- a/views/profile.pug +++ b/views/profile.pug @@ -7,43 +7,60 @@ html body include header article - h1= SITE_NAME + h1 Profile p Welcome, <a class="black" href="/user/#{user.name}">#{user.name}</a>! + + if who.about + p.box= who.about + + p Member since #{human_date(who.ctime)}. + + +userstats(who, ratings) + + h3 Notifications + p Your mail address is #{user.mail} if ENABLE_MAIL - if !user.is_verified - p ⚠ <a href="/verify-mail">Verify your mail address!</a> + if !mail.is_verified + p ⚠ <a href="/account/mail/verify">Verify your mail address!</a> p You must verify your mail address before you can enable notifications. else - if !user.notify - p <a href="/subscribe">Enable mail notifications</a> - if user.notify - p <a href="/unsubscribe">Disable mail notifications</a> + if !mail.notify + p <a href="/account/mail/subscribe">Enable mail notifications</a> + if mail.notify + p <a href="/account/mail/unsubscribe">Disable mail notifications</a> + else + p.error Mail notifications are disabled on this server. + + if ENABLE_WEBHOOKS + if !webhook + p <a href="/account/webhook">Configure webhook</a> + else if webhook.error + dl + dt <a href="/account/webhook">Configure webhook</a> + dd.error ERROR: #{webhook.error} + else + dl + dt <a href="/account/webhook">Configure webhook</a> + dd= new URL(webhook.url).hostname + else + p.error Webhook notifications are disabled on this server. + + h3 Account p - | <a href="/change-password">Change password</a> + | <a href="/account/change-password">Change password</a> br - | <a href="/change-mail">Change mail address</a> + | <a href="/account/change-mail">Change mail address</a> br - | <a href="/change-name">Change user name</a> + | <a href="/account/change-name">Change user name</a> br - | <a href="/change-about">Change profile text</a> - br - | <a href="/delete-account">Delete account</a> + | <a href="/account/change-about">Change profile text</a> - if ENABLE_WEBHOOKS - if !user.webhook - p <a href="/webhook">Configure webhook</a> - else if user.webhook.error - dl - dt <a href="/webhook">Configure webhook</a> - dd.error ERROR: #{user.webhook.error} - else - dl - dt <a href="/webhook">Configure webhook</a> - dd= new URL(user.webhook.url).hostname + p + | <a href="/account/delete">Delete account</a> p form(action="/logout" method="post") diff --git a/views/signup.pug b/views/signup.pug index 63b9ab3..5dec458 100644 --- a/views/signup.pug +++ b/views/signup.pug @@ -15,7 +15,7 @@ html if user p You're already logged in! else - form(method="post" action="/signup") + form(method="post") p You need to sign up for a free account to play games on #{SITE_NAME_P} p label Name: diff --git a/views/game_stats.pug b/views/stats_title.pug index 692c30b..aed6e63 100644 --- a/views/game_stats.pug +++ b/views/stats_title.pug @@ -19,7 +19,7 @@ html tbody each row in ratings tr - td= row.name + td <a class="black" href="/stats/user/#{row.name}">#{row.name}</a> td.r= row.rating td.r= row.count td.r= row.last diff --git a/views/user_stats.pug b/views/stats_user.pug index 3b201ef..3b201ef 100644 --- a/views/user_stats.pug +++ b/views/stats_user.pug diff --git a/views/info.pug b/views/title.pug index 32837e9..32837e9 100644 --- a/views/info.pug +++ b/views/title.pug diff --git a/views/tm_seed.pug b/views/tm_seed.pug index 45659c5..abf02c1 100644 --- a/views/tm_seed.pug +++ b/views/tm_seed.pug @@ -67,11 +67,11 @@ html if user if queue.find(p => p.user_id === user.user_id) - form(method="post" action="/api/tm/withdraw/" + seed.seed_id + "/" + (ix+1)) + form(method="post" action="/tm/withdraw/" + seed.seed_id + "/" + (ix+1)) button(disabled) Register button(type="submit") Withdraw else if may_register && may_join_seed_level(user.user_id, seed.seed_id, ix+1) - form(method="post" action="/api/tm/register/" + seed.seed_id + "/" + (ix+1)) + form(method="post" action="/tm/register/" + seed.seed_id + "/" + (ix+1)) button(type="submit") Register button(disabled) Withdraw else @@ -81,7 +81,7 @@ html if user.user_id === 1 if queue.length >= seed.pool_size - form(method="post" action="/api/tm/start/" + seed.seed_id + "/" + (ix+1)) + form(method="post" action="/tm/start/" + seed.seed_id + "/" + (ix+1)) button(type="submit") Start else p <a href="/login">Login</a> or <a href="/signup">sign up</a> to register. diff --git a/views/user.pug b/views/user.pug index c3e8925..b5859a2 100644 --- a/views/user.pug +++ b/views/user.pug @@ -24,7 +24,7 @@ html h1= who.name if who.about - p.about= who.about + p.box= who.about p Member since #{human_date(who.ctime)}. p Last seen #{human_date(who.atime)}. @@ -42,37 +42,7 @@ html br a(href="/contacts/add-enemy/"+who.name) Add to blacklist - if (who.move_time_mean !== null) - h3 Response time - div Average response time: #{format_minutes(who.move_time_mean)} - if (who.move_time_q2 !== null) - div Median response time: #{format_minutes(who.move_time_q2)} - if (who.move_time_q1 !== null && who.move_time_q2 !== null) - div Middle half of response times: #{format_minutes(who.move_time_q1)} to #{format_minutes(who.move_time_q3)} - - h3 Timeouts - div Total number of timeouts: #{who.timeout_total} - div Games completed since last timeout: #{who.games_since_timeout} - - if ratings.length > 0 - h3 Most played games - table - thead - tr - th Title - th Count - th Last played - if user && user.user_id === 1 - th Elo - tbody - each row in ratings - tr - td - a.black(href="/" + row.title_id)= row.title_name - td.r= row.count - td.r= row.last - if user && user.user_id === 1 - td.r= row.rating + +userstats(who, ratings) +tourlist(null, active_pools, finished_pools) diff --git a/views/verify_mail.pug b/views/verify_mail.pug deleted file mode 100644 index 01a2e82..0000000 --- a/views/verify_mail.pug +++ /dev/null @@ -1,21 +0,0 @@ -//- vim:ts=4:sw=4: -doctype html -html - head - include head - title Verify mail - body - include header - article - h1 Verify mail - if flash - p.error= flash - - form(method="post" action="/verify-mail") - p A token has been sent to #{user.mail}. - p - label Enter the token here: - br - input(type="text" name="token" size=32 value=token style="font-family:monospace" required) - p - button(type="submit") Verify |