[Enhance] API for purchases, items, operations

- Add API endpoints for purchases, items, and operations
- Header checker? :)
This commit is contained in:
Lost-MSth
2023-02-08 18:18:04 +08:00
parent fbd5d83626
commit 6f39274b99
19 changed files with 538 additions and 76 deletions

View File

@@ -1,10 +1,11 @@
from flask import Blueprint
from . import users
from . import songs
from . import token
from . import (users, songs, token, system, items, purchases)
bp = Blueprint('api', __name__, url_prefix='/api/v1')
bp.register_blueprint(users.bp)
bp.register_blueprint(songs.bp)
bp.register_blueprint(token.bp)
bp.register_blueprint(system.bp)
bp.register_blueprint(items.bp)
bp.register_blueprint(purchases.bp)

View File

@@ -1,15 +1,17 @@
from functools import wraps
from traceback import format_exc
from base64 import b64decode
from functools import wraps
from json import loads
from traceback import format_exc
from flask import current_app
from core.api_user import APIUser
from core.config_manager import Config
from core.error import ArcError, NoAccess, PostError
from core.error import ArcError, InputError, NoAccess, PostError
from core.sql import Connect
from flask import current_app
from .api_code import error_return
from .constant import Constant
def role_required(request, powers=[]):
@@ -48,7 +50,7 @@ def role_required(request, powers=[]):
return decorator
def request_json_handle(request, required_keys: list = [], optional_keys: list = [], must_change: bool = False):
def request_json_handle(request, required_keys: list = [], optional_keys: list = [], must_change: bool = False, is_batch: bool = False):
'''
提取post参数返回dict写成了修饰器\
parameters: \
@@ -64,7 +66,7 @@ def request_json_handle(request, required_keys: list = [], optional_keys: list =
data = {}
if request.data:
json_data = request.json
json_data = request.get_json()
else:
if request.method == 'GET' and 'query' in request.args:
# 处理axios没法GET传data的问题
@@ -78,15 +80,24 @@ def request_json_handle(request, required_keys: list = [], optional_keys: list =
for key in required_keys:
if key not in json_data:
return error_return(PostError(f'Missing parameter: {key}', api_error_code=-100))
return error_return(InputError(f'Missing parameter: {key}', api_error_code=-100))
data[key] = json_data[key]
for key in optional_keys:
if key in json_data:
data[key] = json_data[key]
if is_batch:
for key in Constant.PATCH_KEYS:
if key in json_data:
data[key] = json_data[key]
if not isinstance(data[key], list):
return error_return(InputError(f'Parameter {key} must be a list', api_error_code=-100))
if not data:
return error_return(InputError('No change', api_error_code=-100))
else:
for key in optional_keys:
if key in json_data:
data[key] = json_data[key]
if must_change and not data:
return error_return(PostError('No change', api_error_code=-100))
if must_change and not data:
return error_return(InputError('No change', api_error_code=-100))
return view(data, *args, **kwargs)

View File

@@ -18,6 +18,11 @@ CODE_MSG = {
-104: 'Invalid sort order parameter',
-105: 'Invalid URL parameter',
-110: 'Invalid user_id',
-120: 'Invalid item type',
-121: 'No such item',
-122: 'Item already exists',
-123: 'Purchase already has this item',
-124: 'Purchase does not have this item',
-200: 'No permission', # 2xx用户相关错误
-201: 'Wrong username or password',
-202: 'User is banned',

View File

@@ -1,2 +1,4 @@
class Constant:
QUERY_KEYS = ['limit', 'offset', 'query', 'fuzzy_query', 'sort']
PATCH_KEYS = ['create', 'update', 'remove']

115
latest version/api/items.py Normal file
View File

@@ -0,0 +1,115 @@
from flask import Blueprint, request
from core.error import DataExist, InputError, NoData
from core.item import Item, ItemFactory
from core.sql import Connect, Query, Sql
from .api_auth import api_try, request_json_handle, role_required
from .api_code import success_return
from .constant import Constant
bp = Blueprint('items', __name__, url_prefix='/items')
@bp.route('', methods=['GET'])
@role_required(request, ['select'])
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
@api_try
def items_get(data, user):
'''查询全物品信息'''
with Connect() as c:
query = Query(['item_id', 'type'], ['item_id'],
['item_id']).from_dict(data)
x = Sql(c).select('item', query=query)
r: 'list[Item]' = []
for i in x:
r.append(ItemFactory.from_dict({
'item_id': i[0],
'type': i[1],
'is_available': i[2] == 1
}))
if not r:
raise NoData(api_error_code=-2)
return success_return([x.to_dict(has_is_available=True, has_amount=False) for x in r])
ALLOW_ITEM_TYPE = ['pack', 'single', 'world_song', 'character']
@bp.route('', methods=['POST'])
@role_required(request, ['change'])
@request_json_handle(request, required_keys=['item_id', 'type'], optional_keys=['is_available'])
@api_try
def items_post(data, user):
'''新增物品'''
if data['type'] not in ALLOW_ITEM_TYPE:
raise InputError(
f'Invalid item type: `{data["type"]}`', api_error_code=-120)
with Connect() as c:
item = ItemFactory.from_dict(data, c=c)
if item.select_exists():
raise DataExist(
f'Item `{item.item_type}`: `{item.item_id}` already exists', api_error_code=-122)
item.insert()
return success_return(item.to_dict(has_is_available=True, has_amount=False))
@bp.route('/<string:item_type>/<string:item_id>', methods=['DELETE'])
@role_required(request, ['change'])
@api_try
def items_item_delete(user, item_type, item_id):
'''删除物品'''
if item_type not in ALLOW_ITEM_TYPE:
raise InputError(
f'Invalid item type: `{item_type}`', api_error_code=-120)
with Connect() as c:
item = ItemFactory.from_dict({
'item_id': item_id,
'type': item_type
}, c=c)
if not item.select_exists():
raise NoData(
f'No such item `{item_type}`: `{item_id}`', api_error_code=-121)
item.delete()
return success_return()
@bp.route('/<string:item_type>/<string:item_id>', methods=['PUT'])
@role_required(request, ['change'])
@request_json_handle(request, optional_keys=['is_available'], must_change=True)
@api_try
def items_item_put(data, user, item_type, item_id):
'''修改物品'''
if item_type not in ALLOW_ITEM_TYPE:
raise InputError(
f'Invalid item type: `{item_type}`', api_error_code=-120)
if not isinstance(data['is_available'], bool):
raise InputError('`is_available` must be a boolean',
api_error_code=-101)
with Connect() as c:
item = ItemFactory.from_dict({
'item_id': item_id,
'type': item_type,
'is_available': data['is_available']
}, c=c)
if not item.select_exists():
raise NoData(
f'No such item `{item_type}`: `{item_id}`', api_error_code=-121)
item.update()
return success_return(item.to_dict(has_is_available=True, has_amount=False))
@bp.route('/<string:item_type>/<string:item_id>', methods=['GET'])
@role_required(request, ['select'])
@api_try
def items_item_get(user, item_type, item_id):
'''查询单个物品信息'''
with Connect() as c:
item = ItemFactory.from_dict({
'item_id': item_id,
'type': item_type
}, c=c)
item.select()
return success_return(item.to_dict(has_is_available=True, has_amount=False))

View File

@@ -0,0 +1,164 @@
from flask import Blueprint, request
from core.error import DataExist, InputError, NoData
from core.item import ItemFactory
from core.purchase import Purchase
from core.sql import Connect, Query, Sql
from .api_auth import api_try, request_json_handle, role_required
from .api_code import success_return
from .constant import Constant
bp = Blueprint('purchases', __name__, url_prefix='/purchases')
@bp.route('', methods=['GET'])
@role_required(request, ['select'])
@request_json_handle(request, optional_keys=Constant.QUERY_KEYS)
@api_try
def purchases_get(data, user):
'''查询全购买信息'''
with Connect() as c:
query = Query(['purchase_name', 'discount_reason'], ['purchase_name'], [
'price', 'orig_price', 'discount_from', 'discount_to']).from_dict(data)
x = Sql(c).select('purchase', query=query)
r = [Purchase().from_list(i) for i in x]
if not r:
raise NoData(api_error_code=-2)
return success_return([x.to_dict(has_items=False, show_real_price=False) for x in r])
@bp.route('', methods=['POST'])
@role_required(request, ['change'])
@request_json_handle(request, required_keys=['purchase_name', 'orig_price'], optional_keys=['price', 'discount_from', 'discount_to', 'discount_reason', 'items'])
@api_try
def purchases_post(data, user):
'''新增购买注意可以有items不存在的item会自动创建'''
with Connect() as c:
purchase = Purchase(c).from_dict(data)
if purchase.select_exists():
raise DataExist(
f'Purchase `{purchase.purchase_name}` already exists')
purchase.insert_all()
return success_return(purchase.to_dict(has_items='items' in data, show_real_price=False))
@bp.route('/<string:purchase_name>', methods=['GET'])
@role_required(request, ['select'])
@api_try
def purchases_purchase_get(user, purchase_name: str):
'''查询单个购买信息'''
with Connect() as c:
return success_return(Purchase(c).select(purchase_name).to_dict(show_real_price=False))
@bp.route('/<string:purchase_name>', methods=['DELETE'])
@role_required(request, ['change'])
@api_try
def purchases_purchase_delete(user, purchase_name: str):
'''删除单个购买信息会连带删除purchase_item'''
with Connect() as c:
purchase = Purchase(c).select(purchase_name)
purchase.delete_all()
return success_return()
@bp.route('/<string:purchase_name>', methods=['PUT'])
@role_required(request, ['change'])
@request_json_handle(request, optional_keys=['price', 'orig_price', 'discount_from', 'discount_to', 'discount_reason'], must_change=True)
@api_try
def purchases_purchase_put(data, user, purchase_name: str):
'''修改单个购买信息注意不能有items'''
with Connect() as c:
purchase = Purchase(c).select(purchase_name)
t = ['price', 'orig_price', 'discount_from', 'discount_to']
for i in t:
if i in data:
setattr(purchase, i, int(data[i]))
if 'discount_reason' in data:
purchase.discount_reason = str(data['discount_reason'])
purchase.update()
return success_return(purchase.to_dict(has_items=False, show_real_price=False))
@bp.route('/<string:purchase_name>/items', methods=['GET'])
@role_required(request, ['select'])
@api_try
def purchases_purchase_items_get(user, purchase_name: str):
'''查询单个购买的所有items'''
with Connect() as c:
p = Purchase(c)
p.purchase_name = purchase_name
p.select_items()
return success_return([x.to_dict(has_is_available=True) for x in p.items])
# @bp.route('/<string:purchase_name>/items', methods=['POST'])
# @role_required(request, ['change'])
# @request_json_handle(request, required_keys=['item_id', 'type'], optional_keys=['amount'])
# @api_try
# def purchases_purchase_items_post(data, user, purchase_name: str):
# '''新增单个购买的批量items'''
# with Connect() as c:
# p = Purchase(c)
# p.purchase_name = purchase_name
# p.select_items()
# p.add_items([ItemFactory().from_dict(data)])
# return success_return([x.to_dict(has_is_available=True) for x in p.items])
@bp.route('/<string:purchase_name>/items', methods=['PATCH'])
@role_required(request, ['change'])
@request_json_handle(request, is_batch=True)
@api_try
def purchases_purchase_items_patch(data, user, purchase_name: str):
'''增删改单个购买的批量items'''
with Connect() as c:
p = Purchase(c)
p.purchase_name = purchase_name
p.select_items()
p.delete_items([ItemFactory.from_dict(x, c=c)
for x in data.get('remove', [])])
p.add_items([ItemFactory.from_dict(x, c=c)
for x in data.get('create', [])])
updates = data.get('update', [])
for x in updates:
if 'amount' not in x:
raise InputError('`amount` is required in `update`')
if not isinstance(x['amount'], int) or x['amount'] <= 0:
raise InputError(
'`amount` must be a positive integer', api_error_code=-101)
p.update_items([ItemFactory.from_dict(x, c=c) for x in updates])
return success_return([x.to_dict(has_is_available=True) for x in p.items])
# @bp.route('/<string:purchase_name>/items/<string:item_type>/<string:item_id>', methods=['DELETE'])
# @role_required(request, ['change'])
# @api_try
# def purchases_purchase_items_item_delete(user, purchase_name: str, item_type: str, item_id: str):
# '''删除单个购买的单个item'''
# with Connect() as c:
# p = Purchase(c)
# p.purchase_name = purchase_name
# p.select_items()
# p.delete_items([ItemFactory().from_dict(
# {'item_type': item_type, 'item_id': item_id})])
# return success_return()
# @bp.route('/<string:purchase_name>/items/<string:item_type>/<string:item_id>', methods=['PUT'])
# @role_required(request, ['change'])
# @request_json_handle(request, optional_keys=['amount', 'is_available'], must_change=True)
# @api_try
# def purchases_purchase_items_item_put(data, user, purchase_name: str, item_type: str, item_id: str):
# '''修改单个购买的单个item'''
# with Connect() as c:
# p = Purchase(c)
# p.purchase_name = purchase_name
# pass
# return success_return()

View File

@@ -0,0 +1,32 @@
from flask import Blueprint, request
from core.error import ArcError
from core.operation import BaseOperation
from .api_auth import api_try, role_required
from .api_code import success_return
bp = Blueprint('system', __name__, url_prefix='/system')
operation_dict = {i._name: i for i in BaseOperation.__subclasses__()}
@bp.route('/operations', methods=['GET'])
@role_required(request, ['system'])
@api_try
def operations_get(user):
return success_return(list(operation_dict.keys()))
@bp.route('/operations/<string:operation_name>', methods=['POST'])
@role_required(request, ['system'])
@api_try
def operations_operation_post(user, operation_name: str):
if operation_name not in operation_dict:
raise ArcError(
f'No such operation: `{operation_name}`', api_error_code=-1, status=404)
x = operation_dict[operation_name]()
x.set_params(**request.get_json())
x.run()
return success_return()

View File

@@ -1,9 +1,10 @@
from base64 import b64decode
from flask import Blueprint, request
from core.api_user import APIUser
from core.error import PostError
from core.sql import Connect
from flask import Blueprint, request
from .api_auth import api_try, request_json_handle, role_required
from .api_code import success_return

View File

@@ -1,9 +1,10 @@
from flask import Blueprint, request
from core.api_user import APIUser
from core.error import InputError, NoAccess, NoData
from core.score import Potential, UserScoreList
from core.sql import Connect, Query, Sql
from core.user import UserChanger, UserInfo, UserRegister
from core.api_user import APIUser
from flask import Blueprint, request
from .api_auth import api_try, request_json_handle, role_required
from .api_code import error_return, success_return

View File

@@ -97,12 +97,6 @@ class DatabaseInit:
with open(self.single_path, 'rb') as f:
self.insert_purchase_item(load(f))
self.c.execute(
'''select exists(select * from item where item_id='epilogue')''')
if self.c.fetchone() == (0,):
self.c.execute(
'''insert into item values('epilogue','pack',1)''')
def course_init(self) -> None:
'''初始化课题信息'''
courses = []

View File

@@ -5,10 +5,14 @@ from .error import InputError, ItemNotEnough, ItemUnavailable, NoData
class Item:
item_type = None
def __init__(self) -> None:
def __init__(self, c=None) -> None:
self.item_id = None
self.__amount = None
self.is_available = None
self.c = c
def __eq__(self, other: 'Item') -> bool:
return self.item_id == other.item_id and self.item_type == other.item_type
@property
def amount(self):
@@ -18,12 +22,13 @@ class Item:
def amount(self, value: int):
self.__amount = int(value)
def to_dict(self, has_is_available: bool = False) -> dict:
def to_dict(self, has_is_available: bool = False, has_amount: bool = True) -> dict:
r = {
'id': self.item_id,
'amount': self.amount,
'type': self.item_type
}
if has_amount:
r['amount'] = self.amount
if has_is_available:
r['is_available'] = self.is_available
return r
@@ -32,6 +37,32 @@ class Item:
# parameter: user - User类或子类的实例
pass
def select_exists(self):
self.c.execute('''select exists(select * from item where item_id=? and type=?)''',
(self.item_id, self.item_type))
return bool(self.c.fetchone()[0])
def insert(self):
self.c.execute('''insert into item values(?,?,?)''',
(self.item_id, self.item_type, self.is_available))
def delete(self):
self.c.execute('''delete from item where item_id=? and type=?''',
(self.item_id, self.item_type))
def update(self):
self.c.execute('''update item set is_available=? where item_id=? and type=?''',
(self.is_available, self.item_id, self.item_type))
def select(self):
self.c.execute('''select is_available from item where item_id=? and type=?''',
(self.item_id, self.item_type))
x = self.c.fetchone()
if not x:
raise NoData(
f'No such item `{self.item_type}`: `{self.item_id}`', api_error_code=-121)
self.is_available = x[0]
class UserItem(Item):
@@ -40,7 +71,7 @@ class UserItem(Item):
self.c = c
self.user = None
def select(self, user=None):
def select_user_item(self, user=None):
'''
查询用户item\
parameter: `user` - `User`类或子类的实例

View File

@@ -1,4 +1,5 @@
from .download import DownloadList
from .error import NoData
from .save import SaveData
from .score import Score
from .sql import Connect, Sql
@@ -214,6 +215,11 @@ class UnlockUserItem(BaseOperation):
def _one_user_insert(self):
with Connect() as c:
c.execute(
'''select exists(select * from user where user_id = ?)''', (self.user.user_id,))
if not c.fetchone()[0]:
raise NoData(
f'No such user: `{self.user.user_id}`', api_error_code=-110)
c.execute(
f'''select item_id, type from item where type in ({','.join(['?'] * len(self.item_types))})''', self.item_types)
sql_list = [(self.user.user_id, i[0], i[1])

View File

@@ -1,6 +1,6 @@
from time import time
from .error import NoData, TicketNotEnough
from .error import DataExist, InputError, NoData, TicketNotEnough
from .item import ItemFactory
@@ -34,31 +34,35 @@ class Purchase:
if self.discount_reason == 'anni5tix':
x = ItemFactory(self.c).get_item('anni5tix')
x.item_id = 'anni5tix'
x.select(self.user)
x.select_user_item(self.user)
if x.amount >= 1:
return 0
return self.price
return self.orig_price
def to_dict(self) -> dict:
price = self.price_displayed
def to_dict(self, has_items: bool = True, show_real_price: bool = True) -> dict:
if show_real_price:
price = self.price_displayed
r = {
'name': self.purchase_name,
'price': price,
'price': price if show_real_price else self.price,
'orig_price': self.orig_price,
'items': [x.to_dict(has_is_available=True) for x in self.items]
}
if has_items:
r['items'] = [x.to_dict(has_is_available=True) for x in self.items]
if self.discount_from > 0 and self.discount_to > 0:
r['discount_from'] = self.discount_from
r['discount_to'] = self.discount_to
if self.discount_reason == 'anni5tix' and price == 0:
if not show_real_price or (self.discount_reason == 'anni5tix' and price == 0):
r['discount_reason'] = self.discount_reason
return r
def from_dict(self, d: dict) -> 'Purchase':
self.purchase_name = d['name']
self.price = d['price']
self.orig_price = d['orig_price']
self.purchase_name = d.get('name') or d.get('purchase_name')
if not self.purchase_name:
raise InputError('purchase_name is required')
self.orig_price = int(d['orig_price'])
self.price = d.get('price', self.orig_price)
self.discount_from = d.get('discount_from', -1)
self.discount_to = d.get('discount_to', -1)
self.discount_reason = d.get('discount_reason', '')
@@ -67,24 +71,50 @@ class Purchase:
return self
def from_list(self, l: list) -> 'Purchase':
self.purchase_name = l[0]
self.price = l[1]
self.orig_price = l[2]
self.discount_from = l[3] if l[3] else -1
self.discount_to = l[4] if l[4] else -1
self.discount_reason = l[5] if l[5] else ''
return self
def insert_all(self) -> None:
'''向数据库插入包括item表和purchase_item表'''
self.insert()
self.insert_items()
def insert(self) -> None:
'''向数据库插入不包括item表和purchase_item表'''
self.c.execute('''insert into purchase values(?,?,?,?,?,?)''',
(self.purchase_name, self.price, self.orig_price, self.discount_from, self.discount_to, self.discount_reason))
self.insert_items()
# def insert_only_purchase_item(self) -> None:
# '''向数据库插入purchase_item表'''
# for i in self.items:
# self.c.execute('''insert into purchase_item values(?,?,?,?)''',
# (self.purchase_name, i.item_id, i.item_type, i.amount))
def insert_items(self) -> None:
'''向数据库插入物品,注意已存在的物品不会变更'''
for i in self.items:
self.c.execute(
'''select exists(select * from item where item_id=?)''', (i.item_id,))
if self.c.fetchone() == (0,):
self.c.execute('''insert into item values(?,?,?)''',
(i.item_id, i.item_type, i.is_available))
self.c.execute('''insert or ignore into item values(?,?,?)''',
(i.item_id, i.item_type, i.is_available))
self.c.execute('''insert into purchase_item values(?,?,?,?)''',
self.c.execute('''insert or ignore into purchase_item values(?,?,?,?)''',
(self.purchase_name, i.item_id, i.item_type, i.amount))
def select_exists(self, purchase_name: str = None) -> bool:
'''
用purchase_name查询存在性
'''
if purchase_name:
self.purchase_name = purchase_name
self.c.execute(
'''select exists(select * from purchase where purchase_name=?)''', (self.purchase_name,))
return self.c.fetchone() == (1,)
def select(self, purchase_name: str = None) -> 'Purchase':
'''
用purchase_name查询信息
@@ -93,11 +123,11 @@ class Purchase:
self.purchase_name = purchase_name
self.c.execute(
'''select * from purchase where purchase_name=:name''', {'name': purchase_name})
'''select * from purchase where purchase_name=:name''', {'name': self.purchase_name})
x = self.c.fetchone()
if not x:
raise NoData('The purchase `%s` does not exist.' %
purchase_name, 501)
raise NoData(
f'Purchase `{self.purchase_name}` does not exist.', 501)
self.price = x[1]
self.orig_price = x[2]
@@ -112,9 +142,9 @@ class Purchase:
self.c.execute(
'''select item_id, type, amount from purchase_item where purchase_name=:a''', {'a': self.purchase_name})
x = self.c.fetchall()
if not x:
raise NoData('The items of the purchase `%s` does not exist.' %
self.purchase_name, 501)
# if not x:
# raise NoData(
# f'The items of the purchase `{self.purchase_name}` does not exist.', 501)
self.items = []
t = None
@@ -162,6 +192,61 @@ class Purchase:
for i in self.items:
i.user_claim_item(self.user)
def delete_purchase_item(self) -> None:
'''删除purchase_item表'''
self.c.execute(
'''delete from purchase_item where purchase_name=?''', (self.purchase_name, ))
def delete(self) -> None:
'''删除purchase表'''
self.c.execute(
'''delete from purchase where purchase_name=?''', (self.purchase_name, ))
def delete_all(self) -> None:
'''删除purchase表和purchase_item表'''
self.delete_purchase_item()
self.delete()
def update(self) -> None:
'''更新purchase表'''
self.c.execute('''update purchase set price=:a, orig_price=:b, discount_from=:c, discount_to=:d, discount_reason=:e where purchase_name=:f''', {
'a': self.price, 'b': self.orig_price, 'c': self.discount_from, 'd': self.discount_to, 'e': self.discount_reason, 'f': self.purchase_name})
def add_items(self, items: list) -> None:
'''添加purchase_item表'''
for i in items:
if not i.select_exists():
raise NoData(
f'No such item `{i.item_type}`: `{i.item_id}`', api_error_code=-121)
if i in self.items:
raise DataExist(
f'Item `{i.item_type}`: `{i.item_id}` already exists in purchase `{self.purchase_name}`', api_error_code=-123)
self.c.executemany('''insert into purchase_item values (?, ?, ?, ?)''', [
(self.purchase_name, i.item_id, i.item_type, i.amount) for i in items])
self.items.extend(items)
def delete_items(self, items: list) -> None:
'''删除purchase_item表'''
for i in items:
if i not in self.items:
raise NoData(
f'No such item `{i.item_type}`: `{i.item_id}` in purchase `{self.purchase_name}`', api_error_code=-124)
self.c.executemany('''delete from purchase_item where purchase_name=? and item_id=? and type=?''', [
(self.purchase_name, i.item_id, i.item_type) for i in items])
for i in items:
self.items.remove(i)
def update_items(self, items: list) -> None:
'''更新purchase_item表只能更新amount'''
for i in items:
if i not in self.items:
raise NoData(
f'No such item `{i.item_type}`: `{i.item_id}` in purchase `{self.purchase_name}`', api_error_code=-124)
self.c.executemany('''update purchase_item set amount=? where purchase_name=? and item_id=? and type=?''', [
(i.amount, self.purchase_name, i.item_id, i.item_type) for i in items])
for i in items:
self.items[self.items.index(i)].amount = i.amount
class PurchaseList:
'''

View File

@@ -381,13 +381,6 @@ class DatabaseMigrator:
c.executemany('''insert into user_char_full values(?,?,?,?,?,?)''', [
(j[0], i[0], i[1], exp, i[2], 0) for j in y])
@staticmethod
def update_user_epilogue(c) -> None:
'''给用户添加epilogue包'''
c.execute('''select user_id from user''')
Sql(c).insert_many('user_item', [], [(i[0], 'epilogue', 'pack', 1)
for i in c.fetchall()], insert_type='ignore')
def update_database(self) -> None:
'''
将c1数据库不存在数据加入或覆盖到c2数据库上
@@ -402,7 +395,6 @@ class DatabaseMigrator:
self.update_one_table(c1, c2, 'character')
self.update_user_char_full(c2) # 更新user_char_full
self.update_user_epilogue(c2) # 更新user的epilogue
class MemoryDatabase:

View File

@@ -301,7 +301,7 @@ class UserMap(Map):
if self.require_type in ['pack', 'single']:
item = ItemFactory(self.c).get_item(self.require_type)
item.item_id = self.require_id
item.select(self.user)
item.select_user_item(self.user)
if not item.amount:
self.is_locked = True

View File

@@ -1,13 +1,12 @@
import base64
from functools import wraps
from core.config_manager import Config
from core.error import ArcError, NoAccess
from core.sql import Connect
from core.user import UserAuth, UserLogin
from flask import Blueprint, g, jsonify, request
from flask import Blueprint, g, jsonify, request, current_app
from .func import arc_try, error_return
from .func import arc_try, error_return, header_check
bp = Blueprint('auth', __name__, url_prefix='/auth')
@@ -16,9 +15,9 @@ bp = Blueprint('auth', __name__, url_prefix='/auth')
@arc_try
def login():
headers = request.headers
if Config.ALLOW_APPVERSION: # 版本检查
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
raise NoAccess('Invalid app version.', 1203)
e = header_check(request)
if e is not None:
raise e
request.form['grant_type']
with Connect() as c:
@@ -44,9 +43,11 @@ def auth_required(request):
headers = request.headers
if Config.ALLOW_APPVERSION: # 版本检查
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return error_return(NoAccess('Invalid app version.', 1203))
e = header_check(request)
if e is not None:
current_app.logger.warning(
f' - {e.error_code}|{e.api_error_code}: {e}')
return error_return(e)
with Connect() as c:
try:

View File

@@ -19,7 +19,7 @@ def course_me(user_id):
user = UserOnline(c, user_id)
core = ItemCore(c)
core.item_id = 'core_course_skip_purchase'
core.select(user)
core.select_user_item(user)
x = UserCourseList(c, user)
x.select_all()
return success_return({

View File

@@ -1,10 +1,18 @@
from functools import wraps
from traceback import format_exc
from core.config_manager import Config
from core.error import ArcError
from flask import current_app, g, jsonify
from core.config_manager import Config
from core.error import ArcError, NoAccess
has_arc_hash = False
try:
from core.arc_crypto import ArcHashChecker # type: ignore
has_arc_hash = True
except ModuleNotFoundError:
pass
default_error = ArcError('Unknown Error', status=500)
@@ -89,3 +97,16 @@ def arc_try(view):
return error_return(e)
return wrapped_view
def header_check(request) -> ArcError:
'''检查请求头是否合法'''
headers = request.headers
if Config.ALLOW_APPVERSION: # 版本检查
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
return NoAccess('Invalid app version', 1203)
if has_arc_hash and not ArcHashChecker(request).check():
return NoAccess('Invalid request')
return None

View File

@@ -8,7 +8,7 @@ from core.user import User, UserLogin, UserOnline, UserRegister
from flask import Blueprint, request
from .auth import auth_required
from .func import arc_try, success_return
from .func import arc_try, header_check, success_return
bp = Blueprint('user', __name__, url_prefix='/user')
@@ -17,9 +17,9 @@ bp = Blueprint('user', __name__, url_prefix='/user')
@arc_try
def register():
headers = request.headers
if Config.ALLOW_APPVERSION: # 版本检查
if 'AppVersion' not in headers or headers['AppVersion'] not in Config.ALLOW_APPVERSION:
raise NoAccess('Invalid app version.', 1203)
error = header_check(request)
if error is not None:
raise error
with Connect() as c:
new_user = UserRegister(c)