From 162728c3d335c07367d6f6561c96f58eaa53ec88 Mon Sep 17 00:00:00 2001 From: shit <36964527+7ez@users.noreply.github.com> Date: Thu, 26 Aug 2021 22:25:44 +0200 Subject: [PATCH] aochi commits to asahi-web (no way) --- README.md | 56 ++++++++------- blueprints/frontend.py | 128 ++++++++++++++++++--------------- constants/regexes.py | 2 +- ext/config.sample.py | 8 +-- ext/requirements.txt | 4 +- main.py | 8 ++- objects/glob.py | 6 +- objects/privileges.py | 57 +++++++-------- static/js/pages/leaderboard.js | 45 ++++++------ static/js/pages/profile.js | 113 ++++++++++++++--------------- templates/leaderboard.html | 54 ++++++-------- templates/profile.html | 70 +++++++++--------- 12 files changed, 271 insertions(+), 280 deletions(-) diff --git a/README.md b/README.md index 07527dab..a0b5c93f 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,31 @@ + +[![Discord](https://discordapp.com/api/guilds/833325274934411274/widget.png?style=shield)](https://discord.gg/d62tzSYv3z) + Table of Contents ================== - [Table of Contents](#table-of-contents) - - [What is guweb?](#what-is-guweb) - - [Requirements](#requirements) + - [What is asahi-web?](#what-is-asahi-web) - [Setup](#setup) - [Directory Structure](#directory-structure) - - [The team](#the-team) - [The End](#the-end) -What is guweb? +What is asahi-web? ------ -guweb is the front-facing appearance of the osu! server protocol, [gulag](https://github.com/cmyui/gulag)! +asahi-web is the front-facing appearance of the osu! server protocol, [Asahi](https://github.com/tsunyoku/Asahi)! + +It's a project by me, that uses [guweb](https://github.com/Varkaria/guweb)'s source code, but is made to work with Asahi. + Using native async/await syntax written on top of [Quart](https://github.com/pgjones/quart) and -[cmyui's multipurpose library](https://github.com/cmyui/cmyui_pkg), guweb achieves flexability, cleanliness, +[cmyui's multipurpose library](https://github.com/cmyui/cmyui_pkg), asahi-web achieves flexability, cleanliness, and efficiency not seen in other frontend implementations - all while maintaining the simplicity of Python. +A primary goal of asahi-web is to keep our codebase a developer-friendly API, so that +programming remains about the logic and ideas, rather than the code itself. + +gulag-web is written by Yo-ru and Varkaria, but me and other contributors are mainly writing asahi-web. + + Requirements ------ @@ -31,7 +41,7 @@ Setup is relatively simple - these commands should set you right up. Notes: - Ubuntu 20.04 is known to have issues with NGINX and osu! for unknown reasons? -- If you have any difficulties setting up guweb, feel free to join the Discord server at the top of the README, we now have a bit of a community! +- If you have any difficulties setting up asahi-web, feel free to join the Discord server at the top of the README, we now have a bit of a community! ```sh # Install Python >=3.9 and latest version of PIP. @@ -43,9 +53,9 @@ python3.9 get-pip.py && rm get-pip.py # Install MySQL and NGINX. sudo apt install mysql-server nginx -# Clone guweb from GitHub. -git clone https://github.com/varkaria/guweb.git -cd guweb +# Clone asahi-web from GitHub. +git clone https://github.com/7ez/asahi-web.git +cd asahi-web # Initialize and update the submodules. git submodule init && git submodule update @@ -53,18 +63,18 @@ git submodule init && git submodule update # Install requirements from pip. python3.9 -m pip install -r ext/requirements.txt -# Add and configure guweb's NGINX config to your nginx/sites-enabled. -sudo ln -r -s ext/nginx.conf /etc/nginx/sites-enabled/guweb.conf +# Add and configure asahi-web's NGINX config to your nginx/sites-enabled. +sudo ln -r -s ext/nginx.conf /etc/nginx/sites-enabled/asahi-web.conf sudo nano ext/nginx.conf sudo nginx -s reload -# Configure guweb. +# Configure asahi-web. cp ext/config.sample.py config.py nano config.py -# Run guweb. +# Run asahi-web. python3.9 main.py # Run directly to access debug features for development! (Port 5000) -hypercorn main.py # Please run guweb with hypercorn when in production! It will improve performance drastically by disabling all of the debug features a developer would need! (Port 8000) +hypercorn main.py # Please run asahi-web with hypercorn when in production! It will improve performance drastically by disabling all of the debug features a developer would need! (Port 8000) ``` Directory Structure @@ -72,22 +82,14 @@ Directory Structure . ├── blueprints # Modular routes such as the API, Frontend, or Admin Panel. - ├── docs # Markdown files used in guweb's documentation system. - ├── ext # External files from guweb's primary operation. + ├── docs # Markdown files used in asahi-web's documentation system. + ├── ext # External files from asahi-web's primary operation. ├── objects # Code for representing privileges, global objects, and more. - ├── static # Code or content that is not modified or processed by guweb itself. + ├── static # Code or content that is not modified or processed by asahi-web itself. ├── templates # HTML that contains content that is rendered after the page has loaded. ├── admin # Templated content for the admin panel (/admin). ├── settings # Templated content for settings (/settings). - └ ... # Templated content for all of guweb (/). - - -The team ------- -- [Yoru](https://github.com/Yo-ru) | Backend, Grammar Checking [Deprecated] -- [Varkaria](https://github.com/Varkaria) | Frontend, Backend? + └ ... # Templated content for all of asahi-web (/). The End ------ - -Well know that you know everything, why not check out the original code guweb was based off of in [this](https://github.com/yo-ru/gulag-web) i think i should continue this work to finish work? diff --git a/blueprints/frontend.py b/blueprints/frontend.py index 20900898..9f6c46a7 100644 --- a/blueprints/frontend.py +++ b/blueprints/frontend.py @@ -2,7 +2,6 @@ __all__ = () -import bcrypt import hashlib import os import time @@ -12,6 +11,9 @@ from functools import wraps from PIL import Image from pathlib import Path +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.kdf.hkdf import HKDFExpand +from cryptography.hazmat.backends import default_backend as backend from quart import Blueprint from quart import redirect from quart import render_template @@ -76,7 +78,7 @@ async def settings_profile_post(): return await flash('error', 'No changes have been made.', 'settings/profile') if new_name != old_name: - if not session['user_data']['is_donator']: + if not session['user_data']['is_donator'] or not session['user_data']['is_staff']: return await flash('error', 'Username changes are currently a supporter perk.', 'settings/profile') # Usernames must: @@ -138,7 +140,7 @@ async def settings_avatar(): @login_required async def settings_avatar_post(): # constants - AVATARS_PATH = f'{glob.config.path_to_gulag}.data/avatars' + AVATARS_PATH = f'{glob.config.path_to_asahi}.data/avatars' ALLOWED_EXTENSIONS = ['.jpeg', '.jpg', '.png'] avatar = (await request.files).get('avatar') @@ -251,44 +253,48 @@ async def settings_password_post(): return await flash('error', 'Your new password was deemed too simple.', 'settings/password') # cache and other password related information - bcrypt_cache = glob.cache['bcrypt'] - pw_bcrypt = (await glob.db.fetch( - 'SELECT pw_bcrypt ' + pw_cache = glob.cache['pw'] + pw_hash = (await glob.db.fetchval( + 'SELECT pw ' 'FROM users ' 'WHERE id = %s', [session['user_data']['id']]) - )['pw_bcrypt'].encode() + )['pw_bcrypt'].encode('ISO-8859-1').decode('unicode-escape').encode('ISO-8859-1') pw_md5 = hashlib.md5(old_password.encode()).hexdigest().encode() # check old password against db # intentionally slow, will cache to speed up - if pw_bcrypt in bcrypt_cache: - if pw_md5 != bcrypt_cache[pw_bcrypt]: # ~0.1ms + if pw_hash in pw_cache: + if pw_md5 != pw_cache[pw_hash]: # ~0.1ms if glob.config.debug: log(f"{session['user_data']['name']}'s change pw failed - pw incorrect.", Ansi.LYELLOW) return await flash('error', 'Your old password is incorrect.', 'settings/password') else: # ~200ms - if not bcrypt.checkpw(pw_md5, pw_bcrypt): + k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) + try: + k.verify(pw_hash, pw_md5) + except: if glob.config.debug: log(f"{session['user_data']['name']}'s change pw failed - pw incorrect.", Ansi.LYELLOW) return await flash('error', 'Your old password is incorrect.', 'settings/password') # remove old password from cache - if pw_bcrypt in bcrypt_cache: - del bcrypt_cache[pw_bcrypt] + if pw_hash in pw_cache: + del pw_cache[pw_hash] # calculate new md5 & bcrypt pw pw_md5 = hashlib.md5(new_password.encode()).hexdigest().encode() - pw_bcrypt = bcrypt.hashpw(pw_md5, bcrypt.gensalt()) + k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) + pw_hash_new = k.derive(pw_md5).decode('unicode-escape') # update password in cache and db - bcrypt_cache[pw_bcrypt] = pw_md5 + pw_cache[pw_hash_new] = pw_md5 await glob.db.execute( 'UPDATE users ' - 'SET pw_bcrypt = %s ' + 'SET pw = %s ' 'WHERE safe_name = %s', - [pw_bcrypt, utils.get_safe_name(session['user_data']['name'])] + [pw_hash_new, utils.get_safe_name(session['user_data']['name'])] ) # logout @@ -314,7 +320,7 @@ async def profile(id): else: mods = 'vn' - user_data = await glob.db.fetch( + user_data = await glob.db.fetchrow( 'SELECT name, id, priv, country ' 'FROM users ' 'WHERE id = %s', @@ -323,7 +329,7 @@ async def profile(id): # user is banned and we're not staff; render 404 is_staff = 'authenticated' in session and session['user_data']['is_staff'] - if not user_data or not (user_data['priv'] & Privileges.Normal or is_staff): + if not user_data or not (user_data['priv'] & Privileges.Verified or is_staff): return (await render_template('404.html'), 404) user_data['customisation'] = utils.has_profile_customizations(id) @@ -332,10 +338,10 @@ async def profile(id): @frontend.route('/leaderboard') @frontend.route('/lb') -@frontend.route('/leaderboard///') -@frontend.route('/lb///') -async def leaderboard(mode='std', sort='pp', mods='vn'): - return await render_template('leaderboard.html', mode=mode, sort=sort, mods=mods) +@frontend.route('/leaderboard//') +@frontend.route('/lb//') +async def leaderboard(mode='std', mods='vn'): + return await render_template('leaderboard.html', mode=mode, mods=mods) @frontend.route('/login') async def login(): @@ -360,9 +366,9 @@ async def login_post(): return await flash('error', 'Invalid parameters.', 'home') # check if account exists - user_info = await glob.db.fetch( + user_info = await glob.db.fetchrow( 'SELECT id, name, email, priv, ' - 'pw_bcrypt, silence_end ' + 'pw, silence_end ' 'FROM users ' 'WHERE safe_name = %s', [utils.get_safe_name(username)] @@ -376,25 +382,28 @@ async def login_post(): return await flash('error', 'Account does not exist.', 'login') # cache and other related password information - bcrypt_cache = glob.cache['bcrypt'] - pw_bcrypt = user_info['pw_bcrypt'].encode() + pw_cache = glob.cache['pw'] + pw_hash = user_info['pw'].encode('ISO-8859-1').decode('unicode-escape').encode('ISO-8859-1') pw_md5 = hashlib.md5(passwd_txt.encode()).hexdigest().encode() # check credentials (password) against db # intentionally slow, will cache to speed up - if pw_bcrypt in bcrypt_cache: - if pw_md5 != bcrypt_cache[pw_bcrypt]: # ~0.1ms + if pw_hash in pw_cache: + if pw_md5 != pw_cache[pw_hash]: # ~0.1ms if glob.config.debug: log(f"{username}'s login failed - pw incorrect.", Ansi.LYELLOW) return await flash('error', 'Password is incorrect.', 'login') else: # ~200ms - if not bcrypt.checkpw(pw_md5, pw_bcrypt): + k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) + try: + k.verify(pw_md5, pw_hash) + except: if glob.config.debug: log(f"{username}'s login failed - pw incorrect.", Ansi.LYELLOW) return await flash('error', 'Password is incorrect.', 'login') # login successful; cache password for next login - bcrypt_cache[pw_bcrypt] = pw_md5 + pw_cache[pw_hash] = pw_md5 # user not verified; render verify if not user_info['priv'] & Privileges.Verified: @@ -419,8 +428,8 @@ async def login_post(): 'email': user_info['email'], 'priv': user_info['priv'], 'silence_end': user_info['silence_end'], - 'is_staff': user_info['priv'] & Privileges.Staff != 0, - 'is_donator': user_info['priv'] & Privileges.Donator != 0 + 'is_staff': user_info['priv'] & Privileges.Staff, + 'is_donator': user_info['priv'] & Privileges.Supporter } if glob.config.debug: @@ -506,37 +515,38 @@ async def register_post(): # TODO: add correct locking # (start of lock) pw_md5 = hashlib.md5(passwd_txt.encode()).hexdigest().encode() - pw_bcrypt = bcrypt.hashpw(pw_md5, bcrypt.gensalt()) - glob.cache['bcrypt'][pw_bcrypt] = pw_md5 # cache pw + k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) + pw_hash = k.derive(pw_md5).decode('unicode-escape') + glob.cache['pw'][pw_hash] = pw_md5 # cache pw safe_name = utils.get_safe_name(username) # fetch the users' country - if ( - request.headers and - (ip := request.headers.get('X-Real-IP', type=str)) is not None - ): - country = await utils.fetch_geoloc(ip) - else: - country = 'xx' - - async with glob.db.pool.acquire() as conn: - async with conn.cursor() as db_cursor: - # add to `users` table. - await db_cursor.execute( - 'INSERT INTO users ' - '(name, safe_name, email, pw_bcrypt, country, creation_time, latest_activity) ' - 'VALUES (%s, %s, %s, %s, %s, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())', - [username, safe_name, email, pw_bcrypt, country] - ) - user_id = db_cursor.lastrowid - - # add to `stats` table. - await db_cursor.executemany( - 'INSERT INTO stats ' - '(id, mode) VALUES (%s, %s)', - [(user_id, mode) for mode in range(8)] - ) + #if 'CF-Connecting-IP' in request.headers: + # ip = request.headers['CF-Connecting-IP'] + #else: + # ip = request.headers['X-Forwarded-For'].split(',')[0] + + #try: + # country = await utils.fetch_geoloc(ip) + #except: + country = 'PL' + + user = await glob.db.execute( + 'INSERT INTO users ' + '(name, safe_name, email, pw, country, registered_at) ' + 'VALUES (%s, %s, %s, %s, %s, UNIX_TIMESTAMP())', + [username, safe_name, email, pw_hash, country] + ) + + user_id = user + + # add to `stats` table. + await glob.db.execute( + 'INSERT INTO stats ' + '(id) VALUES (%s)', + user_id + ) # (end of lock) diff --git a/constants/regexes.py b/constants/regexes.py index f4a7da49..2bfd231b 100644 --- a/constants/regexes.py +++ b/constants/regexes.py @@ -3,4 +3,4 @@ import re username = re.compile(r'^[\w \[\]-]{2,15}$') -email = re.compile(r'^[^@\s]{1,200}@[^@\s\.]{1,30}\.[^@\.\s]{1,24}$') +email = re.compile(r'^[^@\s]{1,200}@[^@\s\.]{1,30}(?:\.[^@\.\s]{2,24})+$') diff --git a/ext/config.sample.py b/ext/config.sample.py index 313417a6..defa2095 100644 --- a/ext/config.sample.py +++ b/ext/config.sample.py @@ -21,8 +21,8 @@ 'password': 'changeme', } -# path to gulag root (must have leading and following slash) -path_to_gulag = '/path/to/gulag/' +# path to asahi root (must have leading and following slash) +path_to_asahi = '/path/to/asahi/' # enable debug (disable when in production to improve performance) debug = False @@ -41,8 +41,8 @@ # enable registration registration = True -# social links (used throughout guweb) -github = 'https://github.com/varkaria/guweb' +# social links (used throughout asahi-web) +github = 'https://github.com/7ez/asahi-web' discord_server = 'https://discord.com/invite/Y5uPvcNpD9' youtube = 'https://youtube.com/' twitter = 'https://twitter.com/' diff --git a/ext/requirements.txt b/ext/requirements.txt index 495f0894..65dfc1e6 100644 --- a/ext/requirements.txt +++ b/ext/requirements.txt @@ -1,10 +1,10 @@ cmyui quart -bcrypt +cryptography +fatFuckSQL aiomysql aiohttp mysql-connector -asyncpg orjson timeago markdown2 diff --git a/main.py b/main.py index 452ff494..0389a719 100755 --- a/main.py +++ b/main.py @@ -12,12 +12,15 @@ from cmyui.logging import Ansi from cmyui.logging import log -from cmyui.mysql import AsyncSQLPool from cmyui.version import Version +from fatFuckSQL import fatFawkSQL + from objects import glob app = Quart(__name__) +app.config["TEMPLATES_AUTO_RELOAD"] = True +app.config["PERMANENT_SESSION_LIFETIME"] = 315400000 version = Version(1, 3, 0) @@ -27,8 +30,7 @@ @app.before_serving async def mysql_conn() -> None: - glob.db = AsyncSQLPool() - await glob.db.connect(glob.config.mysql) + glob.db = await fatFawkSQL.connect(**glob.config.mysql) log('Connected to MySQL!', Ansi.LGREEN) @app.before_serving diff --git a/objects/glob.py b/objects/glob.py index e384e02c..bde18a81 100644 --- a/objects/glob.py +++ b/objects/glob.py @@ -8,13 +8,13 @@ if TYPE_CHECKING: from aiohttp import ClientSession - from cmyui.mysql import AsyncSQLPool + from fatFuckSQL import fatFawkSQL from cmyui.version import Version -db: 'AsyncSQLPool' +db: 'fatFawkSQL' http: 'ClientSession' version: 'Version' cache = { - 'bcrypt': {} + 'pw': {} } diff --git a/objects/privileges.py b/objects/privileges.py index 2a541a35..96755efe 100644 --- a/objects/privileges.py +++ b/objects/privileges.py @@ -1,34 +1,29 @@ -# -*- coding: utf-8 -*- - from enum import IntFlag -from enum import unique - -__all__ = ('Privileges') +from typing import Optional -@unique class Privileges(IntFlag): - """Server side user privileges.""" - - # privileges intended for all normal players. - Normal = 1 << 0 # is an unbanned player. - Verified = 1 << 1 # has logged in to the server in-game. - - # has bypass to low-ceiling anticheat measures (trusted). - Whitelisted = 1 << 2 - - # donation tiers, receives some extra benefits. - Supporter = 1 << 4 - Premium = 1 << 5 - - # notable users, receives some extra benefits. - Alumni = 1 << 7 - - # staff permissions, able to manage server state. - Tournament = 1 << 10 # able to manage match state without host. - Nominator = 1 << 11 # able to manage maps ranked status. - Mod = 1 << 12 # able to manage users (level 1). - Admin = 1 << 13 # able to manage users (level 2). - Dangerous = 1 << 14 # able to manage full server state. - - Donator = Supporter | Premium - Staff = Mod | Admin | Dangerous + Normal = 1 << 0 + Verified = 1 << 1 + Supporter = 1 << 2 + + Nominator = 1 << 3 + Admin = 1 << 4 + Developer = 1 << 5 + Owner = 1 << 6 + + Restricted = 1 << 7 + Banned = 1 << 8 + + BypassAnticheat = 1 << 9 # can bypass anticheat checks + Frozen = 1 << 10 + Whitelisted = 1 << 11 # can bypass pp cap + + Staff = Nominator | Admin | Developer | Owner + Manager = Admin | Developer | Owner + Master = Normal | Verified | Supporter | Nominator | Admin | Developer | Owner | BypassAnticheat | Whitelisted + Disallowed = Restricted | Banned + + @classmethod + def get(cls, name) -> Optional['Privileges']: + if name in cls.__members__: + return cls[name] \ No newline at end of file diff --git a/static/js/pages/leaderboard.js b/static/js/pages/leaderboard.js index ac2f9f90..4a2aed2f 100644 --- a/static/js/pages/leaderboard.js +++ b/static/js/pages/leaderboard.js @@ -7,7 +7,6 @@ new Vue({ boards : {}, mode : 'std', mods : 'vn', - sort : 'pp', load : false, no_player : false, // soon }; @@ -17,25 +16,23 @@ new Vue({ this.LoadLeaderboard(sort, mode, mods); }, methods: { - LoadData(mode, mods, sort) { + LoadData(mode, mods) { this.$set(this, 'mode', mode); this.$set(this, 'mods', mods); - this.$set(this, 'sort', sort); }, - LoadLeaderboard(sort, mode, mods) { + LoadLeaderboard(mode, mods) { if (window.event) window.event.preventDefault(); - - window.history.replaceState('', document.title, `/leaderboard/${this.mode}/${this.sort}/${this.mods}`); + this.$set(this, 'mode', mode); this.$set(this, 'mods', mods); - this.$set(this, 'sort', sort); this.$set(this, 'load', true); - this.$axios.get(`${window.location.protocol}//osu.${domain}/api/get_leaderboard`, { params: { - mode: this.StrtoGulagInt(), - sort: this.sort + window.history.replaceState('', document.title, `/leaderboard/${this.mode}/${this.mods}`); + this.$axios.get(`${window.location.protocol}//api.${domain}/get_leaderboard`, { params: { + mode: this.StrtoModeInt(), + rx: this.StrtoModInt() }}).then(res => { - this.boards = res.data.leaderboard; + this.boards = res.data; this.$set(this, 'load', false); }); }, @@ -59,17 +56,21 @@ new Vue({ } return x1 + x2; }, - StrtoGulagInt() { - switch (this.mode + "|" + this.mods) { - case 'std|vn': return 0; - case 'taiko|vn': return 1; - case 'catch|vn': return 2; - case 'mania|vn': return 3; - case 'std|rx': return 4; - case 'taiko|rx': return 5; - case 'catch|rx': return 6; - case 'std|ap': return 7; - default: return -1; + StrtoModeInt() { + switch (this.mode) { + case 'std': return 0; + case 'taiko': return 1; + case 'catch': return 2; + case 'mania': return 3; + default: return 0; + } + }, + StrtoModInt() { + switch (this.mods) { + case 'vn': return 0; + case 'rx': return 1; + case 'ap': return 2; + default: return 0; } }, }, diff --git a/static/js/pages/profile.js b/static/js/pages/profile.js index 8b88d343..97c2b199 100644 --- a/static/js/pages/profile.js +++ b/static/js/pages/profile.js @@ -37,20 +37,23 @@ new Vue({ } } }, - status: {} + status: { + out: [{}], + load: true + } }, mode: mode, mods: mods, - modegulag: 0, + modeasahi: 0, userid: userid }; }, created() { // starting a page - this.modegulag = this.StrtoGulagInt(); + this.mode = this.StrtoModeInt(); this.LoadProfileData(); + this.LoadPlayerStatus(); this.LoadAllofdata(); - this.LoadUserStatus(); }, methods: { LoadAllofdata() { @@ -60,24 +63,38 @@ new Vue({ }, LoadProfileData() { this.$set(this.data.stats, 'load', true); - this.$axios.get(`${window.location.protocol}//osu.${domain}/api/get_player_info`, { + this.$axios.get(`${window.location.protocol}//api.${domain}/player`, { params: { id: this.userid, - scope: 'all' + mode: this.StrtoModeInt(), + rx: this.StrtoModInt() } }) .then(res => { - this.$set(this.data.stats, 'out', res.data.player.stats); + this.$set(this.data.stats, 'out', res.data.stats); this.data.stats.load = false; }); }, + LoadPlayerStatus() { + this.$set(this.data.status, 'load', true); + this.$axios.get(`${window.location.protocol}//api.${domain}/player_status`, { + params: { + id: this.userid + } + }) + .then(res => { + this.$set(this.data.status, 'out', res.data.status); + this.data.status.load = false; + }); + }, LoadScores(sort) { this.$set(this.data.scores[`${sort}`], 'load', true); - this.$axios.get(`${window.location.protocol}//osu.${domain}/api/get_player_scores`, { + this.$axios.get(`${window.location.protocol}//api.${domain}/player_scores`, { params: { id: this.userid, - mode: this.StrtoGulagInt(), - scope: sort, + mode: this.StrtoModeInt(), + rx: this.StrtoModInt(), + type: sort, limit: this.data.scores[`${sort}`].more.limit } }) @@ -89,11 +106,12 @@ new Vue({ }, LoadMostBeatmaps() { this.$set(this.data.maps.most, 'load', true); - this.$axios.get(`${window.location.protocol}//osu.${domain}/api/get_player_most_played`, { + this.$axios.get(`${window.location.protocol}//api.${domain}/player_most_played`, { params: { id: this.userid, - mode: this.StrtoGulagInt(), - limit: this.data.maps.most.more.limit + mode: this.StrtoModeInt(), + limit: this.data.maps.most.more.limit, + rx: this.StrtoModInt() } }) .then(res => { @@ -102,21 +120,6 @@ new Vue({ this.data.maps.most.more.full = this.data.maps.most.out.length != this.data.maps.most.more.limit; }); }, - LoadUserStatus() { - this.$axios.get(`${window.location.protocol}//osu.${domain}/api/get_player_status`, { - params: { - id: this.userid - } - }) - .then(res => { - this.$set(this.data, 'status', res.data.player_status) - }) - .catch(function (error) { - clearTimeout(loop); - console.log(error); - }); - loop = setTimeout(this.LoadUserStatus, 5000); - }, ChangeModeMods(mode, mods) { if (window.event) window.event.preventDefault(); @@ -124,7 +127,7 @@ new Vue({ this.mode = mode; this.mods = mods; - this.modegulag = this.StrtoGulagInt(); + this.modegulag = this.StrtoModeInt(); this.data.scores.recent.more.limit = 5 this.data.scores.best.more.limit = 5 this.data.maps.most.more.limit = 6 @@ -146,31 +149,31 @@ new Vue({ } }, actionIntToStr(d) { - switch (d.action) { + switch (d[`action`]) { case 0: return 'Idle: 🔍 Song Select'; case 1: return '🌙 AFK'; case 2: - return `Playing: 🎶 ${d.info_text}`; + return `Playing: 🎶 ${d[`info`]}`; case 3: - return `Editing: 🔨 ${d.info_text}`; + return `Editing: 🔨 ${d[`info`]}`; case 4: - return `Modding: 🔨 ${d.info_text}`; + return `Modding: 🔨 ${d[`info`]}`; case 5: return 'In Multiplayer: Song Select'; case 6: - return `Watching: 👓 ${d.info_text}`; + return `Watching: 👓 ${d[`info`]}`; // 7 not used case 8: - return `Testing: 🎾 ${d.info_text}`; + return `Testing: 🎾 ${d[`info`]}`; case 9: - return `Submitting: 🧼 ${d.info_text}`; + return `Submitting: 🧼 ${d[`info`]}`; // 10 paused, never used case 11: return 'Idle: 🏢 In multiplayer lobby'; case 12: - return `In Multiplayer: Playing 🌍 ${d.info_text} 🎶`; + return `In Multiplayer: Playing 🌍 ${d[`info`]} 🎶`; case 13: return 'Idle: 🔍 Searching for beatmaps in osu!direct'; default: @@ -195,28 +198,6 @@ new Vue({ var mDisplay = `${Math.floor(seconds % 3600 / 60)}m `; return dDisplay + hDisplay + mDisplay; }, - StrtoGulagInt() { - switch (this.mode + "|" + this.mods) { - case 'std|vn': - return 0; - case 'taiko|vn': - return 1; - case 'catch|vn': - return 2; - case 'mania|vn': - return 3; - case 'std|rx': - return 4; - case 'taiko|rx': - return 5; - case 'catch|rx': - return 6; - case 'std|ap': - return 7; - default: - return -1; - } - }, StrtoModeInt() { switch (this.mode) { case 'std': @@ -227,6 +208,20 @@ new Vue({ return 2; case 'mania': return 3; + default: + return 0; + } + }, + StrtoModInt() { + switch (this.mods) { + case 'vn': + return 0; + case 'rx': + return 1; + case 'ap': + return 2; + default: + return 0; } }, }, diff --git a/templates/leaderboard.html b/templates/leaderboard.html index bc304cca..cd43f722 100644 --- a/templates/leaderboard.html +++ b/templates/leaderboard.html @@ -20,85 +20,71 @@
- PP - Score -
-
Vanilla + @click="LoadLeaderboard(mode, 'vn')">Vanilla Relax + @click="LoadLeaderboard(mode, 'rx')">Relax Autopilot + @click="LoadLeaderboard(mode, 'ap')">Autopilot
-
+
- + - + - - - + - diff --git a/templates/profile.html b/templates/profile.html index 23c5d5e3..504dcffd 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -9,26 +9,26 @@
-
+
-
- <% addCommas(map.score) %> / <% map.max_combo %>x + <% addCommas(map[`score`]) %>x
@@ -38,16 +38,16 @@
- <% map.pp.toFixed() %>pp + <% map[`pp`].toFixed() %>pp
accuracy:  - <% map.acc.toFixed(2) %>% + <% map[`acc`].toFixed(2) %>%
- <% map.grade.replace("X", "SS" ).replace("H", "" ) %> + <% map[`grade`].replace("X", "SS" ).replace("H", "" ) %>
@@ -84,16 +84,16 @@

No scores available

@@ -296,13 +296,13 @@

SS
- <% data.stats.out[modegulag].x_count %> + <% data.stats.out.x_count %>
SS
- <% data.stats.out[modegulag].xh_count %> + <% data.stats.out.xh_count %>
@@ -310,19 +310,19 @@

S
- <% data.stats.out[modegulag].s_count %> + <% data.stats.out.s_count %>
S
- <% data.stats.out[modegulag].sh_count %> + <% data.stats.out.sh_count %>
A
- <% data.stats.out[modegulag].a_count %> + <% data.stats.out.a_count %>

Rank - <% (sort=='pp' ? "PP" : "" ) %> - <% (sort=='rscore' ? "Score" : "" ) %> - PP Accuracy Playcount Max Combo
#<% index + 1 %> +
#<% user[`rank`] %> -
- <% flags[user.country.toUpperCase()] %> + <% flags[user[`country`].toUpperCase()] %>
- - <% user.name %> + + <% user[`name`] %>
- <% (sort=='pp' ? user.pp+'pp' : "" ) %> - <% (sort=='rscore' ? scoreFormat(user.rscore) : "" ) %> - - <% user.acc.toFixed(2) %>% + <% user[`pp`] %>pp - <% addCommas(user.plays) %> + <% user[`acc`].toFixed(2) %>% - <% addCommas(user.max_combo) %> + <% addCommas(user[`playcount`]) %>
PP - <% addCommas(data.stats.out[modegulag].pp) %> + <% addCommas(data.stats.out.pp) %>
Ranked score - <% addCommas(data.stats.out[modegulag].rscore) %> + <% addCommas(data.stats.out.rscore) %>
Total score - <% addCommas(data.stats.out[modegulag].tscore) %> + <% addCommas(data.stats.out.tscore) %>
Max combo - <% addCommas(data.stats.out[modegulag].max_combo) %> + <% addCommas(data.stats.out.max_combo) %>
Playcount - <% addCommas(data.stats.out[modegulag].plays) %> + <% addCommas(data.stats.out.pc) %>
Playtime - <% secondsToDhm(data.stats.out[modegulag].playtime) %> + <% secondsToDhm(data.stats.out.playtime) %>
Accuracy - <% data.stats.out[modegulag].acc %>% + <% data.stats.out.acc.toFixed(2) %>%