mirror of https://github.com/flucont/btcloud.git

31 changed files with 6478 additions and 128 deletions
-
6README.md
-
52app/command/CleanViteJs.php
-
52app/command/DecryptFile.php
-
8app/common.php
-
17app/controller/Admin.php
-
76app/controller/Api.php
-
11app/lib/BtPlugins.php
-
41app/lib/Plugins.php
-
16app/lib/ThirdPlugins.php
-
6app/script/convert.sh
-
3app/view/admin/layout.html
-
276app/view/admin/pluginsen.html
-
124app/view/admin/set.html
-
54app/view/index/download.html
-
3install.sql
-
2public/install/install_6.0.sh
-
2027public/install/install_7.0_en.sh
-
2033public/install/install_pro_en.sh
-
BINpublic/install/src/panel6.zip
-
BINpublic/install/src/panel_7_en.zip
-
BINpublic/install/update/LinuxPanel-9.3.0.zip
-
BINpublic/install/update/LinuxPanel_EN-7.0.13.zip
-
2public/install/update6.sh
-
1017public/install/update_7.x_en.sh
-
BINpublic/static/file/en/kaixin.zip
-
BINpublic/static/file/kaixin.zip
-
BINpublic/static/images/aapanel.png
-
17route/app.php
-
122wiki/aapanel.md
-
408wiki/files/aapanel/PluginLoader.py
-
83wiki/files/aapanel/bt.js
@ -0,0 +1,276 @@ |
|||||
|
{extend name="admin/layout" /} |
||||
|
{block name="title"}插件列表{/block} |
||||
|
{block name="main"} |
||||
|
<style> |
||||
|
td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:340px;} |
||||
|
.bt-ico-ask { |
||||
|
border: 1px solid #fb7d00; |
||||
|
border-radius: 8px; |
||||
|
color: #fb7d00; |
||||
|
cursor: help; |
||||
|
display: inline-block; |
||||
|
font-family: arial; |
||||
|
font-size: 11px; |
||||
|
font-style: normal; |
||||
|
height: 16px; |
||||
|
line-height: 16px; |
||||
|
margin-left: 5px; |
||||
|
text-align: center; |
||||
|
width: 16px |
||||
|
} |
||||
|
.bt-ico-ask:hover { |
||||
|
background-color: #fb7d00; |
||||
|
color: #fff |
||||
|
} |
||||
|
</style> |
||||
|
<div class="modal fade" id="help" tabindex="-1" role="dialog"> |
||||
|
<div class="modal-dialog" role="document"> |
||||
|
<div class="modal-content"> |
||||
|
<div class="modal-header"> |
||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> |
||||
|
<span aria-hidden="true">×</span> |
||||
|
</button> |
||||
|
<h4 class="modal-title">帮助</h4> |
||||
|
</div> |
||||
|
<div class="modal-body"> |
||||
|
<p>“版本与状态”一列中,红色的按钮代表本地不存在该版本插件包,需要点击下载;绿色的按钮代表已存在。</p> |
||||
|
<p>官方插件包本地存储路径是/data/en/plugins/package/软件标识-版本号.zip,第三方插件包路径是/data/plugins/other/other/,对于部分包含二次验证的插件可以自行修改。</p> |
||||
|
</div> |
||||
|
<div class="modal-footer"> |
||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="container" style="padding-top:70px;"> |
||||
|
<div class="col-xs-12 center-block" style="float: none;"> |
||||
|
|
||||
|
<div id="searchToolbar"> |
||||
|
<form onsubmit="return searchSubmit()" method="GET" class="form-inline"> |
||||
|
<div class="form-group"> |
||||
|
<label>搜索</label> |
||||
|
<input type="text" class="form-control" name="keyword" placeholder="应用名称"> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<select name="type" class="form-control"><option value="0">全部插件</option> |
||||
|
{foreach $typelist as $k=>$v}<option value="{$k}">{$v}</option>{/foreach} </select> |
||||
|
</div> |
||||
|
<div class="form-group"> |
||||
|
<button class="btn btn-primary" type="submit"><i class="fa fa-search"></i> 搜索</button> |
||||
|
<a href="javascript:searchClear()" class="btn btn-default"><i class="fa fa-repeat"></i> 重置</a> |
||||
|
<a href="javascript:refresh_plugins()" class="btn btn-success"><i class="fa fa-refresh"></i> 刷新列表</a> |
||||
|
<a href="javascript:download_plugins()" class="btn btn-warning" id="batch_down" style="display:none;"><i class="fa fa-download"></i> 批量下载</a> |
||||
|
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#help"><i class="fa fa-info-circle"></i> 帮助</button> |
||||
|
</div> |
||||
|
</form> |
||||
|
</div> |
||||
|
|
||||
|
<table id="listTable"> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
<script src="{$cdnpublic}layer/3.5.1/layer.js"></script> |
||||
|
<script src="{$cdnpublic}bootstrap-table/1.19.1/bootstrap-table.min.js"></script> |
||||
|
<script src="{$cdnpublic}bootstrap-table/1.19.1/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script> |
||||
|
<script src="/static/js/custom.js"></script> |
||||
|
<script> |
||||
|
|
||||
|
function download_version(name, version, status){ |
||||
|
if(status == true){ |
||||
|
var confirm = layer.confirm('是否确定重新下载'+version+'版本插件包?', { |
||||
|
btn: ['确定','取消'] |
||||
|
}, function(){ |
||||
|
download_plugin(name, version) |
||||
|
}, function(){ |
||||
|
layer.close(confirm) |
||||
|
}); |
||||
|
}else{ |
||||
|
download_plugin(name, version) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function download_plugin(name, version){ |
||||
|
var ii = layer.msg('正在下载,请稍候...', {icon: 16, shade:0.1, time: 0}); |
||||
|
$.ajax({ |
||||
|
type : 'POST', |
||||
|
url : '/admin/download_plugin', |
||||
|
data: { name:name, version:version, os:'en'}, |
||||
|
dataType : 'json', |
||||
|
success : function(data) { |
||||
|
layer.close(ii) |
||||
|
if(data.code == 0){ |
||||
|
layer.alert(data.msg, {icon:1}, function(){layer.closeAll();searchSubmit();}); |
||||
|
}else{ |
||||
|
layer.alert(data.msg, {icon:2}); |
||||
|
} |
||||
|
}, |
||||
|
error:function(data){ |
||||
|
layer.close(ii) |
||||
|
layer.msg('服务器错误', {icon:2}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function refresh_plugins(){ |
||||
|
var confirm = layer.confirm('是否确定从宝塔官方获取最新插件列表?', { |
||||
|
btn: ['确定','取消'] |
||||
|
}, function(){ |
||||
|
layer.close(confirm) |
||||
|
var ii = layer.msg('正在获取插件列表,请稍候...', {icon: 16, shade:0.1, time: 0}); |
||||
|
$.ajax({ |
||||
|
type : 'GET', |
||||
|
url : '/admin/refresh_plugins?os=en', |
||||
|
dataType : 'json', |
||||
|
success : function(data) { |
||||
|
layer.close(ii) |
||||
|
if(data.code == 0){ |
||||
|
layer.alert(data.msg, {icon:1}, function(){layer.closeAll();searchSubmit();}); |
||||
|
}else{ |
||||
|
layer.alert(data.msg, {icon:2}); |
||||
|
} |
||||
|
}, |
||||
|
error:function(data){ |
||||
|
layer.close(ii) |
||||
|
layer.msg('服务器错误', {icon:2}); |
||||
|
} |
||||
|
}); |
||||
|
}, function(){ |
||||
|
layer.close(confirm) |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function download_plugins(){ |
||||
|
var confirm = layer.confirm('批量下载当前分类下未下载的插件包', { |
||||
|
btn: ['确定','取消'] |
||||
|
}, function(){ |
||||
|
layer.close(confirm) |
||||
|
$.downloadCount = 0; |
||||
|
$.preDownloadCount = $.preDownload.length; |
||||
|
download_item(); |
||||
|
}, function(){ |
||||
|
layer.close(confirm) |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function download_item(){ |
||||
|
if($.preDownload.length == 0){ |
||||
|
layer.alert('成功下载'+$.downloadCount+'个插件包!', {icon:1}, function(){layer.closeAll();searchSubmit();}); |
||||
|
return; |
||||
|
} |
||||
|
$.downloadCount++; |
||||
|
var plugin = $.preDownload[0]; |
||||
|
var ii = layer.msg('['+$.downloadCount+'/'+$.preDownloadCount+']正在下载'+plugin.name+'-'+plugin.version, {icon: 16, shade:0.1, time: 0}); |
||||
|
$.ajax({ |
||||
|
type : 'POST', |
||||
|
url : '/admin/download_plugin', |
||||
|
data: { name:plugin.name, version:plugin.version, os:'en'}, |
||||
|
dataType : 'json', |
||||
|
success : function(data) { |
||||
|
layer.close(ii) |
||||
|
if(data.code == 0){ |
||||
|
$.preDownload.shift(); |
||||
|
download_item(); |
||||
|
}else{ |
||||
|
layer.alert(data.msg, {icon:2}); |
||||
|
} |
||||
|
}, |
||||
|
error:function(data){ |
||||
|
layer.close(ii) |
||||
|
layer.msg('服务器错误', {icon:2}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function searchByType(type){ |
||||
|
$("input[name=keyword]").val(''); |
||||
|
$("select[name=type]").val(type); |
||||
|
searchSubmit() |
||||
|
} |
||||
|
|
||||
|
$(document).ready(function(){ |
||||
|
updateToolbar(); |
||||
|
const defaultPageSize = 20; |
||||
|
|
||||
|
$("#listTable").bootstrapTable({ |
||||
|
url: '/admin/plugins_data?os=en', |
||||
|
pageNumber: 1, |
||||
|
pageSize: 15, |
||||
|
sidePagination: 'client', |
||||
|
classes: 'table table-striped table-hover table-bottom-border', |
||||
|
columns: [ |
||||
|
{ |
||||
|
field: 'name', |
||||
|
title: '软件标识', |
||||
|
formatter: function(value, row, index) { |
||||
|
return '<b>'+value+'</b>'; |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
field: 'title', |
||||
|
title: '软件名称' |
||||
|
}, |
||||
|
{ |
||||
|
field: 'type', |
||||
|
title: '软件分类', |
||||
|
formatter: function(value, row, index) { |
||||
|
return '<a href="javascript:searchByType('+value+')" title="查看该分类下的插件">'+row.typename+'</a>'; |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
field: 'desc', |
||||
|
title: '说明', |
||||
|
}, |
||||
|
{ |
||||
|
field: 'price', |
||||
|
title: '价格', |
||||
|
formatter: function(value, row, index) { |
||||
|
return value > 0 ? '<span style="color:#fc6d26">¥'+value+'</span>' : '免费'; |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
field: 'author', |
||||
|
title: '开发商' |
||||
|
}, |
||||
|
{ |
||||
|
field: 'versions', |
||||
|
title: '版本与状态', |
||||
|
formatter: function(value, row, index) { |
||||
|
var html = ''; |
||||
|
if(row.type == 5 || row.name == 'mail_sys' || row.name == 'dns_manager'){ |
||||
|
html += '<a href="javascript:" class="btn btn-xs btn-success" disabled>无需下载</a>'; |
||||
|
}else{ |
||||
|
$.each(value, function(index,item){ |
||||
|
if(item.status) |
||||
|
html += '<a href="javascript:download_version(\''+row.name+'\',\''+item.version+'\','+item.status+')" class="btn btn-xs btn-success">'+item.version+'</a> '; |
||||
|
else |
||||
|
html += '<a href="javascript:download_version(\''+row.name+'\',\''+item.version+'\','+item.status+')" class="btn btn-xs btn-danger">'+item.version+'</a> '; |
||||
|
}) |
||||
|
} |
||||
|
return html |
||||
|
} |
||||
|
}, |
||||
|
], |
||||
|
onLoadSuccess: function(data){ |
||||
|
$.preDownload = []; |
||||
|
var type = $("select[name=type] option:selected").text(); |
||||
|
if(type != '全部插件' && type != '运行环境' && type != '第三方应用'){ |
||||
|
$("#batch_down").show(); |
||||
|
if(data.length > 0){ |
||||
|
$.each(data, function(index, plugin){ |
||||
|
if(plugin.versions.length > 0 && plugin.name!='mail_sys' && plugin.name!='dns_manager'){ |
||||
|
$.each(plugin.versions, function(index, version){ |
||||
|
if(!version.status){ |
||||
|
$.preDownload.push({name:plugin.name, version:version.version}) |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}else{ |
||||
|
$("#batch_down").hide(); |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
</script> |
||||
|
{/block} |
2027
public/install/install_7.0_en.sh
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
2033
public/install/install_pro_en.sh
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1017
public/install/update_7.x_en.sh
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
After Width: 1920 | Height: 1350 | Size: 94 KiB |
@ -0,0 +1,122 @@ |
|||||
|
# aapanel面板官方更新包修改记录 |
||||
|
|
||||
|
查询最新版本号:https://brandnew.aapanel.com/api/panel/getLatestOfficialVersion |
||||
|
|
||||
|
官方更新包下载链接:http://download.bt.cn/install/update/LinuxPanel_EN-版本号.zip |
||||
|
|
||||
|
假设搭建的宝塔第三方云端网址是 http://www.example.com |
||||
|
|
||||
|
- 将class文件夹里面所有的.so文件删除 |
||||
|
|
||||
|
- 将aapanel/PluginLoader.py复制到class文件夹 |
||||
|
|
||||
|
- 批量解密模块文件:执行 php think decrypt classdir <面板class文件夹路径> |
||||
|
|
||||
|
php think decrypt classdir <面板class_v2文件夹路径> |
||||
|
|
||||
|
- 全局搜索替换 https://wafapi2.aapanel.com => http://www.example.com(需排除task.py、ipsModel.py、js文件) |
||||
|
|
||||
|
- 全局搜索替换 https://node.aapanel.com/install/update_7.x_en.sh => http://www.example.com/install/update_7.x_en.sh |
||||
|
|
||||
|
https://node.aapanel.com/install/update_pro_en.sh => http://www.example.com/install/update_7.x_en.sh |
||||
|
|
||||
|
- 搜索并删除提交异常报告的代码 bt_error/index.php |
||||
|
|
||||
|
- class/ajax.py 文件 \#是否执行升级程序 下面的 public.get_url() 改成 public.OfficialApiBase() |
||||
|
|
||||
|
class/ajax.py 文件 __official_url = 'https://www.aapanel.com' 改成 http://www.example.com |
||||
|
|
||||
|
class/jobs.py 文件 \#尝试升级到独立环境 下面的 public.get_url() 改成 public.OfficialApiBase() |
||||
|
|
||||
|
class/system.py 文件 RepPanel和UpdatePro方法内的 public.get_url() 改成 public.OfficialApiBase() |
||||
|
|
||||
|
- class/public/common.py |
||||
|
|
||||
|
def OfficialApiBase(): 改成 return 'http://www.example.com' |
||||
|
|
||||
|
def load_soft_list 去除 if force 部分 |
||||
|
|
||||
|
plugin_list_data = PluginLoader.get_plugin_list(0) 部分改成 plugin_list_data = PluginLoader.get_plugin_list(force) |
||||
|
|
||||
|
在 def check_domain_cloud(domain): 这一行下面加上 return |
||||
|
|
||||
|
在 def count_wp(): 这一行下面加上 return |
||||
|
|
||||
|
在 def err_collect 这一行下面加上 return |
||||
|
|
||||
|
在 def get_improvement(): 这一行下面加上 return False |
||||
|
|
||||
|
在free_login_area方法内get_free_ips_area替换成get_ips_area |
||||
|
|
||||
|
在login_send_body方法内,free_login_area(login_ip=server_ip_area的server_ip_area改成login_ip |
||||
|
|
||||
|
在 def write_request_log(reques=None): 这一行下面加上 return |
||||
|
|
||||
|
- class/panelPlugin.py 文件,set_pyenv方法内,temp_file = public.readFile(filename)这行代码下面加上 |
||||
|
|
||||
|
```python |
||||
|
temp_file = temp_file.replace('http://download.bt.cn/install/public.sh', 'http://www.example.com/install/public.sh') |
||||
|
temp_file = temp_file.replace('https://download.bt.cn/install/public.sh', 'http://www.example.com/install/public.sh') |
||||
|
``` |
||||
|
|
||||
|
def check_status(self, softInfo): 方法最后一行加上 |
||||
|
|
||||
|
```python |
||||
|
if 'endtime' in softInfo: |
||||
|
softInfo['endtime'] = time.time() + 86400 * 3650 |
||||
|
``` |
||||
|
|
||||
|
- class_v2/btdockerModelV2/flush_plugin.py 文件,删除clear_hosts()一行 |
||||
|
|
||||
|
- install/install_soft.sh 在. 执行之前加入以下代码 |
||||
|
|
||||
|
```shell |
||||
|
sed -i "s/http:\/\/download.bt.cn\/install\/public.sh/http:\/\/www.example.com\/install\/public.sh/" $name.sh |
||||
|
sed -i "s/https:\/\/download.bt.cn\/install\/public.sh/http:\/\/www.example.com\/install\/public.sh/" $name.sh |
||||
|
``` |
||||
|
|
||||
|
- install/public.sh 用官网最新版的[public.sh](http://download.bt.cn/install/public.sh)替换,并去除最下面bt_check一行 |
||||
|
|
||||
|
- 去除无用的定时任务:task.py 文件 删除以下几行 |
||||
|
|
||||
|
"update_software_list": update_software_list, |
||||
|
|
||||
|
"check_panel_msg": check_panel_msg, |
||||
|
|
||||
|
"check_panel_auth": check_panel_auth, |
||||
|
|
||||
|
"count_ssh_logs": count_ssh_logs, |
||||
|
|
||||
|
"submit_email_statistics": submit_email_statistics, |
||||
|
|
||||
|
"submit_module_call_statistics": submit_module_call_statistics, |
||||
|
|
||||
|
"mailsys_domain_restrictions": mailsys_domain_restrictions, |
||||
|
|
||||
|
- [可选]去除各种计算题:将bt.js里面的内容复制到 BTPanel/static/vite/oldjs/public_backup.js 末尾 |
||||
|
|
||||
|
- [可选]去除创建网站自动创建的垃圾文件:在class/panelSite.py,分别删除 |
||||
|
|
||||
|
htaccess = self.sitePath + '/.htaccess' |
||||
|
|
||||
|
index = self.sitePath + '/index.html' |
||||
|
|
||||
|
doc404 = self.sitePath + '/404.html' |
||||
|
|
||||
|
这3行及分别接下来的4行代码 |
||||
|
|
||||
|
- [可选]关闭未绑定域名提示页面:在class/panelSite.py,root /www/server/nginx/html改成return 400 |
||||
|
|
||||
|
- [可选]上传文件默认选中覆盖,在BTPanel/static/vite/oldjs/upload-drog.js,id="all_operation"加checked属性 |
||||
|
|
||||
|
- [可选] BTPanel/static/vite/oldjs/site.js,优化SSL证书配置页面 |
||||
|
|
||||
|
- [可选]新版vite页面去除需求反馈、各种广告、计算题等,执行 php think cleanvitejs <面板BTPanel/static/js路径> |
||||
|
|
||||
|
- 新增简体中文语言:修改BTPanel/languages/settings.json,并增加 zh/server.json、all/zh.json |
||||
|
|
||||
|
|
||||
|
解压安装包[panel_7_en.zip](http://download.bt.cn/install/src/panel_7_en.zip),将更新包改好的文件覆盖到里面,然后重新打包,即可更新安装包。( |
||||
|
|
||||
|
别忘了删除class文件夹里面所有的.so文件) |
||||
|
|
@ -0,0 +1,408 @@ |
|||||
|
#coding: utf-8 |
||||
|
# +------------------------------------------------------------------- |
||||
|
# | 宝塔Linux面板 |
||||
|
# +------------------------------------------------------------------- |
||||
|
# | Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved. |
||||
|
# +------------------------------------------------------------------- |
||||
|
# | Author: hwliang <hwl@bt.cn> |
||||
|
# +------------------------------------------------------------------- |
||||
|
|
||||
|
#+-------------------------------------------------------------------- |
||||
|
#| 插件和模块加载器 |
||||
|
#+-------------------------------------------------------------------- |
||||
|
|
||||
|
import public,os,sys,json,hashlib |
||||
|
|
||||
|
def plugin_run(plugin_name,def_name,args): |
||||
|
''' |
||||
|
@name 执行插件方法 |
||||
|
@param plugin_name<string> 插件名称 |
||||
|
@param def_name<string> 方法名称 |
||||
|
@param args<dict_obj> 参数对像 |
||||
|
@return mixed |
||||
|
''' |
||||
|
if not plugin_name or not def_name: return public.returnMsg(False,'parameter incorrect: module_name and def_name cannot be empty.') |
||||
|
|
||||
|
# 获取插件目录 |
||||
|
plugin_path = public.get_plugin_path(plugin_name) |
||||
|
is_php = os.path.exists(os.path.join(plugin_path,'index.php')) |
||||
|
|
||||
|
# 检查插件目录是否合法 |
||||
|
if is_php: |
||||
|
plugin_file = os.path.join(plugin_path,'index.php') |
||||
|
else: |
||||
|
plugin_file = os.path.join(plugin_path, plugin_name + '_main.py') |
||||
|
if not public.path_safe_check(plugin_file): return public.returnMsg(False,'parameter incorrect: module_name and def_name cannot contains special symbols.') |
||||
|
|
||||
|
# 检查插件入口文件是否存在 |
||||
|
if not os.path.exists(plugin_file): return public.returnMsg(False,'plugin not found') |
||||
|
|
||||
|
# 添加插件目录到系统路径 |
||||
|
public.sys_path_append(plugin_path) |
||||
|
|
||||
|
if not is_php: |
||||
|
# 引用插件入口文件 |
||||
|
_name = "{}_main".format(plugin_name) |
||||
|
plugin_main = __import__(_name) |
||||
|
|
||||
|
# 检查类名是否符合规范 |
||||
|
if not hasattr(plugin_main,_name): |
||||
|
return public.returnMsg(False,'plugin class name is invalid') |
||||
|
|
||||
|
try: |
||||
|
if sys.version_info[0] == 2: |
||||
|
reload(plugin_main) |
||||
|
else: |
||||
|
from imp import reload |
||||
|
reload(plugin_main) |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
# 实例化插件类 |
||||
|
plugin_obj = getattr(plugin_main,_name)() |
||||
|
|
||||
|
# 检查方法是否存在 |
||||
|
if not hasattr(plugin_obj,def_name): |
||||
|
return public.returnMsg(False,'not find method [%s] in plugin [%s]' % (def_name,plugin_name)) |
||||
|
|
||||
|
if args is not None and 'plugin_get_object' in args and args.plugin_get_object == 1: |
||||
|
return getattr(plugin_obj, def_name) |
||||
|
|
||||
|
# 执行方法 |
||||
|
return getattr(plugin_obj,def_name)(args) |
||||
|
else: |
||||
|
if args is not None and 'plugin_get_object' in args and args.plugin_get_object == 1: |
||||
|
return None |
||||
|
import panelPHP |
||||
|
args.s = def_name |
||||
|
args.name = plugin_name |
||||
|
return panelPHP.panelPHP(plugin_name).exec_php_script(args) |
||||
|
|
||||
|
|
||||
|
def get_module_list(): |
||||
|
''' |
||||
|
@name 获取模块列表 |
||||
|
@return list |
||||
|
''' |
||||
|
module_list = [] |
||||
|
class_path = public.get_class_path() |
||||
|
for name in os.listdir(class_path): |
||||
|
path = os.path.join(class_path,name) |
||||
|
# 过滤无效文件 |
||||
|
if not name or name.endswith('.py') or name[0] == '.' or not name.endswith('Model') or os.path.isfile(path):continue |
||||
|
module_list.append(name) |
||||
|
return module_list |
||||
|
|
||||
|
def module_run(module_name,def_name,args): |
||||
|
''' |
||||
|
@name 执行模块方法 |
||||
|
@param module_name<string> 模块名称 |
||||
|
@param def_name<string> 方法名称 |
||||
|
@param args<dict_obj> 参数对像 |
||||
|
@return mixed |
||||
|
''' |
||||
|
if not module_name or not def_name: return public.returnMsg(False,'parameter incorrect: module_name and def_name cannot be empty.') |
||||
|
model_index = args.get('model_index',None) |
||||
|
class_path = public.get_class_path() |
||||
|
panel_path = public.get_panel_path() |
||||
|
|
||||
|
module_file = None |
||||
|
if 'model_index' in args: |
||||
|
# 新模块目录 |
||||
|
if model_index in ['mod']: |
||||
|
module_file = os.path.join(panel_path,'mod','project',module_name + 'Mod.py') |
||||
|
elif model_index: |
||||
|
# 旧模块目录 |
||||
|
module_file = os.path.join(class_path,model_index+"Model",module_name + 'Model.py') |
||||
|
else: |
||||
|
module_file = os.path.join(class_path,"projectModel",module_name + 'Model.py') |
||||
|
else: |
||||
|
# 如果没指定模块名称,则遍历所有模块目录 |
||||
|
module_list = get_module_list() |
||||
|
for name in module_list: |
||||
|
module_file = os.path.join(class_path,name,module_name + 'Model.py') |
||||
|
if os.path.exists(module_file): break |
||||
|
|
||||
|
# 判断模块入口文件是否存在 |
||||
|
if not os.path.exists(module_file): |
||||
|
return public.returnMsg(False,'module file [%s] not exist' % module_name) |
||||
|
|
||||
|
# 判断模块路径是否合法 |
||||
|
if not public.path_safe_check(module_file): |
||||
|
return public.returnMsg(False,'parameter incorrect: module_name and def_name cannot contains special symbols.') |
||||
|
|
||||
|
def_object = public.get_script_object(module_file) |
||||
|
if not def_object: return public.returnMsg(False,'module [%s] not found' % module_name) |
||||
|
|
||||
|
# 模块实例化并返回方法对象 |
||||
|
try: |
||||
|
run_object = getattr(def_object.main(),def_name,None) |
||||
|
except: |
||||
|
return public.returnMsg(False,'module [%s] failed to instance class' % module_name) |
||||
|
if not run_object: return public.returnMsg(False,'not found method [%s] in module [%s]' % (def_name,module_name)) |
||||
|
|
||||
|
if 'module_get_object' in args and args.module_get_object == 1: |
||||
|
return run_object |
||||
|
|
||||
|
# 执行方法 |
||||
|
result = run_object(args) |
||||
|
return result |
||||
|
|
||||
|
|
||||
|
def get_plugin_list(upgrade_force = False): |
||||
|
''' |
||||
|
@name 获取插件列表 |
||||
|
@param upgrade_force<bool> 是否强制重新获取列表 |
||||
|
@return dict |
||||
|
''' |
||||
|
|
||||
|
api_root_url = public.OfficialApiBase() |
||||
|
api_url = api_root_url+ '/api/panel/getSoftListEn' |
||||
|
panel_path = public.get_panel_path() |
||||
|
data_path = os.path.join(panel_path,'data') |
||||
|
|
||||
|
if not os.path.exists(data_path): |
||||
|
os.makedirs(data_path,384) |
||||
|
|
||||
|
plugin_list = {} |
||||
|
plugin_list_file = os.path.join(data_path,'plugin_list.json') |
||||
|
if os.path.exists(plugin_list_file) and not upgrade_force: |
||||
|
plugin_list_body = public.readFile(plugin_list_file) |
||||
|
try: |
||||
|
plugin_list = json.loads(plugin_list_body) |
||||
|
except: |
||||
|
plugin_list = {} |
||||
|
|
||||
|
if not os.path.exists(plugin_list_file) or upgrade_force or not plugin_list: |
||||
|
try: |
||||
|
res = public.HttpGet(api_url) |
||||
|
except Exception as ex: |
||||
|
raise public.error_conn_cloud(str(ex)) |
||||
|
if not res: raise Exception(False,'failed to get soft list') |
||||
|
|
||||
|
plugin_list = json.loads(res) |
||||
|
if type(plugin_list)!=dict or 'list' not in plugin_list: |
||||
|
if type(plugin_list)==str: |
||||
|
raise Exception(plugin_list) |
||||
|
else: |
||||
|
raise Exception('failed to parse soft list') |
||||
|
content = json.dumps(plugin_list) |
||||
|
public.writeFile(plugin_list_file,content) |
||||
|
|
||||
|
plugin_bin_file = os.path.join(data_path,'plugin_bin.pl') |
||||
|
encode_content = __encode_plugin_list(content) |
||||
|
if encode_content: |
||||
|
public.writeFile(plugin_bin_file,encode_content) |
||||
|
|
||||
|
return plugin_list |
||||
|
|
||||
|
def __encode_plugin_list(content): |
||||
|
try: |
||||
|
userInfo = public.get_user_info() |
||||
|
if not userInfo or 'server_id' not in userInfo: return None |
||||
|
block_size = 51200 |
||||
|
uid = str(userInfo['uid']) |
||||
|
server_id = userInfo['server_id'] |
||||
|
key = server_id[10:26] + uid + server_id |
||||
|
key = hashlib.md5(key.encode()).hexdigest() |
||||
|
iv = key + server_id |
||||
|
iv = hashlib.md5(iv.encode()).hexdigest() |
||||
|
key = key[8:24] |
||||
|
iv = iv[8:24] |
||||
|
blocks = [content[i:i + block_size] for i in range(0, len(content), block_size)] |
||||
|
encrypted_content = '' |
||||
|
for block in blocks: |
||||
|
encrypted_content += __aes_encrypt(block, key, iv) + '\n' |
||||
|
return encrypted_content |
||||
|
except: |
||||
|
pass |
||||
|
return None |
||||
|
|
||||
|
def get_module(filename): |
||||
|
if not filename: return public.returnMsg(False,'parameter error: get_module(filename<str>)') |
||||
|
if filename[0:2] == './': return public.returnMsg(False,'filename cannot be relative path.') |
||||
|
if not public.path_safe_check(filename): return public.returnMsg(False,'filename cannot contains special symbols.') |
||||
|
if not os.path.exists(filename): return public.returnMsg(False,'file does not exist') |
||||
|
return __get_class_module(filename) |
||||
|
|
||||
|
def __get_class_module(filename): |
||||
|
_obj = sys.modules.get(filename, None) |
||||
|
if _obj: return _obj |
||||
|
_code = public.readFile(filename) |
||||
|
if _code.find('import') == -1: |
||||
|
en_arr = _code.split('\n') |
||||
|
de_text = '' |
||||
|
for data in en_arr: |
||||
|
data = str.strip(data) |
||||
|
if not data: continue |
||||
|
de_text += __aes_decrypt_module(data) |
||||
|
_code = de_text |
||||
|
if not _code or _code.find('import') == -1: |
||||
|
return public.returnMsg(False,'load failed: decode error') |
||||
|
_code_object = compile(_code, filename, 'exec') |
||||
|
from types import ModuleType |
||||
|
_obj = sys.modules.setdefault(filename, ModuleType(filename)) |
||||
|
_obj.__file__ = filename |
||||
|
_obj.__package__ = '' |
||||
|
exec(_code_object, _obj.__dict__) |
||||
|
return _obj |
||||
|
|
||||
|
def start_total(): |
||||
|
''' |
||||
|
@name 启动统计服务 |
||||
|
@return dict |
||||
|
''' |
||||
|
pass |
||||
|
|
||||
|
def get_soft_list(args): |
||||
|
''' |
||||
|
@name 获取软件列表 |
||||
|
@param args<dict_obj> 参数对像 |
||||
|
@return dict |
||||
|
''' |
||||
|
pass |
||||
|
|
||||
|
def db_encrypt(data): |
||||
|
''' |
||||
|
@name 数据库加密 |
||||
|
@param args<dict_obj> 参数对像 |
||||
|
@return dict |
||||
|
''' |
||||
|
try: |
||||
|
key = __get_db_sgin() |
||||
|
iv = __get_db_iv() |
||||
|
str_arr = data.split('\n') |
||||
|
res_str = '' |
||||
|
for data in str_arr: |
||||
|
if not data: continue |
||||
|
res_str += __aes_encrypt(data, key, iv) |
||||
|
except: |
||||
|
res_str = data |
||||
|
result = { |
||||
|
'status' : True, |
||||
|
'msg' : res_str |
||||
|
} |
||||
|
return result |
||||
|
|
||||
|
def db_decrypt(data): |
||||
|
''' |
||||
|
@name 数据库解密 |
||||
|
@param args<dict_obj> 参数对像 |
||||
|
@return dict |
||||
|
''' |
||||
|
try: |
||||
|
key = __get_db_sgin() |
||||
|
iv = __get_db_iv() |
||||
|
str_arr = data.split('\n') |
||||
|
res_str = '' |
||||
|
for data in str_arr: |
||||
|
if not data: continue |
||||
|
res_str += __aes_decrypt(data, key, iv) |
||||
|
except: |
||||
|
res_str = data |
||||
|
result = { |
||||
|
'status' : True, |
||||
|
'msg' : res_str |
||||
|
} |
||||
|
return result |
||||
|
|
||||
|
def __get_db_sgin(): |
||||
|
keystr = '3gP7+k_7lSNg3$+Fj!PKW+6$KYgHtw#R' |
||||
|
key = '' |
||||
|
for i in range(31): |
||||
|
if i & 1 == 0: |
||||
|
key += keystr[i] |
||||
|
return key |
||||
|
|
||||
|
def __get_db_iv(): |
||||
|
div_file = "{}/data/div.pl".format(public.get_panel_path()) |
||||
|
if not os.path.exists(div_file): |
||||
|
str = public.GetRandomString(16) |
||||
|
str = __aes_encrypt_module(str) |
||||
|
div = public.get_div(str) |
||||
|
public.WriteFile(div_file, div) |
||||
|
if os.path.exists(div_file): |
||||
|
div = public.ReadFile(div_file) |
||||
|
div = __aes_decrypt_module(div) |
||||
|
else: |
||||
|
keystr = '4jHCpBOFzL4*piTn^-4IHBhj-OL!fGlB' |
||||
|
div = '' |
||||
|
for i in range(31): |
||||
|
if i & 1 == 0: |
||||
|
div += keystr[i] |
||||
|
return div |
||||
|
|
||||
|
def __aes_encrypt_module(data): |
||||
|
key = 'Z2B87NEAS2BkxTrh' |
||||
|
iv = 'WwadH66EGWpeeTT6' |
||||
|
return __aes_encrypt(data, key, iv) |
||||
|
|
||||
|
def __aes_decrypt_module(data): |
||||
|
key = 'Z2B87NEAS2BkxTrh' |
||||
|
iv = 'WwadH66EGWpeeTT6' |
||||
|
return __aes_decrypt(data, key, iv) |
||||
|
|
||||
|
def __aes_decrypt(data, key, iv): |
||||
|
from Crypto.Cipher import AES |
||||
|
import base64 |
||||
|
encodebytes = base64.decodebytes(data.encode('utf-8')) |
||||
|
aes = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8')) |
||||
|
de_text = aes.decrypt(encodebytes) |
||||
|
unpad = lambda s: s[0:-s[-1]] |
||||
|
de_text = unpad(de_text) |
||||
|
return de_text.decode('utf-8') |
||||
|
|
||||
|
def __aes_encrypt(data, key, iv): |
||||
|
from Crypto.Cipher import AES |
||||
|
import base64 |
||||
|
data = (lambda s: s + (16 - len(s) % 16) * chr(16 - len(s) % 16).encode('utf-8'))(data.encode('utf-8')) |
||||
|
aes = AES.new(key.encode('utf8'), AES.MODE_CBC, iv.encode('utf8')) |
||||
|
encryptedbytes = aes.encrypt(data) |
||||
|
en_text = base64.b64encode(encryptedbytes) |
||||
|
return en_text.decode('utf-8') |
||||
|
|
||||
|
def plugin_end(): |
||||
|
''' |
||||
|
@name 插件到期处理 |
||||
|
@return dict |
||||
|
''' |
||||
|
pass |
||||
|
|
||||
|
def daemon_task(): |
||||
|
''' |
||||
|
@name 后台任务守护 |
||||
|
@return dict |
||||
|
''' |
||||
|
pass |
||||
|
|
||||
|
def daemon_panel(): |
||||
|
''' |
||||
|
@name 面板守护 |
||||
|
@return dict |
||||
|
''' |
||||
|
pass |
||||
|
|
||||
|
def flush_auth_key(): |
||||
|
''' |
||||
|
@name 刷新授权密钥 |
||||
|
@return dict |
||||
|
''' |
||||
|
pass |
||||
|
|
||||
|
def get_auth_state(): |
||||
|
''' |
||||
|
@name 获取授权状态 |
||||
|
@return 返回:0.免费版 1.专业版 2.企业版 -1.获取失败 |
||||
|
''' |
||||
|
try: |
||||
|
softList = get_plugin_list() |
||||
|
if softList['ltd'] > -1: |
||||
|
return 2 |
||||
|
elif softList['pro'] > -1: |
||||
|
return 1 |
||||
|
else: |
||||
|
return 0 |
||||
|
except: |
||||
|
return -1 |
||||
|
|
||||
|
|
@ -0,0 +1,83 @@ |
|||||
|
/* |
||||
|
*宝塔面板去除各种计算题与延时等待 |
||||
|
*/ |
||||
|
if("undefined" != typeof bt && bt.hasOwnProperty("show_confirm")){ |
||||
|
bt.show_confirm = function(title, msg, fun, error) { |
||||
|
layer.open({ |
||||
|
type: 1, |
||||
|
title: title, |
||||
|
area: "350px", |
||||
|
closeBtn: 2, |
||||
|
shadeClose: true, |
||||
|
btn: [lan['public'].ok, lan['public'].cancel], |
||||
|
content: "<div class='bt-form webDelete pd20'>\ |
||||
|
<p>" + msg + "</p>" + (error || '') + "\ |
||||
|
</div>", |
||||
|
yes: function (index, layero) { |
||||
|
layer.close(index); |
||||
|
if (fun) fun(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
if("undefined" != typeof bt && bt.hasOwnProperty("prompt_confirm")){ |
||||
|
bt.prompt_confirm = function (title, msg, callback) { |
||||
|
layer.open({ |
||||
|
type: 1, |
||||
|
title: title, |
||||
|
area: "480px", |
||||
|
closeBtn: 2, |
||||
|
btn: ['OK', 'Cancel'], |
||||
|
content: "<div class='bt-form promptDelete pd20'>\ |
||||
|
<p>" + msg + "</p>\ |
||||
|
</div>", |
||||
|
yes: function (layers, index) { |
||||
|
layer.close(layers) |
||||
|
if (callback) callback() |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
if("undefined" != typeof bt && bt.hasOwnProperty("compute_confirm")){ |
||||
|
bt.compute_confirm = function (config, callback) { |
||||
|
layer.open({ |
||||
|
type: 1, |
||||
|
title: config.title, |
||||
|
area: '430px', |
||||
|
closeBtn: 2, |
||||
|
shadeClose: true, |
||||
|
btn: [lan['public'].ok, lan['public'].cancel], |
||||
|
content: |
||||
|
'<div class="bt-form hint_confirm pd30">\ |
||||
|
<div class="hint_title">\ |
||||
|
<i class="hint-confirm-icon"></i>\ |
||||
|
<div class="hint_con">' + |
||||
|
config.msg + |
||||
|
'</div>\ |
||||
|
</div>\ |
||||
|
</div>', |
||||
|
yes: function (layers, index) { |
||||
|
layer.close(layers) |
||||
|
if (callback) callback() |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
function SafeMessage(j, h, g, f) { |
||||
|
if (f == undefined) f = ''; |
||||
|
var mess = layer.open({ |
||||
|
type: 1, |
||||
|
title: j, |
||||
|
area: "350px", |
||||
|
closeBtn: 2, |
||||
|
shadeClose: true, |
||||
|
content: "<div class='bt-form webDelete pd20 pb70'><p>" + h + "</p>" + f + "<div class='bt-form-submit-btn'><button type='button' class='btn btn-danger btn-sm bt-cancel'>"+lan.public.cancel+"</button> <button type='button' id='toSubmit' class='btn btn-success btn-sm' >"+lan.public.ok+"</button></div></div>" |
||||
|
}); |
||||
|
$(".bt-cancel").click(function(){ |
||||
|
layer.close(mess); |
||||
|
}); |
||||
|
$("#toSubmit").click(function() { |
||||
|
layer.close(mess); |
||||
|
g(); |
||||
|
}) |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue