commit 02f3a1b884a12f5a806f41011a7b7d24b4a66759 Author: Arseniy Romenskiy Date: Sat Dec 27 05:02:10 2025 +0300 fix diff --git a/Start.py b/Start.py new file mode 100644 index 0000000..ee11eaf --- /dev/null +++ b/Start.py @@ -0,0 +1,476 @@ +#!/usr/bin/python3 + +import os + +from flask import Flask, flash, request, redirect, url_for, send_from_directory, render_template, jsonify +from werkzeug.utils import secure_filename + +from time import strftime, localtime, sleep #Для (Time) +import json +import hashlib +import logging +import asyncio + +import bd_module +import db_module +from datetime import date + +put = os.path.dirname(os.path.realpath(__file__)) + "/"#Путь- (part-1) +with open(f"{put}settings.json", "r") as read_file: + settings = json.load(read_file) +import logging +if settings["log_type"] == "INFO": + level=logging.INFO +elif settings["log_type"] == "DEBUG": + level=logging.DEBUG +elif settings["log_type"] == "ERROR": + level=logging.ERROR +else: + print('Тип лога указан не верно, авто "INFO"\nВареанты: "INFO", "DEBUG", ERROR') + level=logging.INFO + + +logging.basicConfig(filename=settings["log_file"], level=level) + + + + +def authentication(token): + uid = bd_module.check_user_short_token(token) + #return uid, gid + return uid + +def mi_ip(request): + try: + return request.headers['x-forwarded-for'] + except: + return request.remote_addr + + + +UPLOAD_FOLDER = put + 'png/' +app = Flask(__name__) +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER +print(UPLOAD_FOLDER) +print("SAS") + +if not os.path.isdir(UPLOAD_FOLDER): + os.mkdir(UPLOAD_FOLDER) + + + +@app.route('/login/', methods=['GET', 'POST']) +def login(): + logging.debug(f"request.headers - {request.headers}") + email = request.headers['email'] + password = request.headers['password'] + remember = request.headers['remember'] + ps_info_name = request.headers['User-Agent'] + uid ,user_name, active = db_module.check_user(email, password) + if uid == None: + logging.info(f"Авторизаии под пользователям {email} !!!ОТКАЗ!!!") + return "login not found", 412 + elif active == 0: + logging.info(f"Авторизаии под пользователям {email} !!!ОТКЛЮЧИНА!!!") + return "Учётная запесь отключина!", 423 + else: + if remember == "1": + + short_token, live_token = bd_module.add_live_token(uid, ps_info_name, mi_ip(request), date.today(),30*24*60*60) + #short_token = bd_module.add_short_token(uid) + #live_token = bd_module.add_live_token(uid) + else: + short_token, live_token = bd_module.add_live_token(uid, ps_info_name, mi_ip(request), date.today(),12*60*60) + #short_token = bd_module.add_short_token(uid) + #live_token = None + print(short_token, " - " ,live_token) + logging.info(f"Авторизаии под пользователям {email} !УСПЕХ!") + return json.dumps({"short_token": short_token, "live_token": live_token, "user_id": uid, "user_name": user_name}, separators=(',', ':')) + + +@app.route('/ls/', methods=['GET', 'POST']) +def ls(): + print("ls") + print(request.headers) + print(request.headers['short_token']) + uid = authentication(request.headers['short_token']) + print(uid) + if uid == None: + print("/ls/' - 403") + return "403", 426 + return json.dumps({ + "ls": [ + {"id": 1, "name": "office-laptop", "status": "active", "ip_local": "10.0.0.2/32", "ip_server": "198.51.100.10:51820"}, + {"id": 2, "name": "phone", "status": "disabled", "ip_local": "10.0.0.3/32", "ip_server": "198.51.100.10:51820"} + ] +}, separators=(',', ':')) + #return json.dumps({"ls": bd_module.ls_item(uid)}, separators=(',', ':')) + + +@app.route('/item_add/', methods=['GET', 'POST']) +def item_add(): + print("item_add") + uid = authentication(request.headers['short_token']) + if uid == None: + logging.debug(f"ls - Отказ по токену! IP - {mi_ip(request)}") + print("/ls/' - 403") + return "403", 426 + print(request) + content = request.json + print(content) + item_name = content['item_name'] + status = content['status'] + user_id_1 = content['user_id_1'] + user_id_2 = content['user_id_2'] + comments = content['comments'] + inventory_id = content['inventory_id'] + id = bd_module.add_item(uid, user_id_1, user_id_2, item_name, status, None, comments, inventory_id) + if id != 0: + logging.info(f"Пользователь {uid} создал предмет {id}") + return json.dumps({"item_id": id}, separators=(',', ':')) + else: + logging.info(f"Пользователю {uid} отказано в создании предмета") + return "Отказ", 403 + +@app.route('/item_edit/', methods=['GET', 'POST']) +def item_edit(): + print("item_edit") + uid = authentication(request.headers['short_token']) + if uid == None: + logging.debug(f"item_edit - Отказ по токену! IP - {mi_ip(request)}") + print("/item_edit/' - 403") + return "403", 426 + content = request.json + item_id = content['item_id'] + item_name = content['item_name'] + status = content['status'] + user_id_1 = content['user_id_1'] + user_id_2 = content['user_id_2'] + comments = content['comments'] + inventory_id = content['inventory_id'] + id = bd_module.item_edit(uid, item_id, user_id_1, user_id_2, item_name, status, None, comments, inventory_id) + if id == 1: + logging.info(f"Пользователь {uid} изменил предмета {item_id}") + return "OK", 200 + else: + logging.info(f"Пользователю {uid} отказано в редактировании предмета {item_id}") + return "Отказ", 403 + +@app.route('/item_rm/', methods=['GET', 'POST']) +def item_rm(): + print("item_rm") + uid = authentication(request.headers['short_token']) + if uid == None: + logging.debug(f"item_rm - Отказ по токену! IP - {mi_ip(request)}") + print("/item_rm/' - 403") + return "403", 426 + content = request.json + item_id = content['item_id'] + id = bd_module.item_rm(uid, item_id) + if id == 1: + if os.path.isfile(f"{put}png/item/{item_id}"): + os.remove(f"{put}png/item/{item_id}") + logging.info(f"Иконка предмета {item_id} удалена") + logging.info(f"Пользователь {uid} удолил предмета {item_id}") + return "OK", 200 + else: + logging.info(f"Пользователю {uid} отказанно в удоление предмета {item_id}") + return "Отказ", 403 + + + + + +@app.route('/user_ls/', methods=['GET', 'POST']) +def user_ls(): + print("user_ls") + print(request.headers['short_token']) + uid = authentication(request.headers['short_token']) + print(uid) + if uid == None: + logging.debug(f"user_ls - Отказ по токену! IP - {mi_ip(request)}") + print("/user_ls/' - 403") + return "403", 426 + return json.dumps({"ls": bd_module.ls_user(uid)}, separators=(',', ':')) + +@app.route('/user_info/', methods=['GET', 'POST']) +def user_info(): + uid = authentication(request.headers['short_token']) + user_id = (request.headers['user_id']) + #uid = 455435 + if uid == None: + logging.debug(f"user_info - Отказ по токену! IP - {mi_ip(request)}") + print("/user_info/' - 403") + return "403", 426 + user_info = bd_module.user_info(uid, user_id) + print(user_info) + if user_info != None: + ######################### + directory = app.config['UPLOAD_FOLDER'] + # user_info + print(user_info) ################################################################################################################ + student = { + "user_name" : user_info[0], + "email" : user_info[1], + "avatar" : user_info[2], + "active": user_info[3], + "group_id": user_info[4], + "permission": user_info[5], + } + b = json.dumps(student) + + return b + else: + return "Отказ в доступе!", 403 + + + +@app.route('/renew/', methods=['GET', 'POST']) +def renew(): + print("renew") + short_token, live_token = bd_module.update_short_token(request.headers['user_id'], request.headers['live_token']) + #short_token = bd_module.check_user_live_token(request.headers['live_token']) + if short_token == None: + logging.debug(f"renew - Отказ по токену! IP - {mi_ip(request)}") + print("/renew/' - 403") + return "Токен не верен", 426 + return json.dumps({"short_token": short_token, "live_token": live_token}, separators=(',', ':')) + + +""" +@app.route('/exit/', methods=['GET', 'POST']) # ??? Что это ??? +def exit(): + print("exit") + uid = authentication(request.headers['short_token']) + if uid == None: + logging.debug(f"exit - Отказ по токену!") + print("/exit/' - 403") + return "403", 426 + return json.dumps({"short_token": short_token}, separators=(',', ':')) +""" + + + +@app.route('/kill_session/', methods=['GET', 'POST']) +def kill_session(): + print("kill_session") + user_id = request.headers['user_id'] + live_token = request.headers['live_token'] + print(live_token) + A = bd_module.rm_live_token(user_id, live_token) + if A: + return "OK" + else: + return "404", 404 + +@app.route('/ls_sessions/', methods=['GET', 'POST']) +def ls_sessions(): # !!! МОГУТ БЫТЬ ПРОБЛЕМЫ !!! + uid = authentication(request.headers['short_token']) + if uid == None: + logging.debug(f"ls_sessions - Отказ по токену! IP - {mi_ip(request)}") + print("/ls_sessions/' - 426") + return "426", 426 + A = bd_module.ls_sessions(uid) + return json.dumps({"matrix": A}, separators=(',', ':')) + +@app.route('/exiting_session/', methods=['GET', 'POST']) +def exiting_session(): # !!! МОГУТ БЫТЬ ПРОБЛЕМЫ !!! + uid = authentication(request.headers['short_token']) + if uid == None: + logging.debug(f"exiting_session - Отказ по токену! IP - {mi_ip(request)}") + print("/exiting_session/' - 426") + return "426", 426 + position = request.headers['position'] + A = bd_module.rm_live_token_position(uid, position) + return "OK" + +@app.route('/full_closure_session/', methods=['GET', 'POST']) +def full_closure_session(): # !!! МОГУТ БЫТЬ ПРОБЛЕМЫ !!! + uid = authentication(request.headers['short_token']) + if uid == None: + logging.debug(f"full_closure_session - Отказ по токену! IP - {mi_ip(request)}") + print("/full_closure_session/' - 426") + return "426", 426 + A = bd_module.full_sessions_kill(uid) + return "OK" + +@app.route('/user_add/', methods=['GET', 'POST']) +def user_add(): + uid = authentication(request.headers['short_token']) + if uid == None: + logging.debug(f"user_add - Отказ по токену! IP - {mi_ip(request)}") + print("/user_add/' - 426") + return "426", 426 + content = request.json + user_name = content['user_name'] + email = content['email'] + password = content['password'] + avatar = content['avatar'] + active = content['active'] + group_id = content['group_id'] + A = bd_module.user_add(uid, user_name, email, password, avatar, active, group_id) + if A == None: + return "Отказ в доступе!", 403 + return json.dumps({"user_id": A}, separators=(',', ':')) + + + +@app.route('/user_edit/', methods=['GET', 'POST']) +def user_edit(): + uid = authentication(request.headers['short_token']) + if uid == None: + logging.debug(f"user_edit - Отказ по токену! IP - {mi_ip(request)}") + print("/user_edit/' - 426") + return "426", 426 + content = request.json + user_id = content['user_id'] + user_name = content['user_name'] + email = content['email'] + password = content['password'] + avatar = content['avatar'] + active = content['active'] + group_id = content['group_id'] + + A = bd_module.user_edit(uid, user_id, user_name, email, password, avatar, active, group_id) + + if A == 0: + return "Отказ в доступе!", 403 + return "OK" + +@app.route('/ls_group/', methods=['GET', 'POST']) +def ls_group(): + uid = authentication(request.headers['short_token']) + if uid == None: + logging.debug(f"ls_group - Отказ по токену! IP - {mi_ip(request)}") + print("/ls_group/' - 426") + return "426", 426 + A = bd_module.ls_group(uid) + return json.dumps({"matrix": A}, separators=(',', ':')) + +""" +@app.route('/permission/', methods=['GET', 'POST']) +def permission(): + print("permission") + print(request.headers['short_token']) + uid = authentication(request.headers['short_token']) + print(uid) + if uid == None: + print("/permission/' - 426") + return "426", 426 + user_id = request.headers["user_id"] + A = permission(uid,user_id) + return json.dumps({"ls": bd_module.ls_user(uid)}, separators=(',', ':')) +""" + +@app.route('/', methods=['GET', 'POST']) +def lol(): + #return request.remote_addr + #return request.headers['x-forwarded-for'] + #return render_template("frontend/index.html") + return mi_ip(request) + +@app.route('/SAS/', methods=['GET', 'POST']) +def ASA(): + content = request.json + print(content) + + return "SAS" + + + + +@app.route('/rm_item_icon/', methods=['GET', 'POST']) +def rm_item_icon(): + uid = authentication(request.headers['short_token']) + #uid = 1 + item_id = request.headers['item_id'] + if uid == None: + logging.debug(f"rm_item_icon - Отказ по токену! IP - {mi_ip(request)}") + print("/rm_item_icon/' - 403") + return "403", 426 + a = bd_module.item_edit(uid, item_id, None, None, None, None, '-1', None, None) + if a == 1: + if os.path.isfile(f"{put}png/item/{item_id}"): + os.remove(f"{put}png/item/{item_id}") + + return "OK" + return "Отказ в доступе!", 403 + + +@app.route('/add_item_icon/', methods=['GET', 'POST']) +def add_item_icon(): + uid = authentication(request.headers['short_token']) + #uid = 1 + item_id = request.headers['item_id'] + if uid == None: + logging.debug(f"add_item_icon - Отказ по токену! IP - {mi_ip(request)}") + print("/add_item_icon/' - 403") + return "403", 426 + if 'file' in request.files: + if bd_module.item_w_test(uid,item_id) == 1: + file = request.files['file'] + print(file) + # безопасно извлекаем оригинальное имя файла + filename = secure_filename(file.filename) + directory = app.config['UPLOAD_FOLDER'] + "item" + file.save(os.path.join(directory, item_id)) + hash = hashlib.md5(open(f"{UPLOAD_FOLDER}item/{item_id}",'rb').read()).hexdigest() + bd_module.item_edit(uid, item_id, None, None, None, None, hash, None, None) + + + return f"{hash}" + return "Отказ в доступе!", 403 + return "Файла нет" + """ + if request.method == 'POST': + file = request.files['file'] + #if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) + return redirect(url_for('uploaded_file', filename=filename)) + return "OF" + """ + +@app.route('/ls_item_icon/', methods=['GET', 'POST']) +def ls_item_icon(): + print("item_icon") + item_id = request.headers['item_id'] + + uid = authentication(request.headers['short_token']) + #uid = 1 + print(uid) + if uid == None: + logging.debug(f"ls_item_icon - Отказ по токену! IP - {mi_ip(request)}") + print("/ls_item_icon/' - 403") + return "403", 426 + item_info = bd_module.item_info(uid,item_id) + if item_info != None: + if item_info[5] != None: + directory = app.config['UPLOAD_FOLDER'] + return send_from_directory(directory=directory+"item", path=str(item_id)) + return "Нет изображения", 404 + return "Нет такого предмета", 404 + + +@app.route('/ls_avatar_icon/', methods=['GET', 'POST']) +def ls_avatar_icon(): + uid = authentication(request.headers['short_token']) + url = request.headers['uid'] + #uid = 455435 + if uid == None: + logging.debug(f"avatar - Отказ по токену! IP - {mi_ip(request)}") + print("/avatar/' - 403") + return "403", 426 + url = bd_module.avatar_png(uid) + if url != None: + if int(url) == uid: + directory = app.config['UPLOAD_FOLDER'] + return send_from_directory(directory=directory+"user", path=uid) + ### SAS + directory = app.config['UPLOAD_FOLDER'] + return send_from_directory(directory=directory+"user", path=url) + return None + + +if __name__ == "__main__": + app.run(host=settings["host"], port=settings["port"]) diff --git a/__pycache__/bd_module.cpython-313.pyc b/__pycache__/bd_module.cpython-313.pyc new file mode 100644 index 0000000..7327b93 Binary files /dev/null and b/__pycache__/bd_module.cpython-313.pyc differ diff --git a/__pycache__/db_module.cpython-313.pyc b/__pycache__/db_module.cpython-313.pyc new file mode 100644 index 0000000..bbe120d Binary files /dev/null and b/__pycache__/db_module.cpython-313.pyc differ diff --git a/bd_module)_OLD.py b/bd_module)_OLD.py new file mode 100644 index 0000000..325ed06 --- /dev/null +++ b/bd_module)_OLD.py @@ -0,0 +1,1593 @@ +import os + +from time import strftime #Для (Time) +import random + +# + Просмотор предметов. ls_item(user_id) +# + Просмотор пользователей. ls_user(user_id) +# + Информация о пользователе. user_info(user_id,user_id_rec) +# ? - Информация о предмете. item_info(user_id,item_id) +# + Редактирование пользователя. user_edit(user_id,user_id_rec, user_name, email, password, avatar, active, group_id) +# - Редактирование пользователя (Подгруппы). +# + Редактирование предмета. item_edit(user_id, item_id, user_id_1, user_id_2, name, status, icon, comments, inventory_id) +# + Создание пользователя. user_add(user_id, user_name, email, password, avatar, active, group_id) +# + Создание предмета. add_item(user_id, user_id_2, name, status, icon, comments, inventory_id) +# ! - Удоление пользователя. +# + Удоление предмета. rm_item(user_id,item_id) +# - Создание груп. +# + Просмотор груп. ls_group(user_id) + + + + + +import secrets +import json + +put = os.path.dirname(os.path.realpath(__file__)) + "/"#Путь- (part-1) + +#import logging +#logging.basicConfig(filename="bd.log", level=logging.INFO) + + +with open(f"{put}settings.json", "r") as read_file: + settings = json.load(read_file) + +# BEGIN mariadb +import mariadb +import sys + +""" +try: + bd = mariadb.connect( + user="recording_spark", + password="", + host=IP, + port=3306 + ) +except mariadb.Error as e: + print(f"Error connecting to MariaDB Platform: {e}") + sys.exit(1) + +sql = bd.cursor() +""" +pool = mariadb.ConnectionPool( + pool_name = "web-app", + pool_size = 3, + pool_reset_connection = False, +) + +pool.set_config( + host=settings["Mariadb"]["host"], + port=settings["Mariadb"]["port"], + user=settings["Mariadb"]["user"], + password=settings["Mariadb"]["password"]) + +pool.add_connection() + +#pool = mariadb.ConnectionPool(user="recording_spark",password="",host=IP,port=3306,pool_name="web-app",pool_size=20) +#try: +# bd = pool.get_connection() +#except mariadb.PoolError as e: +# print(f"Error opening connection from pool: {e}") + + +bd = pool.get_connection() +sql = bd.cursor() ## TODO SAS + + + + +def ping_db(): + global bd, sql, pool + try: + bd.ping() + print("SAS_1") + except: + print("sus_1") + sql.close() + bd.close() + pool.add_connection() + bd = pool.get_connection() + sql = bd.cursor() + + + #bd.close() + #pool.add_connection() + #bd = pool.get_connection() + #sql = bd.cursor() + #bd, sql, pool = Start() + + + + + +# BEGIN bd CREATE +sql.execute(""" + CREATE TABLE IF NOT EXISTS recording_spark.group ( + group_id INT NOT NULL AUTO_INCREMENT, + name TEXT, + rights INT(12) NOT NULL, + PRIMARY KEY (group_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +""") + +sql.execute(""" +CREATE TABLE IF NOT EXISTS recording_spark.user ( + user_id INT(24) NOT NULL AUTO_INCREMENT, + user_name TEXT NOT NULL, + email TEXT, + password TEXT, + avatar tinyint(1) NOT NULL DEFAULT 0, + active tinyint(1) NOT NULL DEFAULT 0, + group_id INT NOT NULL, + PRIMARY KEY (user_id), + KEY user_FK (group_id), + CONSTRAINT user_FK FOREIGN KEY (group_id) REFERENCES recording_spark.group (group_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +""") + +sql.execute(""" + CREATE TABLE IF NOT EXISTS recording_spark.item ( + item_id INT(24) NOT NULL AUTO_INCREMENT, + item_name TEXT NOT NULL, + status TEXT, + user_id_1 INT(24) NOT NULL, + user_id_2 INT(24) NOT NULL, + icon TEXT DEFAULT NULL, + comments TEXT, + inventory_id bigint, + PRIMARY KEY (item_id), + KEY item_FK (user_id_1), + KEY item_FK_1 (user_id_2), + CONSTRAINT item_FK FOREIGN KEY (user_id_1) REFERENCES recording_spark.user (user_id), + CONSTRAINT item_FK_1 FOREIGN KEY (user_id_2) REFERENCES recording_spark.user (user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +""") + +sql.execute(""" +CREATE TABLE IF NOT EXISTS recording_spark.ownership_over_group ( + group_id INT NOT NULL, + user_id INT(24) NOT NULL, + KEY group_id (group_id), + KEY user_id (user_id), + CONSTRAINT ownership_over_group_ibfk_1 FOREIGN KEY (group_id) REFERENCES recording_spark.group (group_id), + CONSTRAINT ownership_over_group_ibfk_2 FOREIGN KEY (user_id) REFERENCES recording_spark.user (user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3; + +""") +# END + +#sql.execute("CREATE TABLE IF NOT EXISTS recording_spark.user (user_id INT, user_name TEXT, email TEXT, password TEXT, avatar TEXT, active BOOL, group_id INT)") +#sql.execute("CREATE TABLE IF NOT EXISTS recording_spark.group (group_id INT, name TEXT, user_id INT, permission BIGINT)") # group 1 - отправитель, 2 - user, 3 - admin +#sql.execute("CREATE TABLE IF NOT EXISTS recording_spark.item (item_id INT NOT NULL AUTO_INCREMENT, item_name TEXT, status TEXT, user_id_1 INT, user_id_2 INT, png_id_name TEXT, comments TEXT, inventory_id BIGINT, PRIMARY KEY (item_id))") + + +#sql.execute(""" +#CREATE TABLE IF NOT EXISTS recording_spark.ownership_over_group ( +#group_id INT NOT NULL, user_id INT NOT NULL, +#FOREIGN KEY (group_id) REFERENCES recording_spark.group(group_id), +#FOREIGN KEY (user_id) REFERENCES recording_spark.user(user_id)) + +#""") + +#sql.execute("ALTER TABLE recording_spark.user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci") # Смена кодировки! +#sql.execute("ALTER TABLE recording_spark.group CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci") # Смена кодировки! +#sql.execute("ALTER TABLE recording_spark.item CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci") # Смена кодировки! + + +#sql.execute("CREATE TABLE IF NOT EXISTS recording_spark.user_group (id INT NOT NULL AUTO_INCREMENT, group_id INT, PRIMARY KEY (id))") +#sql.execute("CREATE TABLE IF NOT EXISTS recording_spark.user_group_2 (id INT NOT NULL AUTO_INCREMENT, user_id INT, FOREIGN KEY (id) REFERENCES recording_spark.user_group(id))") + +#sql.execute("CREATE TABLE IF NOT EXISTS recording_spark.user_group (group_id INT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL AUTO_INCREMENT, FOREIGN KEY (group_id, user_id) REFERENCES recording_spark.group(group_id, user_id))") + + +#sql.execute("CREATE TABLE IF NOT EXISTS recording_spark.user_group_2 (id INT NOT NULL AUTO_INCREMENT, user_id INT, FOREIGN KEY (id) REFERENCES recording_spark.user(user_id))") + +# (SELECT group_id FROM recording_spark.user_group WHERE recording_spark.user_group.user_id = ?) +user_id = 2 + +# SELECT group_id FROM recording_spark.group WHERE recording_spark.user_group.user_id = ?) <= group_id + +# SELECT MAX(group_id) FROM recording_spark.group WHERE recording_spark.group.item = 5 AND group_id IN (SELECT group_id FROM recording_spark.user_group WHERE recording_spark.user_group.user_id = ?) + +#DECLARE SSS INT(11); + + + + +# Есть пользователи. +# Есть админестраторы. +# У польователей есть группы, а у админестраторов их нет. +# У каждого есть группа и она может быть только 1. +# У также у каждого есть спиок груп которые может польователь метья и упровлять. +# У Обычных польователей неттаких груп, а у администраторов они есть. +""" +SELECT user_id FROM recording_spark.ownership_over_group WHERE group_id IN +(SELECT group_id FROM recording_spark.ownership_over_group WHERE recording_spark.ownership_over_group.user_id = ? AND recording_spark.ownership_over_group.group_id >= +(SELECT MAX(group_id) FROM recording_spark.group WHERE recording_spark.group.item = 5 AND group_id IN (SELECT group_id FROM recording_spark.ownership_over_group WHERE recording_spark.ownership_over_group.user_id = ?))) +""" + +# SELECT item_id FROM recording_spark.item WHERE user_id_1 OR user_id_2 +#sql.execute( +"""SELECT recording_spark.item.item_id, recording_spark.item.item_name, recording_spark.item.status, recording_spark.item.user_id_1, SAS.user_name, recording_spark.item.user_id_2, SUS.user_name, recording_spark.item.png_id_name, recording_spark.item.comments, recording_spark.item.inventory_id + FROM recording_spark.item + JOIN recording_spark.user SAS + ON recording_spark.item.user_id_1 = SAS.user_id + + JOIN recording_spark.user SUS + ON recording_spark.item.user_id_2 = SUS.user_id + + WHERE recording_spark.item.user_id_1 = ? + OR recording_spark.item.user_id_2 = ? + OR recording_spark.item.user_id_2 IN + +(SELECT user_id FROM recording_spark.user WHERE group_id IN +(SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?) + +) + +""" +#,(user_id,user_id,user_id,)) +#A = sql.fetchone() +#while A != None: +# print(A) +# A = sql.fetchone() + + + + + +# CONSTRAINT recording_spark.user PRIMARY KEY (user_id) +# product_no integer PRIMARY KEY, +""" +( website_id INT(11) NOT NULL AUTO_INCREMENT, + website_name VARCHAR(25) NOT NULL, + server_name VARCHAR(20), + creation_date DATE, + CONSTRAINT websites_pk PRIMARY KEY (website_id) + +""" +# END + +# BEGIN redis +import redis +from datetime import timedelta +r = redis.StrictRedis( + host=settings["Redis"]["host"], + port=settings["Redis"]["port"], + password=settings["Redis"]["password"], + charset="utf-8", + decode_responses=True, + db = settings["Redis"]["db"] +) +# END +# BEGIN L +# Пользователь может быть в нескольких группах. +# ID levol Это уровень групы +# Если ID levol администратора меньше ID levol пользователя, то всё ок. +# Если ID levol администратора равен ID levol пользователя, то прова уразаны в двое (Не досупны функции "Редактировать/Удолять/Создовать"). +# Админестрарор может создать или выдать пользователю только те групы, в которых сам состоит. +# Только люди с ID levol 0 имеют право изменять emeil или пароль другим членам. + +# Люжи с ID levol 0 имеют обсолютные прова. + + +# Доступ на чтение <= (user_id[ID levol] <= ID levol пользователей предмета) + +# ID levol +""" +r.rpush(f"recording_spark:user_group:{4344}", 4) +r.rpush(f"recording_spark:user_group:{4344}", 2) +r.rpush(f"recording_spark:user_group:{34545}", 4) +r.rpush(f"recording_spark:user_group:{23324}", 4) +""" + + + +[1,2,3,4,5] +#- - + - - +#- - + + + + +# + +# + + + + + + + +user_id = 1 +#sql.execute( +"""SELECT recording_spark.item.item_id, recording_spark.item.item_name, recording_spark.item.status, recording_spark.item.user_id_1, SAS.user_name, recording_spark.item.user_id_2, SUS.user_name, recording_spark.item.png_id_name, recording_spark.item.comments, recording_spark.item.inventory_id + FROM recording_spark.item + JOIN recording_spark.user SAS + ON recording_spark.item.user_id_1 = SAS.user_id + + JOIN recording_spark.user SUS + ON recording_spark.item.user_id_2 = SUS.user_id + + WHERE recording_spark.item.user_id_1 = ? + OR recording_spark.item.user_id_2 = ? + OR recording_spark.item.user_id_2 + IN (SELECT recording_spark.user_group.user_id FROM recording_spark.group WHERE group_id + IN (SELECT group_id FROM recording_spark.group WHERE group_id IN (SELECT group_id FROM recording_spark.group WHERE recording_spark.user_group.user_id = ?) <= group_id + IN (SELECT MAX(group_id) FROM recording_spark.group WHERE recording_spark.group.item = 5 AND group_id + IN (SELECT group_id FROM recording_spark.user_group WHERE recording_spark.user_group.user_id = ?)))) + """ +#, (user_id, user_id, user_id, user_id)) + +#A = sql.fetchone() +#while A != None: +# print(A) +# A = sql.fetchone() + +# Пользователи +# # Сам (Читать, Редактировать) +# # Своя група (Читать, Редактировать/Удолять/Создовать) +# # Остольное (Читать, Редактировать/Удолять/Создовать) + +# Предметы +# # Своё (Читать, Редактировать/Удолять/Создовать) +# # Своя група (Читать, Редактировать/Удолять/Создовать) +# # Остольное (Читать, Редактировать/Удолять/Создовать) +""" +0000000000000001 - Предметы - Своё - Читать +0000000001000000 - Пользователи - Своё - Читать +0000000100000000 - Пользователи - Своя група - Читать +1000000000000000 - ID levol - 8 + +1000000101000001 - 33089 + + +000001 +000010 +000100 +001000 +010000 +100000 +""" + +""" +IDL - ID levol + + 11111111 + IDL user item +|10000000|100000|100000| + 1000 000101 000001 - 33089 + 1000 + 8 + + + + +111111111111 +000101000001 +000101000001 + +1000000101000001 + + +33089-4095 + +8191 - +""" + +# END + + +""" + IDL user item +|10000000|100000|100000| + 1000 000101 000001 - 33089 + 1100100 000001 000001 - 409665 + 1010 001111 001111 - 41935 + 1000 011111 111111 - 34815 + + 000101 000101 - 325 + 000001 000001 - 65 + 001111 001111 - 975 + 011111 111111 - 2047 + + + +""" +# 111111111111 +# 100 + + + +# 1000001 + +#10000 + +# # Своё (Читать, Редактировать/Удолять/Создовать) +# # Своя група (Читать, Редактировать/Удолять/Создовать) +# # Остольное (Читать, Редактировать/Удолять/Создовать) + +# 000001 - 1 +# 000100 - 4 +# 010000 - 16 + +# SELECT 65 & 16, 65 & 4, 65 & 1 +#user_id = 1 +#sql.execute( + +""" + +SELECT + +@a:=(SELECT rights FROM recording_spark.group WHERE group_id = (SELECT group_id FROM recording_spark.user WHERE user_id = ?)), +IF (1=1,@a,0) + +""" +#,(user_id, )) + + +#A = sql.fetchone() +#while A != None: +# print(A) +# A = sql.fetchone() +print("@@@@@@@@@@@@@@@@@@@") +#sql.execute( + +""" + +SELECT item_id, item_name, @a & 4, @q FROM recording_spark.item WHERE +user_id_1 = ? +OR user_id_2 = ? +OR @a & 4 = 4 +AND user_id_2 IN + + +(SELECT user_id FROM recording_spark.user WHERE group_id IN +(SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?) AND + +@a:=(SELECT rights FROM recording_spark.group WHERE group_id +IN (SELECT group_id FROM recording_spark.user WHERE user_id = ?)) + +) + + +""" +#,(user_id,user_id,user_id,user_id)) + + +#A = sql.fetchone() +#while A != None: +# print(A) +# A = sql.fetchone() + + +""" +SELECT rights & 1, rights >> 2 & 1, rights >> 4 & 1 FROM recording_spark.group WHERE group_id = +(SELECT group_id FROM recording_spark.user WHERE user_id = ?) +""" +# IF (? = 0, rights & 1, IF (? = 1, rights >> 1 & 1, IF (? = 2, rights >> 6 & 1,rights >> 7 & 1))), +def permissions(u_id,L): # 0 - item_r,1 - item_w ,2 - user_r , 3 - user_w + sql.execute(""" +SELECT +IF (? = 0, rights & 1, IF (? = 1, rights >> 1 & 1, IF (? = 2, rights >> 6 & 1,rights >> 7 & 1))), +IF (? = 0, rights >> 2 & 1, IF (? = 1, rights >> 3 & 1, IF (? = 2, rights >> 8 & 1,rights >> 9 & 1))), +IF (? = 0, rights >> 4 & 1, IF (? = 1, rights >> 5 & 1, IF (? = 2, rights >> 10 & 1,rights >> 11 & 1))) + +FROM recording_spark.group WHERE group_id = +(SELECT group_id FROM recording_spark.user WHERE user_id = ?) + """, (L ,L ,L ,L ,L ,L ,L ,L ,L ,u_id)) + A = sql.fetchone() + if A == None: + return None + else: + #print(A) + return(A) +def permissions_item_r(u_id): + return(permissions(u_id,0)) +def permissions_item_w(u_id): + return(permissions(u_id,1)) +def permissions_user_r(u_id): + return(permissions(u_id,2)) +def permissions_user_w(u_id): + return(permissions(u_id,3)) + +#print(permissions_item_r(1)) +#print(permissions_item_w(1)) +#print(permissions_user_r(1)) +#print(permissions_user_w(1)) + +admin = 0 +meneger = 34815 +sender = 41935 +user = 409665 +# BEGIN Redis +# +# Используем 2 токена, тороткоживучий 30 мин. +# И долгоживучий 30 дней (12ч. если пользовотель попросил его не запоминать.) +# Если пользователь хочет прдить свой токен то он получит не только ноый короткий токен, +# но и новый доинный, предыдуший будет немедленно удолён. +# +# BEGIN token + +def update_short_token(user_id, live_token): + TTL = r.pttl(f"recording_spark:{user_id}:{live_token}:PC-INFO") / 1000 + #print(int(L)) + if TTL > 31*60: + rm_short_token(user_id, live_token) + list_A = r.lrange(f"recording_spark:{user_id}:{live_token}:PC-INFO", 0, -1) + for key in r.scan_iter(f"recording_spark:{user_id}:{live_token}:*"): + r.delete(key) + short_token, live_token = add_live_token(user_id, list_A[0], list_A[1], list_A[2],int(TTL)) + + #short_token = add_short_token(user_id, live_token) + return short_token, live_token + return None, None + +#rm_short_token() + +def rm_live_token(user_id, live_token): + short_token = r.get(f'recording_spark:{user_id}:{live_token}:short_token') ### !!! ИНЕКЦИЯ !!! + r.delete(f"recording_spark:short_token:{short_token}") + A = False + for key in r.scan_iter(f"recording_spark:{user_id}:{live_token}:*"): + A = True + r.delete(key) + return A + +def rm_short_token(user_id, live_token): + short_token = r.get(f'recording_spark:{user_id}:{live_token}:short_token') ### !!! ИНЕКЦИЯ !!! + r.delete(f"recording_spark:short_token:{short_token}") + r.delete(f'recording_spark:{user_id}:{live_token}:short_token') + +def add_short_token(user_id, live_token): + short_token = secrets.token_urlsafe(32) + r.set(f"recording_spark:short_token:{short_token}", user_id, 30*60) + r.set(f"recording_spark:{user_id}:{live_token}:short_token", short_token, 30*60) + return short_token + + +def add_live_token(user_id, ps_info_name, ps_info_ip, ps_info_date,TTL): + live_token = secrets.token_urlsafe(128) + #if remember == 1: + # TTL = 30*24*60*60 + #elif remember == 0: + # TTL = 12*60*60 + #print(ps_info_date) + + r.rpush(f"recording_spark:{str(user_id)}:{live_token}:PC-INFO", ps_info_name, ps_info_ip, str(ps_info_date)) + r.expire(f'recording_spark:{user_id}:{live_token}:PC-INFO', timedelta(seconds=TTL)) + #r.ttl(f"recording_spark:{user_id}:{live_token}:PC-INFO", int(TTL)) + #r.rpushx(f"recording_spark:{user_id}:{live_token}:PC-INFO", int(TTL)) + short_token = add_short_token(user_id, live_token) + return short_token, live_token + + + + + #r.set(f"recording_spark:{user_id}:{live_token}:PC-INFO", ps_info, 30*24*60*60) + return live_token + +def check_user_live_token(user_id,live_token): + user_id = r.get(f'recording_spark:{user_id}:{live_token}:PC-INFO') ### !!! ИНЕКЦИЯ !!! + if user_id == None: + print("None") + return None + else: + short_token = update_short_token(user_id, live_token) + #short_token = add_short_token(user_id) + #r.expire(f'recording_spark:live_token:{live_token}', timedelta(seconds=30*24*60*60)) + return short_token + + +#add_live_token(1, "40-PC", "192.168.1.40", "25-04-2022",1) + +""" +def add_short_token(user_id): + short_token = secrets.token_urlsafe(32) + r.set(f"recording_spark:short_token:{short_token}", user_id, 12*60*60) + return short_token + +def add_live_token(user_id): + live_token = secrets.token_urlsafe(128) + r.set(f"recording_spark:live_token:{live_token}", user_id, 30*24*60*60) + return live_token + +""" +def check_user_short_token(short_token): + user_id = r.get(f'recording_spark:short_token:{short_token}') ### !!! ИНЕКЦИЯ !!! + if user_id == None: + return None + else: + return user_id + + +def ls_sessions(user_id): # !!! МОГУТ БЫТЬ ПРОБЛЕМЫ !!! + A = 0 + Q = [] + for key in r.scan_iter(f"recording_spark:{user_id}:*:PC-INFO"): + print(key) + short_token = r.lrange(key, 0, -1) + short_token.append(A) + Q.append(short_token) + A = A + 1 + return Q + +#print(ls_sessions(1)) + + +def rm_live_token_position(user_id, position): # !!! МОГУТ БЫТЬ ПРОБЛЕМЫ !!! + A = 0 + for key in r.scan_iter(f"recording_spark:{user_id}:*"): + if position == A: + short_token = r.get(f'recording_spark:{user_id}:{key}:short_token') ### !!! ИНЕКЦИЯ !!! + r.delete(f"recording_spark:short_token:{short_token}") + for key in r.scan_iter(f"recording_spark:{user_id}:{key}:*"): + r.delete(key) + A = A + 1 + + +def full_sessions_kill(user_id): + for key in r.scan_iter(f"recording_spark:{user_id}:*:short_token"): + short_token = r.get(key) + r.delete(f"recording_spark:short_token:{short_token}") + r.delete(key) + for key in r.scan_iter(f"recording_spark:{user_id}:*"): + r.delete(key) + +#full_sessions_kill(1) +#print(update_short_token(1, "tAVtQvl-_NmPOXbRoqfPWVtlx5gblZjcjVZmUy6qGa60Dzc7bWqHzj0ybsoyvv1rj5EG-jLc2eEuTR4riEvQy_ggVMLZVx9VDlI_z1fqpWvW6wRQDqxW4upiSLmqseCPHuqJMbNpXqa8l-fIH8Lrb2r73wcCklhFLdqh7lRe9og")) + +#add_live_token(1, "80-PC", "192.168.1.64", "26-04-2022",12*60*60) + + + + +#r.delete(f"recording_spark:1:*") + +"SGSlPpX0m-bWDmgDkaLrtAf9JAjno46Wg01KmEAf0Qs" +""" +def check_user_live_token(live_token): + user_id = r.get(f'recording_spark:live_token:{live_token}') ### !!! ИНЕКЦИЯ !!! + if user_id == None: + print("None") + return None + else: + short_token = add_short_token(user_id) + r.expire(f'recording_spark:live_token:{live_token}', timedelta(seconds=30*24*60*60)) + return short_token +""" + +# END + +def add_user_registration(email, password): + G = int(strftime("%Y")) # Год + M = int(strftime("%m")) # Месяц + D = int(strftime("%d")) # День + ch = int(strftime("%H")) + m = int(strftime("%M")) + s = int(strftime("%S")) + #id = str(D) + str(ch) + str(m) + str(s) + str(random.randint(0, 100000)) + id = f"{D}{ch}{m}{s}{random.randint(0, 100000)}" + + + date = f"{G}-{M}-{D}" + r.set(f"recording_spark:registration:{id}:email", email, 7*24*60*60) + r.set(f"recording_spark:registration:{id}:password", password, 7*24*60*60) + r.set(f"recording_spark:registration:{id}:date", date, 7*24*60*60) + return id + + + + +# END +# BEGIN mariadb_SAS + +def user_name(user_id): + ping_db() + sql.execute("SELECT user_name FROM recording_spark.user WHERE user_id = ?", (user_id,)) + A = sql.fetchone() + if A == None: + return None + else: + return(A[0]) + + +def avatar_png(user_id): + ping_db() + sql.execute("SELECT avatar FROM recording_spark.user WHERE user_id = ?", (user_id,)) + A = sql.fetchone() + if A == None: + return None + else: + return(A[0]) + + +def check_user(email, password): + ping_db() + sql.execute("SELECT user_id, user_name, active FROM recording_spark.user WHERE email = ? AND password = ?", (email, password)) + A = sql.fetchone() + if A == None: + return None, None, None + else: + print(A[0]) + return(A[0], A[1], A[2]) + + +#def ls_item2(user_id): +# Q = [[],[],[],[],[]] +# sql.execute("SELECT item_id, item_name, status, user_id_1, user_id_2 FROM recording_spark.item WHERE user_id_1 = ? OR user_id_2 = ?", (user_id, user_id)) +# A = sql.fetchone() +# if A == None: +# return None +# R = 0 +# while A != None: +# for a in A: +# Q[R].append(a) +# A = sql.fetchone() +# R = R + 1 + #print(Q[2][1]) +# return Q +# OR LLL.user_id IN (SELECT user_id FROM recording_spark.group WHERE group_id = ?) +""" + JOIN recording_spark.group LLL + ON recording_spark.item.user_id_2 = LLL.user_id +""" + +def ls_user(user_id): + ping_db() + A = permissions_user_r(user_id) + print(A) + sql.execute("""SELECT recording_spark.user.user_id, recording_spark.user.user_name, recording_spark.user.email, recording_spark.user.avatar, recording_spark.user.active, SAS.group_id, SAS.name, SAS.rights + FROM recording_spark.user + JOIN recording_spark.group SAS + ON recording_spark.user.group_id = SAS.group_id + + WHERE + IF (? = 1, user_id >= 0, + IF (? = 1, user_id = ?, NULL) OR + IF (? = 1, +user_id IN +(SELECT user_id FROM recording_spark.user WHERE group_id IN +(SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)), +NULL) + ) + + """, (A[2], A[0], user_id, A[1], user_id)) + A = sql.fetchall() + if A == None: + return None + else: + print(A) + print(A[0]) + #return(A[0]) + return A + + +def ls_user__3(user_id): + A = permissions_user_r(user_id) + print(A) + sql.execute("""SELECT user_id, user_name, email, avatar, active, group_id + FROM recording_spark.user + WHERE + IF (? = 1, user_id >= 0, + IF (? = 1, user_id = ?, NULL) OR + IF (? = 1, +user_id IN +(SELECT user_id FROM recording_spark.user WHERE group_id IN +(SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)), +NULL) + ) + + """, (A[2], A[0], user_id, A[1], user_id)) + Q = [] + A = sql.fetchone() + while A != None: + print(A) + Q.append(list(A)) + A = sql.fetchone() + return Q + + +#print(ls_user(4)) +import time + +def ls_item(user_id): + ping_db() + tic = time.perf_counter() + if settings["Mariadb"]["select_commit"] == True: + bd.commit() + toc = time.perf_counter() + print(f"Вычисление заняло {toc - tic:0.8f} секунд") + A = permissions_item_r(user_id) + #if A[2] == 1: + sql.execute("""SELECT recording_spark.item.item_id, recording_spark.item.item_name, recording_spark.item.status, recording_spark.item.user_id_1, SAS.user_name, recording_spark.item.user_id_2, SUS.user_name, recording_spark.item.icon, recording_spark.item.comments, recording_spark.item.inventory_id + + FROM recording_spark.item + + JOIN recording_spark.user SAS + ON recording_spark.item.user_id_1 = SAS.user_id + + JOIN recording_spark.user SUS + ON recording_spark.item.user_id_2 = SUS.user_id + + WHERE + IF (? = 1, recording_spark.item.item_id >= 0, + + + IF (? = 1, + recording_spark.item.user_id_1 = ? + OR recording_spark.item.user_id_2 = ?, + NULL) OR + IF (? = 1, recording_spark.item.user_id_2 IN +(SELECT user_id FROM recording_spark.user WHERE group_id IN +(SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)), +NULL) +) + + """, (A[2], A[0], user_id, user_id, A[1], user_id)) + Q = [] + A = sql.fetchone() + while A != None: + #print(A) + Q.append(list(A)) + A = sql.fetchone() + return Q + + + +#print(ls_item(3)) + + + +#def ls_item(user_id): + #Q = [[],[],[],[],[],[],[],[],[],[]] +# sql.execute( + """SELECT recording_spark.item.item_id, recording_spark.item.item_name, recording_spark.item.status, recording_spark.item.user_id_1, SAS.user_name, recording_spark.item.user_id_2, SUS.user_name, recording_spark.item.png_id_name, recording_spark.item.comments, recording_spark.item.inventory_id + FROM recording_spark.item + JOIN recording_spark.user SAS + ON recording_spark.item.user_id_1 = SAS.user_id + + JOIN recording_spark.user SUS + ON recording_spark.item.user_id_2 = SUS.user_id + + WHERE recording_spark.item.user_id_1 = ? + OR recording_spark.item.user_id_2 = ? + OR recording_spark.item.user_id_2 IN + +(SELECT user_id FROM recording_spark.user WHERE group_id IN +(SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?) + +) + """ +# , (user_id, user_id, user_id)) +# A = sql.fetchone() +# while A != None: +# print(A) +# Q.append(A) + #for a in A: + # print(a) + """ + if A == None: + return None + R = 0 + while A != None: + for a in A: + print(a) + if a == None: + Q[R].append("None") + else: + Q[R].append(a) + R = R + 1 + print("--------------") + A = sql.fetchone() + R = 0 + return Q + """ + + +#bd.commit() +#sql.execute( +""" + SELECT * FROM recording_spark.user + WHERE + ? = user_id +""" +#,(id,)) + +#A = sql.fetchone() +#print(A) + + +# 3 зп! +def user_add(user_id:int, user_name:str, email:str, password:str, avatar:bool, active:bool, group_id:int): + ping_db() + def add(user_name, email, password, avatar, active, group_id): + print(user_name, email, password, avatar, active, group_id) + sql.execute(""" + INSERT INTO recording_spark.user + VALUES + (NULL, ?, ?, ?, ?, ?, ?) + """,(user_name, email, password, avatar, active, group_id)) + id = sql.lastrowid + print(id) + bd.commit() + return id + A = permissions_user_w(user_id) + print(A) + if A[2] == 1: + id = add(user_name, email, password, avatar, active, group_id) + return id + elif A[1] == 1: + sql.execute(""" + SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ? + """,(user_id,)) + Q = [] + A = sql.fetchone() + while A != None: + print(A) + Q.append(A[0]) + A = sql.fetchone() + print(Q) + for a in Q: + + if a == group_id: + id = add(user_name, email, password, avatar, active, group_id) + bd.commit() + return id + else: + return None + + +#id = user_add(1, "Фёдор", None, "mnvbijdseahicihsf72e3wh89hfuh34h89f", 0, 1, 3) +#print(id) + +# Удолить могут только отпровители или те кто имеет власть над отпровителями. + +# 3-4 зп! +def item_edit_2(user_id, item_id, user_id_1, user_id_2, name, status, icon, comments, inventory_id): + A = permissions_item_w(user_id) + print(A) + sql.execute("""SELECT IF + (? IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)) + , 1,0), IF + (? IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)) + , 1,0) + + """,(user_id_1,user_id, user_id_2,user_id)) + Al = sql.fetchone() + print(Al) + if Al[0] == 1 or user_id_1 == user_id or user_id_1 == -1 and Al[1] == 1 or user_id_2 == user_id or user_id_2 == -1: + sql.execute("""SELECT IF ( + (SELECT user_id_1 FROM recording_spark.item WHERE item_id = ?) IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)) + , 1,0 + + ) + + """,(item_id,user_id,)) + Am = sql.fetchone() + print(Am) + if Am[0] == 1: + sql.execute("""UPDATE recording_spark.item + SET + item_name= IF (? = 'None', item_name, ?), + status = IF (? = 'None', status, ?), + user_id_1 = IF (? = -1, user_id_1, ?), + user_id_2 = IF (? = -1, user_id_2, ?), + icon = IF (? = 'None', icon, ?), + comments = IF (? = 'None', comments, ?), + inventory_id = IF (? = 'None', inventory_id, ?) + + WHERE item_id = ? + + + """,(name,name,status,status,user_id_1,user_id_1,user_id_2,user_id_2,icon,icon,comments,comments,inventory_id,inventory_id,item_id)) + + + + + + +item_id = 3 +user_id = 23 +user_id_1 = 1 +user_id_2 = -1 +#A = permissions_user_w(user_id) +#print(f'uw{A}') +#Al = permissions_user_r(user_id) +#print(f'ur{Al}') +#A = permissions_item_r(user_id) +#print(f'ir{A}') +#A = permissions_item_w(user_id) +#print(f'iw{A}') +# 2 зп! +def item_edit(user_id, item_id, user_id_1, user_id_2, name, status, icon, comments, inventory_id): + ping_db() + print(user_id, item_id, user_id_1, user_id_2, name, status, icon, comments, inventory_id) + Al = permissions_user_r(user_id) + print(f'ur{Al}') + A = permissions_item_w(user_id) + print(f'iw{A}') + sql.execute("""UPDATE recording_spark.item + SET + item_name= IF (? IS NULL, item_name, ?), + status = IF (? IS NULL, status, ?), + user_id_1 = IF (? IS NULL, user_id_1, ?), + user_id_2 = IF (? IS NULL, user_id_2, ?), + icon = IF (? IS NULL, icon, IF (? = '-1', NULL, ?)), + comments = IF (? IS NULL, comments, ?), + inventory_id = IF (? IS NULL, inventory_id, IF (? = -1, NULL, ?)) + + WHERE item_id = + + IF (1 = + + IF (? = 1, 1, + IF (? = 1 AND (? = (SELECT user_id_1 FROM recording_spark.item WHERE item_id = ?)), 1, + IF (? = 1 AND + ( + (SELECT user_id_1 FROM recording_spark.item WHERE item_id = ?) IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?))), 1, 0 + + ) + ) + ) AND + + IF (? = 1, 1, + + IF (? IS NULL, 1, + IF (? = 1 AND ? = ?, 1, + IF (? = 1 AND + (? IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?))),1,0) + + ) + + ) + + AND + IF (? IS NULL, 1, + IF (? = 1 AND ? = ?, 1, + IF (? = 1 AND + (? IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?))),1,0) + + ) + ) + + ) + + + , ?, NULL) + + + """,(name,name,status,status,user_id_1,user_id_1,user_id_2,user_id_2,icon,icon,icon,comments,comments,inventory_id,inventory_id,inventory_id, + A[2], A[0],user_id,item_id, A[1],item_id,user_id, + Al[2], + user_id_1, Al[0],user_id_1,user_id, Al[1],user_id_1,user_id, + user_id_2, Al[0],user_id_2,user_id, Al[1],user_id_2,user_id, + item_id + )) + #Al = sql.fetchone() + id = sql.rowcount + print(id) + bd.commit() + return int(id) + + +#item_edit(1, 32, None, None, None, None, -1, None, None) + +#item_edit(user_id, item_id, user_id_1, user_id_2, "None", "None", -1, "None", 465) + + +def item_edit_3(user_id, item_id, user_id_1, user_id_2, name, status, icon, comments, inventory_id): + A = permissions_item_w(user_id) + print(A) + sql.execute("""UPDATE recording_spark.item + SET + item_name= IF (? IS NULL, item_name, ?), + status = IF (? IS NULL, status, ?), + user_id_1 = IF (? IS NULL, user_id_1, ?), + user_id_2 = IF (? IS NULL, user_id_2, ?), + icon = IF (? IS NULL, icon, ?), + comments = IF (? IS NULL, comments, ?), + inventory_id = IF (? IS NULL, inventory_id, ?) + + WHERE + + IF (? = + (SELECT user_id_1 FROM recording_spark.item WHERE item_id = ?) + AND ? = 1, item_id = ?, + IF (? = 1, item_id = ?, + IF (? = 1, + + IF + ((SELECT user_id_1 FROM recording_spark.item WHERE item_id = ?) IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)) + , + + + ,NULL)))) + + + , NULL))) + + + + + + """,(name,name,status,status,user_id_1,user_id_1,user_id_2,user_id_2,icon,icon,comments,comments,inventory_id,inventory_id,item_id)) + id = sql.rowcount + print(id) + +#item_edit(1, 3, -1, -1, "SAS", "None", 0, "None", -1) + +""" +(SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?) + +SELECT user_id_1 FROM recording_spark.item WHERE ? = item_id + +SELECT + IF (? = ?, (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?), + + IF (? = 1, (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?), + IF (? = 1 AND ( + + ? IN + + (SELECT user_id FROM uid WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?))), + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?) + , NULL))) + + +""" + + +def permission(u_id,user_id): + ping_db() + A = permissions_user_r(u_id) + print(A) + sql.execute("""SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = + IF (? = ? AND ? = 1, ?, + IF (? = 1, ?, + IF (? = 1 AND + + ? IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)), ?, NULL))) + + """,(u_id, user_id,A[0],user_id,A[2],user_id, A[1], user_id, u_id, user_id)) + id = sql.fetchall() + #id = sql.rowcount + print(id) + sql.execute("""SELECT group_id FROM recording_spark.user WHERE user_id = + IF (? = ? AND ? = 1, ?, + IF (? = 1, ?, + IF (? = 1 AND + + ? IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)), ?, NULL))) + + """,(u_id, user_id,A[0],user_id,A[2],user_id, A[1], user_id, u_id, user_id)) + permission_int = sql.fetchall() + + return id, permission_int + +# permission(3,1) + +# 2 зп! +def item_rm(user_id,item_id): + ping_db() + A = permissions_item_w(user_id) + sql.execute("""DELETE FROM recording_spark.item WHERE item_id = + + IF (? = (SELECT user_id_1 FROM recording_spark.item WHERE item_id = ?) + AND + (? = 1), ?, + + IF (? = 1, ?, + IF (? = 1 AND ( + + ? IN + + (SELECT item_id FROM recording_spark.item WHERE user_id_1 + + IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)))), ?, NULL))) + + + + + """,(user_id,item_id,A[0],item_id, A[2],item_id, A[1],item_id,user_id, item_id)) + #id = sql.fetchone() + id = sql.rowcount + print(id) + return id + +#item_rm(5,2) +#sql.execute( + """ + DELETE recording_spark.user, recording_spark.ownership_over_group + FROM recording_spark.ownership_over_group + INNER JOIN recording_spark.user + + WHERE + + recording_spark.ownership_over_group.user_id = 23 AND + recording_spark.user.user_id = 23 + + + + + """ + #) +#id = sql.rowcount +#print(id) +def user_rm(user_id,user_id_rm): + ping_db() + A = permissions_user_w(user_id) + sql.execute("""DELETE + recording_spark.user, recording_spark.ownership_over_group FROM + recording_spark.user + INNER JOIN recording_spark.user uid + INNER JOIN recording_spark.ownership_over_group + + WHERE + IF (? = ?, user_id = NULL, + + IF (? = 1, user_id = ?, + IF (? = 1 AND ( + + ? IN + + (SELECT user_id FROM uid WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?))), user_id = ?, user_id = NULL))) + + """,(user_id, user_id_rm,A[0],user_id_rm, A[2],user_id_rm, user_id,user_id_rm)) + #id = sql.fetchone() + id = sql.rowcount + print(id) + return id +#item_rm(5,2) +#user_rm(1,23) + +############################################### + !!! ПЕРЕДАЛАТЬ !!! +def item_rm_2(user_id,item_id): + A = permissions_item_w(user_id) + print(A) + if A[2] != 1: + sql.execute("DELETE FROM recording_spark.item WHERE ? = item_id",(item_id,)) + bd.commit() + return 1 + if A[0] != 1: + print("AAA") + return 0 + sql.execute("SELECT user_id_1 FROM recording_spark.item WHERE ? = item_id",(item_id,)) + Al = sql.fetchone() + if len(Al) != 0: + if user_id == Al[0]: + sql.execute("DELETE FROM recording_spark.item WHERE ? = item_id",(item_id,)) + bd.commit() + return 1 + elif A[1] != 1: + sql.execute("""SELECT IF + (? IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)) + , 1,0) + """,(Al[0],user_id,)) + Al = sql.fetchone() + if Al == 1: + sql.execute("DELETE FROM recording_spark.item WHERE ? = item_id",(item_id,)) + bd.commit() + return 1 + else: + print("SSS") + return 0 + + + else: + print("Нет такого!") + return 0 + +# 4 зп! +def add_item(user_id, user_id_1, user_id_2, name, status, icon, comments, inventory_id): + ping_db() + print(user_id, user_id_1, user_id_2, name, status, icon, comments, inventory_id) + Al = permissions_user_r(user_id) + print(f'ur{Al}') + A = permissions_item_w(user_id) + print(f'iw{A}') + sql.execute("""SELECT + + (SELECT + IF (1 = + + IF ( + IF ((? = ?) AND (? = ?), 1,0) = 0, + IF (? = 1,1, + IF (? = 1, + + + + IF (1 = + + IF (? = -1, 1, IF ( + ? + IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)) OR ? = ?, + 1, 0 + ) + ) + + AND + + IF (? = -1, 1, IF ( + ? + IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)) OR ? = ?, + 1, 0 + ) + + ),1,0),0)),1) + + + + , + + IF (? = 1, 1, + IF (? = 1, 1, + IF (? = 1, + + 1,0))),0)) + + + """,(user_id_1, user_id, user_id_2, user_id,Al[2],Al[1],user_id_1, user_id_1, user_id,user_id_1,user_id, user_id_2, user_id_2, user_id,user_id_2,user_id, A[0],A[2], A[1])) + id = sql.fetchone() + if id[0] == 1: + + print("Ура!!!") + sql.execute(""" + INSERT INTO recording_spark.item + VALUES + (NULL, ?, ?, ?, ?, ?, ?, ?) + """,(name, status, user_id_1, user_id_2, icon, comments, inventory_id)) + id = sql.lastrowid + print(id) + bd.commit() + return id + #id = sql.rowcount + print(id) + #bd.commit() + return id + + +#add_item(1, 5, 2, "SAS", "SUS", 0, "SGS", 3333) + +# 3 зп! +# Работает, НЕ ТРОГОЙ!!! но мне пох))) +def add_item_3(user_id, user_id_2, name, status, icon, comments, inventory_id): + A = permissions_item_w(user_id) + print(A) + if A[0] == 1 or A[1] == 1 or A[2] == 1: + sql.execute(""" + SELECT IF + (? IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)) + , 1,0) + """,(user_id_2,user_id)) + Al = sql.fetchone() + if Al[0] == 1: + sql.execute(""" + INSERT INTO recording_spark.item + VALUES + (NULL, ?, ?, ?, ?, ?, ?, ?) + """,(name, status, user_id, user_id_2, icon, comments, inventory_id)) + id = sql.lastrowid + print(id) + bd.commit() + return id + return 0 + +#id = add_item(1, 23, "Морковь", "В пути", 0, "2кг.", 784538673428743) +#print(id) + +# 2 зп! +def user_edit(user_id,user_id_rec, user_name, email, password, avatar, active, group_id): + ping_db() + A = permissions_user_w(user_id) + print(user_id) + print(A) + print(user_id,user_id_rec, user_name, email, password, avatar, active, group_id) + sql.execute("""UPDATE recording_spark.user + SET + user_name = IF (? IS NULL, user_name, ?), + email = IF (? IS NULL, email, ?), + password = IF (? IS NULL, password, ?), + avatar = IF (? IS NULL, avatar, ?), + active = IF (? IS NULL, active, ?), + group_id = IF (? IS NULL, group_id, ?) + + WHERE + IF (? = ? AND ? = 1, recording_spark.user.user_id = ?, + IF (? = 1, recording_spark.user.user_id = ?, + IF (? = 1, + +? = user_id and user_id IN +(SELECT user_id FROM recording_spark.user WHERE group_id IN +(SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)), NULL))) + + """, (user_name, user_name, email, email, password, password, avatar, avatar, active, active, group_id, group_id, user_id, user_id_rec, A[0], user_id, A[2], user_id_rec, A[1] , user_id_rec, user_id)) + bd.commit() + id = sql.rowcount + print(id) + if (id == 1 and active == 0) or (id == 1 and password != None): + full_sessions_kill(user_id_rec) + return id + #A = sql.fetchone() + #if A == None: + #return None + #else: + #print(A) + + +#sql.execute("SELECT IF (? IS NULL, 1, 0)", (None,)) +#print(sql.fetchone()) + +#user_edit(1,5, None, "GGG", None,None,None,None) + +# 2 зп! +def user_info(user_id,user_id_rec): + ping_db() + A = permissions_user_r(user_id) + print(A) + sql.execute("""SELECT recording_spark.user.user_name, recording_spark.user.email, recording_spark.user.avatar, recording_spark.user.active, SAS.group_id, SAS.rights + FROM recording_spark.user + JOIN recording_spark.group SAS + ON recording_spark.user.group_id = SAS.group_id + + WHERE + IF (? = ? AND ? = 1, recording_spark.user.user_id = ?, + IF (? = 1, recording_spark.user.user_id = ?, + IF (? = 1, + +? = user_id and user_id IN +(SELECT user_id FROM recording_spark.user WHERE group_id IN +(SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)), NULL))) + + """, (int(user_id),int(user_id_rec), A[0],int(user_id), A[2],int(user_id_rec), A[1] ,int(user_id_rec),int(user_id))) + A = sql.fetchone() + if A == None: + return None + else: + print(A) + print(A[0]) + #return(A[0]) + return A + +def ls_group(user_id): + ping_db() + A = permissions_user_r(user_id) + if A[2] == 1: + sql.execute("SELECT * FROM recording_spark.group") + group_list = sql.fetchall() + else: + sql.execute("""SELECT * FROM recording_spark.group WHERE group_id + IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?) + + + """, (user_id, user_id)) + group_list = sql.fetchall() + return group_list + + + +def item_info(user_id,item_id): + ping_db() + A = permissions_item_r(user_id) + sql.execute("""SELECT * FROM recording_spark.item + WHERE item_id = + + IF (? = (SELECT user_id_1 FROM recording_spark.item WHERE item_id = ?) + AND + (? = 1), ?, + + IF (? = 1, ?, + IF (? = 1 AND ( + + ? IN + + (SELECT item_id FROM recording_spark.item WHERE user_id_1 + + IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)))), ?, NULL))) + + + + + """,(user_id,item_id,A[0],item_id, A[2],item_id, A[1],item_id,user_id, item_id)) + group_list = sql.fetchone() + return group_list + +def item_w_test(user_id,item_id): + ping_db() + A = permissions_item_w(user_id) + sql.execute("""SELECT + + IF (? = (SELECT user_id_1 FROM recording_spark.item WHERE item_id = ?) + AND + (? = 1), 1, + + IF (? = 1, 1, + IF (? = 1 AND ( + + ? IN + + (SELECT item_id FROM recording_spark.item WHERE user_id_1 + + IN + (SELECT user_id FROM recording_spark.user WHERE group_id IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?)))), 1, 0))) + + + + + """,(user_id,item_id,A[0], A[2], A[1],item_id,user_id)) + group_list = sql.fetchone() + print(group_list[0]) + return int(group_list[0]) + +#print(item_info(1,20)) +#print(item_w_test(1,100)) + + +"""SELECT * FROM recording_spark.group WHERE group_id + IN + (SELECT group_id FROM recording_spark.ownership_over_group WHERE user_id = ?) OR group_id = + (SELECT group_id FROM recording_spark.user WHERE user_id = ?) + + + """ + +#print(ls_group(2)) + + +#user_info(2,4) + + + +#print(ls_item(5)) + +#print(permissions_item_w(3)) + + +# END + + +#sql.execute("INSERT INTO recording_spark.group VALUES (4, 'user', 34545, 409665)") +#sql.execute("INSERT INTO recording_spark.group VALUES (4, 'user', 455435, 409665)") +#bd.commit() +#print(ls_item(1)) + + + + + + +#ls_item(455435) + + + + + + diff --git a/bd_module.py b/bd_module.py new file mode 100644 index 0000000..ea3d7f1 --- /dev/null +++ b/bd_module.py @@ -0,0 +1,748 @@ +import os +import json +import random +import secrets +from time import strftime # Для (Time) +from datetime import timedelta + +import asyncio +import aiosqlite + +# BEGIN redis (оставлено синхронным как в исходнике) +import redis + +put = os.path.dirname(os.path.realpath(__file__)) + "/" + +with open(f"{put}settings.json", "r", encoding="utf-8") as read_file: + settings = json.load(read_file) + +r = redis.StrictRedis( + host=settings["Redis"]["host"], + port=settings["Redis"]["port"], + password=settings["Redis"]["password"], + encoding="utf-8", + decode_responses=True, + db=settings["Redis"]["db"], +) +# END redis + + +# ------------------------- +# SQLite (async) backend +# ------------------------- + +_SQLITE_PATH = ( + settings.get("Sqlite", {}).get("path") + or os.path.join(put, "recording_spark.sqlite3") +) + +# Если раньше в settings.json было Mariadb.select_commit — оставим аналог +_SQLITE_SELECT_COMMIT = bool(settings.get("Sqlite", {}).get("select_commit", False)) + +_db: aiosqlite.Connection | None = None +_db_init_lock = asyncio.Lock() +_db_initialized = False + + +async def _connect_db() -> aiosqlite.Connection: + """ + Возвращает глобальное соединение aiosqlite, инициализирует схему при первом обращении. + """ + global _db, _db_initialized + async with _db_init_lock: + if _db is None: + _db = await aiosqlite.connect(_SQLITE_PATH) + _db.row_factory = aiosqlite.Row + await _db.execute("PRAGMA foreign_keys = ON;") + await _db.execute("PRAGMA journal_mode = WAL;") + await _db.execute("PRAGMA synchronous = NORMAL;") + await _db.execute("PRAGMA busy_timeout = 5000;") + + if not _db_initialized: + await _init_schema(_db) + _db_initialized = True + + return _db + + +async def _init_schema(db: aiosqlite.Connection) -> None: + """ + Создаёт таблицы и индексы в SQLite. + Заменяет MySQL/MariaDB схему recording_spark.* на rs_* (без schema prefix). + """ + await db.executescript( + """ + CREATE TABLE IF NOT EXISTS rs_group ( + group_id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT, + rights INTEGER NOT NULL + ); + + CREATE TABLE IF NOT EXISTS rs_user ( + user_id INTEGER PRIMARY KEY AUTOINCREMENT, + user_name TEXT NOT NULL, + email TEXT, + password TEXT, + avatar INTEGER NOT NULL DEFAULT 0, + active INTEGER NOT NULL DEFAULT 0, + group_id INTEGER NOT NULL, + FOREIGN KEY (group_id) REFERENCES rs_group (group_id) + ); + + CREATE TABLE IF NOT EXISTS rs_item ( + item_id INTEGER PRIMARY KEY AUTOINCREMENT, + item_name TEXT NOT NULL, + status TEXT, + user_id_1 INTEGER NOT NULL, + user_id_2 INTEGER NOT NULL, + icon TEXT DEFAULT NULL, + comments TEXT, + inventory_id INTEGER, + FOREIGN KEY (user_id_1) REFERENCES rs_user (user_id), + FOREIGN KEY (user_id_2) REFERENCES rs_user (user_id) + ); + + CREATE TABLE IF NOT EXISTS rs_ownership_over_group ( + group_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY (group_id) REFERENCES rs_group (group_id), + FOREIGN KEY (user_id) REFERENCES rs_user (user_id) + ); + + CREATE INDEX IF NOT EXISTS idx_rs_user_group_id ON rs_user(group_id); + CREATE INDEX IF NOT EXISTS idx_rs_item_user1 ON rs_item(user_id_1); + CREATE INDEX IF NOT EXISTS idx_rs_item_user2 ON rs_item(user_id_2); + CREATE INDEX IF NOT EXISTS idx_rs_oog_group_id ON rs_ownership_over_group(group_id); + CREATE INDEX IF NOT EXISTS idx_rs_oog_user_id ON rs_ownership_over_group(user_id); + """ + ) + await db.commit() + + +async def close_db() -> None: + global _db, _db_initialized + if _db is not None: + await _db.close() + _db = None + _db_initialized = False + + +async def ping_db() -> None: + """ + В MariaDB это был ping/reconnect. В SQLite (aiosqlite) просто гарантируем, что соединение поднято. + """ + await _connect_db() + + +async def _fetchone(q: str, params: tuple = ()) -> aiosqlite.Row | None: + db = await _connect_db() + if _SQLITE_SELECT_COMMIT: + await db.commit() + async with db.execute(q, params) as cur: + return await cur.fetchone() + + +async def _fetchall(q: str, params: tuple = ()) -> list[aiosqlite.Row]: + db = await _connect_db() + if _SQLITE_SELECT_COMMIT: + await db.commit() + async with db.execute(q, params) as cur: + return await cur.fetchall() + + +async def _execute(q: str, params: tuple = (), *, commit: bool = False) -> aiosqlite.Cursor: + db = await _connect_db() + cur = await db.execute(q, params) + if commit: + await db.commit() + return cur + + +# ------------------------- +# Rights / permissions +# ------------------------- + +# Биты прав как в исходнике (mariadb): +# item_r: bits 0,2,4 +# item_w: bits 1,3,5 +# user_r: bits 6,8,10 +# user_w: bits 7,9,11 +_BITS = { + "item_r": (0, 2, 4), + "item_w": (1, 3, 5), + "user_r": (6, 8, 10), + "user_w": (7, 9, 11), +} + + +async def _get_user_rights(user_id: int) -> int | None: + row = await _fetchone( + """ + SELECT g.rights + FROM rs_user u + JOIN rs_group g ON u.group_id = g.group_id + WHERE u.user_id = ? + """, + (int(user_id),), + ) + return None if row is None else int(row["rights"]) + + +def _rights_tuple(rights: int, kind: str) -> tuple[int, int, int]: + b0, b1, b2 = _BITS[kind] + return ((rights >> b0) & 1, (rights >> b1) & 1, (rights >> b2) & 1) + + +async def permissions(user_id: int, kind: int) -> tuple[int, int, int] | None: + """ + kind: 0=item_r, 1=item_w, 2=user_r, 3=user_w + Возвращает 3 флага (self, group, all) как в исходнике. + """ + rights = await _get_user_rights(int(user_id)) + if rights is None: + return None + if kind == 0: + return _rights_tuple(rights, "item_r") + if kind == 1: + return _rights_tuple(rights, "item_w") + if kind == 2: + return _rights_tuple(rights, "user_r") + if kind == 3: + return _rights_tuple(rights, "user_w") + raise ValueError("Unknown kind") + + +async def permissions_item_r(u_id: int): + return await permissions(u_id, 0) + + +async def permissions_item_w(u_id: int): + return await permissions(u_id, 1) + + +async def permissions_user_r(u_id: int): + return await permissions(u_id, 2) + + +async def permissions_user_w(u_id: int): + return await permissions(u_id, 3) + + +async def _owned_group_ids(owner_user_id: int) -> list[int]: + rows = await _fetchall( + "SELECT group_id FROM rs_ownership_over_group WHERE user_id = ?", + (int(owner_user_id),), + ) + return [int(r["group_id"]) for r in rows] + + +async def _owned_user_ids(owner_user_id: int) -> list[int]: + gids = await _owned_group_ids(owner_user_id) + if not gids: + return [] + placeholders = ",".join(["?"] * len(gids)) + rows = await _fetchall( + f"SELECT user_id FROM rs_user WHERE group_id IN ({placeholders})", + tuple(gids), + ) + return [int(r["user_id"]) for r in rows] + + +# ------------------------- +# Token / sessions (Redis) — оставлено как было +# ------------------------- + +def update_short_token(user_id, live_token): + TTL = r.pttl(f"recording_spark:{user_id}:{live_token}:PC-INFO") / 1000 + if TTL > 31 * 60: + rm_short_token(user_id, live_token) + list_A = r.lrange(f"recording_spark:{user_id}:{live_token}:PC-INFO", 0, -1) + for key in r.scan_iter(f"recording_spark:{user_id}:{live_token}:*"): + r.delete(key) + short_token, live_token = add_live_token( + user_id, list_A[0], list_A[1], list_A[2], int(TTL) + ) + return short_token, live_token + return None, None + + +def rm_live_token(user_id, live_token): + short_token = r.get(f"recording_spark:{user_id}:{live_token}:short_token") + r.delete(f"recording_spark:short_token:{short_token}") + A = False + for key in r.scan_iter(f"recording_spark:{user_id}:{live_token}:*"): + A = True + r.delete(key) + return A + + +def rm_short_token(user_id, live_token): + short_token = r.get(f"recording_spark:{user_id}:{live_token}:short_token") + r.delete(f"recording_spark:short_token:{short_token}") + r.delete(f"recording_spark:{user_id}:{live_token}:short_token") + + +def add_short_token(user_id, live_token): + short_token = secrets.token_urlsafe(32) + r.set(f"recording_spark:short_token:{short_token}", user_id, 30 * 60) + r.set(f"recording_spark:{user_id}:{live_token}:short_token", short_token, 30 * 60) + return short_token + + +def add_live_token(user_id, ps_info_name, ps_info_ip, ps_info_date, TTL): + live_token = secrets.token_urlsafe(128) + r.rpush( + f"recording_spark:{str(user_id)}:{live_token}:PC-INFO", + ps_info_name, + ps_info_ip, + str(ps_info_date), + ) + r.expire(f"recording_spark:{user_id}:{live_token}:PC-INFO", timedelta(seconds=TTL)) + short_token = add_short_token(user_id, live_token) + return short_token, live_token + + +def check_user_live_token(user_id, live_token): + user_id = r.get(f"recording_spark:{user_id}:{live_token}:PC-INFO") + if user_id is None: + return None + short_token = update_short_token(user_id, live_token) + return short_token + + +def check_user_short_token(short_token): + user_id = r.get(f"recording_spark:short_token:{short_token}") + return None if user_id is None else user_id + + +def ls_sessions(user_id): + A = 0 + Q = [] + for key in r.scan_iter(f"recording_spark:{user_id}:*:PC-INFO"): + short_token = r.lrange(key, 0, -1) + short_token.append(A) + Q.append(short_token) + A = A + 1 + return Q + + +def rm_live_token_position(user_id, position): + A = 0 + for key in r.scan_iter(f"recording_spark:{user_id}:*"): + if position == A: + short_token = r.get(f"recording_spark:{user_id}:{key}:short_token") + r.delete(f"recording_spark:short_token:{short_token}") + for key in r.scan_iter(f"recording_spark:{user_id}:{key}:*"): + r.delete(key) + A = A + 1 + + +def full_sessions_kill(user_id): + for key in r.scan_iter(f"recording_spark:{user_id}:*:short_token"): + short_token = r.get(key) + r.delete(f"recording_spark:short_token:{short_token}") + r.delete(key) + for key in r.scan_iter(f"recording_spark:{user_id}:*"): + r.delete(key) + + +def add_user_registration(email, password): + G = int(strftime("%Y")) + M = int(strftime("%m")) + D = int(strftime("%d")) + ch = int(strftime("%H")) + m = int(strftime("%M")) + s = int(strftime("%S")) + id = f"{D}{ch}{m}{s}{random.randint(0, 100000)}" + + date = f"{G}-{M}-{D}" + r.set(f"recording_spark:registration:{id}:email", email, 7 * 24 * 60 * 60) + r.set(f"recording_spark:registration:{id}:password", password, 7 * 24 * 60 * 60) + r.set(f"recording_spark:registration:{id}:date", date, 7 * 24 * 60 * 60) + return id + + +# ------------------------- +# DB functions (async) — аналог прежних, но на SQLite +# ------------------------- + +async def user_name(user_id: int): + await ping_db() + row = await _fetchone("SELECT user_name FROM rs_user WHERE user_id = ?", (int(user_id),)) + return None if row is None else row["user_name"] + + +async def avatar_png(user_id: int): + await ping_db() + row = await _fetchone("SELECT avatar FROM rs_user WHERE user_id = ?", (int(user_id),)) + return None if row is None else row["avatar"] + + +async def check_user(email: str, password: str): + await ping_db() + row = await _fetchone( + "SELECT user_id, user_name, active FROM rs_user WHERE email = ? AND password = ?", + (email, password), + ) + if row is None: + return None, None, None + return int(row["user_id"]), row["user_name"], int(row["active"]) + + +async def ls_user(user_id: int): + await ping_db() + A = await permissions_user_r(user_id) + if A is None: + return None + + q = """ + SELECT u.user_id, u.user_name, u.email, u.avatar, u.active, g.group_id, g.name, g.rights + FROM rs_user u + JOIN rs_group g ON u.group_id = g.group_id + """ + params: list = [] + if A[2] == 1: + rows = await _fetchall(q, ()) + return [list(r) for r in rows] + + conds = [] + if A[0] == 1: + conds.append("u.user_id = ?") + params.append(int(user_id)) + if A[1] == 1: + conds.append( + "u.user_id IN (SELECT user_id FROM rs_user WHERE group_id IN (SELECT group_id FROM rs_ownership_over_group WHERE user_id = ?))" + ) + params.append(int(user_id)) + + if not conds: + return [] + + q += " WHERE " + " OR ".join(conds) + rows = await _fetchall(q, tuple(params)) + return [list(r) for r in rows] + + +async def ls_item(user_id: int): + await ping_db() + A = await permissions_item_r(user_id) + if A is None: + return None + + q = """ + SELECT i.item_id, i.item_name, i.status, + i.user_id_1, u1.user_name, + i.user_id_2, u2.user_name, + i.icon, i.comments, i.inventory_id + FROM rs_item i + JOIN rs_user u1 ON i.user_id_1 = u1.user_id + JOIN rs_user u2 ON i.user_id_2 = u2.user_id + """ + params: list = [] + if A[2] == 1: + rows = await _fetchall(q, ()) + return [list(r) for r in rows] + + conds = [] + if A[0] == 1: + conds.append("(i.user_id_1 = ? OR i.user_id_2 = ?)") + params.extend([int(user_id), int(user_id)]) + if A[1] == 1: + conds.append( + "i.user_id_2 IN (SELECT user_id FROM rs_user WHERE group_id IN (SELECT group_id FROM rs_ownership_over_group WHERE user_id = ?))" + ) + params.append(int(user_id)) + + if not conds: + return [] + + q += " WHERE " + " OR ".join(conds) + rows = await _fetchall(q, tuple(params)) + return [list(r) for r in rows] + + +async def user_add(user_id: int, user_name: str, email: str, password: str, avatar: int, active: int, group_id: int): + await ping_db() + A = await permissions_user_w(user_id) + if A is None: + return None + + if A[2] != 1: + if A[1] == 1: + owned = await _owned_group_ids(user_id) + if int(group_id) not in owned: + return None + else: + return None + + cur = await _execute( + """ + INSERT INTO rs_user (user_name, email, password, avatar, active, group_id) + VALUES (?, ?, ?, ?, ?, ?) + """, + (user_name, email, password, int(avatar), int(active), int(group_id)), + commit=True, + ) + return int(cur.lastrowid) + + +async def _can_read_user(viewer_id: int, target_user_id: int) -> bool: + A = await permissions_user_r(viewer_id) + if A is None: + return False + if A[2] == 1: + return True + if A[0] == 1 and int(viewer_id) == int(target_user_id): + return True + if A[1] == 1: + owned_users = await _owned_user_ids(viewer_id) + return int(target_user_id) in owned_users + return False + + +async def _can_write_user(actor_id: int, target_user_id: int) -> bool: + A = await permissions_user_w(actor_id) + if A is None: + return False + if A[2] == 1: + return True + if A[0] == 1 and int(actor_id) == int(target_user_id): + return True + if A[1] == 1: + owned_users = await _owned_user_ids(actor_id) + return int(target_user_id) in owned_users + return False + + +async def user_edit(user_id: int, user_id_rec: int, user_name, email, password, avatar, active, group_id): + await ping_db() + if not await _can_write_user(user_id, user_id_rec): + return 0 + + if group_id is not None: + A = await permissions_user_w(user_id) + if A is None: + return 0 + if A[2] != 1: + owned = await _owned_group_ids(user_id) + if int(group_id) not in owned: + return 0 + + cur = await _execute( + """ + UPDATE rs_user + SET + user_name = COALESCE(?, user_name), + email = COALESCE(?, email), + password = COALESCE(?, password), + avatar = COALESCE(?, avatar), + active = COALESCE(?, active), + group_id = COALESCE(?, group_id) + WHERE user_id = ? + """, + (user_name, email, password, avatar, active, group_id, int(user_id_rec)), + commit=True, + ) + changed = int(cur.rowcount) + + if changed == 1 and (active == 0 or password is not None): + full_sessions_kill(user_id_rec) + + return changed + + +async def user_info(user_id: int, user_id_rec: int): + await ping_db() + if not await _can_read_user(user_id, user_id_rec): + return None + + row = await _fetchone( + """ + SELECT u.user_name, u.email, u.avatar, u.active, g.group_id, g.rights + FROM rs_user u + JOIN rs_group g ON u.group_id = g.group_id + WHERE u.user_id = ? + """, + (int(user_id_rec),), + ) + return None if row is None else tuple(row) + + +async def ls_group(user_id: int): + await ping_db() + A = await permissions_user_r(user_id) + if A is None: + return None + if A[2] == 1: + rows = await _fetchall("SELECT group_id, name, rights FROM rs_group", ()) + return [tuple(r) for r in rows] + rows = await _fetchall( + """ + SELECT group_id, name, rights + FROM rs_group + WHERE group_id IN (SELECT group_id FROM rs_ownership_over_group WHERE user_id = ?) + """, + (int(user_id),), + ) + return [tuple(r) for r in rows] + + +async def _can_read_item(viewer_id: int, item_row: aiosqlite.Row) -> bool: + A = await permissions_item_r(viewer_id) + if A is None: + return False + if A[2] == 1: + return True + if A[0] == 1 and (int(item_row["user_id_1"]) == int(viewer_id) or int(item_row["user_id_2"]) == int(viewer_id)): + return True + if A[1] == 1: + owned_users = await _owned_user_ids(viewer_id) + return int(item_row["user_id_2"]) in owned_users + return False + + +async def _can_write_item(actor_id: int, item_row: aiosqlite.Row) -> bool: + A = await permissions_item_w(actor_id) + if A is None: + return False + if A[2] == 1: + return True + if A[0] == 1 and int(item_row["user_id_1"]) == int(actor_id): + return True + if A[1] == 1: + owned_users = await _owned_user_ids(actor_id) + return int(item_row["user_id_1"]) in owned_users + return False + + +async def item_info(user_id: int, item_id: int): + await ping_db() + row = await _fetchone("SELECT * FROM rs_item WHERE item_id = ?", (int(item_id),)) + if row is None: + return None + if not await _can_read_item(user_id, row): + return None + return tuple(row) + + +async def item_w_test(user_id: int, item_id: int): + await ping_db() + row = await _fetchone("SELECT * FROM rs_item WHERE item_id = ?", (int(item_id),)) + if row is None: + return 0 + return 1 if await _can_write_item(user_id, row) else 0 + + +async def item_edit(user_id: int, item_id: int, user_id_1, user_id_2, name, status, icon, comments, inventory_id): + await ping_db() + row = await _fetchone("SELECT * FROM rs_item WHERE item_id = ?", (int(item_id),)) + if row is None: + return 0 + if not await _can_write_item(user_id, row): + return 0 + + if user_id_1 is not None and int(user_id_1) != -1: + if not await _can_read_user(user_id, int(user_id_1)) and int(user_id) != int(user_id_1): + return 0 + if user_id_2 is not None and int(user_id_2) != -1: + if not await _can_read_user(user_id, int(user_id_2)) and int(user_id) != int(user_id_2): + return 0 + + icon_val = icon + if icon is not None and str(icon) == "-1": + icon_val = None + + inv_val = inventory_id + if inventory_id is not None and int(inventory_id) == -1: + inv_val = None + + cur = await _execute( + """ + UPDATE rs_item + SET + item_name = COALESCE(?, item_name), + status = COALESCE(?, status), + user_id_1 = COALESCE(?, user_id_1), + user_id_2 = COALESCE(?, user_id_2), + icon = COALESCE(?, icon), + comments = COALESCE(?, comments), + inventory_id = COALESCE(?, inventory_id) + WHERE item_id = ? + """, + (name, status, user_id_1, user_id_2, icon_val, comments, inv_val, int(item_id)), + commit=True, + ) + return int(cur.rowcount) + + +async def item_rm(user_id: int, item_id: int): + await ping_db() + row = await _fetchone("SELECT * FROM rs_item WHERE item_id = ?", (int(item_id),)) + if row is None: + return 0 + if not await _can_write_item(user_id, row): + return 0 + cur = await _execute("DELETE FROM rs_item WHERE item_id = ?", (int(item_id),), commit=True) + return int(cur.rowcount) + + +async def add_item(user_id: int, user_id_1: int, user_id_2: int, name: str, status, icon, comments, inventory_id): + await ping_db() + A = await permissions_item_w(user_id) + if A is None: + return 0 + + if A[2] != 1: + ok = False + if A[0] == 1 and (int(user_id_1) == int(user_id) or int(user_id_2) == int(user_id)): + ok = True + if not ok and A[1] == 1: + owned_users = await _owned_user_ids(user_id) + if int(user_id_1) == int(user_id) or int(user_id_2) == int(user_id): + ok = True + if int(user_id_1) in owned_users or int(user_id_2) in owned_users: + ok = True + if not ok: + return 0 + + cur = await _execute( + """ + INSERT INTO rs_item (item_name, status, user_id_1, user_id_2, icon, comments, inventory_id) + VALUES (?, ?, ?, ?, ?, ?, ?) + """, + (name, status, int(user_id_1), int(user_id_2), icon, comments, inventory_id), + commit=True, + ) + return int(cur.lastrowid) + + +async def user_rm(user_id: int, user_id_rm: int): + await ping_db() + if int(user_id) == int(user_id_rm): + return 0 + if not await _can_write_user(user_id, user_id_rm): + return 0 + + db = await _connect_db() + try: + await db.execute("BEGIN;") + await db.execute( + "DELETE FROM rs_ownership_over_group WHERE user_id = ?", + (int(user_id_rm),), + ) + cur2 = await db.execute( + "DELETE FROM rs_user WHERE user_id = ?", + (int(user_id_rm),), + ) + await db.commit() + return int(cur2.rowcount) + except Exception: + await db.rollback() + raise + + +# Константы (оставлены, если где-то используются извне) +admin = 0 +meneger = 34815 +sender = 41935 +user = 409665 diff --git a/data.sqlite3 b/data.sqlite3 new file mode 100644 index 0000000..9f93df6 Binary files /dev/null and b/data.sqlite3 differ diff --git a/db_module.py b/db_module.py new file mode 100644 index 0000000..ada4b59 --- /dev/null +++ b/db_module.py @@ -0,0 +1,32 @@ +import sqlite3 +import os +put = os.path.dirname(os.path.realpath(__file__)) + "/" #Путь- (part-1) +DB_PATH = put + "data.sqlite3" +bd = sqlite3.connect(DB_PATH) +sql = bd.cursor() + +sql.execute("""CREATE TABLE IF NOT EXISTS rs_user ( + user_id INTEGER PRIMARY KEY AUTOINCREMENT, + user_name TEXT NOT NULL, + email TEXT, + password TEXT, + avatar INTEGER NOT NULL DEFAULT 0, + active INTEGER NOT NULL DEFAULT 0 + );""") +sql.close() + +def check_user(email: str, password: str): + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + try: + row = conn.execute( + "SELECT user_id, user_name, active FROM rs_user WHERE email = ? AND password = ?", + (email, password), + ).fetchone() + + if row is None: + return None, None, None + return int(row["user_id"]), row["user_name"], int(row["active"]) + finally: + conn.close() + diff --git a/frontend/app.html b/frontend/app.html new file mode 100755 index 0000000..84a50e1 --- /dev/null +++ b/frontend/app.html @@ -0,0 +1,60 @@ + + + + + + Панель + + + +
+
+
WireGuard
+ +
+
+
Пользователь
+
+
+ +
+
+
+ +
+
+
+
+

Конфиги

+
+
+ +
+ + +
+
+ +
+ + + + + + + + + + + + +
IDИмяСтатусIP локальныйIP серверСкачать
+
+ + +
+
+ + + + diff --git a/frontend/assets/styles.css b/frontend/assets/styles.css new file mode 100755 index 0000000..91a6094 --- /dev/null +++ b/frontend/assets/styles.css @@ -0,0 +1,207 @@ +:root{ + --bg:#0b1020; + --card:#111a33; + --card2:#0f1730; + --text:#e7ecff; + --muted:#aab4da; + --line:#243055; + --shadow: 0 16px 40px rgba(0,0,0,.35); + --radius: 18px; +} + +*{box-sizing:border-box} +html,body{height:100%} +body{ + margin:0; + font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial; + background: radial-gradient(1200px 800px at 20% 20%, #18224a 0%, var(--bg) 55%, #070b17 100%); + color:var(--text); +} + +a{color:inherit} +.link{color:var(--muted); text-decoration:none} +.link:hover{color:var(--text); text-decoration:underline} + +.center{ + min-height:100%; + display:grid; + place-items:center; + padding:24px; +} + +.container{max-width:1100px; margin:0 auto; padding:24px} + +.card{ + width:min(920px, 100%); + background: linear-gradient(180deg, var(--card), var(--card2)); + border: 1px solid rgba(255,255,255,.06); + border-radius: var(--radius); + box-shadow: var(--shadow); + padding: 22px; +} + +.hero{width:min(720px, 100%); padding:32px} +.auth{width:min(520px, 100%)} + +.topline{ + display:flex; + justify-content:space-between; + align-items:center; + margin-bottom:10px; +} + +.title{margin:0 0 10px 0; font-size:32px; letter-spacing:.2px} +.muted{color:var(--muted)} + +.actions{ + display:grid; + grid-template-columns:1fr 1fr; + gap:14px; + margin-top:18px; +} +@media (max-width:640px){ + .actions{grid-template-columns:1fr} +} + +.form{display:grid; gap:12px; margin-top:10px} +.field{display:grid; gap:6px} +.label{color:var(--muted); font-size:14px} +input{ + width:100%; + padding:12px 12px; + border-radius:12px; + border:1px solid rgba(255,255,255,.10); + background: rgba(0,0,0,.18); + color:var(--text); + outline:none; +} +input:focus{border-color: rgba(255,255,255,.25)} + +.check{display:flex; gap:10px; align-items:center; color:var(--muted); font-size:14px} +.msg{min-height:22px; color:var(--muted)} + +.btn{ + appearance:none; + border:1px solid rgba(255,255,255,.12); + background: rgba(255,255,255,.06); + color:var(--text); + border-radius: 14px; + padding: 12px 14px; + cursor:pointer; + text-decoration:none; + display:inline-flex; + align-items:center; + justify-content:center; + gap:10px; + user-select:none; +} +.btn:hover{border-color: rgba(255,255,255,.22)} +.btn:active{transform: translateY(1px)} +.btn-primary{ + background: linear-gradient(180deg, rgba(120,140,255,.28), rgba(90,110,255,.16)); + border-color: rgba(120,140,255,.40); +} +.btn-ghost{background: rgba(255,255,255,.04)} +.btn-xl{padding:18px 16px; font-size:18px; border-radius:16px} +.btn-lg{padding:14px 14px; font-size:16px} +.btn-sm{padding:10px 12px; font-size:14px; border-radius:12px} + +.header{ + position:sticky; + top:0; + z-index:5; + backdrop-filter: blur(10px); + background: rgba(6,10,22,.55); + border-bottom:1px solid rgba(255,255,255,.06); +} +.header-inner{ + max-width:1100px; + margin:0 auto; + padding:14px 24px; + display:flex; + align-items:center; + justify-content:space-between; + gap:16px; +} +.brand{font-weight:700; letter-spacing:.3px} +.header-right{display:flex; align-items:center; gap:14px} +.userbox{display:grid; gap:2px} +.userlabel{font-size:12px; color:var(--muted)} +.username{font-weight:600} + +.table-head{ + display:flex; + align-items:flex-end; + justify-content:space-between; + gap:12px; + margin-bottom:12px; +} +.table-controls{display:flex; align-items:center; gap:10px} +.input{min-width:240px} +@media (max-width:760px){ + .table-head{flex-direction:column; align-items:stretch} + .table-controls{justify-content:space-between} + .input{min-width:0; flex:1} +} + +.table-wrap{ + overflow:auto; + border:1px solid rgba(255,255,255,.06); + border-radius:14px; +} +.table{ + width:100%; + border-collapse:collapse; + min-width:980px; +} +th,td{ + padding:12px 12px; + border-bottom:1px solid rgba(255,255,255,.06); + vertical-align:top; + font-size:14px; +} +th{ + position:sticky; + top:0; + background: rgba(12,18,38,.9); + text-align:left; + color:var(--muted); + font-weight:600; +} +tr:hover td{background: rgba(255,255,255,.03)} +.badge{ + display:inline-flex; + padding:6px 10px; + border-radius:999px; + border:1px solid rgba(255,255,255,.12); + color:var(--muted); + background: rgba(255,255,255,.04); +} + +.pre{ + margin:10px 0 0; + padding:12px; + border-radius:12px; + background: rgba(0,0,0,.18); + border:1px solid rgba(255,255,255,.08); + overflow:auto; +} + +.callout{ + margin-top:14px; + padding:14px; + border-radius:14px; + border:1px solid rgba(255,255,255,.10); + background: rgba(255,255,255,.04); +} +.callout-title{font-weight:600; color:var(--text)} + +.errorbox{ + margin-top:12px; + padding:12px; + border-radius:14px; + border:1px solid rgba(255,80,80,.35); + background: rgba(255,80,80,.08); + color: var(--text); +} +.hidden{display:none} diff --git a/frontend/index.html b/frontend/index.html new file mode 100755 index 0000000..0c21921 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,24 @@ + + + + + + Главная + + + +
+
+

Я крутой

+

Панель WireGuard

+ + +
+
+ + + + diff --git a/frontend/js/api.js b/frontend/js/api.js new file mode 100755 index 0000000..e017ac2 --- /dev/null +++ b/frontend/js/api.js @@ -0,0 +1,97 @@ +import { CONFIG } from "./config.js"; +import { loadSession, saveSession, clearSession } from "./storage.js"; + +function urlJoin(base, path) { + if (!base) return path; + return base.replace(/\/+$/, "") + "/" + path.replace(/^\/+/, ""); +} + +async function parseResponse(res) { + const ct = (res.headers.get("content-type") || "").toLowerCase(); + if (ct.includes("application/json")) return await res.json(); + const text = await res.text(); + // иногда сервер шлёт json как text/plain + try { return JSON.parse(text); } catch { return text; } +} + +export async function renewTokens() { + const s = loadSession(); + if (!s) throw new Error("NO_SESSION"); + + // /renew/ ждёт user_id и live_token в headers :contentReference[oaicite:1]{index=1} + const res = await fetch(urlJoin(CONFIG.API_BASE_URL, CONFIG.ENDPOINTS.renew), { + method: "GET", + headers: { + "user_id": String(s.user_id), + "live_token": String(s.live_token) + } + }); + + if (res.status === CONFIG.AUTH_ERROR_STATUS) { + clearSession(); + throw new Error("RENEW_DENIED"); + } + + if (!res.ok) { + const body = await parseResponse(res); + throw new Error(typeof body === "string" ? body : "RENEW_FAILED"); + } + + const data = await parseResponse(res); + if (!data?.short_token || !data?.live_token) { + throw new Error("BAD_RENEW_RESPONSE"); + } + + const next = { ...s, short_token: data.short_token, live_token: data.live_token }; + saveSession(next); + return next; +} + +export async function apiFetch(path, { + method = "GET", + headers = {}, + body = null, + auth = true +} = {}) { + const s = loadSession(); + + const reqHeaders = { ...headers }; + if (auth) { + if (!s?.short_token) throw new Error("NO_AUTH"); + // Все защищённые ручки проверяют short_token в headers :contentReference[oaicite:2]{index=2} + reqHeaders["short_token"] = String(s.short_token); + } + + const doRequest = async () => { + const res = await fetch(urlJoin(CONFIG.API_BASE_URL, path), { + method, + headers: { + ...reqHeaders, + ...(body ? { "content-type": "application/json" } : {}) + }, + body: body ? JSON.stringify(body) : null + }); + return res; + }; + + let res = await doRequest(); + + // Авто-обновление токенов при 426 :contentReference[oaicite:3]{index=3} + if (auth && res.status === CONFIG.AUTH_ERROR_STATUS) { + await renewTokens(); + // обновим заголовок short_token и повторим один раз + const s2 = loadSession(); + reqHeaders["short_token"] = String(s2.short_token); + res = await doRequest(); + } + + const payload = await parseResponse(res); + if (!res.ok) { + const err = new Error("API_ERROR"); + err.status = res.status; + err.payload = payload; + throw err; + } + + return payload; +} diff --git a/frontend/js/app.js b/frontend/js/app.js new file mode 100755 index 0000000..b5040a9 --- /dev/null +++ b/frontend/js/app.js @@ -0,0 +1,207 @@ +import { requireAuthOrRedirect, logout } from "./auth.js"; +import { apiFetch } from "./api.js"; +import { CONFIG } from "./config.js"; +import { loadSession, saveSession, clearSession } from "./storage.js"; + +if (!requireAuthOrRedirect()) { + // редирект уже сделан +} + +const userNameEl = document.getElementById("userName"); +const logoutBtn = document.getElementById("logoutBtn"); +const reloadBtn = document.getElementById("reloadBtn"); +const statusEl = document.getElementById("status"); +const tbody = document.getElementById("tbody"); +const errorBox = document.getElementById("errorBox"); +const searchEl = document.getElementById("search"); + +let rows = []; + +function showError(text) { + errorBox.textContent = text; + errorBox.classList.remove("hidden"); +} + +function clearError() { + errorBox.textContent = ""; + errorBox.classList.add("hidden"); +} + +function setStatus(text) { + statusEl.textContent = text; +} + +function normalizeCell(v) { + if (v === null || v === undefined) return ""; + return String(v); +} + +function isWireguardLike(text) { + const t = String(text || ""); + return t.includes("[Interface]") || t.includes("[Peer]") || t.includes("PrivateKey") || t.includes("AllowedIPs"); +} + +async function copyToClipboard(text) { + try { + await navigator.clipboard.writeText(text); + } catch { + // fallback + const ta = document.createElement("textarea"); + ta.value = text; + document.body.appendChild(ta); + ta.select(); + document.execCommand("copy"); + ta.remove(); + } +} + +function renderTable(filter = "") { + const q = filter.trim().toLowerCase(); + const filtered = !q ? rows : rows.filter(r => r.searchBlob.includes(q)); + + tbody.innerHTML = ""; + + for (const r of filtered) { + const tr = document.createElement("tr"); + + tr.innerHTML = ` + ${escapeHtml(r.id)} + ${escapeHtml(r.name)} + ${escapeHtml(r.status)} + ${escapeHtml(r.ipLocal)} + ${escapeHtml(r.ipServer)} + + `; + + const tdBtn = tr.lastElementChild; + const btn = document.createElement("button"); + btn.className = "btn btn-ghost btn-sm"; + btn.textContent = "Скачать"; + btn.addEventListener("click", async () => { + clearError(); + try { + await triggerDownloadById(r.id); + } catch (err) { + if (err?.status === CONFIG.AUTH_ERROR_STATUS) { + await logout(); + location.replace("./login.html"); + return; + } + showError("Не удалось скачать конфиг."); + setStatus(""); + } + }); + tdBtn.appendChild(btn); + + tbody.appendChild(tr); + } +} + +function escapeHtml(s) { + return String(s) + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); +} + +async function loadData() { + clearError(); + setStatus("Загрузка…"); + + try { + const data = await apiFetch(CONFIG.ENDPOINTS.list, { method: "GET", auth: true }); + const list = Array.isArray(data?.ls) ? data.ls : Array.isArray(data) ? data : []; + + rows = list + .map(mapLsItem) + .filter(Boolean) + .map(r => ({ + ...r, + searchBlob: [r.id, r.name, r.status, r.ipLocal, r.ipServer].join(" ").toLowerCase() + })); + + renderTable(searchEl.value); + setStatus(`Готово. Строк: ${rows.length}`); + } catch (err) { + if (err?.status === CONFIG.AUTH_ERROR_STATUS) { + await logout(); + location.replace("./login.html"); + return; + } + showError("Ошибка загрузки данных."); + setStatus(""); + } +} + +(function init() { + const s = loadSession(); + userNameEl.textContent = s?.user_name || `#${s?.user_id || "—"}`; + + logoutBtn.addEventListener("click", async () => { + await logout(); + location.replace("./index.html"); + }); + + reloadBtn.addEventListener("click", loadData); + + searchEl.addEventListener("input", () => renderTable(searchEl.value)); + + loadData(); +})(); + + +function filenameFromDisposition(cd) { + if (!cd) return null; + + // filename*=UTF-8''... + let m = cd.match(/filename\*\s*=\s*UTF-8''([^;]+)/i); + if (m?.[1]) return decodeURIComponent(m[1].replace(/"/g, "").trim()); + + // filename="..." + m = cd.match(/filename\s*=\s*"?([^";]+)"?/i); + if (m?.[1]) return m[1].trim(); + + return null; +} + +export async function apiFetchBlob(path, { method = "GET", headers = {}, auth = true } = {}) { + const s = loadSession(); + const reqHeaders = { ...headers }; + + if (auth) { + if (!s?.short_token) throw new Error("NO_AUTH"); + reqHeaders["short_token"] = String(s.short_token); + reqHeaders["Authorization"] = `Bearer ${t}`; + } + + const doRequest = async () => { + return await fetch(path.startsWith("http") ? path : (CONFIG.API_BASE_URL ? CONFIG.API_BASE_URL.replace(/\/+$/, "") + path : path), { + method, + headers: reqHeaders + }); + }; + + let res = await doRequest(); + + if (auth && res.status === CONFIG.AUTH_ERROR_STATUS) { + // renew + retry один раз + await (await import("./api.js")).renewTokens?.(); // на случай циклического импорта (если будет) — безопасно + const s2 = loadSession(); + reqHeaders["short_token"] = String(s2.short_token); + res = await doRequest(); + } + + if (!res.ok) { + const text = await res.text().catch(() => ""); + const err = new Error("API_BLOB_ERROR"); + err.status = res.status; + err.payload = text; + throw err; + } + + const blob = await res.blob(); + const filename = filenameFromDisposition(res.headers.get("content-disposition")); + return { blob, filename }; +} diff --git a/frontend/js/auth.js b/frontend/js/auth.js new file mode 100755 index 0000000..cd2a423 --- /dev/null +++ b/frontend/js/auth.js @@ -0,0 +1,65 @@ +import { CONFIG } from "./config.js"; +import { saveSession, loadSession, clearSession } from "./storage.js"; +import { apiFetch } from "./api.js"; + +function deviceLabel() { + const ua = navigator.userAgent || "browser"; + return ua.slice(0, 120); +} + +export function isLoggedIn() { + return !!loadSession(); +} + +export function requireAuthOrRedirect() { + if (!isLoggedIn()) { + location.replace("./login.html"); + return false; + } + return true; +} + + +export async function login(email, password, remember) { + const headers = { + email: String(email), + password: String(password), + remember: remember ? "1" : "0" + }; + + const data = await apiFetch(CONFIG.ENDPOINTS.login, { method: "GET", headers, auth: false }); + + if (!data?.short_token || !data?.live_token || !data?.user_id) { + throw new Error("BAD_LOGIN_RESPONSE"); + } + + saveSession({ + short_token: data.short_token, + live_token: data.live_token, + user_id: data.user_id, + user_name: data.user_name || "" + }); + + return data; +} + +export async function logout() { + const s = loadSession(); + try { + if (s?.user_id && s?.live_token) { + // /kill_session/ удаляет live_token :contentReference[oaicite:5]{index=5} + await apiFetch(CONFIG.ENDPOINTS.logout, { + method: "GET", + headers: { + user_id: String(s.user_id), + live_token: String(s.live_token) + }, + auth: false + }); + } + } catch { + // игнор: локально всё равно очищаем + } finally { + clearSession(); + } +} diff --git a/frontend/js/config.js b/frontend/js/config.js new file mode 100755 index 0000000..acefe86 --- /dev/null +++ b/frontend/js/config.js @@ -0,0 +1,16 @@ +export const CONFIG = { + // Если фронт и API на одном домене — оставь пусто: "" + // Если API отдельно — например: "https://api.example.com" + API_BASE_URL: "", + + ENDPOINTS: { + login: "/api/login/", + renew: "/api/renew/", + list: "/api/ls/", + logout:"/api/kill_session/", + download_conf_prefix: "/api/download_conf/" + }, + + // В твоём сервере токен-ошибка возвращается как 426 :contentReference[oaicite:0]{index=0} + AUTH_ERROR_STATUS: 426 +}; diff --git a/frontend/js/login.js b/frontend/js/login.js new file mode 100755 index 0000000..893dc2f --- /dev/null +++ b/frontend/js/login.js @@ -0,0 +1,32 @@ +import { login, isLoggedIn } from "./auth.js"; + +if (isLoggedIn()) { + location.replace("./app.html"); +} + +const form = document.getElementById("loginForm"); +const msg = document.getElementById("msg"); + +function setMsg(text) { + msg.textContent = text || ""; +} + +form.addEventListener("submit", async (e) => { + e.preventDefault(); + setMsg("Вход…"); + + const email = document.getElementById("email").value.trim(); + const password = document.getElementById("password").value; + const remember = document.getElementById("remember").checked; + + try { + await login(email, password, remember); + location.replace("./app.html"); + } catch (err) { + // Сервер: 412 (не найден логин), 423 (отключён), 426 (токен) :contentReference[oaicite:6]{index=6} + const status = err?.status; + if (status === 412) return setMsg("Логин/пароль неверны."); + if (status === 423) return setMsg("Учётная запись отключена."); + setMsg("Ошибка входа."); + } +}); diff --git a/frontend/js/storage.js b/frontend/js/storage.js new file mode 100755 index 0000000..abad957 --- /dev/null +++ b/frontend/js/storage.js @@ -0,0 +1,21 @@ +const KEY = "wg_front_session_v1"; + +export function loadSession() { + try { + const raw = localStorage.getItem(KEY); + if (!raw) return null; + const s = JSON.parse(raw); + if (!s?.short_token || !s?.live_token || !s?.user_id) return null; + return s; + } catch { + return null; + } +} + +export function saveSession(session) { + localStorage.setItem(KEY, JSON.stringify(session)); +} + +export function clearSession() { + localStorage.removeItem(KEY); +} diff --git a/frontend/login.html b/frontend/login.html new file mode 100755 index 0000000..81fef27 --- /dev/null +++ b/frontend/login.html @@ -0,0 +1,43 @@ + + + + + + Вход + + + +
+
+ + +

Вход

+ +
+ + + + + + + + +
+
+
+
+ + + + diff --git a/frontend/request.html b/frontend/request.html new file mode 100755 index 0000000..93747e6 --- /dev/null +++ b/frontend/request.html @@ -0,0 +1,33 @@ + + + + + + Запрос учётной записи + + + +
+
+ + +

Запрос учётной записи

+

+ Тут обычно: кому писать/что отправить. Если у тебя есть API-ручка под регистрацию — подключишь в `js/api.js`. +

+ +
+
Шаблон заявки
+
Нужен доступ к WireGuard.
+ФИО:
+Контакт:
+Причина:
+Устройство/ОС:
+
+
+
+ + diff --git a/server.log b/server.log new file mode 100644 index 0000000..55cb811 --- /dev/null +++ b/server.log @@ -0,0 +1,1153 @@ +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +ERROR:Start:Exception on /login/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 65, in login + ps_info_name = request.headers['ps_info_name'] + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_PS_INFO_NAME' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 03:30:05] "GET /login/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +DEBUG:root:request.headers - Host: 127.0.0.1 +X-Real-Ip: 127.0.0.1 +X-Forwarded-For: 127.0.0.1 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0 +Accept: */* +Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate, br, zstd +Referer: http://127.0.0.1/login.html +Email: aaaa +Password: aaa +Remember: 1 +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Priority: u=0 +Pragma: no-cache +Cache-Control: no-cache + + +ERROR:Start:Exception on /login/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 66, in login + ps_info_name = request.headers['ps_info_name'] + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_PS_INFO_NAME' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 03:33:20] "GET /login/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +DEBUG:root:request.headers - Host: 127.0.0.1 +X-Real-Ip: 127.0.0.1 +X-Forwarded-For: 127.0.0.1 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0 +Accept: */* +Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate, br, zstd +Referer: http://127.0.0.1/login.html +Email: aaa +Password: aaa +Remember: 1 +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Priority: u=0 +Pragma: no-cache +Cache-Control: no-cache + + +ERROR:Start:Exception on /login/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 66, in login + ps_info_name = request.headers['ps_info_name'] + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_PS_INFO_NAME' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 03:36:20] "GET /login/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +DEBUG:root:request.headers - Host: 127.0.0.1 +X-Real-Ip: 127.0.0.1 +X-Forwarded-For: 127.0.0.1 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0 +Accept: */* +Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate, br, zstd +Referer: http://127.0.0.1/login.html +Email: aaa +Password: aaa +Remember: 1 +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Priority: u=0 +Pragma: no-cache +Cache-Control: no-cache + + +ERROR:Start:Exception on /login/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 67, in login + uid ,user_name, active = bd_module.check_user(email, password) + ^^^^^^^^^^^^^^^^^^^^^^ +TypeError: cannot unpack non-iterable coroutine object +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 03:38:28] "GET /login/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +DEBUG:root:request.headers - Host: 127.0.0.1 +X-Real-Ip: 127.0.0.1 +X-Forwarded-For: 127.0.0.1 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0 +Accept: */* +Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate, br, zstd +Referer: http://127.0.0.1/login.html +Email: aaa +Password: aaa +Remember: 1 +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Priority: u=0 +Pragma: no-cache +Cache-Control: no-cache + + +ERROR:Start:Exception on /login/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 70, in login + uid ,user_name, active = db_module.check_user(email, password) + ^^^^^^^^^^^^^^^^^^^^^^ +TypeError: cannot unpack non-iterable coroutine object +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 03:56:33] "GET /login/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +DEBUG:root:request.headers - Host: 127.0.0.1 +X-Real-Ip: 127.0.0.1 +X-Forwarded-For: 127.0.0.1 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0 +Accept: */* +Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate, br, zstd +Referer: http://127.0.0.1/login.html +Email: aaa +Password: aaa +Remember: 1 +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Priority: u=0 +Pragma: no-cache +Cache-Control: no-cache + + +ERROR:Start:Exception on /login/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 69, in login + uid ,user_name, active = db_module.check_user(email, password) + ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/db_module.py", line 18, in check_user + row = fetchone( + ^^^^^^^^ +NameError: name 'fetchone' is not defined +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:01:39] "GET /login/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +DEBUG:root:request.headers - Host: 127.0.0.1 +X-Real-Ip: 127.0.0.1 +X-Forwarded-For: 127.0.0.1 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0 +Accept: */* +Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate, br, zstd +Referer: http://127.0.0.1/login.html +Email: aaa +Password: aaa +Remember: 1 +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Priority: u=0 +Pragma: no-cache +Cache-Control: no-cache + + +ERROR:Start:Exception on /login/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 69, in login + uid ,user_name, active = db_module.check_user(email, password) + ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/db_module.py", line 26, in check_user + row = fetchone( + "SELECT user_id, user_name, active FROM rs_user WHERE email = ? AND password = ?", + (email, password), + ) + File "/home/romenskiy2012/Документы/web/wg_2/db_module.py", line 8, in fetchone + conn = sqlite3.connect(DB_PATH) + ^^^^^^^ +NameError: name 'DB_PATH' is not defined +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:02:46] "GET /login/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +DEBUG:root:request.headers - Host: 127.0.0.1 +X-Real-Ip: 127.0.0.1 +X-Forwarded-For: 127.0.0.1 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0 +Accept: */* +Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate, br, zstd +Referer: http://127.0.0.1/login.html +Email: aaa +Password: aaa +Remember: 1 +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Priority: u=0 +Pragma: no-cache +Cache-Control: no-cache + + +INFO:root:Авторизаии под пользователям aaa !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:08:07] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 127.0.0.1 +X-Real-Ip: 127.0.0.1 +X-Forwarded-For: 127.0.0.1 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0 +Accept: */* +Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate, br, zstd +Referer: http://127.0.0.1/login.html +Email: admin +Password: aaa +Remember: 1 +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Priority: u=0 +Pragma: no-cache +Cache-Control: no-cache + + +INFO:root:Авторизаии под пользователям admin !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:08:27] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 127.0.0.1 +X-Real-Ip: 127.0.0.1 +X-Forwarded-For: 127.0.0.1 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0 +Accept: */* +Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate, br, zstd +Referer: http://127.0.0.1/login.html +Email: admin +Password: 123 +Remember: 1 +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Priority: u=0 +Pragma: no-cache +Cache-Control: no-cache + + +INFO:root:Авторизаии под пользователям admin !УСПЕХ! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:08:36] "GET /login/ HTTP/1.0" 200 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:08:36] "GET /ls/ HTTP/1.0" 500 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email: 1 +Password: 1 +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям 1 !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:25:48] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email: 1 +Password: 1 +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям 1 !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:25:50] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email: 1 +Password: 1 +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям 1 !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:25:51] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email: 1 +Password: 1 +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям 1 !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:25:51] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email: 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +Password: 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:26:07] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email: \ +Password: 1 +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям \ !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:27:02] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email:  +Password: 1 +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям  !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:27:32] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email:  +Password:  +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям  !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:27:37] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email: admin +Password: admin +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям admin !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:28:38] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email: admin +Password: 132 +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям admin !!!ОТКАЗ!!! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:28:42] "GET /login/ HTTP/1.0" 412 - +DEBUG:root:request.headers - Host: 10.0.1.10 +X-Real-Ip: 10.1.1.200 +X-Forwarded-For: 10.1.1.200 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0 +Accept: */* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate +Referer: http://10.0.1.10/login.html +Email: admin +Password: 123 +Remember: 0 +Dnt: 1 +Priority: u=0 + + +INFO:root:Авторизаии под пользователям admin !УСПЕХ! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:28:50] "GET /login/ HTTP/1.0" 200 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:28:51] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:29:40] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:29:42] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:29:44] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:29:48] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:29:53] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:30:09] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:30:38] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:30:40] "GET /ls/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:37:56] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:37:58] "GET /ls/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 94, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:40:05] "GET /ls/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 95, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:41:22] "GET /ls/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 95, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:47:45] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 95, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:47:56] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 95, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:48:02] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /kill_session/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 257, in kill_session + user_id = request.headers['user_id'] + ~~~~~~~~~~~~~~~^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_USER_ID' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:48:15] "GET /kill_session/ HTTP/1.0" 500 - +DEBUG:root:request.headers - Host: 127.0.0.1 +X-Real-Ip: 127.0.0.1 +X-Forwarded-For: 127.0.0.1 +Connection: close +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0 +Accept: */* +Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 +Accept-Encoding: gzip, deflate, br, zstd +Referer: http://127.0.0.1/login.html +Email: admin +Password: 123 +Remember: 1 +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +Priority: u=0 +Pragma: no-cache +Cache-Control: no-cache + + +INFO:root:Авторизаии под пользователям admin !УСПЕХ! +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:48:30] "GET /login/ HTTP/1.0" 200 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 95, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:48:31] "GET /ls/ HTTP/1.0" 500 - +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 95, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:49:05] "GET /ls/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 95, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:51:19] "GET /ls/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 95, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 04:53:41] "GET /ls/ HTTP/1.0" 500 - +INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on all addresses (0.0.0.0) + * Running on http://127.0.0.1:5000 + * Running on http://10.0.1.10:5000 +INFO:werkzeug:Press CTRL+C to quit +ERROR:Start:Exception on /ls/ [GET] +Traceback (most recent call last): + File "/usr/lib/python3/site-packages/flask/app.py", line 1511, in wsgi_app + response = self.full_dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 919, in full_dispatch_request + rv = self.handle_user_exception(e) + File "/usr/lib/python3/site-packages/flask/app.py", line 917, in full_dispatch_request + rv = self.dispatch_request() + File "/usr/lib/python3/site-packages/flask/app.py", line 902, in dispatch_request + return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ + File "/home/romenskiy2012/Документы/web/wg_2/Start.py", line 95, in ls + print(request.headers['short_token']) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 628, in __getitem__ + return self._get_key(key) + ~~~~~~~~~~~~~^^^^^ + File "/usr/lib/python3/site-packages/werkzeug/datastructures/headers.py", line 639, in _get_key + return self.environ[f"HTTP_{key}"] # type: ignore[no-any-return] + ~~~~~~~~~~~~^^^^^^^^^^^^^^^ +KeyError: 'HTTP_SHORT_TOKEN' +INFO:werkzeug:127.0.0.1 - - [27/Dec/2025 05:01:41] "GET /ls/ HTTP/1.0" 500 - diff --git a/settings.json b/settings.json new file mode 100644 index 0000000..d13bbb8 --- /dev/null +++ b/settings.json @@ -0,0 +1,20 @@ +{ + "Mariadb": { + "host": "127.0.0.1", + "port": 3306, + "user": "recording_spark", + "password": "", + "select_commit": true + }, + "Redis": { + "host": "127.0.0.1", + "port": 6379, + "password": "", + "db": 1 + }, + "host": "0.0.0.0", + "port": 5000, + + "log_file": "server.log", + "log_type": "DEBUG" +} diff --git a/uset/admin/tesla.conf b/uset/admin/tesla.conf new file mode 100644 index 0000000..6ef5905 --- /dev/null +++ b/uset/admin/tesla.conf @@ -0,0 +1,9 @@ +[Interface] +PrivateKey = gAmqfD4pbhJio9j5Ubr0bgHxtBkDSGBJtVQ/TPXtCEU= +Address = 10.0.21.10/32 + +[Peer] +PublicKey = C2HmaZ4h3aQicjEZpDljtXyeq1DKVdiCmv11ZZiTUDw= +AllowedIPs = 10.0.20.0/24, 10.0.21.1/32 +Endpoint = 95.31.187.233:51821 +PersistentKeepalive = 15 diff --git a/wg/app.html b/wg/app.html new file mode 100755 index 0000000..5434302 --- /dev/null +++ b/wg/app.html @@ -0,0 +1,62 @@ + + + + + + Панель + + + +
+
+
WireGuard
+ +
+
+
Пользователь
+
+
+ +
+
+
+ +
+
+
+
+

Конфиги

+
+
+ +
+ + +
+
+ +
+ + + + + + + + + + + + + + +
IDНазваниеСтатусВладелецКомуКомментарий / WGInventory/WG-ID
+
+ + +
+
+ + + + diff --git a/wg/assets/styles.css b/wg/assets/styles.css new file mode 100755 index 0000000..91a6094 --- /dev/null +++ b/wg/assets/styles.css @@ -0,0 +1,207 @@ +:root{ + --bg:#0b1020; + --card:#111a33; + --card2:#0f1730; + --text:#e7ecff; + --muted:#aab4da; + --line:#243055; + --shadow: 0 16px 40px rgba(0,0,0,.35); + --radius: 18px; +} + +*{box-sizing:border-box} +html,body{height:100%} +body{ + margin:0; + font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial; + background: radial-gradient(1200px 800px at 20% 20%, #18224a 0%, var(--bg) 55%, #070b17 100%); + color:var(--text); +} + +a{color:inherit} +.link{color:var(--muted); text-decoration:none} +.link:hover{color:var(--text); text-decoration:underline} + +.center{ + min-height:100%; + display:grid; + place-items:center; + padding:24px; +} + +.container{max-width:1100px; margin:0 auto; padding:24px} + +.card{ + width:min(920px, 100%); + background: linear-gradient(180deg, var(--card), var(--card2)); + border: 1px solid rgba(255,255,255,.06); + border-radius: var(--radius); + box-shadow: var(--shadow); + padding: 22px; +} + +.hero{width:min(720px, 100%); padding:32px} +.auth{width:min(520px, 100%)} + +.topline{ + display:flex; + justify-content:space-between; + align-items:center; + margin-bottom:10px; +} + +.title{margin:0 0 10px 0; font-size:32px; letter-spacing:.2px} +.muted{color:var(--muted)} + +.actions{ + display:grid; + grid-template-columns:1fr 1fr; + gap:14px; + margin-top:18px; +} +@media (max-width:640px){ + .actions{grid-template-columns:1fr} +} + +.form{display:grid; gap:12px; margin-top:10px} +.field{display:grid; gap:6px} +.label{color:var(--muted); font-size:14px} +input{ + width:100%; + padding:12px 12px; + border-radius:12px; + border:1px solid rgba(255,255,255,.10); + background: rgba(0,0,0,.18); + color:var(--text); + outline:none; +} +input:focus{border-color: rgba(255,255,255,.25)} + +.check{display:flex; gap:10px; align-items:center; color:var(--muted); font-size:14px} +.msg{min-height:22px; color:var(--muted)} + +.btn{ + appearance:none; + border:1px solid rgba(255,255,255,.12); + background: rgba(255,255,255,.06); + color:var(--text); + border-radius: 14px; + padding: 12px 14px; + cursor:pointer; + text-decoration:none; + display:inline-flex; + align-items:center; + justify-content:center; + gap:10px; + user-select:none; +} +.btn:hover{border-color: rgba(255,255,255,.22)} +.btn:active{transform: translateY(1px)} +.btn-primary{ + background: linear-gradient(180deg, rgba(120,140,255,.28), rgba(90,110,255,.16)); + border-color: rgba(120,140,255,.40); +} +.btn-ghost{background: rgba(255,255,255,.04)} +.btn-xl{padding:18px 16px; font-size:18px; border-radius:16px} +.btn-lg{padding:14px 14px; font-size:16px} +.btn-sm{padding:10px 12px; font-size:14px; border-radius:12px} + +.header{ + position:sticky; + top:0; + z-index:5; + backdrop-filter: blur(10px); + background: rgba(6,10,22,.55); + border-bottom:1px solid rgba(255,255,255,.06); +} +.header-inner{ + max-width:1100px; + margin:0 auto; + padding:14px 24px; + display:flex; + align-items:center; + justify-content:space-between; + gap:16px; +} +.brand{font-weight:700; letter-spacing:.3px} +.header-right{display:flex; align-items:center; gap:14px} +.userbox{display:grid; gap:2px} +.userlabel{font-size:12px; color:var(--muted)} +.username{font-weight:600} + +.table-head{ + display:flex; + align-items:flex-end; + justify-content:space-between; + gap:12px; + margin-bottom:12px; +} +.table-controls{display:flex; align-items:center; gap:10px} +.input{min-width:240px} +@media (max-width:760px){ + .table-head{flex-direction:column; align-items:stretch} + .table-controls{justify-content:space-between} + .input{min-width:0; flex:1} +} + +.table-wrap{ + overflow:auto; + border:1px solid rgba(255,255,255,.06); + border-radius:14px; +} +.table{ + width:100%; + border-collapse:collapse; + min-width:980px; +} +th,td{ + padding:12px 12px; + border-bottom:1px solid rgba(255,255,255,.06); + vertical-align:top; + font-size:14px; +} +th{ + position:sticky; + top:0; + background: rgba(12,18,38,.9); + text-align:left; + color:var(--muted); + font-weight:600; +} +tr:hover td{background: rgba(255,255,255,.03)} +.badge{ + display:inline-flex; + padding:6px 10px; + border-radius:999px; + border:1px solid rgba(255,255,255,.12); + color:var(--muted); + background: rgba(255,255,255,.04); +} + +.pre{ + margin:10px 0 0; + padding:12px; + border-radius:12px; + background: rgba(0,0,0,.18); + border:1px solid rgba(255,255,255,.08); + overflow:auto; +} + +.callout{ + margin-top:14px; + padding:14px; + border-radius:14px; + border:1px solid rgba(255,255,255,.10); + background: rgba(255,255,255,.04); +} +.callout-title{font-weight:600; color:var(--text)} + +.errorbox{ + margin-top:12px; + padding:12px; + border-radius:14px; + border:1px solid rgba(255,80,80,.35); + background: rgba(255,80,80,.08); + color: var(--text); +} +.hidden{display:none} diff --git a/wg/index.html b/wg/index.html new file mode 100755 index 0000000..0c21921 --- /dev/null +++ b/wg/index.html @@ -0,0 +1,24 @@ + + + + + + Главная + + + +
+
+

Я крутой

+

Панель WireGuard

+ + +
+
+ + + + diff --git a/wg/js/api.js b/wg/js/api.js new file mode 100755 index 0000000..e017ac2 --- /dev/null +++ b/wg/js/api.js @@ -0,0 +1,97 @@ +import { CONFIG } from "./config.js"; +import { loadSession, saveSession, clearSession } from "./storage.js"; + +function urlJoin(base, path) { + if (!base) return path; + return base.replace(/\/+$/, "") + "/" + path.replace(/^\/+/, ""); +} + +async function parseResponse(res) { + const ct = (res.headers.get("content-type") || "").toLowerCase(); + if (ct.includes("application/json")) return await res.json(); + const text = await res.text(); + // иногда сервер шлёт json как text/plain + try { return JSON.parse(text); } catch { return text; } +} + +export async function renewTokens() { + const s = loadSession(); + if (!s) throw new Error("NO_SESSION"); + + // /renew/ ждёт user_id и live_token в headers :contentReference[oaicite:1]{index=1} + const res = await fetch(urlJoin(CONFIG.API_BASE_URL, CONFIG.ENDPOINTS.renew), { + method: "GET", + headers: { + "user_id": String(s.user_id), + "live_token": String(s.live_token) + } + }); + + if (res.status === CONFIG.AUTH_ERROR_STATUS) { + clearSession(); + throw new Error("RENEW_DENIED"); + } + + if (!res.ok) { + const body = await parseResponse(res); + throw new Error(typeof body === "string" ? body : "RENEW_FAILED"); + } + + const data = await parseResponse(res); + if (!data?.short_token || !data?.live_token) { + throw new Error("BAD_RENEW_RESPONSE"); + } + + const next = { ...s, short_token: data.short_token, live_token: data.live_token }; + saveSession(next); + return next; +} + +export async function apiFetch(path, { + method = "GET", + headers = {}, + body = null, + auth = true +} = {}) { + const s = loadSession(); + + const reqHeaders = { ...headers }; + if (auth) { + if (!s?.short_token) throw new Error("NO_AUTH"); + // Все защищённые ручки проверяют short_token в headers :contentReference[oaicite:2]{index=2} + reqHeaders["short_token"] = String(s.short_token); + } + + const doRequest = async () => { + const res = await fetch(urlJoin(CONFIG.API_BASE_URL, path), { + method, + headers: { + ...reqHeaders, + ...(body ? { "content-type": "application/json" } : {}) + }, + body: body ? JSON.stringify(body) : null + }); + return res; + }; + + let res = await doRequest(); + + // Авто-обновление токенов при 426 :contentReference[oaicite:3]{index=3} + if (auth && res.status === CONFIG.AUTH_ERROR_STATUS) { + await renewTokens(); + // обновим заголовок short_token и повторим один раз + const s2 = loadSession(); + reqHeaders["short_token"] = String(s2.short_token); + res = await doRequest(); + } + + const payload = await parseResponse(res); + if (!res.ok) { + const err = new Error("API_ERROR"); + err.status = res.status; + err.payload = payload; + throw err; + } + + return payload; +} diff --git a/wg/js/app.js b/wg/js/app.js new file mode 100755 index 0000000..274c0d6 --- /dev/null +++ b/wg/js/app.js @@ -0,0 +1,162 @@ +import { requireAuthOrRedirect, logout } from "./auth.js"; +import { apiFetch } from "./api.js"; +import { loadSession } from "./storage.js"; +import { CONFIG } from "./config.js"; + +if (!requireAuthOrRedirect()) { + // редирект уже сделан +} + +const userNameEl = document.getElementById("userName"); +const logoutBtn = document.getElementById("logoutBtn"); +const reloadBtn = document.getElementById("reloadBtn"); +const statusEl = document.getElementById("status"); +const tbody = document.getElementById("tbody"); +const errorBox = document.getElementById("errorBox"); +const searchEl = document.getElementById("search"); + +let rows = []; + +function showError(text) { + errorBox.textContent = text; + errorBox.classList.remove("hidden"); +} + +function clearError() { + errorBox.textContent = ""; + errorBox.classList.add("hidden"); +} + +function setStatus(text) { + statusEl.textContent = text; +} + +function normalizeCell(v) { + if (v === null || v === undefined) return ""; + return String(v); +} + +function isWireguardLike(text) { + const t = String(text || ""); + return t.includes("[Interface]") || t.includes("[Peer]") || t.includes("PrivateKey") || t.includes("AllowedIPs"); +} + +async function copyToClipboard(text) { + try { + await navigator.clipboard.writeText(text); + } catch { + // fallback + const ta = document.createElement("textarea"); + ta.value = text; + document.body.appendChild(ta); + ta.select(); + document.execCommand("copy"); + ta.remove(); + } +} + +function renderTable(filter = "") { + const q = filter.trim().toLowerCase(); + const filtered = !q ? rows : rows.filter(r => r.searchBlob.includes(q)); + + tbody.innerHTML = ""; + for (const r of filtered) { + const tr = document.createElement("tr"); + + const commentPreview = r.comments.length > 180 ? (r.comments.slice(0, 180) + "…") : r.comments; + + tr.innerHTML = ` + ${r.id} + ${escapeHtml(r.name)} + ${escapeHtml(r.status)} + ${escapeHtml(r.ownerName)} (#${r.ownerId}) + ${escapeHtml(r.targetName)} (#${r.targetId}) +
${escapeHtml(commentPreview)}
+ ${escapeHtml(r.inventoryId)} + + `; + + const actionTd = tr.lastElementChild; + + if (isWireguardLike(r.comments)) { + const btn = document.createElement("button"); + btn.className = "btn btn-ghost btn-sm"; + btn.textContent = "Копировать WG"; + btn.addEventListener("click", async () => { + await copyToClipboard(r.comments); + setStatus("Скопировано."); + setTimeout(() => setStatus(""), 1200); + }); + actionTd.appendChild(btn); + } + + tbody.appendChild(tr); + } +} + +function escapeHtml(s) { + return String(s) + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); +} + +async function loadData() { + clearError(); + setStatus("Загрузка…"); + + try { + // /ls/ возвращает {"ls": bd_module.ls_item(uid)} :contentReference[oaicite:7]{index=7} + // Формат строк из БД: [item_id,item_name,status,user_id_1,user_name1,user_id_2,user_name2,icon,comments,inventory_id] :contentReference[oaicite:8]{index=8} + const data = await apiFetch(CONFIG.ENDPOINTS.list, { method: "GET", auth: true }); + + const list = Array.isArray(data?.ls) ? data.ls : []; + rows = list.map((a) => { + const id = normalizeCell(a?.[0]); + const name = normalizeCell(a?.[1]); + const status = normalizeCell(a?.[2]); + const ownerId = normalizeCell(a?.[3]); + const ownerName = normalizeCell(a?.[4]); + const targetId = normalizeCell(a?.[5]); + const targetName = normalizeCell(a?.[6]); + const comments = normalizeCell(a?.[8]); + const inventoryId = normalizeCell(a?.[9]); + + const searchBlob = [ + id, name, status, ownerId, ownerName, targetId, targetName, comments, inventoryId + ].join(" ").toLowerCase(); + + return { id, name, status, ownerId, ownerName, targetId, targetName, comments, inventoryId, searchBlob }; + }); + + renderTable(searchEl.value); + setStatus(`Готово. Строк: ${rows.length}`); + } catch (err) { + if (err?.status === CONFIG.AUTH_ERROR_STATUS) { + // если даже renew не помог — сессия умерла + await logout(); + location.replace("./login.html"); + return; + } + showError("Ошибка загрузки данных."); + setStatus(""); + } +} + +(function init() { + const s = loadSession(); + userNameEl.textContent = s?.user_name || `#${s?.user_id || "—"}`; + + logoutBtn.addEventListener("click", async () => { + await logout(); + location.replace("./index.html"); + }); + + reloadBtn.addEventListener("click", loadData); + + searchEl.addEventListener("input", () => renderTable(searchEl.value)); + + loadData(); +})(); diff --git a/wg/js/auth.js b/wg/js/auth.js new file mode 100755 index 0000000..cd2a423 --- /dev/null +++ b/wg/js/auth.js @@ -0,0 +1,65 @@ +import { CONFIG } from "./config.js"; +import { saveSession, loadSession, clearSession } from "./storage.js"; +import { apiFetch } from "./api.js"; + +function deviceLabel() { + const ua = navigator.userAgent || "browser"; + return ua.slice(0, 120); +} + +export function isLoggedIn() { + return !!loadSession(); +} + +export function requireAuthOrRedirect() { + if (!isLoggedIn()) { + location.replace("./login.html"); + return false; + } + return true; +} + + +export async function login(email, password, remember) { + const headers = { + email: String(email), + password: String(password), + remember: remember ? "1" : "0" + }; + + const data = await apiFetch(CONFIG.ENDPOINTS.login, { method: "GET", headers, auth: false }); + + if (!data?.short_token || !data?.live_token || !data?.user_id) { + throw new Error("BAD_LOGIN_RESPONSE"); + } + + saveSession({ + short_token: data.short_token, + live_token: data.live_token, + user_id: data.user_id, + user_name: data.user_name || "" + }); + + return data; +} + +export async function logout() { + const s = loadSession(); + try { + if (s?.user_id && s?.live_token) { + // /kill_session/ удаляет live_token :contentReference[oaicite:5]{index=5} + await apiFetch(CONFIG.ENDPOINTS.logout, { + method: "GET", + headers: { + user_id: String(s.user_id), + live_token: String(s.live_token) + }, + auth: false + }); + } + } catch { + // игнор: локально всё равно очищаем + } finally { + clearSession(); + } +} diff --git a/wg/js/config.js b/wg/js/config.js new file mode 100755 index 0000000..3da289d --- /dev/null +++ b/wg/js/config.js @@ -0,0 +1,15 @@ +export const CONFIG = { + // Если фронт и API на одном домене — оставь пусто: "" + // Если API отдельно — например: "https://api.example.com" + API_BASE_URL: "", + + ENDPOINTS: { + login: "/api/login/", + renew: "/api/renew/", + list: "/api/ls/", + logout:"/api/kill_session/" + }, + + // В твоём сервере токен-ошибка возвращается как 426 :contentReference[oaicite:0]{index=0} + AUTH_ERROR_STATUS: 426 +}; diff --git a/wg/js/login.js b/wg/js/login.js new file mode 100755 index 0000000..893dc2f --- /dev/null +++ b/wg/js/login.js @@ -0,0 +1,32 @@ +import { login, isLoggedIn } from "./auth.js"; + +if (isLoggedIn()) { + location.replace("./app.html"); +} + +const form = document.getElementById("loginForm"); +const msg = document.getElementById("msg"); + +function setMsg(text) { + msg.textContent = text || ""; +} + +form.addEventListener("submit", async (e) => { + e.preventDefault(); + setMsg("Вход…"); + + const email = document.getElementById("email").value.trim(); + const password = document.getElementById("password").value; + const remember = document.getElementById("remember").checked; + + try { + await login(email, password, remember); + location.replace("./app.html"); + } catch (err) { + // Сервер: 412 (не найден логин), 423 (отключён), 426 (токен) :contentReference[oaicite:6]{index=6} + const status = err?.status; + if (status === 412) return setMsg("Логин/пароль неверны."); + if (status === 423) return setMsg("Учётная запись отключена."); + setMsg("Ошибка входа."); + } +}); diff --git a/wg/js/storage.js b/wg/js/storage.js new file mode 100755 index 0000000..abad957 --- /dev/null +++ b/wg/js/storage.js @@ -0,0 +1,21 @@ +const KEY = "wg_front_session_v1"; + +export function loadSession() { + try { + const raw = localStorage.getItem(KEY); + if (!raw) return null; + const s = JSON.parse(raw); + if (!s?.short_token || !s?.live_token || !s?.user_id) return null; + return s; + } catch { + return null; + } +} + +export function saveSession(session) { + localStorage.setItem(KEY, JSON.stringify(session)); +} + +export function clearSession() { + localStorage.removeItem(KEY); +} diff --git a/wg/login.html b/wg/login.html new file mode 100755 index 0000000..81fef27 --- /dev/null +++ b/wg/login.html @@ -0,0 +1,43 @@ + + + + + + Вход + + + +
+
+ + +

Вход

+ +
+ + + + + + + + +
+
+
+
+ + + + diff --git a/wg/request.html b/wg/request.html new file mode 100755 index 0000000..93747e6 --- /dev/null +++ b/wg/request.html @@ -0,0 +1,33 @@ + + + + + + Запрос учётной записи + + + +
+
+ + +

Запрос учётной записи

+

+ Тут обычно: кому писать/что отправить. Если у тебя есть API-ручка под регистрацию — подключишь в `js/api.js`. +

+ +
+
Шаблон заявки
+
Нужен доступ к WireGuard.
+ФИО:
+Контакт:
+Причина:
+Устройство/ОС:
+
+
+
+ +