Migrating the project to an asynchronous model

This commit is contained in:
Bigsk 2024-09-26 18:45:19 +08:00
parent eeff5c307c
commit 463b2bcd8c
5 changed files with 228 additions and 427 deletions

View File

@ -1,14 +1,20 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ghink Universe - 404</title> <title>Ghink Universe - 404</title>
<link rel="icon" href="https://cdn.ghink.net/image/ghink/favicon.ico" type="image/x-icon"> <link rel="icon" href="https://cdn.gh.ink/image/ghink/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="https://cdn.gh.ink/assembly/aplayer/1.10.0/APlayer.min.css">
<script src="https://cdn.gh.ink/js/vue/2.6.14/vue.min.js"></script>
<script src="https://cdn.gh.ink/js/axios/1.1.3/axios.min.js"></script>
<script src="https://cdn.gh.ink/site/public/js/msg.js"></script>
<style> <style>
body { body {
background-color: #2F3242; background-color: #2F3242;
} }
svg { .svg {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
@ -21,7 +27,7 @@
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
margin-top: -100px; margin-top: -200px;
margin-left: 50px; margin-left: 50px;
color: #FFF; color: #FFF;
font-family: Roboto; font-family: Roboto;
@ -32,6 +38,10 @@
line-height: 46px; line-height: 46px;
margin-bottom: 40px; margin-bottom: 40px;
} }
.music {
margin-top: 50px;
color: #000;
}
.buttons-con .action-link-wrap { .buttons-con .action-link-wrap {
margin-top: 40px; margin-top: 40px;
} }
@ -74,8 +84,7 @@
} }
} }
@media (max-width: 450px) { @media (max-width: 450px) {
svg { .svg {
position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
margin-top: -250px; margin-top: -250px;
@ -88,11 +97,15 @@
margin-left: -190px; margin-left: -190px;
text-align: center; text-align: center;
} }
.music {
margin-top: 50px;
color: #000;
}
} }
</style> </style>
</head> </head>
<body> <body>
<svg width="380px" height="500px" viewBox="0 0 837 1045" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> <svg width="380px" height="500px" viewBox="0 0 837 1045" version="1.1" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink" xmlns:sketch="https://www.bohemiancoding.com/sketch/ns" class="svg">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<path d="M353,9 L626.664028,170 L626.664028,487 L353,642 L79.3359724,487 L79.3359724,170 L353,9 Z" id="Polygon-1" stroke="#007FB2" stroke-width="6" sketch:type="MSShapeGroup"></path> <path d="M353,9 L626.664028,170 L626.664028,487 L353,642 L79.3359724,487 L79.3359724,170 L353,9 Z" id="Polygon-1" stroke="#007FB2" stroke-width="6" sketch:type="MSShapeGroup"></path>
<path d="M78.5,529 L147,569.186414 L147,648.311216 L78.5,687 L10,648.311216 L10,569.186414 L78.5,529 Z" id="Polygon-2" stroke="#EF4A5B" stroke-width="6" sketch:type="MSShapeGroup"></path> <path d="M78.5,529 L147,569.186414 L147,648.311216 L78.5,687 L10,648.311216 L10,569.186414 L78.5,529 Z" id="Polygon-2" stroke="#EF4A5B" stroke-width="6" sketch:type="MSShapeGroup"></path>
@ -103,13 +116,40 @@
</svg> </svg>
<div class="message-box"> <div class="message-box">
<h1>404</h1> <h1>404</h1>
<p>Page not found 页面找不到了</p> <p>
<strong>Page not found 页面找不到了</strong>
</p>
<div class="buttons-con"> <div class="buttons-con">
<div class="action-link-wrap"> <div class="action-link-wrap">
<a onclick="history.back(-1)" class="link-button link-back-button">Go Back 返回</a> <a onclick="history.back(-1)" class="link-button link-back-button">Go Back 返回</a>
<a href="https://www.ghink.net" class="link-button">Go Home 返回主页</a> <a href="https://www.ghink.net" class="link-button">Go Home 返回主页</a>
</div> </div>
<div id="aplayer" class="music"></div>
</div> </div>
</div> </div>
<script src="https://cdn.gh.ink/assembly/aplayer/1.10.0/APlayer.min.js"></script>
<script>
new Vue({
el: '#app',
mounted () {
axios
.get('https://api.gh.ink/404music')
.then(function (response) {
const ap = new APlayer({
container: document.getElementById('aplayer'),
autoplay: false,
loop: 'all',
listFolded: true,
theme: '#5A5C6C',
order: "random",
audio: response.data.content
});
})
.catch(function (error) {
console.log(error);
});
}
})
</script>
</body> </body>
</html> </html>

View File

@ -1,3 +1,9 @@
# short_link # Short Link
Ghink Universe Short Link Service Source Code Ghink Universe Short Link Service Source Code
## Usage
1. Install all requirements
2. Configure like example config
3. Run it!

File diff suppressed because one or more lines are too long

170
main.py
View File

@ -1,6 +1,11 @@
import time, random, json, threading import time
import pymysql import random
from flask import Flask, request, redirect import json
from quart import Quart, request, redirect
import aiomysql
import aiofiles
with open("config.json", "r") as fb: with open("config.json", "r") as fb:
config = json.loads(fb.read()) config = json.loads(fb.read())
@ -9,13 +14,8 @@ with open("config.json", "r") as fb:
LISTEN = config["LISTEN"] LISTEN = config["LISTEN"]
DEBUG = config["DEBUG"] DEBUG = config["DEBUG"]
app = Flask("Ghink Short Link Service") app = Quart("Ghink Short Link Service")
db = pymysql.connect( db_pool = None
host=DB["host"],
user=DB["user"],
password=DB["password"],
database=DB["database"]
)
field_map = { field_map = {
'A': 0, 'a': 1, 'B': 2, 'b': 3, 'A': 0, 'a': 1, 'B': 2, 'b': 3,
@ -33,63 +33,67 @@ field_map = {
'v': 48, 'W': 49, 'w': 50, 'X': 51, 'v': 48, 'W': 49, 'w': 50, 'X': 51,
'x': 52, '6': 53, 'Y': 54, 'y': 55, 'x': 52, '6': 53, 'Y': 54, 'y': 55,
'Z': 56, 'z': 57, '7': 58, '8': 59, 'Z': 56, 'z': 57, '7': 58, '8': 59,
'9': 60, '0': 61} '9': 60, '0': 61
}
async def init_db():
@app.route("/", methods=["GET"]) global db_pool
def index(): db_pool = await aiomysql.create_pool(
with open("index.html", "r", encoding="utf-8") as fb:
return fb.read()
@app.route("/<string:link_id>", methods=["GET"])
def route(link_id: str):
global db
for char in link_id:
if char not in tuple(field_map.keys()):
return redirect("https://www.ghink.net")
link_id_converted = 0
for i in range(len(link_id)):
link_id_converted += field_map[link_id[::-1][i]] * 62 ** i
try:
db.ping()
except pymysql.err.InterfaceError:
db = pymysql.connect(
host=DB["host"], host=DB["host"],
user=DB["user"], user=DB["user"],
password=DB["password"], password=DB["password"],
database=DB["database"] db=DB["database"],
autocommit=True
) )
cursor = db.cursor() @app.before_serving
cursor.execute('SELECT link, validity FROM links WHERE id=%s', link_id_converted) async def startup():
db.commit() await init_db()
link = cursor.fetchone()
@app.after_serving
async def shutdown():
db_pool.close()
await db_pool.wait_closed()
@app.route("/", methods=["GET"])
async def index():
return redirect("https://k76u22n4gd.apifox.cn")
@app.route("/<string:link_id>", methods=["GET"])
async def route(link_id: str):
for char in link_id:
if char not in field_map:
return redirect("https://www.ghink.net")
link_id_converted = sum(field_map[link_id[::-1][i]] * 62 ** i for i in range(len(link_id)))
async with db_pool.acquire() as conn:
async with conn.cursor() as cursor:
await cursor.execute('SELECT link, validity FROM links WHERE id=%s', (link_id_converted,))
link = await cursor.fetchone()
if link is None or link[0] is None: if link is None or link[0] is None:
with open("404.html", "r", encoding="utf-8") as fb: async with aiofiles.open("404.html", "r", encoding="utf-8") as fb:
return fb.read(), 404 return await fb.read(), 404
if link[1] is not None and link[1] < time.time(): if link[1] is not None and link[1] < time.time():
remove_thread = threading.Thread(target=remove_link, args=(link_id_converted,)) await remove_link(link_id_converted)
remove_thread.start() async with aiofiles.open("404.html", "r", encoding="utf-8") as fb:
with open("404.html", "r", encoding="utf-8") as fb: return await fb.read(), 404
return fb.read(), 404
return redirect(link[0]) return redirect(link[0])
@app.route("/", methods=["POST"]) @app.route("/", methods=["POST"])
def add(): async def add():
global db form = await request.form # Await the form to get the data
key = request.form.get("key") key = form.get("key") # Access the key
link = request.form.get("link") link = form.get("link") # Access the link
validity = request.form.get("validity") validity = form.get("validity") # Access the validity
# Judge whether fields are empty
if key == "" or link == "": if not key or not link:
return json.dumps({"ok": False, "message": "bad field(s)", "id": ""}) return json.dumps({"ok": False, "message": "bad field(s)", "id": ""})
# No access
if key not in KEYS: if key not in KEYS:
return json.dumps({"ok": False, "message": "forbidden", "id": ""}) return json.dumps({"ok": False, "message": "forbidden", "id": ""})
# Check validity
if validity: if validity:
if validity.isdecimal() and int(validity) > time.time(): if validity.isdecimal() and int(validity) > time.time():
validity = int(validity) validity = int(validity)
@ -98,37 +102,25 @@ def add():
else: else:
validity = None validity = None
# Random
while True: while True:
link_id_random = ''.join(random.sample(tuple(field_map.keys()), 6)) link_id_random = ''.join(random.sample(field_map.keys(), 6))
link_id_converted = 0 link_id_converted = sum(field_map[link_id_random[::-1][i]] * 62 ** i for i in range(len(link_id_random)))
for i in range(len(link_id_random)):
link_id_converted += field_map[link_id_random[::-1][i]] * 62 ** i async with db_pool.acquire() as conn:
# Get Cursor async with conn.cursor() as cursor:
try: await cursor.execute('SELECT link FROM links WHERE id=%s', (link_id_converted,))
db.ping() link_selected = await cursor.fetchone()
except pymysql.err.InterfaceError:
db = pymysql.connect(
host=DB["host"],
user=DB["user"],
password=DB["password"],
database=DB["database"]
)
cursor = db.cursor()
cursor.execute('SELECT link FROM links WHERE id=%s', link_id_converted)
db.commit()
link_selected = cursor.fetchone()
if link_selected is None: if link_selected is None:
break break
# Insert
cursor.execute("INSERT INTO `links` VALUES (%s, %s, %s)", [link_id_converted, link, validity]) async with db_pool.acquire() as conn:
db.commit() async with conn.cursor() as cursor:
await cursor.execute("INSERT INTO `links` VALUES (%s, %s, %s)", (link_id_converted, link, validity))
return json.dumps({"ok": True, "message": "successful", "id": link_id_random}) return json.dumps({"ok": True, "message": "successful", "id": link_id_random})
@app.route("/", methods=["PATCH"]) @app.route("/", methods=["PATCH"])
def reload(): async def reload():
global config, DB, KEYS, LISTEN, DEBUG global config, DB, KEYS, LISTEN, DEBUG
with open("config.json", "r") as fb: with open("config.json", "r") as fb:
@ -140,24 +132,10 @@ def reload():
return json.dumps({"ok": True, "message": "successful"}) return json.dumps({"ok": True, "message": "successful"})
async def remove_link(id):
def remove_link(id): async with db_pool.acquire() as conn:
global db async with conn.cursor() as cursor:
# Get Cursor await cursor.execute('DELETE FROM links WHERE id=%s', (id,))
try:
db.ping()
except pymysql.err.InterfaceError:
db = pymysql.connect(
host=DB["host"],
user=DB["user"],
password=DB["password"],
database=DB["database"]
)
cursor = db.cursor()
cursor.execute('DELETE FROM links WHERE id=%s', id)
db.commit()
if __name__ == "__main__": if __name__ == "__main__":
app.run(LISTEN[0], LISTEN[1], DEBUG) app.run(host=LISTEN[0], port=LISTEN[1], debug=DEBUG)
db.close()

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
aiomysql
aiofiles
quart