diff --git a/404.html b/404.html new file mode 100644 index 0000000..4ee47a3 --- /dev/null +++ b/404.html @@ -0,0 +1,115 @@ + + + + + Ghink Universe - 404 + + + + + + + + + + + + + +
+

404

+

Page not found 页面找不到了

+
+ +
+
+ + diff --git a/config.json b/config.json new file mode 100644 index 0000000..03c74f3 --- /dev/null +++ b/config.json @@ -0,0 +1,16 @@ +{ + "DEBUG": false, + "DB": { + "host": "localhost", + "user": "root", + "password": "root", + "database": "short_link" + }, + "KEYS": [ + "Example" + ], + "LISTEN": [ + "0.0.0.0", + 50002 + ] +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..592f80c --- /dev/null +++ b/index.html @@ -0,0 +1,226 @@ + + + + + + 极科宇视 - 短链接服务 + + + + + + + +
+
Copyright Ghink © 2014
+
浙ICP备2021025087号
+ + + + + + diff --git a/main.py b/main.py new file mode 100644 index 0000000..c44d47c --- /dev/null +++ b/main.py @@ -0,0 +1,163 @@ +import time, random, json, threading +import pymysql +from flask import Flask, request, redirect + +with open("config.json", "r") as fb: + config = json.loads(fb.read()) + DB = config["DB"] + KEYS = config["KEYS"] + 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"] +) + +field_map = { + 'A': 0, 'a': 1, 'B': 2, 'b': 3, + 'C': 4, 'c': 5, 'D': 6, 'd': 7, + '1': 8, 'E': 9, 'e': 10, 'F': 11, + 'f': 12, 'G': 13, 'g': 14, 'H': 15, + 'h': 16, '2': 17, 'I': 18, 'i': 19, + 'J': 20, 'j': 21, 'K': 22, 'k': 23, + 'L': 24, 'l': 25, '3': 26, 'M': 27, + 'm': 28, 'N': 29, 'n': 30, 'O': 31, + 'o': 32, 'P': 33, 'p': 34, '4': 35, + 'Q': 36, 'q': 37, 'R': 38, 'r': 39, + 'S': 40, 's': 41, 'T': 42, 't': 43, + '5': 44, 'U': 45, 'u': 46, 'V': 47, + '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} + + +@app.route("/", methods=["GET"]) +def index(): + with open("index.html", "r", encoding="utf-8") as fb: + return fb.read() + + +@app.route("/", 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]] * 56 ** i + + 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, validity FROM links WHERE id=%s', link_id_converted) + db.commit() + link = 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 + 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 + 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 == "": + 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) + else: + return json.dumps({"ok": False, "message": "bad field(s)", "id": ""}) + 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]] * 56 ** 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() + + return json.dumps({"ok": True, "message": "successful", "id": link_id_random}) + + +@app.route("/", methods=["PATCH"]) +def reload(): + global config, DB, KEYS, LISTEN, DEBUG + + with open("config.json", "r") as fb: + config = json.loads(fb.read()) + DB = config["DB"] + KEYS = config["KEYS"] + LISTEN = config["LISTEN"] + DEBUG = config["DEBUG"] + + 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() + + +if __name__ == "__main__": + app.run(LISTEN[0], LISTEN[1], DEBUG) + db.close() diff --git a/short_link.sql b/short_link.sql new file mode 100644 index 0000000..f871704 --- /dev/null +++ b/short_link.sql @@ -0,0 +1,22 @@ +SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; +START TRANSACTION; +SET time_zone = "+00:00"; + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8mb4 */; + +CREATE TABLE `links` ( + `id` bigint(20) NOT NULL, + `link` text NOT NULL, + `validity` bigint(20) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +ALTER TABLE `links` + ADD PRIMARY KEY (`id`); +COMMIT; + +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;