diff --git a/.gitignore b/.gitignore index 09664631dcf2a71e6f8733a332fa3748092a60ca..d7db213cbcef52dff2b0402b07754333d407c990 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,10 @@ bin-release/ *.ipa *.apk .venv/ +.data/config/ +main.spec +.build/ +.dist/ # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` # should NOT be excluded as they contain compiler settings and other important # information for Eclipse / Flash Builder. diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 359bb5307e8535ab7d59faf27a7377033291821e..0000000000000000000000000000000000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# 默认忽略的文件 -/shelf/ -/workspace.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index d72b3f9c6425e5ad077406ef2dbe8f19ba10daac..3fe72cc82585802007d59170240cd2f73cb68384 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/app/controller/api.py b/app/controller/api.py new file mode 100644 index 0000000000000000000000000000000000000000..32edff5725d30ce01bf6df441bcb2fd2330c06c7 --- /dev/null +++ b/app/controller/api.py @@ -0,0 +1,18 @@ +from flask import jsonify, session +import hashlib +import json +def service(path, cfg, data): + if path == "login": + print(data) + if data["username"] in cfg: + # 登录用户名正确 + print(hashlib.sha256(data["pw"].encode()).hexdigest()) + if hashlib.sha256(data["pw"].encode()).hexdigest() == cfg[data["username"]]: + # 密码正确 + session['username'] = data["username"] + session['pw'] = hashlib.sha256(data["pw"].encode()).hexdigest() + return jsonify({'code': 200, 'msg': 'Login successful'}) + else: + return jsonify({'code': 401, 'msg': 'Wrong password'}) + else: + return jsonify({'code': 404, 'msg': 'User not found'}) diff --git a/app/controller/index.py b/app/controller/index.py new file mode 100644 index 0000000000000000000000000000000000000000..15c3bd36d2916d07f57f6bea2c4da7e03cc34485 --- /dev/null +++ b/app/controller/index.py @@ -0,0 +1,16 @@ +from flask import Flask, Response +from app.utils import Route +from app.utils import Config + + +def service_start(): + app = Flask(__name__, template_folder='../../templates') + app.static_folder = "../../public/static" + # 获取配置文件 + cfg = Config.get_config() + app.secret_key = cfg["secret_key"] + print(cfg) + # 可以在路径内以/<参数名>的形式指定参数,默认接收到的参数类型是string + Route.get_route(app,cfg) + + app.run(port=cfg["port"]) diff --git a/app/controller/install.py b/app/controller/install.py index 442989fd41755ffd468ad4d14bb9c9fb864152ed..cf329d972589c302b594ffbea835639ccf8de273 100644 --- a/app/controller/install.py +++ b/app/controller/install.py @@ -4,6 +4,8 @@ import json import random import string import hashlib +from app.utils import Service + def install(): system = sy.get_system_name() @@ -13,13 +15,21 @@ def install(): # 生成随机字母 username = ''.join(random.choices(string.ascii_letters, k=5)) pw = ''.join(random.choices(string.ascii_letters + string.digits, k=8)) - port = random.randint(8888, 8900) + port = random.randint(8888, 9000) ip = sy.get_system_ip() - print("用户名:", username, "\n密码:", pw, "\n端口:", port) - print("面板访问地址: http://" + ip + ":" + str(port) + "/") + secret_key = ''.join(random.choices(string.ascii_letters + string.digits, k=9)) + # 生成随机安全入口 + safe_entry = ''.join(random.choices(string.ascii_letters, k=5)) + print("用户名:", username, "\n密码:", pw, "\n端口:", port, "\n安全入口:", safe_entry) + print("面板访问地址: http://" + ip + ":" + str(port) + "/" + safe_entry) # 写入文件 - json_data = {username: hashlib.sha256(pw.encode()).hexdigest(), "port": port} + json_data = { + username: hashlib.sha256(pw.encode()).hexdigest(), + "port": port, + "safe_entry": safe_entry, + "secret_key": secret_key + } with open("./data/config/panel.json", 'w') as file: json.dump(json_data, file) @@ -37,7 +47,16 @@ def install(): print("文件权限设置成功") else: print("文件权限设置失败:", error.decode('utf-8')) + return False else: # 设置文件权限为root可读写,其它用户无法读写 os.chmod("./data/config/panel.json", 0o600) - print("安装完成") + + # 设置开机自启动 + service_name = "QPanel面板服务" + + service_file = Service.create_systemd_service(service_name) + if service_file: + return True + else: + return False diff --git a/app/utils/Config.py b/app/utils/Config.py new file mode 100644 index 0000000000000000000000000000000000000000..95a543977e8c868c8bc3c9831f642b8464629f82 --- /dev/null +++ b/app/utils/Config.py @@ -0,0 +1,13 @@ +import json + +# JSON 文件路径 +def get_config(): + file_path = "./data/config/panel.json" + + # 打开 JSON 文件并读取数据 + with open(file_path, "r") as json_file: + # 使用 json 模块的 load() 方法将 JSON 数据解析为 Python 对象 + json_data = json.load(json_file) + + # 打印 JSON 对象 + return json_data diff --git a/app/utils/Route.py b/app/utils/Route.py new file mode 100644 index 0000000000000000000000000000000000000000..c9e0ffeb4020b89046e902f9002e20ee2a2dec64 --- /dev/null +++ b/app/utils/Route.py @@ -0,0 +1,42 @@ +from flask import render_template, send_from_directory, request, session, abort +from app.controller import api + + +def get_route(app, cfg): + @app.before_request + def before_request_func(): + # 打印出所有路径参数及其值 + print(request.path) + if "username"in session and "pw" in session: + if cfg[session.get('username')] != session.get('pw'): + if request.path != "/" + cfg["safe_entry"]: + if "/static" not in request.path and "/api/post/login" not in request.path: + # 未登录 + abort(404) + else: + if request.path != "/" + cfg["safe_entry"]: + if "/static" not in request.path and "/api/post/login" not in request.path: + # 未登录 + abort(404) + + + @app.errorhandler(404) + def page_not_found(error): + return render_template('404.html'), 404 + + @app.route("/") + def home(): + return render_template('index.html') + + @app.route("/" + cfg["safe_entry"]) + def login(): + return render_template('login.html') + + @app.route('/static/') + def static_file(path): + # 使用send_from_directory函数来发送静态文件 + return send_from_directory(app.static_folder, path) + + @app.route('/api/post/', methods=['POST']) + def api_controller(path): + return api.service(path, cfg, request.json) diff --git a/app/utils/Service.py b/app/utils/Service.py new file mode 100644 index 0000000000000000000000000000000000000000..6f3bc3ca1df042cb2f989192b79dba5e2df678bb --- /dev/null +++ b/app/utils/Service.py @@ -0,0 +1,46 @@ +import os +from app.utils import System + + +def create_systemd_service(service_name): + if System.get_system_name() == "Linux": + print(1) + username = os.getlogin() # 获取当前用户名 + script_path = "/home/quick-panel/" + service_content = f"""\ + [Unit] + Description=My Python Script + After=network.target + + [Service] + User={username} + WorkingDirectory={os.path.dirname(script_path)} + ExecStart=/usr/bin/python3 {script_path} + Restart=always + + [Install] + WantedBy=multi-user.target + """ + service_file = f"/etc/systemd/system/{service_name}.service" + with open(service_file, 'w') as f: + f.write(service_content) + os.system(f"sudo systemctl daemon-reload") + os.system(f"sudo systemctl enable {service_name}.service") + return True + elif System.get_system_name() == "Windows": + + # Windows 系统下的服务创建逻辑 + import winreg + key = r"Software\Microsoft\Windows\CurrentVersion\Run" + try: + # 打开指定路径下的注册表项 + key_handle = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key, 0, winreg.KEY_WRITE) + # 设置注册表项的值 + winreg.SetValueEx(key_handle, "QPanel面板", 0, winreg.REG_SZ, "C:\Program Files\QPanel\main.exe") + # 关闭注册表项 + winreg.CloseKey(key_handle) + return True + except Exception as e: + print(f"Error creating startup entry: {e}") + return False + diff --git a/install.sh b/install.sh new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/main.py b/main.py index 9426731f4904d7afb187bad0e29d25370d4aaae7..2f362a1913cf19fb9bd69a4013a5ffb75ff5259b 100644 --- a/main.py +++ b/main.py @@ -1,37 +1,30 @@ -from flask import Flask, Response +import subprocess + import app.utils.File as f import app.controller.install as paninstall - -app = Flask(__name__) - -# 可以在路径内以/<参数名>的形式指定参数,默认接收到的参数类型是string +from app.controller import index +import sys # 检测安装 if f.Had("data/config/panel.json"): print("已安装") - - - @app.route("/index/", ) - def index(id): - if id == 1: - return 'first' - elif id == 2: - return 'second' - elif id == 3: - return 'thrid' - else: - return 'hello world!' - - - if __name__ == '__main__': - app.run() + index.service_start() else: print("未安装面板") install = input("是否安装面板(y/n):") if install == "y": # 安装面板 print("开始安装面板...") - paninstall.install() + if paninstall.install(): + print("安装成功") + # 获取当前可执行文件的路径 + executable = sys.executable + # 重新启动当前程序 + subprocess.Popen([executable] + sys.argv) + # 退出当前程序 + sys.exit() + else: + print("安装失败") else: print("退出程序") diff --git a/public/static/css/login.css b/public/static/css/login.css new file mode 100644 index 0000000000000000000000000000000000000000..2f5f060390a8c5f849cbbe57a14a683e47cc5143 --- /dev/null +++ b/public/static/css/login.css @@ -0,0 +1,54 @@ + body { + margin: 0; + padding: 0; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + background-color: #f5f9ff; + } + .form-container { + background-color: #e2f1ff; + padding: 20px; + border-radius: 10px; + /* box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.1); */ + } + .form-container h2 { + text-align: center; + } + .form-group { + margin-bottom: 20px; + padding-inline:50px + } + .form-group label { + display: block; + margin-bottom: 5px; + } + .form-group input { + width: 100%; + padding: 10px; + border: 2px solid #dde8ff; /* 使用基色设置边框 */ + border-radius: 5px; + box-sizing: border-box; + transition: border-color 0.3s ease; /* 添加过渡效果 */ + } + .form-group input:focus { + border-color: #6A9BFF; /* 使用悬浮色设置边框 */ + outline: none; + } + .form-group button { + width: 100%; + padding: 10px; + border: none; + border-radius: 5px; + background-color: #4A81FF; /* 使用基色设置背景色 */ + color: #fff; + cursor: pointer; + transition: background-color 0.3s ease; /* 添加过渡效果 */ + } + .form-group button:hover { + background-color: #6A9BFF; /* 使用悬浮色设置背景色 */ + } + .form-group button:active { + background-color: #2E5AD2; /* 使用点击色设置背景色 */ + } \ No newline at end of file diff --git a/public/static/js/login.js b/public/static/js/login.js new file mode 100644 index 0000000000000000000000000000000000000000..748cd8f43a7463b20ed2966ee7f7778e0731acc1 --- /dev/null +++ b/public/static/js/login.js @@ -0,0 +1,43 @@ +document.addEventListener("DOMContentLoaded", function() { + // 监听表单提交事件 + document.querySelector("form").addEventListener("submit", function(event) { + // 阻止默认表单提交行为 + event.preventDefault(); + + // 获取用户名和密码 + var username = document.getElementById("username").value; + var password = document.getElementById("password").value; + + // 构造要发送的数据对象 + var data = { + username: username, + pw: password + }; + + // 发送 POST 请求 + fetch("/api/post/login", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(data) + }) + .then(response => response.json()) + .then(data => { + if (data.code === 200) { + // 登录成功,可以进行相应的操作,比如跳转页面等 + console.log("登录成功"); + window.location.href = "/"; // 示例:跳转到仪表盘页面 + } else { + // 登录失败,处理错误 + console.error("登录失败"); + alert("登录失败,请检查用户名和密码!"); + } + }) + .catch(error => { + // 发生网络错误或其他异常 + console.error("发生错误", error); + alert("发生错误,请稍后重试!"); + }); + }); +}); diff --git a/templates/404.html b/templates/404.html new file mode 100644 index 0000000000000000000000000000000000000000..6f17eaf5da06745d954aa7a99a5b9adff10cfe92 --- /dev/null +++ b/templates/404.html @@ -0,0 +1,7 @@ + +404 Not Found + +

404 Not Found

+
nginx
+ + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000000000000000000000000000000000000..c2e00d517712e070a93161191031c2450c099fdd --- /dev/null +++ b/templates/login.html @@ -0,0 +1,28 @@ + + + + + +QPanel登录 + + + + +
+

QPanel登录

+
+
+ + +
+
+ + +
+
+ +
+
+
+ +