From bf35508e95862c91aef16ef6c9d0bdbdad922513 Mon Sep 17 00:00:00 2001 From: MarkusJoe Date: Sat, 26 Mar 2022 08:02:25 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99&=E9=87=8D=E6=9E=84=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE,=20=E4=BC=98=E5=8C=96=E9=A1=B9=E7=9B=AE=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Procfile | 2 +- README.md | 10 +- app.py | 90 -------- app/__init__.py | 26 +++ {bin/tests => app/db}/__init__.py | 18 +- app/db/db.py | 138 +++++++++++ app/main/__init__.py | 13 ++ app/main/errors.py | 29 +++ app/main/views.py | 62 +++++ {static => app/static}/favicon.ico | Bin {templates => app/templates}/index.html | 2 +- {templates => app/templates}/view.html | 2 +- app/tests/__init__.py | 6 + app/utils/__init__.py | 23 ++ {bin => app}/utils/view.py | 23 +- bin/__init__.py | 45 ---- bin/conf/config.yml | 34 --- bin/db/__init__.py | 19 -- bin/db/db.py | 290 ------------------------ bin/log/.gitkeep | 0 bin/tests/down.py | 101 --------- bin/tests/downloader.py | 55 ----- bin/tests/length_down.py | 12 - bin/tests/stress_test.py | 18 -- bin/tests/test.http | 4 - bin/utils/__init__.py | 9 - bin/utils/error.py | 62 ----- bin/utils/features.py | 36 --- bin/utils/logger.py | 55 ----- bin/utils/settings.py | 100 -------- docs/README.md | 6 +- manage.py | 17 ++ requirements.txt | 3 - 33 files changed, 344 insertions(+), 966 deletions(-) delete mode 100644 app.py create mode 100644 app/__init__.py rename {bin/tests => app/db}/__init__.py (58%) create mode 100644 app/db/db.py create mode 100644 app/main/__init__.py create mode 100644 app/main/errors.py create mode 100644 app/main/views.py rename {static => app/static}/favicon.ico (100%) rename {templates => app/templates}/index.html (93%) rename {templates => app/templates}/view.html (90%) create mode 100644 app/tests/__init__.py create mode 100644 app/utils/__init__.py rename {bin => app}/utils/view.py (84%) delete mode 100644 bin/__init__.py delete mode 100644 bin/conf/config.yml delete mode 100644 bin/db/__init__.py delete mode 100644 bin/db/db.py delete mode 100644 bin/log/.gitkeep delete mode 100644 bin/tests/down.py delete mode 100644 bin/tests/downloader.py delete mode 100644 bin/tests/length_down.py delete mode 100644 bin/tests/stress_test.py delete mode 100644 bin/tests/test.http delete mode 100644 bin/utils/__init__.py delete mode 100644 bin/utils/error.py delete mode 100644 bin/utils/features.py delete mode 100644 bin/utils/logger.py delete mode 100644 bin/utils/settings.py create mode 100644 manage.py diff --git a/Procfile b/Procfile index 8001d1a..f213311 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: gunicorn app:app \ No newline at end of file +web: gunicorn manage:app \ No newline at end of file diff --git a/README.md b/README.md index bc2b95e..a30a2ac 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,11 @@ ### 部署到Heroku 1. `fork` 本项目到你的仓库 -2. 此步骤为 `非必要` 你可以在 `fork` 本仓库到你的仓库时, 修改 `./bin/conf/config.yml` 内的配置文件来修改配置 (前提时你的仓库为私人仓库, 否则可能泄露一些私人信息) -3. 在[Heroku](https://www.heroku.com/) 注册账号 -4. 在[Dashboard](https://dashboard.heroku.com/apps) 新建App -5. 流程: 进入网址 -> 点击右上角`New` -> 点击 `Create new app` -> 输入App名称 -> `Create app` -> 选择`Github` (登陆完成后) -> 点击`Search` -> +2. 在[Heroku](https://www.heroku.com/) 注册账号 +3. 在[Dashboard](https://dashboard.heroku.com/apps) 新建App +4. 流程: 进入网址 -> 点击右上角`New` -> 点击 `Create new app` -> 输入App名称 -> `Create app` -> 选择`Github` (登陆完成后) -> 点击`Search` -> 选择你fork的项目并点击`Connect` -> 滑到末尾点击`Deploy branch`(如果你想在仓库更新时自动部署的话可以把`Enable Automatic Deploy`勾选) -> 等待完成 -6. App的地址就是 `App名称` + `.herokuapp.com` +5. App的地址就是 `App名称` + `.herokuapp.com` ### 部署到本地服务器 @@ -56,7 +55,6 @@ $ python3 app.py - 最大可以计数`10`位数, 超过则重置 - 可以自定义显示位数默认`7`位数最大`10`位 - 可以自己选择更多的主题只需要加上请求参数: `theme` 再加上想要的主题即可 -- 在`theme`请求参数中写入`ls`可以获取所有可选的主题 (来自于`./bin/assets/theme.db`) # 一些信息 diff --git a/app.py b/app.py deleted file mode 100644 index c5b0aa6..0000000 --- a/app.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/2/3 -# @File Name: app.py - - -from gevent import pywsgi -from flask import Flask -from flask import Response -from flask import request -from flask import make_response -from flask import render_template -from bin.utils.logger import logger -from bin.utils.features import Features -from bin.utils.settings import Settings -from bin.utils.error import ErrorProcess -from bin.utils.view import view_template - -conf = Settings() - -if conf.type.lower() == 'mysql': - from bin.db.db import MySQL as db -else: - from bin.db.db import SQLite as db - -app = Flask(__name__, static_url_path='') -app.config['JSON_SORT_KEYS'] = False # 设置JSON消息不根据字母顺序重新排序 -app.config['JSON_AS_ASCII'] = False # 设置JSON消息显示中文 - - -@app.before_request -def requests_log() -> None: - """ - 向日志文件内请求记录 - :return: - """ - if request.path != '/favicon.ico': # 不记录favicon.ico的请求记录 - logger.info(f'{request.remote_addr} {request.method} {request.base_url}') - - -@app.route('/count/', methods=['GET', 'POST']) # 允许 GET 和 POST 方法 -def main(name: str) -> Response or str: - """ - API 页面函数 - :return: - """ - args = request.args - theme = args.get('theme', type=str) - length = args.get('length', type=int) - if not bool(theme): # 判断主题是否存在查询参数内如果不存在则使用配置文件内的默认主题 - theme = conf.default_style - elif theme == 'ls': - return Features(db).theme_list() - if not bool(length): - length = 7 - if not db().exists_name(name): - db().insert(name) - count = db().fetch(name) - if not db().exists_table(theme): - return ErrorProcess(db).theme_error(theme) - if 7 <= int(length) <= 10: # 限定自定义长度阈值 - view_number = '0' * (int(length) - len(str(count[1]))) + str(count[1]) - response = make_response(view_template(theme, int(length), name, view_number, db)) # 设置响应体 和 响应头 - response.headers['Content-Type'] = 'image/svg+xml; charset=utf-8' - response.headers['cache-control'] = 'max-age=0, no-cache, no-store, must-revalidate' - return response - else: - return ErrorProcess(db).length_error(length) - - -@app.route('/', methods=['GET', 'POST']) -def home() -> str: - """ - 主页面 - :return: - """ - return render_template('index.html', remote_address=request.remote_addr) - - -if __name__ == '__main__': - logger.info(f'服务器已在 http://127.0.0.1:{conf.port} 运行') - try: - server = pywsgi.WSGIServer((conf.host, conf.port), app, log=None) # log=None 关闭日志输出, 使用自写的日志器记录 - server.serve_forever() - except OSError: - logger.critical(f'{conf.port} 端口被占用 已退出') - except KeyboardInterrupt: - logger.info('程序已退出') diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..6df1310 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -- coding:utf-8 -- +# @Author: markushammered@gmail.com +# @Development Tool: PyCharm +# @Create Time: 2022/3/25 +# @File Name: __init__.py + + +from flask import Flask +from .main import main +from .db.db import SQLite + + +class Config: + """基类配置""" + JSON_SORT_KEYS = False + JSON_AS_ASCII = False + + +def create_app(): + """创建主app""" + app = Flask(__name__) + app.register_blueprint(main) + app.config.from_object(Config) + + return app diff --git a/bin/tests/__init__.py b/app/db/__init__.py similarity index 58% rename from bin/tests/__init__.py rename to app/db/__init__.py index 3d22f75..5f1ffbf 100644 --- a/bin/tests/__init__.py +++ b/app/db/__init__.py @@ -1,12 +1,6 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/2/3 -# @File Name: __init__.py.py - - -import sys - -sys.stdout.write('禁止调用') -sys.exit(-1) +#!/usr/bin/env python3 +# -- coding:utf-8 -- +# @Author: markushammered@gmail.com +# @Development Tool: PyCharm +# @Create Time: 2022/3/25 +# @File Name: __init__.py.py diff --git a/app/db/db.py b/app/db/db.py new file mode 100644 index 0000000..fa470ea --- /dev/null +++ b/app/db/db.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# -- coding:utf-8 -- +# @Author: markushammered@gmail.com +# @Development Tool: PyCharm +# @Create Time: 2022/3/25 +# @File Name: db.py + + +import sqlite3 +from typing import List + + +class SQLite: + """操作SQLite数据库""" + + def __init__(self) -> None: + """ + 初始化SQLite对象 + 完成后会自动提交, 自动关闭 + """ + self.__path = './app/db/data.db' + self.__conn = sqlite3.connect(self.__path) + self.__cursor = self.__conn.cursor() + + def __del__(self) -> None: + """ + 自动提交 + 自动关闭连接 + :return: + """ + self.__conn.commit() + self.__cursor.close() + self.__conn.close() + + @property + def show_tables(self) -> list: + """ + 列出所有在数据库内的表 + :return: + """ + self.__cursor.execute('select * from sqlite_master where type="table"') + tables = self.__cursor.fetchall() + lst = [] + for table in tables: + lst.append(table[1]) + del tables + return lst + + def exists_name(self, name: str) -> bool: + """ + 判断当前名称是否在数据库内 + 调用self.fetch()方法 + :param name: + :return: + """ + status = self.fetch(name, True) + if status == (): + return False + else: + return True + + def exists_table(self, table: str) -> bool: + """ + 判断表是否在数据库内 + 调用self.fetching_table()方法 + :param table: + :return: + """ + tables = self.show_tables + if table in tables: + return True + else: + return False + + def insert(self, name: str, times: int = 0) -> bool: + """ + 插入数据 + :param name: + :param times: + :return: + """ + self.__cursor.execute( + 'insert into reqcount (name, times) values("%(name)s", %(times)s)' % {'name': name, 'times': times}) + return True + + def delete(self, name: str) -> bool: + """ + 删除数据 + 暂时不会用到此接口 + :param name: + :return: + """ + self.__cursor.execute('delete from reqcount where name="%(name)s"' % {'name': name}) + return True + + def fetch(self, name: str, is_check: bool = False) -> tuple: + """ + 抓取数据 + :param name: + :param is_check: + :return: + """ + self.__cursor.execute('select * from reqcount where name="%(name)s"' % {'name': name}) + data = self.__cursor.fetchall()[0] + if not is_check: + self.update(name, data[-1]) + return data + + def update(self, name: str, times: int) -> bool: + """ + 更新数据 + :param name: + :param times: + :return: + """ + self.__cursor.execute( + 'update reqcount set times=%(times)s where name="%(name)s"' % {'times': times + 1, 'name': name}) + return True + + def fetching_table(self, table: str) -> List[dict]: + """ + 抓取主题表的数据 + :param table: + :return: + """ + status = self.exists_table(table) + if status: + tables = self.__cursor.execute('select * from %(table)s' % {'table': table}) + lst = [] + for data in tables: + lst.append({'index': data[0], + 'base64': data[1], + 'width': data[2], + 'height': data[3]}) + del tables + return lst + else: + return [] diff --git a/app/main/__init__.py b/app/main/__init__.py new file mode 100644 index 0000000..e609c02 --- /dev/null +++ b/app/main/__init__.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# -- coding:utf-8 -- +# @Author: markushammered@gmail.com +# @Development Tool: PyCharm +# @Create Time: 2022/3/25 +# @File Name: __init__.py.py + + +from flask import Blueprint + +main = Blueprint('main', __name__) + +from . import views, errors diff --git a/app/main/errors.py b/app/main/errors.py new file mode 100644 index 0000000..aefa404 --- /dev/null +++ b/app/main/errors.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# -- coding:utf-8 -- +# @Author: markushammered@gmail.com +# @Development Tool: PyCharm +# @Create Time: 2022/3/25 +# @File Name: errors.py + +from . import main + + +@main.app_errorhandler(404) +def page_not_found(e): + """ + 404页面未找到 + :param e: + :return: + """ + return {'code': 404, 'msg': '页面未找到', 'data': []}, 404 + + +@main.app_errorhandler(500) +def internal_server_error(e): + """ + 500服务器内部错误 + :param e: + :return: + """ + return {'code': 500, 'msg': '服务器内部错误', 'data': []}, 500 + diff --git a/app/main/views.py b/app/main/views.py new file mode 100644 index 0000000..a475110 --- /dev/null +++ b/app/main/views.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -- coding:utf-8 -- +# @Author: markushammered@gmail.com +# @Development Tool: PyCharm +# @Create Time: 2022/3/25 +# @File Name: views.py + + +from flask import abort +from flask import jsonify +from flask import request +from flask import Response +from flask import make_response +from flask import render_template +from . import main +from ..db.db import SQLite as db +from ..utils.view import view + + +@main.route('/', methods=['GET', 'POST']) +def index(): + """ + 主页 + :return: + """ + return render_template('index.html', remote_address=request.remote_addr) + + +@main.route('/api/v1/', methods=['GET', 'POST']) +@main.route('/count/', methods=['GET', 'POST']) +def api_v1(name: str): + length = request.args.get('length', type=int) + theme = request.args.get('theme', type=str) + if not bool(length): + length = 7 + if not bool(theme): + theme = 'lewd' + if not db().exists_name(name): + db().insert(name) + if not db().exists_table(theme): + abort(500) + if 7 <= length <= 10: + data = db().fetch(name) + number = '0' * (length - len(str(data[-1]))) + str(data[-1]) + # 将设置的长度减去数据库内已有的数据库字符串长度剩下的结果为[0]的个数 + # 再将这个字符串后拼接上数据库内已有次数的字符串, 就可以得到最终的结果 + response = make_response(view(theme, int(length), name, number)) + response.headers['Content-Type'] = 'image/svg+xml; charset=utf-8' + response.headers['cache-control'] = 'max-age=0, no-cache, no-store, must-revalidate' + return response + return abort(500) + + +@main.route('/exists-table', methods=['GET', 'POST']) +@main.route('/exists', methods=['GET', 'POST']) +def show_tables() -> Response: + """ + 返回已有表 + :return: + """ + tables = db().show_tables + return jsonify({'code': 200, 'msg': '已有表', 'data': [tables]}) diff --git a/static/favicon.ico b/app/static/favicon.ico similarity index 100% rename from static/favicon.ico rename to app/static/favicon.ico diff --git a/templates/index.html b/app/templates/index.html similarity index 93% rename from templates/index.html rename to app/templates/index.html index d67c2a7..b38d953 100644 --- a/templates/index.html +++ b/app/templates/index.html @@ -24,7 +24,7 @@
服务端使用Flask开发没有使用异步编程
- 点击查看本地部署示例 + 点击查看本地部署示例
点击查看已部署好的调用示例
diff --git a/templates/view.html b/app/templates/view.html similarity index 90% rename from templates/view.html rename to app/templates/view.html index bd8655b..1553c74 100644 --- a/templates/view.html +++ b/app/templates/view.html @@ -1,7 +1,7 @@ - {{ title }} + {{ title }} | {{ address }} {% for value in context %} str or tuple[bool, str]: +def view(theme: str, + length: int, + name: str, + count: str + ) -> str or Tuple[bool, str]: """ 渲染模板 - :param style: + :param theme: :param length: :param name: :param count: - :param DB: :return: """ - datas = DB().fetching_table(style) + datas = db().fetching_table(theme) context = [] for i in count: if i == '0': @@ -70,7 +75,7 @@ def view_template(style: str, length: int, name: str, count: str, DB) -> str or general_height = datas[0]['height'] # 总宽度 return render_template('view.html', - context=context, - title=name, - general_height=general_height, - general_width=general_width) + context=context, + title=name, + general_height=general_height, + general_width=general_width) diff --git a/bin/__init__.py b/bin/__init__.py deleted file mode 100644 index ad30f51..0000000 --- a/bin/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/2/17 -# @File Name: __init__.py - - -import os -import time -import requests -from bin.utils.logger import logger - - -def cost(func): - def wrapper(*args, **kwargs): - logger.info('开始下载') - s = time.time() - execute = func(*args, **kwargs) - e = time.time() - logger.info(f'下载结束, 用时: {round(e - s, 2)} s') - return execute - - return wrapper - - -@cost -def download(url: str) -> None: - """ - 单线程下载资源 - :param url: - :return: - """ - content = requests.get(url, timeout=10, stream=True).content - with open('./bin/db/data.db', 'wb') as fp: - fp.write(content) - - -if __name__ != '__main__': - if not os.path.exists('./bin/db/data.db'): - logger.warning('数据库文件不存在, 即将开始下载') - try: - download('https://resource-base.herokuapp.com/download/data.db') - except requests.Timeout: - logger.critical('连接超时') diff --git a/bin/conf/config.yml b/bin/conf/config.yml deleted file mode 100644 index 9c5b287..0000000 --- a/bin/conf/config.yml +++ /dev/null @@ -1,34 +0,0 @@ -servers: - # 服务器建立的地址 - host: "0.0.0.0" - # 服务器监听的端口 - port: 5000 - - -view: - # 默认显示主题 - style: "lewd" - - -database: - # 数据库类型 - # 可选 'SQLite' 'MySQL' - type: "SQLite" - SQLite: - path: "./bin/db/data.db" - MySQL: - # 数据库地址 - host: "127.0.0.1" - # 数据库端口 - port: 3306 - # 数据库用户 - user: "" - # 数据库密码 - password: "" - # 数据库名称 - # 如果该数据库不存在则自动创建 - db: "" - - - - diff --git a/bin/db/__init__.py b/bin/db/__init__.py deleted file mode 100644 index bc9f86b..0000000 --- a/bin/db/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/2/4 -# @File Name: __init__.py.py - -import os -from bin.utils.logger import logger -from bin.utils.settings import Settings - -if __name__ != '__main__': - if Settings().type.lower() == 'mysql' and not os.path.exists('./static/origin.sql'): - logger.warning('当前使用的数据库为MySQL请前往 https://filebase.vercel.app/db.sql 下载sql文件') - logger.warning('并使用 source 命令来导入MySQL数据库') - logger.warning('此消息只显示一次, 下次启动不显示') - open('./static/origin.sql', 'w').close() - -__all__ = ['db'] diff --git a/bin/db/db.py b/bin/db/db.py deleted file mode 100644 index 2f45040..0000000 --- a/bin/db/db.py +++ /dev/null @@ -1,290 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/2/24 -# @File Name: db.py - - -import sqlite3 -import pymysql -from bin.utils.settings import Settings - -conf = Settings() - - -class SQLite: - """操作SQLite数据库""" - - def __init__(self, **kwargs) -> None: - """ - 初始化SQLite对象 - 完成后会自动提交, 自动关闭 - :param path: 数据库路径 - """ - self.__path = './bin/db/data.db' - self.__conn = sqlite3.connect(self.__path) - self.__cursor = self.__conn.cursor() - - def __del__(self) -> None: - """ - 自动提交 - 自动关闭连接 - :return: - """ - self.__conn.commit() - self.__cursor.close() - self.__conn.close() - - @property - def show_tables(self) -> list: - """ - 列出所有在数据库内的表 - :return: - """ - self.__cursor.execute('select * from sqlite_master where type="table"') - tables = self.__cursor.fetchall() - lst = [] - for table in tables: - lst.append(table[1]) - del tables - return lst - - def exists_name(self, name: str) -> bool: - """ - 判断当前名称是否在数据库内 - 调用self.fetch()方法 - :param name: - :return: - """ - status = self.fetch(name, True) - if status == (): - return False - else: - return True - - def exists_table(self, table: str) -> bool: - """ - 判断表是否在数据库内 - 调用self.fetching_table()方法 - :param table: - :return: - """ - tables = self.show_tables - if table in tables: - return True - else: - return False - - def insert(self, name: str, times: int = 0) -> bool: - """ - 插入数据 - :param name: - :param times: - :return: - """ - self.__cursor.execute( - 'insert into reqcount (name, times) values("%(name)s", %(times)s)' % {'name': name, 'times': times}) - return True - - def delete(self, name: str) -> bool: - """ - 删除数据 - 暂时不会用到此接口 - :param name: - :return: - """ - self.__cursor.execute('delete from reqcount where name="%(name)s"' % {'name': name}) - return True - - def fetch(self, name: str, is_check: bool = False) -> tuple: - """ - 抓取数据 - :param name: - :param is_check: - :return: - """ - self.__cursor.execute('select * from reqcount where name="%(name)s"' % {'name': name}) - data = self.__cursor.fetchall() - if not is_check: - self.update(name, data[0][1]) - if len(data): - return data[0] - else: - return tuple(data) - - def update(self, name: str, times: int) -> bool: - """ - 更新数据 - :param name: - :param times: - :return: - """ - self.__cursor.execute( - 'update reqcount set times=%(times)s where name="%(name)s"' % {'times': times + 1, 'name': name}) - return True - - def fetching_table(self, table: str) -> list[dict]: - """ - 抓取主题表的数据 - :param table: - :return: - """ - status = self.exists_table(table) - if status: - tables = self.__cursor.execute('select * from %(table)s' % {'table': table}) - lst = [] - for data in tables: - lst.append({'index': data[0], - 'base64': data[1], - 'width': data[2], - 'height': data[3]}) - del tables - return lst - else: - return [] - - -class MySQL: - """操作MySQL数据库""" - - def __init__(self, **kwargs) -> None: - """ - 初始化MySQL对象并创建一个连接 - 创建的连接会在执行完毕后自动提交以及自动关闭 - :param host: 数据库地址 - :param user: 数据库用户名 - :param pwd: 数据库密码 - :param database: 数据库名 - """ - self.__host = conf.m_host - self.__user = conf.m_user - self.__password = conf.m_pwd - self.__database = conf.m_db - self.__conn = pymysql.connect(user=self.__user, - password=self.__password, - host=self.__host, - database=self.__database) # 创建连接 - self.__cursor = self.__conn.cursor() - - def __del__(self): - """ - 自动提交 - 自动关闭连接 - :return: - """ - self.__conn.commit() - self.__cursor.close() - self.__conn.close() - - @property - def show_tables(self) -> list: - """ - 查询已有的表 - :return: - """ - self.__cursor.execute('show tables;') # 列出所有的表名 - tuple_tables = self.__cursor.fetchall() - # 将元组类型转换为列表 - tables = [] - for origin in tuple_tables: - tables.append(origin[0]) - del tuple_tables - return tables - - def exists_name(self, name: str): - """ - 判断是否存在于数据库内 - :param name: - :return: - """ - status = self.fetch(name, True) - if status == (): - return False - else: - return True - - def exists_table(self, table: str) -> bool: - """ - 判断表是否在数据库内 - :param table: - :return: - """ - tables = self.show_tables - if table in tables: - return True - else: - return False - - def insert(self, name: str, times: int = 0) -> bool: - """ - 插入数据 - :param name: - :param times: - :return: - """ - try: - self.__cursor.execute( - 'insert into reqcount(name, times) values("%(name)s", %(times)s);' % {'name': name, 'times': times}) - return True - except pymysql.err.IntegrityError: - return False - - def delete(self, name: str) -> bool: - """ - 删除数据 - 暂时不会用到此接口 - :param name: - :return: - """ - self.__cursor.execute('delete from reqcount where name="%(name)s";' % {'name': name}) - return True - - def fetch(self, name: str, is_check: bool = False) -> tuple: - """ - 抓取数据 - :param name: - :param is_check: - :return: - """ - self.__cursor.execute('select * from reqcount where name="%(name)s";' % {'name': name}) - data = self.__cursor.fetchall() - if not is_check: # 条件适用于使用self.exists_name检查时不将数据库的计数加一 - self.update(name, data[0][1]) - if data == (): - return () - else: - return data[0] - - def update(self, name: str, times: int) -> bool: - """ - 更改数据 - :param name: - :param times: - :return: - """ - self.__cursor.execute( - 'update reqcount set times=%(times)s where name="%(name)s";' % {'times': times + 1, 'name': name}) - return True - - def fetching_table(self, table: str) -> list: - """ - 抓取主题表内的数据 - :param table: - :return: - """ - self.__cursor.execute('select * from %(table)s' % {'table': table}) - datas = self.__cursor.fetchall() - lst = [] - for i in datas: - lst.append({'index': i[0], - 'base64': i[1], - 'width': i[2], - 'height': i[3]} - ) # 向空列表内添加抓取到的数据 -> 将元组数据转换为列表 - - del datas - return lst - - -__all__ = ['SQLite', 'MySQL'] diff --git a/bin/log/.gitkeep b/bin/log/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/bin/tests/down.py b/bin/tests/down.py deleted file mode 100644 index a191639..0000000 --- a/bin/tests/down.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/3/4 -# @File Name: down.py - - -import sys -import time -import requests -import threading - - -class Downloader(threading.Thread): - """继承threading实现多线程""" - - def __init__(self, start_: int, end_: int, t_id: int, url: str, name: str): - """ - 初始化类 - :param start_: seek开始点 - :param end_: seek结束点 - :param t_id: 线程id - :param url: 资源地址 - :param name: 文件名 - """ - super(Downloader, self).__init__() - self.start_ = start_ - self.end_ = end_ - self.t_id = t_id - self.url = url - self.name = name - - def download(self): - """ - 开始下载 - :return: - """ - print(f'{self.t_id} 开始下载') - res = requests.get(self.url, headers={'Range': f'Bytes={self.start_}-{self.end_}'}).content - with open(self.name, 'r+b') as fp: - fp.seek(self.start_) - fp.write(res) - print(f'{self.t_id} 结束下载') - - def run(self): - """ - 开始运行 - :return: - """ - self.download() - - -def cost(func): - def wrapper(*args, **kwargs): - print('文件开始开始下载') - s = time.time() - execute = func(*args, **kwargs) - e = time.time() - print(f'下载结束, 用时: {round(e - s, 2)} s') - return execute - - return wrapper - - -@cost -def main(url: str, threads: int = 4): - """ - 处理各种数据 - :param url: 资源地址 - :param threads: 线程数 - :return: - """ - try: - res = requests.get(url) - except requests.exceptions.ConnectionError: - print('资源连接错误') - sys.exit(-1) - if res.status_code == 302: - url = res.headers['Location'] - print(f'资源已重定向到: {url}') - file_name = url.split('/')[-1] - open(file_name, 'w').close() - file_size = int(res.headers['Content-Length']) - offset = file_size // threads - start = 0 - for i in range(threads): - if i == 0: - end = offset - elif i == threads - 1: - end = file_size - else: - end = i * offset - t = Downloader(start, end, i, url, file_name) - t.start() - t.join() - start = end - - -if __name__ == '__main__': - main('https://i0.hdslb.com/bfs/archive/ef0945f7d54a505953272022063a6335a9d3becf.jpg') diff --git a/bin/tests/downloader.py b/bin/tests/downloader.py deleted file mode 100644 index 8e1ffb9..0000000 --- a/bin/tests/downloader.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/3/2 -# @File Name: downloader.py - - -import requests -import threading - - -class Threaded(threading.Thread): - def __init__(self, s, e, fp, id_, url): - super().__init__() - self.start_ = s - self.end_ = e - self.fp = fp - self.id = id_ - self.url = url - - def download(self): - print(f'线程: {self.id} 开始下载') - res = requests.get(self.url, headers={'Range': f'Bytes={self.start_}-{self.end_}'}).content - self.fp.seek(self.start_) - self.fp.write(res) - print(f'线程: {self.id} 结束下载') - - def run(self): - self.download() - - -def main(url: str, path: str = '.', workers: int = 8): - print(f'本次下载使用线程数: {workers}') - file_name = url.split('/')[-1] - file_size = int(requests.get(url).headers['Content-Length']) - if requests.get(url).status_code == '302': - url = requests.get(url).headers['Location'] - offset = int(file_size / workers) - start = 0 - open(path + file_name, 'wb').close() - fp = open(path + file_name, 'r+b') - for i in range(workers): - if i == workers - 1: - end = file_size - elif i != 0: - end = i * offset - else: - end = offset - Threaded(start, end, fp, i, url).start() - start = end + 1 - - -if __name__ == '__main__': - main('https://i0.hdslb.com/bfs/archive/cc013c0a726082e07772ec77d5c0444ac7d40a6f.jpg') diff --git a/bin/tests/length_down.py b/bin/tests/length_down.py deleted file mode 100644 index b3d4d2f..0000000 --- a/bin/tests/length_down.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/3/13 -# @File Name: length_down.py - - -import requests - - -print(requests.get('http://127.0.0.1:8000/data.db').headers) \ No newline at end of file diff --git a/bin/tests/stress_test.py b/bin/tests/stress_test.py deleted file mode 100644 index b59730a..0000000 --- a/bin/tests/stress_test.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/2/7 -# @File Name: stress_test.py - - -import requests -import threading - - -def thread_(): - print(requests.get('http://127.0.0.1:5000/get?name=MarkusJoe&theme=lewd').elapsed.microseconds) - - -for i in range(100): - threading.Thread(target=thread_).start() diff --git a/bin/tests/test.http b/bin/tests/test.http deleted file mode 100644 index fbc4864..0000000 --- a/bin/tests/test.http +++ /dev/null @@ -1,4 +0,0 @@ -# http -GET http://localhost:5000/get?name=HTTPtest -Accept: application/json - diff --git a/bin/utils/__init__.py b/bin/utils/__init__.py deleted file mode 100644 index bcef236..0000000 --- a/bin/utils/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/2/12 -# @File Name: __init__.py - - -__all__ = ['error', 'logger', 'features', 'settings', 'view'] diff --git a/bin/utils/error.py b/bin/utils/error.py deleted file mode 100644 index 19cdb84..0000000 --- a/bin/utils/error.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/2/9 -# @File Name: error.py - - -from typing import Optional -from flask import jsonify -from flask import Response -from bin.utils.logger import logger - - -class ErrorProcess: - """处理错误的页面""" - - def __init__(self, db) -> None: - """ - 初始化 - :param db: - """ - self.db = db # 数据库对象 - self.response = {'code': Optional[int], - 'msg': Optional[str], - 'data': Optional[list]} - - def theme_error(self, theme: str) -> Response: - """ - 错误的主题 - :param theme: - :return: - """ - logger.debug('错误的主题') - table_list = self.db().show_tables - self.response['code'] = -2 - self.response['msg'] = f'错误的主题: {theme}. 以下是已保存的主题' - self.response['data'] = table_list - return jsonify(self.response) - - def length_error(self, length: int or str) -> Response: - """ - 数值太长显示此页面 - :param length: - :return: - """ - self.response['code'] = -2 - self.response['msg'] = f'错误的长度: {length}' - self.response['data'] = [] - return jsonify(self.response) - - def count_error(self, name: str) -> Response: - """ - 数据过长重置数据 - :param name: - :return: - """ - self.db().update(name, 0) - self.response['code'] = 200 - self.response['msg'] = '当前长度已超过最大可计数范围. 已将此名称的计数器重置为零' - self.response['data'] = [] - return jsonify(self.response) diff --git a/bin/utils/features.py b/bin/utils/features.py deleted file mode 100644 index 8a55f64..0000000 --- a/bin/utils/features.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/2/28 -# @File Name: features.py - - -from typing import Optional -from flask import jsonify -from flask import Response - - -class Features: - """特殊操作""" - - def __init__(self, db) -> None: - """ - 初始化 - :param db: - """ - self.db = db - self.response = {'code': Optional[int], - 'msg': Optional[str], - 'data': Optional[list]} - - def theme_list(self) -> Response: - """ - 读取已有数据库并返回一个列表 - :return: - """ - table_list = self.db().show_tables - self.response['code'] = 200 - self.response['msg'] = '当前已保存到数据库的主题如下' - self.response['data'] = table_list - return jsonify(self.response) diff --git a/bin/utils/logger.py b/bin/utils/logger.py deleted file mode 100644 index 3ac569c..0000000 --- a/bin/utils/logger.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2021/12/15 -# @File Name: logger.py - - -import time -import logging.handlers -from logging import Logger -from colorlog import ColoredFormatter - - -class MakeLogger: - def __init__(self) -> None: - self.date_format = '%H:%M:%S' - self.format_console = '%(log_color)s[%(asctime)s] |%(filename)s[%(lineno)-3s] |%(levelname)-8s |%(message)s' - self.format_file = '[%(asctime)s] |%(filename)s[%(funcName)sline:%(lineno)d] |%(levelname)-8s |%(message)s' - - def setup_logger(self) -> Logger: - """ - 建立日志器 - :return: - """ - formatter = ColoredFormatter(fmt=self.format_console, - datefmt=self.date_format, - reset=True, - log_colors={ - 'DEBUG': 'light_purple', - 'INFO': 'light_cyan', - 'WARNING': 'yellow', - 'ERROR': 'red', - 'CRITICAL': 'red,bold_red'}) # 定义终端输出颜色 - formatter_file = logging.Formatter(fmt=self.format_file, - datefmt=self.date_format) # 文件输入不使用颜色 - - _logger = logging.getLogger('RequestCounter') # 设置日志器名称 - _logger.setLevel(logging.INFO) # 设置等级 - - console_logger = logging.StreamHandler() # 输出到终端 - console_logger.setFormatter(formatter) # 设置输出格式化 - log_name = time.strftime('%Y-%m-%d %H') # 一小时内使用的日志文件都是同一个 - file_logger = logging.handlers.RotatingFileHandler(filename=f'./bin/log/{log_name}.log', - maxBytes=102400, - backupCount=5, - encoding='utf-8') # 每个日志文件最大102400字节(100Kb) - file_logger.setFormatter(formatter_file) - _logger.addHandler(console_logger) - _logger.addHandler(file_logger) - return _logger - - -if __name__ != '__main__': - logger = MakeLogger().setup_logger() diff --git a/bin/utils/settings.py b/bin/utils/settings.py deleted file mode 100644 index 909d559..0000000 --- a/bin/utils/settings.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python3 -# -- coding:utf-8 -- -# @Author: markushammered@gmail.com -# @Development Tool: PyCharm -# @Create Time: 2022/2/23 -# @File Name: settings.py - - -import yaml -from typing import Dict - - -class Settings: - def __init__(self): - self.__setting_file = './bin/conf/config.yml' - with open(self.__setting_file, encoding='utf-8') as fp: - self.data = yaml.load(fp.read(), Loader=yaml.Loader) - self.__servers = self.data['servers'] - self.__database = self.data['database'] - self.__view = self.data['view'] - - def all_data(self) -> Dict: - """ - 获取原始配置文件 - :return: - """ - return self.data - - @property - def default_style(self) -> str: - """ - 默认显示主题 - :return: - """ - return self.__view['style'] - - @property - def host(self) -> str: - """ - 应用地址 - :return: - """ - return self.__servers['host'] - - @property - def port(self) -> str: - """ - 应用端口 - :return: - """ - return self.__servers['port'] - - @property - def type(self) -> str: - """ - 数据库类型 - :return: - """ - return self.__database['type'] - - @property - def m_host(self) -> str: - """ - mysql数据库的地址 - :return: - """ - return self.__database['MySQL']['host'] - - @property - def m_port(self) -> int: - """ - mysql数据库的端口 - 默认3306 - :return: - """ - return self.__database['MySQL']['port'] - - @property - def m_user(self) -> str: - """ - mysql数据库的用户名 - :return: - """ - return self.__database['MySQL']['user'] - - @property - def m_pwd(self) -> str: - """ - mysql数据库的密码 - :return: - """ - return self.__database['MySQL']['password'] - - @property - def m_db(self) -> str: - """ - 保存数据库数据库名 - :return: - """ - return self.__database['MySQL']['db'] diff --git a/docs/README.md b/docs/README.md index 4771b04..0662b0d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,14 +22,14 @@ # 基本信息 - Python版本: `3.10.x` - 请求方法: `GET` `POST` -- 请求地址: `/count/` -- 你需要在 `/count/` 后加入你需要使用的名称来进行统计 +- 请求地址: `/count/` or `/api/v1/` +- 你需要在 请求地址末尾加入你需要使用的名称来进行计数 - 可选参数: `theme` `length` ## 获取主题数据库文件 * 此接口没有在本项目中, 该仓库为私人仓库暂不开源 -* 访问 `https://themedatabase.vercel.app/assets` 获取 +* 访问 `http://resource-base.herokuapp.com/` or `https://filebase.vercel.app/download/data.db` 获取数据库文件 * 由于速度较慢建议使用多线程进行下载 * 项目中自动下载主题数据库的文件也是来自此处 diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..00136bc --- /dev/null +++ b/manage.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# -- coding:utf-8 -- +# @Author: markushammered@gmail.com +# @Development Tool: PyCharm +# @Create Time: 2022/3/25 +# @File Name: manage.py + + +from gevent import pywsgi +from app import create_app + +app = create_app() + + +if __name__ == '__main__': + server = pywsgi.WSGIServer(('0.0.0.0', 5000), app, log=None) + server.serve_forever() diff --git a/requirements.txt b/requirements.txt index 8f1c1cc..13421f9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,8 +2,5 @@ flask~=2.0.3 requests~=2.27.1 gevent~=21.12.0 gunicorn~=20.1.0 -colorlog~=6.6.0 -PyMySQL~=1.0.2 -PyYAML~=6.0 Werkzeug~=2.0.3 itsdangerous~=2.1.0 \ No newline at end of file