Archived
删除历史版本, 初始化仓库
This commit is contained in:
37 files changed
+1773
No files matched your search
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/3/25
|
||||
# @File Name: __init__.py
|
||||
|
||||
|
||||
import os
|
||||
from flask import Flask
|
||||
from flask_sslify import SSLify
|
||||
from .main import main as main_blueprint
|
||||
from .api import api as api_blueprint
|
||||
from .config import config
|
||||
from .utils.password import generate_pwd
|
||||
|
||||
|
||||
def create_app(config_name):
|
||||
"""
|
||||
创建 app
|
||||
:param config_name:
|
||||
:return:
|
||||
"""
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(config[config_name])
|
||||
config[config_name].init_app(app)
|
||||
if app.config['SSL_REDIRECT']:
|
||||
SSLify(app)
|
||||
|
||||
app.register_blueprint(main_blueprint)
|
||||
app.register_blueprint(api_blueprint, url_prefix='/api/v1/')
|
||||
|
||||
access_key = os.getenv('ACCESS_KEY')
|
||||
secret = app.config['SECRET_KEY']
|
||||
try:
|
||||
if not access_key and not secret:
|
||||
access_key = generate_pwd(16)
|
||||
elif not access_key and secret:
|
||||
access_key = secret
|
||||
finally:
|
||||
os.environ['ACCESS_KEY'] = access_key
|
||||
|
||||
app.logger.info('访问密钥: {}'.format(access_key))
|
||||
|
||||
return app
|
||||
|
||||
|
||||
__all__ = [
|
||||
'db',
|
||||
'main',
|
||||
'tests',
|
||||
'utils',
|
||||
'create_app'
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/4/9
|
||||
# @File Name: __init__.py.py
|
||||
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
api = Blueprint('api', __name__)
|
||||
|
||||
from . import views, errors
|
||||
|
||||
__all__ = [
|
||||
'api',
|
||||
'views',
|
||||
'errors'
|
||||
]
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: Pycharm
|
||||
# @Create Time: 2022/5/2
|
||||
# @File Name: auth.py
|
||||
|
||||
|
||||
import os
|
||||
from flask import request
|
||||
from flask_httpauth import HTTPTokenAuth
|
||||
|
||||
auth = HTTPTokenAuth()
|
||||
|
||||
|
||||
@auth.verify_token
|
||||
def verify_token(token):
|
||||
secret_key = os.getenv('ACCESS_KEY')
|
||||
key = request.args.get('key')
|
||||
if token or key == secret_key:
|
||||
return True
|
||||
elif (token == 'unittest' or
|
||||
key == 'unittest') and \
|
||||
'system' in request.path.split('/'): # 限定在system这个接口使用unittest密钥获取数据
|
||||
return True
|
||||
return False
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/4/9
|
||||
# @File Name: errors.py
|
||||
|
||||
from . import api
|
||||
from .auth import auth
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
@api.errorhandler(403)
|
||||
@auth.error_handler
|
||||
def forbidden(e):
|
||||
return jsonify(
|
||||
{
|
||||
'code': 403,
|
||||
'msg': 'API: Permission Denied.',
|
||||
'data': None
|
||||
}
|
||||
), 403
|
||||
|
||||
|
||||
@api.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
return jsonify(
|
||||
{
|
||||
'code': 404,
|
||||
'msg': 'API: Page Not Found.',
|
||||
'data': None
|
||||
}
|
||||
), 404
|
||||
|
||||
|
||||
@api.errorhandler(500)
|
||||
def internal_server_error(e):
|
||||
return jsonify(
|
||||
{
|
||||
'code': 500,
|
||||
'msg': 'API: Internal Server Error.',
|
||||
'data': None
|
||||
}
|
||||
), 500
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/4/9
|
||||
# @File Name: views.py
|
||||
|
||||
|
||||
import sys
|
||||
import psutil
|
||||
import platform
|
||||
from flask import jsonify
|
||||
from flask import request
|
||||
from flask import send_file
|
||||
from ..db.db import SQLite as db
|
||||
from . import api
|
||||
from .auth import auth
|
||||
|
||||
|
||||
@api.route('/test/', methods=['GET', 'POST'])
|
||||
def test_api():
|
||||
"""
|
||||
测试专用接口
|
||||
:return:
|
||||
"""
|
||||
data = db().fetch('test-example')
|
||||
return str(data[1])
|
||||
|
||||
|
||||
@api.route('/overall/', methods=['GET', 'POST'])
|
||||
def overall():
|
||||
"""
|
||||
查询ReqCount表内所有的源数据
|
||||
:return:
|
||||
"""
|
||||
limit = request.args.get('limit', type=int)
|
||||
try:
|
||||
data = db().exec('select * from reqcount;')
|
||||
format_data = []
|
||||
for i in data:
|
||||
format_data.append({'name': i[0], 'times': i[1]})
|
||||
return jsonify(
|
||||
{
|
||||
'code': 200,
|
||||
'msg': 'Success. Total: %s row(s)' % len(data),
|
||||
'data': format_data[:limit]
|
||||
}
|
||||
), 200
|
||||
except Exception as e:
|
||||
return jsonify(
|
||||
{
|
||||
'code': -200,
|
||||
'msg': 'Error',
|
||||
'data': e
|
||||
}
|
||||
), 500
|
||||
|
||||
|
||||
@api.route('/query/', methods=['GET', 'POST'])
|
||||
def query():
|
||||
"""
|
||||
查询指定名称的计数数据
|
||||
:return:
|
||||
"""
|
||||
name = request.args.get('name', type=str)
|
||||
nochange = request.args.get('nochange', type=bool)
|
||||
if nochange:
|
||||
nochange = True
|
||||
if not db().exists_name(name):
|
||||
return jsonify(
|
||||
{
|
||||
'code': -200,
|
||||
'msg': 'Failed. No such name',
|
||||
'data': None
|
||||
}
|
||||
), 404
|
||||
else:
|
||||
data = db().fetch(name, nochange)
|
||||
return jsonify(
|
||||
{
|
||||
'code': 200,
|
||||
'msg': 'Success',
|
||||
'data': data
|
||||
}
|
||||
), 200
|
||||
|
||||
|
||||
@api.route('/theme/', methods=['GET', 'POST'])
|
||||
def theme():
|
||||
"""
|
||||
查询数据库内主题的源数据
|
||||
:return:
|
||||
"""
|
||||
_theme = request.args.get('name', type=str)
|
||||
if not db().exists_table(_theme):
|
||||
return jsonify(
|
||||
{
|
||||
'code': 404,
|
||||
'msg': 'Failed. No such theme',
|
||||
'data': None
|
||||
}
|
||||
), 404
|
||||
else:
|
||||
data = db().fetching_table(_theme)
|
||||
return jsonify(
|
||||
{
|
||||
'code': 200,
|
||||
'msg': 'Success',
|
||||
'data': data
|
||||
}
|
||||
), 200
|
||||
|
||||
|
||||
@api.route('/alltables/', methods=['GET', 'POST'])
|
||||
def all_tables():
|
||||
"""
|
||||
查询所有已有表
|
||||
:return:
|
||||
"""
|
||||
data = db().show_tables
|
||||
return jsonify(
|
||||
{
|
||||
'code': 200,
|
||||
'msg': 'Success',
|
||||
'data': data
|
||||
}
|
||||
), 200
|
||||
|
||||
|
||||
@api.route('/export/', methods=['GET', 'POST'])
|
||||
@auth.login_required
|
||||
def export():
|
||||
"""
|
||||
导出数据库文件
|
||||
:return:
|
||||
"""
|
||||
return send_file('./db/data.sqlite', as_attachment=True), 200
|
||||
|
||||
|
||||
@api.route('/system/', methods=['GET', 'POST'])
|
||||
@auth.login_required
|
||||
def system_info():
|
||||
"""
|
||||
获取当前部署服务器的系统状态信息
|
||||
:return:
|
||||
"""
|
||||
_platform = platform.system()
|
||||
_cpu_count = psutil.cpu_count()
|
||||
_memory_size = psutil.virtual_memory()
|
||||
_total_memory_size = round(_memory_size[0] / 1024 / 1024 / 1024, 2)
|
||||
_free_memory_size = round(_memory_size[4] / 1024 / 1024 / 1024, 2)
|
||||
_used_memory_size = round(_memory_size[3] / 1024 / 1024 / 1024, 2)
|
||||
_percent = _memory_size[2]
|
||||
_py_version = sys.version
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
'code': 200,
|
||||
'msg': 'Success',
|
||||
'data': {
|
||||
'Platform': _platform,
|
||||
'CPU-Count': _cpu_count,
|
||||
'Memory-Size(GigaBytes)': {
|
||||
'Total': _total_memory_size,
|
||||
'used': _used_memory_size,
|
||||
'Free': _free_memory_size,
|
||||
'Percent': _percent
|
||||
},
|
||||
'Python': _py_version
|
||||
}
|
||||
}
|
||||
), 200
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/4/5
|
||||
# @File Name: config.py
|
||||
|
||||
|
||||
import os
|
||||
import logging
|
||||
import platform
|
||||
from logging.handlers import *
|
||||
|
||||
|
||||
class Config:
|
||||
JSON_SORT_KEYS = False
|
||||
JSON_AS_ASCII = False
|
||||
SSL_REDIRECT = False
|
||||
SECRET_KEY = ''
|
||||
|
||||
@staticmethod
|
||||
def init_app(app):
|
||||
if not os.path.exists('./app/logs'):
|
||||
os.mkdir('./app/logs')
|
||||
|
||||
formatter = logging.Formatter(
|
||||
fmt='[%(asctime)s] |%(filename)s[%(funcName)s:%(lineno)d] |%(levelname)-8s |%(message)s',
|
||||
datefmt='%H:%M:%S')
|
||||
handler = logging.handlers.RotatingFileHandler(filename='./app/logs/access.log',
|
||||
maxBytes=10240,
|
||||
backupCount=10)
|
||||
handler.setFormatter(formatter)
|
||||
handler.setLevel(logging.DEBUG)
|
||||
|
||||
if platform.system() == 'Linux':
|
||||
syslog_handler = SysLogHandler()
|
||||
syslog_handler.setLevel(logging.INFO)
|
||||
app.logger.addHandler(syslog_handler)
|
||||
|
||||
app.logger.addHandler(handler)
|
||||
|
||||
|
||||
class DevelopmentConfig(Config):
|
||||
DEBUG = True
|
||||
|
||||
|
||||
class ProductionConfig(Config):
|
||||
DEBUG = False
|
||||
|
||||
|
||||
class HerokuConfig(ProductionConfig):
|
||||
SSL_REDIRECT = True
|
||||
|
||||
|
||||
class ProfessionalConfig(ProductionConfig):
|
||||
SSL_REDIRECT = True
|
||||
|
||||
|
||||
class VersionConfig:
|
||||
# 暂未开放/Not available
|
||||
version = ['v0.2.4']
|
||||
|
||||
|
||||
config = {
|
||||
'development': DevelopmentConfig,
|
||||
'production': ProductionConfig,
|
||||
'professional': ProfessionalConfig,
|
||||
'heroku': HerokuConfig,
|
||||
|
||||
'default': DevelopmentConfig
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/3/25
|
||||
# @File Name: __init__.py.py
|
||||
|
||||
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
|
||||
|
||||
def download():
|
||||
res = requests.get('https://filebase.vercel.app/download/data.sqlite')
|
||||
with open('./app/db/data.sqlite', 'wb') as fp:
|
||||
fp.write(res.content)
|
||||
|
||||
|
||||
if not os.path.exists('./app/db/data.sqlite'):
|
||||
try:
|
||||
download()
|
||||
except requests.exceptions as error:
|
||||
sys.exit(1)
|
||||
|
||||
__all__ = [
|
||||
'db'
|
||||
]
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
#!/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, Any
|
||||
|
||||
|
||||
class SQLite:
|
||||
"""操作SQLite数据库"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
初始化SQLite对象
|
||||
完成后会自动提交, 自动关闭
|
||||
"""
|
||||
self.__path = './app/db/data.sqlite'
|
||||
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:
|
||||
if table[1] != 'reqcount':
|
||||
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 not bool(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.fetchone()
|
||||
if not is_check:
|
||||
self.update(name, data[-1])
|
||||
self.__cursor.execute('select * from reqcount where name="%(name)s"' % {'name': name})
|
||||
data = self.__cursor.fetchone()
|
||||
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],
|
||||
'url': data[1],
|
||||
'width': data[2],
|
||||
'height': data[3]})
|
||||
del tables
|
||||
return lst
|
||||
else:
|
||||
return []
|
||||
|
||||
def exec(self, cmd: str) -> List[Any]:
|
||||
"""
|
||||
执行特殊sql语句
|
||||
:param cmd:
|
||||
:return:
|
||||
"""
|
||||
self.__cursor.execute(cmd)
|
||||
result = self.__cursor.fetchall()
|
||||
return result
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/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
|
||||
|
||||
__all__ = [
|
||||
'main',
|
||||
'views',
|
||||
'errors'
|
||||
]
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/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
|
||||
from flask import jsonify
|
||||
|
||||
|
||||
@main.app_errorhandler(404)
|
||||
def page_not_found(e):
|
||||
return jsonify(
|
||||
{
|
||||
'code': 404,
|
||||
'msg': '页面未找到',
|
||||
'data': None
|
||||
}
|
||||
), 404
|
||||
|
||||
|
||||
@main.app_errorhandler(500)
|
||||
def internal_server_error(e):
|
||||
return jsonify(
|
||||
{
|
||||
'code': 500,
|
||||
'msg': '服务器内部错误',
|
||||
'data': None
|
||||
}
|
||||
), 500
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/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 request
|
||||
from flask import make_response
|
||||
from flask import render_template
|
||||
from ..db.db import SQLite as db
|
||||
from ..utils.response import index_
|
||||
from . import main
|
||||
|
||||
|
||||
@main.route('/', methods=['GET', 'POST'])
|
||||
def index():
|
||||
"""
|
||||
主页
|
||||
:return:
|
||||
"""
|
||||
return render_template('index.html', remote_address=request.remote_addr)
|
||||
|
||||
|
||||
@main.route('/count/<string:name>', methods=['GET', 'POST'])
|
||||
def home(name: str):
|
||||
"""
|
||||
主视图
|
||||
:param name:
|
||||
:return:
|
||||
"""
|
||||
length = request.args.get('length', 7, type=int)
|
||||
theme = request.args.get('theme', 'lewd', type=str)
|
||||
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)
|
||||
if len(str(data[1])) >= 10: # 计数的数据长度超过最大位数将次数设为1
|
||||
db().update(name, 0)
|
||||
# 使用设置的长度减去已有数据的长度, 将结果转换为string类型, 再和数据库内的数据进行拼接
|
||||
number = '0' * (length - len(str(data[-1]))) + str(data[-1])
|
||||
response = make_response(index_(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)
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -0,0 +1,15 @@
|
||||
.intro {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.head {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.example {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
color: green;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
function request_async(){
|
||||
// 使用fetch()函数进行异步请求如果服务端响应速度极慢则不会阻塞页面
|
||||
fetch('/api/v1/test').then(
|
||||
response => response.text()
|
||||
).then(
|
||||
text => {
|
||||
document.getElementById('fast_test_example_text').innerText = text
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Request Counter | {{ remote_address }}</title>
|
||||
<link href="{{ url_for('static', filename='favicon.ico') }}" rel="shortcut icon">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='request.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="head">
|
||||
<h1>
|
||||
应用名: 访问次数计数器
|
||||
</h1>
|
||||
<h3>
|
||||
简介: 可以使用SVG矢量图的形式来显示页面的访问次数
|
||||
<br>
|
||||
或者通过API的方式获取原始计数数据
|
||||
</h3>
|
||||
</div>
|
||||
<div class="intro">
|
||||
<div class="current_deployment_test">
|
||||
<i><a href="{{ url_for('main.home', name='test-example') }}">点击查看当但实例调用示例</a></i>
|
||||
</div>
|
||||
<div class="remote_deployment_test">
|
||||
<i><a href="https://requestcounter.herokuapp.com/count/test-example">点击查看已部署好的调用示例</a></i>
|
||||
</div>
|
||||
<div class="note">
|
||||
<i><b>注: 调用支持POST 和 GET 方法请求</b></i>
|
||||
</div>
|
||||
<div class="open_source_protocol">
|
||||
<i>项目开源协议: <b>Apache-2.0</b> 即:
|
||||
<br>
|
||||
你可以直接使用该项目提供的功能, 无需任何授权
|
||||
<br>
|
||||
你可以在注明来源版权信息的情况下对源代码进行任意分发和修改以及衍生
|
||||
</i>
|
||||
</div>
|
||||
<div class="documents">
|
||||
<div class="vercel">
|
||||
<i><a href="https://request-counter-docs.vercel.app/#/" target="_blank">点击查看托管在<i>Vercel</i>上的文档</a></i>
|
||||
</div>
|
||||
<div class="github">
|
||||
<i><a href="https://markusjoe.github.io/RequestCounter/" target="_blank">点击查看托管在<i>GitHub</i>上的文档</a></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ip_address">
|
||||
<i><b>请求地址来源: {{ remote_address }}</b></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="example">
|
||||
<div class="svg">
|
||||
<img src="https://static-file-hosting.vercel.app/static/lewd/6" alt="fast_test_example_svg">
|
||||
<img src="https://static-file-hosting.vercel.app/static/lewd/1" alt="fast_test_example_svg">
|
||||
<img src="https://static-file-hosting.vercel.app/static/lewd/5" alt="fast_test_example_svg">
|
||||
<img src="https://static-file-hosting.vercel.app/static/lewd/9" alt="fast_test_example_svg">
|
||||
<img src="https://static-file-hosting.vercel.app/static/lewd/0" alt="fast_test_example_svg">
|
||||
<img src="https://static-file-hosting.vercel.app/static/lewd/3" alt="fast_test_example_svg">
|
||||
<img src="https://static-file-hosting.vercel.app/static/lewd/7" alt="fast_test_example_svg">
|
||||
<img src="https://static-file-hosting.vercel.app/static/lewd/8" alt="fast_test_example_svg">
|
||||
<img src="https://static-file-hosting.vercel.app/static/lewd/2" alt="fast_test_example_svg">
|
||||
<img src="https://static-file-hosting.vercel.app/static/lewd/4" alt="fast_test_example_svg">
|
||||
</div>
|
||||
<div class="api">
|
||||
<button onclick="request_async()">点击获取计数数值</button>
|
||||
<div class="result">
|
||||
<i><b>test-example</b>当前计数数据值: </i>
|
||||
<p id="fast_test_example_text"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="{{ general_width }}" height="{{ general_height }}" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>{{ title }} | {{ address }}</title>
|
||||
<g>
|
||||
{% for value in context %}
|
||||
<image x="{{ value.position }}" y="0" width="{{ value.width }}" height="{{ value.height }}"
|
||||
xlink:href="{{ value.url }}"></image>
|
||||
{% endfor %}
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 488 B |
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/3/26
|
||||
# @File Name: __init__.py.py
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: Pycharm
|
||||
# @Create Time: 2022/5/1
|
||||
# @File Name: test_api.py
|
||||
|
||||
|
||||
import random
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
|
||||
class TestAPI(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.api = 'http://127.0.0.1:5000/api/v1/'
|
||||
|
||||
def test_query(self):
|
||||
res = requests.post(self.api + 'query', params={'name': 'test-example'})
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_query_failed(self):
|
||||
letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
r_name = ''.join(random.choice(letters) for _ in range(10))
|
||||
res = requests.post(self.api + 'query', params={'name': r_name})
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_overall(self):
|
||||
res = requests.post(self.api + 'overall')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_alltables(self):
|
||||
res = requests.post(self.api + 'alltables')
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_test(self):
|
||||
res = int(requests.post(self.api + 'test').text)
|
||||
self.assertIs(type(res), int)
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: Pycharm
|
||||
# @Create Time: 2022/5/2
|
||||
# @File Name: test_auth.py
|
||||
|
||||
|
||||
import random
|
||||
import requests
|
||||
import unittest
|
||||
|
||||
|
||||
class TestAuth(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.api = 'http://127.0.0.1:5000/api/v1/'
|
||||
|
||||
@property
|
||||
def r_pwd(self):
|
||||
letters = 'abcdefghijklmnopqrstuvwxyz'
|
||||
r_pwd = ''.join(random.choice(letters) for _ in range(16))
|
||||
return r_pwd
|
||||
|
||||
def test_api_system_header(self):
|
||||
res = requests.post(self.api + 'system', headers={'Authorization': 'bearer unittest'})
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_api_system_query(self):
|
||||
res = requests.post(self.api + 'system', params={'key': 'unittest'})
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_api_system_header_failed(self):
|
||||
res = requests.post(self.api + 'system', headers={'Authorization': f'bearer {self.r_pwd}'})
|
||||
self.assertEqual(res.status_code, 403)
|
||||
|
||||
def test_api_system_query_failed(self):
|
||||
res = requests.post(self.api + 'system', params={'key': self.r_pwd})
|
||||
self.assertEqual(res.status_code, 403)
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: Pycharm
|
||||
# @Create Time: 2022/5/1
|
||||
# @File Name: test_count.py
|
||||
|
||||
|
||||
import requests
|
||||
import unittest
|
||||
|
||||
|
||||
class TestCount(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.api = 'http://127.0.0.1:5000/'
|
||||
|
||||
def test_index(self):
|
||||
res = requests.post(self.api).text
|
||||
self.assertIsNotNone(res)
|
||||
|
||||
def test_count(self):
|
||||
res = requests.post(self.api + 'count/test-example').text
|
||||
self.assertIsNotNone(res)
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: Pycharm
|
||||
# @Create Time: 2022/5/1
|
||||
# @File Name: test_resources.py
|
||||
|
||||
|
||||
import requests
|
||||
import unittest
|
||||
|
||||
|
||||
class TestResources(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.resources_urls = {
|
||||
'docs': [
|
||||
'https://request-counter-docs.vercel.app/#/',
|
||||
'https://markusjoe.github.io/RequestCounter/#/'
|
||||
],
|
||||
'database': 'https://filebase.vercel.app/download/data.sqlite',
|
||||
'themes':
|
||||
{
|
||||
'lewd': 'https://static-file-hosting.vercel.app/static/lewd/0',
|
||||
'gelbooru': 'https://static-file-hosting.vercel.app/static/gelbooru/0',
|
||||
'moebooru': 'https://static-file-hosting.vercel.app/static/moebooru/0',
|
||||
'blacked': 'https://static-file-hosting.vercel.app/static/blacked/0',
|
||||
'lisu': 'https://static-file-hosting.vercel.app/static/lisu/0'
|
||||
}
|
||||
}
|
||||
|
||||
def test_doc_vercel(self):
|
||||
res = requests.post(self.resources_urls['docs'][0]).text
|
||||
self.assertIsNotNone(res)
|
||||
|
||||
def test_doc_github(self):
|
||||
res = requests.post(self.resources_urls['docs'][1]).text
|
||||
self.assertIsNotNone(res)
|
||||
|
||||
def test_database(self):
|
||||
res = requests.post(self.resources_urls['database'])
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_themes(self):
|
||||
for t in self.resources_urls['themes']:
|
||||
res = requests.get(self.resources_urls['themes'][t])
|
||||
self.assertEqual(res.status_code, 200)
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: Pycharm
|
||||
# @Create Time: 2022/5/2
|
||||
# @File Name: test_view.py
|
||||
|
||||
|
||||
import random
|
||||
import requests
|
||||
import unittest
|
||||
|
||||
|
||||
class TestViews(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.api = 'http://127.0.0.1:5000/'
|
||||
|
||||
@property
|
||||
def r_name(self):
|
||||
letters = 'abcdefghijklmnopqrstuvwxyz'
|
||||
r_name = [random.choice(letters) for _ in range(10)]
|
||||
return r_name
|
||||
|
||||
def test_not_found(self):
|
||||
res = requests.get(self.api + 'not_exist_page')
|
||||
self.assertEqual(res.status_code, 404)
|
||||
|
||||
def test_server_internal_error(self):
|
||||
res = requests.get(self.api + f'count/{self.r_name}', params={'length': 0})
|
||||
self.assertEqual(res.status_code, 500)
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/3/25
|
||||
# @File Name: __init__.py.py
|
||||
|
||||
__all__ = [
|
||||
'response',
|
||||
'password'
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/4/11
|
||||
# @File Name: password.py
|
||||
|
||||
import random
|
||||
|
||||
|
||||
def generate_pwd(length):
|
||||
"""
|
||||
生成随机密码
|
||||
:param length:
|
||||
:return:
|
||||
"""
|
||||
letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
return ''.join(random.choice(letters) for _ in range(length))
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: PyCharm
|
||||
# @Create Time: 2022/3/25
|
||||
# @File Name: response.py
|
||||
|
||||
|
||||
from ..db.db import SQLite as db
|
||||
from flask import request
|
||||
from flask import render_template
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
def index_(theme: str, length: int, name: str, count: str) -> str or Tuple[bool, str]:
|
||||
"""
|
||||
渲染模板
|
||||
:param theme:
|
||||
:param length:
|
||||
:param name:
|
||||
:param count:
|
||||
:return:
|
||||
"""
|
||||
datas = db().fetching_table(theme)
|
||||
context = []
|
||||
for i in count:
|
||||
if i == '0':
|
||||
context.append({'url': datas[0]['url'],
|
||||
'width': datas[0]['width'],
|
||||
'height': datas[0]['height']})
|
||||
elif i == '1':
|
||||
context.append({'url': datas[1]['url'],
|
||||
'width': datas[1]['width'],
|
||||
'height': datas[1]['height']})
|
||||
elif i == '2':
|
||||
context.append({'url': datas[2]['url'],
|
||||
'width': datas[2]['width'],
|
||||
'height': datas[2]['height']})
|
||||
elif i == '3':
|
||||
context.append({'url': datas[3]['url'],
|
||||
'width': datas[3]['width'],
|
||||
'height': datas[3]['height']})
|
||||
elif i == '4':
|
||||
context.append({'url': datas[4]['url'],
|
||||
'width': datas[4]['width'],
|
||||
'height': datas[4]['height']})
|
||||
elif i == '5':
|
||||
context.append({'url': datas[5]['url'],
|
||||
'width': datas[5]['width'],
|
||||
'height': datas[5]['height']})
|
||||
elif i == '6':
|
||||
context.append({'url': datas[6]['url'],
|
||||
'width': datas[6]['width'],
|
||||
'height': datas[6]['height']})
|
||||
elif i == '7':
|
||||
context.append({'url': datas[7]['url'],
|
||||
'width': datas[7]['width'],
|
||||
'height': datas[7]['height']})
|
||||
elif i == '8':
|
||||
context.append({'url': datas[8]['url'],
|
||||
'width': datas[8]['width'],
|
||||
'height': datas[8]['height']})
|
||||
elif i == '9':
|
||||
context.append({'url': datas[9]['url'],
|
||||
'width': datas[9]['width'],
|
||||
'height': datas[9]['height']})
|
||||
|
||||
for p, i in zip(context, range(0, length)):
|
||||
p['position'] = i * p['width'] # 设置每张图片对应的位置
|
||||
|
||||
general_width = datas[0]['width'] * length # 计算出图片总长度
|
||||
general_height = datas[0]['height'] # 总宽度
|
||||
|
||||
return render_template('view.html',
|
||||
context=context,
|
||||
title=name,
|
||||
address=request.remote_addr,
|
||||
general_height=general_height,
|
||||
general_width=general_width)
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
# -- coding:utf-8 --
|
||||
# @Author: markushammered@gmail.com
|
||||
# @Development Tool: Pycharm
|
||||
# @Create Time: 2022/4/29
|
||||
# @File Name: update.py
|
||||
|
||||
|
||||
import os
|
||||
import base64
|
||||
import logging
|
||||
import requests
|
||||
from ..config import VersionConfig
|
||||
from typing import Dict, List
|
||||
|
||||
|
||||
class SelfUpdateVersion:
|
||||
"""更新本体 -> 更新单个文件"""
|
||||
|
||||
def __init__(self, logger: logging.Logger):
|
||||
self.api = 'https://api.github.com/repos/MarkusJoe/RequestCounter/tags'
|
||||
self.headers = {'Authorization': 'token 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'}
|
||||
self.resp = requests.get(self.api, headers=self.headers).json()
|
||||
self.logger = logger
|
||||
|
||||
def commit_details(self):
|
||||
"""
|
||||
获取提交细节
|
||||
:return:
|
||||
"""
|
||||
commit_url = self.resp[0]['commit']['url']
|
||||
res = requests.get(commit_url, headers=self.headers).json()
|
||||
files = res['files']
|
||||
modified = []
|
||||
deleted = []
|
||||
|
||||
for f in files:
|
||||
file_path = f['filename']
|
||||
base64_content = requests.get(f['contents_url'], headers=self.headers).json()['content'] # 直接获取修改文件的base64
|
||||
if f['status'] in ['modified', 'added']:
|
||||
modified.append({'filename': f'./{file_path}', 'content': base64.b64decode(base64_content)})
|
||||
else:
|
||||
deleted.append(f'./{file_path}')
|
||||
|
||||
self.apply_change(modified, deleted)
|
||||
|
||||
def apply_change(self, modified: List[Dict], deleted: List):
|
||||
"""
|
||||
应用已经更改的文件
|
||||
:param modified:
|
||||
:param deleted:
|
||||
:return:
|
||||
"""
|
||||
for d in deleted:
|
||||
os.remove(d)
|
||||
self.logger.info(f'删除了: {d}')
|
||||
|
||||
for m in modified:
|
||||
with open(m['filename'], 'wb') as modify:
|
||||
modify.write(m['content'])
|
||||
self.logger.info(f'修改了: {m["filename"]}')
|
||||
|
||||
with open('./app/config.py', 'wb') as update_config:
|
||||
content = requests.get('https://api.github.com/repos/MarkusJoe/RequestCounter/contents/app/config.py',
|
||||
headers=self.headers).json()['content']
|
||||
update_config.write(base64.b64decode(content))
|
||||
|
||||
exit()
|
||||
|
||||
def check(self):
|
||||
current = VersionConfig.version[0]
|
||||
newest = self.resp[0]['name']
|
||||
if current != newest:
|
||||
self.logger.warning(f'当前版本已经落后于最新版本. 当前: {current} -> 远程: {newest}')
|
||||
self.commit_details()
|
||||
return False
|
||||
else:
|
||||
self.logger.info('当前版本为最新版本')
|
||||
return True
|
||||
Reference in New Issue
Block a user