diff --git a/app/__init__.py b/app/__init__.py index 051a759..eb56ab5 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -6,6 +6,8 @@ # @File Name: __init__.py +import os +import random from flask import Flask from flask_sslify import SSLify from .main import main as main_blueprint @@ -13,17 +15,38 @@ from .api import api as api_blueprint from app.config import config +def generate_random_string(length): + """ + 生成随机密码 + :param length: + :return: + """ + letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + return ''.join(random.choice(letters) for _ in range(length)) + + def create_app(config_name): - """创建主app""" + """ + 创建 app + :param config_name: + :return: + """ app = Flask(__name__) app.config.from_object(config[config_name]) config[config_name].init_app(app) - if config[config_name].SSL_REDIRECT: - sslify = SSLify(app) + if os.environ.get('SSL_REDIRECT'): + SSLify(app) app.register_blueprint(main_blueprint) app.register_blueprint(api_blueprint, url_prefix='/api/v1/') + if not os.getenv('ACCESS_KEY'): + access_key = generate_random_string(32) + os.environ['ACCESS_KEY'] = access_key + else: + access_key = os.getenv('ACCESS_KEY') + print('==========Access Key: {}=========='.format(access_key)) + return app diff --git a/app/api/errors.py b/app/api/errors.py index 98dc2e4..38b6351 100644 --- a/app/api/errors.py +++ b/app/api/errors.py @@ -9,12 +9,23 @@ from . import api from flask import jsonify +@api.errorhandler(403) +def forbidden(e): + return jsonify( + { + 'code': 403, + 'msg': 'API: 没有权限', + 'data': None + } + ), 403 + + @api.errorhandler(404) def page_not_found(e): return jsonify( { 'code': 404, - 'msg': '页面未找到', + 'msg': 'API: 页面未找到', 'data': None } ), 404 @@ -25,7 +36,7 @@ def internal_server_error(e): return jsonify( { 'code': 500, - 'msg': '服务器内部错误', + 'msg': 'API: 服务器内部错误', 'data': None } ), 500 diff --git a/app/api/views.py b/app/api/views.py index 1625d8e..be39388 100644 --- a/app/api/views.py +++ b/app/api/views.py @@ -6,14 +6,33 @@ # @File Name: views.py +import os +from functools import wraps +from flask import abort from flask import jsonify from flask import request +from flask import send_file from ..db.db import SQLite as db from . import api +def permission_required(func): + @wraps(func) + def decorated_func(*args, **kwargs): + if request.args.get('key') != os.getenv('ACCESS_KEY'): + abort(403) + return func(*args, **kwargs) + + return decorated_func + + @api.route('/overall/', methods=['GET', 'POST']) +@permission_required def overall(): + """ + 查询ReqCount表内所有的源数据 + :return: + """ limit = request.args.get('limit', type=int) try: data = db().exec('select * from reqcount;') @@ -38,7 +57,12 @@ def overall(): @api.route('/query/', methods=['GET', 'POST']) +@permission_required def query(): + """ + 查询指定名称的计数数据 + :return: + """ name = request.args.get('name', type=str) nochange = request.args.get('nochange', type=bool) if nochange: @@ -63,7 +87,12 @@ def query(): @api.route('/theme/', methods=['GET', 'POST']) +@permission_required def theme(): + """ + 查询数据库内主题的源数据 + :return: + """ _theme = request.args.get('name', type=str) if not db().exists_table(_theme): return jsonify( @@ -85,7 +114,12 @@ def theme(): @api.route('/alltables/', methods=['GET', 'POST']) +@permission_required def all_tables(): + """ + 查询所有已有表 + :return: + """ data = db().show_tables return jsonify( { @@ -94,3 +128,14 @@ def all_tables(): 'data': data } ), 200 + + +@api.route('/export/', methods=['GET', 'POST']) +@api.route('/export/', methods=['GET', 'POST']) +@permission_required +def export(key: str = None): + """ + 导出数据库文件 + :return: + """ + return send_file('./db/data.sqlite', as_attachment=True) diff --git a/app/config.py b/app/config.py index 7c3d9f2..a1d8f8a 100644 --- a/app/config.py +++ b/app/config.py @@ -31,5 +31,5 @@ config = { 'development': DevelopmentConfig, 'production': ProductionConfig, - 'default': ProductionConfig + 'default': DevelopmentConfig } diff --git a/app/db/__init__.py b/app/db/__init__.py index 9e4c30a..b68926f 100644 --- a/app/db/__init__.py +++ b/app/db/__init__.py @@ -7,9 +7,23 @@ import os +import time import sys import requests -from ..decorators import time_it +from functools import wraps + + +def time_it(func): + @wraps(func) + def wrapper(*args, **kwargs): + print('I: 数据库文件开始下载') + start = time.time() + result = func(*args, **kwargs) + end = time.time() + print('I: 下载结束, 耗时 {} 秒'.format(round(end - start, 2))) + return result + + return wrapper @time_it diff --git a/app/db/data.db b/app/db/data.db deleted file mode 100644 index 4465f73..0000000 Binary files a/app/db/data.db and /dev/null differ diff --git a/app/decorators.py b/app/decorators.py deleted file mode 100644 index f844577..0000000 --- a/app/decorators.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/4/5 -# @File Name: decorators.py - -import time -from functools import wraps - - -def time_it(func): - @wraps(func) - def wrapper(*args, **kwargs): - print('I: 数据库文件开始下载') - start = time.time() - result = func(*args, **kwargs) - end = time.time() - print('I: 下载结束, 耗时 {} 秒'.format(round(end - start, 2))) - return result - - return wrapper - - - diff --git a/app/tests/test_decorator.py b/app/tests/test_decorator.py new file mode 100644 index 0000000..8cd6b4c --- /dev/null +++ b/app/tests/test_decorator.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# -- coding:utf-8 -- +# @Author: markushammered@gmail.com +# @Development Tool: PyCharm +# @Create Time: 2022/4/10 +# @File Name: test_decorator.py + + +def check(func): + def wrapper(*args, **kwargs): + print(kwargs) + return func(*args, **kwargs) + return wrapper + + +@check() +def test(name: str): + print(name) + + +test('Markus') \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index ccff59e..675163f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -32,7 +32,12 @@ - 可选参数: `theme` `length` # API -> 注意事项: ***调用接口必须在路径末尾加入 `/` (不是参数末尾)*** + +* 注意事项 + +1. `/api/v1/`内的所有接口都是需要`key`来访问 +2. `key`是从环境变量中获取或自动生成, 这取决与你部署到`Heroku`时是否设置了`ACCESS_KEY`环境变量 +3. `key`的长度为`32`位的随机英文字母大小写字符串 > 地址前缀: `/api/v1/` @@ -41,13 +46,14 @@ ## `/overall/` 接口 -> 使用此接口获取数据库中所有的数据 +> 使用此接口获取数据库中所有的数据 + > 必选参数: `limit: int` > 自定义查询数量, 输入的数字大于等于最大值时, 将返回最大值 ### 调用示例 ```shell -$ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/overall/?limit=20 +$ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/overall/?limit=20&key= ``` ```json @@ -73,14 +79,16 @@ $ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/overall/?limit=20 ***使用此接口查询的名称计数数值会加一*** -> 必选参数: `name: str` > 查询指定名称的计数数据 +> 必选参数: `name: str` > 查询指定名称的计数数据 + > 可选参数: `nochange: Any` + > `nochange`: 'Any` > 将参数值设置为任意值则仅查询不增加 ### 调用示例 ```shell -$ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/query/?name=main&nochange=1 +$ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/query/?name=main&nochange=1&key= ``` ```json @@ -97,12 +105,13 @@ $ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/query/?name=main&no ## `/theme/` 接口 > 使用此接口获取数据库内原始的base64编码的主题图片 + > 必选参数: `theme: str` ### 调用示例 ```shell -$ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/theme/?name=lewd +$ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/theme/?name=lewd&key= ``` ```json @@ -124,11 +133,12 @@ $ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/theme/?name=lewd ## `/alltables/` 接口 -> 此接口可以获取数据库内所有的表名 > 返回的数据中不包含`ReqCount` +> 此接口可以获取数据库内所有的表名 > 返回的数据中不包含`ReqCount` + > 无参数 ```shell -$ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/alltables/ +$ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/alltables/&key= ``` ```json @@ -141,6 +151,18 @@ $ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/alltables/ } ``` +## `/export/` 接口 + +> 此接口可以导出应用的数据库文件 + +> 无参数 + +```shell +$ curl -L -X GET https://requestcounter.herokuapp.com/api/v1/export/&key= +``` + +> 该接口请求成功后返回文件 + # 关于 ## 开源