add old lci module
This commit is contained in:
1 parent
38a6e815f0
commit
8499631091
171 files changed
+25562
No files matched your search
@@ -0,0 +1,6 @@
|
||||
# mgmt
|
||||
这是基于pmc模块的管理平台,负责对子系统进行管理。
|
||||
|
||||
## 放置秘钥
|
||||
|
||||
为了和pmc系统通信,我们需要在该目录下放置pmc的rsa私钥
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
cd "$(dirname "$0")" || exit
|
||||
bash ./_setup.sh
|
||||
source ./.env/bin/activate
|
||||
python main.py
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
cd "$(dirname "$0")" || exit
|
||||
python3 -m venv .env
|
||||
#sudo chmod +x .env/bin/activate
|
||||
source .env/bin/activate
|
||||
pip install -r requirements.txt -i https://pypi.org/simple
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
from app.login.routes import login_bp
|
||||
from app.log.routes import log_bp
|
||||
from app.module.routes import module_bp
|
||||
from app.process.routes import process_bp
|
||||
from app.user.routes import user_bp
|
||||
|
||||
|
||||
def register_blueprints(app):
|
||||
# 默认访问家目录
|
||||
app.register_blueprint(login_bp, url_prefix='/')
|
||||
app.register_blueprint(log_bp, url_prefix='/log')
|
||||
app.register_blueprint(module_bp, url_prefix='/module')
|
||||
app.register_blueprint(process_bp, url_prefix='/process')
|
||||
app.register_blueprint(user_bp, url_prefix='/user')
|
||||
Whitespace-only changes.
@@ -0,0 +1,113 @@
|
||||
import datetime
|
||||
import hashlib
|
||||
from functools import wraps
|
||||
|
||||
from pmc import DnfsComm
|
||||
from dnfs import UsersTable, DnfsTable
|
||||
|
||||
import jwt
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, current_app, make_response, jsonify
|
||||
|
||||
login_bp = Blueprint('login', __name__, template_folder='templates/login')
|
||||
|
||||
|
||||
comm = DnfsComm("http://localhost:8006")
|
||||
|
||||
|
||||
def get_user_from_token():
|
||||
token = request.cookies.get('token')
|
||||
if not token:
|
||||
return None
|
||||
try:
|
||||
payload = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
|
||||
return payload # 包含 user、user_id、exp 等字段
|
||||
except jwt.ExpiredSignatureError:
|
||||
return None
|
||||
except jwt.InvalidTokenError:
|
||||
return None
|
||||
|
||||
|
||||
@login_bp.after_request
|
||||
def add_cors_headers(response):
|
||||
response.headers['Access-Control-Allow-Origin'] = '*' # 允许所有来源跨域访问,如需限制可替换为具体域名
|
||||
response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization'
|
||||
response.headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
|
||||
return response
|
||||
|
||||
|
||||
def token_required(f):
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
# 从cookie中获取token
|
||||
token = request.cookies.get('token')
|
||||
if not token:
|
||||
return redirect(url_for('login.index'))
|
||||
try:
|
||||
jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
|
||||
except jwt.ExpiredSignatureError:
|
||||
return redirect(url_for('login.index'))
|
||||
except jwt.InvalidTokenError:
|
||||
return redirect(url_for('login.index'))
|
||||
return f(*args, **kwargs)
|
||||
return decorated
|
||||
|
||||
|
||||
@login_bp.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@login_bp.route('/api/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
username = request.form.get('user-name')
|
||||
password = request.form.get('user-passwd')
|
||||
password = password.encode("utf-8")
|
||||
hashed_password = hashlib.sha256(password).hexdigest()
|
||||
try:
|
||||
data = comm.get_and_trans(0)
|
||||
dnfs_tab = DnfsTable(data)
|
||||
users = dnfs_tab.query_by_title('Users')
|
||||
table = UsersTable(users.content)
|
||||
data = table.user_exists(username, hashed_password)
|
||||
if not data:
|
||||
return "用户登录失败", 401
|
||||
_user_id, _username, _password, _role = data
|
||||
except DnfsComm.GetFailed:
|
||||
return "获取数据失败", 500
|
||||
except DnfsTable.NotFound:
|
||||
return "未找到用户表", 404
|
||||
except UsersTable.NotFound:
|
||||
return "用户不存在", 401
|
||||
except Exception as e:
|
||||
return str(e), 404
|
||||
# 生成 Token
|
||||
token = jwt.encode({
|
||||
'user_id': _user_id,
|
||||
'username': _username,
|
||||
'role': _role,
|
||||
'exp': datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=3)
|
||||
}, current_app.config['SECRET_KEY'], algorithm='HS256')
|
||||
|
||||
# 构造 JSON 响应体
|
||||
response = make_response(jsonify({
|
||||
"redirect": url_for('login.mgmt'), # 跳转路径
|
||||
"token": token
|
||||
}))
|
||||
|
||||
# 设置 Cookie
|
||||
response.set_cookie('token', token)
|
||||
return response
|
||||
else:
|
||||
return "登录失败"
|
||||
|
||||
|
||||
@login_bp.route('/api/mgmt')
|
||||
@token_required
|
||||
def mgmt():
|
||||
payload = get_user_from_token()
|
||||
if not payload:
|
||||
return redirect(url_for('login.index'))
|
||||
username = payload.get('username')
|
||||
role = payload.get('role')
|
||||
return render_template('mgmt.html', username=username, role=role)
|
||||
Whitespace-only changes.
@@ -0,0 +1,34 @@
|
||||
from pmc import PmcComm
|
||||
from flask import Blueprint, render_template
|
||||
|
||||
module_bp = Blueprint('module', __name__, template_folder='templates/features/module.html')
|
||||
|
||||
pmc = PmcComm('http://localhost:8012')
|
||||
pmc.Set_RSA_Pri_Key("private_key.pem")
|
||||
pmc.Try_Get_AES_Key()
|
||||
|
||||
|
||||
@module_bp.after_request
|
||||
def add_cors_headers(response):
|
||||
response.headers['Access-Control-Allow-Origin'] = '*' # 允许所有来源跨域访问,如需限制可替换为具体域名
|
||||
response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization'
|
||||
response.headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
|
||||
return response
|
||||
|
||||
|
||||
@module_bp.route('/api/mgmt/module', methods=['GET', 'POST'])
|
||||
def module():
|
||||
data = pmc.list_mods()
|
||||
modules = data.split('\n')
|
||||
return render_template('features/module.html', modules=modules)
|
||||
|
||||
|
||||
@module_bp.route('/create_module<string:mod_name>', methods=['GET', 'POST'])
|
||||
def create_module(mod_name):
|
||||
pass
|
||||
|
||||
|
||||
@module_bp.route('/delete_module<string:mod_name>', methods=['GET', 'POST'])
|
||||
def delete_module(mod_name):
|
||||
pass
|
||||
|
||||
Whitespace-only changes.
@@ -0,0 +1,53 @@
|
||||
from flask import Blueprint, render_template, jsonify, request
|
||||
from pmc import PmcComm
|
||||
|
||||
|
||||
process_bp = Blueprint('process', __name__, template_folder='templates/features/process.html')
|
||||
|
||||
pmc = PmcComm('http://localhost:8012')
|
||||
pmc.Set_RSA_Pri_Key("private_key.pem")
|
||||
pmc.Try_Get_AES_Key()
|
||||
|
||||
|
||||
@process_bp.after_request
|
||||
def add_cors_headers(response):
|
||||
response.headers['Access-Control-Allow-Origin'] = '*' # 允许所有来源跨域访问,如需限制可替换为具体域名
|
||||
response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization'
|
||||
response.headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
|
||||
return response
|
||||
|
||||
|
||||
@process_bp.route('/api/mgmt/process', methods=['GET', 'POST'])
|
||||
def process():
|
||||
pid = []
|
||||
status = []
|
||||
order = []
|
||||
ls = pmc.get_list()
|
||||
if ls != "":
|
||||
ls_1 = ls.split('\n')
|
||||
for l in ls_1:
|
||||
i, j, k = l.split('\t')
|
||||
pid.append(i)
|
||||
status.append(j)
|
||||
order.append(k)
|
||||
data = [{"pid": p, "status": s, "order": o} for p, s, o in zip(pid, status, order)]
|
||||
|
||||
return render_template('features/process.html', response=data)
|
||||
|
||||
|
||||
@process_bp.route('/kill_process<int:pid>', methods=['POST'])
|
||||
def kill_process(pid):
|
||||
pmc.kill(pid)
|
||||
return jsonify({"status": "success"})
|
||||
|
||||
|
||||
@process_bp.route('/clear_process', methods=['POST'])
|
||||
def clear_process():
|
||||
pmc.clear()
|
||||
return jsonify({"status": "success"})
|
||||
|
||||
|
||||
@process_bp.route('/create_process<string:name>', methods=['POST'])
|
||||
def create_process(name):
|
||||
pmc.start(name)
|
||||
return jsonify({"status": "success"})
|
||||
Whitespace-only changes.
@@ -0,0 +1,111 @@
|
||||
import jwt
|
||||
import hashlib
|
||||
from flask import Blueprint, render_template, request, current_app, jsonify
|
||||
from pmc import DnfsComm
|
||||
from dnfs import UsersTable, DnfsTable
|
||||
|
||||
user_bp = Blueprint('user', __name__, template_folder='templates/features/user.html')
|
||||
|
||||
comm = DnfsComm("http://localhost:8006")
|
||||
|
||||
|
||||
|
||||
def get_user_from_token():
|
||||
token = request.cookies.get('token')
|
||||
if not token:
|
||||
return None
|
||||
try:
|
||||
payload = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256'])
|
||||
return payload # 包含 user、user_id、exp 等字段
|
||||
except jwt.ExpiredSignatureError:
|
||||
return None
|
||||
except jwt.InvalidTokenError:
|
||||
return None
|
||||
|
||||
|
||||
def get_node():
|
||||
try:
|
||||
data = comm.get_and_trans(0)
|
||||
except DnfsComm.GetFailed:
|
||||
return "获取数据失败", 500
|
||||
for item in data:
|
||||
if item['title'] == 'Users':
|
||||
node_id = item['id']
|
||||
node_content = item['content']
|
||||
return node_id, node_content
|
||||
return None
|
||||
|
||||
|
||||
@user_bp.after_request
|
||||
def add_cors_headers(response):
|
||||
response.headers['Access-Control-Allow-Origin'] = '*' # 允许所有来源跨域访问,如需限制可替换为具体域名
|
||||
response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization'
|
||||
response.headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'
|
||||
return response
|
||||
|
||||
|
||||
@user_bp.route('/api/mgmt/user', methods=['GET', 'POST'])
|
||||
def user():
|
||||
try:
|
||||
data = comm.get_and_trans(0)
|
||||
dnfs_tab = DnfsTable(data)
|
||||
users = dnfs_tab.query_by_title('Users')
|
||||
table = UsersTable(users.content)
|
||||
crt_dict = lambda user: {'user_id': user.id, 'username': user.name, 'role': user.privileges}
|
||||
user_list = [crt_dict(user) for user in table.arr]
|
||||
except DnfsComm.GetFailed:
|
||||
return "获取数据失败", 500
|
||||
except DnfsTable.NotFound:
|
||||
return "未找到用户表", 404
|
||||
except UsersTable.NotFound:
|
||||
return "用户不存在", 401
|
||||
except Exception as e:
|
||||
return str(e), 404
|
||||
payload = get_user_from_token()
|
||||
if not payload:
|
||||
return jsonify({"error": "未授权访问"}), 401
|
||||
|
||||
user_id = payload.get('user_id')
|
||||
role = payload.get('role')
|
||||
|
||||
return render_template('features/user.html',
|
||||
response=user_list,
|
||||
current_user_id=user_id,
|
||||
current_role=role)
|
||||
|
||||
|
||||
@user_bp.route('/add_user', methods=['POST'])
|
||||
def add_user():
|
||||
node_id, msg = get_node()
|
||||
username = request.form.get('username')
|
||||
password = request.form.get('password')
|
||||
hashed_password = hashlib.sha256(password.encode("utf-8")).hexdigest()
|
||||
role = "normal"
|
||||
table = UsersTable(msg)
|
||||
table.add_user(username, hashed_password, role)
|
||||
table = str(table)
|
||||
comm.upd(node_id, 0, 'Users', table)
|
||||
return jsonify({"message": "用户添加成功"})
|
||||
|
||||
|
||||
@user_bp.route('/change_password', methods=['POST'])
|
||||
def change_password():
|
||||
node_id, msg = get_node()
|
||||
user_id = request.form.get('user_id')
|
||||
new_password = request.form.get('new_password')
|
||||
hashed_password = hashlib.sha256(new_password.encode("utf-8")).hexdigest()
|
||||
table = UsersTable(msg)
|
||||
table.change_password(int(user_id), hashed_password)
|
||||
table = str(table)
|
||||
comm.upd(node_id, 0, 'Users', table)
|
||||
return jsonify({"message": "密码修改成功"})
|
||||
|
||||
|
||||
@user_bp.route('/delete_user<int:user_id>', methods=['POST'])
|
||||
def delete_user(user_id):
|
||||
node_id, msg = get_node()
|
||||
table = UsersTable(msg)
|
||||
table.del_user(user_id)
|
||||
table = str(table)
|
||||
comm.upd(node_id, 0, 'Users', table)
|
||||
return jsonify({"message": "用户删除成功"})
|
||||
@@ -0,0 +1,17 @@
|
||||
from app import register_blueprints
|
||||
from flask import Flask
|
||||
from waitress import serve
|
||||
|
||||
# 初始化Flask应用
|
||||
app = Flask(__name__)
|
||||
|
||||
# 设置一个密钥
|
||||
app.secret_key = 'secret_key'
|
||||
|
||||
# 注册蓝图
|
||||
register_blueprints(app)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
#app.run(host='0.0.0.0', port=8009, debug=True)
|
||||
serve(app, host="0.0.0.0", port=8009)
|
||||
@@ -0,0 +1,5 @@
|
||||
../_pmc
|
||||
../_dnfs
|
||||
flask
|
||||
PyJWT
|
||||
waitress
|
||||
Whitespace-only changes.
@@ -0,0 +1,35 @@
|
||||
.top-bar-con {
|
||||
width: 230px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.newBtn,
|
||||
.flushBtn,
|
||||
.removeBtn {
|
||||
width: 84px;
|
||||
height: 42px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.newBtn {
|
||||
background-color: #5cff007d;
|
||||
}
|
||||
|
||||
.flushBtn {
|
||||
background-color: #5757577d;
|
||||
}
|
||||
|
||||
.removeBtn {
|
||||
background-color: #ff00007d;
|
||||
color: #000000b8;
|
||||
}
|
||||
|
||||
td {
|
||||
width: 180px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
.newBtn,
|
||||
.flushBtn,
|
||||
.stopBtn {
|
||||
width: 84px;
|
||||
height: 42px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.newBtn {
|
||||
background-color: #5cff007d;
|
||||
}
|
||||
|
||||
.flushBtn {
|
||||
background-color: #5757577d;
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.stopBtn {
|
||||
background-color: #ff00007d;
|
||||
}
|
||||
|
||||
.text,
|
||||
.title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
width: 180px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#inProgressText {
|
||||
font-size: 20px;
|
||||
margin-top: 5px;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
/* modal window style */
|
||||
.modal-con {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(68, 94, 65, 0.77);
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.modal-con .window {
|
||||
width: 200px;
|
||||
height: 120px;
|
||||
background-color: #5cff007d;
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
.modal-con .window .top-bar {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.modal-con .window .top-bar .close-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-con .window .create-btn,
|
||||
.modal-con .window .input .input-area {
|
||||
margin-top: 5px;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
.top-bar-con {
|
||||
width: 230px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
td {
|
||||
width: 180px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.addUserBtn,
|
||||
.flushBtn,
|
||||
.changePasswdBtn,
|
||||
.deleteUserBtn {
|
||||
width: 84px;
|
||||
height: 42px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.addUserBtn {
|
||||
background-color: #5cff007d;
|
||||
}
|
||||
|
||||
.flushBtn {
|
||||
background-color: #5757577d;
|
||||
}
|
||||
|
||||
.changePasswdBtn {
|
||||
background-color: #e6db407d;
|
||||
color: #fb0202b0;
|
||||
}
|
||||
|
||||
.deleteUserBtn {
|
||||
background-color: #ff00007d;
|
||||
color: #000000b8;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.main {
|
||||
background-color: rgb(233, 233, 233);
|
||||
padding: 20px 10px;
|
||||
}
|
||||
|
||||
.main .title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.main .name-con,
|
||||
.main .passwd-con {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.main .name-con {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.main .passwd-con {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.main .name-con .user-name-text,
|
||||
.main .passwd-con .user-passwd-text {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.main .login-con {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
:root {
|
||||
--listMargin: 15%;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.top-con {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.top-con >*{
|
||||
margin-left: 25px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.top-con div button {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.feature-list {
|
||||
width: 150px;
|
||||
height: 100%;
|
||||
background-color: #FFFFCC;
|
||||
padding: 0px 10px 0px 10px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.feature-list .item {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #0066cc;
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-top: var(--listMargin);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pages {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<link rel="icon" href="data:;base64,=">
|
||||
</head>
|
||||
<body>
|
||||
log
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<link rel="icon" href="data:;base64,=">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='features/module.css') }}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="top-bar-con">
|
||||
<button class="newBtn">新建</button>
|
||||
<button class="flushBtn">刷新</button>
|
||||
</div>
|
||||
<table border="1">
|
||||
<thead>
|
||||
<td>模块名</td>
|
||||
<td>操作</td>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in modules %}
|
||||
<tr>
|
||||
<td>{{ item }}</td>
|
||||
<td>
|
||||
<button class="removeBtn" onclick="alert('暂未完成')">移除</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,199 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<link rel="icon" href="data:;base64,=">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='features/process.css') }}">
|
||||
<!--
|
||||
<script>
|
||||
window.onload = function init() {
|
||||
const initReq = new XMLHttpRequest()
|
||||
const url = "http://qsont.xyz:8011/get-process"
|
||||
|
||||
initReq.open("get", url, true)
|
||||
initReq.send()
|
||||
addInProgessText()
|
||||
|
||||
initReq.onload = () => {
|
||||
if (initReq.status == 200 && initReq.readyState == 4) {
|
||||
const res = JSON.parse(initReq.responseText)
|
||||
let tBody = document.getElementById("tBody")
|
||||
|
||||
removeInProgessText()
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
const pid = res[i].pid
|
||||
const status = res[i].status
|
||||
const cmd = res[i].cmd
|
||||
let trEle = document.createElement("tr")
|
||||
trEle.innerHTML = `
|
||||
<td class="text">${pid}</td>
|
||||
<td class="text">${status}</td>
|
||||
<td class="text">${cmd}</td>
|
||||
<td><button class="stopBtn" onclick="stopBtn('${pid}')">stop</button></td>
|
||||
`
|
||||
tBody.append(trEle)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (initReq.status == 500) {
|
||||
alert("服务端错误")
|
||||
return
|
||||
} else {
|
||||
alert(`意外错误 (${initReq.status})`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
initReq.onerror = () => {
|
||||
alert(`无法获取进程列表 (${initReq.status})`)
|
||||
}
|
||||
|
||||
function addInProgessText() {
|
||||
let inProgressText = document.getElementById("inProgressText")
|
||||
const dotAmount = 3
|
||||
let currDotAmount = 0
|
||||
let dot = ""
|
||||
setInterval(() => {
|
||||
dot = ""
|
||||
for (let i = 0; i < currDotAmount; i++) {
|
||||
dot = dot + "."
|
||||
}
|
||||
inProgressText.innerText = `Loading${dot}`
|
||||
if (currDotAmount >= dotAmount) {
|
||||
dot = ""
|
||||
currDotAmount = 0
|
||||
} else {
|
||||
currDotAmount++
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
function removeInProgessText() {
|
||||
let inProgressText = document.getElementById("inProgressText")
|
||||
inProgressText.remove()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- stop, new flush -->
|
||||
<button class="newBtn" onclick="newBtn()">new</button>
|
||||
<button class="flushBtn" onclick="flushBtn()">flush</button>
|
||||
<table border="1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="title">PID</th>
|
||||
<th class="title">状态</th>
|
||||
<th class="title">命令</th>
|
||||
<th class="title">操作</th>
|
||||
</tr>
|
||||
{% for item in response %}
|
||||
<tr>
|
||||
<td>{{ item.pid }}</td>
|
||||
<td>{{ item.status }}</td>
|
||||
<td>{{ item.order }}</td>
|
||||
<td><button class="stopBtn" onclick="stopBtn('{{ item.pid }}')">stop</button></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</thead>
|
||||
<tbody id="tBody"></tbody>
|
||||
</table>
|
||||
<div id="inProgressText"></div>
|
||||
|
||||
<div class="modal-con" id="modal-con">
|
||||
<div class="window">
|
||||
<div class="top-bar">
|
||||
<div class="close-btn" onclick="closeModal()">X</div>
|
||||
</div>
|
||||
|
||||
<div class="input">
|
||||
<div class="prompt">输入模块名:</div>
|
||||
<input id="input-area" class="input-area" type="text">
|
||||
</div>
|
||||
<button class="create-btn" onclick="createProcess()">创建</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function newBtn() {
|
||||
const modal = document.getElementById("modal-con")
|
||||
modal.style.display = "flex"
|
||||
}
|
||||
|
||||
function createProcess() {
|
||||
const moduleName = document.getElementById("input-area").value
|
||||
const createReq = new XMLHttpRequest()
|
||||
const url = `{{ url_for('process.create_process', name=0) }}`.replace('0', moduleName);
|
||||
|
||||
createReq.open("post", url, true)
|
||||
createReq.send()
|
||||
|
||||
createReq.onload = () => {
|
||||
if (createReq.status == 200 && createReq.readyState == 4) {
|
||||
const res = JSON.parse(createReq.responseText)
|
||||
if (res.status == "success") {
|
||||
flushBtn()
|
||||
return
|
||||
}
|
||||
alert(`意外错误 (${createReq.status})`)
|
||||
return
|
||||
}
|
||||
|
||||
if (createReq.status == 500) {
|
||||
alert("服务端错误")
|
||||
return
|
||||
} else {
|
||||
alert(`意外错误 (${createReq.status})`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function flushBtn() {
|
||||
window.location = "{{ url_for('process.process') }}"
|
||||
}
|
||||
|
||||
function stopBtn(pid) {
|
||||
if (!confirm("确认停止该服务?")) {
|
||||
return
|
||||
}
|
||||
const url = `{{ url_for('process.kill_process', pid=0) }}`.replace('0', pid);
|
||||
const stopReq = new XMLHttpRequest();
|
||||
|
||||
stopReq.open("POST", url, true);
|
||||
stopReq.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
stopReq.send();
|
||||
|
||||
stopReq.onload = () => {
|
||||
if (stopReq.status == 200 && stopReq.readyState == 4) {
|
||||
flushBtn()
|
||||
return
|
||||
}
|
||||
|
||||
if (stopReq.status == 500) {
|
||||
alert("服务端错误")
|
||||
return
|
||||
} else {
|
||||
alert(`意外错误 (${stopReq.status})`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
const modal = document.getElementById("modal-con")
|
||||
const input = document.getElementById("input-area")
|
||||
|
||||
input.value = ""
|
||||
modal.style.display = "none"
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,162 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<link rel="icon" href="data:;base64,=">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='features/user.css') }}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="top-bar-con">
|
||||
<button class="addUserBtn" onclick="addUser()">添加用户</button>
|
||||
<button class="flushBtn" onclick="flush()">刷新</button>
|
||||
</div>
|
||||
|
||||
<table border="1">
|
||||
<thead>
|
||||
<td>用户ID</td>
|
||||
<td>用户名</td>
|
||||
<td>用户角色</td>
|
||||
<td>操作</td>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in response %}
|
||||
<tr>
|
||||
<td>{{ item.user_id}}</td>
|
||||
<td>{{ item.username }}</td>
|
||||
<td>{{ item.role }}</td>
|
||||
<td>
|
||||
{% if current_role == "admin" %}
|
||||
<button class="changePasswdBtn" onclick="changePasswd('{{ item.user_id }}')">修改密码</button>
|
||||
{% if item.username != "admin" %}
|
||||
<button class="deleteUserBtn" onclick="deleteUser('{{ item.user_id }}')">删除用户</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if current_role != "admin" and current_user_id == item.user_id %}
|
||||
<button class="changePasswdBtn" onclick="changePasswd('{{ item.user_id }}')">修改密码</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function addUser() {
|
||||
const url = "{{ url_for('user.add_user') }}"
|
||||
const addReq = new XMLHttpRequest()
|
||||
|
||||
let data = new URLSearchParams()
|
||||
// get user name
|
||||
const name = prompt("请输入用户名")
|
||||
if (name === null) return
|
||||
if (name === "") {
|
||||
alert("请输入用户名")
|
||||
return
|
||||
} else {
|
||||
data.append("username", name)
|
||||
}
|
||||
// get user password
|
||||
const passwd = prompt("请输入密码")
|
||||
if (passwd === null) return
|
||||
if (passwd === "") {
|
||||
alert("请输入密码")
|
||||
return
|
||||
} else {
|
||||
data.append("password", passwd)
|
||||
}
|
||||
|
||||
addReq.open("post", url, true)
|
||||
addReq.send(data)
|
||||
|
||||
addReq.onload = () => {
|
||||
if (addReq.status == 200 && addReq.readyState == 4) {
|
||||
alert(JSON.parse(addReq.responseText).message)
|
||||
flush()
|
||||
return
|
||||
}
|
||||
|
||||
if (addReq.status == 500) {
|
||||
alert("服务端错误")
|
||||
return
|
||||
} else {
|
||||
alert(`意外错误 (${addReq.status})`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function changePasswd(id) {
|
||||
const url = "{{ url_for('user.change_password') }}"
|
||||
const changeReq = new XMLHttpRequest()
|
||||
|
||||
let data = new URLSearchParams()
|
||||
data.append("user_id", id)
|
||||
|
||||
// get user password
|
||||
const passwd = prompt("请输入新密码")
|
||||
if (passwd === null) return
|
||||
if (passwd === "") {
|
||||
alert("请输入密码")
|
||||
return
|
||||
} else {
|
||||
data.append("new_password", passwd)
|
||||
}
|
||||
|
||||
changeReq.open("post", url, true)
|
||||
changeReq.send(data)
|
||||
changeReq.onload = () => {
|
||||
if (changeReq.status == 200 && changeReq.readyState == 4) {
|
||||
alert(JSON.parse(changeReq.responseText).message)
|
||||
flush()
|
||||
return
|
||||
}
|
||||
|
||||
if (changeReq.status == 500) {
|
||||
alert("服务端错误")
|
||||
return
|
||||
} else {
|
||||
alert(`意外错误 (${changeReq.status})`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deleteUser(id) {
|
||||
if (!confirm("删除用户后不可恢复,确认继续?")) {
|
||||
return
|
||||
}
|
||||
const url = "{{ url_for('user.delete_user', user_id=0) }}".replace("0", id)
|
||||
const delReq = new XMLHttpRequest()
|
||||
|
||||
delReq.open("post", url, true)
|
||||
delReq.send()
|
||||
|
||||
delReq.onload = () => {
|
||||
if (delReq.status == 200 && delReq.readyState == 4) {
|
||||
alert(JSON.parse(delReq.responseText).message)
|
||||
flush()
|
||||
return
|
||||
}
|
||||
|
||||
if (delReq.status == 500) {
|
||||
alert("服务端错误")
|
||||
return
|
||||
} else {
|
||||
alert(`意外错误 (${delReq.status})`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function flush() {
|
||||
window.location = "{{ url_for('user.user') }}"
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,88 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>pmc management</title>
|
||||
<link rel="icon" href="data:;base64,=">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="main">
|
||||
<div class="title">Welcome to pmc management system</div>
|
||||
<div class="name-con">
|
||||
<div class="user-name-text">Name: </div>
|
||||
<input class="user-name-input" id="user-name" type="text">
|
||||
</div>
|
||||
<div class="passwd-con">
|
||||
<div class="user-passwd-text">Password: </div>
|
||||
<input class="user-passwd-input" id="user-passwd" type="password">
|
||||
</div>
|
||||
<div class="login-con">
|
||||
<button onclick="login()">login</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function login() {
|
||||
const name = document.getElementById("user-name").value
|
||||
const passwd = document.getElementById("user-passwd").value
|
||||
const request = new XMLHttpRequest()
|
||||
const REQUEST_URL = "/api/login"
|
||||
|
||||
// if user not input any information
|
||||
if (name === "" || passwd === "") {
|
||||
alert("please input name or passwd")
|
||||
return
|
||||
}
|
||||
|
||||
// request api
|
||||
let requestData = new URLSearchParams()
|
||||
requestData.append("user-name", name)
|
||||
requestData.append("user-passwd", passwd)
|
||||
request.open("post", REQUEST_URL, true)
|
||||
request.send(requestData)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 401) {
|
||||
alert(request.responseText)
|
||||
return
|
||||
}
|
||||
|
||||
if (request.status === 404) {
|
||||
alert(request.responseText)
|
||||
return
|
||||
}
|
||||
|
||||
if (request.status === 500) {
|
||||
alert("服务端错误")
|
||||
return
|
||||
}
|
||||
|
||||
if (request.status === 200 && request.readyState === 4) {
|
||||
const token = JSON.parse(request.responseText).token
|
||||
let date = new Date()
|
||||
date.setTime(date.getTime() + (3 * 24 * 60 * 60 * 1000))
|
||||
const UTCTime = date.toUTCString()
|
||||
document.cookie = `token=${token}; path=/; expires=${UTCTime}`
|
||||
window.location = JSON.parse(request.responseText).redirect;
|
||||
return
|
||||
} else {
|
||||
alert(`意外错误 (${request.status})`)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key != "Enter") {
|
||||
return
|
||||
}
|
||||
login()
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>management page</title>
|
||||
<link rel="icon" href="data:;base64,=">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='mgmt.css') }}">
|
||||
|
||||
<script>
|
||||
window.onload = () => {
|
||||
const featureList = [
|
||||
{
|
||||
name: "进程",
|
||||
url: "{{ url_for('process.process') }}"
|
||||
},
|
||||
{
|
||||
name: "模块",
|
||||
url: "{{ url_for('module.module') }}"
|
||||
},
|
||||
// {
|
||||
// name: "日志",
|
||||
// url: "{{ url_for('log.log') }}"
|
||||
// },
|
||||
{
|
||||
name: "用户",
|
||||
url: "{{ url_for('user.user') }}"
|
||||
}
|
||||
]
|
||||
|
||||
const featureListEle = document.getElementById("feature-list")
|
||||
for (let i = 0; i < featureList.length; i++) {
|
||||
// get name and url
|
||||
const name = featureList[i].name
|
||||
const url = featureList[i].url
|
||||
|
||||
// Create a item
|
||||
let listItemEle = document.createElement("div")
|
||||
listItemEle.setAttribute("class", "item")
|
||||
listItemEle.innerText = name
|
||||
listItemEle.setAttribute("onclick", `switchPage('${url}')`)
|
||||
|
||||
// append element into feature list
|
||||
featureListEle.append(listItemEle)
|
||||
}
|
||||
|
||||
// set default page in iframe
|
||||
const DEFAULT_PAGE = "<!DOCTYPE html><h1>请在左侧选择功能</h1>"
|
||||
document.getElementById("page").contentDocument.write(DEFAULT_PAGE)
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="top-con">
|
||||
<div id="current-user">当前用户: {{ username }}</div>
|
||||
<div id="current-role">当前角色: {{ role }}</div>
|
||||
<div class="button-con">
|
||||
<button onclick="logout()">退出登陆</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="feature-list" id="feature-list"></div>
|
||||
<div class="pages">
|
||||
<iframe id="page" src="" frameborder="1" width="100%" height="100%"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// change the iframe's url
|
||||
function switchPage(url) {
|
||||
const page = document.getElementById("page")
|
||||
page.setAttribute("src", url)
|
||||
}
|
||||
|
||||
function logout() {
|
||||
if (!confirm("确认退出?")) {
|
||||
return
|
||||
}
|
||||
document.cookie = "token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
||||
window.location = "/"
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in new issue
Block a user