import fs from 'fs'; import {Eta} from 'eta'; import twemoji from 'twemoji'; import Config from './util/config.js'; import countries from './countries.js'; import db from './database.js'; import {get_user} from './user.js'; import {get_user_rank} from './elo.js'; import {get_user_matches, get_leaderboard_page} from './website_api.js'; const eta = new Eta({ autoTrim: ['nl', 'slurp'], cache: true, debug: !Config.IS_PRODUCTION, views: './views', }); const base = fs.readFileSync('views/main.eta', 'utf-8'); const user_not_found_page = base.replace('<%~ it.body %>', 'User not found.'); // TODO better looking page const faq_page = eta.render('faq.eta'); const js_page = eta.render('jspage.eta'); function mini_cache(res) { if (!Config.IS_PRODUCTION) return; // Cache that only lasts 10 seconds, to reduce load while still keeping content fresh res.set('Cache-Control', 'public, max-age=10'); } function get_mode(req) { const ruleset = req.subdomains[0] || 'osu'; if (ruleset == 'osu') { return 0; } else if (ruleset == 'taiko') { return 1; } else if (ruleset == 'catch') { return 2; } else if (ruleset == 'mania') { return 3; } else { throw RULESET_NOT_FOUND; } } function country_code_to_flag_html(country_code) { // Unknown country if (country_code?.length != 2) { return `?`; } const codepoints = country_code.split('').map((char) => 127397 + char.charCodeAt(0)); const emoji = String.fromCodePoint(...codepoints); return twemoji.parse(emoji); } async function render_user_profile(req, res, page_num) { mini_cache(res); const mode = get_mode(req); const user = await get_user(req.params.userId); if (!user) { return res.status(404).end(user_not_found_page); } const rank = get_user_rank(user.user_id, mode); if (!rank) { return res.status(404).end(user_not_found_page); } mini_cache(res); // Dirty hack to make Discord embeds work if (req.get('User-Agent').indexOf('Discordbot') != -1) { return res.end(` hi :) `); } let elo = rank.elo; const elo_evolution = [elo]; const last_90 = db.prepare( `SELECT elo_diff FROM score WHERE user_id = ? AND mode = ? ORDER BY created_at DESC LIMIT 29`, ).all(user.user_id, mode); for (const row of last_90) { elo -= row.elo_diff; elo_evolution.unshift(elo); } const osu_rulesets = ['osu', 'taiko', 'fruits', 'mania']; const matches = await get_user_matches(user.user_id, mode, page_num); return res.end(eta.render('user.eta', { user, rank, matches, elo_evolution, flag: country_code_to_flag_html(user.country_code), country_name: countries[user.country_code] || 'Unknown country', osu_ruleset: osu_rulesets[mode], })); } async function render_leaderboard(req, res, page_num) { mini_cache(res); const data = await get_leaderboard_page(get_mode(req), page_num); for (const player of data.players) { player.flag = country_code_to_flag_html(player.country_code); } return res.end(eta.render('leaderboard.eta', data)); } async function register_routes(app) { const get_page_num = (req) => { let page_num = 1; if (req.params.pageStr.indexOf('page-') == 0) { page_num = parseInt(req.params.pageStr.split('-')[1], 10); if (page_num <= 0 || isNaN(page_num)) { page_num = 1; } } return page_num; }; app.get('/faq/', (req, res) => { mini_cache(res); res.end(faq_page); }); app.get('/lobbies/', (req, res) => { mini_cache(res); res.end(js_page); }); // Login-gated lobby creation app.get('/create-lobby/', (req, res) => { if (req.signedCookies.user) { return res.end(js_page); } else { res.cookie('login_to', `${req.protocol}://${req.hostname}/create-lobby/`, { domain: Config.domain_name, maxAge: 30000, httpOnly: false, secure: Config.IS_PRODUCTION, signed: false, sameSite: 'Strict', }); return res.redirect('/osu_login'); } }); // Login-gated lobby reopening app.get('/reopen-lobby/*', (req, res) => { if (req.signedCookies.user) { return res.end(js_page); } else { res.cookie('login_to', `${req.protocol}://${req.hostname}${req.originalUrl}`, { domain: Config.domain_name, maxAge: 30000, httpOnly: false, secure: Config.IS_PRODUCTION, signed: false, sameSite: 'Strict', }); return res.redirect('/osu_login'); } }); // User pages app.get('/u/:userId/:pageStr/', (req, res) => { const page_num = get_page_num(req); return render_user_profile(req, res, page_num); }); app.get('/u/:userId', (req, res) => { return render_user_profile(req, res, 1); }); // Leaderboard pages app.get('/leaderboard/:pageStr/', (req, res) => { const page_num = get_page_num(req); return render_leaderboard(req, res, page_num); }); app.get('/leaderboard/', (req, res) => { return render_leaderboard(req, res, 1); }); } export { register_routes, };