Files
mixio/ejs/manage.ejs
2025-09-06 23:55:54 +08:00

637 lines
34 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>MixIO管理后台</title>
<link rel="shortcut icon" href="img/shortcut.png"/>
<link rel="stylesheet" href="css/all.css">
<script src="js/jquery.min.js"></script>
<script src="js/lang.js?v=5"></script>
<script src="js/farbtastic.js"></script>
<script src="js/bootstrap.bundle.min.js"></script>
<script src="js/jquery.easing.min.js"></script>
<script src="js/echarts.min.js"></script>
<script src="js/mqtt.min.js"></script>
<link rel="stylesheet" href="css/dataTables.bootstrap4.min.css">
<script src="js/jquery.dataTables.min.js"></script>
<script src="js/dataTables.bootstrap4.min.js"></script>
<script src="js/tools.js?v=14"></script>
<script src="js/manage.js"></script>
</head>
<body id="page-top" class="sidebar-toggled" style="position:relative;overflow-x:hidden;color: black;">
<div id="wrapper">
<ul class="navbar-nav bg-gradient-primary sidebar sidebar-dark accordion" id="accordionSidebar">
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="index">
<div class="sidebar-brand-icon">
<i class="fa fa-cog"></i>
</div>
<div class="sidebar-brand-text mx-3" >MixIO Admin</div>
</a>
<hr class="sidebar-divider my-0">
<hr class="sidebar-divider">
<div class="sidebar-heading lang" key="MANAGE">
</div>
<li class="nav-item active" id="view1">
<a class="nav-link" onclick="view(1)" style="cursor: pointer;" id="manage_prj">
<i class="fa fa-fw fa-tachometer"></i>
<span class="lang" key="BASICADMIN"></span></a>
</li>
<li class="nav-item" id="view2">
<a class="nav-link" onclick="view(2)" style="cursor: pointer;" id="manage_data">
<i class="fa fa-fw fa-database"></i>
<span class="lang" key="DATAADMIN"></span></a>
</li>
<li class="nav-item" id="view3">
<a class="nav-link" onclick="view(3)" style="cursor: pointer;" id="manage_strg">
<i class="fa fa-fw fa-user"></i>
<span class="lang" key="USERADMIN"></span></a>
</li>
<hr class="sidebar-divider">
</ul>
<div id="content-wrapper" class="d-flex flex-column">
<div id="content">
<nav class="navbar navbar-expand navbar-light bg-white topbar static-top shadow" style="display:flex;justify-content:space-between" id="project_nav">
<h1 class="d-sm-inline-block h3 mb-0 text-gray-800 lang" key="ADMIN" id="title" style="margin-left:10px;font-size:1.25rem;display:inline-block;"></h1>
</nav>
<div class="container-fluid" id="detail">
<div id="detail1" class="row">
<div class="col-xl-4 col-md-6 mb-4">
<div class="card shadow" style="margin-top:1.5rem;border-radius:10px">
<div class="card-body">
<div class="card border-left-primary h-100 py-2" style="margin-bottom:15px">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
平台类型</div>
<div class="h5 mb-0 font-weight-bold text-gray-800" id="platform"><%=platform%></div>
</div>
<div class="col-auto">
<i class="fa fa-code fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
<div class="card border-left-primary h-100 py-2" style="margin-bottom:15px">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
MixIO版本号</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><%=version%></div>
</div>
<div class="col-auto">
<i class="fa fa-code fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
<div class="card border-left-primary h-100 py-2" style="margin-bottom:15px">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
Mixly编程服务器版本号</div>
<div class="h5 mb-0 font-weight-bold text-gray-800"><%=versionmixly%></div>
</div>
<div class="col-auto">
<i class="fa fa-code fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
<div class="card border-left-primary h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
服务器时钟</div>
<div class="h5 mb-0 font-weight-bold text-gray-800" id="time"></div>
</div>
<div class="col-auto">
<i class="fa fa-hourglass-half fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
<div class="row" style="display:flex;align-items:center;justify-content:center">
<button class="btn btn-lg btn-primary" style="margin-top:20px;margin-right:10px" onclick="fresh('mixio', '<%=platform%>')"><i class="fa fa-refresh"></i> 更新MixIO服务器</button>
</div>
<div class="row" style="display:flex;align-items:center;justify-content:center">
<button class="btn btn-lg btn-primary" style="margin-top:20px;margin-right:10px" onclick="fresh('mixly', 'all')"><i class="fa fa-refresh"></i> 更新Mixly编程服务器</button>
<!--
<% if (status=="运行中") { %>
<button class="btn btn-lg btn-danger" style="margin-top:20px;margin-left:10px" onclick="stopServer()"><i class="fa fa-ban"></i> 暂停服务器</button>
<%} else { %>
<button class="btn btn-lg btn-success" style="margin-top:20px;margin-left:10px" onclick="startServer()"><i class="fa fa-play"></i> 启动服务器</button>
<% } %>
-->
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-md-6 mb-4">
<div class="card shadow" style="margin-top:1.5rem;border-radius:10px">
<div class="card-body" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
<table>
<tr>
<td>
<span>管理员账号:</span>
</td>
<td>
<input type="text" id="ADMIN_USERNAME">
</td>
</tr>
<tr>
<td>
<span>管理员密码:</span>
</td>
<td>
<input type="password" id="ADMIN_PASSWORD">
</td>
<tr>
<td>
<span>离线模式:</span>
</td>
<td>
<input type="checkbox" id="OFFLINE_MODE">
</td>
</tr>
<tr>
<td>
<span>允许自助注册:</span>
</td>
<td>
<input type="checkbox" id="ALLOW_REGISTER">
</td>
</tr>
<tr>
<td>
<span>允许离线数据:</span>
</td>
<td>
<input type="checkbox" id="ALLOW_HOOK">
</td>
</tr>
<tr>
<td>
<span>HTTP 端口:</span>
</td>
<td>
<input type="number" id="MIXIO_HTTP_PORT">
</td>
</tr>
<tr>
<td>
<span>HTTPS 端口:</span>
</td>
<td>
<input type="number" id="MIXIO_HTTPS_PORT">
</td>
</tr>
<tr>
<td>
<span>MQTT 端口:</span>
</td>
<td>
<input type="number" id="MIXIO_MQTT_PORT">
</td>
</tr>
<tr>
<td>
<span>WS 端口:</span>
</td>
<td>
<input type="number" id="MIXIO_WS_PORT">
</td>
</tr>
<tr>
<td>
<span>WSS 端口:</span>
</td>
<td>
<input type="number" id="MIXIO_WSS_PORT">
</td>
</tr>
<tr>
<td>
<span>SSL证书私钥</span>
</td>
<td>
<input type="text" id="HTTPS_PRIVATE_PEM">
</td>
</tr>
<tr>
<td>
<span>SSL证书公钥</span>
</td>
<td>
<input type="text" id="HTTPS_CRT_FILE">
</td>
</tr>
<tr>
<td>
<span>单用户最大项目数:</span>
</td>
<td>
<input type="text" id="MAX_PROJECT_NUM_PER_USER">
</td>
</tr>
<tr>
<td>
<span>单用户最大消息数:</span>
</td>
<td>
<input type="text" id="MAX_MESSAGE_PER_USER">
</td>
</tr>
<tr>
<td>
<span>消息频率限制(次/秒):</span>
</td>
<td>
<input type="text" id="MAX_MESSAGE_PER_SECOND">
</td>
</tr>
<tr>
<td>
<span>百度地图开发者AK客户端应用</span>
</td>
<td>
<input type="text" id="BAIDU_MAP_AK">
</td>
</tr>
<tr>
<td>
<span>百度地图开发者AK服务端应用</span>
</td>
<td>
<input type="text" id="BAIDU_MAP_SERVER_AK">
</td>
</tr>
<tr>
<td>
<span>百度统计链接AK</span>
</td>
<td>
<input type="text" id="BAIDU_STAT_LINK">
</td>
</tr>
</table>
<button class="btn btn-lg btn-success" style="margin-top:20px;" onclick="saveAndRestart()" id="saveAndRestart"><i class="fa fa-check"></i> 保存设置并重新启动</button>
</div>
</div>
</div>
</div>
<div id="detail3" hidden>
<div class="col-xl-4 col-md-6 mb-4">
<div class="card shadow" style="margin-top:1.5rem;border-radius:10px">
<div class="card-body" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
<p>格式(每个账号一行): 账号,密码,密保问题,问题答案</p>
<textarea name="" id="acc" rows="10" style="margin-bottom: 20px;width:95%">12345@mixly.com,123456,你就读的班级是?,三年二班</textarea>
<div>
<button class="btn btn-lg btn-success" onclick="addAcc()"><i class="fa fa-check"></i> 确定添加</button>
</div>
</div>
</div>
</div>
</div>
<div id="detail2" hidden>
<div class="col-xl-8 col-md-6 mb-4">
<div class="card shadow" style="margin-top:1.5rem;border-radius:10px">
<div class="card-body" >
<table id="table">
<thead>
<td style="min-width:100px">
用户
</td>
<td style="min-width:100px">
项目数
</td>
<td style="min-width:100px">
消息量
</td>
<td style="min-width:100px">
执行操作
</td>
</thead>
<tbody id="tbody">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
input{
min-width:0!important;
height:30px
}
td{
min-height: 30px;
}
</style>
<script>
var clearMessage = function(userName){
$.get('clearMessage',{
"userName":userName
},function(res){
if(res == 1)
{
showtext("操作成功!")
setTimeout(function(){
window.location.href = window.location.href
},1000)
}
else
{
showtext("操作失败")
}
})
}
$("#time").html(new Date().toLocaleTimeString())
setInterval(() => {
$("#time").html(new Date().toLocaleTimeString())
}, 1000);
var MIXIO_HTTP_PORT = "<%=configs["MIXIO_HTTP_PORT"]%>"
var MIXIO_HTTPS_PORT = "<%=configs["MIXIO_HTTPS_PORT"]%>"
var MIXIO_MQTT_PORT = "<%=configs["MIXIO_MQTT_PORT"]%>"
var MIXIO_WS_PORT = "<%=configs["MIXIO_WS_PORT"]%>"
var MIXIO_WSS_PORT = "<%=configs["MIXIO_WSS_PORT"]%>"
var HTTPS_PRIVATE_PEM = "<%=configs["HTTPS_PRIVATE_PEM"]%>"
var HTTPS_CRT_FILE = "<%=configs["HTTPS_CRT_FILE"]%>"
var MAX_PROJECT_NUM_PER_USER = "<%=configs["MAX_PROJECT_NUM_PER_USER"]%>"
var MAX_MESSAGE_PER_USER = "<%=configs["MAX_MESSAGE_PER_USER"]%>"
var MAX_MESSAGE_PER_SECOND= "<%=configs["MAX_MESSAGE_PER_SECOND"]%>"
var ALLOW_REGISTER = <%=configs["ALLOW_REGISTER"]%>
var ALLOW_HOOK = <%=configs["ALLOW_HOOK"]%>
var OFFLINE_MODE = <%=configs["OFFLINE_MODE"]%>
var BAIDU_MAP_AK = "<%=configs["BAIDU_MAP_AK"]%>"
var BAIDU_MAP_SERVER_AK = "<%=configs["BAIDU_MAP_SERVER_AK"]%>"
var BAIDU_STAT_LINK = "<%=configs["BAIDU_STAT_LINK"]%>"
var ADMIN_USERNAME = "<%=configs["ADMIN_USERNAME"]%>"
var ADMIN_PASSWORD = "<%=configs["ADMIN_PASSWORD"]%>"
$("#OFFLINE_MODE").bind('change',function(){
if($("#OFFLINE_MODE").prop("checked"))
{
$("#BAIDU_MAP_AK").attr("disabled","true")
$("#BAIDU_MAP_SERVER_AK").attr("disabled","true")
}
else
{
$("#BAIDU_MAP_AK").removeAttr("disabled")
$("#BAIDU_MAP_SERVER_AK").removeAttr("disabled")
}
})
var loadConfig = function(){
$("#MIXIO_HTTP_PORT").val(MIXIO_HTTP_PORT)
$("#MIXIO_HTTPS_PORT").val(MIXIO_HTTPS_PORT)
$("#MIXIO_MQTT_PORT").val(MIXIO_MQTT_PORT)
$("#MIXIO_WS_PORT").val(MIXIO_WS_PORT)
$("#MIXIO_WSS_PORT").val(MIXIO_WSS_PORT)
$("#HTTPS_PRIVATE_PEM").val(HTTPS_PRIVATE_PEM)
$("#HTTPS_CRT_FILE").val(HTTPS_CRT_FILE)
$("#ALLOW_REGISTER").prop("checked",ALLOW_REGISTER)
$("#ALLOW_HOOK").prop("checked",ALLOW_HOOK)
$("#OFFLINE_MODE").prop("checked",OFFLINE_MODE)
$("#BAIDU_MAP_AK").val(BAIDU_MAP_AK)
$("#BAIDU_MAP_SERVER_AK").val(BAIDU_MAP_SERVER_AK)
$("#MAX_PROJECT_NUM_PER_USER").val(MAX_PROJECT_NUM_PER_USER)
$("#MAX_MESSAGE_PER_USER").val(MAX_MESSAGE_PER_USER)
$("#MAX_MESSAGE_PER_SECOND").val(MAX_MESSAGE_PER_SECOND)
$("#ADMIN_USERNAME").val(ADMIN_USERNAME)
$("#ADMIN_PASSWORD").val(ADMIN_PASSWORD)
$("#BAIDU_STAT_LINK").val(BAIDU_STAT_LINK)
if($("#OFFLINE_MODE").prop("checked"))
{
$("#BAIDU_MAP_AK").attr("disabled","true")
$("#BAIDU_MAP_SERVER_AK").attr("disabled","true")
}
else
{
$("#BAIDU_MAP_AK").removeAttr("disabled")
$("#BAIDU_MAP_SERVER_AK").removeAttr("disabled")
}
}
loadConfig()
var saveAndRestart = function(){
var modald = showmodaltext("<div style='text-align:center'><i class='fa fa-spin fa-cog' style='font-size:2rem;color:#4e73df'></i><p style='margin-top:6px;margin-bottom:0;color:#4e73df;font-size:1rem;font-weight:bold'>"+JSLang[lang].loading2+"</p></div>")
configs = {}
configs["MIXIO_HTTP_PORT"] = parseInt($("#MIXIO_HTTP_PORT").val())
configs["MIXIO_HTTPS_PORT"] = parseInt($("#MIXIO_HTTPS_PORT").val())
configs["MIXIO_MQTT_PORT"] = parseInt($("#MIXIO_MQTT_PORT").val())
configs["MIXIO_WS_PORT"] = parseInt($("#MIXIO_WS_PORT").val())
configs["MIXIO_WSS_PORT"] = parseInt($("#MIXIO_WSS_PORT").val())
configs["HTTPS_PRIVATE_PEM"] = $("#HTTPS_PRIVATE_PEM").val()
configs["HTTPS_CRT_FILE"] = $("#HTTPS_CRT_FILE").val()
configs["MAX_PROJECT_NUM_PER_USER"] = parseInt($("#MAX_PROJECT_NUM_PER_USER").val())
configs["MAX_MESSAGE_PER_USER"] = parseInt($("#MAX_MESSAGE_PER_USER").val())
configs["MAX_MESSAGE_PER_SECOND"] = parseInt($("#MAX_MESSAGE_PER_SECOND").val())
configs["ALLOW_REGISTER"] = $("#ALLOW_REGISTER").prop("checked")
configs["ALLOW_HOOK"] = $("#ALLOW_HOOK").prop("checked")
configs["OFFLINE_MODE"] = $("#OFFLINE_MODE").prop("checked")
configs["BAIDU_MAP_AK"] = $("#BAIDU_MAP_AK").val()
configs["BAIDU_MAP_SERVER_AK"] = $("#BAIDU_MAP_SERVER_AK").val()
configs["ADMIN_USERNAME"] = $("#ADMIN_USERNAME").val()
configs["ADMIN_PASSWORD"] = $("#ADMIN_PASSWORD").val()
configs["BAIDU_STAT_LINK"] = $("#BAIDU_STAT_LINK").val()
$.get('/saveAndRestart',{'configs':JSON.stringify(configs,null,2)},function(res){
if(res=="1")
{
showtext('保存成功, 正在重新启动服务器。')
}
else
showtext('保存失败')
modald.close()
})
}
var stopServer = function(){
var modald = showmodaltext("<div style='text-align:center'><i class='fa fa-spin fa-cog' style='font-size:2rem;color:#4e73df'></i><p style='margin-top:6px;margin-bottom:0;color:#4e73df;font-size:1rem;font-weight:bold'>"+JSLang[lang].loading2+"</p></div>")
$.get('/stop',function(res){
modald.close()
if(res=="1")
{
window.location.href = window.location.href
}
else
showtext('状态异常')
})
}
var startServer = function(){
var modald = showmodaltext("<div style='text-align:center'><i class='fa fa-spin fa-cog' style='font-size:2rem;color:#4e73df'></i><p style='margin-top:6px;margin-bottom:0;color:#4e73df;font-size:1rem;font-weight:bold'>"+JSLang[lang].loading2+"</p></div>")
$.get('/start',function(res){
modald.close()
if(res=="1")
{
window.location.href = window.location.href
}
})
}
var fresh = function(product, platform){
var textarea = $("<textarea readonly cols='50' rows='20' style='color:white; background-color:black'></textarea>")
var add_text = function(text){
textarea.text(textarea.text() + text + "\n")
textarea.scrollTop(textarea[0].scrollHeight);
}
var d = dialog({
content: textarea
});
d.showModal();
add_text("发送更新请求中...")
fetch('/api/check-update', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
product: product,
platform: platform
})
})
.then((response) => response.json())
.then((result) => {
add_text("本地版本:" + result["localVersion"])
add_text("云端版本:" + result["cloudVersion"])
add_text("需要更新:" + result["needsUpdate"])
var url = result["cloudFile"]
var error = result["error"]
if(error)
{
add_text(error)
}
if(result["needsUpdate"])
{
const eventSource = new EventSource(`/api/download?url=${encodeURIComponent(url)}&cloudVersion=${result["cloudVersion"]}`);
add_text("下载开始...")
eventSource.onmessage = function(event) {
console.log(event)
const data = JSON.parse(event.data);
console.log(data)
if (data.type === 'progress') {
add_text(`下载中: ${data.progress}%`)
}
else if (data.type === 'unzip') {
add_text(`解压中...`)
}
else if (data.type === 'complete') {
add_text(`${data.version}更新完成!`);
eventSource.close();
if(data.version == "mixio"){
setTimeout(
function(){
window.location.reload()
}
, 10000)
alert("更新已完成服务器将在10秒左右自动重启")
}
add_text('5秒后自动退出...');
setTimeout(function(){
d.close()
}, 5000)
window.location.reload()
}
};
eventSource.onerror = function(error) {
console.log(error)
add_text('下载失败5秒后自动退出...');
setTimeout(function(){
d.close()
}, 5000)
eventSource.close();
};
}
else
{
add_text("已经是最新版本5秒后自动退出...")
setTimeout(function(){
d.close()
}, 5000)
}
})
.catch((error) => {
add_text(error);
});
}
var view = function(index){
a = [1,2,3]
for (i in a){
v = a[i]
if (index==v){
$("#detail"+v).removeAttr("hidden")
$("#view"+v).addClass("active")
}
else
{
$("#detail"+v).attr("hidden","hidden")
$("#view"+v).removeClass("active")
}
}
}
var addAcc = function(){
var modald = showmodaltext("<div style='text-align:center'><i class='fa fa-spin fa-cog' style='font-size:2rem;color:#4e73df'></i><p style='margin-top:6px;margin-bottom:0;color:#4e73df;font-size:1rem;font-weight:bold'>"+JSLang[lang].loading2+"</p></div>")
var acc = $("#acc").val()
accs = acc.split('\n')
acct = []
for(acc in accs){
if(accs[acc]!="")
acct.push(accs[acc])
}
var successCount = 0
var failCount = 0
var failInfo = []
for(i in acct){
info = acct[i].split(',')
if(info.length!=4)
{
failCount+=1
failInfo.push({
'account':info[0],
'reason':'格式不正确'
})
}
else
{
$.get('addAccount',{'userName':info[0],'password':info[1],'question':info[2],'answer':info[3]},function(res){
modald.close()
if(res=='1')
successCount+=1
else if(res=='2')
{
failCount+=1
failInfo.push({
'account':info[0],
'reason':'用户已存在'
})
}
else if(res=='3')
{
failCount+=1
failInfo.push({
'account':info[0],
'reason':'连接失败'
})
}
var s = "成功个数:"+successCount+"; 失败个数:"+failCount+"; 失败原因:"+JSON.stringify(failInfo)
showtext(s)
})
}
}
}
</script>
</body>