[Enhance] Add limiter & Building API

- Add a custom limiter and use it for limiting users' download instead of using database
> So add a requirement `limits`.

- Fix a character's value
- Change the email max length to 64
- Change something about API's roles and powers
- Add an API endpoint for getting users' roles and powers
This commit is contained in:
Lost-MSth
2022-10-12 15:27:45 +08:00
parent a04df8bba6
commit 68a83a29d2
13 changed files with 140 additions and 104 deletions

View File

@@ -99,7 +99,7 @@ It is just so interesting. What it can do is under exploration.
## 运行环境与依赖 Running environment and requirements ## 运行环境与依赖 Running environment and requirements
- Windows/Linux/Mac OS/Android... - Windows/Linux/Mac OS/Android...
- Python 3 - Python 3
- Flask module >= 2.0, Cryptography module >= 35.0.0 - Flask module >= 2.0, Cryptography module >= 35.0.0, limits >= 2.7.0
- Charles, IDA, proxy app... (optional) - Charles, IDA, proxy app... (optional)
<!-- <!--

View File

@@ -26,10 +26,7 @@ def role_required(request, powers=[]):
user = APIUser() user = APIUser()
if Config.API_TOKEN == request.headers['Token'] and Config.API_TOKEN != '': if Config.API_TOKEN == request.headers['Token'] and Config.API_TOKEN != '':
user.user_id = 0 user.set_role_system()
elif powers == []:
# 无powers则非本地权限API_TOKEN规定的无法访问
return error_return(NoAccess('No permission', api_error_code=-1), 403)
else: else:
with Connect() as c: with Connect() as c:
try: try:
@@ -38,7 +35,7 @@ def role_required(request, powers=[]):
request.headers['Token']) request.headers['Token'])
user.select_role_and_powers() user.select_role_and_powers()
if not any([y in [x.power_name for x in user.role.powers] for y in powers]): if not any(user.role.has_power(y) for y in powers):
return error_return(NoAccess('No permission', api_error_code=-1), 403) return error_return(NoAccess('No permission', api_error_code=-1), 403)
except ArcError as e: except ArcError as e:
return error_return(e, 401) return error_return(e, 401)

View File

@@ -2,6 +2,7 @@ from core.error import InputError, NoAccess, NoData
from core.score import Potential, UserScoreList from core.score import Potential, UserScoreList
from core.sql import Connect, Query, Sql from core.sql import Connect, Query, Sql
from core.user import UserInfo, UserRegister from core.user import UserInfo, UserRegister
from core.api_user import APIUser
from flask import Blueprint, request from flask import Blueprint, request
from .api_auth import api_try, request_json_handle, role_required from .api_auth import api_try, request_json_handle, role_required
@@ -15,7 +16,7 @@ bp = Blueprint('users', __name__, url_prefix='/users')
@role_required(request, ['change']) @role_required(request, ['change'])
@request_json_handle(request, ['name', 'password', 'email']) @request_json_handle(request, ['name', 'password', 'email'])
@api_try @api_try
def users_post(data, _): def users_post(data, user):
'''注册一个用户''' '''注册一个用户'''
with Connect() as c: with Connect() as c:
new_user = UserRegister(c) new_user = UserRegister(c)
@@ -67,7 +68,7 @@ def users_user_get(user, user_id):
if user_id <= 0: if user_id <= 0:
return error_return(InputError(api_error_code=-4)) return error_return(InputError(api_error_code=-4))
# 查别人需要select权限 # 查别人需要select权限
if user_id != user.user_id and user.user_id != 0 and not user.role.has_power('select'): if user_id != user.user_id and not user.role.has_power('select'):
return error_return(NoAccess('No permission', api_error_code=-1), 403) return error_return(NoAccess('No permission', api_error_code=-1), 403)
with Connect() as c: with Connect() as c:
@@ -83,7 +84,7 @@ def users_user_b30_get(user, user_id):
if user_id <= 0: if user_id <= 0:
return error_return(InputError(api_error_code=-4)) return error_return(InputError(api_error_code=-4))
# 查别人需要select权限 # 查别人需要select权限
if user_id != user.user_id and user.user_id != 0 and not user.role.has_power('select'): if user_id != user.user_id and not user.role.has_power('select'):
return error_return(NoAccess('No permission', api_error_code=-1), 403) return error_return(NoAccess('No permission', api_error_code=-1), 403)
with Connect() as c: with Connect() as c:
@@ -105,7 +106,7 @@ def users_user_best_get(data, user, user_id):
if user_id <= 0: if user_id <= 0:
return error_return(InputError(api_error_code=-4)) return error_return(InputError(api_error_code=-4))
# 查别人需要select权限 # 查别人需要select权限
if user_id != user.user_id and user.user_id != 0 and not user.role.has_power('select'): if user_id != user.user_id and not user.role.has_power('select'):
return error_return(NoAccess('No permission', api_error_code=-1), 403) return error_return(NoAccess('No permission', api_error_code=-1), 403)
with Connect() as c: with Connect() as c:
@@ -125,9 +126,30 @@ def users_user_r30_get(user, user_id):
if user_id <= 0: if user_id <= 0:
return error_return(InputError(api_error_code=-4)) return error_return(InputError(api_error_code=-4))
# 查别人需要select权限 # 查别人需要select权限
if user_id != user.user_id and user.user_id != 0 and not user.role.has_power('select'): if user_id != user.user_id and not user.role.has_power('select'):
return error_return(NoAccess('No permission', api_error_code=-1), 403) return error_return(NoAccess('No permission', api_error_code=-1), 403)
with Connect() as c: with Connect() as c:
p = Potential(c, UserInfo(c, user_id)) p = Potential(c, UserInfo(c, user_id))
return success_return({'user_id': user_id, 'r10_ptt': p.recent_10 / 10, 'data': p.recent_30_to_dict_list()}) return success_return({'user_id': user_id, 'r10_ptt': p.recent_10 / 10, 'data': p.recent_30_to_dict_list()})
@bp.route('/<int:user_id>/role', methods=['GET'])
@role_required(request, ['select', 'select_me'])
@api_try
def users_user_role_get(user, user_id):
'''查询用户role和powers'''
if user_id <= 0:
return error_return(InputError(api_error_code=-4))
if user_id == user.user_id:
return success_return({'user_id': user.user_id, 'role': user.role.role_id, 'powers': [i.power_id for i in user.role.powers]})
# 查别人需要select权限
if not user.role.has_power('select'):
return error_return(NoAccess('No permission', api_error_code=-1), 403)
with Connect() as c:
x = APIUser(c, user_id)
x.select_role_and_powers()
return success_return({'user_id': x.user_id, 'role': x.role.role_id, 'powers': [i.power_id for i in x.role.powers]})

View File

@@ -9,50 +9,40 @@ from .user import UserOnline
class Power: class Power:
def __init__(self, c=None): def __init__(self, c=None):
self.c = c self.c = c
self.power_id: int = None self.power_id: str = None
self.power_name: str = None
self.caption: str = None self.caption: str = None
@classmethod @classmethod
def from_dict(cls, d: dict, c=None) -> 'Power': def from_dict(cls, d: dict, c=None) -> 'Power':
p = cls(c) p = cls(c)
p.power_id = d['power_id'] p.power_id = d['power_id']
p.power_name = d['power_name']
p.caption = d['caption'] p.caption = d['caption']
return p return p
def select_from_name(self, power_name: str) -> 'Power':
pass
class Role: class Role:
def __init__(self, c=None): def __init__(self, c=None):
self.c = c self.c = c
self.role_id: int = None self.role_id: str = None
self.role_name: str = None
self.caption: str = None self.caption: str = None
self.powers: list = None self.powers: list = None
def has_power(self, power_name: str) -> bool: def has_power(self, power_id: str) -> bool:
'''判断role是否有power''' '''判断role是否有power'''
for i in self.powers: return any(power_id == i.power_id for i in self.powers)
if i.power_name == power_name:
return True
return False
def select_from_id(self, role_id: int = None) -> 'Role': def select_from_id(self, role_id: int = None) -> 'Role':
'''用role_id查询role''' '''用role_id查询role'''
if role_id is not None: if role_id is not None:
self.role_id = role_id self.role_id = role_id
self.c.execute('''select role_name, caption from role where role_id = :a''', self.c.execute('''select caption from role where role_id = :a''',
{'a': self.role_id}) {'a': self.role_id})
x = self.c.fetchone() x = self.c.fetchone()
if x is None: if x is None:
raise NoData('The role `%s` does not exist.' % raise NoData('The role `%s` does not exist.' %
self.role_id, api_error_code=-200) self.role_id, api_error_code=-200)
self.role_name = x[0] self.caption = x[0]
self.caption = x[1]
return self return self
def select_powers(self) -> None: def select_powers(self) -> None:
@@ -63,7 +53,7 @@ class Role:
x = self.c.fetchall() x = self.c.fetchall()
for i in x: for i in x:
self.powers.append(Power.from_dict( self.powers.append(Power.from_dict(
{'power_id': i[0], 'power_name': i[1], 'caption': i[2]}, self.c)) {'power_id': i[0], 'caption': i[1]}, self.c))
class APIUser(UserOnline): class APIUser(UserOnline):
@@ -74,6 +64,13 @@ class APIUser(UserOnline):
self.ip: str = None self.ip: str = None
def set_role_system(self) -> None:
'''设置为最高权限用户API接口'''
self.user_id = 0
self.role = Role(self.c)
self.role.role_id = 'system'
self.role.select_powers()
def select_role(self) -> None: def select_role(self) -> None:
'''查询user的role''' '''查询user的role'''
self.c.execute('''select role_id from user_role where user_id = :a''', self.c.execute('''select role_id from user_role where user_id = :a''',
@@ -82,10 +79,9 @@ class APIUser(UserOnline):
self.role = Role(self.c) self.role = Role(self.c)
if x is None: if x is None:
# 默认role为user # 默认role为user
self.role.role_id = 1 self.role.role_id = 'user'
else: else:
self.role.role_id = int(x[0]) self.role.role_id = x[0]
self.role.select_from_id()
def select_role_and_powers(self) -> None: def select_role_and_powers(self) -> None:
'''查询user的role以及role的powers''' '''查询user的role以及role的powers'''

View File

@@ -7,6 +7,7 @@ from flask import url_for
from .constant import Constant from .constant import Constant
from .error import NoAccess from .error import NoAccess
from .limiter import ArcLimiter
from .user import User from .user import User
from .util import get_file_md5, md5 from .util import get_file_md5, md5
@@ -50,6 +51,9 @@ class UserDownload:
properties: `user` - `User`类或子类的实例 properties: `user` - `User`类或子类的实例
''' '''
limiter = ArcLimiter(
str(Constant.DOWNLOAD_TIMES_LIMIT) + '/day', 'download')
def __init__(self, c=None, user=None) -> None: def __init__(self, c=None, user=None) -> None:
self.c = c self.c = c
self.user = user self.user = user
@@ -60,19 +64,13 @@ class UserDownload:
self.token: str = None self.token: str = None
self.token_time: int = None self.token_time: int = None
def clear_user_download(self) -> None:
self.c.execute(
'''delete from user_download where user_id = :a and time <= :b''', {'a': self.user.user_id, 'b': int(time()) - 24*3600})
@property @property
def is_limited(self) -> bool: def is_limited(self) -> bool:
'''是否达到用户最大下载量''' '''是否达到用户最大下载量'''
if self.user is None: if self.user is None:
self.select_for_check() self.select_for_check()
self.c.execute(
'''select count(*) from user_download where user_id = :a''', {'a': self.user.user_id}) return not self.limiter.test(str(self.user.user_id))
y = self.c.fetchone()
return y is not None and y[0] > Constant.DOWNLOAD_TIMES_LIMIT
@property @property
def is_valid(self) -> bool: def is_valid(self) -> bool:
@@ -81,10 +79,9 @@ class UserDownload:
self.select_for_check() self.select_for_check()
return int(time()) - self.token_time <= Constant.DOWNLOAD_TIME_GAP_LIMIT return int(time()) - self.token_time <= Constant.DOWNLOAD_TIME_GAP_LIMIT
def insert_user_download(self) -> None: def download_hit(self) -> bool:
'''记录下载信息''' '''下载次数+1返回成功与否bool值'''
self.c.execute('''insert into user_download values(:a,:b,:c)''', { return self.limiter.hit(str(self.user.user_id))
'a': self.user.user_id, 'c': self.token, 'b': int(time())})
def select_for_check(self) -> None: def select_for_check(self) -> None:
'''利用token、song_id、file_name查询其它信息''' '''利用token、song_id、file_name查询其它信息'''
@@ -93,7 +90,8 @@ class UserDownload:
x = self.c.fetchone() x = self.c.fetchone()
if not x: if not x:
raise NoAccess('The token `%s` is not valid.' % self.token, status=403) raise NoAccess('The token `%s` is not valid.' %
self.token, status=403)
self.user = User() self.user = User()
self.user.user_id = x[0] self.user.user_id = x[0]
self.token_time = x[1] self.token_time = x[1]

View File

@@ -0,0 +1,28 @@
from limits import parse, strategies
from limits.storage import storage_from_string
class ArcLimiter:
storage = storage_from_string("memory://")
strategy = strategies.FixedWindowRateLimiter(storage)
def __init__(self, limit: str = None, namespace: str = None):
self._limit = None
self.limit = limit
self.namespace = namespace
@property
def limit(self):
return self._limit
@limit.setter
def limit(self, value):
if value is None:
return
self._limit = parse(value)
def hit(self, key: str, cost: int = 1) -> bool:
return self.strategy.hit(self.limit, self.namespace, key, cost=cost)
def test(self, key: str) -> bool:
return self.strategy.test(self.limit, self.namespace, key)

View File

@@ -76,7 +76,7 @@ class UserRegister(User):
def set_email(self, email: str): def set_email(self, email: str):
# 邮箱格式懒得多判断 # 邮箱格式懒得多判断
if 4 <= len(email) <= 32 and '@' in email and '.' in email: if 4 <= len(email) <= 64 and '@' in email and '.' in email:
self.c.execute( self.c.execute(
'''select exists(select * from user where email = :email)''', {'email': email}) '''select exists(select * from user where email = :email)''', {'email': email})
if self.c.fetchone() == (0,): if self.c.fetchone() == (0,):

View File

@@ -4,7 +4,7 @@ import json
# 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用 # 数据库初始化文件删掉arcaea_database.db文件后运行即可谨慎使用
ARCAEA_SERVER_VERSION = 'v2.10.0' ARCAEA_SERVER_VERSION = 'v2.10.0.1'
def main(path='./'): def main(path='./'):
@@ -207,11 +207,11 @@ def main(path='./'):
time int, time int,
primary key(user_id, song_id, file_name) primary key(user_id, song_id, file_name)
);''') );''')
c.execute('''create table if not exists user_download(user_id int, # c.execute('''create table if not exists user_download(user_id int,
time int, # time int,
token text, # token text,
primary key(user_id, time, token) # primary key(user_id, time, token)
);''') # );''')
c.execute('''create table if not exists item(item_id text, c.execute('''create table if not exists item(item_id text,
type text, type text,
is_available int, is_available int,
@@ -283,20 +283,18 @@ def main(path='./'):
primary key(code, item_id, type) primary key(code, item_id, type)
);''') );''')
c.execute('''create table if not exists role(role_id int primary key, c.execute('''create table if not exists role(role_id text primary key,
role_name text,
caption text caption text
);''') );''')
c.execute('''create table if not exists user_role(user_id int, c.execute('''create table if not exists user_role(user_id int,
role_id int, role_id text,
primary key(user_id, role_id) primary key(user_id, role_id)
);''') );''')
c.execute('''create table if not exists power(power_id int primary key, c.execute('''create table if not exists power(power_id text primary key,
power_name text,
caption text caption text
);''') );''')
c.execute('''create table if not exists role_power(role_id int, c.execute('''create table if not exists role_power(role_id text,
power_id int, power_id text,
primary key(role_id, power_id) primary key(role_id, power_id)
);''') );''')
c.execute('''create table if not exists api_login(user_id int, c.execute('''create table if not exists api_login(user_id int,
@@ -365,22 +363,22 @@ def main(path='./'):
48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 38, 30, 49, 15, 34, 45, 45, 38, 67, 120, 44, 33] 48, 65, 45, 55, 44, 25, 46, 44, 33, 45, 45, 37, 25, 27, 50, 20, 45, 63, 21, 47, 61, 47, 65, 80, 38, 30, 49, 15, 34, 45, 45, 38, 67, 120, 44, 33]
frag20 = [78, 80, 90, 75, 70, 79, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52, frag20 = [78, 80, 90, 75, 70, 79, 70, 79, 65, 40, 50, 80, 90, 82, 0, 61, 67, 92, 85, 50, 86, 52,
65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50, 34, 85] 65, 85, 67, 88, 64, 0.5, 95, 70, 95, 50, 80, 87, 71, 50, 85, 0, 80, 75, 50, 70, 70, 90, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50, 35, 85]
prog20 = [61, 80, 70, 75, 90, 70, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73, prog20 = [61, 80, 70, 75, 90, 70, 90, 102, 84, 78, 105, 67, 63, 68, 0, 99, 80, 66, 46, 83, 40, 73,
80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75, 210, 34, 86] 80, 90, 93, 50, 86, 78, 89, 98, 75, 80, 50, 64, 55, 100, 90, 110, 80, 50, 74, 90, 70, 70, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75, 210, 35, 86]
overdrive20 = [61, 80, 47, 75, 70, 70, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64, overdrive20 = [61, 80, 47, 75, 70, 70, 95, 79, 65, 31, 50, 59, 90, 58, 0, 78, 50, 70, 62, 49, 64,
46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100, 200, 84, 50] 46, 73, 95, 67, 84, 70, 78, 69, 70, 50, 80, 80, 63, 25, 50, 72, 55, 50, 95, 55, 70, 90, 70, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100, 200, 85, 50]
frag30 = [88, 90, 100, 75, 80, 89, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62, frag30 = [88, 90, 100, 75, 80, 89, 70, 79, 65, 40, 50, 90, 100, 92, 0, 61, 67, 92, 85, 50, 86, 62,
65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50, 34, 85] 65, 85, 67, 88, 74, 0.5, 105, 80, 95, 50, 80, 87, 71, 50, 95, 0, 80, 75, 50, 70, 80, 100, 65, 80, 61, 50, 68, 60, 90, 67, 50, 60, 51, 50, 35, 85]
prog30 = [71, 90, 80, 75, 100, 80, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83, prog30 = [71, 90, 80, 75, 100, 80, 90, 102, 84, 78, 105, 77, 73, 78, 0, 99, 80, 66, 46, 93, 40, 83,
80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75, 210, 34, 86] 80, 90, 93, 50, 96, 88, 99, 108, 75, 80, 50, 64, 55, 100, 100, 110, 80, 50, 74, 90, 80, 80, 56, 80, 79, 55, 65, 59, 90, 50, 90, 90, 75, 210, 35, 86]
overdrive30 = [71, 90, 57, 75, 80, 80, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64, overdrive30 = [71, 90, 57, 75, 80, 80, 95, 79, 65, 31, 50, 69, 100, 68, 0, 78, 50, 70, 62, 59, 64,
56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100, 200, 84, 50] 56, 73, 95, 67, 84, 80, 88, 79, 80, 50, 80, 80, 63, 25, 50, 82, 55, 50, 95, 55, 70, 100, 80, 99, 80, 61, 40, 69, 62, 51, 90, 67, 60, 100, 200, 85, 50]
char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1, char_type = [1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 2, 0, 0, 0, 2, 3, 1, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 2] 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 2]
@@ -420,28 +418,26 @@ def main(path='./'):
'shirahime', 38, 33, 28, 66, 58, 50, 66, 58, 50, 'frags_preferred_song', 0, 0, '', 0)) 'shirahime', 38, 33, 28, 66, 58, 50, 66, 58, 50, 'frags_preferred_song', 0, 0, '', 0))
for i in char_core: for i in char_core:
for j in char_core[i]: c.executemany('''insert into char_item values(?,?,'core',?)''', [
c.execute('''insert into char_item values(?,?,'core',?)''', (i, j['core_id'], j['amount']) for j in char_core[i]])
(i, j['core_id'], j['amount']))
cores = ['core_hollow', 'core_desolate', 'core_chunithm', 'core_crimson', cores = ['core_hollow', 'core_desolate', 'core_chunithm', 'core_crimson',
'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary', 'core_colorful', 'core_course_skip_purchase'] 'core_ambivalent', 'core_scarlet', 'core_groove', 'core_generic', 'core_binary', 'core_colorful', 'core_course_skip_purchase']
c.executemany('''insert into item values(?,"core",1,'')''',
for i in cores: [(i,) for i in cores])
c.execute('''insert into item values(?,"core",1,'')''', (i,))
world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster", world_songs = ["babaroque", "shadesoflight", "kanagawa", "lucifer", "anokumene", "ignotus", "rabbitintheblackroom", "qualia", "redandblue", "bookmaker", "darakunosono", "espebranch", "blacklotus", "givemeanightmare", "vividtheory", "onefr", "gekka", "vexaria3", "infinityheaven3", "fairytale3", "goodtek3", "suomi", "rugie", "faintlight", "harutopia", "goodtek", "dreaminattraction", "syro", "diode", "freefall", "grimheart", "blaster",
"cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia", 'altair', 'mukishitsu', 'trapcrow', 'redandblue3', 'ignotus3', 'singularity3', 'dropdead3', 'arcahv'] "cyberneciacatharsis", "monochromeprincess", "revixy", "vector", "supernova", "nhelv", "purgatorium3", "dement3", "crossover", "guardina", "axiumcrisis", "worldvanquisher", "sheriruth", "pragmatism", "gloryroad", "etherstrike", "corpssansorganes", "lostdesire", "blrink", "essenceoftwilight", "lapis", "solitarydream", "lumia3", "purpleverse", "moonheart3", "glow", "enchantedlove", "take", "lifeispiano", "vandalism", "nexttoyou3", "lostcivilization3", "turbocharger", "bookmaker3", "laqryma3", "kyogenkigo", "hivemind", "seclusion", "quonwacca3", "bluecomet", "energysynergymatrix", "gengaozo", "lastendconductor3", "antithese3", "qualia3", "kanagawa3", "heavensdoor3", "pragmatism3", "nulctrl", "avril", "ddd", "merlin3", "omakeno3", "nekonote", "sanskia", 'altair', 'mukishitsu', 'trapcrow', 'redandblue3', 'ignotus3', 'singularity3', 'dropdead3', 'arcahv']
for i in world_songs: c.executemany('''insert into item values(?,"world_song",1,'')''', [
c.execute('''insert into item values(?,"world_song",1,'')''', (i,)) (i,) for i in world_songs])
world_unlocks = ["scenery_chap1", "scenery_chap2", world_unlocks = ["scenery_chap1", "scenery_chap2",
"scenery_chap3", "scenery_chap4", "scenery_chap5", "scenery_chap6", "scenery_chap7"] "scenery_chap3", "scenery_chap4", "scenery_chap5", "scenery_chap6", "scenery_chap7"]
for i in world_unlocks: c.executemany('''insert into item values(?,"world_unlock",1,'')''', [
c.execute('''insert into item values(?,"world_unlock",1,'')''', (i,)) (i,) for i in world_unlocks])
course_banners = ['course_banner_' + str(i) for i in range(1, 12)] course_banners = ['course_banner_' + str(i) for i in range(1, 12)]
for i in course_banners: c.executemany('''insert into item values(?,"course_banner",1,'')''', [
c.execute('''insert into item values(?,"course_banner",1,'')''', (i,)) (i,) for i in course_banners])
c.execute('''insert into item values(?,?,?,?)''', c.execute('''insert into item values(?,?,?,?)''',
('fragment', 'fragment', 1, '')) ('fragment', 'fragment', 1, ''))
@@ -523,29 +519,29 @@ def main(path='./'):
x.insert_all() x.insert_all()
# api权限与权限组初始化 # api权限与权限组初始化
role = ['admin', 'user', 'selecter'] role = ['system', 'admin', 'user', 'selecter']
role_caption = ['管理员', '用户', '查询接口'] role_caption = ['系统', '管理员', '用户', '查询接口']
power = ['select', 'select_me', 'change', 'change_me', 'grant', power = ['system', 'select', 'select_me', 'change', 'change_me', 'grant',
'grant_inf', 'select_song_rank', 'select_song_info'] 'grant_inf', 'select_song_rank', 'select_song_info']
power_caption = ['总体查询权限', '自我查询权限', '总体修改权限', power_caption = ['系统权限', '总体查询权限', '自我查询权限', '总体修改权限',
'自我修改权限', '授权权限', '下级授权权限', '歌曲排行榜查询权限', '歌曲信息查询权限'] '自我修改权限', '授权权限', '下级授权权限', '歌曲排行榜查询权限', '歌曲信息查询权限']
role_power = {'0': [0, 1, 3, 5, 6, 7], role_power = {'system': power,
'1': [1, 3, 6, 7], 'admin': ['select', 'select_me', 'change_me', 'grant_inf', 'select_song_rank', 'select_song_info'],
'2': [0] 'user': ['select_me', 'change_me', 'select_song_rank', 'select_song_info'],
'selector': ['select']
} }
for i in range(0, len(role)): c.executemany('''insert into role values(?,?)''', [
c.execute('''insert into role values(:a,:b,:c)''', { (role[i], role_caption[i]) for i in range(len(role))])
'a': i, 'b': role[i], 'c': role_caption[i]})
for i in range(0, len(power)): c.executemany('''insert into power values(?,?)''', [
c.execute('''insert into power values(:a,:b,:c)''', { (power[i], power_caption[i]) for i in range(len(power))])
'a': i, 'b': power[i], 'c': power_caption[i]})
for i in role_power: for i in role_power:
for j in role_power[i]: c.executemany('''insert into role_power values(?,?)''',
c.execute('''insert into role_power values(:a,:b)''', [(i, j) for j in role_power[i]])
{'a': int(i), 'b': j})
conn.commit() conn.commit()
conn.close() conn.close()

View File

@@ -63,10 +63,10 @@ def download(file_path):
x.select_for_check() x.select_for_check()
if x.is_limited: if x.is_limited:
raise ArcError( raise ArcError(
'You have reached the download limit.', 903, status=403) 'You have reached the download limit.', 903, status=429)
if not x.is_valid: if not x.is_valid:
raise ArcError('Expired token.', status=403) raise ArcError('Expired token.', status=403)
x.insert_user_download() x.download_hit()
# response = make_response() # response = make_response()
# response.headers['Content-Type'] = 'application/octet-stream' # response.headers['Content-Type'] = 'application/octet-stream'
# response.headers['X-Accel-Redirect'] = '/nginx_download/' + file_path # response.headers['X-Accel-Redirect'] = '/nginx_download/' + file_path

View File

@@ -1,2 +1,3 @@
flask>=2.0.2 flask>=2.0.2
cryptography>=35.0.0 cryptography>=35.0.0
limits>=2.7.0

View File

@@ -54,11 +54,10 @@ def auth_required(request):
try: try:
user = UserAuth(c) user = UserAuth(c)
user.token = headers['Authorization'][7:] user.token = headers['Authorization'][7:]
return view(user.token_get_id(), *args, **kwargs) user_id = user.token_get_id()
except ArcError as e: except ArcError as e:
return error_return(e) return error_return(e)
return view(user_id, *args, **kwargs)
return error_return()
return wrapped_view return wrapped_view
return decorator return decorator

View File

@@ -12,7 +12,7 @@ from werkzeug.datastructures import ImmutableMultiDict
from .auth import auth_required from .auth import auth_required
from .func import arc_try, error_return, success_return from .func import arc_try, error_return, success_return
from .present import present_info from .present import present_info
from .purchase import bundle_pack, bundle_bundle from .purchase import bundle_bundle, bundle_pack
from .score import song_score_friend from .score import song_score_friend
from .user import user_me from .user import user_me
from .world import world_all from .world import world_all
@@ -33,8 +33,7 @@ def download_song(user_id):
x = DownloadList(c, UserOnline(c, user_id)) x = DownloadList(c, UserOnline(c, user_id))
x.song_ids = request.args.getlist('sid') x.song_ids = request.args.getlist('sid')
x.url_flag = json.loads(request.args.get('url', 'true')) x.url_flag = json.loads(request.args.get('url', 'true'))
x.clear_user_download() if x.url_flag and x.is_limited:
if x.is_limited and x.url_flag:
raise ArcError('You have reached the download limit.', 903) raise ArcError('You have reached the download limit.', 903)
x.add_songs() x.add_songs()

View File

@@ -209,10 +209,10 @@ def update_database():
update_one_table(c1, c2, 'redeem') update_one_table(c1, c2, 'redeem')
update_one_table(c1, c2, 'user_redeem') update_one_table(c1, c2, 'user_redeem')
update_one_table(c1, c2, 'redeem_item') update_one_table(c1, c2, 'redeem_item')
update_one_table(c1, c2, 'role') # update_one_table(c1, c2, 'role')
update_one_table(c1, c2, 'user_role') # update_one_table(c1, c2, 'user_role')
update_one_table(c1, c2, 'power') # update_one_table(c1, c2, 'power')
update_one_table(c1, c2, 'role_power') # update_one_table(c1, c2, 'role_power')
update_one_table(c1, c2, 'api_login') update_one_table(c1, c2, 'api_login')
update_one_table(c1, c2, 'chart') update_one_table(c1, c2, 'chart')
update_one_table(c1, c2, 'user_course') update_one_table(c1, c2, 'user_course')