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

248
404.html
View File

@ -1,115 +1,155 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Ghink Universe - 404</title>
<link rel="icon" href="https://cdn.ghink.net/image/ghink/favicon.ico" type="image/x-icon">
<style>
body {
background-color: #2F3242;
}
svg {
position: absolute;
top: 50%;
left: 50%;
margin-top: -250px;
margin-left: -400px;
}
.message-box {
height: 200px;
width: 380px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -100px;
margin-left: 50px;
color: #FFF;
font-family: Roboto;
font-weight: 300;
}
.message-box h1 {
font-size: 60px;
line-height: 46px;
margin-bottom: 40px;
}
.buttons-con .action-link-wrap {
margin-top: 40px;
}
.buttons-con .action-link-wrap a {
background: #68c950;
padding: 8px 25px;
border-radius: 4px;
color: #FFF;
font-weight: bold;
font-size: 14px;
transition: all 0.3s linear;
cursor: pointer;
text-decoration: none;
margin-right: 10px
}
.buttons-con .action-link-wrap a:hover {
background: #5A5C6C;
color: #fff;
}
#Polygon-1 , #Polygon-2 , #Polygon-3 , #Polygon-4 , #Polygon-4, #Polygon-5 {
animation: float 1s infinite ease-in-out alternate;
}
#Polygon-2 {
animation-delay: .2s;
}
#Polygon-3 {
animation-delay: .4s;
}
#Polygon-4 {
animation-delay: .6s;
}
#Polygon-5 {
animation-delay: .8s;
}
@keyframes float {
100% {
transform: translateY(20px);
}
}
@media (max-width: 450px) {
svg {
position: absolute;
top: 50%;
left: 50%;
margin-top: -250px;
margin-left: -190px;
}
.message-box {
top: 50%;
left: 50%;
margin-top: -100px;
margin-left: -190px;
text-align: center;
}
}
</style>
<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>
body {
background-color: #2F3242;
}
.svg {
position: absolute;
top: 50%;
left: 50%;
margin-top: -250px;
margin-left: -400px;
}
.message-box {
height: 200px;
width: 380px;
position: absolute;
top: 50%;
left: 50%;
margin-top: -200px;
margin-left: 50px;
color: #FFF;
font-family: Roboto;
font-weight: 300;
}
.message-box h1 {
font-size: 60px;
line-height: 46px;
margin-bottom: 40px;
}
.music {
margin-top: 50px;
color: #000;
}
.buttons-con .action-link-wrap {
margin-top: 40px;
}
.buttons-con .action-link-wrap a {
background: #68c950;
padding: 8px 25px;
border-radius: 4px;
color: #FFF;
font-weight: bold;
font-size: 14px;
transition: all 0.3s linear;
cursor: pointer;
text-decoration: none;
margin-right: 10px
}
.buttons-con .action-link-wrap a:hover {
background: #5A5C6C;
color: #fff;
}
#Polygon-1 , #Polygon-2 , #Polygon-3 , #Polygon-4 , #Polygon-4, #Polygon-5 {
animation: float 1s infinite ease-in-out alternate;
}
#Polygon-2 {
animation-delay: .2s;
}
#Polygon-3 {
animation-delay: .4s;
}
#Polygon-4 {
animation-delay: .6s;
}
#Polygon-5 {
animation-delay: .8s;
}
@keyframes float {
100% {
transform: translateY(20px);
}
}
@media (max-width: 450px) {
.svg {
top: 50%;
left: 50%;
margin-top: -250px;
margin-left: -190px;
}
.message-box {
top: 50%;
left: 50%;
margin-top: -100px;
margin-left: -190px;
text-align: center;
}
.music {
margin-top: 50px;
color: #000;
}
}
</style>
</head>
<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">
<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="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="M773,186 L827,217.538705 L827,279.636651 L773,310 L719,279.636651 L719,217.538705 L773,186 Z" id="Polygon-3" stroke="#795D9C" stroke-width="6" sketch:type="MSShapeGroup"></path>
<path d="M639,529 L773,607.846761 L773,763.091627 L639,839 L505,763.091627 L505,607.846761 L639,529 Z" id="Polygon-4" stroke="#F2773F" stroke-width="6" sketch:type="MSShapeGroup"></path>
<path d="M281,801 L383,861.025276 L383,979.21169 L281,1037 L179,979.21169 L179,861.025276 L281,801 Z" id="Polygon-5" stroke="#36B455" stroke-width="6" sketch:type="MSShapeGroup"></path>
</g>
<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">
<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="M773,186 L827,217.538705 L827,279.636651 L773,310 L719,279.636651 L719,217.538705 L773,186 Z" id="Polygon-3" stroke="#795D9C" stroke-width="6" sketch:type="MSShapeGroup"></path>
<path d="M639,529 L773,607.846761 L773,763.091627 L639,839 L505,763.091627 L505,607.846761 L639,529 Z" id="Polygon-4" stroke="#F2773F" stroke-width="6" sketch:type="MSShapeGroup"></path>
<path d="M281,801 L383,861.025276 L383,979.21169 L281,1037 L179,979.21169 L179,861.025276 L281,801 Z" id="Polygon-5" stroke="#36B455" stroke-width="6" sketch:type="MSShapeGroup"></path>
</g>
</svg>
<div class="message-box">
<h1>404</h1>
<p>Page not found 页面找不到了</p>
<div class="buttons-con">
<div class="action-link-wrap">
<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>
</div>
</div>
<h1>404</h1>
<p>
<strong>Page not found 页面找不到了</strong>
</p>
<div class="buttons-con">
<div class="action-link-wrap">
<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>
</div>
<div id="aplayer" class="music"></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>
</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

168
main.py
View File

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

3
requirements.txt Normal file
View File

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