123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- 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 `<img class="emoji" draggable="false" alt="?" src="/unknown_flag.png" />`;
- }
- 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(`<html>
- <head>
- <meta content="${user.username} - o!RL" property="og:title" />
- <meta content="#${rank.rank_nb} - ${rank.text}" property="og:description" />
- <meta content="https://${req.hostname}/u/${user.user_id}" property="og:url" />
- <meta content="https://s.ppy.sh/a/${user.user_id}" property="og:image" />
- </head>
- <body>hi :)</body>
- </html>`);
- }
- 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,
- };
|