diff --git a/.gitignore b/.gitignore index d7db213cbcef52dff2b0402b07754333d407c990..cbea666ecda36bf13a2498a3290f91eb5384c223 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ bin-release/ main.spec .build/ .dist/ +/running.lock # 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/misc.xml b/.idea/misc.xml index 3fe72cc82585802007d59170240cd2f73cb68384..9d6492689bca0b05200a9a3ea7080fa9dd967d3d 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/.idea/quick-panel.iml b/.idea/quick-panel.iml index 74d515a027de98657e9d3d5f0f1831882fd81374..44acbbd6ca458fb2a6f500ae1aa36d7bb8d86952 100644 --- a/.idea/quick-panel.iml +++ b/.idea/quick-panel.iml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/app/controller/api.py b/app/controller/Api.py similarity index 86% rename from app/controller/api.py rename to app/controller/Api.py index 9839f4af586a856a17b962a557c8b05322a22d31..8cdc7c97213ccf0fc12f981f466f9e8444aa9f00 100644 --- a/app/controller/api.py +++ b/app/controller/Api.py @@ -1,9 +1,18 @@ +import json + from flask import jsonify, session import hashlib import psutil +import platform + + def service(path, cfg, data): if path == "post/login": print(data) + print(cfg) + + data = data.decode('utf-8') + data = json.loads(data) if data["username"] in cfg: # 登录用户名正确 print(hashlib.sha256(data["pw"].encode()).hexdigest()) @@ -40,7 +49,6 @@ def service(path, cfg, data): reverse=True )[:3] - data = { "cpu_core": cpu_count, "cpu_usage": cpu_percent, @@ -51,4 +59,12 @@ def service(path, cfg, data): "memory": memory_top_processes } } + print(data) + return jsonify(data) + elif path == "get/system_info": + data = { + "code": 200, + "data": platform.platform() + } + print(data) return jsonify(data) diff --git a/app/controller/index.py b/app/controller/index.py index b88fc39bd2fefe25509bf0f05fa7ee20294d132f..940a240c1cacac05cfe5a67320165df942603048 100644 --- a/app/controller/index.py +++ b/app/controller/index.py @@ -5,15 +5,15 @@ from app.utils import Route from app.utils import Config -def service_start(): - app = Flask(__name__, template_folder='../../templates') - app.static_folder = "../../public/static" +def service_start(ROOT_PATH): + app = Flask(__name__, template_folder=ROOT_PATH + '/templates') + app.static_folder = ROOT_PATH + "/public/static" # 获取配置文件 cfg = Config.get_config() app.secret_key = cfg["secret_key"] print(os.getcwd()) print(cfg) # 可以在路径内以/<参数名>的形式指定参数,默认接收到的参数类型是string - Route.get_route(app,cfg) + Route.get_route(app, cfg, ROOT_PATH) - app.run(port=cfg["port"]) + app.run(host='0.0.0.0', port=cfg["port"]) diff --git a/app/controller/install.py b/app/controller/install.py index cf329d972589c302b594ffbea835639ccf8de273..4ef248edac98266f6e036c960e93ae895f8f7fc0 100644 --- a/app/controller/install.py +++ b/app/controller/install.py @@ -53,7 +53,7 @@ def install(): os.chmod("./data/config/panel.json", 0o600) # 设置开机自启动 - service_name = "QPanel面板服务" + service_name = "QPanel_Service" service_file = Service.create_systemd_service(service_name) if service_file: diff --git a/app/utils/Route.py b/app/utils/Route.py index d44522da89c9282f14b769a8af49da855086a6bc..6d24403b14ea394f6f916b6421acfd1be9731394 100644 --- a/app/utils/Route.py +++ b/app/utils/Route.py @@ -1,20 +1,20 @@ -from flask import render_template, send_from_directory, request, session, abort -from app.controller import api +from flask import render_template, send_from_directory, request, session, abort, send_file +from app.controller import Api -def get_route(app, cfg): +def get_route(app, cfg, ROOT_PATH): @app.before_request def before_request_func(): - 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: - # 未登录 + if cfg["safe_entry"] not in str(request.path): + + if "username" in session and "pw" in session: + if cfg[session.get('username')] != session.get('pw'): + print(1) + if cfg["safe_entry"] not in str(request.referrer): abort(404) - else: - if request.path != "/" + cfg["safe_entry"]: - if "/static" not in request.path and "/api/post/login" not in request.path: - # 未登录 + else: + if cfg["safe_entry"] not in str(request.referrer): + print(str(request.referrer)) abort(404) @app.errorhandler(404) @@ -38,6 +38,11 @@ def get_route(app, cfg): # 使用send_from_directory函数来发送静态文件 return send_from_directory(app.static_folder, path) + @app.route('/favicon.ico') + def favicon(): + # 使用send_from_directory函数来发送静态文件 + return send_file(ROOT_PATH + '/public/favicon.ico', mimetype='image/vnd.microsoft.icon') + @app.route('/api/', methods=['POST', 'GET']) def api_controller(path): - return api.service(path, cfg, request.json) + return Api.service(path, cfg, request.data) diff --git a/app/utils/Waf.py b/app/utils/Waf.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/install.bat b/install.bat new file mode 100644 index 0000000000000000000000000000000000000000..b8e9c52bfa7d0b2eb30c9bd3d7d14ee5203138ee --- /dev/null +++ b/install.bat @@ -0,0 +1,18 @@ +@echo off +set DOWNLOAD_URL=https://cn-sy1.rains3.com/stardream/windows/win_0.0.6.zip +set DOWNLOAD_PATH=C:\Program Files\win_0.0.6.zip +set EXTRACT_PATH=C:\Program Files\quick-panel +set MAIN_EXE_PATH=%EXTRACT_PATH%\main.exe + +echo 正在下载文件... +curl -o "%DOWNLOAD_PATH%" %DOWNLOAD_URL% + +echo 正在解压文件... +mkdir "%EXTRACT_PATH%" +tar -xf "%DOWNLOAD_PATH%" -C "%EXTRACT_PATH%" + +echo 正在以管理员权限运行 main.exe... +cd /d "%EXTRACT_PATH%" +powershell Start-Process "%MAIN_EXE_PATH%" -Verb RunAs + +echo 执行完毕! diff --git a/install.sh b/install.sh index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..812219f57d0c1c52979b43b1ea183558e7928ec8 100644 --- a/install.sh +++ b/install.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# 克隆代码库 +git clone https://gitee.com/codekpy/quick-panel.git /home/quick-panel + +# 进入项目目录 +cd /home/quick-panel + +# 创建Python虚拟环境 +python3 -m venv venv + +# 激活虚拟环境 +source venv/bin/activate + +# 安装flask库和pyinstaller库 +pip install flask pyinstaller + +# 退出虚拟环境 +deactivate + +# 打包main.py +cd /home/quick-panel +source venv/bin/activate +pyinstaller --onefile main.py + +# 退出虚拟环境 +deactivate diff --git a/main.py b/main.py index 63343c1781c128e6777d013b49455ea5f18e4f2e..f1c00ec50f8e6c1eee5fca4066a2957c67925df0 100644 --- a/main.py +++ b/main.py @@ -1,34 +1,204 @@ +import ctypes import subprocess import app.utils.File as f import app.controller.install as paninstall from app.controller import index +from app.utils import System +import os import sys -# 检测安装 -if f.Had("data/config/panel.json"): - print("已安装") - print("开始启动面板...") - print("------------------------------------") - index.service_start() +LOCK_FILE = "running.lock" + +ROOT_PATH = os.getcwd() + +def main(): + if f.Had("data/config/panel.json"): + print("已安装") + print("开始启动面板...") + print("------------------------------------") + index.service_start(ROOT_PATH) + else: + print("未安装面板") + if System.get_system_name() == "Linux": + if os.getuid() != 0: + sys.exit("请使用root用户运行此程序") + else: + try: + # 使用 ctypes.windll.shell32.IsUserAnAdmin 来检查当前用户是否是管理员 + if ctypes.windll.shell32.IsUserAnAdmin() == 0: + sys.exit("请使用管理员用户运行此程序") + except AttributeError: + # 如果 IsUserAnAdmin 函数不可用,则当前系统不支持该方法,因此返回 False + sys.exit("请使用管理员用户运行此程序") + install = input("是否安装面板(y/n):") + if install == "y": + # 安装面板 + print("开始安装面板...") + if paninstall.install(): + print("安装成功") + # 获取当前可执行文件的路径 + executable = sys.executable + if System.get_system_name() == "Linux": + input("请及时保留您的登录信息(按下任意键结束进程并重启)") + else: + input("请及时保留您的登录信息(按下任意键结束进程并手动重启)") + subprocess.Popen([executable] + sys.argv) + + # 退出当前程序 + sys.exit() + else: + print("安装失败") + else: + print("退出程序") + + +if System.get_system_name() == "Linux": + pass else: - print("未安装面板") - install = input("是否安装面板(y/n):") - if install == "y": - # 安装面板 - print("开始安装面板...") - if paninstall.install(): - print("安装成功") - # 获取当前可执行文件的路径 - executable = sys.executable - # 重新启动当前程序 - subprocess.Popen([executable] + sys.argv) - # 退出当前程序 - sys.exit() + import win32file + import win32con + import pywintypes + import winerror + import sys + + + class SingleInstanceGuardian: + def __init__(self, lock_file_path): + self._lock_file_path = lock_file_path + self._lock_file = None + + def _acquire_lock(self): + try: + # 创建一个文件用于锁定 + self._lock_file = win32file.CreateFile( + self._lock_file_path, + win32file.GENERIC_WRITE, + 0, + None, + win32file.CREATE_ALWAYS, + win32file.FILE_ATTRIBUTE_NORMAL, + None + ) + # 尝试获取文件锁 + win32file.LockFileEx(self._lock_file, + win32con.LOCKFILE_EXCLUSIVE_LOCK | win32con.LOCKFILE_FAIL_IMMEDIATELY, 0, + -0x10000, pywintypes.OVERLAPPED()) + return True + except pywintypes.error as e: + if e.winerror == winerror.ERROR_LOCK_VIOLATION: + return False + else: + raise + + def _release_lock(self): + if self._lock_file: + win32file.UnlockFileEx(self._lock_file, 0, -0x10000, pywintypes.OVERLAPPED()) + win32file.CloseHandle(self._lock_file) + + def is_already_running(self): + return not self._acquire_lock() + + def run_task(self): + if f.Had("data/config/panel.json"): + print("已安装") + print("开始启动面板...") + print("------------------------------------") + index.service_start(ROOT_PATH) + else: + print("未安装面板") + if System.get_system_name() == "Linux": + if os.getuid() != 0: + sys.exit("请使用root用户运行此程序") + else: + try: + # 使用 ctypes.windll.shell32.IsUserAnAdmin 来检查当前用户是否是管理员 + if ctypes.windll.shell32.IsUserAnAdmin() == 0: + sys.exit("请使用管理员用户运行此程序") + except AttributeError: + # 如果 IsUserAnAdmin 函数不可用,则当前系统不支持该方法,因此返回 False + sys.exit("请使用管理员用户运行此程序") + install = input("是否安装面板(y/n):") + if install == "y": + # 安装面板 + print("开始安装面板...") + if paninstall.install(): + print("安装成功") + # 获取当前可执行文件的路径 + executable = sys.executable + if System.get_system_name() == "Linux": + input("请及时保留您的登录信息(按下任意键结束进程并重启)") + else: + input("请及时保留您的登录信息(按下任意键结束进程并手动重启)") + subprocess.Popen([executable] + sys.argv) + + # 退出当前程序 + sys.exit() + else: + print("安装失败") + else: + print("退出程序") + + # 任务完成后释放锁 + self._release_lock() + + + # 使用示例 + lock_file_path = LOCK_FILE + print("启动Windows文件锁") + guardian = SingleInstanceGuardian(lock_file_path) + +# 检查命令行参数 +if len(sys.argv) > 1: + if sys.argv[1] == "start": + # 启动程序 + if System.get_system_name() == "Linux": + import fcntl + + lockfile = open(LOCK_FILE, "w") + try: + fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError: + print("程序已经在运行中") + sys.exit(1) + lockfile.close() + os.unlink(LOCK_FILE) + main() else: - print("安装失败") - else: - print("退出程序") + guardian.run_task() + elif sys.argv[1] == "stop": + # 停止程序 + if System.get_system_name() == "Linux": + if os.path.exists(LOCK_FILE): + os.unlink(LOCK_FILE) + print("停止程序") + else: + print("程序未在运行中") + else: + # Windows + guardian._release_lock() + else: + print("无效的命令") +else: + # 启动程序 + if System.get_system_name() == "Linux": + # 启动程序 + if System.get_system_name() == "Linux": + import fcntl + lockfile = open(LOCK_FILE, "w") + try: + fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError: + print("程序已经在运行中") + sys.exit(1) + lockfile.close() + os.unlink(LOCK_FILE) + main() + else: + guardian.run_task() + else: + print("启动项目") + guardian.run_task() diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d86dafa53ef50462557e3f769943b7f6ff236217 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/static/css/home.css b/public/static/css/home.css new file mode 100644 index 0000000000000000000000000000000000000000..70b774072f6058e3bea9599838f172984e8bc20e --- /dev/null +++ b/public/static/css/home.css @@ -0,0 +1,42 @@ +* { + box-sizing: border-box; + margin: 0 +} + +.box-content{ + display: flex; + flex-direction: row; + background-color: #FFFFFF; + width: 95%; + margin-left: 2.5%; + box-shadow: 0px 4px 10px 0px rgba(223, 223, 223, 0.3); + margin-top: 1rem; + padding: 8px 10px; +} + +body{ + background-color: #F4F8FF; + display: flex; + flex-wrap: nowrap; + flex-direction: column; +} + +#system{ + color:#69A0FF; +} + + +.box-content div{ + min-height: 1rem; + display: flex; + flex-direction: column; + align-content: center; + flex-wrap: nowrap; + justify-content: center; + align-items: center; +} + +.box-content div svg{ + width:50%; + height:50%; +} \ No newline at end of file diff --git a/public/static/css/index.css b/public/static/css/index.css index a8cb3d5769a92c7d0abeaf58b53a38d2568567c7..a0b9de6a00af0fe584d5e07bc81e6f866a629118 100644 --- a/public/static/css/index.css +++ b/public/static/css/index.css @@ -5,37 +5,47 @@ body,html { } .container { display:flex; + width:100%; height:100%; } .menu { min-width:80px; width:10%; max-width:120px; - background-color:#D5E2FF; + background-color:#233E79; color:#fff; padding: 1%; box-sizing:border-box; + display: flex; + flex-wrap: nowrap; + justify-content: flex-start; + flex-direction: column; + align-content: center; + align-items: baseline; } .menu-item { display: flex; padding: 0px; margin-top:10px; + margin-left:5%; /* 矩形 2 */ left:32px; top:142px; - color:#000000; - width:85px; + color:#FFFFFF; + width:90%; height:41px; border-radius:5px; opacity:1; - background:rgba(228,236,255,0.85); - box-shadow:0px 1px 7px 0px rgba(205,205,205,0.3); + background:#233E79; + align-items: center; justify-content: center; + } .menu-item:hover { - background-color:#eff7ff; + background-color:#335192; + box-shadow:0px 1px 7px 0px rgba(205,205,205,0.3); } .menu img { @@ -44,7 +54,7 @@ body,html { margin-bottom:10px; } .content { - width:90%; + width:100%; height:100%; border:none; } diff --git a/public/static/css/safe.css b/public/static/css/safe.css new file mode 100644 index 0000000000000000000000000000000000000000..02dde10fbb27e53795fbffbccbca12808ef82436 --- /dev/null +++ b/public/static/css/safe.css @@ -0,0 +1,48 @@ +* { + box-sizing: border-box; + margin: 0 +} + +.box-content{ + display: flex; + flex-direction: row; + background-color: #FFFFFF; + width: 95%; + margin-left: 2.5%; + box-shadow: 0px 4px 10px 0px rgba(223, 223, 223, 0.3); + margin-top: 1rem; + padding: 8px 10px; +} + +body{ + background-color: #F4F8FF; + display: flex; + flex-wrap: nowrap; + flex-direction: column; +} + +#system{ + color:#69A0FF; +} + +.round{ + padding-top: 2.4rem; + padding-bottom: 2.4rem; +} + + .safe_tabs__nav.top { + display: flex; + justify-content: space-between; /* 使选项之间有间隔 */ + gap: 10px; /* 设置选项之间的间隔大小 */ + } + + .safe_tabs__item { + padding: 10px 20px; /* 设置选项的内边距 */ + cursor: pointer; /* 将鼠标悬停样式设置为手型 */ + transition: background-color 0.3s; /* 添加过渡效果 */ + } + + .safe_tabs__item:hover { + background-color: #EDEDED; /* 悬停时的背景色 */ + color:#69A0FF; + } \ No newline at end of file diff --git a/public/static/js/home.js b/public/static/js/home.js new file mode 100644 index 0000000000000000000000000000000000000000..99a9549b78f9ae3c292af0d912a08f61c53ca3e4 --- /dev/null +++ b/public/static/js/home.js @@ -0,0 +1,113 @@ +window.onload = function() { + // 使用 Fetch API 发起 GET 请求 + function runtime_state(){ + + fetch('/api/get/runtime_state') + .then(function(response) { + // 当收到响应时,将其转换为 JSON + return response.json(); + }) + .then(function(data) { + // 处理获取到的数据 + console.log(data); + //在这处理 + + //循环4次 + var injectedHTML = "

状态

" + let number = 0; + for (let i = 0; i < 2; i++) { + if (i==0){ + cpu_core = data.cpu_core; + cpu_usage = data.cpu_usage; + cpu = cpu_usage / cpu_core; + proportion = cpu; + console.log("CPU使用率:" + cpu); + number = parseInt(cpu * 100); + text = "CPU占用" + }else{ + memory_total = data.memory_total; + memory_usage = data.memory_usage; + memory = memory_usage / memory_total; + proportion = memory; + console.log("内存使用率:" + memory_usage); + number = parseInt(memory_usage); + text = "内存占用" + } + injectedHTML += `
+ + + + + + +
+ +
+ ` + number + `% +
+ +
+ ` + text + ` +
+
+
+
+
` + } + + + // 获取 id 为 "status" 的元素 + var statusElement = document.getElementById('status'); + + // 将 HTML 注入到元素中 + if (statusElement) { + statusElement.innerHTML = ''; + statusElement.innerHTML += injectedHTML; + } else { + console.error('找不到id为"status"的元素'); + } + + }) + .catch(function(error) { + // 处理错误 + console.error('发生错误:', error); + }); + } + runtime_state() + setInterval(runtime_state, 5000) + + fetch('/api/get/system_info') + .then(function(response) { + // 当收到响应时,将其转换为 JSON + return response.json(); + }) + .then(function(data) { + // 处理获取到的数据 + // 获取 id 为 system 的元素 + var systemElement = document.getElementById('system'); + systemElement.textContent = data.data; + }) + .catch(function(error) { + // 处理错误 + console.error('发生错误:', error); + }); +} \ No newline at end of file diff --git a/public/static/js/index.js b/public/static/js/index.js new file mode 100644 index 0000000000000000000000000000000000000000..f68e6b06e72676ea4e70026fb17fad8452d96c18 --- /dev/null +++ b/public/static/js/index.js @@ -0,0 +1,36 @@ +//当页面完全加载时 +window.onload = function() { + if (localStorage.getItem('page')){ + turn(localStorage.getItem('page')) + }else{ + turn('1') + } +} + +function turn (id) { + // 设置所有 class 为 .menu-item 的元素背景为 #233E79 + var menuItems = document.getElementsByClassName('menu-item'); + for (var i = 0; i < menuItems.length; i++) { + menuItems[i].style.backgroundColor = '#233E79'; + } + + var elementWithId1 = document.getElementById(id); + if (elementWithId1) { + elementWithId1.style.backgroundColor = '#407AFF'; + } + + // 设置页面中 + diff --git a/templates/p/db.html b/templates/p/db.html new file mode 100644 index 0000000000000000000000000000000000000000..fd5c7ba2841e775709d0ba3f58cf90e0a6230f56 --- /dev/null +++ b/templates/p/db.html @@ -0,0 +1,15 @@ + + + + + + + QPanel + + + + + + + \ No newline at end of file diff --git a/templates/p/home.html b/templates/p/home.html index a598d25f94f18b5026bf316a7343bb9383672aff..fc379c39b371bff430f2c041cd9a7dea2fed661b 100644 --- a/templates/p/home.html +++ b/templates/p/home.html @@ -6,10 +6,20 @@ content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> QPanel + + +
+

系统:

+

获取中

+
+
+

状态

+ +
\ No newline at end of file diff --git a/templates/p/safe.html b/templates/p/safe.html new file mode 100644 index 0000000000000000000000000000000000000000..77a63d744d5bfa4d71646318f8f9821122e7c98c --- /dev/null +++ b/templates/p/safe.html @@ -0,0 +1,29 @@ + + + + + + + QPanel + + + + +
+
+
+
+
+
系统防火墙
+
SSH管理
+
安全检测
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/templates/p/site.html b/templates/p/site.html new file mode 100644 index 0000000000000000000000000000000000000000..fd5c7ba2841e775709d0ba3f58cf90e0a6230f56 --- /dev/null +++ b/templates/p/site.html @@ -0,0 +1,15 @@ + + + + + + + QPanel + + + + + + + \ No newline at end of file