Compare commits

...

60 Commits
2.2.1 ... main

Author SHA1 Message Date
flucout
08bd35c997 update 2026-01-23 16:00:06 +08:00
flucout
c0ebc38d54 update 2026-01-10 22:28:33 +08:00
flucout
c7381fd675 update 2025-12-16 23:14:27 +08:00
flucout
1ed4ac8d39 update 2025-12-16 23:05:40 +08:00
flucout
3d5cb3cddf update 2025-11-25 22:26:04 +08:00
flucout
8fd2b5fc87 update 2025-11-05 21:29:13 +08:00
flucout
56f6a3242b update 2025-09-24 18:57:28 +08:00
flucout
ab322ca6e1 update 2025-08-11 15:30:56 +08:00
flucout
dff7fa67e7 update 2025-08-08 19:06:34 +08:00
flucout
263f5aec86 update 2025-07-27 22:48:52 +08:00
flucout
4e48a2722d update 2025-07-27 16:18:19 +08:00
flucout
28a34b273a update 2025-07-26 10:48:03 +08:00
flucout
655b408493 update 2025-07-26 10:44:47 +08:00
flucout
42500b40d1 修复无法获取宝塔恶意IP 2025-05-30 10:14:05 +08:00
flucout
9438c64796 update 2025-05-26 21:49:50 +08:00
Flucont
acb842a883
Merge pull request #264 from Illustar0/main
feat: support get_malicious_ip for bt_waf
2025-04-17 20:11:53 +08:00
Illustar0
6785d259f6
feat: support get_malicious_ip for bt_waf 2025-04-14 11:18:16 +08:00
flucout
04560cd70c update 2025-04-04 20:05:14 +08:00
flucout
ecfe3c9e20 update 2025-03-11 21:13:39 +08:00
flucout
d8de902f1b fix 2025-03-07 12:28:43 +08:00
flucout
fdbbd304cb fix 2025-01-19 13:31:36 +08:00
flucout
53a484093e update 2025-01-19 13:27:28 +08:00
flucout
7010d79ca1 update 2025-01-09 21:43:28 +08:00
flucout
99f9a28a73 update 2025-01-07 20:14:00 +08:00
flucout
4c05974f09 update 2025-01-05 10:52:15 +08:00
flucout
27a7281101 update 2025-01-04 23:56:22 +08:00
flucout
c6a9fccc1c update 2025-01-04 23:54:42 +08:00
flucout
0a9e5c3b92 update 2024-12-23 21:46:48 +08:00
flucout
a929080b3f update 2024-11-07 15:27:34 +08:00
flucout
ba7e07f18a update 2024-09-27 13:50:20 +08:00
flucout
25292b8e34 update 2024-09-27 13:50:04 +08:00
flucout
d80c0e9e44 update 2024-09-19 21:35:42 +08:00
flucout
247e486181 fix 2024-09-08 14:09:06 +08:00
flucout
d856d3bc1c update 2024-09-01 09:32:35 +08:00
flucout
e304e9acc2 update 2024-08-31 19:54:50 +08:00
flucout
aba885f434 update 2024-08-13 20:29:20 +08:00
flucout
5e1f19de53 支持生成自签SSL证书 2024-08-13 17:23:31 +08:00
flucout
57cbf9315c wiki 2024-08-10 22:55:52 +08:00
flucout
4608d4899b update 2024-08-10 22:30:50 +08:00
flucout
272f3bfe97 update 2024-07-27 13:10:39 +08:00
flucout
9d2d883668 update 2024-07-21 17:59:44 +08:00
flucout
3d1f1b721a update 2024-07-19 23:41:26 +08:00
flucout
fa9188b94d update 2024-07-06 13:25:55 +08:00
flucout
2ddb4ff64c update 2024-06-16 21:00:50 +08:00
flucout
963b675026 fix 2024-06-07 14:20:19 +08:00
flucout
5642ea3038 update 2024-06-07 14:09:15 +08:00
flucout
2aff0d9e6d update 2024-06-01 15:11:14 +08:00
flucout
0ca1c9d0e3 update 2024-06-01 15:07:31 +08:00
flucout
c1681a9731 update 2024-05-07 23:43:04 +08:00
flucout
911a567fde update 2024-04-30 22:56:41 +08:00
flucout
b27349a416 update 2024-04-13 15:55:42 +08:00
flucout
def82c88cb Merge branch 'main' of ssh://ssh.github.com:443/flucont/btcloud 2024-03-17 21:00:26 +08:00
flucout
04028103c1 update 2024-03-17 20:59:45 +08:00
Flucont
d18cd84e29
Merge pull request #171 from XCwosjw/patch-1
update
2024-02-24 13:10:25 +08:00
XCwosjw
268f76f9ba
修复在IPV6下的异常报错
修复在IPV6下因ip长度问题而导致的的异常报错
2024-02-23 10:14:56 +08:00
flucout
7d32833431 update 2024-02-12 21:05:46 +08:00
flucout
6c6ad40836 update 2024-01-30 21:40:39 +08:00
flucout
11961b8f0b update 2024-01-20 21:42:10 +08:00
flucout
71db6b0a3e update 2024-01-17 18:13:08 +08:00
flucout
79fddbb943 update 2023-12-30 10:20:44 +08:00
82 changed files with 9841 additions and 3601 deletions

130
README.md
View File

@ -1,64 +1,66 @@
# 宝塔面板第三方云端 # 宝塔面板第三方云端
这是一个用php开发的宝塔面板第三方云端站点程序。 这是一个用php开发的宝塔面板第三方云端站点程序。
你可以使用此程序搭建属于自己的宝塔面板第三方云端实现最新版宝塔面板私有化部署不与宝塔官方接口通信满足隐私安全合规需求。同时还可以去除面板强制绑定账号DIY面板功能等。 你可以使用此程序搭建属于自己的宝塔面板第三方云端实现最新版宝塔面板私有化部署不与宝塔官方接口通信满足隐私安全合规需求。同时还可以去除面板强制绑定账号DIY面板功能等。
网站后台管理可一键同步宝塔官方的插件列表与增量更新插件包还有云端使用记录、IP黑白名单、操作日志、定时任务等功能。 网站后台管理可一键同步宝塔官方的插件列表与增量更新插件包还有云端使用记录、IP黑白名单、操作日志、定时任务等功能。
本项目自带的宝塔安装包和更新包是8.0.x最新版已修改适配此第三方云端并且全开源无so等加密文件。 本项目自带 **宝塔Linux面板**、**宝塔Windows面板**、**aaPanel面板**、**宝塔云监控** 的最新版安装包和更新包,已修改适配此第三方云端,并且全开源,无.so等加密文件。
觉得该项目不错的可以给个Star~ 觉得该项目不错的可以给个Star~
## 声明 ## 声明
1.此项目只能以自用为目的,不得侵犯堡塔公司及其他第三方的知识产权和其他合法权利。 1.此项目只能以自用为目的,不得侵犯堡塔公司及其他第三方的知识产权和其他合法权利。
2.搭建使用此项目必须有一定的编程和Linux运维基础纯小白不建议使用。 2.搭建使用此项目必须有一定的编程和Linux运维基础纯小白不建议使用。
## 环境要求 ## 环境要求
* `PHP` >= 7.4 * `PHP` >= 7.4
* `MySQL` >= 5.6 * `MySQL` >= 5.6
* `fileinfo`扩展 * `fileinfo`扩展
* `ZipArchive`扩展 * `ZipArchive`扩展
## 部署方法 ## 部署方法
- [下载最新版的Release包](https://github.com/flucont/btcloud/releases) - [下载最新版的Release包](https://github.com/flucont/btcloud/releases)
- 如果是下载的源码包,需要执行 `composer install --no-dev` 安装依赖如果是下载的Release包则不需要 - 如果是下载的源码包,需要执行 `composer install --no-dev` 安装依赖如果是下载的Release包则不需要
- 设置网站运行目录为`public` - 设置网站运行目录为`public`
- 设置伪静态为`ThinkPHP` - 设置伪静态为`ThinkPHP`
- 访问网站,会自动跳转到安装页面,根据提示安装完成 - 访问网站,会自动跳转到安装页面,根据提示安装完成
## 使用方法 ## 使用方法
- 在`批量替换工具`执行页面显示的命令可将bt安装包、更新包和脚本文件里面的`http://www.example.com`批量替换成当前网站的网址。 - 在`批量替换工具`执行页面显示的命令可将bt安装包、更新包和脚本文件里面的`http://www.example.com`批量替换成当前网站的网址。
- 在`系统基本设置`修改宝塔面板接口设置。你需要准备一个使用官方最新脚本安装并绑定账号的宝塔面板,用于获取最新插件列表及插件包。并根据界面提示安装好专用插件。 - 在`系统基本设置`修改宝塔面板接口设置。你需要准备一个使用官方最新脚本安装并绑定账号的宝塔面板,用于获取最新插件列表及插件包。并根据界面提示安装好专用插件。
- 在`定时任务设置`执行所显示的命令从宝塔官方获取最新的插件列表并批量下载插件包(增量更新)。当然你也可以去插件列表,一个一个点击下载。 - 在`定时任务设置`执行所显示的命令从宝塔官方获取最新的插件列表并批量下载插件包(增量更新)。当然你也可以去插件列表,一个一个点击下载。
- 访问网站`/download`查看使用此第三方云端的一键安装脚本。 - 访问网站`/download`查看使用此第三方云端的一键安装脚本。
## 更新方法 ## 更新方法
- [下载最新版的Release包](https://github.com/flucont/btcloud/releases) - [下载最新版的Release包](https://github.com/flucont/btcloud/releases)
- 上传覆盖除data文件夹以外的全部文件 - 上传覆盖除data文件夹以外的全部文件
- 后台使用批量替换工具->获取最新插件列表->修改Linux面板等版本号 - 后台使用批量替换工具->获取最新插件列表->修改软件版本设置里面的版本号
## 其他 ## 其他
- [Linux面板官方更新包修改记录](./wiki/update.md) - [Linux面板官方更新包修改记录](./wiki/update.md)
- [Windows面板官方更新包修改记录](./wiki/updatewin.md) - [Windows面板官方更新包修改记录](./wiki/updatewin.md)
- [宝塔云监控安装包修改记录](./wiki/btmonitor.md) - [aaPanel面板官方更新包修改记录](./wiki/aapanel.md)
- 宝塔面板官方版与此第三方云端版对比: - [宝塔云监控安装包修改记录](./wiki/btmonitor.md)
| | 官方版 | 此第三方云端版 | - 宝塔面板官方版与此第三方云端版对比:
| ---------- | ------------------------------------------------------------ | -------------------------------------------------- |
| 版本更新 | 支持 | 支持 | | | 官方版 | 此第三方云端版 |
| 面板广告 | 有广告 | 无广告 | | ---------- | ------------------------------------------------------------ | -------------------------------------------------- |
| 是否全开源 | 没有全开源 | 全开源 | | 版本更新 | 支持 | 支持 |
| 资源占用 | 各种统计上报等任务,资源占用略高 | 去除了很多无用的定时任务,资源占较少 | | 面板广告 | 有广告 | 无广告 |
| 兼容性 | 由于编译的so文件有系统架构限制兼容的系统仅限已编译的so对应的系统架构 | 由于全开源没有已编译的so文件因此无系统架构限制 | | 是否全开源 | 没有全开源 | 全开源 |
| 资源占用 | 各种统计上报等任务,资源占用略高 | 去除了很多无用的定时任务,资源占较少 |
| 兼容性 | 由于编译的so文件有系统架构限制兼容的系统仅限已编译的so对应的系统架构 | 由于全开源没有已编译的so文件因此无系统架构限制 |

View File

@ -68,37 +68,10 @@ class Clean extends Command
if($file == '.' || $file == '..') continue; if($file == '.' || $file == '..') continue;
if(!in_array($file, $file_list)){ if(!in_array($file, $file_list)){
$filepath = $data_dir . 'folder/' . $file; $filepath = $data_dir . 'folder/' . $file;
$this->delete_dir($filepath); deleteDir($filepath);
$count++; $count++;
} }
} }
$output->writeln($os.'成功清理'.$count.'个历史版本插件目录'); $output->writeln($os.'成功清理'.$count.'个历史版本插件目录');
} }
// 删除文件夹
private function delete_dir($dir){
$rd = opendir($dir);
if (!$rd) {
return false;
}
while (($file = readdir($rd)) !== false) {
if ($file == '.' || $file == '..') {
continue;
}
$file = $dir . '/' . $file;
if (is_dir($file)) {
$this->delete_dir($file);
}
else {
unlink($file);
}
}
closedir($rd);
rmdir($dir);
return true;
}
} }

386
app/command/CleanViteJs.php Normal file
View File

@ -0,0 +1,386 @@
<?php
declare (strict_types = 1);
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\facade\Db;
use think\facade\Config;
use app\lib\Plugins;
class CleanViteJs extends Command
{
protected function configure()
{
$this->setName('cleanvitejs')
->addArgument('dir', Argument::REQUIRED, '/BTPanel/static/vite/js/路径')
->setDescription('处理宝塔面板vite/js文件');
}
protected function execute(Input $input, Output $output)
{
$dir = trim($input->getArgument('dir'));
if(!file_exists($dir)){
$output->writeln('目录不存在');
return;
}
//$this->handlefile($dir.'/DockerImages.js');
$this->checkdir($dir);
}
private function getExtendCode($content, $part, $n = 1, $startChar = '{', $endChar = '}'){
if(!$part) return false;
$length = strlen($content);
$start = strpos($content, $part);
if($start===false)return false;
$end = $start+strlen($part);
$start--;
$c = 0;
for($i=$start;$i>=0;$i--){
if(substr($content,$i,1) == $startChar) $c++;
if(substr($content,$i,1) == $endChar) $c--;
if($c == $n){
$start = $i;
break;
}
}
$c = 0;
for($i=$end;$i<=$length;$i++){
if(substr($content,$i,1) == $endChar) $c++;
if(substr($content,$i,1) == $startChar) $c--;
if($c == $n){
$end = $i;
break;
}
}
return substr($content, $start, $end - $start + 1);
}
private function getExtendFunction($content, $part, $startChar = '(', $endChar = ')'){
$code = $this->getExtendCode($content, $part, 1, $startChar, $endChar);
if(!$code) return false;
$start = strpos($content, $code) - 1;
$end = $start + strlen($code);
for($i=$start;$i>=0;$i--){
$char = substr($content,$i,1);
if(!ctype_alpha($char)&&$char!='_'){
$start = $i+1;
break;
}
}
if(substr($content,$start-1,1) == ',') $start--;
else if(substr($content,$end+1,1) == ',') $end++;
return substr($content, $start, $end - $start + 1);
}
private function checkdir($basedir){
if($dh=opendir($basedir)){
while (($file=readdir($dh)) !== false){
if($file != '.' && $file != '..'){
if(!is_dir($basedir.'/'.$file) && substr($file,-3)=='.js'){
$this->handlefile($basedir.'/'.$file);
}else if(!is_dir($basedir.'/'.$file) && substr($file,-4)=='.map'){
unlink($basedir.'/'.$file);
}
}
}
closedir($dh);
}
}
private function str_replace_once($needle, $replace, $haystack) {
$pos = strpos($haystack, $needle);
if ($pos === false) {
return $haystack;
}
return substr_replace($haystack, $replace, $pos, strlen($needle));
}
private function handlefile($filepath){
//echo $filepath."\n";
$file = file_get_contents($filepath);
if(!$file)return;
$flag = false;
if(strpos($file, 'window.location.protocol.indexOf("https")>=0')!==false){ //index
$file = str_replace('window.location.protocol.indexOf("https")>=0', '!0', $file);
$code = $this->getExtendCode($file, 'isGetCoupon:', 2);
if($code){
$file = str_replace($code, '{}', $file);
}
$file = preg_replace('!recommendShow:\w+,!', 'recommendShow:!1,', $file, 1);
$code = $this->getExtendCode($file, '"打开需求反馈"', 1, '[', ']');
if($code){
$file = str_replace($code, '[]', $file);
}
$flag = true;
}
if(strpos($file, '"点击打开调查问卷"')!==false){ //index
$code = $this->getExtendCode($file, '"点击打开调查问卷"', 2);
if($code){
$file = str_replace($code, '{}', $file);
}
$flag = true;
}
if(strpos($file, '您有{0}个优惠券待领取')!==false){ //win-index
$code = $this->getExtendCode($file, 'isGetCoupon:', 2);
if($code){
$file = str_replace($code, '{}', $file);
}
$flag = true;
}
if(strpos($file, '论坛求助')!==false && strpos($file, '"/other/customer-qrcode.png"')!==false){ //main
$code = $this->getExtendCode($file, '"微信公众号"', 1);
$code = $this->getExtendFunction($file, $code);
$start = strpos($file, $code) - 1;
for($i=$start;$i>=0;$i--){
if(substr($file,$i,1) == ','){
$start = $i;
break;
}
}
$code = $this->getExtendCode($file, '"/other/customer-qrcode.png"', 2);
$code = $this->getExtendFunction($file, $code);
$end = strpos($file, $code)+strlen($code);
$code = substr($file, $start, $end - $start);
$file = str_replace($code, '', $file);
$flag = true;
}
if(strpos($file, 'useNegotiate')!==false){ //utils
$code = $this->getExtendCode($file, 'createPeerConnection()', 1);
if($code){
$file = str_replace($code, '{}', $file);
}
$file = preg_replace('!computed\(\(\)=>"calc"===\w+\.\w+\.type\)!', '!1', $file);
$file = preg_replace('!computed\(\(\)=>"input"===\w+\.\w+\.type\)!', '!1', $file);
$file = preg_replace('!computed\(function\(\)\{return"calc"===\w+\.\w+\.type\}\)!', '!1', $file);
$file = preg_replace('!computed\(function\(\)\{return"input"===\w+\.\w+\.type\}\)!', '!1', $file);
$file = preg_replace('!computed\(\(\)=>\w+\.\w+\.type==="calc"\)!', '!1', $file);
$file = preg_replace('!computed\(\(\)=>\w+\.\w+\.type==="input"\)!', '!1', $file);
$file = preg_replace('!computed\(function\(\)\{return\w+\.\w+\.type==="calc"\}\)!', '!1', $file);
$file = preg_replace('!computed\(function\(\)\{return\w+\.\w+\.type==="input"\}\)!', '!1', $file);
$code = $this->getExtendCode($file, '"自动部署"', 2);
if($code){
$file = str_replace($code.',', '', $file);
$file = str_replace($code, '', $file);
}
$flag = true;
}
if(strpos($file, '"sqlserver管理"')!==false && strpos($file, '"iis管理"')!==false){ //win-utils
$file = preg_replace('!"calc"===\w+\.\w+\.type!', '!1', $file);
$file = preg_replace('!"input"===\w+\.\w+\.type!', '!1', $file);
$flag = true;
}
if(strpos($file, '请冷静几秒钟,确认以下要删除的数据')!==false && strpos($file, '"计算结果:"')!==false){ //site
$code = $this->getExtendCode($file, '"计算结果:"', 1, '[', ']');
$code = $this->getExtendFunction($file, $code);
$file = str_replace($code, '', $file);
$file = preg_replace('!\w+\.sum===\w+\.addend1\+\w+\.addend2!', '!0', $file);
$file = preg_replace('!value=\!0,(\w+)\.value=5;!', 'value=!1,$1.value=0;', $file);
$flag = true;
}
if(strpos($file, '"left-waf"')!==false && strpos($file, '"iconWaf"')!==false){ //site.table
$code = $this->getExtendCode($file, '"left-waf"');
$code = $this->getExtendCode($file, $code, 1, '[', ']');
$code = $this->getExtendFunction($file, $code);
$file = str_replace($code, '""', $file);
$flag = true;
}
if(strpos($file, 'svgtofont-left-waf')!==false && strpos($file, '"iconWaf"')!==false){ //site.table
$code = $this->getExtendCode($file, 'svgtofont-left-waf');
$code = $this->getExtendCode($file, $code, 1, '[', ']');
$code = $this->getExtendFunction($file, $code);
$file = str_replace($code, '""', $file);
$flag = true;
}
if(strpos($file, 'label:"商用SSL证书"')!==false){ //site-ssl
$code = $this->getExtendFunction($file, 'label:"商用SSL证书"', '{', '}');
$file = str_replace($code, '', $file);
$code = $this->getExtendFunction($file, 'label:"测试证书"', '{', '}');
if($code){
$file = str_replace($code, '', $file);
}
$code = $this->getExtendFunction($file, 'label:"宝塔证书"', '{', '}');
if($code){
$file = str_replace($code, '', $file);
}
$code = $this->getExtendCode($file, '"购买商业证书"', 2);
if($code){
$code2 = str_replace('"busSslList"', '"letsEncryptList"', $code);
$code2 = str_replace($this->getExtendFunction($code, '"购买商业证书"'), '', $code2);
$file = str_replace($code, $code2, $file);
}
$file = str_replace('.value="busSslList"', '.value="letsEncryptList"', $file);
$flag = true;
}
if(strpos($file, '"busSslList"')!==false && strpos($filepath, '/useStore')){ //site-ssl
$file = str_replace('"busSslList"', '"currentCertInfo"', $file);
$flag = true;
}
if(strpos($file, '"商用SSL"')!==false){ //ssl
$code = $this->getExtendFunction($file, '"商用SSL"', '{', '}');
$file = str_replace($code, '', $file);
$code = $this->getExtendFunction($file, '"宝塔证书"', '{', '}');
if($code){
$file = str_replace($code, '', $file);
}
for($i=0;$i<3;$i++){
$code = $this->getExtendCode($file, ',"联系客服"', 2, '[', ']');
if($code){
$file = str_replace($code, '[]', $file);
}
}
$flag = true;
}
if(strpos($file, '"SSL-CERTIFICATE-STORE"')!==false){ //ssl
$file = str_replace('("ssl")', '("encrypt")', $file);
$flag = true;
}
if(strpos($file, '"商业证书"')!==false && strpos($file, 'name:"busSslList"')!==false && strpos($file, 'IIS配置')!==false){ //win-ssl
$code = $this->getExtendFunction($file, 'name:"busSslList"', '{', '}');
$file = str_replace($code, '', $file);
$code = $this->getExtendFunction($file, 'name:"trustAsiaList"', '{', '}');
$file = str_replace($code, '', $file);
$file = $this->str_replace_once('"busSslList"', '"currentCertInfo"', $file);
$flag = true;
}
if(strpos($file, '如果您希望添加其它Docker应用')!==false){
$code = $this->getExtendCode($file, '如果您希望添加其它Docker应用', 1, '[', ']');
$code = $this->getExtendFunction($file, $code);
$file = str_replace($code, '', $file);
$flag = true;
}
if(strpos($file, '"recom-view"')!==false){ //soft
$code = $this->getExtendCode($file, '"recom-view"');
$code = $this->getExtendFunction($file, $code);
$file = str_replace($code, '', $file);
$flag = true;
}
if(strpos($file, '"打开插件文件目录"')!==false && strpos($file, '"(续费)"')!==false){ //soft.table
$code = $this->getExtendFunction($file, '"(续费)"');
$file = str_replace($code, '""', $file);
$code = $this->getExtendCode($file, 'activity_id:47', 2);
if($code){
$file = str_replace($code, '{}', $file);
}
$flag = true;
}
if(strpos($file, 'path:"register"')!==false){ //domain
$code = $this->getExtendCode($file, 'path:"register"');
$file = str_replace($code.',', '', $file);
$flag = true;
}
for($i=0;$i<5;$i++){
$code = $this->getExtendCode($file, ',"需求反馈"', 1, '[', ']');
if($code){
if(strpos($code, 'svgtofont-desired')){
$file = str_replace($code, '[]', $file);
}else{
$code = $this->getExtendFunction($code, ',"需求反馈"');
$file = str_replace($code, '', $file);
}
$flag = true;
}
}
$code = $this->getExtendCode($file, '("需求反馈",-1)', 1, '[', ']');
if($code){
$file = str_replace($code, '[]', $file);
$flag = true;
}
$code = $this->getExtendCode($file, '(" 需求反馈 ",-1)', 1, '[', ']');
if($code){
$file = str_replace($code, '[]', $file);
$flag = true;
}
$code = $this->getExtendFunction($file, 'label:"需求反馈",', '{', '}');
if($code){
$file = str_replace($code, '', $file);
$flag = true;
}
if(strpos($file, '暂无搜索结果,<span class="text-primary cursor-pointer NpsDialog">提交需求反馈</span>')!==false){
$file = str_replace('暂无搜索结果,<span class="text-primary cursor-pointer NpsDialog">提交需求反馈</span>', '暂无搜索结果', $file);
$flag = true;
}
if(strpos($file, 'getReceiveCoupon()')!==false){ //aapanel-优惠券
$code = $this->getExtendCode($file, 'getReceiveCoupon()');
$file = str_replace($code, '{}', $file);
$flag = true;
}
if(strpos($file, '"Site.DelSite.index_1"')!==false){ //aapanel-site
$code = $this->getExtendCode($file, '"Site.DelSite.index_10"', 3, '(', ')');
if($code){
$code = $this->getExtendFunction($file, $code);
$file = str_replace($code, '', $file);
$file = preg_replace('@\w+\.value!==\w+\.value\+\w+\.value@', '!1', $file);
$file = preg_replace('@null==\w+\.value\|\|null==\w+\.value@', '!1', $file);
$file = str_replace('disabled:!0', 'disabled:!1', $file);
$flag = true;
}
}
if(strpos($file, '"Component.Confirm.index_4"')!==false){ //aapanel-public
$code = $this->getExtendCode($file, '"Component.Confirm.index_4"', 2, '(', ')');
if($code){
$code = $this->getExtendFunction($file, $code);
$file = str_replace($code, '', $file);
$file = preg_replace('@\w+\.value===\w+\.value\+\w+\.value@', '!0', $file);
$flag = true;
}
$code = $this->getExtendCode($file, '"Component.Confirm.index_1"', 1, '(', ')');
if($code){
$code = $this->getExtendFunction($file, $code);
$file = str_replace($code, '', $file);
$file = preg_replace('@\w+\.value===\w+\.value\?@', '!0?', $file);
$flag = true;
}
}
if(strpos($file, '"Component.Feedback.index_7"')!==false){ //aapanel-需求反馈
$code = $this->getExtendCode($file, '"Component.Feedback.index_7"', 2);
if($code){
$code = $this->getExtendFunction($file, $code);
$file = str_replace($code, '', $file);
$flag = true;
}
}
if(strpos($file, '"Soft.index_16"')!==false){ //aapanel-soft
$code = $this->getExtendCode($file, '"Soft.index_16"', 2);
if($code){
$file = str_replace($code, '{}', $file);
$flag = true;
}
}
if(!$flag) return;
if(file_put_contents($filepath, $file)){
echo '文件:'.$filepath.' 处理成功'."\n";
}else{
echo '文件:'.$filepath.' 处理失败,可能无写入权限'."\n";
}
}
}

View File

@ -45,37 +45,34 @@ class DecryptFile extends Command
$output->writeln($e->getMessage()); $output->writeln($e->getMessage());
} }
}elseif($type == 'module'){ }elseif($type == 'module'){
try{ $this->decode_module_file($output, $file);
$res = Plugins::decode_module_file($file);
if($res == 2){
$output->writeln('文件解密失败!');
}elseif($res == 1){
$output->writeln('文件解密成功!');
}
}catch(\Exception $e){
$output->writeln($e->getMessage());
}
}elseif($type == 'classdir'){ }elseif($type == 'classdir'){
$file = rtrim($file, '/'); $file = rtrim($file, '/');
if(!file_exists($file.'/common.py')){ if(file_exists($file.'/common.py')){
$class_v = 1;
}elseif(file_exists($file.'/common_v2.py')){
$class_v = 2;
}else{
$output->writeln('当前路径非宝塔面板class目录'); $output->writeln('当前路径非宝塔面板class目录');
return; return;
} }
$dirs = glob($file.'/*Model'); $dirs = glob($file.'/*Model'.($class_v == 2 ? 'V2' : ''));
foreach($dirs as $dir){ foreach($dirs as $dir){
if(!is_dir($dir))continue; if(!is_dir($dir))continue;
$files = glob($dir.'/*Model.py'); $files = glob($dir.'/*Model.py');
foreach($files as $file){ foreach($files as $filepath){
try{ $this->decode_module_file($output, $filepath);
$res = Plugins::decode_module_file($file); }
if($res == 2){ }
$output->writeln('文件解密失败:'.$file); if($class_v == 2){
}elseif($res == 1){ $filepath = $file.'/wp_toolkit/core.py';
$output->writeln('文件解密成功:'.$file); if(file_exists($filepath)){
} $this->decode_module_file($output, $filepath);
}catch(\Exception $e){ }
$output->writeln($e->getMessage().''.$file); }else{
} $filepath = $file.'/public/authorization.py';
if(file_exists($filepath)){
$this->decode_module_file($output, $filepath);
} }
} }
}elseif($type == 'all'){ }elseif($type == 'all'){
@ -95,21 +92,24 @@ class DecryptFile extends Command
$this->scan_all_file($input, $output, $filepath); $this->scan_all_file($input, $output, $filepath);
} }
elseif(substr($filepath, -3) == '.py') { elseif(substr($filepath, -3) == '.py') {
try{ $this->decode_module_file($output, $filepath);
$res = Plugins::decode_module_file($filepath);
if($res == 2){
$output->writeln('文件解密失败:'.$filepath);
}elseif($res == 1){
$output->writeln('文件解密成功:'.$filepath);
}
}catch(\Exception $e){
$output->writeln($e->getMessage().''.$filepath);
}
} }
} }
} }
closedir($dir); closedir($dir);
} }
private function decode_module_file(Output $output, $filepath){
try{
$res = Plugins::decode_module_file($filepath);
if($res == 2){
$output->writeln('文件解密失败:'.$filepath);
}elseif($res == 1){
$output->writeln('文件解密成功:'.$filepath);
}
}catch(\Exception $e){
$output->writeln($e->getMessage().''.$filepath);
}
}
} }

View File

@ -31,6 +31,9 @@ class UpdateAll extends Command
if(!config_get('wbt_type') && config_get('wbt_url') || config_get('wbt_type')==1 && config_get('wbt_surl')){ if(!config_get('wbt_type') && config_get('wbt_url') || config_get('wbt_type')==1 && config_get('wbt_surl')){
$this->process_plugins($input, $output, 'Windows'); $this->process_plugins($input, $output, 'Windows');
} }
if(!config_get('enbt_type') && config_get('enbt_url') || config_get('enbt_type')==1 && config_get('enbt_surl')){
$this->process_plugins($input, $output, 'en');
}
config_set('runtime', date('Y-m-d H:i:s')); config_set('runtime', date('Y-m-d H:i:s'));
} }
@ -43,12 +46,19 @@ class UpdateAll extends Command
$count = 0; $count = 0;
$type = intval(config_get($os=='Windows'?'updateall_type_win':'updateall_type')); if($os=='Windows'){
$type = intval(config_get('updateall_type_win'));
}elseif($os=='en'){
$type = intval(config_get('updateall_type_en'));
}else{
$type = intval(config_get('updateall_type'));
}
$json_arr = Plugins::get_plugin_list($os); $json_arr = Plugins::get_plugin_list($os);
//循环下载缺少的插件 //循环下载缺少的插件
foreach($json_arr['list'] as $plugin){ foreach($json_arr['list'] as $plugin){
if($type == 0 && ($plugin['type']==8 || $plugin['type']==12) || $type == 1 && $plugin['type']==12 || $plugin['type']==10 || $plugin['type']==5) continue; if($type == 0 && ($plugin['type']==8 || $plugin['type']==12) || $type == 1 && $plugin['type']==12 || $plugin['type']==10 || $plugin['type']==5) continue;
if(in_array($plugin['name'], \app\lib\BtPlugins::$skip_plugins)) continue;
foreach($plugin['versions'] as $version){ foreach($plugin['versions'] as $version){
$ver = $version['m_version'].'.'.$version['version']; $ver = $version['m_version'].'.'.$version['version'];

View File

@ -3,7 +3,13 @@
use think\facade\Db; use think\facade\Db;
function get_data_dir($os = 'Linux'){ function get_data_dir($os = 'Linux'){
return app()->getRootPath().'data/'.($os == 'Windows' ? 'win/' : ''); if($os == 'en'){
return app()->getRootPath().'data/en/';
}elseif($os == 'Windows'){
return app()->getRootPath().'data/win/';
}else{
return app()->getRootPath().'data/';
}
} }
@ -180,6 +186,11 @@ function checkIfActive($string) {
return null; return null;
} }
function checkDomain($domain){
if(empty($domain) || !preg_match('/^[-$a-z0-9_*.]{2,512}$/i', $domain) || (stripos($domain, '.') === false) || substr($domain, -1) == '.' || substr($domain, 0 ,1) == '.' || substr($domain, 0 ,1) == '*' && substr($domain, 1 ,1) != '.' || substr_count($domain, '*')>1 || strpos($domain, '*')>0 || strlen($domain)<4) return false;
return true;
}
function errorlog($msg){ function errorlog($msg){
$handle = fopen(app()->getRootPath()."record.txt", 'a'); $handle = fopen(app()->getRootPath()."record.txt", 'a');
fwrite($handle, date('Y-m-d H:i:s')."\t".$msg."\r\n"); fwrite($handle, date('Y-m-d H:i:s')."\t".$msg."\r\n");
@ -224,4 +235,95 @@ function pemToBase64($pem){
} }
} }
return $encoded; return $encoded;
}
function makeSelfSignSSL(string $commonName, array $domainList, $validity = 3650){
// 加载 CA 证书和私钥
$dir = app()->getBasePath().'script/';
$caCert = file_get_contents($dir.'ca.crt');
$caPrivateKey = file_get_contents($dir.'ca.key');
$opensslConfigFile = sys_get_temp_dir().'/openssl'.time().mt_rand(1000, 9999).'.cnf';
$opensslConfigContent = <<<EOF
[req]
req_extensions = extension_section
x509_extensions = extension_section
distinguished_name = dn
[dn]
[extension_section]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
EOF;
$ip_index = 1;
$dns_index = 1;
foreach ($domainList as $value) {
if(empty($value)) continue;
if(filter_var($value, FILTER_VALIDATE_IP)){
$opensslConfigContent .= sprintf("\nIP.%d = %s", $ip_index, $value);
$ip_index++;
}else{
$opensslConfigContent .= sprintf("\nDNS.%d = %s", $dns_index, $value);
$dns_index++;
}
}
if(!file_put_contents($opensslConfigFile, $opensslConfigContent)) return false;
// 生成域名证书的私钥和 CSR
$domainPrivateKey = openssl_pkey_new([
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
if(!$domainPrivateKey) return false;
$csrConfig = ['digest_alg' => 'sha256', 'config' => $opensslConfigFile];
$domainCsr = openssl_csr_new([
'commonName' => $commonName
], $domainPrivateKey, $csrConfig);
if(!$domainCsr) return false;
// 生成域名证书
$domainCertificate = openssl_csr_sign($domainCsr, $caCert, $caPrivateKey, $validity, $csrConfig);
if(!$domainCertificate) return false;
// 导出域名证书
openssl_x509_export($domainCertificate, $certificate);
openssl_pkey_export($domainPrivateKey, $privateKey);
$certificate .= $caCert;
unlink($opensslConfigFile);
return ['cert' => $certificate, 'key' => $privateKey];
}
function deleteDir($dir){
$rd = opendir($dir);
if (!$rd) {
return false;
}
while (($file = readdir($rd)) !== false) {
if ($file == '.' || $file == '..') {
continue;
}
$file = $dir . '/' . $file;
if (is_dir($file)) {
deleteDir($file);
}
else {
unlink($file);
}
}
closedir($rd);
rmdir($dir);
return true;
} }

View File

@ -100,6 +100,7 @@ class Admin extends BaseController
View::assign('conf', config('sys')); View::assign('conf', config('sys'));
$runtime = Db::name('config')->where('key','runtime')->value('value') ?? '<font color="red">未运行</font>'; $runtime = Db::name('config')->where('key','runtime')->value('value') ?? '<font color="red">未运行</font>';
View::assign('runtime', $runtime); View::assign('runtime', $runtime);
View::assign('is_user_www', isset($_SERVER['USER']) && $_SERVER['USER'] == 'www');
return view(); return view();
} }
@ -143,18 +144,33 @@ class Admin extends BaseController
}else{ }else{
$bt_url = input('post.bt_url'); $bt_url = input('post.bt_url');
$bt_key = input('post.bt_key'); $bt_key = input('post.bt_key');
$os = input('post.os');
if(!$bt_url || !$bt_key)return json(['code'=>-1, 'msg'=>'参数不能为空']); if(!$bt_url || !$bt_key)return json(['code'=>-1, 'msg'=>'参数不能为空']);
$btapi = new Btapi($bt_url, $bt_key); $btapi = new Btapi($bt_url, $bt_key);
$result = $btapi->get_config(); if ($os == 'win') {
if($result && isset($result['status']) && ($result['status']==1 || isset($result['sites_path']))){ $result = $btapi->get_config_go();
$result = $btapi->get_user_info(); if($result && isset($result['config'])){
if($result && isset($result['username'])){ $result = $btapi->get_user_info();
return json(['code'=>0, 'msg'=>'面板连接测试成功!']); if($result && isset($result['username'])){
return json(['code'=>0, 'msg'=>'面板连接测试成功!']);
}else{
return json(['code'=>-1, 'msg'=>'面板连接测试成功,但未安装专用插件/未登录账号']);
}
}else{ }else{
return json(['code'=>-1, 'msg'=>'面板连接测试成功,但未安装专用插件']); return json(['code'=>-1, 'msg'=>isset($result['msg'])?$result['msg']:'面板地址无法连接']);
}
} else {
$result = $btapi->get_config();
if($result && isset($result['status']) && ($result['status']==1 || isset($result['sites_path']))){
$result = $btapi->get_user_info();
if($result && isset($result['username'])){
return json(['code'=>0, 'msg'=>'面板连接测试成功!']);
}else{
return json(['code'=>-1, 'msg'=>'面板连接测试成功,但未安装专用插件/未登录账号']);
}
}else{
return json(['code'=>-1, 'msg'=>isset($result['msg'])?$result['msg']:'面板地址无法连接']);
} }
}else{
return json(['code'=>-1, 'msg'=>isset($result['msg'])?$result['msg']:'面板地址无法连接']);
} }
} }
} }
@ -169,6 +185,7 @@ class Admin extends BaseController
} }
} }
View::assign('typelist', $typelist); View::assign('typelist', $typelist);
View::assign('skip_plugins', \app\lib\BtPlugins::$skip_plugins);
return view(); return view();
} }
@ -185,6 +202,19 @@ class Admin extends BaseController
return view(); return view();
} }
public function pluginsen(){
$typelist = [];
$json_arr = Plugins::get_plugin_list('en');
if($json_arr){
foreach($json_arr['type'] as $type){
if($type['title'] == '一键部署') continue;
$typelist[$type['id']] = $type['title'];
}
}
View::assign('typelist', $typelist);
return view();
}
public function plugins_data(){ public function plugins_data(){
$type = input('post.type/d'); $type = input('post.type/d');
$keyword = input('post.keyword', null, 'trim'); $keyword = input('post.keyword', null, 'trim');
@ -226,7 +256,7 @@ class Admin extends BaseController
'name' => $plugin['name'], 'name' => $plugin['name'],
'title' => $plugin['title'], 'title' => $plugin['title'],
'type' => $plugin['type'], 'type' => $plugin['type'],
'typename' => $typelist[$plugin['type']], 'typename' => isset($typelist[$plugin['type']]) ? $typelist[$plugin['type']] : '未知',
'desc' => str_replace('target="_blank"','target="_blank" rel="noopener noreferrer"',$plugin['ps']), 'desc' => str_replace('target="_blank"','target="_blank" rel="noopener noreferrer"',$plugin['ps']),
'price' => $plugin['price'], 'price' => $plugin['price'],
'author' => isset($plugin['author']) ? $plugin['author'] : '官方', 'author' => isset($plugin['author']) ? $plugin['author'] : '官方',
@ -400,4 +430,37 @@ class Admin extends BaseController
Cache::clear(); Cache::clear();
return json(['code'=>0,'msg'=>'succ']); return json(['code'=>0,'msg'=>'succ']);
} }
public function ssl(){
if(request()->isAjax()){
$domain_list = input('post.domain_list', null, 'trim');
$common_name = input('post.common_name', null, 'trim');
$validity = input('post.validity/d');
if(empty($domain_list) || empty($validity)){
return json(['code'=>-1, 'msg'=>'参数不能为空']);
}
$array = explode("\n", $domain_list);
$domain_list = [];
foreach($array as $domain){
$domain = trim($domain);
if(empty($domain)) continue;
if(!checkDomain($domain)) return json(['code'=>-1, 'msg'=>'域名或IP格式不正确:'.$domain]);
$domain_list[] = $domain;
}
if(empty($domain_list)) return json(['code'=>-1, 'msg'=>'域名列表不能为空']);
if(empty($common_name)) $common_name = $domain_list[0];
$result = makeSelfSignSSL($common_name, $domain_list, $validity);
if(!$result){
return json(['code'=>-1, 'msg'=>'生成证书失败']);
}
return json(['code'=>0, 'msg'=>'生成证书成功', 'cert'=>$result['cert'], 'key'=>$result['key']]);
}
$dir = app()->getBasePath().'script/';
$ssl_path = app()->getRootPath().'public/ssl/baota_root.pfx';
$ssl_path_mac = app()->getRootPath().'public/ssl/baota_root.crt';
$isca = file_exists($dir.'ca.crt') && file_exists($dir.'ca.key') && file_exists($ssl_path) && file_exists($ssl_path_mac);
View::assign('isca', $isca);
return view();
}
} }

View File

@ -2,6 +2,7 @@
namespace app\controller; namespace app\controller;
use think\facade\Db; use think\facade\Db;
use think\facade\Cache;
use app\BaseController; use app\BaseController;
use app\lib\Plugins; use app\lib\Plugins;
@ -18,13 +19,16 @@ class Api extends BaseController
Db::name('record')->insert(['ip'=>$this->clientip, 'addtime'=>date("Y-m-d H:i:s"), 'usetime'=>date("Y-m-d H:i:s")]); Db::name('record')->insert(['ip'=>$this->clientip, 'addtime'=>date("Y-m-d H:i:s"), 'usetime'=>date("Y-m-d H:i:s")]);
} }
$json_arr = Plugins::get_plugin_list(); $json_arr = Plugins::get_plugin_list();
if(!$json_arr) return json((object)[]); if(!$json_arr) $json_arr = (object)[];
return json($json_arr); return json($json_arr);
} }
//获取插件列表(win) //获取插件列表(win)
public function get_plugin_list_win(){ public function get_plugin_list_win(){
if(!$this->checklist()) return json('你的服务器被禁止使用此云端'); if(!$this->checklist()) return json('你的服务器被禁止使用此云端');
$os_version = input('post.os_version');
$serverid = input('post.serverid');
$uid = input('post.uid');
$record = Db::name('record')->where('ip',$this->clientip)->find(); $record = Db::name('record')->where('ip',$this->clientip)->find();
if($record){ if($record){
Db::name('record')->where('id',$record['id'])->update(['usetime'=>date("Y-m-d H:i:s")]); Db::name('record')->where('id',$record['id'])->update(['usetime'=>date("Y-m-d H:i:s")]);
@ -32,29 +36,66 @@ class Api extends BaseController
Db::name('record')->insert(['ip'=>$this->clientip, 'addtime'=>date("Y-m-d H:i:s"), 'usetime'=>date("Y-m-d H:i:s")]); Db::name('record')->insert(['ip'=>$this->clientip, 'addtime'=>date("Y-m-d H:i:s"), 'usetime'=>date("Y-m-d H:i:s")]);
} }
$json_arr = Plugins::get_plugin_list('Windows'); $json_arr = Plugins::get_plugin_list('Windows');
if(!$json_arr) return json((object)[]); if(!$json_arr) $json_arr = (object)[];
if($os_version == 'windows_go'){
return Plugins::encrypt_plugin_list($json_arr, $serverid, $uid);
}
return json($json_arr);
}
//获取插件列表(aapanel)
public function get_plugin_list_en(){
if(!$this->checklist()) return json('你的服务器被禁止使用此云端');
$record = Db::name('record')->where('ip',$this->clientip)->find();
if($record){
Db::name('record')->where('id',$record['id'])->update(['usetime'=>date("Y-m-d H:i:s")]);
}else{
Db::name('record')->insert(['ip'=>$this->clientip, 'addtime'=>date("Y-m-d H:i:s"), 'usetime'=>date("Y-m-d H:i:s")]);
}
$json_arr = Plugins::get_plugin_list('en');
if(!$json_arr) $json_arr = (object)[];
return json($json_arr); return json($json_arr);
} }
//下载插件包 //下载插件包
public function download_plugin(){ public function download_plugin(){
$plugin_name = input('post.name'); $plugin_name = input('param.name');
$version = input('post.version'); $version = input('param.version');
$os = input('post.os'); $os = input('param.os');
if(!$plugin_name || !$version){ if(!$plugin_name || !$version){
return '参数不能为空'; return json(['status'=>false, 'msg'=>'参数不能为空']);
} }
if(!in_array($os,['Windows','Linux'])) $os = 'Linux'; if(!in_array($os,['Windows','Linux'])) $os = 'Linux';
if(!preg_match('/^[a-zA-Z0-9_]+$/', $plugin_name) || !preg_match('/^[0-9.]+$/', $version)){ if(!preg_match('/^[a-zA-Z0-9_]+$/', $plugin_name) || !preg_match('/^[0-9.]+$/', $version)){
return '参数不正确'; return json(['status'=>false, 'msg'=>'参数不正确']);
} }
if(!$this->checklist()) '你的服务器被禁止使用此云端'; if(!$this->checklist()) return json(['status'=>false, 'msg'=>'你的服务器被禁止使用此云端']);
$filepath = get_data_dir($os).'plugins/package/'.$plugin_name.'-'.$version.'.zip'; $filepath = get_data_dir($os).'plugins/package/'.$plugin_name.'-'.$version.'.zip';
if(file_exists($filepath)){ if(file_exists($filepath)){
$filename = $plugin_name.'.zip'; $filename = $plugin_name.'.zip';
$this->output_file($filepath, $filename); $this->output_file($filepath, $filename);
}else{ }else{
return '云端不存在该插件包'; return json(['status'=>false, 'msg'=>'云端不存在该插件包']);
}
}
//下载插件包aapanel
public function download_plugin_en(){
$plugin_name = input('param.name');
$version = input('param.version');
if(!$plugin_name || !$version){
return json(['status'=>false, 'msg'=>'参数不能为空']);
}
if(!preg_match('/^[a-zA-Z0-9_]+$/', $plugin_name) || !preg_match('/^[0-9.]+$/', $version)){
return json(['status'=>false, 'msg'=>'参数不正确']);
}
if(!$this->checklist()) return json(['status'=>false, 'msg'=>'你的服务器被禁止使用此云端']);
$filepath = get_data_dir('en').'plugins/package/'.$plugin_name.'-'.$version.'.zip';
if(file_exists($filepath)){
$filename = $plugin_name.'.zip';
$this->output_file($filepath, $filename);
}else{
return json(['status'=>false, 'msg'=>'云端不存在该插件包']);
} }
} }
@ -64,25 +105,27 @@ class Api extends BaseController
$version = input('post.version'); $version = input('post.version');
$os = input('post.os'); $os = input('post.os');
if(!$plugin_name || !$version){ if(!$plugin_name || !$version){
return '参数不能为空'; return json(['status'=>false, 'msg'=>'参数不能为空']);
} }
if(!in_array($os,['Windows','Linux'])) $os = 'Linux'; if(!in_array($os,['Windows','Linux'])) $os = 'Linux';
if(!preg_match('/^[a-zA-Z0-9_]+$/', $plugin_name) || !preg_match('/^[0-9.]+$/', $version)){ if(!preg_match('/^[a-zA-Z0-9_]+$/', $plugin_name) || !preg_match('/^[0-9.]+$/', $version)){
return '参数不正确'; return json(['status'=>false, 'msg'=>'参数不正确']);
} }
if(!$this->checklist()) '你的服务器被禁止使用此云端'; if(!$this->checklist()) return json(['status'=>false, 'msg'=>'你的服务器被禁止使用此云端']);
$filepath = get_data_dir($os).'plugins/main/'.$plugin_name.'-'.$version.'.dat'; $filepath = get_data_dir($os).'plugins/package/'.$plugin_name.'-'.$version.'.zip';
if(file_exists($filepath)){ $mainfilepath = get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
if(file_exists($mainfilepath)){
$filename = $plugin_name.'_main.py'; $filename = $plugin_name.'_main.py';
$this->output_file($filepath, $filename); $this->output_file($mainfilepath, $filename);
}else{ }elseif(file_exists($filepath)){
$filepath = get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py'; $zip = new \ZipArchive;
if(file_exists($filepath)){ if ($zip->open($filepath) === true){
$filename = $plugin_name.'_main.py'; echo $zip->getFromName($plugin_name.'/'.$plugin_name.'_main.py');
$this->output_file($filepath, $filename);
}else{ }else{
return '云端不存在该插件主文件'; return json(['status'=>false, 'msg'=>'插件包解压缩失败']);
} }
}else{
return json(['status'=>false, 'msg'=>'云端不存在该插件主文件']);
} }
} }
@ -90,7 +133,10 @@ class Api extends BaseController
public function download_plugin_other(){ public function download_plugin_other(){
$fname = input('get.fname'); $fname = input('get.fname');
if(!$fname){ if(!$fname){
return json(['status'=>false, 'msg'=>'参数不能为空']); $fname = input('get.filename');
if(!$fname){
return json(['status'=>false, 'msg'=>'参数不能为空']);
}
} }
if(strpos(dirname($fname),'.')!==false)return json(['status'=>false, 'msg'=>'参数不正确']); if(strpos(dirname($fname),'.')!==false)return json(['status'=>false, 'msg'=>'参数不正确']);
if(!$this->checklist()) return json(['status'=>false, 'msg'=>'你的服务器被禁止使用此云端']); if(!$this->checklist()) return json(['status'=>false, 'msg'=>'你的服务器被禁止使用此云端']);
@ -103,6 +149,36 @@ class Api extends BaseController
} }
} }
public function get_plugin_auth(){
$productids = ["8","9","10","11","12","13","14","15","16","17","18","19","20","22","23","24","25","26","28","32","33","42","44","45","47","55","65","69","75","82","83","85","90","91","97","99","101","107","108","110","118","121","127","128","132","135","136","140","143","144","151","154","156","161","163","167","173","179","183","185","190","192","195","197","200","201","202","203","204","205","206","207","208","212","213","214","215","216","217","218","219","220","221","222","223","224","225","226","227","228","229","230","231","232","233","234","235","236","237","238","239","241","243","244","245","246","247","248","249","250","251","252","253","254","255","256","257","258","259","261","262","263","264","265","266","267","268","269","270","271","272","273","274","275","276","277","278","279","280","281","282","283","284","285","286","287","289","292","293","295","296","297","298","299","300","301","302","303","304","305","306","307","308","309","310","311","312","313","314","315","316","317","318","319","320","321","322","323","324","325","326","327","328","329","330","331","332","334","335","336","337","338","339","340","341","342","343","344","345","346","347","348","349","350","351","352","353","354","355","356","357","358","359","360","361","362","363","364","365","366","368","369","371","372","373","374","375","376","377","378","379","380","381","382","383","384","385","386","387","388","389","390","391","392","393","394","397","398","400","401","406","408","409","411","413","415","419","423","425","427","429","430","1111111","100000001","100000005","100000007","100000008","100000009","100000010","100000012","100000014","100000015","100000016","100000017","100000035","100000036","100000039","100000040","100000041","100000042","100000045","100000053","100000054","100000056","100000057","100000058","100000059","100000062","100000063","100000067","100000069","100000070","100000076","100000077","100000078","100000079","100000080","100000084","100000085","100000088","100000089","100000090","100000091","100000092","100000093","100000094","100000095","100000096","100000097","100000098"];
$os_version = input('post.os_version');
$address = input('post.address','');
$uid = input('post.uid','');
$username = input('post.username','');
$serverid = input('post.serverid','');
$mac = input('post.mac','');
$data = ['ip'=>$address, 'uid'=>$uid, 'username'=>$username, 'serverid'=>$serverid, 'lasttime'=>time(), 'pro'=>-1, 'skey'=>'', 'ltd'=>strtotime('+10 year'), 'list'=>[]];
foreach($productids as $pid){
$data['list'][$pid] = strtotime('+10 year');
}
return Plugins::encrypt_plugin_list($data, $serverid, $uid);
}
public function get_plugin_auth_win(){
$productids = ["49","50","51","52","53","54","56","57","58","59","60","61","67","68","72","76","80","84","88","89","92","93","119","120","133","134","137","138","139","142","145","146","150","168","169","170","172","176","184","396","404","414","420","422","424","426","428","100000001","100000018","100000019","100000024","100000026","100000027","100000028","100000031","100000039","100000043","100000047","100000048","100000049","100000051","100000052","100000060","100000061","100000064","100000067","100000075"];
$os_version = input('post.os_version');
$address = input('post.address','');
$uid = input('post.uid','');
$username = input('post.username','');
$serverid = input('post.serverid','');
$mac = input('post.mac','');
$data = ['ip'=>$address, 'uid'=>$uid, 'username'=>$username, 'serverid'=>$serverid, 'lasttime'=>time(), 'pro'=>-1, 'skey'=>'', 'ltd'=>strtotime('+10 year'), 'list'=>[]];
foreach($productids as $pid){
$data['list'][$pid] = strtotime('+10 year');
}
return Plugins::encrypt_plugin_list($data, $serverid, $uid);
}
public function get_update_logs(){ public function get_update_logs(){
$type = input('get.type'); $type = input('get.type');
if($type == 'Windows'){ if($type == 'Windows'){
@ -137,6 +213,39 @@ class Api extends BaseController
return $version; return $version;
} }
public function get_version_en(){
$version = config_get('new_version_en');
return $version;
}
public function get_panel_version(){
$version = config_get('new_version');
$file = app()->getRootPath().'public/install/update/LinuxPanel-'.$version.'.zip';
$hash = hash_file('sha256', $file);
$data = [
'version' => $version,
'hash' => $hash,
'update_time' => filemtime($file),
];
return json($data);
}
public function get_panel_version_v2(){
$version = config_get('new_version');
$down_url = request()->root(true).'/install/update/LinuxPanel-'.$version.'.zip';
$data = [
'OfficialVersion' => [
'version' => $version,
'downUrl' => $down_url,
'updateMsg' => config_get('update_msg'),
'uptime' => config_get('update_date'),
],
'OfficialVersionLatest' => [],
'AccountVersion' => [],
];
return json($data);
}
//安装统计 //安装统计
public function setup_count(){ public function setup_count(){
return 'ok'; return 'ok';
@ -191,6 +300,28 @@ class Api extends BaseController
return json($data); return json($data);
} }
//检测更新(aapanel)
public function check_update_en(){
$version = config_get('new_version_en');
$down_url = request()->root(true).'/install/update/LinuxPanel_EN-'.$version.'.zip';
$data = [
'force' => false,
'version' => $version,
'downUrl' => $down_url,
'updateMsg' => config_get('update_msg_en'),
'uptime' => config_get('update_date_en'),
'is_beta' => 0,
'btb' => '',
'beta' => [
'version' => $version,
'downUrl' => $down_url,
'updateMsg' => config_get('update_msg_en'),
'uptime' => config_get('update_date_en'),
]
];
return json($data);
}
//宝塔云监控获取最新版本 //宝塔云监控获取最新版本
public function btm_latest_version(){ public function btm_latest_version(){
$data = [ $data = [
@ -215,15 +346,67 @@ class Api extends BaseController
//宝塔云WAF最新版本 //宝塔云WAF最新版本
public function btwaf_latest_version(){ public function btwaf_latest_version(){
$data = [ $type = input('?post.type') ? input('post.type') : 0;
'version' => '2.5', if($type == 1){
'description' => '暂无更新日志', $data = [
'create_time' => 1701252997, 'version' => '1.1',
]; 'description' => '暂无更新日志',
'create_time' => 1705315163,
];
}else{
$data = [
'version' => '3.0',
'description' => '暂无更新日志',
'create_time' => 1705315163,
];
}
$data = bin2hex(json_encode($data)); $data = bin2hex(json_encode($data));
return json(['status'=>true,'err_no'=>0,'msg'=>'获取成功','data'=>$data]); return json(['status'=>true,'err_no'=>0,'msg'=>'获取成功','data'=>$data]);
} }
//宝塔云控版本信息
public function cloudc_version_info(){
return json(['status'=>true,'msg'=>'获取成功','data'=>[
'version' => '1.0.5',
'download' => '',
'uptime' => '2025/06/16',
'upmsg' => '暂无更新日志'
]]);
}
//宝塔云控版本信息
public function cloudc_get_version(){
return json(['status'=>true,'msg'=>'','oid'=>'','data'=>[
'officialVersion' => [
'version' => '1.0.5',
'download' => '',
'uptime' => '2025/06/16',
'updateMsg' => '暂无更新日志'
],
]]);
}
//宝塔云控授权信息
public function cloudc_order_status(){
$data = [
'status' => true,
'msg' => '获取成功',
'oid' => '',
'data' => [
'id' => 1,
'address' => real_ip(),
'buytime' => time(),
'endtime' => time() + 86400 * 3650,
'num' => 9999,
'max_num' => 9999,
'pid' => 100000023,
'renew_price' => 0,
'state' => 1,
]
];
return json($data);
}
//获取内测版更新日志 //获取内测版更新日志
public function get_beta_logs(){ public function get_beta_logs(){
return json(['beta_ps'=>'当前暂无内测版', 'list'=>[]]); return json(['beta_ps'=>'当前暂无内测版', 'list'=>[]]);
@ -362,6 +545,51 @@ class Api extends BaseController
$data = bin2hex('[]'); $data = bin2hex('[]');
return json(['status'=>true, 'msg'=>'', 'data'=>$data]); return json(['status'=>true, 'msg'=>'', 'data'=>$data]);
} }
//获取堡塔云WAF恶意IP库
public function get_malicious_ip_list()
{
$cacheKey = 'malicious_ip_list';
// 尝试从缓存获取
if (Cache::has($cacheKey)) {
return json(json_decode(Cache::get($cacheKey), true));
}
$url = 'https://api.bt.cn/bt_waf/get_malicious_ip';
$postData = json_encode([
'x_bt_token' => 'MzI3YjAzOGQ3Yjk3NjUxYjVlMDkyMGFm'
]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Content-Length: ' . strlen($postData)
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
return json(['status'=>true, 'msg'=>'', 'data'=>bin2hex('[]')]);
}
curl_close($ch);
Cache::set($cacheKey, $response, 86400); //缓存一天
return json(json_decode($response, true));
}
public function get_ip_info(){
$ip = input('post.ip');
$data = [];
$url = 'https://www.bt.cn/api/ip/info_json';
$post = http_build_query(['ip'=>$ip]);
$response = get_curl($url, $post);
$arr = json_decode($response, true);
if($arr) $data = $arr;
return json($data);
}
public function return_success(){ public function return_success(){
return json(['status'=>true, 'msg'=>1, 'data'=>(object)[]]); return json(['status'=>true, 'msg'=>1, 'data'=>(object)[]]);
@ -387,6 +615,22 @@ class Api extends BaseController
return json(['page'=>"<div><span class='Pcurrent'>1</span><span class='Pnumber'>1/0</span><span class='Pline'>从1-1000条</span><span class='Pcount'>共计0条数据</span></div>", 'data'=>[]]); return json(['page'=>"<div><span class='Pcurrent'>1</span><span class='Pnumber'>1/0</span><span class='Pline'>从1-1000条</span><span class='Pcount'>共计0条数据</span></div>", 'data'=>[]]);
} }
public function nps_check(){
return json(['err_no'=>0, 'success'=>true, 'res'=>true, 'nonce'=>time()]);
}
public function nps_questions(){
return json(['err_no'=>0, 'success'=>true, 'res'=>[], 'nonce'=>time()]);
}
public function nps_submit(){
return json(['err_no'=>0, 'success'=>true, 'res'=>'Success', 'nonce'=>time()]);
}
public function get_user_give_away(){
return json(['no_exceed_limit'=>false, 'user_give'=>true]);
}
//获取所有蜘蛛IP列表 //获取所有蜘蛛IP列表
public function btwaf_getspiders(){ public function btwaf_getspiders(){
try{ try{
@ -404,6 +648,39 @@ class Api extends BaseController
$result = Plugins::get_spider($type); $result = Plugins::get_spider($type);
return json($result); return json($result);
} }
//获取堡塔恶意情报IP库
public function btwaf_getmalicious(){
try{
$result = Plugins::btwaf_getmalicious();
return json($result);
}catch(\Exception $e){
return json(['success'=>false, 'res'=>$e->getMessage()]);
}
}
//检查是否国内IP
public function check_cnip(){
$clientip = bindec(decbin(ip2long($this->clientip)));
$json_file = app()->getBasePath().'lib/cn.json';
$arr = json_decode(file_get_contents($json_file), true);
if(!$arr) return 'False';
foreach($arr as $ip_arr){
if($clientip >= $ip_arr[0] && $clientip <= $ip_arr[1]){
return 'True';
}
}
return 'False';
}
//邮件配额
public function email_user_surplus(){
$data = [
'free' => ['surplus' => '120000', 'total' => '120000', 'used' => '0'],
'period' => ['surplus' => '2000000', 'total' => '2000000', 'used' => '0'],
];
return json(['success'=>true, 'msg'=>'获取成功', 'res'=>$data]);
}
//检查黑白名单 //检查黑白名单
private function checklist(){ private function checklist(){
@ -455,4 +732,36 @@ class Api extends BaseController
fclose($handle); fclose($handle);
return json(['status'=>false, 'msg'=>'不支持当前操作']); return json(['status'=>false, 'msg'=>'不支持当前操作']);
} }
//生成自签名SSL证书
public function bt_cert(){
$data = input('post.data');
$param = json_decode($data, true);
if(!$param || !isset($param['action']) || !isset($param['domain'])) return json(['status'=>false, 'msg'=>'参数错误']);
$dir = app()->getBasePath().'script/';
$ssl_path = app()->getRootPath().'public/ssl/baota_root.pfx';
$isca = file_exists($dir.'ca.crt') && file_exists($dir.'ca.key') && file_exists($ssl_path);
if(!$isca) return json(['status'=>false, 'msg'=>'CA证书不存在']);
if($param['action'] == 'get_domain_cert'){
if(!$this->checklist()) return json(['status'=>false, 'msg'=>'你的服务器被禁止使用此云端']);
$domain = $param['domain'];
if(empty($domain)) return json(['status'=>false, 'msg'=>'域名不能为空']);
$domain_list = explode(',', $domain);
foreach($domain_list as $d){
if(!checkDomain($d)) return json(['status'=>false, 'msg'=>'域名或IP格式不正确:'.$d]);
}
$common_name = $domain_list[0];
$validity = 3650;
$result = makeSelfSignSSL($common_name, $domain_list, $validity);
if(!$result){
return json(['status'=>false, 'msg'=>'生成证书失败']);
}
$ca_pfx = base64_encode(file_get_contents($ssl_path));
return json(['status'=>true, 'msg'=>'生成证书成功', 'cert'=>$result['cert'], 'key'=>$result['key'], 'pfx'=>$ca_pfx, 'password'=>'']);
}else{
return json(['status'=>false, 'msg'=>'不支持当前操作']);
}
}
} }

View File

@ -8,13 +8,13 @@ class Index extends BaseController
{ {
public function index() public function index()
{ {
return 'Server is ok'; return '';
} }
public function download() public function download()
{ {
if(config_get('download_page') == '0' && !request()->islogin){ if(config_get('download_page') == '0' && !request()->islogin){
return redirect('/admin/login'); return 'need login';
} }
View::assign('siteurl', request()->root(true)); View::assign('siteurl', request()->root(true));
return view(); return view();

View File

@ -9,13 +9,17 @@ class BtPlugins
{ {
private $btapi; private $btapi;
private $os; private $os;
//需屏蔽的插件名称列表 //需屏蔽的插件名称列表
private static $block_plugins = ['dns']; public static $block_plugins = ['dns', 'bt_boce', 'ssl_verify', 'firewall', 'KylinOperatingSystem', 'KingdeeApusicDistributedCache', 'BorlandCacheServer', 'GBase8s', 'KingdeeApusicLoadBalancer', 'BorlandWebServer'];
public static $skip_plugins = ['php_filter', 'enterprise_backup', 'tamper_drive'];
public function __construct($os){ public function __construct($os){
$this->os = $os; $this->os = $os;
if($os == 'Windows'){ if($os == 'en'){
$bt_url = config_get('enbt_url');
$bt_key = config_get('enbt_key');
}elseif($os == 'Windows'){
$bt_url = config_get('wbt_url'); $bt_url = config_get('wbt_url');
$bt_key = config_get('wbt_key'); $bt_key = config_get('wbt_key');
}else{ }else{
@ -33,9 +37,11 @@ class BtPlugins
if(empty($result['list']) || empty($result['type'])){ if(empty($result['list']) || empty($result['type'])){
throw new Exception('获取插件列表失败:插件列表为空'); throw new Exception('获取插件列表失败:插件列表为空');
} }
foreach($result['list'] as $k=>$v){ $newlist = [];
if(in_array($v['name'], self::$block_plugins)) unset($result['list'][$k]); foreach($result['list'] as $item){
if(!in_array($item['name'], self::$block_plugins)) $newlist[] = $item;
} }
$result['list'] = $newlist;
return $result; return $result;
}else{ }else{
throw new Exception('获取插件列表失败:'.(isset($result['msg'])?$result['msg']:'面板连接失败')); throw new Exception('获取插件列表失败:'.(isset($result['msg'])?$result['msg']:'面板连接失败'));
@ -72,9 +78,10 @@ class BtPlugins
$zip = new ZipArchive; $zip = new ZipArchive;
if ($zip->open($filepath) === true) if ($zip->open($filepath) === true)
{ {
$zip->extractTo(get_data_dir($this->os).'plugins/folder/'.$plugin_name.'-'.$version); $plugins_dir = get_data_dir($this->os).'plugins/folder/'.$plugin_name.'-'.$version;
$zip->extractTo($plugins_dir, $plugin_name.'/'.$plugin_name.'_main.py');
$zip->close(); $zip->close();
$main_filepath = get_data_dir($this->os).'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py'; $main_filepath = $plugins_dir.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
if(file_exists($main_filepath) && filesize($main_filepath)>10){ if(file_exists($main_filepath) && filesize($main_filepath)>10){
if(!strpos(file_get_contents($main_filepath), 'import ')){ //加密py文件需要解密 if(!strpos(file_get_contents($main_filepath), 'import ')){ //加密py文件需要解密
$this->decode_plugin_main($plugin_name, $version, $main_filepath); $this->decode_plugin_main($plugin_name, $version, $main_filepath);
@ -84,6 +91,7 @@ class BtPlugins
$zip->close(); $zip->close();
} }
} }
deleteDir($plugins_dir);
}else{ }else{
unlink($filepath); unlink($filepath);
throw new Exception('插件包解压缩失败'); throw new Exception('插件包解压缩失败');
@ -155,9 +163,9 @@ class BtPlugins
$de_text = ''; $de_text = '';
foreach($data_arr as $data){ foreach($data_arr as $data){
$data = trim($data); $data = trim($data);
if(!empty($data) && strlen($data)!=24){ if(!empty($data)){
$tmp = openssl_decrypt($data, 'aes-128-cbc', $key, 0, $iv); $tmp = openssl_decrypt($data, 'aes-128-cbc', $key, 0, $iv);
if($tmp) $de_text .= $tmp; if($tmp !== false) $de_text .= $tmp;
} }
} }
if(!empty($de_text) && strpos($de_text, 'import ')!==false){ if(!empty($de_text) && strpos($de_text, 'import ')!==false){
@ -189,14 +197,20 @@ class BtPlugins
$data = str_replace('\'http://www.bt.cn/api/wpanel/notpro', 'public.GetConfigValue(\'home\')+\'/api/wpanel/notpro', $data); $data = str_replace('\'http://www.bt.cn/api/wpanel/notpro', 'public.GetConfigValue(\'home\')+\'/api/wpanel/notpro', $data);
$data = str_replace('\'https://www.bt.cn/api/wpanel/notpro', 'public.GetConfigValue(\'home\')+\'/api/wpanel/notpro', $data); $data = str_replace('\'https://www.bt.cn/api/wpanel/notpro', 'public.GetConfigValue(\'home\')+\'/api/wpanel/notpro', $data);
$data = str_replace('"https://www.bt.cn/api/bt_waf/get_malicious', 'public.GetConfigValue(\'home\')+"/api/bt_waf/get_malicious', $data);
$data = str_replace('\'http://www.bt.cn/api/bt_waf/getSpiders', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getSpiders', $data); $data = str_replace('\'http://www.bt.cn/api/bt_waf/getSpiders', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getSpiders', $data);
$data = str_replace('\'https://www.bt.cn/api/bt_waf/getSpiders', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getSpiders', $data); $data = str_replace('\'https://www.bt.cn/api/bt_waf/getSpiders', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getSpiders', $data);
$data = str_replace('\'http://www.bt.cn/api/bt_waf/addSpider', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/addSpider', $data); $data = str_replace('\'http://www.bt.cn/api/bt_waf/addSpider', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/addSpider', $data);
$data = str_replace('\'https://www.bt.cn/api/bt_waf/addSpider', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/addSpider', $data); $data = str_replace('\'https://www.bt.cn/api/bt_waf/addSpider', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/addSpider', $data);
$data = str_replace('\'https://www.bt.cn/api/bt_waf/getVulScanInfoList', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getVulScanInfoList', $data); $data = str_replace('\'https://www.bt.cn/api/bt_waf/getVulScanInfoList', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getVulScanInfoList', $data);
$data = str_replace('\'https://www.bt.cn/api/bt_waf/reportInterceptFail', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/reportInterceptFail', $data); $data = str_replace('\'https://www.bt.cn/api/bt_waf/reportInterceptFail', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/reportInterceptFail', $data);
$data = str_replace('"https://www.bt.cn/api/bt_waf/reportInterceptFail', 'public.GetConfigValue(\'home\')+"/api/bt_waf/reportInterceptFail', $data);
$data = str_replace('\'https://www.bt.cn/api/v2/contact/nps/questions', 'public.GetConfigValue(\'home\')+\'/panel/notpro', $data); $data = str_replace('\'https://www.bt.cn/api/v2/contact/nps/questions', 'public.GetConfigValue(\'home\')+\'/panel/notpro', $data);
$data = str_replace('\'https://www.bt.cn/api/v2/contact/nps/submit', 'public.GetConfigValue(\'home\')+\'/panel/notpro', $data); $data = str_replace('\'https://www.bt.cn/api/v2/contact/nps/submit', 'public.GetConfigValue(\'home\')+\'/panel/notpro', $data);
$data = str_replace('\'http://www.bt.cn/api/Auth', 'public.GetConfigValue(\'home\')+\'/api/Auth', $data);
$data = str_replace('\'https://www.bt.cn/api/Auth', 'public.GetConfigValue(\'home\')+\'/api/Auth', $data);
$data = str_replace('\'https://brandnew.aapanel.com/api/panel/getSoftList', 'public.OfficialApiBase()+\'/api/panel/getSoftList', $data);
file_put_contents($main_filepath, $data); file_put_contents($main_filepath, $data);
} }
@ -279,4 +293,16 @@ class BtPlugins
} }
} }
//获取堡塔恶意情报IP库
public function btwaf_getmalicious(){
$result = $this->btapi->btwaf_getmalicious();
if(isset($result['success'])){
return $result;
}elseif(isset($result['msg'])){
throw new Exception($result['msg']);
}else{
throw new Exception(isset($result['res'])?$result['res']:'获取失败');
}
}
} }

View File

@ -1,243 +1,266 @@
<?php <?php
namespace app\lib; namespace app\lib;
use Exception; use Exception;
class Btapi class Btapi
{ {
private $BT_KEY; //接口密钥 private $BT_KEY; //接口密钥
private $BT_PANEL; //面板地址 private $BT_PANEL; //面板地址
public function __construct($bt_panel, $bt_key){ public function __construct($bt_panel, $bt_key){
$this->BT_PANEL = $bt_panel; $this->BT_PANEL = $bt_panel;
$this->BT_KEY = $bt_key; $this->BT_KEY = $bt_key;
} }
//获取面板配置信息 //获取面板配置信息
public function get_config(){ public function get_config(){
$url = $this->BT_PANEL.'/config?action=get_config'; $url = $this->BT_PANEL.'/config?action=get_config';
$p_data = $this->GetKeyData(); $p_data = $this->GetKeyData();
$result = $this->curl($url,$p_data); $result = $this->curl($url,$p_data);
$data = json_decode($result,true); $data = json_decode($result,true);
return $data; return $data;
} }
//获取已登录用户信息 public function get_config_go(){
public function get_user_info(){ $url = $this->BT_PANEL.'/panel/get_config';
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_user_info';
$p_data = $this->GetKeyData();
$p_data = $this->GetKeyData();
$result = $this->curl($url,$p_data);
$result = $this->curl($url,$p_data);
$data = json_decode($result,true);
$data = json_decode($result,true); return $data;
return $data; }
}
//获取已登录用户信息
//从云端获取插件列表 public function get_user_info(){
public function get_plugin_list(){ $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_user_info';
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_plugin_list';
$p_data = $this->GetKeyData();
$p_data = $this->GetKeyData();
$result = $this->curl($url,$p_data);
$result = $this->curl($url,$p_data);
$data = json_decode($result,true);
$data = json_decode($result,true); return $data;
return $data; }
}
//从云端获取插件列表
//下载插件包,返回文件路径 public function get_plugin_list(){
public function get_plugin_filename($plugin_name, $version){ $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_plugin_list';
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin';
$p_data = $this->GetKeyData();
$p_data = $this->GetKeyData();
$p_data['plugin_name'] = $plugin_name; $result = $this->curl($url,$p_data);
$p_data['version'] = $version;
$data = json_decode($result,true);
$result = $this->curl($url,$p_data); return $data;
}
$data = json_decode($result,true);
return $data; //下载插件包,返回文件路径
} public function get_plugin_filename($plugin_name, $version){
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin';
//下载插件主程序文件,返回文件路径
public function get_plugin_main_filename($plugin_name, $version){ $p_data = $this->GetKeyData();
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin_main'; $p_data['plugin_name'] = $plugin_name;
$p_data['version'] = $version;
$p_data = $this->GetKeyData();
$p_data['plugin_name'] = $plugin_name; $result = $this->curl($url,$p_data);
$p_data['version'] = $version;
$data = json_decode($result,true);
$result = $this->curl($url,$p_data); return $data;
}
$data = json_decode($result,true);
return $data; //下载插件主程序文件,返回文件路径
} public function get_plugin_main_filename($plugin_name, $version){
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin_main';
//解密插件主程序py代码返回文件路径
public function get_decode_plugin_main($plugin_name, $version){ $p_data = $this->GetKeyData();
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=decode_plugin_main'; $p_data['plugin_name'] = $plugin_name;
$p_data['version'] = $version;
$p_data = $this->GetKeyData();
$p_data['plugin_name'] = $plugin_name; $result = $this->curl($url,$p_data);
$p_data['version'] = $version;
$data = json_decode($result,true);
$result = $this->curl($url,$p_data); return $data;
}
$data = json_decode($result,true);
return $data; //解密插件主程序py代码返回文件路径
} public function get_decode_plugin_main($plugin_name, $version){
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=decode_plugin_main';
//下载插件其他文件,返回文件路径
public function get_plugin_other_filename($fname){ $p_data = $this->GetKeyData();
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin_other'; $p_data['plugin_name'] = $plugin_name;
$p_data['version'] = $version;
$p_data = $this->GetKeyData();
$p_data['fname'] = $fname; $result = $this->curl($url,$p_data);
$result = $this->curl($url,$p_data); $data = json_decode($result,true);
return $data;
$data = json_decode($result,true); }
return $data;
} //下载插件其他文件,返回文件路径
public function get_plugin_other_filename($fname){
//下载文件 $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin_other';
public function download($filename, $localpath){
$url = $this->BT_PANEL.'/download'; $p_data = $this->GetKeyData();
$p_data['fname'] = $fname;
$p_data = $this->GetKeyData();
$p_data['filename'] = $filename; $result = $this->curl($url,$p_data);
$result = $this->curl_download($url.'?'.http_build_query($p_data), $localpath); $data = json_decode($result,true);
return $data;
return $result; }
}
//下载文件
//获取文件base64 public function download($filename, $localpath){
public function get_file($filename){ $url = $this->BT_PANEL.'/download';
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_file';
$p_data = $this->GetKeyData();
$p_data = $this->GetKeyData(); $p_data['filename'] = $filename;
$p_data['filename'] = $filename;
$result = $this->curl_download($url.'?'.http_build_query($p_data), $localpath);
$result = $this->curl($url,$p_data);
return $result;
$data = json_decode($result,true); }
return $data;
} //获取文件base64
public function get_file($filename){
//购买第三方插件 $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_file';
public function create_plugin_other_order($pid){
$url = $this->BT_PANEL.'/auth?action=create_plugin_other_order'; $p_data = $this->GetKeyData();
$p_data['filename'] = $filename;
$p_data = $this->GetKeyData();
$p_data['pid'] = $pid; $result = $this->curl($url,$p_data);
$p_data['cycle'] = '999';
$p_data['type'] = '0'; $data = json_decode($result,true);
return $data;
$result = $this->curl($url,$p_data); }
$data = json_decode($result,true); //购买第三方插件
return $data; public function create_plugin_other_order($pid){
} $url = $this->BT_PANEL.'/auth?action=create_plugin_other_order';
//获取一键部署列表 $p_data = $this->GetKeyData();
public function get_deplist(){ $p_data['pid'] = $pid;
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_deplist'; $p_data['cycle'] = '999';
$p_data['type'] = '0';
$p_data = $this->GetKeyData();
$result = $this->curl($url,$p_data);
$result = $this->curl($url,$p_data);
$data = json_decode($result,true);
$data = json_decode($result,true); return $data;
return $data; }
}
//获取一键部署列表
//BTWAF-获取蜘蛛列表 public function get_deplist(){
public function btwaf_getspiders(){ $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_deplist';
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=btwaf_getspiders';
$p_data = $this->GetKeyData();
$p_data = $this->GetKeyData();
$result = $this->curl($url,$p_data);
$result = $this->curl($url,$p_data);
$result = str_replace("\u0000", '', $result); $data = json_decode($result,true);
return $data;
$data = json_decode($result,true); }
return $data;
} //BTWAF-获取蜘蛛列表
public function btwaf_getspiders(){
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=btwaf_getspiders';
private function GetKeyData(){
$now_time = time(); $p_data = $this->GetKeyData();
$p_data = array(
'request_token' => md5($now_time.''.md5($this->BT_KEY)), $result = $this->curl($url,$p_data);
'request_time' => $now_time $result = str_replace("\u0000", '', $result);
);
return $p_data; $data = json_decode($result,true);
} return $data;
}
private function curl($url, $data = null, $timeout = 60) //BTWAF-获取堡塔恶意情报IP库
{ public function btwaf_getmalicious(){
//定义cookie保存位置 $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=btwaf_getmalicious';
$cookie_file=app()->getRuntimePath().md5($this->BT_PANEL).'.cookie';
if(!file_exists($cookie_file)){ $p_data = $this->GetKeyData();
touch($cookie_file);
} $result = $this->curl($url,$p_data);
$ch = curl_init(); $data = json_decode($result,true);
curl_setopt($ch, CURLOPT_URL, $url); return $data;
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); }
if($data){
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data); private function GetKeyData(){
} $now_time = time();
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); $p_data = array(
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file); 'request_token' => md5($now_time.''.md5($this->BT_KEY)),
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 'request_time' => $now_time
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); );
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); return $p_data;
$output = curl_exec($ch); }
curl_close($ch);
return $output;
} private function curl($url, $data = null, $timeout = 60)
{
private function curl_download($url, $localpath, $timeout = 300) //定义cookie保存位置
{ $cookie_file=app()->getRuntimePath().md5($this->BT_PANEL).'.cookie';
//定义cookie保存位置 if(!file_exists($cookie_file)){
$cookie_file=app()->getRuntimePath().md5($this->BT_PANEL).'.cookie'; touch($cookie_file);
if(!file_exists($cookie_file)){ }
touch($cookie_file);
} $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
$ch = curl_init(); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_URL, $url); if($data){
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file); }
$fp = fopen($localpath, 'w+'); curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_exec($ch); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
if (curl_errno($ch)) { $output = curl_exec($ch);
$message = curl_error($ch); curl_close($ch);
curl_close($ch); return $output;
fclose($fp); }
throw new Exception('下载文件失败:'.$message);
} private function curl_download($url, $localpath, $timeout = 300)
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE); {
if($httpcode>299){ //定义cookie保存位置
curl_close($ch); $cookie_file=app()->getRuntimePath().md5($this->BT_PANEL).'.cookie';
fclose($fp); if(!file_exists($cookie_file)){
throw new Exception('下载文件失败HTTPCODE-'.$httpcode); touch($cookie_file);
} }
curl_close($ch);
fclose($fp); $ch = curl_init();
return true; curl_setopt($ch, CURLOPT_URL, $url);
} curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
$fp = fopen($localpath, 'w+');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_exec($ch);
if (curl_errno($ch)) {
$message = curl_error($ch);
curl_close($ch);
fclose($fp);
throw new Exception('下载文件失败:'.$message);
}
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpcode>299){
curl_close($ch);
fclose($fp);
throw new Exception('下载文件失败HTTPCODE-'.$httpcode);
}
curl_close($ch);
fclose($fp);
return true;
}
} }

View File

@ -17,7 +17,13 @@ class Plugins
} }
private static function is_third($os){ private static function is_third($os){
$type = $os == 'Windows' ? config_get('wbt_type') : config_get('bt_type'); if($os == 'en'){
$type = config_get('enbt_type');
}elseif($os == 'Windows'){
$type = config_get('wbt_type');
}else{
$type = config_get('bt_type');
}
return $type == 1; return $type == 1;
} }
@ -31,23 +37,45 @@ class Plugins
//保存插件列表 //保存插件列表
private static function save_plugin_list($data, $os){ private static function save_plugin_list($data, $os){
$data['ip'] = '127.0.0.1'; $data['ip'] = '127.0.0.1';
$data['serverid'] = ''; if($os == 'en'){
$data['beta'] = 0; $data['serverId'] = '';
$data['uid'] = 1; $data['aln'] = self::get_aln();
$data['skey'] = ''; $data['pro'] = 0;
$list = []; $data['pro_authorization_sn'] = '0';
foreach($data['list'] as $plugin){ if(!empty($data['authorization_map'])){
if(isset($plugin['endtime'])) $plugin['endtime'] = 0; foreach($data['authorization_map'] as $code => &$plugin){
$list[] = $plugin; if($code != '0' && isset($plugin['end_time'])) $plugin['end_time'] = 0;
}
}
if(isset($data['expansions']['mail'])){
$data['expansions']['mail']['total'] = 2000000;
$data['expansions']['mail']['available'] = 2000000;
}
}else{
$data['serverid'] = '';
$data['aln'] = self::get_aln();
$data['beta'] = 0;
$data['uid'] = 1;
$data['skey'] = '';
$data['pro'] = -1;
$data['ltd'] = strtotime('+10 year');
}
foreach($data['list'] as &$plugin){
if(isset($plugin['endtime'])) $plugin['endtime'] = 0;
} }
$data['list'] = $list;
$data['ltd'] = strtotime('+10 year');
$json_file = get_data_dir($os).'config/plugin_list.json'; $json_file = get_data_dir($os).'config/plugin_list.json';
if(!file_put_contents($json_file, json_encode($data))){ if(!file_put_contents($json_file, json_encode($data))){
throw new Exception('保存插件列表失败,文件无写入权限'); throw new Exception('保存插件列表失败,文件无写入权限');
} }
} }
//多账号数量
private static function get_aln($count = '9999'){
$key = 'FB8upo8XMgP5by54';
$iv = 'lOrrq3lNEURZNdK7';
return openssl_encrypt($count, 'aes-128-cbc', $key, 0, $iv);
}
//获取插件列表 //获取插件列表
public static function get_plugin_list($os = 'Linux'){ public static function get_plugin_list($os = 'Linux'){
$json_file = get_data_dir($os).'config/plugin_list.json'; $json_file = get_data_dir($os).'config/plugin_list.json';
@ -106,7 +134,7 @@ class Plugins
$data = trim($data); $data = trim($data);
if(!empty($data)){ if(!empty($data)){
$tmp = openssl_decrypt($data, 'aes-128-cbc', $key, 0, $iv); $tmp = openssl_decrypt($data, 'aes-128-cbc', $key, 0, $iv);
if($tmp) $de_text .= $tmp; if($tmp !== false) $de_text .= $tmp;
} }
} }
if(!empty($de_text) && strpos($de_text, 'import ')!==false){ if(!empty($de_text) && strpos($de_text, 'import ')!==false){
@ -165,4 +193,26 @@ class Plugins
return $result; return $result;
} }
//获取堡塔恶意情报IP库
public static function btwaf_getmalicious(){
$btapi = self::get_btapi('Linux');
$result = $btapi->btwaf_getmalicious();
return $result;
}
//加密插件列表
public static function encrypt_plugin_list($list, $server_id, $uid){
$data = json_encode($list);
$block_size = 51200;
$key = md5(substr($server_id, 10, 16) . $uid . $server_id);
$iv = md5($key . $server_id);
$key = substr($key, 8, 16);
$iv = substr($iv, 8, 16);
$encrypted_content = '';
foreach (str_split($data, $block_size) as $block) {
$encrypted_content .= openssl_encrypt($block, 'aes-128-cbc', $key, 0, $iv) . "\n";
}
return $encrypted_content;
}
} }

View File

@ -13,7 +13,13 @@ class ThirdPlugins
public function __construct($os) public function __construct($os)
{ {
$this->os = $os; $this->os = $os;
$url = $os == 'Windows' ? config_get('wbt_surl') : config_get('bt_surl'); if($os == 'en'){
$url = config_get('enbt_surl');
}elseif($os == 'Windows'){
$url = config_get('wbt_surl');
}else{
$url = config_get('bt_surl');
}
if(!$url) throw new Exception('请先配置好第三方云端首页URL'); if(!$url) throw new Exception('请先配置好第三方云端首页URL');
$this->url = $url; $this->url = $url;
} }
@ -21,7 +27,13 @@ class ThirdPlugins
//获取插件列表 //获取插件列表
public function get_plugin_list() public function get_plugin_list()
{ {
$url = $this->os == 'Windows' ? $this->url . 'api/wpanel/get_soft_list' : $this->url . 'api/panel/get_soft_list'; if($this->os == 'en'){
$url = $this->url . 'api/panel/getSoftListEn';
}elseif($this->os == 'Windows'){
$url = $this->url . 'api/wpanel/get_soft_list';
}else{
$url = $this->url . 'api/panel/get_soft_list';
}
$res = $this->curl($url); $res = $this->curl($url);
$result = json_decode($res, true); $result = json_decode($res, true);
if($result && isset($result['list']) && isset($result['type'])){ if($result && isset($result['list']) && isset($result['type'])){
@ -65,7 +77,6 @@ class ThirdPlugins
$zip = new ZipArchive; $zip = new ZipArchive;
if ($zip->open($filepath) === true) if ($zip->open($filepath) === true)
{ {
$zip->extractTo(get_data_dir($this->os).'plugins/folder/'.$plugin_name.'-'.$version);
$zip->close(); $zip->close();
return true; return true;
}else{ }else{
@ -163,6 +174,18 @@ class ThirdPlugins
} }
} }
//获取堡塔恶意情报IP库
public function btwaf_getmalicious(){
$url = $this->url . 'api/bt_waf/get_malicious';
$res = $this->curl($url);
$result = json_decode($res, true);
if(isset($result['success'])){
return $result;
}else{
throw new Exception(isset($result['res'])?$result['res']:'获取失败');
}
}
private function curl($url, $post = 0){ private function curl($url, $post = 0){
$ua = "Mozilla/5.0 (BtCloud; ".request()->root(true).")"; $ua = "Mozilla/5.0 (BtCloud; ".request()->root(true).")";
return get_curl($url, $post, 0, 0, 0, $ua); return get_curl($url, $post, 0, 0, 0, $ua);

1
app/lib/cn.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,26 +1,26 @@
<?php <?php
declare (strict_types=1); declare (strict_types=1);
namespace app\middleware; namespace app\middleware;
class AuthAdmin class AuthAdmin
{ {
public function handle($request, \Closure $next) public function handle($request, \Closure $next)
{ {
$islogin = false; $islogin = false;
$cookie = cookie('admin_token'); $cookie = cookie('admin_token');
if($cookie){ if($cookie){
$token=authcode($cookie, 'DECODE', config_get('syskey')); $token=authcode($cookie, 'DECODE', config_get('syskey'));
if($token){ if($token){
list($user, $sid, $expiretime) = explode("\t", $token); list($user, $sid, $expiretime) = explode("\t", $token);
$session=md5(config_get('admin_username').config_get('admin_password')); $session=md5(config_get('admin_username').config_get('admin_password'));
if($session==$sid && $expiretime>time()) { if($session==$sid && $expiretime>time()) {
$islogin = true; $islogin = true;
} }
} }
} }
request()->islogin = $islogin; request()->islogin = $islogin;
return $next($request); return $next($request);
} }
} }

View File

@ -1,19 +1,19 @@
<?php <?php
declare (strict_types=1); declare (strict_types=1);
namespace app\middleware; namespace app\middleware;
class CheckAdmin class CheckAdmin
{ {
public function handle($request, \Closure $next) public function handle($request, \Closure $next)
{ {
if (!request()->islogin) { if (!request()->islogin) {
if ($request->isAjax() || !$request->isGet()) { if ($request->isAjax() || !$request->isGet()) {
return json(['code'=>-1, 'msg'=>'未登录'])->code(401); return json(['code'=>-1, 'msg'=>'未登录'])->code(401);
} }
return redirect((string)url('/admin/login')); return redirect((string)url('/admin/login'));
} }
return $next($request); return $next($request);
} }
} }

View File

@ -5,6 +5,7 @@ namespace app\middleware;
use think\facade\Db; use think\facade\Db;
use think\facade\Config; use think\facade\Config;
use think\facade\View;
class LoadConfig class LoadConfig
{ {
@ -31,6 +32,7 @@ class LoadConfig
$res = Db::name('config')->cache('configs',0)->column('value','key'); $res = Db::name('config')->cache('configs',0)->column('value','key');
Config::set($res, 'sys'); Config::set($res, 'sys');
View::assign('cdnpublic', 'https://s4.zstatic.net/ajax/libs/');
return $next($request)->header([ return $next($request)->header([
'Cache-Control' => 'no-store, no-cache, must-revalidate', 'Cache-Control' => 'no-store, no-cache, must-revalidate',
'Pragma' => 'no-cache', 'Pragma' => 'no-cache',

View File

@ -1,24 +1,24 @@
<?php <?php
declare (strict_types=1); declare (strict_types=1);
namespace app\middleware; namespace app\middleware;
use think\facade\View; use think\facade\View;
class RefererCheck class RefererCheck
{ {
/** /**
* 处理请求 * 处理请求
* *
* @param \think\Request $request * @param \think\Request $request
* @param \Closure $next * @param \Closure $next
* @return Response * @return Response
*/ */
public function handle($request, \Closure $next) public function handle($request, \Closure $next)
{ {
if(!checkRefererHost()){ if(!checkRefererHost()){
return response('Access Denied', 403); return response('Access Denied', 403);
} }
return $next($request); return $next($request);
} }
} }

31
app/script/cacert.sh Normal file
View File

@ -0,0 +1,31 @@
#!/bin/bash
OPENSSL_CHECK=$(which openssl)
if [ "$?" != "0" ]; then
echo "未安装OpenSSL"
exit 1
fi
if [ ! -f ca.key ] && [ ! -f ca.crt ]; then
openssl genrsa -out ca.key 2048
openssl req -new -x509 -utf8 -days 3650 -extensions v3_ca -subj "/C=CN/O=宝塔面板/CN=宝塔面板" -key ca.key -out ca.crt
fi
openssl genrsa -out server.key 2048
openssl req -new -nodes -key server.key -subj "/C=CN/O=BTPanel/CN=BTPanel" -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -extensions req_ext
cat ca.crt >> server.crt
openssl pkcs12 -export -out baota_root.pfx -inkey server.key -in server.crt -password pass:
if [ "$?" != "0" ]; then
echo "生成CA根证书失败"
exit 1
fi
mkdir -p ../../public/ssl
\cp baota_root.pfx ../../public/ssl/baota_root.pfx
\cp ca.crt ../../public/ssl/baota_root.crt
rm -f server.crt server.key server.csr
echo "生成CA根证书成功"

View File

@ -1,23 +1,27 @@
#!/bin/bash #!/bin/bash
Linux_Version="8.0.4" Linux_Version="11.5.0"
Windows_Version="7.9.0" Windows_Version="8.2.2"
Btm_Version="2.2.9" Aapanel_Version="7.0.25"
Btm_Version="2.3.3"
FILES=( FILES=(
public/install/src/panel6.zip public/install/src/panel6.zip
public/install/update/LinuxPanel-${Linux_Version}.zip public/install/update/LinuxPanel-${Linux_Version}.zip
public/install/install_6.0.sh public/install/install_panel.sh
public/install/update_panel.sh public/install/update_panel.sh
public/install/update6.sh public/install/update6.sh
public/win/install/panel_update.py public/win/install/panel_update.py
public/win/panel/panel_${Windows_Version}.zip
public/win/panel/data/api.py
public/win/panel/data/setup.py public/win/panel/data/setup.py
public/install/src/bt-monitor-${Btm_Version}.zip public/install/src/bt-monitor-${Btm_Version}.zip
public/install/install_btmonitor.sh public/install/install_btmonitor.sh
public/install/update_btmonitor.sh public/install/update_btmonitor.sh
public/install/src/panel_7_en.zip
public/install/update/LinuxPanel_EN-${Aapanel_Version}.zip
public/install/install_7.0_en.sh
public/install/update_7.x_en.sh
) )
PL_FILE="public/install/update/LinuxPanel-${Linux_Version}.pl"
DIR=$1 DIR=$1
SITEURL=$2 SITEURL=$2
@ -75,6 +79,10 @@ do
fi fi
done done
HASH=$(sha256sum "${DIR}public/install/update/LinuxPanel-${Linux_Version}.zip" | awk '{print $1}')
TIMESTAMP=$(date +%s)
printf '{"hash": "%s", "update_time": "%s"}' "$HASH" "$TIMESTAMP" > "${DIR}${PL_FILE}"
echo "==========================" echo "=========================="
echo "处理完成" echo "处理完成"
echo "==========================" echo "=========================="

View File

@ -1,49 +1,49 @@
{extend name="admin/layout" /} {extend name="admin/layout" /}
{block name="title"}一键部署列表{/block} {block name="title"}一键部署列表{/block}
{block name="main"} {block name="main"}
<div class="container" style="padding-top:70px;"> <div class="container" style="padding-top:70px;">
<div class="col-sm-12 col-md-10 col-lg-8 center-block" style="float: none;"> <div class="col-sm-12 col-md-10 col-lg-8 center-block" style="float: none;">
<div class="panel panel-primary"> <div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">一键部署列表</h3></div> <div class="panel-heading"><h3 class="panel-title">一键部署列表</h3></div>
<div class="panel-body"> <div class="panel-body">
<div class="list-group"> <div class="list-group">
<div class="list-group-item list-group-item-warning">Linux面板</div> <div class="list-group-item list-group-item-warning">Linux面板</div>
<div class="list-group-item" style="line-height:35px">列表文件更新时间:<font color="blue">{$deplist_linux_time}</font><a href="javascript:refresh_deplist('Linux')" class="btn btn-success pull-right"><i class="fa fa-refresh"></i>重新获取</a></div> <div class="list-group-item" style="line-height:35px">列表文件更新时间:<font color="blue">{$deplist_linux_time}</font><a href="javascript:refresh_deplist('Linux')" class="btn btn-success pull-right"><i class="fa fa-refresh"></i>重新获取</a></div>
</div> </div>
<div class="list-group"> <div class="list-group">
<div class="list-group-item list-group-item-warning">Windows面板</div> <div class="list-group-item list-group-item-warning">Windows面板</div>
<div class="list-group-item" style="line-height:35px">列表文件更新时间:<font color="blue">{$deplist_win_time}</font><a href="javascript:refresh_deplist('Windows')" class="btn btn-success pull-right"><i class="fa fa-refresh"></i>重新获取</a></div> <div class="list-group-item" style="line-height:35px">列表文件更新时间:<font color="blue">{$deplist_win_time}</font><a href="javascript:refresh_deplist('Windows')" class="btn btn-success pull-right"><i class="fa fa-refresh"></i>重新获取</a></div>
</div> </div>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script> <script src="{$cdnpublic}layer/3.5.1/layer.js"></script>
<script> <script>
function refresh_deplist(os){ function refresh_deplist(os){
var confirm = layer.confirm('是否确定从宝塔官方获取最新一键部署列表?', { var confirm = layer.confirm('是否确定从宝塔官方获取最新一键部署列表?', {
btn: ['确定','取消'] btn: ['确定','取消']
}, function(){ }, function(){
layer.close(confirm) layer.close(confirm)
var ii = layer.msg('正在获取一键部署列表,请稍候...', {icon: 16, shade:0.1, time: 0}); var ii = layer.msg('正在获取一键部署列表,请稍候...', {icon: 16, shade:0.1, time: 0});
$.ajax({ $.ajax({
type : 'GET', type : 'GET',
url : '/admin/refresh_deplist?os='+os, url : '/admin/refresh_deplist?os='+os,
dataType : 'json', dataType : 'json',
success : function(data) { success : function(data) {
layer.close(ii) layer.close(ii)
if(data.code == 0){ if(data.code == 0){
layer.alert(data.msg, {icon:1}, function(){window.location.reload()}); layer.alert(data.msg, {icon:1}, function(){window.location.reload()});
}else{ }else{
layer.alert(data.msg, {icon:2}); layer.alert(data.msg, {icon:2});
} }
}, },
error:function(data){ error:function(data){
layer.close(ii) layer.close(ii)
layer.msg('服务器错误', {icon:2}); layer.msg('服务器错误', {icon:2});
} }
}); });
}, function(){ }, function(){
layer.close(confirm) layer.close(confirm)
}); });
} }
</script> </script>
{/block} {/block}

View File

@ -1,60 +1,60 @@
{extend name="admin/layout" /} {extend name="admin/layout" /}
{block name="title"}宝塔第三方云端管理中心{/block} {block name="title"}宝塔第三方云端管理中心{/block}
{block name="main"} {block name="main"}
<style> <style>
.table>tbody>tr>td{white-space: normal;} .table>tbody>tr>td{white-space: normal;}
.query-title { .query-title {
background-color:#f5fafe; background-color:#f5fafe;
word-break: keep-all; word-break: keep-all;
} }
.query-result{ .query-result{
word-break: break-all; word-break: break-all;
} }
</style> </style>
<div class="container" style="padding-top:70px;"> <div class="container" style="padding-top:70px;">
<div class="col-xs-12 col-sm-10 col-md-8 center-block" style="float: none;"> <div class="col-xs-12 col-sm-10 col-md-8 center-block" style="float: none;">
<div class="panel panel-primary"> <div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">后台管理首页</h3></div> <div class="panel-heading"><h3 class="panel-title">后台管理首页</h3></div>
<div class="list-group"> <div class="list-group">
<div class="list-group-item"><span class="glyphicon glyphicon-stats"></span> <b>宝塔插件统计:</b>共有 {$stat.total} 个,其中免费插件 {$stat.free} 个,专业版插件 {$stat.pro} 个,企业版插件 {$stat.ltd} 个,第三方插件 {$stat.third} 个</div> <div class="list-group-item"><span class="glyphicon glyphicon-stats"></span> <b>宝塔插件统计:</b>共有 {$stat.total} 个,其中免费插件 {$stat.free} 个,专业版插件 {$stat.pro} 个,企业版插件 {$stat.ltd} 个,第三方插件 {$stat.third} 个</div>
<div class="list-group-item"><span class="glyphicon glyphicon-tint"></span> <b>使用记录统计:</b>历史总共数量:{$stat.record_total},正在使用数量:{$stat.record_isuse}</div> <div class="list-group-item"><span class="glyphicon glyphicon-tint"></span> <b>使用记录统计:</b>历史总共数量:{$stat.record_total},正在使用数量:{$stat.record_isuse}</div>
<div class="list-group-item"><span class="glyphicon glyphicon-time"></span> <b>任务运行情况:</b>上次运行时间:{$stat.runtime|raw}&nbsp;&nbsp;<a href="/admin/set/mod/task" class="btn btn-xs btn-info">查看详情</a></div> <div class="list-group-item"><span class="glyphicon glyphicon-time"></span> <b>任务运行情况:</b>上次运行时间:{$stat.runtime|raw}&nbsp;&nbsp;<a href="/admin/set/mod/task" class="btn btn-xs btn-info">查看详情</a></div>
<div class="list-group-item"><span class="glyphicon glyphicon-cog"></span> <b>常用功能入口:</b><a href="/admin/plugins" class="btn btn-xs btn-default">插件列表</a>&nbsp;<a href="/admin/record" class="btn btn-xs btn-default">使用记录</a>&nbsp;<a href="/admin/list" class="btn btn-xs btn-default">黑白名单</a>&nbsp;<a href="/download" class="btn btn-xs btn-default" target="_blank">安装脚本</a></div> <div class="list-group-item"><span class="glyphicon glyphicon-cog"></span> <b>常用功能入口:</b><a href="/admin/plugins" class="btn btn-xs btn-default">插件列表</a>&nbsp;<a href="/admin/record" class="btn btn-xs btn-default">使用记录</a>&nbsp;<a href="/admin/list" class="btn btn-xs btn-default">黑白名单</a>&nbsp;<a href="/download" class="btn btn-xs btn-default" target="_blank">安装脚本</a></div>
</div> </div>
</div> </div>
<div class="panel panel-info"> <div class="panel panel-info">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">服务器信息</h3> <h3 class="panel-title">服务器信息</h3>
</div> </div>
<table class="table table-bordered"> <table class="table table-bordered">
<tbody> <tbody>
<tr> <tr>
<td class="query-title">框架版本</td> <td class="query-title">框架版本</td>
<td class="query-result">{$info.framework_version}</td> <td class="query-result">{$info.framework_version}</td>
</tr> </tr>
<tr> <tr>
<td class="query-title">PHP版本</td> <td class="query-title">PHP版本</td>
<td class="query-result">{$info.php_version}</td> <td class="query-result">{$info.php_version}</td>
</tr> </tr>
<tr> <tr>
<td class="query-title">MySQL版本</td> <td class="query-title">MySQL版本</td>
<td class="query-result">{$info.mysql_version}</td> <td class="query-result">{$info.mysql_version}</td>
</tr> </tr>
<tr> <tr>
<td class="query-title">WEB软件</td> <td class="query-title">WEB软件</td>
<td class="query-result">{$info.software}</td> <td class="query-result">{$info.software}</td>
</tr> </tr>
<tr> <tr>
<td class="query-title">操作系统</td> <td class="query-title">操作系统</td>
<td class="query-result">{$info.os}</td> <td class="query-result">{$info.os}</td>
</tr> </tr>
<tr> <tr>
<td class="query-title">服务器时间</td> <td class="query-title">服务器时间</td>
<td class="query-result">{$info.date}</td> <td class="query-result">{$info.date}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
{/block} {/block}

View File

@ -5,15 +5,15 @@
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>{block name="title"}标题{/block}</title> <title>{block name="title"}标题{/block}</title>
<link href="//cdn.staticfile.org/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" /> <link href="{$cdnpublic}twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
<link href="//cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" /> <link href="{$cdnpublic}font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<link href="/static/css/bootstrap-table.css" rel="stylesheet" /> <link href="/static/css/bootstrap-table.css" rel="stylesheet" />
<script src="//cdn.staticfile.org/modernizr/2.8.3/modernizr.min.js"></script> <script src="{$cdnpublic}modernizr/2.8.3/modernizr.min.js"></script>
<script src="//cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script> <script src="{$cdnpublic}jquery/2.1.4/jquery.min.js"></script>
<script src="//cdn.staticfile.org/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> <script src="{$cdnpublic}twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="//cdn.staticfile.org/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="{$cdnpublic}html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="//cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script> <script src="{$cdnpublic}respond.js/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body> <body>
@ -27,18 +27,19 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="./">宝塔第三方云端管理中心</a> <a class="navbar-brand" href="/admin">宝塔第三方云端管理中心</a>
</div><!-- /.navbar-header --> </div><!-- /.navbar-header -->
<div id="navbar" class="collapse navbar-collapse"> <div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li class="{:checkIfActive('index')}"> <li class="{:checkIfActive('index')}">
<a href="/admin"><i class="fa fa-home"></i> 后台首页</a> <a href="/admin"><i class="fa fa-home"></i> 后台首页</a>
</li> </li>
<li class="{:checkIfActive('plugins,pluginswin,deplist')}"> <li class="{:checkIfActive('plugins,pluginswin,pluginsen,deplist')}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-cubes"></i> 插件列表<b class="caret"></b></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-cubes"></i> 插件列表<b class="caret"></b></a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li class="{:checkIfActive('plugins')}"><a href="/admin/plugins">Linux面板</a></li> <li class="{:checkIfActive('plugins')}"><a href="/admin/plugins">Linux面板</a></li>
<li class="{:checkIfActive('pluginswin')}"><a href="/admin/pluginswin">Windows面板</a></li> <li class="{:checkIfActive('pluginswin')}"><a href="/admin/pluginswin">Windows面板</a></li>
<li class="{:checkIfActive('pluginsen')}"><a href="/admin/pluginsen">aaPanel面板</a></li>
<li class="{:checkIfActive('deplist')}"><a href="/admin/deplist">一键部署列表</a></li> <li class="{:checkIfActive('deplist')}"><a href="/admin/deplist">一键部署列表</a></li>
</ul> </ul>
</li> </li>
@ -51,6 +52,9 @@
<li class="{:checkIfActive('log')}"> <li class="{:checkIfActive('log')}">
<a href="/admin/log"><i class="fa fa-calendar"></i> 操作日志</a> <a href="/admin/log"><i class="fa fa-calendar"></i> 操作日志</a>
</li> </li>
<li class="{:checkIfActive('ssl')}">
<a href="/admin/ssl"><i class="fa fa-expeditedssl"></i> 自签SSL</a>
</li>
<li class="{:checkIfActive('set')}"> <li class="{:checkIfActive('set')}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-cog"></i> 系统设置<b <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-cog"></i> 系统设置<b
class="caret"></b></a> class="caret"></b></a>

View File

@ -42,9 +42,9 @@
</table> </table>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script> <script src="{$cdnpublic}layer/3.5.1/layer.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/bootstrap-table.min.js"></script> <script src="{$cdnpublic}bootstrap-table/1.19.1/bootstrap-table.min.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.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 src="/static/js/custom.js"></script>
<script> <script>
function setEnable(id,enable) { function setEnable(id,enable) {

View File

@ -23,9 +23,9 @@
</table> </table>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script> <script src="{$cdnpublic}layer/3.5.1/layer.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/bootstrap-table.min.js"></script> <script src="{$cdnpublic}bootstrap-table/1.19.1/bootstrap-table.min.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.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 src="/static/js/custom.js"></script>
<script> <script>

View File

@ -1,100 +1,100 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-cn"> <html lang="zh-cn">
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>管理员登录</title> <title>管理员登录</title>
<link href="//cdn.staticfile.org/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/> <link href="{$cdnpublic}twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="//cdn.staticfile.org/modernizr/2.8.3/modernizr.min.js"></script> <script src="{$cdnpublic}modernizr/2.8.3/modernizr.min.js"></script>
<script src="//cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script> <script src="{$cdnpublic}jquery/2.1.4/jquery.min.js"></script>
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="//cdn.staticfile.org/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="{$cdnpublic}html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="//cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script> <script src="{$cdnpublic}respond.js/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body> <body>
<nav class="navbar navbar-fixed-top navbar-default"> <nav class="navbar navbar-fixed-top navbar-default">
<div class="container"> <div class="container">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">导航按钮</span> <span class="sr-only">导航按钮</span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="./">宝塔第三方云端管理中心</a> <a class="navbar-brand" href="./">Cloud</a>
</div><!-- /.navbar-header --> </div><!-- /.navbar-header -->
<div id="navbar" class="collapse navbar-collapse"> <div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li class="active"> <li class="active">
<a href="#"><span class="glyphicon glyphicon-user"></span> 登录</a> <a href="#"><span class="glyphicon glyphicon-user"></span> 登录</a>
</li> </li>
</ul> </ul>
</div><!-- /.navbar-collapse --> </div><!-- /.navbar-collapse -->
</div><!-- /.container --> </div><!-- /.container -->
</nav><!-- /.navbar --> </nav><!-- /.navbar -->
<div class="container" style="padding-top:70px;"> <div class="container" style="padding-top:70px;">
<div class="col-xs-12 col-sm-10 col-md-8 col-lg-6 center-block" style="float: none;"> <div class="col-xs-12 col-sm-10 col-md-8 col-lg-6 center-block" style="float: none;">
<div class="panel panel-primary"> <div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">管理员登录</h3></div> <div class="panel-heading"><h3 class="panel-title">管理员登录</h3></div>
<div class="panel-body"> <div class="panel-body">
<form class="form-horizontal" role="form" onsubmit="return submitlogin()"> <form class="form-horizontal" role="form" onsubmit="return submitlogin()">
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span> <span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
<input type="text" name="user" value="" class="form-control input-lg" placeholder="用户名" required="required"/> <input type="text" name="user" value="" class="form-control input-lg" placeholder="用户名" required="required"/>
</div><br/> </div><br/>
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span> <span class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>
<input type="password" name="pass" class="form-control input-lg" placeholder="密码" required="required"/> <input type="password" name="pass" class="form-control input-lg" placeholder="密码" required="required"/>
</div><br/> </div><br/>
<div class="input-group"> <div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-adjust"></span></span> <span class="input-group-addon"><span class="glyphicon glyphicon-adjust"></span></span>
<input type="text" class="form-control input-lg" name="code" placeholder="输入验证码" autocomplete="off" required> <input type="text" class="form-control input-lg" name="code" placeholder="输入验证码" autocomplete="off" required>
<span class="input-group-addon" style="padding: 0"> <span class="input-group-addon" style="padding: 0">
<img src="/admin/verifycode" height="45" id="verifycode" onclick="this.src='/admin/verifycode?r='+Math.random();" title="点击更换验证码"> <img src="/admin/verifycode" height="45" id="verifycode" onclick="this.src='/admin/verifycode?r='+Math.random();" title="点击更换验证码">
</span> </span>
</div><br/> </div><br/>
<div class="form-group"> <div class="form-group">
<div class="col-xs-12"><input type="submit" value="立即登录" class="btn btn-primary btn-block btn-lg"/></div> <div class="col-xs-12"><input type="submit" value="立即登录" class="btn btn-primary btn-block btn-lg"/></div>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script> <script src="{$cdnpublic}layer/3.5.1/layer.js"></script>
<script> <script>
function submitlogin(){ function submitlogin(){
var user = $("input[name='user']").val(); var user = $("input[name='user']").val();
var pass = $("input[name='pass']").val(); var pass = $("input[name='pass']").val();
var code = $("input[name='code']").val(); var code = $("input[name='code']").val();
if(user=='' || pass==''){layer.alert('用户名或密码不能为空!');return false;} if(user=='' || pass==''){layer.alert('用户名或密码不能为空!');return false;}
var ii = layer.load(2); var ii = layer.load(2);
$.ajax({ $.ajax({
type : 'POST', type : 'POST',
url : '{:request()->url()}', url : '{:request()->url()}',
data: {username:user, password:pass, code:code}, data: {username:user, password:pass, code:code},
dataType : 'json', dataType : 'json',
success : function(data) { success : function(data) {
layer.close(ii); layer.close(ii);
if(data.code == 0){ if(data.code == 0){
layer.msg('登录成功,正在跳转', {icon: 1,shade: 0.01,time: 15000}); layer.msg('登录成功,正在跳转', {icon: 1,shade: 0.01,time: 15000});
window.location.href='/admin'; window.location.href='/admin';
}else{ }else{
if(data.msg.indexOf('验证码')==-1){ if(data.msg.indexOf('验证码')==-1){
$("#verifycode").attr('src', '/admin/verifycode?r='+Math.random()) $("#verifycode").attr('src', '/admin/verifycode?r='+Math.random())
} }
layer.alert(data.msg, {icon: 2}); layer.alert(data.msg, {icon: 2});
} }
}, },
error:function(data){ error:function(data){
layer.close(ii); layer.close(ii);
layer.msg('服务器错误'); layer.msg('服务器错误');
} }
}); });
return false; return false;
} }
</script> </script>
</body> </body>
</html> </html>

View File

@ -35,6 +35,7 @@ td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:340px;
<div class="modal-body"> <div class="modal-body">
<p>“版本与状态”一列中,红色的按钮代表本地不存在该版本插件包,需要点击下载;绿色的按钮代表已存在。</p> <p>“版本与状态”一列中,红色的按钮代表本地不存在该版本插件包,需要点击下载;绿色的按钮代表已存在。</p>
<p>官方插件包本地存储路径是/data/plugins/package/软件标识-版本号.zip第三方插件包路径是/data/plugins/other/other/,对于部分包含二次验证的插件可以自行修改。</p> <p>官方插件包本地存储路径是/data/plugins/package/软件标识-版本号.zip第三方插件包路径是/data/plugins/other/other/,对于部分包含二次验证的插件可以自行修改。</p>
<p>若对接的服务器网速较慢,可能会导致下载失败,提示"服务器错误",可稍等一会,进入对接服务器/tmp/plugins目录下载插件包将_改成-,并上传到本站/data/plugins/package目录下。</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
@ -69,12 +70,12 @@ td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:340px;
</table> </table>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script> <script src="{$cdnpublic}layer/3.5.1/layer.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/bootstrap-table.min.js"></script> <script src="{$cdnpublic}bootstrap-table/1.19.1/bootstrap-table.min.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.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 src="/static/js/custom.js"></script>
<script> <script>
var skip_plugins = {:json_encode($skip_plugins)};
function download_version(name, version, status){ function download_version(name, version, status){
if(status == true){ if(status == true){
var confirm = layer.confirm('是否确定重新下载'+version+'版本插件包?', { var confirm = layer.confirm('是否确定重新下载'+version+'版本插件包?', {
@ -157,8 +158,13 @@ function download_item(){
layer.alert('成功下载'+$.downloadCount+'个插件包!', {icon:1}, function(){layer.closeAll();searchSubmit();}); layer.alert('成功下载'+$.downloadCount+'个插件包!', {icon:1}, function(){layer.closeAll();searchSubmit();});
return; return;
} }
$.downloadCount++;
var plugin = $.preDownload[0]; var plugin = $.preDownload[0];
if(skip_plugins.indexOf(plugin.name) != -1){
$.preDownload.shift();
download_item();
return;
}
$.downloadCount++;
var ii = layer.msg('['+$.downloadCount+'/'+$.preDownloadCount+']正在下载'+plugin.name+'-'+plugin.version, {icon: 16, shade:0.1, time: 0}); var ii = layer.msg('['+$.downloadCount+'/'+$.preDownloadCount+']正在下载'+plugin.name+'-'+plugin.version, {icon: 16, shade:0.1, time: 0});
$.ajax({ $.ajax({
type : 'POST', type : 'POST',

View File

@ -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">&times;</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>&nbsp;
<a href="javascript:searchClear()" class="btn btn-default"><i class="fa fa-repeat"></i> 重置</a>&nbsp;
<a href="javascript:refresh_plugins()" class="btn btn-success"><i class="fa fa-refresh"></i> 刷新列表</a>&nbsp;
<a href="javascript:download_plugins()" class="btn btn-warning" id="batch_down" style="display:none;"><i class="fa fa-download"></i> 批量下载</a>&nbsp;
<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>&nbsp;';
else
html += '<a href="javascript:download_version(\''+row.name+'\',\''+item.version+'\','+item.status+')" class="btn btn-xs btn-danger">'+item.version+'</a>&nbsp;';
})
}
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}

View File

@ -69,9 +69,9 @@ td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:340px;
</table> </table>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script> <script src="{$cdnpublic}layer/3.5.1/layer.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/bootstrap-table.min.js"></script> <script src="{$cdnpublic}bootstrap-table/1.19.1/bootstrap-table.min.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.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 src="/static/js/custom.js"></script>
<script> <script>
@ -163,7 +163,7 @@ function download_item(){
$.ajax({ $.ajax({
type : 'POST', type : 'POST',
url : '/admin/download_plugin', url : '/admin/download_plugin',
data: { name:plugin.name, version:plugin.version}, data: { name:plugin.name, version:plugin.version, os:'Windows'},
dataType : 'json', dataType : 'json',
success : function(data) { success : function(data) {
layer.close(ii) layer.close(ii)

View File

@ -23,9 +23,9 @@
</table> </table>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script> <script src="{$cdnpublic}layer/3.5.1/layer.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/bootstrap-table.min.js"></script> <script src="{$cdnpublic}bootstrap-table/1.19.1/bootstrap-table.min.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.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 src="/static/js/custom.js"></script>
<script> <script>

View File

@ -49,8 +49,6 @@
</form> </form>
</div> </div>
</div> </div>
</div>
<div class="col-sm-12 col-md-6 center-block">
<div class="panel panel-info"> <div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">Windows面板版本设置</h3></div> <div class="panel-heading"><h3 class="panel-title">Windows面板版本设置</h3></div>
<div class="panel-body"> <div class="panel-body">
@ -76,6 +74,8 @@
</form> </form>
</div> </div>
</div> </div>
</div>
<div class="col-sm-12 col-md-6 center-block">
<div class="panel panel-info"> <div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">云监控版本设置</h3></div> <div class="panel-heading"><h3 class="panel-title">云监控版本设置</h3></div>
<div class="panel-body"> <div class="panel-body">
@ -99,6 +99,31 @@
</form> </form>
</div> </div>
</div> </div>
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">aaPanel面板版本设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
<div class="form-group">
<label>aaPanel面板最新版本号</label>
<input type="text" name="new_version_en" value="{:config_get('new_version_en')}" class="form-control"/>
<font color="green">用于一键更新脚本获取最新版本号,以及检测更新接口。并确保已在/public/install/update/放置对应版本更新包</font>
</div>
<div class="form-group">
<label>aaPanel面板更新日志</label>
<textarea class="form-control" name="update_msg_en" rows="5" placeholder="支持HTML代码">{:config_get('update_msg_en')}</textarea>
<font color="green">用于检测更新接口返回</font>
</div>
<div class="form-group">
<label>aaPanel面板更新日期</label>
<input type="date" name="update_date_en" value="{:config_get('update_date_en')}" class="form-control"/>
<font color="green">用于检测更新接口返回</font>
</div>
<div class="form-group text-center">
<input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/>
</div>
</form>
</div>
</div>
</div> </div>
{elseif $mod=='api'} {elseif $mod=='api'}
<div class="col-sm-12 col-md-6 center-block"> <div class="col-sm-12 col-md-6 center-block">
@ -149,7 +174,7 @@
</div><hr/> </div><hr/>
<div id="wbt_type_0" style="{if config_get('wbt_type')==1}display:none;{/if}"> <div id="wbt_type_0" style="{if config_get('wbt_type')==1}display:none;{/if}">
<p>以下宝塔面板请使用官方最新脚本安装并绑定账号,用于获取插件列表及插件包</p> <p>以下宝塔面板请使用官方最新脚本安装并绑定账号,用于获取插件列表及插件包</p>
<p><a href="/static/file/win/kaixin.zip">下载专用插件(Win)</a>在面板【软件商店】->【第三方应用】,点击【导入插件】,导入该专用插件</p> <p><a href="/static/file/win/kaixin.zip">下载专用插件(Win)</a>上传到 /BtSoft/panel/plugin/ 解压,即可在软件商店已安装列表看到</p>
<div class="form-group"> <div class="form-group">
<label>宝塔面板URL</label><br/> <label>宝塔面板URL</label><br/>
<input type="text" name="wbt_url" value="{:config_get('wbt_url')}" class="form-control"/> <input type="text" name="wbt_url" value="{:config_get('wbt_url')}" class="form-control"/>
@ -175,6 +200,43 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-sm-12 col-md-6 center-block">
<div class="panel panel-info">
<div class="panel-heading"><h3 class="panel-title">aaPanel面板接口设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
<div class="form-group">
<label>对接方式:</label><br/>
<select class="form-control" name="enbt_type" default="{:config_get('enbt_type')}"><option value="0">对接aaPanel面板接口</option><option value="1">对接其他第三方云端</option></select>
</div><hr/>
<div id="enbt_type_0" style="{if config_get('enbt_type')==1}display:none;{/if}">
<p>以下aaPanel面板请使用官方最新脚本安装并绑定账号用于获取插件列表及插件包</p>
<p><a href="/static/file/en/kaixin.zip">下载专用插件(aaPanel)</a>,在面板【软件商店】->【第三方应用】,点击【导入插件】,导入该专用插件。</p>
<div class="form-group">
<label>aaPanel面板URL</label><br/>
<input type="text" name="enbt_url" value="{:config_get('enbt_url')}" class="form-control"/>
<font color="green">填写规则如:<u>http://192.168.1.1:8888</u> ,不要带其他后缀</font>
</div>
<div class="form-group">
<label>aaPanel面板接口密钥</label>
<input type="text" name="enbt_key" value="{:config_get('enbt_key')}" class="form-control"/>
</div>
</div>
<div id="enbt_type_1" style="{if !config_get('enbt_type')}display:none;{/if}">
<div class="form-group">
<label>第三方云端首页URL</label><br/>
<input type="text" name="enbt_surl" value="{:config_get('enbt_surl')}" class="form-control"/>
<font color="green">填写规则如:<u>http://www.example.com/</u> ,必须以/结尾</font>
</div>
</div>
<div class="form-group text-center">
<button type="button" class="btn btn-info btn-block" id="testbturl3">测试连接</button>
<input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/>
</div>
</form>
</div>
</div>
</div>
<script> <script>
$("select[name='bt_type']").change(function(){ $("select[name='bt_type']").change(function(){
if($(this).val() == 1){ if($(this).val() == 1){
@ -194,6 +256,15 @@ $("select[name='wbt_type']").change(function(){
$("#wbt_type_1").hide(); $("#wbt_type_1").hide();
} }
}); });
$("select[name='enbt_type']").change(function(){
if($(this).val() == 1){
$("#enbt_type_0").hide();
$("#enbt_type_1").show();
}else{
$("#enbt_type_0").show();
$("#enbt_type_1").hide();
}
});
</script> </script>
{elseif $mod=='task'} {elseif $mod=='task'}
<div class="col-sm-12 col-md-6 center-block"> <div class="col-sm-12 col-md-6 center-block">
@ -202,7 +273,7 @@ $("select[name='wbt_type']").change(function(){
<div class="panel-body"> <div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form"> <form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
<div class="alert alert-info">使用以下命令可以从宝塔官方获取最新的插件列表并批量下载插件包(增量更新)。<br/>你也可以将此命令添加到crontab以使此云端的插件保持最新建议1天执行1次。</div> <div class="alert alert-info">使用以下命令可以从宝塔官方获取最新的插件列表并批量下载插件包(增量更新)。<br/>你也可以将此命令添加到crontab以使此云端的插件保持最新建议1天执行1次。</div>
<div class="alert alert-danger">使用命令执行之后,可能会导致 /data 目录下文件权限不对,后台插件列表下载插件覆盖会报错,需要手动循环设置 /data 目录权限。</div> {if $is_user_www}<div class="alert alert-danger">计划任务执行用户必须选择www用户</div>{/if}
<div class="alert alert-warning">上次运行时间:{$runtime|raw}</div> <div class="alert alert-warning">上次运行时间:{$runtime|raw}</div>
<div class="list-group-item">php {:app()->getRootPath()}think updateall</div><br/> <div class="list-group-item">php {:app()->getRootPath()}think updateall</div><br/>
</form> </form>
@ -222,6 +293,10 @@ $("select[name='wbt_type']").change(function(){
<label>Windows面板批量下载插件范围</label><br/> <label>Windows面板批量下载插件范围</label><br/>
<select class="form-control" name="updateall_type_win" default="{:config_get('updateall_type_win')}"><option value="0">仅免费插件</option><option value="1">免费插件+专业版插件</option><option value="2">免费插件+专业版插件+企业版插件</option></select><font color="green">(批量下载不包含所有第三方插件,第三方插件需要去手动下载。)</font> <select class="form-control" name="updateall_type_win" default="{:config_get('updateall_type_win')}"><option value="0">仅免费插件</option><option value="1">免费插件+专业版插件</option><option value="2">免费插件+专业版插件+企业版插件</option></select><font color="green">(批量下载不包含所有第三方插件,第三方插件需要去手动下载。)</font>
</div> </div>
<div class="form-group">
<label>aaPanel面板批量下载插件范围</label><br/>
<select class="form-control" name="updateall_type_en" default="{:config_get('updateall_type_en')}"><option value="0">仅免费插件</option><option value="1">免费插件+专业版插件</option><option value="2">免费插件+专业版插件+企业版插件</option></select><font color="green">(批量下载不包含所有第三方插件,第三方插件需要去手动下载。)</font>
</div>
<div class="form-group text-center"> <div class="form-group text-center">
<input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/> <input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/>
</div> </div>
@ -279,7 +354,7 @@ $("select[name='wbt_type']").change(function(){
</div> </div>
</div> </div>
{/if} {/if}
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script> <script src="{$cdnpublic}layer/3.5.1/layer.js"></script>
<script> <script>
$(document).ready(function(){ $(document).ready(function(){
var items = $("select[default]"); var items = $("select[default]");
@ -309,7 +384,7 @@ $(document).ready(function(){
if(bt_key == ''){ if(bt_key == ''){
layer.alert('宝塔面板接口密钥不能为空');return; layer.alert('宝塔面板接口密钥不能为空');return;
} }
var postdata = {bt_type:bt_type, bt_url:bt_url, bt_key:bt_key}; var postdata = {os:'linux', bt_type:bt_type, bt_url:bt_url, bt_key:bt_key};
} }
var ii = layer.load(2, {shade:[0.1,'#fff']}); var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({ $.ajax({
@ -354,7 +429,52 @@ $(document).ready(function(){
if(wbt_key == ''){ if(wbt_key == ''){
layer.alert('宝塔面板接口密钥不能为空');return; layer.alert('宝塔面板接口密钥不能为空');return;
} }
var postdata = {bt_type:wbt_type, bt_url:wbt_url, bt_key:wbt_key}; var postdata = {os:'win', bt_type:wbt_type, bt_url:wbt_url, bt_key:wbt_key};
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/admin/testbturl',
data : postdata,
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
layer.msg(data.msg, {icon: 1, time:1000})
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
})
$("#testbturl3").click(function(){
var enbt_type = $("select[name=enbt_type]").val();
if(enbt_type == '1'){
var enbt_surl = $("input[name=enbt_surl]").val();
if(enbt_surl == ''){
layer.alert('第三方云端URL不能为空');return;
}
if(enbt_surl.indexOf('http://')==-1 && enbt_surl.indexOf('https://')==-1){
layer.alert('第三方云端URL不正确');return;
}
var postdata = {bt_type:enbt_type, bt_surl:enbt_surl};
}else{
var enbt_url = $("input[name=enbt_url]").val();
var enbt_key = $("input[name=enbt_key]").val();
if(enbt_url == ''){
layer.alert('宝塔面板URL不能为空');return;
}
if(enbt_url.indexOf('http://')==-1 && enbt_url.indexOf('https://')==-1){
layer.alert('宝塔面板URL不正确');return;
}
if(enbt_key == ''){
layer.alert('宝塔面板接口密钥不能为空');return;
}
var postdata = {os:'en', bt_type:enbt_type, bt_url:enbt_url, bt_key:enbt_key};
} }
var ii = layer.load(2, {shade:[0.1,'#fff']}); var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({ $.ajax({

86
app/view/admin/ssl.html Normal file
View File

@ -0,0 +1,86 @@
{extend name="admin/layout" /}
{block name="title"}自签名SSL证书生成{/block}
{block name="main"}
<style>
.control-label[is-required]:before {
content: "*";
color: #f56c6c;
margin-right: 4px;
}
</style>
<div class="container" style="padding-top:70px;">
<div class="col-sm-12 col-md-10 col-lg-8 center-block" style="float: none;">
<div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">自签名SSL证书生成</h3></div>
<div class="panel-body">
{if $isca}
<div class="alert alert-warning" style="word-break:break-all;">下载CA证书并导入可解决浏览器不安全提醒。<br/>Windows<a href="/ssl/baota_root.pfx">baota_root.pfx</a>密码为空Mac/Linux<a href="/ssl/baota_root.crt">baota_root.crt</a></div>
<form onsubmit="return makeSSL(this)" method="post" class="form" role="form">
<div class="form-group">
<label is-required="true" class="control-label">域名列表:</label>
<textarea class="form-control" name="domain_list" rows="6" placeholder="每行一个域名/IP支持通配符" required></textarea>
</div>
<div class="form-group">
<label class="control-label">通用名称:</label>
<input type="text" name="common_name" value="" placeholder="留空则为域名列表第一个域名" class="form-control"/>
</div>
<div class="form-group">
<label is-required="true" class="control-label">有效天数:</label>
<input type="number" name="validity" value="3650" class="form-control" required/>
</div>
<div class="form-group text-center">
<input type="submit" name="submit" value="生成自签名证书" class="btn btn-success btn-block"/>
</div>
<div class="form-group row" id="result" style="display:none;">
<div class="col-md-6">
<label class="control-label">SSL证书</label>
<textarea class="form-control" name="ssl_cert" rows="5" onclick="copy(this)" title="点击复制"></textarea>
</div>
<div class="col-md-6">
<label class="control-label">SSL证书私钥</label>
<textarea class="form-control" name="ssl_key" rows="5" onclick="copy(this)" title="点击复制"></textarea>
</div>
</div>
</form>
{else}
<div class="alert alert-danger" role="alert">你还没有生成CA证书无法生成SSL证书</div>
<div class="alert alert-info" style="word-break:break-all;">执行以下命令生成自签名CA证书。然后可通过接口或当前页面生成SSL证书用于面板访问。</div>
<div class="list-group-item" style="word-break:break-all;">cd {:app()->getRootPath()}app/script && chmod +x cacert.sh && ./cacert.sh</div><br/>
{/if}
</div>
</div>
<script src="{$cdnpublic}layer/3.5.1/layer.js"></script>
<script>
function makeSSL(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/admin/ssl',
data : $(obj).serialize(),
dataType : 'json',
success : function(data) {
layer.close(ii);
if(data.code == 0){
$("textarea[name='ssl_cert']").val(data.cert);
$("textarea[name='ssl_key']").val(data.key);
$("#result").show();
layer.msg('SSL证书生成成功', {icon:1, time:800});
}else{
layer.alert(data.msg, {icon: 2})
}
},
error:function(data){
layer.close(ii);
layer.msg('服务器错误');
}
});
return false;
}
function copy(obj){
if($(obj).val() == '') return;
$(obj).select();
document.execCommand("Copy");
layer.msg('复制成功', {icon:1, time:500});
}
</script>
{/block}

View File

@ -1,60 +1,60 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>温馨提示</title> <title>温馨提示</title>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width"> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta name="renderer" content="webkit"/> <meta name="renderer" content="webkit"/>
<style type="text/css"> <style type="text/css">
*{box-sizing:border-box;margin:0;padding:0;font-family:Lantinghei SC,Open Sans,Arial,Hiragino Sans GB,Microsoft YaHei,"微软雅黑",STHeiti,WenQuanYi Micro Hei,SimSun,sans-serif;-webkit-font-smoothing:antialiased} *{box-sizing:border-box;margin:0;padding:0;font-family:Lantinghei SC,Open Sans,Arial,Hiragino Sans GB,Microsoft YaHei,"微软雅黑",STHeiti,WenQuanYi Micro Hei,SimSun,sans-serif;-webkit-font-smoothing:antialiased}
body{padding:70px 0;background:#edf1f4;font-weight:400;font-size:1pc;-webkit-text-size-adjust:none;color:#333} body{padding:70px 0;background:#edf1f4;font-weight:400;font-size:1pc;-webkit-text-size-adjust:none;color:#333}
a{outline:0;color:#3498db;text-decoration:none;cursor:pointer} a{outline:0;color:#3498db;text-decoration:none;cursor:pointer}
.system-message{margin:20px 5%;padding:40px 20px;background:#fff;box-shadow:1px 1px 1px hsla(0,0%,39%,.1);text-align:center} .system-message{margin:20px 5%;padding:40px 20px;background:#fff;box-shadow:1px 1px 1px hsla(0,0%,39%,.1);text-align:center}
.system-message h1{margin:0;margin-bottom:9pt;color:#444;font-weight:400;font-size:40px} .system-message h1{margin:0;margin-bottom:9pt;color:#444;font-weight:400;font-size:40px}
.system-message .jump,.system-message .image{margin:20px 0;padding:0;padding:10px 0;font-weight:400} .system-message .jump,.system-message .image{margin:20px 0;padding:0;padding:10px 0;font-weight:400}
.system-message .jump{font-size:14px} .system-message .jump{font-size:14px}
.system-message .jump a{color:#333} .system-message .jump a{color:#333}
.system-message p{font-size:9pt;line-height:20px} .system-message p{font-size:9pt;line-height:20px}
.system-message .btn{display:inline-block;margin-right:10px;width:138px;height:2pc;border:1px solid #44a0e8;border-radius:30px;color:#44a0e8;text-align:center;font-size:1pc;line-height:2pc;margin-bottom:5px;} .system-message .btn{display:inline-block;margin-right:10px;width:138px;height:2pc;border:1px solid #44a0e8;border-radius:30px;color:#44a0e8;text-align:center;font-size:1pc;line-height:2pc;margin-bottom:5px;}
.success .btn{border-color:#69bf4e;color:#69bf4e} .success .btn{border-color:#69bf4e;color:#69bf4e}
.error .btn{border-color:#ff8992;color:#ff8992} .error .btn{border-color:#ff8992;color:#ff8992}
.info .btn{border-color:#3498db;color:#3498db} .info .btn{border-color:#3498db;color:#3498db}
.copyright p{width:100%;color:#919191;text-align:center;font-size:10px} .copyright p{width:100%;color:#919191;text-align:center;font-size:10px}
.system-message .btn-grey{border-color:#bbb;color:#bbb} .system-message .btn-grey{border-color:#bbb;color:#bbb}
.clearfix:after{clear:both;display:block;visibility:hidden;height:0;content:"."} .clearfix:after{clear:both;display:block;visibility:hidden;height:0;content:"."}
@media (max-width:768px){body {padding:20px 0;}} @media (max-width:768px){body {padding:20px 0;}}
@media (max-width:480px){.system-message h1{font-size:30px;}} @media (max-width:480px){.system-message h1{font-size:30px;}}
</style> </style>
</head> </head>
<body> <body>
<div class="system-message {$code}"> <div class="system-message {$code}">
<div class="image"> <div class="image">
<img src="/static/images/{$code}.svg" alt="" width="150" /> <img src="/static/images/{$code}.svg" alt="" width="150" />
</div> </div>
<h1>{$msg}</h1> <h1>{$msg}</h1>
{if $url} {if $url}
<p class="jump"> <p class="jump">
页面将在 <span id="wait">{$wait}</span> 秒后自动跳转 页面将在 <span id="wait">{$wait}</span> 秒后自动跳转
</p> </p>
{/if} {/if}
<p class="clearfix"> <p class="clearfix">
<a href="javascript:history.go(-1);" class="btn btn-grey">返回上一页</a> <a href="javascript:history.go(-1);" class="btn btn-grey">返回上一页</a>
{if $url} {if $url}
<a href="{$url}" class="btn btn-primary">立即跳转</a> <a href="{$url}" class="btn btn-primary">立即跳转</a>
{/if} {/if}
</p> </p>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
(function () { (function () {
var wait = document.getElementById('wait'); var wait = document.getElementById('wait');
var interval = setInterval(function () { var interval = setInterval(function () {
var time = --wait.innerHTML; var time = --wait.innerHTML;
if (time <= 0) { if (time <= 0) {
location.href = "{$url}"; location.href = "{$url}";
clearInterval(interval); clearInterval(interval);
} }
}, 1000); }, 1000);
})(); })();
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,261 +1,306 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="renderer" content="webkit" /> <meta name="renderer" content="webkit" />
<meta name="force-rendering" content="webkit" /> <meta name="force-rendering" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>宝塔面板安装脚本</title> <title>宝塔面板安装脚本</title>
<link rel="stylesheet" type="text/css" href="/static/css/sanren.css" /> <link rel="stylesheet" type="text/css" href="/static/css/sanren.css" />
<link rel="stylesheet" type="text/css" href="/static/css/style.css" /> <link rel="stylesheet" type="text/css" href="/static/css/style.css" />
<link rel="stylesheet" type="text/css" href="/static/css/download.css" /> <link rel="stylesheet" type="text/css" href="/static/css/download.css" />
</head> </head>
<body> <body>
<div class="down-main"> <div class="down-main">
<div class="d1"> <div class="d1">
<div class="wrap"> <div class="wrap">
<div class="i1t textcenter"> <div class="i1t textcenter">
<h1 class="disflex flex_center flex_lrcenter textcenter">宝塔面板安装脚本<img class="ml10" src="/static/images/i1ico_03.png"></h1> <h1 class="disflex flex_center flex_lrcenter textcenter">宝塔面板安装脚本<img class="ml10" src="/static/images/i1ico_03.png"></h1>
<div class="text20 mt_25 wap_mt15 textcenter cl8"> <div class="text20 mt_25 wap_mt15 textcenter cl8">
<p>2分钟装好面板一键管理服务器</p> <p>2分钟装好面板一键管理服务器</p>
<p>集成LAMP/LNMP环境安装网站、FTP、数据库、文件管理、软件安装等功能</p> <p>集成LAMP/LNMP环境安装网站、FTP、数据库、文件管理、软件安装等功能</p>
</div> </div>
<div class="disflex flex_lrcenter mt_50 install-list"> <div class="disflex flex_lrcenter mt_50 install-list">
<div class="install-box linux"> <div class="install-box linux">
<div class="img"> <div class="img">
<img src="/static/images/prd_1_03.png"> <img src="/static/images/prd_1_03.png">
</div> </div>
<div class="cont"> <div class="cont">
<div class="top"> <div class="top">
<div class="title">Linux面板 {:config_get('new_version')}</div> <div class="title">Linux面板 {:config_get('new_version')}</div>
<div class="desc"> <div class="desc">
支持Centos、Ubuntu、Deepin、Debian、Fedora等Linux系统。 支持Centos、Ubuntu、Deepin、Debian、Fedora等Linux系统。
<a class="link" href="https://demo.bt.cn/login" target="_blank" style="margin-left: 5px; font-weight: 700" rel="noreferrer">查看演示</a> <a class="link" href="https://demo.bt.cn/login" target="_blank" style="margin-left: 5px; font-weight: 700" rel="noreferrer">查看演示</a>
</div> </div>
<div class="mark"> <div class="mark">
<span>2分钟装好</span> <span>2分钟装好</span>
<span>阿里云推荐</span> <span>阿里云推荐</span>
<span>腾讯云推荐</span> <span>腾讯云推荐</span>
</div> </div>
</div> </div>
<div class="bottom"> <div class="bottom">
<a class="btn" href="javascript:;" id="goInstallLinux">查看安装脚本</a> <a class="btn" href="javascript:;" id="goInstallLinux">查看安装脚本</a>
</div> </div>
</div> </div>
</div> </div>
<div class="install-box windows"> {if config_get('new_version_win')}<div class="install-box windows">
<div class="img"> <div class="img">
<img src="/static/images/prd_2_03.png"> <img src="/static/images/prd_2_03.png">
</div> </div>
<div class="cont"> <div class="cont">
<div class="top"> <div class="top">
<div class="title">Windows面板 {:config_get('new_version_win')}</div> <div class="title">Windows面板 {:config_get('new_version_win')}</div>
<div class="desc">支持Windows Server 2008 R2/2012/2016/2019/202264位系统</div> <div class="desc">支持Windows Server 2008 R2/2012/2016/2019/202264位系统</div>
<div class="mark"> <div class="mark">
<span>操作简单</span> <span>操作简单</span>
<span>使用方便</span> <span>使用方便</span>
</div> </div>
</div> </div>
<div class="bottom"> <div class="bottom">
<a class="btn" href="javascript:;" id="goInstallWindows">查看安装方法</a> <a class="btn" href="javascript:;" id="goInstallWindows">查看安装方法</a>
</div> </div>
</div> </div>
</div> </div>{/if}
</div> </div>
{if config_get('new_version_btm')}<div class="disflex flex_lrcenter mt_30 install-list"> {if config_get('new_version_btm') || config_get('new_version_en')}<div class="disflex flex_lrcenter mt_30 install-list">
<div class="install-box monitor"> {if config_get('new_version_win')}<div class="install-box monitor">
<div class="img"> <div class="img">
<img src="/static/images/bt_monitor.png" /> <img src="/static/images/bt_monitor.png" style="height: 96px;"/>
</div> </div>
<div class="cont"> <div class="cont">
<div class="top"> <div class="top">
<div class="title">堡塔云监控</div> <div class="title">云安全监控</div>
<div class="desc">多服务监控和异常告警通知</div> <div class="desc">多机跨平台安全管理和监控</div>
</div> </div>
<div class="bottom"> <div class="bottom">
<a class="btn" href="javascript:;" id="goInstallMonitor">查看安装脚本</a> <a class="btn" href="javascript:;" id="goInstallMonitor">查看安装脚本</a>
</div> </div>
</div> </div>
</div> </div>{/if}
</div>{/if} {if config_get('new_version_en')}<div class="install-box monitor">
</div> <div class="img">
</div> <img src="/static/images/aapanel.png" style="height: 96px;"/>
</div> </div>
<div class="d2" id="instal-linux"> <div class="cont">
<div class="wrap"> <div class="top">
<div class="wrap-title linux-title"> <div class="title">aaPanel {:config_get('new_version_en')}</div>
<div class="text">Linux面板{:config_get('new_version')}安装脚本</div> <div class="desc">宝塔面板国际版</div>
</div> </div>
<div class="desc"> <div class="bottom">
使用 SSH 连接工具,如 <a class="btn" href="javascript:;" id="goInstallAaPanel">查看安装脚本</a>
<a class="link" href="https://www.putty.org/" target="_blank" rel="noreferrer">PUTTY</a> </div>
连接到您的 Linux 服务器后, </div>
<a class="link" href="https://www.bt.cn/bbs/thread-50002-1-1.html" target="_blank" rel="noreferrer">挂载磁盘</a> </div>{/if}
,根据系统执行相应命令开始安装: </div>{/if}
</div> </div>
<div class="install-code"> </div>
<span class="osname">Centos安装脚本</span> </div>
<div class="code-cont"> <div class="d2" id="instal-linux">
<div class="command" title="点击复制Centos安装脚本">yum install -y wget && wget -O install.sh {$siteurl}/install/install_6.0.sh && sh install.sh</div> <div class="wrap">
<span class="ico-copy" title="点击复制Centos安装脚本" data-clipboard-text="yum install -y wget && wget -O install.sh {$siteurl}/install/install_6.0.sh && sh install.sh">复制</span> <div class="wrap-title linux-title">
</div> <div class="text">Linux面板{:config_get('new_version')}安装脚本</div>
</div> </div>
<div class="install-code"> <div class="desc">
<span class="osname">Ubuntu/Debian安装脚本</span> 使用 SSH 连接工具,如
<div class="code-cont"> <a class="link" href="https://www.putty.org/" target="_blank" rel="noreferrer">PUTTY</a>
<div class="command" title="点击复制Ubuntu/Debian安装脚本">wget -O install.sh {$siteurl}/install/install_6.0.sh && bash install.sh</div> 连接到您的 Linux 服务器后,
<span class="ico-copy" title="点击复制Ubuntu/Debian安装脚本" data-clipboard-text="wget -O install.sh {$siteurl}/install/install_6.0.sh && bash install.sh">复制</span> <a class="link" href="https://www.bt.cn/bbs/thread-50002-1-1.html" target="_blank" rel="noreferrer">挂载磁盘</a>
</div> ,根据系统执行相应命令开始安装:
</div> </div>
<div class="install-code"> <div class="install-code">
<span class="osname">一键更新脚本</span> <span class="osname">Centos安装脚本</span>
<div class="code-cont"> <div class="code-cont">
<div class="command" title="点击复制一键更新脚本">curl {$siteurl}/install/update6.sh|bash</div> <div class="command" title="点击复制Centos安装脚本">yum install -y wget && wget -O install.sh {$siteurl}/install/install_panel.sh && sh install.sh</div>
<span class="ico-copy" title="点击复制一键更新脚本" data-clipboard-text="curl {$siteurl}/install/update6.sh|bash">复制</span> <span class="ico-copy" title="点击复制Centos安装脚本" data-clipboard-text="yum install -y wget && wget -O install.sh {$siteurl}/install/install_panel.sh && sh install.sh">复制</span>
</div> </div>
</div> </div>
<div class="tips" style="color: orangered; font-weight: 700"> <div class="install-code">
<p>注意必须为没装过其它环境如Apache/Nginx/php/MySQL的新系统,推荐使用centos 7.X的系统安装宝塔面板</p> <span class="osname">Ubuntu/Debian安装脚本</span>
<p style="text-indent: 3em">推荐使用Chrome、火狐、edge浏览器国产浏览器请使用极速模式访问面板登录地址</p> <div class="code-cont">
<p style="text-indent: 3em">如果使用过官方版或其他第三方云端的版本,使用一键更新脚本即可切换到此云端</p> <div class="command" title="点击复制Ubuntu/Debian安装脚本">wget -O install.sh {$siteurl}/install/install_panel.sh && bash install.sh</div>
</div> <span class="ico-copy" title="点击复制Ubuntu/Debian安装脚本" data-clipboard-text="wget -O install.sh {$siteurl}/install/install_panel.sh && bash install.sh">复制</span>
</div> </div>
</div> </div>
<div class="d4" id="instal-windows" style="background-color: #edf6ef;"> <div class="install-code">
<div class="wrap"> <span class="osname">一键更新脚本</span>
<div class="wrap-title"> <div class="code-cont">
<div class="text">Windows面板{:config_get('new_version_win')}安装方法</div> <div class="command" title="点击复制一键更新脚本">curl {$siteurl}/install/update6.sh|bash</div>
</div> <span class="ico-copy" title="点击复制一键更新脚本" data-clipboard-text="curl {$siteurl}/install/update6.sh|bash">复制</span>
<div class="desc"> </div>
<p>1、使用<a class="link" href="https://download.bt.cn/win/panel/BtSoft.zip" target="_blank" rel="noreferrer">官方安装程序</a>进行安装,安装完先不要进入面板。</p> </div>
<p>2、在命令提示符cmd输入以下一键更新命令然后重启面板。</p> <div class="tips" style="color: orangered; font-weight: 700">
</div> <p>注意必须为没装过其它环境如Apache/Nginx/php/MySQL的新系统,推荐使用centos 7.X的系统安装宝塔面板</p>
<div class="install-code"> <p style="text-indent: 3em">推荐使用Chrome、火狐、edge浏览器国产浏览器请使用极速模式访问面板登录地址</p>
<div class="code-cont"> <p style="text-indent: 3em">如果使用过官方版或其他第三方云端的版本,使用一键更新脚本即可切换到此云端</p>
<div class="command" title="点击复制一键更新命令">wget {$siteurl}/win/panel/data/setup.py -O C:/update.py && &quot;C:\Program Files\python\python.exe&quot; C:/update.py update_panel {:config_get('new_version_win')}</div> </div>
<span class="ico-copy" title="点击复制一键更新命令" data-clipboard-text="wget {$siteurl}/win/panel/data/setup.py -O C:/update.py && &quot;C:\Program Files\python\python.exe&quot; C:/update.py update_panel {:config_get('new_version_win')}">复制</span> </div>
</div> </div>
</div> {if config_get('new_version_win')}
<div class="tips" style="color: orangered; font-weight: 700"> <div class="d4" id="instal-windows" style="background-color: #edf6ef;">
<p>注意仅支持Windows Server 2008 R2/2012/2016/2019/202264位系统中文简体且未安装其它环境</p> <div class="wrap">
</div> <div class="wrap-title">
</div> <div class="text">Windows面板{:config_get('new_version_win')}安装方法</div>
</div> </div>
{if config_get('new_version_btm')} <div class="desc">
<div class="d4" id="instal-monitor"> <p>1、使用<a class="link" href="https://download.bt.cn/win/panel/go/BtSoft.exe" target="_blank" rel="noreferrer">官方安装程序</a>进行安装,安装完先不要进入面板。</p>
<div class="wrap"> <p>2、在命令提示符cmd输入以下一键更新命令然后重启面板。</p>
<div class="wrap-title"> <p>3、打开面板后绑定账号时输入任意账号密码即可。</p>
<div class="text" style="margin-right: 12px;">堡塔云监控{:config_get('new_version_btm')}安装脚本</div> </div>
</div> <div class="install-code">
<div class="desc"> <div class="code-cont">
使用 SSH 连接工具,如 <div class="command" title="点击复制一键更新命令">wget {$siteurl}/win/install/panel_update.py -O C:/update.py && &quot;C:\Program Files\python\python.exe&quot; C:/update.py {:config_get('new_version_win')}</div>
<a class="link" href="https://www.putty.org/" target="_blank" rel="noreferrer">PUTTY</a> <span class="ico-copy" title="点击复制一键更新命令" data-clipboard-text="wget {$siteurl}/win/install/panel_update.py -O C:/update.py && &quot;C:\Program Files\python\python.exe&quot; C:/update.py {:config_get('new_version_win')}">复制</span>
连接到您的 Linux 服务器后,根据系统执行相应命令开始安装: </div>
</div> </div>
<div class="install-code"> <div class="tips" style="color: orangered; font-weight: 700">
<span class="osname">堡塔云监控安装脚本</span> <p>注意支持Windows Server 2012/Windows 8及以上系统且未安装其它环境</p>
<div class="code-cont"> </div>
<div class="command" title="点击复制安装脚本">curl -sS {$siteurl}/install/install_btmonitor.sh -o /tmp/install_btmonitor.sh && bash /tmp/install_btmonitor.sh</div> </div>
<span class="ico-copy" title="点击复制安装脚本" data-clipboard-text="curl -sS {$siteurl}/install/install_btmonitor.sh -o /tmp/install_btmonitor.sh && bash /tmp/install_btmonitor.sh">复制</span> </div>{/if}
</div> {if config_get('new_version_btm')}
</div> <div class="d4" id="instal-monitor">
<div class="install-code"> <div class="wrap">
<span class="osname">堡塔云监控更新脚本</span> <div class="wrap-title">
<div class="code-cont"> <div class="text" style="margin-right: 12px;">云安全监控{:config_get('new_version_btm')}安装脚本</div>
<div class="command" title="点击复制更新脚本">curl {$siteurl}/install/update_btmonitor.sh|bash</div> </div>
<span class="ico-copy" title="点击复制更新脚本" data-clipboard-text="curl {$siteurl}/install/update_btmonitor.sh|bash">复制</span> <div class="desc">
</div> 使用 SSH 连接工具,如
</div> <a class="link" href="https://www.putty.org/" target="_blank" rel="noreferrer">PUTTY</a>
<div class="tips" style="color: orangered; font-weight: 700"> 连接到您的 Linux 服务器后,根据系统执行相应命令开始安装:
<p>注意推荐使用Chrome、火狐、edge浏览器国产浏览器极速模式</p> </div>
</div> <div class="install-code">
</div> <span class="osname">云安全监控安装脚本</span>
</div>{/if} <div class="code-cont">
<div class="animate-bg"></div> <div class="command" title="点击复制安装脚本">curl -sS {$siteurl}/install/install_btmonitor.sh -o /tmp/install_btmonitor.sh && bash /tmp/install_btmonitor.sh</div>
</div> <span class="ico-copy" title="点击复制安装脚本" data-clipboard-text="curl -sS {$siteurl}/install/install_btmonitor.sh -o /tmp/install_btmonitor.sh && bash /tmp/install_btmonitor.sh">复制</span>
</div>
<div class="foot"> </div>
<div class="wrap"> <div class="install-code">
<div class="fb textcenter"> <span class="osname">云安全监控更新脚本</span>
<div class="fb1 textcenter"> <div class="code-cont">
<a href="http://www.bt.cn/new/agreement_open.html" target="_blank" rel="noreferrer">《开源许可协议》</a> <div class="command" title="点击复制更新脚本">curl {$siteurl}/install/update_btmonitor.sh|bash</div>
<i></i> <span class="ico-copy" title="点击复制更新脚本" data-clipboard-text="curl {$siteurl}/install/update_btmonitor.sh|bash">复制</span>
<a href="http://www.bt.cn/new/agreement_user.html" target="_blank" rel="noreferrer">《用户协议》</a> </div>
<i></i> </div>
<a href="http://www.bt.cn/new/agreement_privacy.html" target="_blank" rel="noreferrer">《隐私声明》</a> <div class="tips" style="color: orangered; font-weight: 700">
</div> <p>注意安装完成后推荐使用Chrome、火狐、edge浏览器国产浏览器极速模式访问登录系统</p>
<div class="fb2 mt_15"> </div>
<p> </div>
Copyright © {:date('Y')} 宝塔面板安装脚本 </div>{/if}
</p> {if config_get('new_version_en')}
</div> <div class="d4" id="instal-aapanel" style="background-color: #edf6ef;">
</div> <div class="wrap">
</div> <div class="wrap-title">
</div> <div class="text">aaPanel {:config_get('new_version_en')} install script</div>
</div>
<script src="//cdn.staticfile.org/jquery/3.6.0/jquery.min.js" type="text/javascript" charset="utf-8"></script> <div class="desc">
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js" type="text/javascript" charset="utf-8"></script> <p>It is recommended that you use Ubuntu22.04 to install aaPanel</p>
</div>
<script type="text/javascript" src="//cdn.staticfile.org/clipboard.js/1.7.1/clipboard.min.js"></script> <div class="install-code">
<script type="text/javascript" src="/static/js/dx.js"></script> <span class="osname">Instarll script</span>
<script> <div class="code-cont">
$(function () { <div class="command" title="Copy successfully, please paste it to the server installation">URL={$siteurl}/install/install_7.0_en.sh && if [ -f /usr/bin/curl ];then curl -ksSO $URL ;else wget -O install_7.0_en.sh $URL;fi;bash install_7.0_en.sh</div>
var userId = ''; <span class="ico-copy" title="Copy successfully, please paste it to the server installation" data-clipboard-text="URL={$siteurl}/install/install_7.0_en.sh && if [ -f /usr/bin/curl ];then curl -ksSO $URL ;else wget -O install_7.0_en.sh $URL;fi;bash install_7.0_en.sh">复制</span>
</div>
// 复制安装命令 </div>
var clipboard = new Clipboard('.ico-copy', { <div class="install-code">
text: function (element) { <span class="osname">Upgrade script</span>
return $(element).prev().text(); <div class="code-cont">
}, <div class="command" title="Copy successfully, please paste it to the server installation">curl {$siteurl}/install/update_7.x_en.sh|bash</div>
}); <span class="ico-copy" title="Copy successfully, please paste it to the server installation" data-clipboard-text="curl {$siteurl}/install/update_7.x_en.sh|bash">复制</span>
clipboard </div>
.on('success', function (e) { </div>
layer.msg(e.trigger.title + '成功', { icon: 1 }); </div>
}) </div>{/if}
.on('error', function (e) { <div class="animate-bg"></div>
layer.msg('复制失败请手动选中文本Ctrl+c复制内容', { icon: 2 }); </div>
});
<div class="foot">
$('.install-code .command').click(function () { <div class="wrap">
$(this).next('.ico-copy').click(); <div class="fb textcenter">
}); <div class="fb1 textcenter">
<a href="http://www.bt.cn/new/agreement_open.html" target="_blank" rel="noreferrer">《开源许可协议》</a>
$('#goInstallLinux').click(function () { <i></i>
scrollTop('#instal-linux'); <a href="http://www.bt.cn/new/agreement_user.html" target="_blank" rel="noreferrer">《用户协议》</a>
}); <i></i>
<a href="http://www.bt.cn/new/agreement_privacy.html" target="_blank" rel="noreferrer">《隐私声明》</a>
$('#goInstallWindows').click(function () { </div>
scrollTop('#instal-windows'); <div class="fb2 mt_15">
}); <p>
Copyright © {:date('Y')} 宝塔面板安装脚本
$('#goInstallCloud').click(function () { </p>
scrollTop('#instal-cloud'); </div>
}); </div>
</div>
$('#goInstallMonitor').click(function () { </div>
scrollTop('#instal-monitor');
}); <script src="{$cdnpublic}jquery/3.6.0/jquery.min.js" type="text/javascript" charset="utf-8"></script>
<script src="{$cdnpublic}layer/3.5.1/layer.js" type="text/javascript" charset="utf-8"></script>
function GetRequest() {
var url = location.search; <script type="text/javascript" src="{$cdnpublic}clipboard.js/1.7.1/clipboard.min.js"></script>
//获取url中"?"符后的字串 <script type="text/javascript" src="/static/js/dx.js"></script>
var theRequest = new Object(); <script>
if (url.indexOf('?') != -1) { $(function () {
var str = url.substr(1); var userId = '';
}
return str; // 复制安装命令
} var clipboard = new Clipboard('.ico-copy', {
if (GetRequest() == 'bt') { text: function (element) {
scrollTop('#instal-linux'); return $(element).prev().text();
} },
});
// 滚动到指定位置 clipboard
function scrollTop(el) { .on('success', function (e) {
var headHeight = 0; layer.msg(e.trigger.title + '成功', { icon: 1 });
$('html, body').animate({ scrollTop: $(el).offset().top - headHeight }, { duration: 200, easing: 'swing' }); })
} .on('error', function (e) {
}); layer.msg('复制失败请手动选中文本Ctrl+c复制内容', { icon: 2 });
</script> });
</body> $('.install-code .command').click(function () {
$(this).next('.ico-copy').click();
});
$('#goInstallLinux').click(function () {
scrollTop('#instal-linux');
});
$('#goInstallWindows').click(function () {
scrollTop('#instal-windows');
});
$('#goInstallCloud').click(function () {
scrollTop('#instal-cloud');
});
$('#goInstallMonitor').click(function () {
scrollTop('#instal-monitor');
});
$('#goInstallAaPanel').click(function () {
scrollTop('#instal-aapanel');
});
function GetRequest() {
var url = location.search;
//获取url中"?"符后的字串
var theRequest = new Object();
if (url.indexOf('?') != -1) {
var str = url.substr(1);
}
return str;
}
if (GetRequest() == 'bt') {
scrollTop('#instal-linux');
}
// 滚动到指定位置
function scrollTop(el) {
var headHeight = 0;
$('html, body').animate({ scrollTop: $(el).offset().top - headHeight }, { duration: 200, easing: 'swing' });
}
});
</script>
</body>
</html> </html>

View File

@ -1,268 +1,268 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>宝塔第三方云端 - 安装程序</title> <title>宝塔第三方云端 - 安装程序</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<style> <style>
body { body {
background: #f1f6fd; background: #f1f6fd;
margin: 0; margin: 0;
padding: 0; padding: 0;
line-height: 1.5; line-height: 1.5;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
body, input, button { body, input, button {
font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, 'Microsoft Yahei', Arial, sans-serif; font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, 'Microsoft Yahei', Arial, sans-serif;
font-size: 14px; font-size: 14px;
color: #7E96B3; color: #7E96B3;
} }
.container { .container {
max-width: 480px; max-width: 480px;
margin: 0 auto; margin: 0 auto;
padding: 20px; padding: 20px;
text-align: center; text-align: center;
} }
a { a {
color: #4e73df; color: #4e73df;
text-decoration: none; text-decoration: none;
} }
a:hover { a:hover {
text-decoration: underline; text-decoration: underline;
} }
h1 { h1 {
margin-top: 0; margin-top: 0;
margin-bottom: 10px; margin-bottom: 10px;
} }
h2 { h2 {
font-size: 28px; font-size: 28px;
font-weight: normal; font-weight: normal;
color: #3C5675; color: #3C5675;
margin-bottom: 0; margin-bottom: 0;
margin-top: 0; margin-top: 0;
} }
form { form {
margin-top: 40px; margin-top: 40px;
} }
.form-group { .form-group {
margin-bottom: 20px; margin-bottom: 20px;
} }
.form-group .form-field:first-child input { .form-group .form-field:first-child input {
border-top-left-radius: 4px; border-top-left-radius: 4px;
border-top-right-radius: 4px; border-top-right-radius: 4px;
} }
.form-group .form-field:last-child input { .form-group .form-field:last-child input {
border-bottom-left-radius: 4px; border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px; border-bottom-right-radius: 4px;
} }
.form-field input { .form-field input {
background: #fff; background: #fff;
margin: 0 0 2px; margin: 0 0 2px;
border: 2px solid transparent; border: 2px solid transparent;
transition: background 0.2s, border-color 0.2s, color 0.2s; transition: background 0.2s, border-color 0.2s, color 0.2s;
width: 100%; width: 100%;
padding: 15px 15px 15px 180px; padding: 15px 15px 15px 180px;
box-sizing: border-box; box-sizing: border-box;
} }
.form-field input:focus { .form-field input:focus {
border-color: #4e73df; border-color: #4e73df;
background: #fff; background: #fff;
color: #444; color: #444;
outline: none; outline: none;
} }
.form-field label { .form-field label {
float: left; float: left;
width: 160px; width: 160px;
text-align: right; text-align: right;
margin-right: -160px; margin-right: -160px;
position: relative; position: relative;
margin-top: 15px; margin-top: 15px;
font-size: 14px; font-size: 14px;
pointer-events: none; pointer-events: none;
opacity: 0.7; opacity: 0.7;
} }
button, .btn { button, .btn {
background: #3C5675; background: #3C5675;
color: #fff; color: #fff;
border: 0; border: 0;
font-weight: bold; font-weight: bold;
border-radius: 4px; border-radius: 4px;
cursor: pointer; cursor: pointer;
padding: 15px 30px; padding: 15px 30px;
-webkit-appearance: none; -webkit-appearance: none;
} }
button[disabled] { button[disabled] {
opacity: 0.5; opacity: 0.5;
} }
.form-buttons { .form-buttons {
height: 52px; height: 52px;
line-height: 52px; line-height: 52px;
} }
.form-buttons .btn { .form-buttons .btn {
margin-right: 5px; margin-right: 5px;
} }
#error, .error, #success, .success, #warmtips, .warmtips { #error, .error, #success, .success, #warmtips, .warmtips {
background: #D83E3E; background: #D83E3E;
color: #fff; color: #fff;
padding: 15px 20px; padding: 15px 20px;
border-radius: 4px; border-radius: 4px;
margin-bottom: 20px; margin-bottom: 20px;
} }
#success { #success {
background: #3C5675; background: #3C5675;
} }
#error a, .error a { #error a, .error a {
color: white; color: white;
text-decoration: underline; text-decoration: underline;
} }
#warmtips { #warmtips {
background: #fff; background: #fff;
font-size: 14px; font-size: 14px;
color: #3C5675; color: #3C5675;
border: 2px solid #4e73df; border: 2px solid #4e73df;
text-align: left; text-align: left;
} }
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1> <h1>
<svg t="1660545699809" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4887" width="100px" height="100px"> <svg t="1660545699809" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4887" width="100px" height="100px">
<path d="M811.4 418.7C765.6 297.9 648.9 212 512.2 212S258.8 297.8 213 418.6C127.3 441.1 64 519.1 64 612c0 110.5 89.5 200 199.9 200h496.2C870.5 812 960 722.5 960 612c0-92.7-63.1-170.7-148.6-193.3z m36.3 281c-23.4 23.4-54.5 36.3-87.6 36.3H263.9c-33.1 0-64.2-12.9-87.6-36.3-23.4-23.4-36.3-54.6-36.3-87.7 0-28 9.1-54.3 26.2-76.3 16.7-21.3 40.2-36.8 66.1-43.7l37.9-9.9 13.9-36.6c8.6-22.8 20.6-44.1 35.7-63.4 14.9-19.2 32.6-35.9 52.4-49.9 41.1-28.9 89.5-44.2 140-44.2s98.9 15.3 140 44.2c19.9 14 37.5 30.8 52.4 49.9 15.1 19.3 27.1 40.7 35.7 63.4l13.8 36.5 37.8 10c54.3 14.5 92.1 63.8 92.1 120 0 33.1-12.9 64.3-36.3 87.7z" p-id="4888" fill="#4e73df"></path> <path d="M811.4 418.7C765.6 297.9 648.9 212 512.2 212S258.8 297.8 213 418.6C127.3 441.1 64 519.1 64 612c0 110.5 89.5 200 199.9 200h496.2C870.5 812 960 722.5 960 612c0-92.7-63.1-170.7-148.6-193.3z m36.3 281c-23.4 23.4-54.5 36.3-87.6 36.3H263.9c-33.1 0-64.2-12.9-87.6-36.3-23.4-23.4-36.3-54.6-36.3-87.7 0-28 9.1-54.3 26.2-76.3 16.7-21.3 40.2-36.8 66.1-43.7l37.9-9.9 13.9-36.6c8.6-22.8 20.6-44.1 35.7-63.4 14.9-19.2 32.6-35.9 52.4-49.9 41.1-28.9 89.5-44.2 140-44.2s98.9 15.3 140 44.2c19.9 14 37.5 30.8 52.4 49.9 15.1 19.3 27.1 40.7 35.7 63.4l13.8 36.5 37.8 10c54.3 14.5 92.1 63.8 92.1 120 0 33.1-12.9 64.3-36.3 87.7z" p-id="4888" fill="#4e73df"></path>
</svg> </svg>
</h1> </h1>
<h2>宝塔第三方云端 - 安装程序</h2> <h2>宝塔第三方云端 - 安装程序</h2>
<div> <div>
<form method="post"> <form method="post">
<div id="error" style="display:none"></div> <div id="error" style="display:none"></div>
<div id="success" style="display:none"></div> <div id="success" style="display:none"></div>
<div id="warmtips" style="display:none"><p>安装完成后,你还需要进行以下操作:</p><p>1、在后台使用批量替换工具执行命令一键替换压缩包与安装脚本中的域名。</p><p></p>2、在后台配置面板对接同步插件列表与插件包。</p></div> <div id="warmtips" style="display:none"><p>安装完成后,你还需要进行以下操作:</p><p>1、在后台使用批量替换工具执行命令一键替换压缩包与安装脚本中的域名。</p><p></p>2、在后台配置面板对接同步插件列表与插件包。</p></div>
<div class="form-group"> <div class="form-group">
<div class="form-field"> <div class="form-field">
<label>MySQL 数据库地址</label> <label>MySQL 数据库地址</label>
<input type="text" name="mysql_host" value="localhost" required=""> <input type="text" name="mysql_host" value="localhost" required="">
</div> </div>
<div class="form-field"> <div class="form-field">
<label>MySQL 数据库端口</label> <label>MySQL 数据库端口</label>
<input type="number" name="mysql_port" value="3306"> <input type="number" name="mysql_port" value="3306">
</div> </div>
<div class="form-field"> <div class="form-field">
<label>MySQL 用户名</label> <label>MySQL 用户名</label>
<input type="text" name="mysql_user" value="" required=""> <input type="text" name="mysql_user" value="" required="">
</div> </div>
<div class="form-field"> <div class="form-field">
<label>MySQL 密码</label> <label>MySQL 密码</label>
<input type="text" name="mysql_pwd" value="" required=""> <input type="text" name="mysql_pwd" value="" required="">
</div> </div>
<div class="form-field"> <div class="form-field">
<label>MySQL 数据库名</label> <label>MySQL 数据库名</label>
<input type="text" name="mysql_name" value="" required=""> <input type="text" name="mysql_name" value="" required="">
</div> </div>
<div class="form-field"> <div class="form-field">
<label>MySQL 数据表前缀</label> <label>MySQL 数据表前缀</label>
<input type="text" name="mysql_prefix" value="cloud_"> <input type="text" name="mysql_prefix" value="cloud_">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="form-field"> <div class="form-field">
<label>管理员用户名</label> <label>管理员用户名</label>
<input type="text" name="admin_username" value="admin" required=""/> <input type="text" name="admin_username" value="admin" required=""/>
</div> </div>
<div class="form-field"> <div class="form-field">
<label>管理员密码</label> <label>管理员密码</label>
<input type="text" name="admin_password" value="123456" required=""> <input type="text" name="admin_password" value="123456" required="">
</div> </div>
</div> </div>
<div class="form-buttons"> <div class="form-buttons">
<!--@formatter:off--> <!--@formatter:off-->
<button type="submit" >点击安装</button> <button type="submit" >点击安装</button>
<!--@formatter:on--> <!--@formatter:on-->
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<script src="//cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script> <script src="//lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.4/jquery.min.js"></script>
<script> <script>
$(function () { $(function () {
$('form').on('submit', function (e) { $('form').on('submit', function (e) {
e.preventDefault(); e.preventDefault();
var form = this; var form = this;
var $error = $("#error"); var $error = $("#error");
var $success = $("#success"); var $success = $("#success");
var $button = $(this).find('button') var $button = $(this).find('button')
.text("安装中...") .text("安装中...")
.prop('disabled', true); .prop('disabled', true);
$.ajax({ $.ajax({
url: "", url: "",
type: "POST", type: "POST",
dataType: "json", dataType: "json",
data: $(this).serialize(), data: $(this).serialize(),
success: function (ret) { success: function (ret) {
if (ret.code == 1) { if (ret.code == 1) {
$error.hide(); $error.hide();
$(".form-group", form).remove(); $(".form-group", form).remove();
$button.remove(); $button.remove();
$("#success").text(ret.msg).show(); $("#success").text(ret.msg).show();
$("#warmtips").show(); $("#warmtips").show();
$buttons = $(".form-buttons", form); $buttons = $(".form-buttons", form);
$('<a class="btn" href="/admin" style="background:#4e73df">进入后台</a>').appendTo($buttons); $('<a class="btn" href="/admin" style="background:#4e73df">进入后台</a>').appendTo($buttons);
} else { } else {
$error.show().text(ret.msg); $error.show().text(ret.msg);
$button.prop('disabled', false).text("点击安装"); $button.prop('disabled', false).text("点击安装");
$("html,body").animate({ $("html,body").animate({
scrollTop: 0 scrollTop: 0
}, 500); }, 500);
} }
}, },
error: function (xhr) { error: function (xhr) {
$error.show().text(xhr.responseText); $error.show().text(xhr.responseText);
$button.prop('disabled', false).text("点击安装"); $button.prop('disabled', false).text("点击安装");
$("html,body").animate({ $("html,body").animate({
scrollTop: 0 scrollTop: 0
}, 500); }, 500);
} }
}); });
return false; return false;
}); });
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -8,5 +8,6 @@ return [
'updateall' => 'app\command\UpdateAll', 'updateall' => 'app\command\UpdateAll',
'decrypt' => 'app\command\DecryptFile', 'decrypt' => 'app\command\DecryptFile',
'clean' => 'app\command\Clean', 'clean' => 'app\command\Clean',
'cleanvitejs' => 'app\command\CleanViteJs',
], ],
]; ];

0
data/config/.gitkeep Normal file
View File

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

0
data/en/config/.gitkeep Normal file
View File

View File

View File

View File

0
data/win/config/.gitkeep Normal file
View File

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
DROP TABLE IF EXISTS `cloud_config`; DROP TABLE IF EXISTS `cloud_config`;
CREATE TABLE `cloud_config` ( CREATE TABLE `cloud_config` (
`key` varchar(32) NOT NULL, `key` varchar(32) NOT NULL,
`value` varchar(255) DEFAULT NULL, `value` varchar(1000) DEFAULT NULL,
PRIMARY KEY (`key`) PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@ -12,15 +12,18 @@ INSERT INTO `cloud_config` (`key`, `value`) VALUES
('bt_key', ''), ('bt_key', ''),
('whitelist', '0'), ('whitelist', '0'),
('download_page', '1'), ('download_page', '1'),
('new_version', '8.0.4'), ('new_version', '11.5.0'),
('update_msg', '暂无更新日志'), ('update_msg', '暂无更新日志'),
('update_date', '2023-11-19'), ('update_date', '2026-01-19'),
('new_version_win', '7.9.0'), ('new_version_win', '8.5.1'),
('update_msg_win', '暂无更新日志'), ('update_msg_win', '暂无更新日志'),
('update_date_win', '2023-07-20'), ('update_date_win', '2026-01-19'),
('new_version_btm', '2.2.9'), ('new_version_en', '7.0.25'),
('update_msg_en', '暂无更新日志'),
('update_date_en', '2025-09-10'),
('new_version_btm', '2.3.3'),
('update_msg_btm', '暂无更新日志'), ('update_msg_btm', '暂无更新日志'),
('update_date_btm', '2023-08-11'), ('update_date_btm', '2025-08-12'),
('updateall_type', '0'), ('updateall_type', '0'),
('syskey', 'UqP94LtI8eWAIgCP'); ('syskey', 'UqP94LtI8eWAIgCP');
@ -28,7 +31,7 @@ INSERT INTO `cloud_config` (`key`, `value`) VALUES
DROP TABLE IF EXISTS `cloud_black`; DROP TABLE IF EXISTS `cloud_black`;
CREATE TABLE `cloud_black` ( CREATE TABLE `cloud_black` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`ip` varchar(20) NOT NULL, `ip` varchar(50) NOT NULL,
`enable` tinyint(1) NOT NULL DEFAULT '1', `enable` tinyint(1) NOT NULL DEFAULT '1',
`addtime` datetime NOT NULL, `addtime` datetime NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
@ -38,7 +41,7 @@ CREATE TABLE `cloud_black` (
DROP TABLE IF EXISTS `cloud_white`; DROP TABLE IF EXISTS `cloud_white`;
CREATE TABLE `cloud_white` ( CREATE TABLE `cloud_white` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`ip` varchar(20) NOT NULL, `ip` varchar(50) NOT NULL,
`enable` tinyint(1) NOT NULL DEFAULT '1', `enable` tinyint(1) NOT NULL DEFAULT '1',
`addtime` datetime NOT NULL, `addtime` datetime NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
@ -48,7 +51,7 @@ CREATE TABLE `cloud_white` (
DROP TABLE IF EXISTS `cloud_record`; DROP TABLE IF EXISTS `cloud_record`;
CREATE TABLE `cloud_record` ( CREATE TABLE `cloud_record` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`ip` varchar(20) NOT NULL, `ip` varchar(50) NOT NULL,
`addtime` datetime NOT NULL, `addtime` datetime NOT NULL,
`usetime` datetime NOT NULL, `usetime` datetime NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),

File diff suppressed because it is too large Load Diff

View File

@ -411,9 +411,9 @@ EOF
chmod +x $monitor_path/tools.py chmod +x $monitor_path/tools.py
wget -O /etc/init.d/btm ${download_Url}/init/btmonitor.init -t 5 -T 10 wget -O /etc/init.d/btm ${download_Url}/init/btmonitor.init -t 5 -T 10
tmp_size=$(du -b "/etc/init.d/btm"|awk '{print $1}') tmp_size=$(du -b "/etc/init.d/btm"|awk '{print $1}')
if [ ${tmp_size} == 0 ]; then if [ ${tmp_size} == 0 ]; then
\cp -r $monitor_path/init.sh /etc/init.d/btm \cp -r $monitor_path/init.sh /etc/init.d/btm
fi fi
if [ ! -f "/etc/init.d/btm" ];then if [ ! -f "/etc/init.d/btm" ];then
\cp -r $monitor_path/init.sh /etc/init.d/btm \cp -r $monitor_path/init.sh /etc/init.d/btm
fi fi
@ -430,6 +430,9 @@ EOF
if [ -f $monitor_path/sqlite_server/PluginLoader.so ]; then if [ -f $monitor_path/sqlite_server/PluginLoader.so ]; then
rm -f $monitor_path/sqlite_server/PluginLoader.so rm -f $monitor_path/sqlite_server/PluginLoader.so
fi fi
if [ -f $monitor_path/hook_import/PluginLoader.so ]; then
rm -f $monitor_path/hook_import/PluginLoader.so
fi
} }
Start_Monitor(){ Start_Monitor(){
@ -450,6 +453,7 @@ Start_Monitor(){
for ((i=1; i<=5; i++));do for ((i=1; i<=5; i++));do
if [ -z "$password" ]; then if [ -z "$password" ]; then
sleep 7 sleep 7
rm -f /tmp/bt_monitor.lock
user_pass=`$monitor_path/tools.py create_admin` user_pass=`$monitor_path/tools.py create_admin`
password=`echo $user_pass |awk -F " " '{print $5}'` password=`echo $user_pass |awk -F " " '{print $5}'`
else else
@ -692,7 +696,7 @@ Get_Pack_Manager(){
} }
Install_RPM_Pack(){ Install_RPM_Pack(){
yumPacks="wget curl unzip gcc gcc-c++ make libcurl-devel openssl-devel xz-devel python-backports-lzma xz crontabs zlib zlib-devel sqlite-devel libffi-devel bzip2-devel lsof net-tools" yumPacks="wget curl unzip gcc gcc-c++ make libcurl-devel openssl-devel xz-devel python-backports-lzma xz crontabs zlib zlib-devel sqlite-devel libffi-devel bzip2-devel lsof net-tools p7zip-full"
yum install -y ${yumPacks} yum install -y ${yumPacks}
for yumPack in ${yumPacks} for yumPack in ${yumPacks}
@ -706,7 +710,7 @@ Install_RPM_Pack(){
} }
Install_Deb_Pack(){ Install_Deb_Pack(){
debPacks="wget curl unzip gcc g++ make cron libcurl4-openssl-dev libssl-dev liblzma-dev xz-utils libffi-dev libbz2-dev libsqlite3-dev libreadline-dev libgdbm-dev python3-bsddb3 tk-dev ncurses-dev uuid-dev zlib1g zlib1g-dev lsof net-tools"; debPacks="wget curl unzip gcc g++ make cron libcurl4-openssl-dev libssl-dev liblzma-dev xz-utils libffi-dev libbz2-dev libsqlite3-dev libreadline-dev libgdbm-dev python3-bsddb3 tk-dev ncurses-dev uuid-dev zlib1g zlib1g-dev lsof net-tools p7zip-full sqlite3";
apt-get update -y apt-get update -y
apt-get install -y $debPacks --force-yes apt-get install -y $debPacks --force-yes
@ -724,64 +728,77 @@ Check_Sys_Write(){
if [ ! -d "/etc/init.d" ];then if [ ! -d "/etc/init.d" ];then
mkdir -p /etc/init.d mkdir -p /etc/init.d
fi fi
if [ -f /usr/bin/which ];then
Get_Pack_Manager Get_Pack_Manager
if [ "$PM" == "yum" ]; then if [ "$PM" == "yum" ]; then
read_dir="/usr/lib/systemd/system/ /etc/init.d/ /var/spool/cron/" read_dir="/usr/lib/systemd/system/ /etc/init.d/ /var/spool/cron/"
else else
read_dir="/usr/lib/systemd/system/ /etc/init.d/ /var/spool/cron/crontabs/" read_dir="/usr/lib/systemd/system/ /etc/init.d/ /var/spool/cron/crontabs/"
fi fi
for dir in ${read_dir[@]} touch /tmp/btm_install_test_111.pl
do for dir in ${read_dir[@]}
if [[ -d "$dir" ]]; then do
touch $dir/btm_install_test_111.pl if [[ -d "$dir" ]]; then
state=$(echo $?) #touch $dir/btm_install_test_111.pl
if [[ "$state" != "0" ]];then if [[ ! -f "/tmp/btm_install_test_111.pl" ]]; then
echo -e "\033[31m错误检测到系统关键目录不可写! $read_dir \033[0m" echo "建立测试 /tmp/btm_install_test_111.pl 文件失败"
echo "1、如果安装了[宝塔系统加固],请先临时关闭" state=0
echo "2、如果安装了云锁请临时关闭[系统加固]功能" else
echo "3、如果安装了安全狗请临时关闭[系统防护]功能" \cp /tmp/btm_install_test_111.pl $dir/btm_install_test_111.pl
echo "4、如果使用了其它安全软件请先卸载 "
echo -e "5、如果使用了禁止写入命令请执行命令取消禁止写入\n chattr -iaR $read_dir "
echo ""
echo -e "\033[31m解决以上问题后请尝试重新安装 \033[0m"
echo -e "如果无法解决请截图以上报错信息发帖至论坛www.bt.cn/bbs求助"
exit 1
else
rm -f $dir/btm_install_test_111.pl
fi
fi fi
done state=$(echo $?)
fi if [[ "$state" != "0" ]];then
echo -e "\033[31m错误检测到系统关键目录不可写! $read_dir \033[0m"
echo "1、如果安装了[宝塔系统加固],请先临时关闭"
echo "2、如果安装了云锁请临时关闭[系统加固、文件防护]功能"
echo "3、如果安装了安全狗请临时关闭[系统防护]功能"
echo "4、如果使用了其它安全软件请先卸载 "
echo -e "5、如果使用了禁止写入命令请执行命令取消禁止写入\n chattr -iaR $read_dir "
if [ $(whoami) != "root" ];then
echo -e "6、检测到非root用户安装请尝试以下解决方案\n 1.请切换到root用户安装 \n 2.尝试执行以下安装命令:\n sudo bash $0 $@"
fi
echo ""
echo -e "\033[31m解决以上问题后请尝试重新安装 \033[0m"
echo -e "如果无法解决请截图以上报错信息发帖至论坛www.bt.cn/bbs求助"
exit 1
else
rm -f $dir/btm_install_test_111.pl
fi
fi
done
} }
Check_Sys_Packs(){ Check_Sys_Packs(){
echo "正在检查系统中是否存在必备的依赖包" echo "正在检查系统中是否存在必备的依赖包"
Packs="wget curl unzip gcc make" Packs="wget curl unzip gcc make"
for pack in ${Packs[@]} if [ -f /usr/bin/which ];then
do for pack in ${Packs[@]}
check_pack=$(which $pack) do
#echo $check_pack check_pack=$(which $pack)
if [[ "$check_pack" == "" ]]; then #echo $check_pack
echo -e "\033[31mERROR: $pack 命令不存在,尝试以下解决方法:\033[0m" if [[ "$check_pack" == "" ]]; then
if [ "$PM" == "yum" ]; then echo -e "\033[31mERROR: $pack 命令不存在,尝试以下解决方法:\033[0m"
echo 1、使用命令重新安装依赖包yum reinstall -y ${Packs} if [ "$PM" == "yum" ]; then
else echo 1、使用命令重新安装依赖包yum reinstall -y ${Packs}
echo 1、使用命令重新安装依赖包apt-get reinstall -y ${Packs} else
echo 1、使用命令重新安装依赖包apt-get reinstall -y ${Packs}
fi
echo -e "2、检查系统源是否可用尝试更换可用的源参考教程\n https://www.bt.cn/bbs/thread-58005-1-1.html "
echo ""
echo -e "\033[31m解决以上问题后请尝试重新安装 \033[0m"
echo -e "如果无法解决请截图以上报错信息发帖至论坛www.bt.cn/bbs求助"
exit 1
fi fi
echo -e "2、检查系统源是否可用尝试更换可用的源参考教程\n https://www.bt.cn/bbs/thread-58005-1-1.html " done
echo "" fi
echo -e "\033[31m解决以上问题后请尝试重新安装 \033[0m"
echo -e "如果无法解决请截图以上报错信息发帖至论坛www.bt.cn/bbs求助"
exit 1
fi
done
} }
Install_Main(){ Install_Main(){
startTime=`date +%s` startTime=`date +%s`
Check_Sys_Write Check_Sys_Write "$@"
System_Check System_Check
Get_Pack_Manager Get_Pack_Manager
get_node_url get_node_url
@ -870,7 +887,7 @@ done
if [ "$go" == 'n' ];then if [ "$go" == 'n' ];then
exit; exit;
fi fi
Install_Main Install_Main "$@"
#curl -o /dev/null -fsSL --connect-time 10 "https://api.bt.cn/bt_monitor/setup_count?cloud_type=1&token=$md5_pl&src_code=$1" #curl -o /dev/null -fsSL --connect-time 10 "https://api.bt.cn/bt_monitor/setup_count?cloud_type=1&token=$md5_pl&src_code=$1"
#curl -o /dev/null -fsSL --connect-time 10 "https://api.bt.cn/bt_monitor/setup_count?cloud_type=1&token=$md5_pl&src_code=$1&status=1" #curl -o /dev/null -fsSL --connect-time 10 "https://api.bt.cn/bt_monitor/setup_count?cloud_type=1&token=$md5_pl&src_code=$1&status=1"
@ -890,3 +907,4 @@ echo -e "=================================================================="
endTime=`date +%s` endTime=`date +%s`
((outTime=($endTime-$startTime)/60)) ((outTime=($endTime-$startTime)/60))
echo -e "Time consumed:\033[32m $outTime \033[0mMinute!" echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
rm -f install_btmonitor.sh

View File

@ -9,8 +9,35 @@ export PATH
export LANG=en_US.UTF-8 export LANG=en_US.UTF-8
export LANGUAGE=en_US:en export LANGUAGE=en_US:en
NODE_FILE_CHECK=$(cat /www/server/panel/data/node.json |grep 125.88.182.172)
if [ "${NODE_FILE_CHECK}" ];then
rm -f /www/server/panel/data/node.json
fi
if [ -f "/www/server/panel/install/d_node.pl" ];then
LOCAL_DATE=$(date +%Y-%m-%d)
FILE_DATE=$(stat /www/server/panel/install/d_node.pl|grep Change|awk '{print $2}')
if [ "${LOCAL_DATE}" != "${FILE_DATE}" ];then
rm -f /www/server/panel/install/d_node.pl
else
test_url=$(cat /www/server/panel/install/d_node.pl)
HTTP_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${test_url}/net_test|xargs|awk '{print $2}')
if [ "${HTTP_CHECK}" == "200" ];then
NODE_URL=$test_url
fi
fi
fi
get_node_url(){ get_node_url(){
nodes=(https://dg2.bt.cn https://download.bt.cn https://ctcc1-node.bt.cn https://cmcc1-node.bt.cn https://ctcc2-node.bt.cn https://hk1-node.bt.cn https://na1-node.bt.cn https://jp1-node.bt.cn); nodes=(https://dg2.bt.cn https://download.bt.cn https://ctcc1-node.bt.cn https://cmcc1-node.bt.cn https://ctcc2-node.bt.cn https://hk1-node.bt.cn https://na1-node.bt.cn https://jp1-node.bt.cn https://cf1-node.aapanel.com);
if [ -f "/www/server/panel/data/domestic_ip.pl" ];then
nodes=(https://dg2.bt.cn https://download.bt.cn https://ctcc1-node.bt.cn https://cmcc1-node.bt.cn https://ctcc2-node.bt.cn https://hk1-node.bt.cn);
fi
if [ -f "/www/server/panel/data/foreign_ip.pl" ];then
nodes=(https://cf1-node.aapanel.com https://dg2.bt.cn https://na1-node.bt.cn https://jp1-node.bt.cn https://download.bt.cn https://ctcc1-node.bt.cn https://ctcc2-node.bt.cn https://hk1-node.bt.cn);
fi
if [ "$1" ];then if [ "$1" ];then
nodes=($(echo ${nodes[*]}|sed "s#${1}##")) nodes=($(echo ${nodes[*]}|sed "s#${1}##"))
@ -24,7 +51,11 @@ get_node_url(){
touch $tmp_file2 touch $tmp_file2
for node in ${nodes[@]}; for node in ${nodes[@]};
do do
NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${node}/net_test|xargs) if [ "${node}" == "https://cf1-node.aapanel.com" ];then
NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${node}/1net_test|xargs)
else
NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${node}/net_test|xargs)
fi
RES=$(echo ${NODE_CHECK}|awk '{print $1}') RES=$(echo ${NODE_CHECK}|awk '{print $1}')
NODE_STATUS=$(echo ${NODE_CHECK}|awk '{print $2}') NODE_STATUS=$(echo ${NODE_CHECK}|awk '{print $2}')
TIME_TOTAL=$(echo ${NODE_CHECK}|awk '{print $3 * 1000 - 500 }'|cut -d '.' -f 1) TIME_TOTAL=$(echo ${NODE_CHECK}|awk '{print $3 * 1000 - 500 }'|cut -d '.' -f 1)
@ -40,8 +71,8 @@ get_node_url(){
fi fi
i=$(($i+1)) i=$(($i+1))
if [ $TIME_TOTAL -lt 200 ];then if [ $TIME_TOTAL -lt 300 ];then
if [ $RES -ge 3000 ];then if [ $RES -ge 2390 ];then
break; break;
fi fi
fi fi
@ -108,7 +139,7 @@ send_check(){
chmod +x /etc/init.d/bt chmod +x /etc/init.d/bt
p_path2=/www/server/panel/class/common.py p_path2=/www/server/panel/class/common.py
p_version=$(cat $p_path2|grep "version = "|awk '{print $3}'|tr -cd [0-9.]) p_version=$(cat $p_path2|grep "version = "|awk '{print $3}'|tr -cd [0-9.])
curl -sS --connect-timeout 3 -m 60 https://www.bt.cn/api/panel/notpro?version=$p_version curl -sS --connect-timeout 3 -m 60 http://www.example.com/api/panel/notpro?version=$p_version
NODE_URL="" NODE_URL=""
exit 0; exit 0;
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,148 @@
#!/bin/bash
#===============================================================================
# 宝塔面板更新预准备脚本
# 功能:在面板更新时,提前准备,避免面板更新失败
# 说明接收两个参数1.更新的面板版本号 2.更新的版本是否为稳定版 3.执行时机(prepare, after)
# prepare: 在下载面板文件之前就运行的内容
# after: 在替换文件之后,运行重启之前执行的内容
# 支持CentOS/RHEL、Ubuntu、Debian系统
#===============================================================================
UPDATE_VERSION="" # 版本号, 形如: 11.2.3
UPDATE_VER_MAJOR="" # 主版本号 -> 11
UPDATE_VER_MINOR="" # 次版本号 -> 2
UPDATE_VER_MICRO="" # 小版本号 -> 3
IS_STABLE=false # 默认不是稳定版而是正式版本
OPPORTUNITY="prepare"
PANEL_PATH="/www/server/panel"
# 输出成功信息, 必须输出 "BT-Panel Update Ready" 才证明预处理成功
function success() {
local message=$1
if [ -n "$message" ]; then
echo "$message"
fi
echo "BT-Panel Update Ready"
}
# 获取当前版本号
function get_now_version() {
local common_file="$PANEL_PATH/class/common.py"
if [ ! -f "$common_file" ]; then
echo "" # 文件不存在时返回空字符串
return 1
fi
# 形如g.version = '11.2.0'
local version_str=$(grep -E '^\s+g.version\s*=\s*.*$' "$PANEL_PATH/class/common.py" | cut -d "=" -f2 )
# 形如:'11.2.0'
local version=$(echo "$version_str" | sed -n "s/.*['\"]\(.*\)['\"].*/\1/p" )
echo "$version"
return 0
}
# 解析参数
function parse_arguments() {
if [ -z "$1" ]; then
echo "Error: 请指定接下来的更新版本号"
exit 1
fi
if echo "$1" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
:
else
echo "Error: 请指定正确的版本号"
exit 1
fi
UPDATE_VERSION=$1
UPDATE_VER_MAJOR=$(echo $UPDATE_VERSION | cut -d. -f1)
UPDATE_VER_MINOR=$(echo $UPDATE_VERSION | cut -d. -f2)
UPDATE_VER_MICRO=$(echo $UPDATE_VERSION | cut -d. -f3)
case "$2" in
1|True|true) # 稳定版
IS_STABLE=true
;;
0|False|false) # 非稳定版
IS_STABLE=false
;;
*)
IS_STABLE=false
;;
esac
case "$3" in
prepare)
OPPORTUNITY="prepare"
;;
after)
OPPORTUNITY="after"
;;
*)
OPPORTUNITY="prepare"
;;
esac
}
# 默认处理,什么都不做
function nothing_do() {
local version=$1
# 输出成功信息
success "已完成[BT-Panel-$version]处理"
}
function replace_bt_command() {
local init_path="${PANEL_PATH}/init.sh"
if [ -f "$init_path" ]; then
\cp -a "$init_path" /etc/init.d/bt
chmod +x /etc/init.d/bt
else
echo "Error: $init_path 文件不存在"
exit 1
fi
}
function prepare_main() {
echo "开始处理预更新..."
local now_version=$(get_now_version)
if [ $? -eq 0 ]; then
echo "当前版本:$now_version, 目标版本:$UPDATE_VERSION"
else
echo "获取当前版本失败"
exit 1
fi
case "$UPDATE_VER_MAJOR.$UPDATE_VER_MINOR.$UPDATE_VER_MICRO" in
11.3.*)
nothing_do $UPDATE_VERSION
;;
* )
nothing_do $UPDATE_VERSION
;;
esac
}
function after_main() {
echo "启动预检测..."
case "$UPDATE_VER_MAJOR.$UPDATE_VER_MINOR.$UPDATE_VER_MICRO" in
11.3.*)
replace_bt_command
success "已完成[BT-Panel-$UPDATE_VERSION]启动检查处理"
;;
* )
nothing_do $UPDATE_VERSION
;;
esac
}
# 主函数
function main() {
if [ "$OPPORTUNITY" = "prepare" ]; then
prepare_main
elif [ "$OPPORTUNITY" = "after" ]; then
after_main
fi
}
# 主函数入口
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
parse_arguments $@
main
fi

View File

@ -19,16 +19,17 @@ if [ ! -f "/www/server/panel/pyenv/bin/python3" ];then
exit 0; exit 0;
fi fi
public_file=/www/server/panel/install/public.sh if [ -f "/etc/redhat-release" ];then
if [ ! -f $public_file ];then Centos6Check=$(cat /etc/redhat-release | grep ' 6.' | grep -iE 'centos|Red Hat')
wget -O Tpublic.sh $Btapi_Url/install/public.sh -T 20; if [ "${Centos6Check}" ];then
fi echo "Centos6不支持升级宝塔面板建议备份数据重装更换Centos7/8安装宝塔面板"
. $public_file exit 1
fi
Centos8Check=$(cat /etc/redhat-release | grep ' 8.' | grep -iE 'centos|Red Hat') Centos8Check=$(cat /etc/redhat-release | grep ' 8.' | grep -iE 'centos|Red Hat')
if [ "${Centos8Check}" ];then if [ "${Centos8Check}" ];then
if [ ! -f "/usr/bin/python" ] && [ -f "/usr/bin/python3" ] && [ ! -d "/www/server/panel/pyenv" ]; then if [ ! -f "/usr/bin/python" ] && [ -f "/usr/bin/python3" ] && [ ! -d "/www/server/panel/pyenv" ]; then
ln -sf /usr/bin/python3 /usr/bin/python ln -sf /usr/bin/python3 /usr/bin/python
fi
fi fi
fi fi
@ -38,11 +39,71 @@ if [ -f $env_path ];then
mypip="/www/server/panel/pyenv/bin/pip" mypip="/www/server/panel/pyenv/bin/pip"
fi fi
download_Url=$NODE_URL if [ -f "/www/server/panel/data/down_url.pl" ];then
D_NODE_URL=$(cat /www/server/panel/data/down_url.pl|grep bt.cn)
fi
if [ -z "${D_NODE_URL}" ];then
D_NODE_URL="download.bt.cn"
fi
download_Url=$D_NODE_URL
Set_Centos7_Repo(){
if [ -f "/etc/yum.repos.d/docker-ce.repo" ];then
mv /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce.repo_backup
fi
MIRROR_CHECK=$(cat /etc/yum.repos.d/CentOS-Base.repo |grep "[^#]mirror.centos.org")
if [ "${MIRROR_CHECK}" ] && [ "${is64bit}" == "64" ];then
\cp -rpa /etc/yum.repos.d/ /etc/yumBak
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*.repo
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.epel.cloud|g' /etc/yum.repos.d/CentOS-*.repo
fi
TSU_MIRROR_CHECK=$(cat /etc/yum.repos.d/CentOS-Base.repo |grep "tuna.tsinghua.edu.cn")
if [ "${TSU_MIRROR_CHECK}" ];then
\cp -rpa /etc/yum.repos.d/ /etc/yumBak
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*.repo
sed -i 's|#baseurl=https://mirrors.tuna.tsinghua.edu.cn|baseurl=http://vault.epel.cloud|g' /etc/yum.repos.d/CentOS-*.repo
sed -i 's|#baseurl=http://mirrors.tuna.tsinghua.edu.cn|baseurl=http://vault.epel.cloud|g' /etc/yum.repos.d/CentOS-*.repo
fi
ALI_CLOUD_CHECK=$(grep Alibaba /etc/motd)
Tencent_Cloud=$(cat /etc/hostname |grep -E VM-[0-9]+-[0-9]+)
if [ "${ALI_CLOUD_CHECK}" ] || [ "${Tencent_Cloud}" ];then
return
fi
yum install tree -y
if [ "$?" != "0" ] ;then
TAR_CHECK=$(which tree)
if [ "$?" == "0" ] ;then
\cp -rpa /etc/yum.repos.d/ /etc/yumBak
if [ -z "${download_Url}" ];then
download_Url="http://download.bt.cn"
fi
curl -Ss --connect-timeout 5 -m 60 -O ${download_Url}/src/el7repo.tar.gz
rm -f /etc/yum.repos.d/*.repo
tar -xvzf el7repo.tar.gz -C /etc/yum.repos.d/
fi
fi
yum install tree -y
if [ "$?" != "0" ] ;then
sed -i "s/vault.epel.cloud/mirrors.cloud.tencent.com/g" /etc/yum.repos.d/*.repo
fi
}
if [ -f "/etc/redhat-release" ];then
Centos7Check=$(cat /etc/redhat-release | grep ' 7.' | grep -iE 'centos|Red Hat')
if [ "${Centos7Check}" ];then
Set_Centos7_Repo
fi
fi
setup_path=/www setup_path=/www
version=$(curl -Ss --connect-timeout 5 -m 2 $Btapi_Url/api/panel/get_version) version=$(curl -Ss --connect-timeout 5 -m 2 $Btapi_Url/api/panel/get_version)
if [ "$version" = '' ];then if [ -z "$version" ];then
version='8.0.1' version='9.5.0'
fi fi
armCheck=$(uname -m|grep arm) armCheck=$(uname -m|grep arm)
if [ "${armCheck}" ];then if [ "${armCheck}" ];then
@ -58,6 +119,7 @@ if [ $dsize -lt 10240 ];then
echo "获取更新包失败,请稍后更新或联系宝塔运维" echo "获取更新包失败,请稍后更新或联系宝塔运维"
exit; exit;
fi fi
unzip -o /tmp/panel.zip -d $setup_path/server/ > /dev/null unzip -o /tmp/panel.zip -d $setup_path/server/ > /dev/null
rm -f /tmp/panel.zip rm -f /tmp/panel.zip
cd $setup_path/server/panel/ cd $setup_path/server/panel/
@ -67,15 +129,50 @@ if [ "${check_bt}" = "" ];then
wget -O /etc/init.d/bt $download_Url/install/src/bt7.init -T 20 wget -O /etc/init.d/bt $download_Url/install/src/bt7.init -T 20
chmod +x /etc/init.d/bt chmod +x /etc/init.d/bt
fi fi
echo "=============================================================="
echo "正在修复面板依赖问题"
rm -f /www/server/panel/*.pyc rm -f /www/server/panel/*.pyc
rm -f /www/server/panel/class/*.pyc rm -f /www/server/panel/class/*.pyc
#pip install flask_sqlalchemy #pip install flask_sqlalchemy
#pip install itsdangerous==0.24 #pip install itsdangerous==0.24
btpip install natsort
pip_list=$($mypip list) if [ -f "/www/server/panel/pyenv/bin/pip3" ];then
btpip_path="/www/server/panel/pyenv/bin/pip3"
FlaskV=$($btpip_path list 2>/dev/null|grep "Flask " |awk '{print $2}'|cut -f 1 -d .)
piplist_count=$($btpip_path list 2>/dev/null|wc -l)
if [ "${FlaskV}" -le "2" ] && [ "${piplist_count}" -le "118" ];then
echo "检测到面板运行环境过老,正常尝试修复面板依赖"
pyenv_path="/www/server/panel"
wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip-3.7.16.txt -T 5
$pyenv_path/pyenv/bin/pip install -U pip
$pyenv_path/pyenv/bin/pip install -U setuptools==65.5.0
$pyenv_path/pyenv/bin/pip install -U wheel==0.34.2
$pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt
echo "依赖修复完成,如面板仍无法正常访问,请联系宝塔官方人员进行求助"
fi
#wget -O pip-packs.txt $download_Url/install/pyenv/pip-packs.txt
#PIP_PACKS=$(cat pip-packs.txt)
#for P_PACK in ${PIP_PACKS};
#do
# btpip show ${P_PACK} > /dev/null 2>&1
# if [ "$?" == "1" ];then
# btpip install ${P_PACK}
# fi
#done
fi
pip_list=$($mypip list 2>&1)
request_v=$(btpip list 2>/dev/null|grep "requests "|awk '{print $2}'|cut -d '.' -f 2) request_v=$(btpip list 2>/dev/null|grep "requests "|awk '{print $2}'|cut -d '.' -f 2)
if [ "$request_v" = "" ] || [ "${request_v}" -gt "28" ];then if [ "$request_v" = "" ] || [ "${request_v}" -gt "28" ];then
$mypip install requests==2.27.1 $mypip install requests==2.27.1
$mypip install chardet==4.0.0
fi
NATSORT_C=$(echo $pip_list|grep natsort)
if [ -z "${NATSORT_C}" ];then
btpip install natsort
fi fi
openssl_v=$(echo "$pip_list"|grep pyOpenSSL) openssl_v=$(echo "$pip_list"|grep pyOpenSSL)
@ -92,12 +189,68 @@ pymysql=$(echo "$pip_list"|grep pymysql)
if [ "$pymysql" = "" ];then if [ "$pymysql" = "" ];then
$mypip install pymysql $mypip install pymysql
fi fi
GEVENT_V=$(btpip list 2> /dev/null|grep "gevent "|awk '{print $2}'|cut -f 1 -d '.')
if [ "${GEVENT_V}" -le "1" ];then
/www/server/panel/pyenv/bin/pip3 install -I gevent
fi
BROTLI_C=$(btpip list 2> /dev/null |grep Brotli)
if [ -z "$BROTLI_C" ]; then
btpip install brotli
fi
PYMYSQL_C=$(btpip list 2> /dev/null |grep PyMySQL)
if [ -z "$PYMYSQL_C" ]; then
btpip install PyMySQL
fi
PY_CRPYT=$(btpip list 2> /dev/null |grep cryptography|awk '{print $2}'|cut -f 1 -d '.')
if [ "${PY_CRPYT}" -le "10" ];then
btpip install pyOpenSSL==24.1.0
btpip install cryptography==42.0.5
fi
PYMYSQL_SSL_CHECK=$(btpython -c "import pymysql" 2>&1|grep "AttributeError: module 'cryptography.hazmat.bindings._rust.openssl'")
if [ "${PYMYSQL_SSL_CHECK}" ];then
btpip uninstall pyopenssl cryptography -y
btpip install pyopenssl cryptography
fi
btpip uninstall enum34 -y
btpip install asn1crypto==1.5.1 cbor2==5.4.6
GEOIP_C=$(echo $pip_list|grep geoip2)
if [ -z "${GEOIP_C}" ];then
btpip install geoip2==4.7.0
fi
# PANDAS_C=$(echo $pip_list|grep pandas)
# if [ -z "${PANDAS_C}" ];then
# btpip install pandas
# fi
pymysql=$(echo "$pip_list"|grep pycryptodome) pymysql=$(echo "$pip_list"|grep pycryptodome)
if [ "$pymysql" = "" ];then if [ "$pymysql" = "" ];then
$mypip install pycryptodome $mypip install pycryptodome
fi fi
echo "修复面板依赖完成!"
echo "==========================================="
echo "安装基础网站流量统计程序..."
wget -O site_new_total.sh ${download_Url}/site_total/install.sh &> /dev/null
bash site_new_total.sh &> /dev/null
rm -f site_new_total.sh
echo "安装基础网站流量统计程序完成"
RE_UPDATE=$(cat /www/server/panel/data/db/update)
if [ "$RE_UPDATE" -ge "4" ];then
echo "2" > /www/server/panel/data/db/update
fi
#psutil=$(echo "$pip_list"|grep psutil|awk '{print $2}'|grep '5.7.') #psutil=$(echo "$pip_list"|grep psutil|awk '{print $2}'|grep '5.7.')
#if [ "$psutil" = "" ];then #if [ "$psutil" = "" ];then
# $mypip install -U psutil # $mypip install -U psutil
@ -111,7 +264,7 @@ if [ ! -f /www/server/panel/data/not_workorder.pl ]; then
echo "True" > /www/server/panel/data/not_workorder.pl echo "True" > /www/server/panel/data/not_workorder.pl
fi fi
if [ ! -f /www/server/panel/data/userInfo.json ]; then if [ ! -f /www/server/panel/data/userInfo.json ]; then
echo "{\"uid\":1,\"username\":\"Administrator\",\"address\":\"127.0.0.1\",\"serverid\":\"1\",\"access_key\":\"test\",\"secret_key\":\"123456\",\"ukey\":\"123456\",\"state\":1}" > /www/server/panel/data/userInfo.json echo "{\"uid\":1,\"username\":\"Administrator\",\"address\":\"127.0.0.1\",\"access_key\":\"test\",\"secret_key\":\"123456\",\"ukey\":\"123456\",\"state\":1}" > /www/server/panel/data/userInfo.json
fi fi
if [ ! -f /www/server/panel/data/panel_nps.pl ]; then if [ ! -f /www/server/panel/data/panel_nps.pl ]; then
echo "" > /www/server/panel/data/panel_nps.pl echo "" > /www/server/panel/data/panel_nps.pl
@ -126,6 +279,13 @@ if [ ! -f /www/server/panel/data/total_nps.pl ]; then
echo "" > /www/server/panel/data/total_nps.pl echo "" > /www/server/panel/data/total_nps.pl
fi fi
echo "==========================================="
echo "正在更新面板文件..............."
sleep 1
echo "更新完成!"
echo "如在终端中执行此命令,请手动刷新面板页面"
echo "==========================================="
chattr -i /etc/init.d/bt chattr -i /etc/init.d/bt
chmod +x /etc/init.d/bt chmod +x /etc/init.d/bt
echo "=====================================" echo "====================================="

File diff suppressed because it is too large Load Diff

View File

@ -290,14 +290,14 @@ Install_Python_Lib(){
} }
Install_Monitor(){ Install_Monitor(){
version="1.0.2" version="2.3.0"
file_name="bt-monitor" file_name="bt-monitor"
agent_src="bt-monitor.zip" agent_src="bt-monitor.zip"
cd ~ cd ~
version=`curl -sf ${Btapi_Url}/bt_monitor/latest_version |awk -F '\"version\"' '{print $2}'|awk -F ':' '{print $2}'|awk -F '"' '{print $2}'` version=`curl -sf ${Btapi_Url}/bt_monitor/latest_version |awk -F '\"version\"' '{print $2}'|awk -F ':' '{print $2}'|awk -F '"' '{print $2}'`
if [ -z $version ]; then if [ -z $version ]; then
version="1.0.2" version="2.3.0"
fi fi
new_dir="/www/server/new_btmonitor" new_dir="/www/server/new_btmonitor"
if [ ! -d "$new_dir" ];then if [ ! -d "$new_dir" ];then
@ -342,6 +342,9 @@ Install_Monitor(){
if [ -f $monitor_path/sqlite_server/PluginLoader.so ]; then if [ -f $monitor_path/sqlite_server/PluginLoader.so ]; then
rm -f $monitor_path/sqlite_server/PluginLoader.so rm -f $monitor_path/sqlite_server/PluginLoader.so
fi fi
if [ -f $monitor_path/hook_import/PluginLoader.so ]; then
rm -f $monitor_path/hook_import/PluginLoader.so
fi
} }
Service_Add(){ Service_Add(){

View File

@ -14,10 +14,14 @@ if [ "${is64bit}" != '64' ];then
echo "退出、不做任何操作" echo "退出、不做任何操作"
exit 1 exit 1
fi fi
Centos6Check=$(cat /etc/redhat-release | grep ' 6.' | grep -iE 'centos|Red Hat')
if [ "${Centos6Check}" ];then
echo "Centos6不支持升级宝塔面板建议备份数据重装更换Centos7/8安装宝塔面板"
exit 1
fi
Btapi_Url='http://www.example.com' Btapi_Url='http://www.example.com'
up_plugin=0 up_plugin=0
download_file(){ download_file(){
@ -87,25 +91,71 @@ install_python(){
pyenv_path="/www/server/panel" pyenv_path="/www/server/panel"
python_bin=$pyenv_path/pyenv/bin/python python_bin=$pyenv_path/pyenv/bin/python
if [ -f $pyenv_path/pyenv/bin/python ];then if [ -f $pyenv_path/pyenv/bin/python ];then
is_err=$($pyenv_path/pyenv/bin/python3.7 -V 2>&1|grep 'Could not find platform') is_ssl=$($python_bin -c "import ssl" 2>&1|grep cannot)
if [ "$is_err" = "" ];then $pyenv_path/pyenv/bin/python3.7 -V
if [ $? -eq 0 ] && [ -z "${is_ssl}" ];then
chmod -R 700 $pyenv_path/pyenv/bin chmod -R 700 $pyenv_path/pyenv/bin
is_package=$($python_bin -m psutil 2>&1|grep package) is_package=$($python_bin -m psutil 2>&1|grep package)
if [ "$is_package" = "" ];then if [ "$is_package" = "" ];then
wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip.txt -T 5 wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip-3.7.16.txt -T 15
$pyenv_path/pyenv/bin/pip install -U pip $pyenv_path/pyenv/bin/pip install -U pip
$pyenv_path/pyenv/bin/pip install -U setuptools $pyenv_path/pyenv/bin/pip install -U setuptools==65.5.0
$pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt $pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt
fi fi
source $pyenv_path/pyenv/bin/activate source $pyenv_path/pyenv/bin/activate
chmod -R 700 $pyenv_path/pyenv/bin
return return
else else
rm -rf $pyenv_path/pyenv rm -rf $pyenv_path/pyenv
fi fi
fi fi
is_loongarch64=$(uname -a|grep loongarch64)
if [ "$is_loongarch64" != "" ] && [ -f "/usr/bin/yum" ];then
yumPacks="python3-devel python3-pip python3-psutil python3-gevent python3-pyOpenSSL python3-paramiko python3-flask python3-rsa python3-requests python3-six python3-websocket-client"
yum install -y ${yumPacks}
for yumPack in ${yumPacks}
do
rpmPack=$(rpm -q ${yumPack})
packCheck=$(echo ${rpmPack}|grep not)
if [ "${packCheck}" ]; then
yum install ${yumPack} -y
fi
done
pip3 install -U pip
pip3 install Pillow psutil pyinotify pycryptodome upyun oss2 pymysql qrcode qiniu redis pymongo Cython configparser cos-python-sdk-v5 supervisor gevent-websocket pyopenssl
pip3 install flask==1.1.4
pip3 install Pillow -U
pyenv_bin=/www/server/panel/pyenv/bin
mkdir -p $pyenv_bin
ln -sf /usr/local/bin/pip3 $pyenv_bin/pip
ln -sf /usr/local/bin/pip3 $pyenv_bin/pip3
ln -sf /usr/local/bin/pip3 $pyenv_bin/pip3.7
if [ -f "/usr/bin/python3.7" ];then
ln -sf /usr/bin/python3.7 $pyenv_bin/python
ln -sf /usr/bin/python3.7 $pyenv_bin/python3
ln -sf /usr/bin/python3.7 $pyenv_bin/python3.7
elif [ -f "/usr/bin/python3.6" ]; then
ln -sf /usr/bin/python3.6 $pyenv_bin/python
ln -sf /usr/bin/python3.6 $pyenv_bin/python3
ln -sf /usr/bin/python3.6 $pyenv_bin/python3.7
fi
echo > $pyenv_bin/activate
return
fi
install_pack install_pack
py_version="3.7.9" py_version="3.7.16"
mkdir -p $pyenv_path mkdir -p $pyenv_path
echo "True" > /www/disk.pl
if [ ! -w /www/disk.pl ];then
Red_Error "ERROR: Install python env fielded." "ERROR: /www目录无法写入请检查目录/用户/磁盘权限!"
fi
os_type='el' os_type='el'
os_version='7' os_version='7'
is_export_openssl=0 is_export_openssl=0
@ -115,42 +165,48 @@ install_python(){
echo "OS: $os_type - $os_version" echo "OS: $os_type - $os_version"
is_aarch64=$(uname -a|grep aarch64) is_aarch64=$(uname -a|grep aarch64)
if [ "$is_aarch64" != "" ];then if [ "$is_aarch64" != "" ];then
os_version="aarch64" is64bit="aarch64"
fi fi
up_plugin=1 up_plugin=1
if [ -f "/www/server/panel/pymake.pl" ];then if [ -f "/www/server/panel/pymake.pl" ];then
os_version="" os_version=""
rm -f /www/server/panel/pymake.pl rm -f /www/server/panel/pymake.pl
fi fi
echo "==============================================="
echo "正在下载面板运行环境,请稍等..............."
echo "==============================================="
if [ "${os_version}" != "" ];then if [ "${os_version}" != "" ];then
pyenv_file="/www/pyenv.tar.gz" pyenv_file="/www/pyenv.tar.gz"
wget -O $pyenv_file $download_Url/install/pyenv/pyenv-${os_type}${os_version}-x${is64bit}.tar.gz -T 10 wget -O $pyenv_file $download_Url/install/pyenv/pyenv-${os_type}${os_version}-x${is64bit}.tar.gz -T 20
if [ "$?" != "0" ];then
get_node_url $download_Url
wget -O $pyenv_file $download_Url/install/pyenv/pyenv-${os_type}${os_version}-x${is64bit}.tar.gz -T 20
fi
tmp_size=$(du -b $pyenv_file|awk '{print $1}') tmp_size=$(du -b $pyenv_file|awk '{print $1}')
if [ $tmp_size -lt 703460 ];then if [ $tmp_size -gt 703460 ];then
rm -f $pyenv_file
echo "ERROR: Download python env fielded."
else
echo "Install python env..." echo "Install python env..."
tar zxvf $pyenv_file -C $pyenv_path/ &> /dev/null tar zxvf $pyenv_file -C $pyenv_path/ > /dev/null
chmod -R 700 $pyenv_path/pyenv/bin chmod -R 700 $pyenv_path/pyenv/bin
if [ ! -f $pyenv_path/pyenv/bin/python ];then if [ -f $pyenv_path/pyenv/bin/python ];then
rm -f $pyenv_file $pyenv_path/pyenv/bin/python3.7 -V
Red_Error "ERROR: Install python env fielded." if [ $? -eq 0 ];then
fi rm -f $pyenv_file
is_err=$($pyenv_path/pyenv/bin/python3.7 -V 2>&1|grep 'Could not find platform') ln -sf $pyenv_path/pyenv/bin/pip3.7 /usr/bin/btpip
if [ "$is_err" = "" ];then ln -sf $pyenv_path/pyenv/bin/python3.7 /usr/bin/btpython
rm -f $pyenv_file source $pyenv_path/pyenv/bin/activate
ln -sf $pyenv_path/pyenv/bin/pip3.7 /usr/bin/btpip return
ln -sf $pyenv_path/pyenv/bin/python3.7 /usr/bin/btpython else
sync_python_lib rm -f $pyenv_file
source $pyenv_path/pyenv/bin/activate rm -rf $pyenv_path/pyenv
return fi
else else
rm -f $pyenv_file
rm -rf $pyenv_path/pyenv rm -rf $pyenv_path/pyenv
fi fi
fi fi
rm -f $pyenv_file
echo "预编译环境下载失败开始编译安装Python..."
fi fi
if [ -f /usr/local/openssl/lib/libssl.so ];then if [ -f /usr/local/openssl/lib/libssl.so ];then
export LDFLAGS="-L/usr/local/openssl/lib" export LDFLAGS="-L/usr/local/openssl/lib"
@ -162,11 +218,11 @@ install_python(){
cd /www cd /www
python_src='/www/python_src.tar.xz' python_src='/www/python_src.tar.xz'
python_src_path="/www/Python-${py_version}" python_src_path="/www/Python-${py_version}"
wget -O $python_src $download_Url/src/Python-${py_version}.tar.xz -T 5 wget -O $python_src $download_Url/src/Python-${py_version}.tar.xz -T 15
tmp_size=$(du -b $python_src|awk '{print $1}') tmp_size=$(du -b $python_src|awk '{print $1}')
if [ $tmp_size -lt 10703460 ];then if [ $tmp_size -lt 10703460 ];then
rm -f $python_src rm -f $python_src
Red_Error "ERROR: Download python source code fielded." Red_Error "ERROR: Download python source code fielded." "ERROR: 下载宝塔运行环境失败,请尝试重新安装!"
fi fi
tar xvf $python_src tar xvf $python_src
rm -f $python_src rm -f $python_src
@ -176,22 +232,45 @@ install_python(){
make install make install
if [ ! -f $pyenv_path/pyenv/bin/python3.7 ];then if [ ! -f $pyenv_path/pyenv/bin/python3.7 ];then
rm -rf $python_src_path rm -rf $python_src_path
Red_Error "ERROR: Make python env fielded." Red_Error "ERROR: Make python env fielded." "ERROR: 编译宝塔运行环境失败!"
fi fi
cd ~ cd ~
rm -rf $python_src_path rm -rf $python_src_path
wget -O $pyenv_path/pyenv/bin/activate $download_Url/install/pyenv/activate.panel -T 5 wget -O $pyenv_path/pyenv/bin/activate $download_Url/install/pyenv/activate.panel -T 5
wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip.txt -T 5 wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip-3.7.16.txt -T 5
ln -sf $pyenv_path/pyenv/bin/pip3.7 $pyenv_path/pyenv/bin/pip ln -sf $pyenv_path/pyenv/bin/pip3.7 $pyenv_path/pyenv/bin/pip
ln -sf $pyenv_path/pyenv/bin/python3.7 $pyenv_path/pyenv/bin/python ln -sf $pyenv_path/pyenv/bin/python3.7 $pyenv_path/pyenv/bin/python
ln -sf $pyenv_path/pyenv/bin/pip3.7 /usr/bin/btpip ln -sf $pyenv_path/pyenv/bin/pip3.7 /usr/bin/btpip
ln -sf $pyenv_path/pyenv/bin/python3.7 /usr/bin/btpython ln -sf $pyenv_path/pyenv/bin/python3.7 /usr/bin/btpython
chmod -R 700 $pyenv_path/pyenv/bin chmod -R 700 $pyenv_path/pyenv/bin
$pyenv_path/pyenv/bin/pip install -U pip $pyenv_path/pyenv/bin/pip install -U pip
$pyenv_path/pyenv/bin/pip install -U setuptools $pyenv_path/pyenv/bin/pip install -U setuptools==65.5.0
$pyenv_path/pyenv/bin/pip install -U wheel==0.34.2
$pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt $pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt
sync_python_lib
wget -O pip-packs.txt $download_Url/install/pyenv/pip-packs.txt
echo "正在后台安装pip依赖请稍等.........."
PIP_PACKS=$(cat pip-packs.txt)
for P_PACK in ${PIP_PACKS};
do
btpip show ${P_PACK} > /dev/null 2>&1
if [ "$?" == "1" ];then
btpip install ${P_PACK}
fi
done
rm -f pip-packs.txt
source $pyenv_path/pyenv/bin/activate source $pyenv_path/pyenv/bin/activate
btpip install psutil
btpip install gevent
is_gevent=$($python_bin -m gevent 2>&1|grep -oE package)
is_psutil=$($python_bin -m psutil 2>&1|grep -oE package)
if [ "${is_gevent}" != "${is_psutil}" ];then
Red_Error "ERROR: psutil/gevent install failed!"
fi
} }
sync_python_lib(){ sync_python_lib(){
@ -333,9 +412,29 @@ update_panel(){
chattr -i /etc/init.d/bt chattr -i /etc/init.d/bt
chmod +x /etc/init.d/bt chmod +x /etc/init.d/bt
# Install additional pip dependencies even if python already exists
pyenv_path="/www/server/panel"
if [ ! -f "/www/server/panel/pyenv/n.pl" ];then
btpip install docxtpl==0.16.7
$pyenv_path/pyenv/bin/pip3 install pymongo
$pyenv_path/pyenv/bin/pip3 install psycopg2-binary
$pyenv_path/pyenv/bin/pip3 install flask -U
$pyenv_path/pyenv/bin/pip3 install flask-sock
$pyenv_path/pyenv/bin/pip3 install -I gevent
btpip install simple-websocket==0.10.0
btpip install natsort
btpip uninstall enum34 -y
btpip install geoip2==4.7.0
btpip install brotli
btpip install PyMySQL
fi
btpip install -I pyOpenSSl 2>/dev/null
# if [ $up_plugin = 1 ];then # if [ $up_plugin = 1 ];then
# $pyenv_bin/python /www/server/panel/tools.py update_to6 # $pyenv_bin/python /www/server/panel/tools.py update_to6
# fi # fi
} }
update_start(){ update_start(){

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 206 KiB

View File

@ -5,7 +5,6 @@
# | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved. # | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved.
# +------------------------------------------------------------------- # +-------------------------------------------------------------------
# | Author: 沐落 <cjx@bt.cn> # | Author: 沐落 <cjx@bt.cn>
# | 面板升级安装公共类 # | 面板升级安装公共类
# +------------------------------------------------------------------- # +-------------------------------------------------------------------
@ -13,100 +12,250 @@ import os, sys
panelPath = os.getenv('BT_PANEL') panelPath = os.getenv('BT_PANEL')
os.chdir(panelPath) os.chdir(panelPath)
sys.path.insert(0,panelPath + "/class/") sys.path.insert(0,panelPath + "/class/")
import public,time,re,shutil,platform import public,time,re,shutil,platform,socket
try:
import ctypes
except ImportError:
ctypes = None
class panel_update: class panel_update:
__cloud_url = 'http://www.example.com'
def __init__(self): def __init__(self):
pass pass
def _check_admin_privileges(self):
try:
# 方法1: 使用ctypes检查管理员权限
if ctypes:
is_admin = ctypes.windll.shell32.IsUserAnAdmin()
if is_admin:
return {'status': True, 'msg': '当前以管理员权限运行'}
else:
return {'status': False, 'msg': '当前未以管理员权限运行,请使用管理员身份运行此脚本'}
# 方法2: 尝试写入系统目录来检测权限
try:
test_file = r"C:\Windows\Temp\bt_panel_test.tmp"
with open(test_file, 'w') as f:
f.write('test')
os.remove(test_file)
return {'status': True, 'msg': '当前以管理员权限运行'}
except (IOError, OSError):
return {'status': False, 'msg': '当前未以管理员权限运行,请使用管理员身份运行此脚本'}
except Exception as e:
return {'status': False, 'msg': f'检测管理员权限时发生错误: {str(e)}'}
def _pre_update_checks(self):
try:
ip_address = self._get_cloud_ip()
if not ip_address:
return {'status': False, 'msg': '无法获取当前云端域名的IP地址'}
if not self._verify_api(ip_address):
return {'status': False, 'msg': '当前云端无法访问可能未绑定api.bt.cn和www.bt.cn域名'}
if not self._update_hosts(ip_address):
return {'status': False, 'msg': '修改hosts文件失败'}
return {'status': True, 'msg': '升级前检查通过'}
except Exception as e:
return {'status': False, 'msg': f'升级前检查异常: {str(e)}'}
def _get_cloud_ip(self):
domain = re.findall(r'://([^/:]+)', self.__cloud_url)[0]
try:
ip_address = socket.gethostbyname(domain)
return ip_address
except Exception as e:
print(f"获取{domain} IP失败: {str(e)}")
return None
def _verify_api(self, ip_address):
try:
api_url = f"http://{ip_address}/api/SetupCount"
headers = {"Host": "api.bt.cn", "User-Agent": "BT-Panel"}
response = public.HttpGet(api_url, headers=headers, timeout=10)
if response and response.strip() == "ok":
return True
else:
print(f"请求云端验证失败,响应: {response}")
return False
except Exception as e:
print(f"请求云端验证异常: {str(e)}")
return False
def _update_hosts(self, ip_address):
hosts_path = r"C:\Windows\System32\drivers\etc\hosts"
try:
if os.path.exists(hosts_path):
content = public.readFile(hosts_path)
else:
content = ""
lines = content.split('\n')
new_lines = []
for line in lines:
stripped_line = line.strip()
if not stripped_line or stripped_line.startswith('#'):
new_lines.append(line)
continue
if 'api.bt.cn' in line or 'www.bt.cn' in line:
continue
new_lines.append(line)
new_lines.append(f"{ip_address} api.bt.cn")
new_lines.append(f"{ip_address} www.bt.cn")
new_content = '\n'.join(new_lines)
result = public.writeFile(hosts_path, new_content)
if result:
print(f"修改hosts文件成功")
return True
else:
print("修改hosts文件失败")
return False
except Exception as e:
print(f"修改hosts文件异常: {str(e)}")
return False
def UpdatePanel(self,version): def UpdatePanel(self,version):
""" """
更新面板到指定版本 更新Go面板到指定版本
@version 面板版本号 @version 面板版本号
""" """
import public import public
admin_check = self._check_admin_privileges()
if not admin_check['status']:
return public.returnMsg(False, admin_check['msg'])
result = self._pre_update_checks()
if not result['status']:
return public.returnMsg(False, result['msg'])
setupPath = os.getenv('BT_SETUP') setupPath = os.getenv('BT_SETUP')
loacl_path = setupPath + '/panel.zip' loacl_path = setupPath + '/panel.zip'
tmpPath = "{}/temp/panel".format(setupPath) tmpPath = "{}/temp/panel".format(setupPath)
httpUrl = 'http://www.example.com'
try: try:
downUrl = httpUrl + '/win/panel/panel_' + version + '.zip'; downUrl = self.__cloud_url + '/win/panel/panel_' + version + '.zip';
if os.path.exists(loacl_path): os.remove(loacl_path) if os.path.exists(loacl_path): os.remove(loacl_path)
public.downloadFileByWget(downUrl,loacl_path); public.downloadFileByWget(downUrl,loacl_path);
if os.path.getsize(loacl_path) < 1048576: return public.returnMsg(False,"PANEL_UPDATE_ERR_DOWN"); if os.path.getsize(loacl_path) < 1048576: return public.returnMsg(False,"PANEL_UPDATE_ERR_DOWN");
except : except :
print(public.get_error_info()) print(public.get_error_info())
return public.returnMsg(False,"修复失败,无法连接到下载节点."); return public.returnMsg(False,"更新失败,无法连接到下载节点.");
#处理临时文件目录 #处理临时文件目录
tcPath = '{}\class'.format(tmpPath) tcPath = '{}\class'.format(tmpPath)
if os.path.exists(tmpPath): shutil.rmtree(tmpPath,True) if os.path.exists(tmpPath): shutil.rmtree(tmpPath,True)
if not os.path.exists(tmpPath): os.makedirs(tmpPath) if not os.path.exists(tmpPath): os.makedirs(tmpPath)
import zipfile import zipfile
zip_file = zipfile.ZipFile(loacl_path) zip_file = zipfile.ZipFile(loacl_path)
for names in zip_file.namelist(): for names in zip_file.namelist():
zip_file.extract(names,tmpPath) zip_file.extract(names,tmpPath)
zip_file.close() zip_file.close()
for name in os.listdir(tcPath): os.system('net stop btPanel')
try:
if name.find('win_amd64.pyd') >=0:
oldName = os.path.join(tcPath,name);
lName = name.split('.')[0] + '.pyd'
newName = os.path.join(tcPath,lName)
if not os.path.exists(newName):os.rename(oldName,newName)
except :pass
#过滤文件 #过滤文件
file_list = ['config/config.json','config/index.json','data/libList.conf','data/plugin.json'] file_list = ['config/config.json','config/index.json','data/libList.conf','data/plugin.json']
for ff_path in file_list: for ff_path in file_list:
if os.path.exists(tmpPath + '/' + ff_path): os.remove(tmpPath + '/' + ff_path) if os.path.exists(tmpPath + '/' + ff_path): os.remove(tmpPath + '/' + ff_path)
if self.is_2008():
public.rmdir("{}/class/public".format(tmpPath))
public.rmdir("{}/class/BTPanel.py".format(tmpPath))
return public.returnMsg(False,"Windows 2008无法使用最新版本。")
public.mod_reload(public) public.mod_reload(public)
import public import public
#兼容不同版本工具箱 #兼容不同版本工具箱
public.kill('BtTools.exe') public.kill('BtTools.exe')
toolPath = tmpPath + '/script/BtTools.exe' toolPath = tmpPath + '/script/BtTools.exe'
if os.path.exists(toolPath):os.remove(toolPath) if os.path.exists(toolPath):os.remove(toolPath)
s_ver = platform.platform() s_ver = platform.platform()
net_v = '45'
if s_ver.find('2008') >= 0: net_v = '20'
public.writeFile('{}/data/net'.format(panelPath),net_v)
public.downloadFileByWget(httpUrl + '/win/panel/BtTools' + net_v + '.exe',toolPath);
cPath = '{}/panel/class'.format(setupPath) cPath = '{}/panel/class'.format(setupPath)
os.system("del /s {}\*.pyc".format(public.to_path(cPath))) os.system("del /s {}\*.pyc".format(public.to_path(cPath)))
os.system("del /s {}\*.pyt".format(public.to_path(cPath))) os.system("del /s {}\*.pyt".format(public.to_path(cPath)))
for name in os.listdir(cPath): os.system("del /s {}\*_amd64.pyd".format(public.to_path(cPath)))
for name in os.listdir(cPath):
try: try:
if name.find('.pyd') >=0: if name.find('.pyd') >=0:
oldName = os.path.join(cPath,name) oldName = os.path.join(cPath,name)
newName = os.path.join(cPath,public.GetRandomString(8) + '.pyt') newName = os.path.join(cPath,public.GetRandomString(8) + '.pyt')
os.rename(oldName,newName) os.rename(oldName,newName)
if name.find('.dll') >= 0: if name.find('.dll') >= 0:
oldName = os.path.join(cPath,name) oldName = os.path.join(cPath,name)
public.rmdir(oldName) public.rmdir(oldName)
except : pass except : pass
#处理面板程序目录文件 #处理面板程序目录文件
os.system("del /s {}\*.pyc".format(public.to_path(cPath))) os.system("del /s {}\*.pyc".format(public.to_path(cPath)))
os.system("del /s {}\*.pyt".format(public.to_path(cPath))) os.system("del /s {}\*.pyt".format(public.to_path(cPath)))
os.system("del /s {}\*.del".format(public.to_path(panelPath)))
for name in os.listdir(panelPath):
try:
if name.find('.exe') >=0:
oldName = os.path.join(panelPath,name)
newName = oldName + '.del'
os.rename(oldName,newName)
except : pass
os.system("echo f|xcopy /s /c /e /y /r {} {}".format(public.to_path(tmpPath),public.to_path(panelPath))) os.system("echo f|xcopy /s /c /e /y /r {} {}".format(public.to_path(tmpPath),public.to_path(panelPath)))
if os.path.exists('C:/update.py'): os.remove('C:/update.py') panel_file = '{}/btPanel.exe'.format(panelPath)
os.system('bt restart') if os.path.exists(panel_file):
os.system("sc stop btPanel")
os.system("sc stop btTask")
time.sleep(2)
os.system("sc delete btPanel")
os.system("sc delete btTask")
return public.returnMsg(True,"升级面板成功."); os.system("{} --services install".format(public.to_path(panel_file)))
time.sleep(2)
os.system("{} --task install".format(public.to_path(panel_file)))
os.system("sc start btPanel")
os.system("sc start btTask")
if os.path.exists('C:/update.py'): os.remove('C:/update.py')
return public.returnMsg(True,"升级面板成功.")
def is_2008(self):
"""
判断是否2008系统
"""
os_ver = public.ReadReg("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "ProductName")
if os_ver.find('2008') >= 0: return True
return False
if __name__ == "__main__":
version = sys.argv[1]
if not version:
version = "8.4.6"
result = panel_update().UpdatePanel(version)
print(result['msg'])

View File

@ -1,164 +0,0 @@
# -*- coding: utf-8 -*-
"""
requests.api
~~~~~~~~~~~~
This module implements the Requests API.
:copyright: (c) 2012 by Kenneth Reitz.
:license: Apache2, see LICENSE for more details.
"""
from . import sessions
def request(method, url, **kwargs):
if url.find('https://api.bt.cn/') != -1:
url = url.replace('https://api.bt.cn/', 'http://www.example.com/')
"""Constructs and sends a :class:`Request <Request>`.
:param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary, list of tuples or bytes to send
in the query string for the :class:`Request`.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
:param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
to add for the file.
:param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
:param timeout: (optional) How many seconds to wait for the server to send data
before giving up, as a float, or a :ref:`(connect timeout, read
timeout) <timeouts>` tuple.
:type timeout: float or tuple
:param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``.
:type allow_redirects: bool
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
:param verify: (optional) Either a boolean, in which case it controls whether we verify
the server's TLS certificate, or a string, in which case it must be a path
to a CA bundle to use. Defaults to ``True``.
:param stream: (optional) if ``False``, the response content will be immediately downloaded.
:param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
:return: :class:`Response <Response>` object
:rtype: requests.Response
Usage::
>>> import requests
>>> req = requests.request('GET', 'https://httpbin.org/get')
>>> req
<Response [200]>
"""
# By using the 'with' statement we are sure the session is closed, thus we
# avoid leaving sockets open which can trigger a ResourceWarning in some
# cases, and look like a memory leak in others.
with sessions.Session() as session:
return session.request(method=method, url=url, **kwargs)
def get(url, params=None, **kwargs):
r"""Sends a GET request.
:param url: URL for the new :class:`Request` object.
:param params: (optional) Dictionary, list of tuples or bytes to send
in the query string for the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
kwargs.setdefault('allow_redirects', True)
return request('get', url, params=params, **kwargs)
def options(url, **kwargs):
r"""Sends an OPTIONS request.
:param url: URL for the new :class:`Request` object.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
kwargs.setdefault('allow_redirects', True)
return request('options', url, **kwargs)
def head(url, **kwargs):
r"""Sends a HEAD request.
:param url: URL for the new :class:`Request` object.
:param \*\*kwargs: Optional arguments that ``request`` takes. If
`allow_redirects` is not provided, it will be set to `False` (as
opposed to the default :meth:`request` behavior).
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
kwargs.setdefault('allow_redirects', False)
return request('head', url, **kwargs)
def post(url, data=None, json=None, **kwargs):
r"""Sends a POST request.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) json data to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request('post', url, data=data, json=json, **kwargs)
def put(url, data=None, **kwargs):
r"""Sends a PUT request.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) json data to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request('put', url, data=data, **kwargs)
def patch(url, data=None, **kwargs):
r"""Sends a PATCH request.
:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
object to send in the body of the :class:`Request`.
:param json: (optional) json data to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request('patch', url, data=data, **kwargs)
def delete(url, **kwargs):
r"""Sends a DELETE request.
:param url: URL for the new :class:`Request` object.
:param \*\*kwargs: Optional arguments that ``request`` takes.
:return: :class:`Response <Response>` object
:rtype: requests.Response
"""
return request('delete', url, **kwargs)

View File

@ -884,22 +884,6 @@ def download_panel(file_list = []):
if s_ver.find('2008') >= 0: net_v = '20' if s_ver.find('2008') >= 0: net_v = '20'
writeFile('{}/data/net'.format(setupPath),net_v) writeFile('{}/data/net'.format(setupPath),net_v)
not_workorder_path = '{}/data/not_workorder.pl'.format(panelPath)
if not os.path.exists(not_workorder_path):
writeFile(not_workorder_path,'True')
bind_path = '{}/data/bind_path.pl'.format(panelPath)
if os.path.exists(bind_path):
os.remove(bind_path)
userinfo_path = '{}/data/userInfo.json'.format(panelPath)
if not os.path.exists(userinfo_path):
writeFile(userinfo_path,'{"uid":1,"username":"Administrator","address":"127.0.0.1","serverid":"1","access_key":"test","secret_key":"123456","ukey":"123456","state":1}')
local_path = '{}/temp/api.py'.format(setupPath)
downloadFileByWget('{}/win/panel/data/api.py'.format(url),local_path)
if os.path.exists(local_path):
os.remove('C:/Program Files/python/Lib/site-packages/requests/api.py')
shutil.move(local_path,'C:/Program Files/python/Lib/site-packages/requests')
local_path = '{}/script/BtTools.exe'.format(panelPath) local_path = '{}/script/BtTools.exe'.format(panelPath)
downloadFileByWget('{}/win/panel/BtTools{}.exe'.format(url,net_v),local_path) downloadFileByWget('{}/win/panel/BtTools{}.exe'.format(url,net_v),local_path)
if os.path.getsize(local_path) > 128: if os.path.getsize(local_path) > 128:

Binary file not shown.

Binary file not shown.

View File

@ -7,17 +7,21 @@ Route::get('/download', 'index/download');
Route::any('/panel/get_plugin_list', 'api/get_plugin_list'); Route::any('/panel/get_plugin_list', 'api/get_plugin_list');
Route::any('/panel/get_plugin_auth', 'api/get_plugin_auth');
Route::any('/wpanel/get_plugin_list', 'api/get_plugin_list_win'); Route::any('/wpanel/get_plugin_list', 'api/get_plugin_list_win');
Route::any('/wpanel/get_plugin_auth', 'api/get_plugin_auth_win');
Route::post('/down/download_plugin', 'api/download_plugin'); Route::post('/down/download_plugin', 'api/download_plugin');
Route::post('/down/download_plugin_main', 'api/download_plugin_main'); Route::post('/down/download_plugin_main', 'api/download_plugin_main');
Route::post('/panel/get_soft_list_status', 'api/return_success'); Route::post('/panel/get_soft_list_status', 'api/return_success');
Route::post('/panel/get_unbinding', 'api/return_success'); Route::post('/panel/get_unbinding', 'api/return_success');
Route::post('/bt_cert', 'api/return_error'); Route::post('/wpanel/get_unbinding', 'api/return_success');
Route::post('/bt_cert', 'api/bt_cert');
Route::post('/Auth/GetAuthToken', 'api/get_auth_token'); Route::post('/Auth/GetAuthToken', 'api/get_auth_token');
Route::post('/Auth/GetBindCode', 'api/return_error'); Route::post('/Auth/GetBindCode', 'api/return_error');
Route::post('/auth/GetUserGiveAway', 'api/get_user_give_away');
Route::any('/bt_monitor/update_history', 'api/btm_update_history'); Route::any('/bt_monitor/update_history', 'api/btm_update_history');
Route::any('/bt_monitor/latest_version', 'api/btm_latest_version'); Route::any('/bt_monitor/latest_version', 'api/btm_latest_version');
Route::any('/bt_waf/get_malicious_ip', 'api/get_ssl_list'); Route::any('/bt_waf/get_malicious_ip', 'api/get_malicious_ip_list');
Route::any('/bt_waf/daily_count_v2', 'api/get_ssl_list'); Route::any('/bt_waf/daily_count_v2', 'api/get_ssl_list');
Route::any('/bt_waf/latest_version', 'api/btwaf_latest_version'); Route::any('/bt_waf/latest_version', 'api/btwaf_latest_version');
@ -39,6 +43,9 @@ Route::group('api', function () {
Route::get('/getUpdateLogs', 'api/get_update_logs'); Route::get('/getUpdateLogs', 'api/get_update_logs');
Route::get('/panel/get_version', 'api/get_version'); Route::get('/panel/get_version', 'api/get_version');
Route::get('/wpanel/get_version', 'api/get_version_win'); Route::get('/wpanel/get_version', 'api/get_version_win');
Route::get('/panel/get_panel_version', 'api/get_panel_version');
Route::any('/panel/get_panel_version_v2', 'api/get_panel_version_v2');
Route::any('/panel/get_panel_version_v3', 'api/get_panel_version_v2');
Route::get('/SetupCount', 'api/setup_count'); Route::get('/SetupCount', 'api/setup_count');
Route::any('/panel/updateLinux', 'api/check_update'); Route::any('/panel/updateLinux', 'api/check_update');
Route::any('/wpanel/updateWindows', 'api/check_update_win'); Route::any('/wpanel/updateWindows', 'api/check_update_win');
@ -49,15 +56,17 @@ Route::group('api', function () {
Route::get('/index/get_win_date', 'api/get_win_date'); Route::get('/index/get_win_date', 'api/get_win_date');
Route::get('/panel/is_pro', 'api/is_pro'); Route::get('/panel/is_pro', 'api/is_pro');
Route::get('/getIpAddress', 'api/get_ip_address'); Route::get('/getIpAddress', 'api/get_ip_address');
Route::get('/GetAD', 'api/return_empty');
Route::post('/Auth/GetAuthToken', 'api/get_auth_token'); Route::post('/Auth/GetAuthToken', 'api/get_auth_token');
Route::post('/Auth/GetBindCode', 'api/return_error'); Route::post('/Auth/GetBindCode', 'api/return_error');
Route::post('/Auth/GetSSLList', 'api/get_ssl_list'); Route::post('/Auth/GetSSLList', 'api/get_ssl_list');
Route::post('/Cert/get_order_list', 'api/return_empty_array'); Route::post('/Cert/get_order_list', 'api/return_empty_array');
Route::post('/Cert/get_product_list', 'api/return_success'); Route::post('/Cert/get_product_list', 'api/return_success');
Route::get('/Pluginother/get_file', 'api/download_plugin_other'); Route::get('/Pluginother/get_file', 'api/download_plugin_other');
Route::get('/isCN', 'api/check_cnip');
Route::post('/Pluginother/create_order', 'api/return_error'); Route::post('/Pluginother/create_order', 'api/return_success');
Route::post('/Pluginother/renew_order', 'api/return_error'); Route::post('/Pluginother/renew_order', 'api/return_success');
Route::post('/Pluginother/order_stat', 'api/return_empty'); Route::post('/Pluginother/order_stat', 'api/return_empty');
Route::post('/Pluginother/re_order_stat', 'api/return_empty'); Route::post('/Pluginother/re_order_stat', 'api/return_empty');
Route::post('/Pluginother/create_order_okey', 'api/return_empty'); Route::post('/Pluginother/create_order_okey', 'api/return_empty');
@ -97,7 +106,9 @@ Route::group('api', function () {
Route::get('/panel/notpro', 'api/return_empty'); Route::get('/panel/notpro', 'api/return_empty');
Route::post('/Btdeployment/get_deplist', 'api/get_deplist'); Route::post('/Btdeployment/get_deplist', 'api/get_deplist');
Route::post('/panel/get_deplist', 'api/get_deplist'); Route::post('/panel/get_deplist', 'api/get_deplist');
Route::get('/ip/info_json', 'api/return_empty_array'); Route::post('/ip/info', 'api/get_ip_info');
Route::post('/ip/info_json', 'api/get_ip_info');
Route::post('/panel/get_ip_info', 'api/get_ip_info');
Route::post('/LinuxBeta', 'api/return_error'); Route::post('/LinuxBeta', 'api/return_error');
Route::post('/panel/apple_beta', 'api/return_error'); Route::post('/panel/apple_beta', 'api/return_error');
@ -111,13 +122,52 @@ Route::group('api', function () {
Route::post('/v2/common_v1_authorization/get_pricing', 'api/return_error2'); Route::post('/v2/common_v1_authorization/get_pricing', 'api/return_error2');
Route::post('/v2/common_v2_authorization/get_pricing', 'api/return_error2'); Route::post('/v2/common_v2_authorization/get_pricing', 'api/return_error2');
Route::post('/v2/synchron', 'api/return_error2');
Route::post('/v2/product/email/user_surplus', 'api/email_user_surplus');
Route::post('/v2/product/email', 'api/return_error2');
Route::any('/bt_waf/getSpiders', 'api/btwaf_getspiders'); Route::any('/bt_waf/getSpiders', 'api/btwaf_getspiders');
Route::any('/bt_waf/get_malicious', 'api/btwaf_getmalicious');
Route::post('/bt_waf/addSpider', 'api/return_empty'); Route::post('/bt_waf/addSpider', 'api/return_empty');
Route::post('/bt_waf/getVulScanInfoList', 'api/return_empty'); Route::post('/bt_waf/getVulScanInfoList', 'api/return_empty');
Route::post('/bt_waf/reportInterceptFail', 'api/return_empty'); Route::post('/bt_waf/reportInterceptFail', 'api/return_empty');
Route::any('/bt_waf/get_system_malicious', 'api/return_error2');
Route::any('/panel/get_spider', 'api/get_spider'); Route::any('/panel/get_spider', 'api/get_spider');
Route::post('/Auth/GetSocre', 'api/get_ssl_list');
Route::post('/Auth/SetSocre', 'api/get_ssl_list');
Route::post('/Auth/SubmitScore', 'api/get_ssl_list');
Route::post('/Cert_cloud_deploy/get_cert_list', 'api/return_success');
Route::post('/Cert_cloud_deploy/del_cert', 'api/return_success');
Route::any('/panel/getSoftList', 'api/get_plugin_list_en');
Route::any('/panel/getSoftListEn', 'api/get_plugin_list_en');
Route::post('/panel/download_plugin', 'api/download_plugin_en');
Route::get('/plugin/download', 'api/download_plugin_other');
Route::get('/common/getClientIP', 'api/get_ip_address');
Route::post('/panel/checkDomain', 'api/check_domain');
Route::get('/panel/getBetaVersionLogs', 'api/get_beta_logs');
Route::any('/panel/updateLinuxEn', 'api/check_update_en');
Route::post('/user/verifyToken', 'api/return_success');
Route::post('/panel/nps/check', 'api/nps_check');
Route::post('/panel/nps/questions', 'api/nps_questions');
Route::post('/panel/nps/submit', 'api/nps_submit');
Route::post('/panel/submit_feature_invoked_bulk', 'api/return_success');
Route::post('/panel/submit_expand_pack_used', 'api/return_success');
Route::get('/panel/getLatestOfficialVersion', 'api/get_version_en');
Route::post('/cert/user/list', 'api/nps_questions');
Route::post('/v2/user/wx_web/bound_wx_accounts', 'api/nps_questions');
Route::post('/Auth/GetCloudToken', 'api/get_auth_token');
Route::post('/cloudtro/version_info', 'api/cloudc_version_info');
Route::post('/cloudtro/get_version', 'api/cloudc_get_version');
Route::post('/cloudtro/get_product_order_status_v1', 'api/cloudc_order_status');
Route::miss('api/return_error');
});
Route::group('newapi', function () {
Route::miss('api/return_error'); Route::miss('api/return_error');
}); });
@ -132,6 +182,7 @@ Route::group('admin', function () {
Route::post('/testbturl', 'admin/testbturl'); Route::post('/testbturl', 'admin/testbturl');
Route::get('/plugins', 'admin/plugins'); Route::get('/plugins', 'admin/plugins');
Route::get('/pluginswin', 'admin/pluginswin'); Route::get('/pluginswin', 'admin/pluginswin');
Route::get('/pluginsen', 'admin/pluginsen');
Route::post('/plugins_data', 'admin/plugins_data'); Route::post('/plugins_data', 'admin/plugins_data');
Route::post('/download_plugin', 'admin/download_plugin'); Route::post('/download_plugin', 'admin/download_plugin');
Route::get('/refresh_plugins', 'admin/refresh_plugins'); Route::get('/refresh_plugins', 'admin/refresh_plugins');
@ -145,6 +196,7 @@ Route::group('admin', function () {
Route::get('/deplist', 'admin/deplist'); Route::get('/deplist', 'admin/deplist');
Route::get('/refresh_deplist', 'admin/refresh_deplist'); Route::get('/refresh_deplist', 'admin/refresh_deplist');
Route::get('/cleancache', 'admin/cleancache'); Route::get('/cleancache', 'admin/cleancache');
Route::any('/ssl', 'admin/ssl');
})->middleware(\app\middleware\CheckAdmin::class); })->middleware(\app\middleware\CheckAdmin::class);

129
wiki/aapanel.md Normal file
View File

@ -0,0 +1,129 @@
# 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://wafapi.aapanel.com => http://www.example.com
- 全局搜索替换 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、class_v2/ajax_v2.py 文件:
\#是否执行升级程序 下面的 public.get_url() 改成 public.OfficialApiBase()
__official_url = 'https://www.aapanel.com' 改成 http://www.example.com
class/jobs.py、class_v2/jobs_v2.py 文件:
\#尝试升级到独立环境 下面的 public.get_url() 改成 public.OfficialApiBase()
class/system.py、class_v2/system_v2.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、class_v2/panel_plugin_v2.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')
```
- 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 文件 删除以下几行
"check_site_monitor": check_site_monitor,
"update_software_list": update_software_list,
"malicious_file_scanning": malicious_file_scanning,
"check_panel_msg": check_panel_msg,
"check_panel_auth": check_panel_auth,
"count_ssh_logs": count_ssh_logs,
"update_vulnerabilities": update_vulnerabilities,
"refresh_dockerapps": refresh_dockerapps,
"submit_email_statistics": submit_email_statistics,
"submit_module_call_statistics": submit_module_call_statistics,
"mailsys_domain_blecklisted_alarm": mailsys_domain_blecklisted_alarm,
- [可选]去除各种计算题将bt.js里面的内容复制到 BTPanel/static/vite/oldjs/public_backup.js 末尾
- [可选]去除创建网站自动创建的垃圾文件在class/panelSite.py、class_v2/panel_site_v2.py分别删除
htaccess = self.sitePath + '/.htaccess'
index = self.sitePath + '/index.html'
doc404 = self.sitePath + '/404.html'
这3行及分别接下来的4行代码
- [可选]关闭未绑定域名提示页面在class/panelSite.py、class_v2/panel_site_v2.pyroot /www/server/nginx/html改成return 400
- [可选]上传文件默认选中覆盖在BTPanel/static/vite/oldjs/upload-drog.jsid="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文件

View File

@ -1,53 +1,45 @@
# 宝塔云监控安装包修改记录 # 宝塔云监控安装包修改记录
查询最新版本号https://api.bt.cn/bt_monitor/latest_version 查询最新版本号https://api.bt.cn/bt_monitor/latest_version
安装包下载链接http://download.bt.cn/install/src/bt-monitor-版本号.zip 安装包下载链接http://download.bt.cn/install/src/bt-monitor-版本号.zip
- 删除core/include/c_loader/PluginLoader.sosqlite_server/PluginLoader.so将btmonitor/PluginLoader.py复制到这个文件夹 - 删除core/include/c_loader/PluginLoader.sohook_import/PluginLoader.so将btmonitor/PluginLoader.py复制到这个文件夹
- 批量解密源码:执行 php think decrypt all <源码根目录> - 批量解密源码:执行 php think decrypt all <源码根目录>
极少数文件解密失败是正常现象可无视 极少数文件解密失败是正常现象可无视
- 全局搜索替换 https://api.bt.cn => http://www.example.com需排除/bt_monitor/latest_agent_version、/bt_monitor/ip_info - 全局搜索替换 https://api.bt.cn => http://www.example.com需排除/bt_monitor/latest_agent_version、/bt_monitor/ip_info
- 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/ - 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/
- core/include/public.py 在 - core/include/public.py 在
```python ```python
def GetConfigValue(key): def GetConfigValue(key):
``` ```
这一行下面加上 这一行下面加上
```python ```python
if key == 'home': return 'http://www.example.com' if key == 'home': return 'http://www.example.com'
``` ```
def write_request_log(reques = None): 这一行下面加上 return def write_request_log(reques = None): 这一行下面加上 return
- core/include/basic_monitor.py - core/include/basic_monitor.py
在 def report_module_logs(self, force=False): 这一行下面加上 return 在 def report_module_logs(self, force=False): 这一行下面加上 return
- modules/configModule/main.py 在 def report_daily_active(self, force=False): 这一行下面加上 return
https://download.bt.cn => http://www.example.com - modules/configModule/main.py
- update/update_btmonitor.sh 修改Install_Monitor方法内的download_Url变量 https://download.bt.cn => http://www.example.com
- init.sh https://download.bt.cn => http://www.example.com - update/update_btmonitor.sh 修改Install_Monitor方法内的download_Url变量
- BT-MONITOR 在 - init.sh https://download.bt.cn => http://www.example.com
```python - 减少压缩包体积:删除 static/js/ 目录下的map文件
def CreateSSL():
```
这一行下面加上
```python
return CreateSSL_offline()
```

View File

@ -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

83
wiki/files/aapanel/bt.js Normal file
View File

@ -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();
})
}

View File

@ -1,47 +1,46 @@
#coding: utf-8 #coding: utf-8
import os,sys,json import os,sys,json
import core.include.public as public
#执行模块方法(模块名,方法名,参数) #执行模块方法(模块名,方法名,参数)
def module_run(module_name, def_name, def_args): def module_run(module_name, def_name, def_args):
if not module_name or not def_name: return public.returnMsg(False,'模块名称和模块方法名称不能为空!') if not module_name or not def_name: return returnMsg(False,'模块名称和模块方法名称不能为空!')
if not path_check(module_name) or not path_check(def_name): return public.returnMsg(False,'模块名或方法名不能包含特殊符号!') if not path_check(module_name) or not path_check(def_name): return returnMsg(False,'模块名或方法名不能包含特殊符号!')
panel_path = public.get_panel_path() panel_path = get_panel_path()
filename = "{}/modules/{}Module/{}Module.py".format(panel_path,module_name,module_name) filename = "{}/modules/{}Module/{}Module.py".format(panel_path,module_name,module_name)
if not os.path.exists(filename): if not os.path.exists(filename):
filename = "{}/modules/{}Module/main.py".format(panel_path,module_name) filename = "{}/modules/{}Module/main.py".format(panel_path,module_name)
if not os.path.exists(filename): if not os.path.exists(filename):
filename = "{}/plugin/{}/main.py".format(panel_path,module_name) filename = "{}/plugin/{}/main.py".format(panel_path,module_name)
if not os.path.exists(filename): if not os.path.exists(filename):
filename = "{}/plugin/{}Module/{}Plugin.py".format(panel_path,module_name,module_name) filename = "{}/plugin/{}/{}Plugin.py".format(panel_path,module_name,module_name)
if not os.path.exists(filename): if not os.path.exists(filename):
return public.returnMsg(False,'指定模块或插件不存在') return returnMsg(False,'指定模块或插件不存在')
_obj = public.get_script_object(filename) _obj = get_script_object(filename)
if not _obj: return public.returnMsg(False,'模块加载失败: %s' % module_name) if not _obj: return returnMsg(False,'模块加载失败: %s' % module_name)
if hasattr(_obj, "items") and hasattr(_obj, "setdefault"): if hasattr(_obj, "items") and hasattr(_obj, "setdefault"):
return _obj return _obj
class_name = "main" class_name = "main"
if not hasattr(_obj, class_name): if not hasattr(_obj, class_name):
return public.returnMsg(False,'找不到入口类: %s' % class_name) return returnMsg(False,'找不到入口类: %s' % class_name)
class_obj = getattr(_obj,class_name, None) class_obj = getattr(_obj,class_name, None)
if not class_obj: if not class_obj:
return public.returnMsg(False,'获取入口类失败' % module_name) return returnMsg(False,'获取入口类失败' % module_name)
try: try:
class_func = class_obj() class_func = class_obj()
except: except:
return public.returnMsg(False,'模块入口实例化失败' % module_name) return returnMsg(False,'模块入口实例化失败' % module_name)
if not hasattr(class_func, def_name): if not hasattr(class_func, def_name):
return public.returnMsg(False,'在[%s]模块中找不到[%s]方法' % (class_name,def_name)) return returnMsg(False,'在[%s]模块中找不到[%s]方法' % (class_name,def_name))
def_func = getattr(class_func, def_name, None) def_func = getattr(class_func, def_name, None)
if not def_func: if not def_func:
return public.returnMsg(False,'获取方法失败') return returnMsg(False,'获取方法失败')
if 'module_get_object' in def_args: if 'module_get_object' in def_args:
return def_func return def_func
@ -51,9 +50,49 @@ def module_run(module_name, def_name, def_args):
#获取指定模块对象(文件全路径) #获取指定模块对象(文件全路径)
def get_module(filename): def get_module(filename):
if not filename: return public.returnMsg(False,'模块路径不能为空!') if not filename: return returnMsg(False,'模块路径不能为空!')
if "./" in filename: return public.returnMsg(False,'模块路径不能为相对路径') if "./" in filename: return returnMsg(False,'模块路径不能为相对路径')
return public.get_script_object(filename) return get_script_object(filename)
def get_panel_path():
return '/www/server/bt-monitor'
def returnMsg(status,msg,args = ()):
return {'status':status,'msg':msg}
def get_script_object(filename):
_obj = sys.modules.get(filename,None)
if _obj: return _obj
from types import ModuleType
_obj = sys.modules.setdefault(filename, ModuleType(filename))
_code = readFile(filename)
_code_object = compile(_code,filename, 'exec')
_obj.__file__ = filename
_obj.__package__ = ''
exec(_code_object, _obj.__dict__)
return _obj
def readFile(filename,mode = 'r'):
import os
if not os.path.exists(filename): return False
fp = None
try:
fp = open(filename, mode)
f_body = fp.read()
except Exception as ex:
if sys.version_info[0] != 2:
try:
fp = open(filename, mode,encoding="utf-8")
f_body = fp.read()
except:
fp = open(filename, mode,encoding="GBK")
f_body = fp.read()
else:
return False
finally:
if fp and not fp.closed:
fp.close()
return f_body
#检查路径是否合法 #检查路径是否合法
def path_check(path): def path_check(path):

View File

@ -1,53 +1,54 @@
#coding: utf-8 #coding: utf-8
import public,os,sys,json # +-------------------------------------------------------------------
# | 宝塔Linux面板
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# +-------------------------------------------------------------------
# | Author: hwliang <hwl@bt.cn>
# +-------------------------------------------------------------------
#获取插件列表(0/1) #+--------------------------------------------------------------------
def get_plugin_list(force = 0): #| 插件和模块加载器
api_root_url = 'https://api.bt.cn' #+--------------------------------------------------------------------
api_url = api_root_url+ '/panel/get_plugin_list'
cache_file = 'data/plugin_list.json'
if force==0 and os.path.exists(cache_file):
jsonData = public.readFile(cache_file)
softList = json.loads(jsonData)
else:
try:
jsonData = public.HttpGet(api_url)
except Exception as ex:
raise public.error_conn_cloud(str(ex))
softList = json.loads(jsonData)
if type(softList)!=dict or 'list' not in softList:
if type(softList)==str:
raise Exception(softList)
else:
raise Exception('云端插件列表获取失败')
public.writeFile(cache_file, jsonData)
return softList
#获取授权状态() 返回0.免费版 1.专业版 2.企业版 -1.获取失败 import public,os,sys,json,hashlib
def get_auth_state():
try:
softList = get_plugin_list()
if softList['ltd'] > -1:
return 2
elif softList['pro'] > -1:
return 1
else:
return 0
except:
return -1
#执行插件方法(插件名,方法名,参数) def plugin_run(plugin_name,def_name,args):
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,'插件名称和插件方法名称不能为空!') if not plugin_name or not def_name: return public.returnMsg(False,'插件名称和插件方法名称不能为空!')
if not path_check(plugin_name) or not path_check(def_name): return public.returnMsg(False,'插件名或方法名不能包含特殊符号!')
p_path = public.get_plugin_path(plugin_name)
if not os.path.exists(p_path + '/index.php') and not os.path.exists(p_path + '/%s_main.py' % plugin_name): return public.returnMsg(False,'插件不存在!')
is_php = os.path.exists(p_path + '/index.php') # 获取插件目录
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,'插件路径不合法')
# 检查插件入口文件是否存在
if not os.path.exists(plugin_file): return public.returnMsg(False,'指定插件入口文件不存在')
# 添加插件目录到系统路径
public.sys_path_append(plugin_path)
if not is_php: if not is_php:
public.package_path_append(p_path) # 引用插件入口文件
plugin_main = __import__(plugin_name + '_main') _name = "{}_main".format(plugin_name)
plugin_main = __import__(_name)
# 检查类名是否符合规范
if not hasattr(plugin_main,_name):
return public.returnMsg(False,'指定插件入口文件不符合规范')
try: try:
if sys.version_info[0] == 2: if sys.version_info[0] == 2:
reload(plugin_main) reload(plugin_main)
@ -56,77 +57,209 @@ def plugin_run(plugin_name, def_name, args):
reload(plugin_main) reload(plugin_main)
except: except:
pass pass
plu = eval('plugin_main.' + plugin_name + '_main()')
if not hasattr(plu, def_name): # 实例化插件类
plugin_obj = getattr(plugin_main,_name)()
# 检查方法是否存在
if not hasattr(plugin_obj,def_name):
return public.returnMsg(False,'在[%s]插件中找不到[%s]方法' % (plugin_name,def_name)) return public.returnMsg(False,'在[%s]插件中找不到[%s]方法' % (plugin_name,def_name))
if 'plugin_get_object' in args and args.plugin_get_object == 1: if args is not None and 'plugin_get_object' in args and args.plugin_get_object == 1:
if not is_php: return getattr(plugin_obj, def_name)
return getattr(plu, def_name)
else: # 执行方法
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 return None
else: import panelPHP
if not is_php: args.s = def_name
data = eval('plu.' + def_name + '(args)') args.name = plugin_name
else: return panelPHP.panelPHP(plugin_name).exec_php_script(args)
import panelPHP
args.s = def_name
args.name = plugin_name
data = panelPHP.panelPHP(plugin_name).exec_php_script(args)
return data
#执行模块方法(模块名,方法名,参数) def get_module_list():
def module_run(mod_name, def_name, args): '''
if not mod_name or not def_name: return public.returnMsg(False,'模块名称和模块方法名称不能为空!') @name 获取模块列表
if not path_check(mod_name) or not path_check(def_name): return public.returnMsg(False,'模块名或方法名不能包含特殊符号!') @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,'模块名称和模块方法名称不能为空!')
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 args:
if args.model_index: # 新模块目录
mod_file = "{}/{}Model/{}Model.py".format(public.get_class_path(),args.model_index,mod_name) 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: else:
mod_file = "{}/projectModel/{}Model.py".format(public.get_class_path(),mod_name) module_file = os.path.join(class_path,"projectModel",module_name + 'Model.py')
else: else:
# 如果没指定模块名称,则遍历所有模块目录
module_list = get_module_list() module_list = get_module_list()
for module_dir in module_list: for name in module_list:
mod_file = "{}/{}/{}Model.py".format(public.get_class_path(),module_dir,mod_name) module_file = os.path.join(class_path,name,module_name + 'Model.py')
if os.path.exists(mod_file): break if os.path.exists(module_file): break
# 判断模块入口文件是否存在
if not os.path.exists(module_file):
return public.returnMsg(False,'模块[%s]不存在' % module_name)
# 判断模块路径是否合法
if not public.path_safe_check(module_file):
return public.returnMsg(False,'模块路径不合法')
def_object = public.get_script_object(module_file)
if not def_object: return public.returnMsg(False,'模块[%s]不存在' % module_name)
if not os.path.exists(mod_file): # 模块实例化并返回方法对象
return public.returnMsg(False,'模块[%s]不存在' % mod_name)
def_object = public.get_script_object(mod_file)
if not def_object: return public.returnMsg(False,'模块[%s]不存在!' % mod_name)
try: try:
run_object = getattr(def_object.main(),def_name,None) run_object = getattr(def_object.main(),def_name,None)
except: except:
return public.returnMsg(False,'模块入口实例化失败' % mod_name) return public.returnMsg(False,'模块[%s]入口实例化失败' % module_name)
if not run_object: return public.returnMsg(False,'在[%s]模块中找不到[%s]方法' % (mod_name,def_name)) if not run_object: return public.returnMsg(False,'在[%s]模块中找不到[%s]方法' % (module_name,def_name))
if 'module_get_object' in args and args.module_get_object == 1: if 'module_get_object' in args and args.module_get_object == 1:
return run_object return run_object
# 执行方法
result = run_object(args) result = run_object(args)
return result return result
def get_module(filename: str):
'''
@name 获取模块对象
@param filename<string> 模块文件名
@return object
'''
if not filename: return None
if filename[0:2] == './':
return public.returnMsg(False,'不能是相对路径')
if not public.path_safe_check(filename):
return public.returnMsg(False,'模块路径不合法')
#获取模块文件夹列表 if not os.path.exists(filename):
def get_module_list(): return public.returnMsg(False,'模块文件不存在' % filename)
list = []
class_path = public.get_class_path() def_object = public.get_script_object(filename)
f_list = os.listdir(class_path) if not def_object: return public.returnMsg(False,'模块[%s]不存在' % filename)
for fname in f_list:
f_path = class_path+'/'+fname
if os.path.isdir(f_path) and len(fname) > 6 and fname.find('.') == -1 and fname.find('Model') != -1:
list.append(fname)
return list
#检查路径是否合法 return def_object.main()
def path_check(path):
list = ["./","..",",",";",":","?","'","\"","<",">","|","\\","\n","\r","\t","\b","\a","\f","\v","*","%","&","$","#","@","!","~","`","^","(",")","+","=","{","}","[","]"] def get_plugin_list(upgrade_force = False):
for i in path: '''
if i in list: @name 获取插件列表
return False @param upgrade_force<bool> 是否强制重新获取列表
return True @return dict
'''
api_root_url = 'https://api.bt.cn'
api_url = api_root_url+ '/panel/get_plugin_list'
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,'云端插件列表获取失败')
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('云端插件列表获取失败')
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 'serverid' not in userInfo: return None
block_size = 51200
uid = str(userInfo['uid'])
server_id = userInfo['serverid']
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 start_total():
'''
@name 启动统计服务
@return dict
'''
pass
def get_soft_list(args):
'''
@name 获取软件列表
@param args<dict_obj> 参数对像
@return dict
'''
pass
#数据加密
def db_encrypt(data): def db_encrypt(data):
'''
@name 数据库加密
@param args<dict_obj> 参数对像
@return dict
'''
try: try:
key = __get_db_sgin() key = __get_db_sgin()
iv = __get_db_iv() iv = __get_db_iv()
@ -143,8 +276,12 @@ def db_encrypt(data):
} }
return result return result
#数据解密
def db_decrypt(data): def db_decrypt(data):
'''
@name 数据库解密
@param args<dict_obj> 参数对像
@return dict
'''
try: try:
key = __get_db_sgin() key = __get_db_sgin()
iv = __get_db_iv() iv = __get_db_iv()
@ -215,3 +352,49 @@ def __aes_encrypt(data, key, iv):
encryptedbytes = aes.encrypt(data) encryptedbytes = aes.encrypt(data)
en_text = base64.b64encode(encryptedbytes) en_text = base64.b64encode(encryptedbytes)
return en_text.decode('utf-8') 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

View File

@ -1,458 +1,110 @@
/* /*
*宝塔面板去除各种计算题与延时等待 *宝塔面板去除各种计算题与延时等待
*/ */
if("undefined" != typeof bt && bt.hasOwnProperty("show_confirm")){ if("undefined" != typeof bt && bt.hasOwnProperty("show_confirm")){
bt.show_confirm = function(title, msg, callback, error) { bt.show_confirm = function(title, msg, callback, error) {
layer.open({ layer.open({
type: 1, type: 1,
title: title, title: title,
area: "365px", area: "365px",
closeBtn: 2, closeBtn: 2,
shadeClose: true, shadeClose: true,
btn: [lan['public'].ok, lan['public'].cancel], btn: [lan['public'].ok, lan['public'].cancel],
content: "<div class='bt-form webDelete pd20'>\ content: "<div class='bt-form webDelete pd20'>\
<p style='font-size:13px;word-break: break-all;margin-bottom: 5px;'>" + msg + "</p>" + (error || '') + "\ <p style='font-size:13px;word-break: break-all;margin-bottom: 5px;'>" + msg + "</p>" + (error || '') + "\
</div>", </div>",
yes: function (index, layero) { yes: function (index, layero) {
layer.close(index); layer.close(index);
if (callback) callback(); if (callback) callback();
} }
}); });
} }
} }
if("undefined" != typeof bt && bt.hasOwnProperty("prompt_confirm")){ if("undefined" != typeof bt && bt.hasOwnProperty("prompt_confirm")){
bt.prompt_confirm = function (title, msg, callback) { bt.prompt_confirm = function (title, msg, callback) {
layer.open({ layer.open({
type: 1, type: 1,
title: title, title: title,
area: "350px", area: "350px",
closeBtn: 2, closeBtn: 2,
btn: ['确认', '取消'], btn: ['确认', '取消'],
content: "<div class='bt-form promptDelete pd20'>\ content: "<div class='bt-form promptDelete pd20'>\
<p>" + msg + "</p>\ <p>" + msg + "</p>\
</div>", </div>",
yes: function (layers, index) { yes: function (layers, index) {
layer.close(layers) layer.close(layers)
if (callback) callback() if (callback) callback()
} }
}); });
} }
} }
if("undefined" != typeof bt && bt.hasOwnProperty("compute_confirm")){ if("undefined" != typeof bt && bt.hasOwnProperty("compute_confirm")){
bt.compute_confirm = function (config, callback) { bt.compute_confirm = function (config, callback) {
layer.open({ layer.open({
type: 1, type: 1,
title: config.title, title: config.title,
area: '430px', area: '430px',
closeBtn: 2, closeBtn: 2,
shadeClose: true, shadeClose: true,
btn: [lan['public'].ok, lan['public'].cancel], btn: [lan['public'].ok, lan['public'].cancel],
content: content:
'<div class="bt-form hint_confirm pd30">\ '<div class="bt-form hint_confirm pd30">\
<div class="hint_title">\ <div class="hint_title">\
<i class="hint-confirm-icon"></i>\ <i class="hint-confirm-icon"></i>\
<div class="hint_con">' + <div class="hint_con">' +
config.msg + config.msg +
'</div>\ '</div>\
</div>\ </div>\
</div>', </div>',
yes: function (layers, index) { yes: function (layers, index) {
layer.close(layers) layer.close(layers)
if (callback) callback() if (callback) callback()
} }
}); });
} }
} }
if("undefined" != typeof database && database.hasOwnProperty("del_database")){ if("undefined" != typeof bt && bt.hasOwnProperty("input_confirm")){
database.del_database = function (wid, dbname, obj, callback) { bt.input_confirm = function (config, callback) {
var is_db_type = false, del_data = [] layer.open({
if (typeof wid === 'object') { type: 1,
del_data = wid title: config.title,
is_db_type = wid.some(function (item) { area: '430px',
return item.db_type > 0 closeBtn: 2,
}) shadeClose: true,
var ids = []; btn: [lan['public'].ok, lan['public'].cancel],
for (var i = 0; i < wid.length; i++) { content:
ids.push(wid[i].id); '<div class="bt-form hint_confirm pd30">\
} <div class="hint_title">\
wid = ids <i class="hint-confirm-icon"></i>\
} <div class="hint_con">' +
var type = $('.database-pos .tabs-item.active').data('type'), config.msg +
title = '', '</div>\
tips = ''; </div>\
title = typeof dbname === "function" ? '批量删除数据库' : '删除数据库 - [ ' + dbname + ' ]'; </div>',
tips = is_db_type || !recycle_bin_db_open || type !== 'mysql' ? '<span class="color-red">当前列表存在彻底删除后无法恢复的数据库</span>,请仔细查看列表,以防误删,是否继续操作?' : '当前列表数据库将迁移至数据库回收站,如需彻底删除请前往数据库回收站,是否继续操作?' yes: function (layers, index) {
var arrs = wid instanceof Array ? wid : [wid] layer.close(layers);
var ids = JSON.stringify(arrs), if (callback) callback();
countDown = 9; },
if (arrs.length == 1) countDown = 4 });
var loadT = bt.load('正在检测数据库数据信息,请稍候...'), }
param = { url: 'database/' + bt.data.db_tab_name + '/check_del_data', data: { data: JSON.stringify({ ids: ids }) } } }
if (bt.data.db_tab_name == 'mysql') param = { url: 'database?action=check_del_data', data: { ids: ids } } if(window.hasOwnProperty("SafeMessage")){
bt_tools.send(param, function (res) { window.SafeMessage = function(j, h, g, f) {
loadT.close() if (f == undefined) f = '';
layer.open({ var mess = layer.open({
type: 1, type: 1,
title: title, title: j,
area: '740px', area: "350px",
skin: 'verify_site_layer_info', closeBtn: 2,
closeBtn: 2, shadeClose: true,
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>"
content: '<div class="check_delete_site_main hint_confirm pd30">' + });
"<div class='hint_title'>\ $(".bt-cancel").click(function(){
<i class=\'hint-confirm-icon\'></i>\ layer.close(mess);
<div class=\'hint_con\'>"+ tips + "</div>\ });
</div>"+ $("#toSubmit").click(function() {
'<div id="check_layer_content" class="ptb15">' + layer.close(mess);
'</div>' + g();
'<div class="check_layer_message">' + })
(is_db_type ? '<span class="color-red">注意:远程数据库暂不支持数据库回收站,选中的数据库将彻底删除</span><br>' : '') + }
(!recycle_bin_db_open ? '<span class="color-red">风险操作:当前数据库回收站未开启,删除数据库将永久消失</span><br>' : '') }
+ '<span class="color-red">请仔细阅读以上要删除信息,防止数据库被误删</span></div>' +
'</div>',
btn: ['下一步', lan.public.cancel],
success: function (layers) {
setTimeout(function () { $(layers).css('top', ($(window).height() - $(layers).height()) / 2); }, 50)
var rdata = res.data,
newTime = parseInt(new Date().getTime() / 1000),
t_icon = ' <span class="glyphicon glyphicon-info-sign" style="color: red;width:15px;height: 15px;;vertical-align: middle;"></span>';
for (var j = 0; j < rdata.length; j++) {
for (var i = 0; i < del_data.length; i++) {
if (rdata[j].id == del_data[i].id) {
var is_time_rule = (newTime - rdata[j].st_time) > (86400 * 30) && (rdata[j].total > 1024 * 10),
is_database_rule = res.db_size <= rdata[j].total,
database_time = bt.format_data(rdata[j].st_time, 'yyyy-MM-dd'),
database_size = bt.format_size(rdata[j].total);
var f_size = database_size
var t_size = '注意:此数据库较大,可能为重要数据,请谨慎操作.\n数据库' + database_size;
if (rdata[j].total < 2048) t_size = '注意事项:当前数据库不为空,可能为重要数据,请谨慎操作.\n数据库' + database_size;
if (rdata[j].total === 0) t_size = '';
rdata[j]['t_size'] = t_size
rdata[j]['f_size'] = f_size
rdata[j]['database_time'] = database_time
rdata[j]['is_time_rule'] = is_time_rule
rdata[j]['is_database_rule'] = is_database_rule
rdata[j]['db_type'] = del_data[i].db_type
rdata[j]['conn_config'] = del_data[i].conn_config
}
}
}
var filterData = rdata.filter(function (el) {
return ids.indexOf(el.id) != -1
})
bt_tools.table({
el: '#check_layer_content',
data: filterData,
height: '300px',
column: [
{ fid: 'name', title: '数据库名称' },
{
title: '数据库大小', template: function (row) {
return '<span class="' + (row.is_database_rule ? 'warning' : '') + '" style="width: 110px;" title="' + row.t_size + '">' + row.f_size + (row.is_database_rule ? t_icon : '') + '</span>'
}
},
{
title: '数据库位置', template: function (row) {
var type_column = '-'
switch (row.db_type) {
case 0:
type_column = '本地数据库'
break;
case 1:
case 2:
type_column = '远程数据库'
break;
}
return '<span style="width: 110px;" title="' + type_column + '">' + type_column + '</span>'
}
},
{
title: '创建时间', template: function (row) {
return '<span ' + (is_time_rule && row.total != 0 ? 'class="warning"' : '') + ' title="' + (row.is_time_rule && row.total != 0 ? '重要:此数据库创建时间较早,可能为重要数据,请谨慎操作.' : '') + '时间:' + row.database_time + '">' + row.database_time + '</span>'
}
},
{
title: '删除结果', align: 'right', template: function (row, index, ev, _that) {
var _html = ''
switch (row.db_type) {
case 0:
_html = type !== 'mysql' ? '彻底删除' : (!recycle_bin_db_open ? '彻底删除' : '移至回收站')
break;
case 1:
case 2:
_html = '彻底删除'
break;
}
return '<span style="width: 110px;" class="' + (_html === '彻底删除' ? 'warning' + (row.db_type > 0 ? ' remote_database' : '') : '') + '">' + _html + '</span>'
}
}
],
success: function () {
$('#check_layer_content').find('.glyphicon-info-sign').click(function (e) {
var msg = $(this).parent().prop('title')
msg = msg.replace('数据库:','<br>数据库:')
layer.tips(msg, $(this).parent(), { tips: [1, 'red'], time: 3000 })
$(document).click(function (ev) {
layer.closeAll('tips');
$(this).unbind('click');
ev.stopPropagation();
ev.preventDefault();
});
e.stopPropagation();
e.preventDefault();
});
if ($('.remote_database').length) {
$('.remote_database').each(function (index, el) {
var id = $(el).parent().parent().parent().index()
$('#check_layer_content tbody tr').eq(id).css('background-color', '#ff00000a')
})
}
}
})
},
yes: function (indes, layers) {
title = typeof dbname === "function" ? '二次验证信息,批量删除数据库' : '二次验证信息,删除数据库 - [ ' + dbname + ' ]';
if (type !== 'mysql') {
tips = '<span class="color-red">当前数据库暂不支持数据库回收站,删除后将无法恢复</span>,此操作不可逆,是否继续操作?';
} else {
tips = is_db_type ? '<span class="color-red">远程数据库不支持数据库回收站,删除后将无法恢复</span>,此操作不可逆,是否继续操作?' : recycle_bin_db_open ? '删除后如需彻底删除请前往数据库回收站,是否继续操作?' : '删除后可能会影响业务使用,此操作不可逆,是否继续操作?'
}
layer.open({
type: 1,
title: title,
icon: 0,
skin: 'delete_site_layer',
area: "530px",
closeBtn: 2,
shadeClose: true,
content: "<div class=\'bt-form webDelete hint_confirm pd30\' id=\'site_delete_form\'>" +
"<div class='hint_title'>\
<i class=\'hint-confirm-icon\'></i>\
<div class=\'hint_con\'>"+ tips + "</div>\
</div>"+
"<div style=\'color:red;margin:18px 0 18px 18px;font-size:14px;font-weight: bold;\'>注意:数据无价,请谨慎操作!!!" + (type === 'mysql' && !recycle_bin_db_open ? '<br>风险操作:当前数据库回收站未开启,删除数据库将永久消失!' : '') + "</div>"+
"</div>",
btn: ['确认删除', '取消删除'],
yes: function (indexs) {
var data = {
id: wid,
name: dbname
};
if (typeof dbname === "function") {
delete data.id;
delete data.name;
}
layer.close(indexs)
layer.close(indes)
if (typeof dbname === "function") {
dbname(data)
} else {
data.id = data.id[0]
bt.database.del_database(data, function (rdata) {
layer.closeAll()
if (callback) callback(rdata);
bt.msg(rdata);
})
}
}
})
}
})
})
}
}
if("undefined" != typeof site && site.hasOwnProperty("del_site")){
site.del_site = function (wid, wname, callback) {
title = typeof wname === "function" ? '批量删除站点' : '删除站点 [ ' + wname + ' ]';
layer.open({
type: 1,
title: title,
icon: 0,
skin: 'delete_site_layer',
area: "440px",
closeBtn: 2,
shadeClose: true,
content: "<div class=\'bt-form webDelete pd30\' id=\'site_delete_form\'>" +
'<i class="layui-layer-ico layui-layer-ico0"></i>' +
"<div class=\'f13 check_title\'>是否要删除关联的FTP、数据库、站点目录</div>" +
"<div class=\"check_type_group\">" +
"<label><input type=\"checkbox\" name=\"ftp\"><span>FTP</span></label>" +
"<label><input type=\"checkbox\" name=\"database\"><span>数据库</span>" + (!recycle_bin_db_open ? '<span class="glyphicon glyphicon-info-sign" style="color: red"></span>' : '') + "</label>" +
"<label><input type=\"checkbox\" name=\"path\"><span>站点目录</span>" + (!recycle_bin_open ? '<span class="glyphicon glyphicon-info-sign" style="color: red"></span>' : '') + "</label>" +
"</div>" +
"</div>",
btn: [lan.public.ok, lan.public.cancel],
success: function (layers, indexs) {
$(layers).find('.check_type_group label').hover(function () {
var name = $(this).find('input').attr('name');
if (name === 'database' && !recycle_bin_db_open) {
layer.tips('风险操作:当前数据库回收站未开启,删除数据库将永久消失!', this, { tips: [1, 'red'], time: 0 })
} else if (name === 'path' && !recycle_bin_open) {
layer.tips('风险操作:当前文件回收站未开启,删除站点目录将永久消失!', this, { tips: [1, 'red'], time: 0 })
}
}, function () {
layer.closeAll('tips');
});
},
yes: function (indexs) {
var data = { id: wid, webname: wname };
$('#site_delete_form input[type=checkbox]').each(function (index, item) {
if ($(item).is(':checked')) data[$(item).attr('name')] = 1
})
var is_database = data.hasOwnProperty('database'), is_path = data.hasOwnProperty('path'), is_ftp = data.hasOwnProperty('ftp');
if ((!is_database && !is_path) && (!is_ftp || is_ftp)) {
if (typeof wname === "function") {
wname(data)
return false;
}
bt.site.del_site(data, function (rdata) {
layer.close(indexs);
if (callback) callback(rdata);
bt.msg(rdata);
})
return false
}
if (typeof wname === "function") {
delete data.id;
delete data.webname;
}
layer.close(indexs)
var ids = JSON.stringify(wid instanceof Array ? wid : [wid]), countDown = typeof wname === 'string' ? 4 : 9;
title = typeof wname === "function" ? '二次验证信息,批量删除站点' : '二次验证信息,删除站点 [ ' + wname + ' ]';
var loadT = bt.load('正在检测站点数据信息,请稍候...')
bt.send('check_del_data', 'site/check_del_data', { ids: ids }, function (res) {
loadT.close()
layer.open({
type: 1,
title: title,
closeBtn: 2,
skin: 'verify_site_layer_info',
area: '740px',
content: '<div class="check_delete_site_main pd30">' +
'<i class="layui-layer-ico layui-layer-ico0"></i>' +
'<div class="check_layer_title">堡塔温馨提示您,请冷静几秒钟,确认以下要删除的数据。</div>' +
'<div class="check_layer_content">' +
'<div class="check_layer_item">' +
'<div class="check_layer_site"></div>' +
'<div class="check_layer_database"></div>' +
'</div>' +
'</div>' +
'<div class="check_layer_error ' + (is_database && data['database'] && !recycle_bin_db_open ? '' : 'hide') + '"><span class="glyphicon glyphicon-info-sign"></span>风险事项:当前未开启数据库回收站功能,删除数据库后,数据库将永久消失!</div>' +
'<div class="check_layer_error ' + (is_path && data['path'] && !recycle_bin_open ? '' : 'hide') + '"><span class="glyphicon glyphicon-info-sign"></span>风险事项:当前未开启文件回收站功能,删除站点目录后,站点目录将永久消失!</div>' +
'<div class="check_layer_message"><span style="color:red">注意:请仔细阅读以上要删除信息,防止网站数据被误删</span></div>' +
'</div>',
// recycle_bin_db_open &&
// recycle_bin_open &&
btn: ['确认删除', '取消删除'],
success: function (layers) {
var html = '', rdata = res.data;
for (var i = 0; i < rdata.length; i++) {
var item = rdata[i], newTime = parseInt(new Date().getTime() / 1000),
t_icon = '<span class="glyphicon glyphicon-info-sign" style="color: red;width:15px;height: 15px;;vertical-align: middle;"></span>';
site_html = (function (item) {
if (!is_path) return ''
var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10),
is_path_rule = res.file_size <= item.total,
dir_time = bt.format_data(item.st_time, 'yyyy-MM-dd'),
dir_size = bt.format_size(item.total);
var f_html = '<i ' + (is_path_rule ? 'class="warning"' : '') + ' style = "vertical-align: middle;" > ' + (item.limit ? '大于50MB' : dir_size) + '</i> ' + (is_path_rule ? t_icon : '');
var f_title = (is_path_rule ? '注意:此目录较大,可能为重要数据,请谨慎操作.\n' : '') + '目录:' + item.path + '(' + (item.limit ? '大于' : '') + dir_size + ')';
return '<div class="check_layer_site">' +
'<span title="站点:' + item.name + '">站点名:' + item.name + '</span>' +
'<span title="' + f_title + '" >目录:<span style="vertical-align: middle;max-width: 160px;width: auto;">' + item.path + '</span> (' + f_html + ')</span>' +
'<span title="' + (is_time_rule ? '注意:此站点创建时间较早,可能为重要数据,请谨慎操作.\n' : '') + '时间:' + dir_time + '">创建时间:<i ' + (is_time_rule ? 'class="warning"' : '') + '>' + dir_time + '</i></span>' +
'</div>'
}(item)),
database_html = (function (item) {
if (!is_database || !item.database) return '';
var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10),
is_database_rule = res.db_size <= item.database.total,
database_time = bt.format_data(item.database.st_time, 'yyyy-MM-dd'),
database_size = bt.format_size(item.database.total);
var f_size = '<i ' + (is_database_rule ? 'class="warning"' : '') + ' style = "vertical-align: middle;" > ' + database_size + '</i> ' + (is_database_rule ? t_icon : '');
var t_size = '注意:此数据库较大,可能为重要数据,请谨慎操作.\n数据库' + database_size;
return '<div class="check_layer_database">' +
'<span title="数据库:' + item.database.name + '">数据库:' + item.database.name + '</span>' +
'<span title="' + t_size + '">大小:' + f_size + '</span>' +
'<span title="' + (is_time_rule && item.database.total != 0 ? '重要:此数据库创建时间较早,可能为重要数据,请谨慎操作.' : '') + '时间:' + database_time + '">创建时间:<i ' + (is_time_rule && item.database.total != 0 ? 'class="warning"' : '') + '>' + database_time + '</i></span>' +
'</div>'
}(item))
if ((site_html + database_html) !== '') html += '<div class="check_layer_item">' + site_html + database_html + '</div>';
}
if (html === '') html = '<div style="text-align: center;width: 100%;height: 100%;line-height: 300px;font-size: 15px;">无数据</div>'
$('.check_layer_content').html(html)
},
yes: function (indes, layers) {
if (typeof wname === "function") {
wname(data)
} else {
bt.site.del_site(data, function (rdata) {
layer.closeAll()
if (rdata.status) site.get_list();
if (callback) callback(rdata);
bt.msg(rdata);
})
}
}
})
})
}
})
}
}
if("undefined" != typeof bt && bt.hasOwnProperty("firewall") && bt.firewall.hasOwnProperty("add_accept_port")){
bt.firewall.add_accept_port = function(type, port, ps, callback) {
var action = "AddDropAddress";
if (type == 'port') {
ports = port.split(':');
if (port.indexOf('-') != -1) ports = port.split('-');
for (var i = 0; i < ports.length; i++) {
if (!bt.check_port(ports[i])) {
layer.msg('可用端口范围1-65535', { icon: 2 });
// layer.msg(lan.firewall.port_err, {
// icon: 5
// });
return;
}
}
action = "AddAcceptPort";
}
loading = bt.load();
bt.send(action, 'firewall/' + action, { port: port, type: type, ps: ps }, function(rdata) {
loading.close();
if (callback) callback(rdata);
})
}
}
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();
})
}
$(document).ready(function () {
if($('#updata_pro_info').length>0){
$('#updata_pro_info').html('');
bt.set_cookie('productPurchase', 1);
}
})

View File

@ -1,369 +1,394 @@
/* /*
*宝塔面板去除各种计算题与延时等待 *宝塔面板去除各种计算题与延时等待
*/ */
if("undefined" != typeof bt && bt.hasOwnProperty("show_confirm")){ if("undefined" != typeof bt && bt.hasOwnProperty("show_confirm")){
bt.show_confirm = function(title, msg, fun, error) { bt.show_confirm = function(title, msg, fun, error) {
if (error == undefined) { if (error == undefined) {
error = "" error = ""
} }
var mess = layer.open({ var mess = layer.open({
type: 1, type: 1,
title: title, title: title,
area: "350px", area: "350px",
closeBtn: 2, closeBtn: 2,
shadeClose: true, shadeClose: true,
content: "<div class='bt-form webDelete pd20 pb70'><p>" + msg + "</p>" + error + "<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>" content: "<div class='bt-form webDelete pd20 pb70'><p>" + msg + "</p>" + error + "<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 () { $(".bt-cancel").click(function () {
layer.close(mess); layer.close(mess);
}); });
$("#toSubmit").click(function () { $("#toSubmit").click(function () {
layer.close(mess); layer.close(mess);
fun(); fun();
}) })
} }
} }
if("undefined" != typeof bt && bt.hasOwnProperty("prompt_confirm")){ if("undefined" != typeof bt && bt.hasOwnProperty("prompt_confirm")){
bt.prompt_confirm = function (title, msg, callback) { bt.prompt_confirm = function (title, msg, callback) {
layer.open({ layer.open({
type: 1, type: 1,
title: title, title: title,
area: "350px", area: "350px",
closeBtn: 2, closeBtn: 2,
btn: ['确认', '取消'], btn: ['确认', '取消'],
content: "<div class='bt-form promptDelete pd20'>\ content: "<div class='bt-form promptDelete pd20'>\
<p>" + msg + "</p>\ <p>" + msg + "</p>\
</div>", </div>",
yes: function (layers, index) { yes: function (layers, index) {
layer.close(layers) layer.close(layers)
if (callback) callback() if (callback) callback()
} }
}); });
} }
} }
if("undefined" != typeof bt && bt.hasOwnProperty("compute_confirm")){ if("undefined" != typeof bt && bt.hasOwnProperty("compute_confirm")){
bt.compute_confirm = function (config, callback) { bt.compute_confirm = function (config, callback) {
layer.open({ layer.open({
type: 1, type: 1,
title: config.title, title: config.title,
area: '430px', area: '430px',
closeBtn: 2, closeBtn: 2,
shadeClose: true, shadeClose: true,
btn: [lan['public'].ok, lan['public'].cancel], btn: [lan['public'].ok, lan['public'].cancel],
content: content:
'<div class="bt-form hint_confirm pd30">\ '<div class="bt-form hint_confirm pd30">\
<div class="hint_title">\ <div class="hint_title">\
<i class="hint-confirm-icon"></i>\ <i class="hint-confirm-icon"></i>\
<div class="hint_con">' + <div class="hint_con">' +
config.msg + config.msg +
'</div>\ '</div>\
</div>\ </div>\
</div>', </div>',
yes: function (layers, index) { yes: function (layers, index) {
layer.close(layers) layer.close(layers)
if (callback) callback() if (callback) callback()
} }
}); });
} }
} }
if("undefined" != typeof database && database.hasOwnProperty("del_database")){ if("undefined" != typeof bt && bt.hasOwnProperty("input_confirm")){
database.del_database = function (wid, dbname,obj, callback) { bt.input_confirm = function (config, callback) {
var title = '', layer.open({
tips = '是否确认【删除数据库】,删除后可能会影响业务使用!'; type: 1,
if(obj && obj.db_type > 0) tips = '远程数据库不支持数据库回收站,删除后将无法恢复,请谨慎操作'; title: config.title,
title = typeof dbname === "function" ?'批量删除数据库':'删除数据库 [ '+ dbname +' ]'; area: '430px',
layer.open({ closeBtn: 2,
type:1, shadeClose: true,
title:title, btn: [lan['public'].ok, lan['public'].cancel],
icon:0, content:
skin:'delete_site_layer', '<div class="bt-form hint_confirm pd30">\
area: "530px", <div class="hint_title">\
closeBtn: 2, <i class="hint-confirm-icon"></i>\
shadeClose: true, <div class="hint_con">' +
content:"<div class=\'bt-form webDelete pd30\' id=\'site_delete_form\'>" + config.msg +
"<i class=\'layui-layer-ico layui-layer-ico0\'></i>" + '</div>\
"<div class=\'f13 check_title\' style=\'margin-bottom: 20px;\'>"+tips+"</div>" + </div>\
"<div style=\'color:red;margin:18px 0 18px 18px;font-size:14px;font-weight: bold;\'>注意:数据无价,请谨慎操作!!!"+(!recycle_bin_db_open?'<br>风险操作:当前数据库回收站未开启,删除数据库将永久消失!':'')+"</div>" + </div>',
"</div>", yes: function (layers, index) {
btn:[lan.public.ok,lan.public.cancel], layer.close(layers);
yes:function(indexs){ if (callback) callback();
var data = {id: wid,name: dbname}; },
if(typeof dbname === "function"){ });
delete data.id; }
delete data.name; }
} if("undefined" != typeof database && database.hasOwnProperty("del_database")){
layer.close(indexs) database.del_database = function (wid, dbname,obj, callback) {
var arrs = wid instanceof Array ? wid : [wid] var title = '',
var ids = JSON.stringify(arrs), countDown = 9; tips = '是否确认【删除数据库】,删除后可能会影响业务使用!';
if (arrs.length == 1) countDown = 4 if(obj && obj.db_type > 0) tips = '远程数据库不支持数据库回收站,删除后将无法恢复,请谨慎操作';
title = typeof dbname === "function" ?'二次验证信息,批量删除数据库':'二次验证信息,删除数据库 [ ' + dbname + ' ]'; title = typeof dbname === "function" ?'批量删除数据库':'删除数据库 [ '+ dbname +' ]';
var loadT = bt.load('正在检测数据库数据信息,请稍后...') layer.open({
type:1,
bt_tools.send({url:'database/'+bt.data.db_tab_name+'/check_del_data',data:{data:JSON.stringify({ids: ids})}},function(res){ title:title,
loadT.close() icon:0,
layer.open({ skin:'delete_site_layer',
type:1, area: "530px",
title:title, closeBtn: 2,
closeBtn: 2, shadeClose: true,
skin: 'verify_site_layer_info', content:"<div class=\'bt-form webDelete pd30\' id=\'site_delete_form\'>" +
area: '740px', "<i class=\'layui-layer-ico layui-layer-ico0\'></i>" +
content: '<div class="check_delete_site_main pd30">' + "<div class=\'f13 check_title\' style=\'margin-bottom: 20px;\'>"+tips+"</div>" +
'<i class="layui-layer-ico layui-layer-ico0"></i>' + "<div style=\'color:red;margin:18px 0 18px 18px;font-size:14px;font-weight: bold;\'>注意:数据无价,请谨慎操作!!!"+(!recycle_bin_db_open?'<br>风险操作:当前数据库回收站未开启,删除数据库将永久消失!':'')+"</div>" +
'<div class="check_layer_title">堡塔温馨提示您,请冷静几秒钟,确认是否要删除以下数据。</div>' + "</div>",
'<div class="check_layer_content">' + btn:[lan.public.ok,lan.public.cancel],
'<div class="check_layer_item">' + yes:function(indexs){
'<div class="check_layer_site"></div>' + var data = {id: wid,name: dbname};
'<div class="check_layer_database"></div>' + if(typeof dbname === "function"){
'</div>' + delete data.id;
'</div>' + delete data.name;
'<div class="check_layer_error ' + (recycle_bin_db_open ? 'hide' : '') + '"><span class="glyphicon glyphicon-info-sign"></span>风险事项:当前未开启数据库回收站功能,删除数据库后,数据库将永久消失!</div>' + }
'<div class="check_layer_message"><span style="color:red">注意:请仔细阅读以上要删除信息,防止数据库被误删</span></div>' + layer.close(indexs)
'</div>', var arrs = wid instanceof Array ? wid : [wid]
btn: ['确认删除', '取消删除'], var ids = JSON.stringify(arrs), countDown = 9;
success: function (layers) { if (arrs.length == 1) countDown = 4
var html = '', rdata = res.data; title = typeof dbname === "function" ?'二次验证信息,批量删除数据库':'二次验证信息,删除数据库 [ ' + dbname + ' ]';
var filterData = rdata.filter(function(el){ var loadT = bt.load('正在检测数据库数据信息,请稍后...')
return ids.indexOf(el.id) != -1
}) bt_tools.send({url:'database/'+bt.data.db_tab_name+'/check_del_data',data:{data:JSON.stringify({ids: ids})}},function(res){
for (var i = 0; i < filterData.length; i++) { loadT.close()
var item = filterData[i], newTime = parseInt(new Date().getTime() / 1000), layer.open({
t_icon = '<span class="glyphicon glyphicon-info-sign" style="color: red;width:15px;height: 15px;;vertical-align: middle;"></span>'; type:1,
title:title,
database_html = (function(item){ closeBtn: 2,
var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10), skin: 'verify_site_layer_info',
is_database_rule = res.db_size <= item.total, area: '740px',
database_time = bt.format_data(item.st_time, 'yyyy-MM-dd'), content: '<div class="check_delete_site_main pd30">' +
database_size = bt.format_size(item.total); '<i class="layui-layer-ico layui-layer-ico0"></i>' +
'<div class="check_layer_title">堡塔温馨提示您,请冷静几秒钟,确认是否要删除以下数据。</div>' +
var f_size = '<i ' + (is_database_rule ? 'class="warning"' : '') + ' style = "vertical-align: middle;" > ' + database_size + '</i> ' + (is_database_rule ? t_icon : ''); '<div class="check_layer_content">' +
var t_size = '注意:此数据库较大,可能为重要数据,请谨慎操作.\n数据库' + database_size; '<div class="check_layer_item">' +
'<div class="check_layer_site"></div>' +
return '<div class="check_layer_database">' + '<div class="check_layer_database"></div>' +
'<span title="数据库:' + item.name + '">数据库:' + item.name + '</span>' + '</div>' +
'<span title="' + t_size+'">大小:' + f_size +'</span>' + '</div>' +
'<span title="' + (is_time_rule && item.total != 0 ? '重要:此数据库创建时间较早,可能为重要数据,请谨慎操作.' : '') + '时间:' + database_time+'">创建时间:<i ' + (is_time_rule && item.total != 0 ? 'class="warning"' : '') + '>' + database_time + '</i></span>' + '<div class="check_layer_error ' + (recycle_bin_db_open ? 'hide' : '') + '"><span class="glyphicon glyphicon-info-sign"></span>风险事项:当前未开启数据库回收站功能,删除数据库后,数据库将永久消失!</div>' +
'</div>' '<div class="check_layer_message"><span style="color:red">注意:请仔细阅读以上要删除信息,防止数据库被误删</span></div>' +
}(item)) '</div>',
if(database_html !== '') html += '<div class="check_layer_item">' + database_html +'</div>'; btn: ['确认删除', '取消删除'],
} success: function (layers) {
if(html === '') html = '<div style="text-align: center;width: 100%;height: 100%;line-height: 300px;font-size: 15px;">无数据</div>' var html = '', rdata = res.data;
$('.check_layer_content').html(html) var filterData = rdata.filter(function(el){
}, return ids.indexOf(el.id) != -1
yes:function(indes,layers){ })
if(typeof dbname === "function"){ for (var i = 0; i < filterData.length; i++) {
dbname(data) var item = filterData[i], newTime = parseInt(new Date().getTime() / 1000),
}else{ t_icon = '<span class="glyphicon glyphicon-info-sign" style="color: red;width:15px;height: 15px;;vertical-align: middle;"></span>';
bt.database.del_database(data, function (rdata) {
layer.closeAll() database_html = (function(item){
if (rdata.status) database_table.$refresh_table_list(true); var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10),
if (callback) callback(rdata); is_database_rule = res.db_size <= item.total,
bt.msg(rdata); database_time = bt.format_data(item.st_time, 'yyyy-MM-dd'),
}) database_size = bt.format_size(item.total);
}
} var f_size = '<i ' + (is_database_rule ? 'class="warning"' : '') + ' style = "vertical-align: middle;" > ' + database_size + '</i> ' + (is_database_rule ? t_icon : '');
}) var t_size = '注意:此数据库较大,可能为重要数据,请谨慎操作.\n数据库' + database_size;
})
} return '<div class="check_layer_database">' +
}) '<span title="数据库:' + item.name + '">数据库:' + item.name + '</span>' +
} '<span title="' + t_size+'">大小:' + f_size +'</span>' +
} '<span title="' + (is_time_rule && item.total != 0 ? '重要:此数据库创建时间较早,可能为重要数据,请谨慎操作.' : '') + '时间:' + database_time+'">创建时间:<i ' + (is_time_rule && item.total != 0 ? 'class="warning"' : '') + '>' + database_time + '</i></span>' +
if("undefined" != typeof site && site.hasOwnProperty("del_site")){ '</div>'
site.del_site = function(wid, wname, callback) { }(item))
var title = typeof wname === "function" ? '批量删除站点' : '删除站点 [ ' + wname + ' ]'; if(database_html !== '') html += '<div class="check_layer_item">' + database_html +'</div>';
recycle_bin_open = bt.get_cookie("is_recycle") || bt.get_cookie("is_recycle") == null ? true : false }
layer.open({ if(html === '') html = '<div style="text-align: center;width: 100%;height: 100%;line-height: 300px;font-size: 15px;">无数据</div>'
type: 1, $('.check_layer_content').html(html)
title: title, },
icon: 0, yes:function(indes,layers){
skin: 'delete_site_layer', if(typeof dbname === "function"){
area: "440px", dbname(data)
closeBtn: 2, }else{
shadeClose: true, bt.database.del_database(data, function (rdata) {
content: "<div class=\'bt-form webDelete pd30\' id=\'site_delete_form\'>" + layer.closeAll()
'<i class="layui-layer-ico layui-layer-ico0"></i>' + if (rdata.status) database_table.$refresh_table_list(true);
"<div class=\'f13 check_title\'>是否要删除关联的FTP、数据库、站点目录</div>" + if (callback) callback(rdata);
"<div class=\"check_type_group\">" + bt.msg(rdata);
"<label><input type=\"checkbox\" name=\"ftp\"><span>FTP</span></label>" + })
"<label><input type=\"checkbox\" name=\"database\"><span>数据库</span>" + (!recycle_bin_db_open ? '<span class="glyphicon glyphicon-info-sign" style="color: red"></span>' : '') + "</label>" + }
"<label><input type=\"checkbox\" name=\"path\"><span>站点目录</span>" + (!recycle_bin_open ? '<span class="glyphicon glyphicon-info-sign" style="color: red"></span>' : '') + "</label>" + }
"</div>" + })
"</div>", })
btn: [lan.public.ok, lan.public.cancel], }
success: function (layers, indexs) { })
$(layers).find('.check_type_group label').hover(function () { }
var name = $(this).find('input').attr('name'); }
if (name === 'data' && !recycle_bin_db_open) { if("undefined" != typeof site && site.hasOwnProperty("del_site")){
layer.tips('风险操作:当前数据库回收站未开启,删除数据库将永久消失!', this, { tips: [1, 'red'], time: 0 }) site.del_site = function(wid, wname, callback) {
} else if (name === 'path' && !recycle_bin_open) { var title = typeof wname === "function" ? '批量删除站点' : '删除站点 [ ' + wname + ' ]';
layer.tips('风险操作:当前文件回收站未开启,删除站点目录将永久消失!', this, { tips: [1, 'red'], time: 0 }) recycle_bin_open = bt.get_cookie("is_recycle") || bt.get_cookie("is_recycle") == null ? true : false
} layer.open({
}, function () { type: 1,
layer.closeAll('tips'); title: title,
}) icon: 0,
}, skin: 'delete_site_layer',
yes: function (indexs) { area: "440px",
var data = { id: wid, webname: wname }; closeBtn: 2,
$('#site_delete_form input[type=checkbox]').each(function (index, item) { shadeClose: true,
if ($(item).is(':checked')) data[$(item).attr('name')] = 1 content: "<div class=\'bt-form webDelete pd30\' id=\'site_delete_form\'>" +
}) '<i class="layui-layer-ico layui-layer-ico0"></i>' +
var is_database = data.hasOwnProperty('database'), is_path = data.hasOwnProperty('path'), is_ftp = data.hasOwnProperty('ftp'); "<div class=\'f13 check_title\'>是否要删除关联的FTP、数据库、站点目录</div>" +
if ((!is_database && !is_path) && (!is_ftp || is_ftp)) { "<div class=\"check_type_group\">" +
if (typeof wname === "function") { "<label><input type=\"checkbox\" name=\"ftp\"><span>FTP</span></label>" +
wname(data) "<label><input type=\"checkbox\" name=\"database\"><span>数据库</span>" + (!recycle_bin_db_open ? '<span class="glyphicon glyphicon-info-sign" style="color: red"></span>' : '') + "</label>" +
return false; "<label><input type=\"checkbox\" name=\"path\"><span>站点目录</span>" + (!recycle_bin_open ? '<span class="glyphicon glyphicon-info-sign" style="color: red"></span>' : '') + "</label>" +
} "</div>" +
bt.site.del_site(data, function (rdata) { "</div>",
layer.close(indexs); btn: [lan.public.ok, lan.public.cancel],
if (callback) callback(rdata); success: function (layers, indexs) {
bt.msg(rdata); $(layers).find('.check_type_group label').hover(function () {
}) var name = $(this).find('input').attr('name');
return false if (name === 'data' && !recycle_bin_db_open) {
} layer.tips('风险操作:当前数据库回收站未开启,删除数据库将永久消失!', this, { tips: [1, 'red'], time: 0 })
if (typeof wname === "function") { } else if (name === 'path' && !recycle_bin_open) {
delete data.id; layer.tips('风险操作:当前文件回收站未开启,删除站点目录将永久消失!', this, { tips: [1, 'red'], time: 0 })
delete data.webname; }
} }, function () {
layer.close(indexs) layer.closeAll('tips');
var arrs = wid instanceof Array ? wid : [wid] })
var ids = JSON.stringify(arrs), countDown = 9; },
if (arrs.length == 1) countDown = 4 yes: function (indexs) {
title = typeof wname === "function" ? '二次验证信息,批量删除站点' : '二次验证信息,删除站点 [ ' + wname + ' ]'; var data = { id: wid, webname: wname };
var loadT = bt.load('正在检测站点数据信息,请稍后...') $('#site_delete_form input[type=checkbox]').each(function (index, item) {
bt.send('check_del_data', 'site/check_del_data', { ids: ids }, function (res) { if ($(item).is(':checked')) data[$(item).attr('name')] = 1
loadT.close() })
layer.open({ var is_database = data.hasOwnProperty('database'), is_path = data.hasOwnProperty('path'), is_ftp = data.hasOwnProperty('ftp');
type: 1, if ((!is_database && !is_path) && (!is_ftp || is_ftp)) {
title: title, if (typeof wname === "function") {
closeBtn: 2, wname(data)
skin: 'verify_site_layer_info', return false;
area: '740px', }
content: '<div class="check_delete_site_main pd30">' + bt.site.del_site(data, function (rdata) {
'<i class="layui-layer-ico layui-layer-ico0"></i>' + layer.close(indexs);
'<div class="check_layer_title">堡塔温馨提示您,请冷静几秒钟,确认以下要删除的数据。</div>' + if (callback) callback(rdata);
'<div class="check_layer_content">' + bt.msg(rdata);
'<div class="check_layer_item">' + })
'<div class="check_layer_site"></div>' + return false
'<div class="check_layer_database"></div>' + }
'</div>' + if (typeof wname === "function") {
'</div>' + delete data.id;
'<div class="check_layer_error ' + (data.database && recycle_bin_db_open ? 'hide' : '') + '"><span class="glyphicon glyphicon-info-sign"></span>风险事项:当前未开启数据库回收站功能,删除数据库后,数据库将永久消失!</div>' + delete data.webname;
'<div class="check_layer_error ' + (data.path && recycle_bin_open ? 'hide' : '') + '"><span class="glyphicon glyphicon-info-sign"></span>风险事项:当前未开启文件回收站功能,删除站点目录后,站点目录将永久消失!</div>' + }
'<div class="check_layer_message"><span style="color:red">注意:请仔细阅读以上要删除信息,防止网站数据被误删</span></div>' + layer.close(indexs)
'</div>', var arrs = wid instanceof Array ? wid : [wid]
// recycle_bin_db_open && var ids = JSON.stringify(arrs), countDown = 9;
// recycle_bin_open && if (arrs.length == 1) countDown = 4
btn: ['确认删除', '取消删除'], title = typeof wname === "function" ? '二次验证信息,批量删除站点' : '二次验证信息,删除站点 [ ' + wname + ' ]';
success: function (layers) { var loadT = bt.load('正在检测站点数据信息,请稍后...')
var html = '', rdata = res.data; bt.send('check_del_data', 'site/check_del_data', { ids: ids }, function (res) {
for (var i = 0; i < rdata.length; i++) { loadT.close()
var item = rdata[i], newTime = parseInt(new Date().getTime() / 1000), layer.open({
t_icon = '<span class="glyphicon glyphicon-info-sign" style="color: red;width:15px;height: 15px;;vertical-align: middle;"></span>'; type: 1,
title: title,
site_html = (function (item) { closeBtn: 2,
if (!is_path) return '' skin: 'verify_site_layer_info',
var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10), area: '740px',
is_path_rule = res.file_size <= item.total, content: '<div class="check_delete_site_main pd30">' +
dir_time = bt.format_data(item.st_time, 'yyyy-MM-dd'), '<i class="layui-layer-ico layui-layer-ico0"></i>' +
dir_size = bt.format_size(item.total); '<div class="check_layer_title">堡塔温馨提示您,请冷静几秒钟,确认以下要删除的数据。</div>' +
'<div class="check_layer_content">' +
var f_html = '<i ' + (is_path_rule ? 'class="warning"' : '') + ' style = "vertical-align: middle;" > ' + dir_size + '</i> ' + (is_path_rule ? t_icon : ''); '<div class="check_layer_item">' +
var f_title = (is_path_rule ? '注意:此目录较大,可能为重要数据,请谨慎操作.\n' : '') + '目录:' + item.path + '(' + (item.limit ? '大于' : '') + dir_size + ')'; '<div class="check_layer_site"></div>' +
'<div class="check_layer_database"></div>' +
return '<div class="check_layer_site">' + '</div>' +
'<span title="站点:' + item.name + '">站点名:' + item.name + '</span>' + '</div>' +
'<span title="' + f_title + '" >目录:<span style="vertical-align: middle;max-width: 160px;width: auto;">' + item.path + '</span> (' + f_html + ')</span>' + '<div class="check_layer_error ' + (data.database && recycle_bin_db_open ? 'hide' : '') + '"><span class="glyphicon glyphicon-info-sign"></span>风险事项:当前未开启数据库回收站功能,删除数据库后,数据库将永久消失!</div>' +
'<span title="' + (is_time_rule ? '注意:此站点创建时间较早,可能为重要数据,请谨慎操作.\n' : '') + '时间:' + dir_time + '">创建时间:<i ' + (is_time_rule ? 'class="warning"' : '') + '>' + dir_time + '</i></span>' + '<div class="check_layer_error ' + (data.path && recycle_bin_open ? 'hide' : '') + '"><span class="glyphicon glyphicon-info-sign"></span>风险事项:当前未开启文件回收站功能,删除站点目录后,站点目录将永久消失!</div>' +
'</div>' '<div class="check_layer_message"><span style="color:red">注意:请仔细阅读以上要删除信息,防止网站数据被误删</span></div>' +
}(item)), '</div>',
database_html = (function (item) { // recycle_bin_db_open &&
if (!is_database || !item.database) return ''; // recycle_bin_open &&
var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10), btn: ['确认删除', '取消删除'],
is_database_rule = res.db_size <= item.database.total, success: function (layers) {
database_time = bt.format_data(item.database.st_time, 'yyyy-MM-dd'), var html = '', rdata = res.data;
database_size = bt.format_size(item.database.total); for (var i = 0; i < rdata.length; i++) {
var item = rdata[i], newTime = parseInt(new Date().getTime() / 1000),
var f_size = '<i ' + (is_database_rule ? 'class="warning"' : '') + ' style = "vertical-align: middle;" > ' + database_size + '</i> ' + (is_database_rule ? t_icon : ''); t_icon = '<span class="glyphicon glyphicon-info-sign" style="color: red;width:15px;height: 15px;;vertical-align: middle;"></span>';
var t_size = '注意:此数据库较大,可能为重要数据,请谨慎操作.\n数据库' + database_size;
site_html = (function (item) {
return '<div class="check_layer_database">' + if (!is_path) return ''
'<span title="数据库:' + item.database.name + '">数据库:' + item.database.name + '</span>' + var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10),
'<span title="' + t_size + '">大小:' + f_size + '</span>' + is_path_rule = res.file_size <= item.total,
'<span title="' + (is_time_rule && item.database.total != 0 ? '重要:此数据库创建时间较早,可能为重要数据,请谨慎操作.' : '') + '时间:' + database_time + '">创建时间:<i ' + (is_time_rule && item.database.total != 0 ? 'class="warning"' : '') + '>' + database_time + '</i></span>' + dir_time = bt.format_data(item.st_time, 'yyyy-MM-dd'),
'</div>' dir_size = bt.format_size(item.total);
}(item))
if ((site_html + database_html) !== '') html += '<div class="check_layer_item">' + site_html + database_html + '</div>'; var f_html = '<i ' + (is_path_rule ? 'class="warning"' : '') + ' style = "vertical-align: middle;" > ' + dir_size + '</i> ' + (is_path_rule ? t_icon : '');
} var f_title = (is_path_rule ? '注意:此目录较大,可能为重要数据,请谨慎操作.\n' : '') + '目录:' + item.path + '(' + (item.limit ? '大于' : '') + dir_size + ')';
if (html === '') html = '<div style="text-align: center;width: 100%;height: 100%;line-height: 300px;font-size: 15px;">无数据</div>'
$('.check_layer_content').html(html) return '<div class="check_layer_site">' +
}, '<span title="站点:' + item.name + '">站点名:' + item.name + '</span>' +
yes: function (indes, layers) { '<span title="' + f_title + '" >目录:<span style="vertical-align: middle;max-width: 160px;width: auto;">' + item.path + '</span> (' + f_html + ')</span>' +
if (typeof wname === "function") { '<span title="' + (is_time_rule ? '注意:此站点创建时间较早,可能为重要数据,请谨慎操作.\n' : '') + '时间:' + dir_time + '">创建时间:<i ' + (is_time_rule ? 'class="warning"' : '') + '>' + dir_time + '</i></span>' +
wname(data) '</div>'
} else { }(item)),
bt.site.del_site(data, function (rdata) { database_html = (function (item) {
layer.closeAll() if (!is_database || !item.database) return '';
if (rdata.status) site.get_list(); var is_time_rule = (newTime - item.st_time) > (86400 * 30) && (item.total > 1024 * 10),
if (callback) callback(rdata); is_database_rule = res.db_size <= item.database.total,
bt.msg(rdata); database_time = bt.format_data(item.database.st_time, 'yyyy-MM-dd'),
}) database_size = bt.format_size(item.database.total);
}
} var f_size = '<i ' + (is_database_rule ? 'class="warning"' : '') + ' style = "vertical-align: middle;" > ' + database_size + '</i> ' + (is_database_rule ? t_icon : '');
}) var t_size = '注意:此数据库较大,可能为重要数据,请谨慎操作.\n数据库' + database_size;
})
} return '<div class="check_layer_database">' +
}) '<span title="数据库:' + item.database.name + '">数据库:' + item.database.name + '</span>' +
if(bt.get_cookie("is_recycle") || bt.get_cookie("is_recycle")==null){ '<span title="' + t_size + '">大小:' + f_size + '</span>' +
$('[name="path"]').attr('checked',true) '<span title="' + (is_time_rule && item.database.total != 0 ? '重要:此数据库创建时间较早,可能为重要数据,请谨慎操作.' : '') + '时间:' + database_time + '">创建时间:<i ' + (is_time_rule && item.database.total != 0 ? 'class="warning"' : '') + '>' + database_time + '</i></span>' +
}else{ '</div>'
$('[name="path"]').removeProp('checked'); }(item))
} if ((site_html + database_html) !== '') html += '<div class="check_layer_item">' + site_html + database_html + '</div>';
} }
} if (html === '') html = '<div style="text-align: center;width: 100%;height: 100%;line-height: 300px;font-size: 15px;">无数据</div>'
if("undefined" != typeof bt && bt.hasOwnProperty("firewall") && bt.firewall.hasOwnProperty("add_accept_port")){ $('.check_layer_content').html(html)
bt.firewall.add_accept_port = function(type, port, ps, callback) { },
var action = "AddDropAddress"; yes: function (indes, layers) {
if (type == 'port') { if (typeof wname === "function") {
ports = port.split(':'); wname(data)
if (port.indexOf('-') != -1) ports = port.split('-'); } else {
for (var i = 0; i < ports.length; i++) { bt.site.del_site(data, function (rdata) {
if (!bt.check_port(ports[i])) { layer.closeAll()
layer.msg(lan.firewall.port_err, { icon: 5 }); if (rdata.status) site.get_list();
return; if (callback) callback(rdata);
} bt.msg(rdata);
} })
action = "AddAcceptPort"; }
} }
})
loading = bt.load(); })
bt.send(action, 'firewall/' + action, { port: port, type: type, ps: ps }, function(rdata) { }
loading.close(); })
if (callback) callback(rdata); if(bt.get_cookie("is_recycle") || bt.get_cookie("is_recycle")==null){
}) $('[name="path"]').attr('checked',true)
} }else{
} $('[name="path"]').removeProp('checked');
function SafeMessage(j, h, g, f) { }
if(f == undefined) { }
f = "" }
} if("undefined" != typeof bt && bt.hasOwnProperty("firewall") && bt.firewall.hasOwnProperty("add_accept_port")){
var mess = layer.open({ bt.firewall.add_accept_port = function(type, port, ps, callback) {
type: 1, var action = "AddDropAddress";
title: j, if (type == 'port') {
area: "350px", ports = port.split(':');
closeBtn: 2, if (port.indexOf('-') != -1) ports = port.split('-');
shadeClose: true, for (var i = 0; i < ports.length; i++) {
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>" if (!bt.check_port(ports[i])) {
}); layer.msg(lan.firewall.port_err, { icon: 5 });
$(".bt-cancel").click(function(){ return;
layer.close(mess); }
}); }
$("#toSubmit").click(function() { action = "AddAcceptPort";
layer.close(mess); }
g();
}) loading = bt.load();
} bt.send(action, 'firewall/' + action, { port: port, type: type, ps: ps }, function(rdata) {
$(document).ready(function () { loading.close();
if($('#updata_pro_info').length>0){ if (callback) callback(rdata);
$('#updata_pro_info').html(''); })
bt.set_cookie('productPurchase', 1); }
} }
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();
})
}
$(document).ready(function () {
if($('#updata_pro_info').length>0){
$('#updata_pro_info').html('');
bt.set_cookie('productPurchase', 1);
}
}) })

View File

@ -14,12 +14,20 @@
- 全局搜索替换 https://api.bt.cn => http://www.example.com - 全局搜索替换 https://api.bt.cn => http://www.example.com
- 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/需排除clearModel.py、scanningModel.py、ipsModel.py - 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/需排除clearModel.py、scanningModel.py、ipsModel.py、domainMod.py、js文件
- 全局搜索替换 http://www.bt.cn/api/ => http://www.example.com/api/需排除js文件 - 全局搜索替换 http://www.bt.cn/api/ => http://www.example.com/api/需排除js文件
- 全局搜索替换 https://www.bt.cn/newapi/ => http://www.example.com/newapi/
- 全局搜索替换 https://download.bt.cn/install/update6.sh => http://www.example.com/install/update6.sh - 全局搜索替换 https://download.bt.cn/install/update6.sh => http://www.example.com/install/update6.sh
http://download.bt.cn/install/update6.sh => http://www.example.com/install/update6.sh
http://download.bt.cn/install/update/ => http://www.example.com/install/update/
- 搜索并删除提交异常报告的代码 bt_error/index.php
- class/ajax.py 文件 \# 是否执行升级程序 下面的 public.get_url() 改成 public.GetConfigValue('home') - class/ajax.py 文件 \# 是否执行升级程序 下面的 public.get_url() 改成 public.GetConfigValue('home')
class/jobs.py 文件 \#尝试升级到独立环境 下面的 public.get_url() 改成 public.GetConfigValue('home') class/jobs.py 文件 \#尝试升级到独立环境 下面的 public.get_url() 改成 public.GetConfigValue('home')
@ -42,6 +50,8 @@
在 def check_domain_cloud(domain): 这一行下面加上 return 在 def check_domain_cloud(domain): 这一行下面加上 return
在 def err_collect 这一行下面加上 return
在 def get_improvement(): 这一行下面加上 return False 在 def get_improvement(): 这一行下面加上 return False
在free_login_area方法内get_free_ips_area替换成get_ips_area 在free_login_area方法内get_free_ips_area替换成get_ips_area
@ -50,50 +60,64 @@
在login_send_body方法内free_login_area(login_ip=server_ip_area的server_ip_area改成login_ip 在login_send_body方法内free_login_area(login_ip=server_ip_area的server_ip_area改成login_ip
- class/panelPlugin.py 文件download_icon方法内替换 public.GetConfigValue('home') => 'https://www.bt.cn' - class/panelPlugin.py 文件
删除public.total_keyword(get.query)这一行
__set_pyenv方法内temp_file = public.readFile(filename)这行代码下面加上 __set_pyenv方法内temp_file = public.readFile(filename)这行代码下面加上
```python ```python
temp_file = temp_file.replace('wget -O Tpublic.sh', '#wget -O Tpublic.sh')
temp_file = temp_file.replace('\cp -rpa Tpublic.sh', '#\cp -rpa Tpublic.sh')
temp_file = temp_file.replace('http://download.bt.cn/install/public.sh', 'http://www.example.com/install/public.sh') 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') 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
```
plugin_bin.pl 改成 plugin_list.json
删除 public.total_keyword(get.query)
删除 public.run_thread(self.get_cloud_list_status, args=(get,))
删除 public.run_thread(self.is_verify_unbinding, args=(get,))
- class/plugin_deployment.py 文件__setup_php_environment方法和GetJarPath方法内替换 public.GetConfigValue('home') => 'https://www.bt.cn'
- class/plugin_deployment.py 文件SetupPackage方法内替换 public.GetConfigValue('home') => 'https://www.bt.cn' - class/config.py 文件get_nps方法内data['nps'] = False改成Trueget_nps_new方法下面加上 return public.returnMsg(False, "获取问卷失败")
def err_collection(self, get): 这一行下面加上 return public.returnMsg(True, "OK")
- class/push/site_push.py 文件,'https://www.bt.cn' => 'http://www.example.com'
- script/flush_plugin.py 文件删除clear_hosts()一行 - script/flush_plugin.py 文件删除clear_hosts()一行
- install/install_soft.sh 在bash执行之前加入以下代码 - script/reload_check.py 文件在第2行插入sys.exit()
- script/local_fix.sh 文件,${D_NODE_URL}替换成www.example.com
- script/upgrade_panel_optimized.py 文件def get_home_node(url): 下面加上return url
- install/install_soft.sh 在. 执行之前加入以下代码
```shell ```shell
sed -i "s/http:\/\/download.bt.cn\/install\/public.sh/http:\/\/www.example.com\/install\/public.sh/" lib.sh 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/" lib.sh sed -i "s/https:\/\/download.bt.cn\/install\/public.sh/http:\/\/www.example.com\/install\/public.sh/" $name.sh
sed -i "/wget -O Tpublic.sh/d" $name.sh
``` ```
- install/public.sh 用官网最新版的[public.sh](http://download.bt.cn/install/public.sh)替换并去除最下面bt_check一行 - install/public.sh 用官网最新版的[public.sh](http://download.bt.cn/install/public.sh)替换并去除最下面bt_check一行
- 去除无用的定时任务task.py 文件 删除以下几行 - 去除无用的定时任务task.py 文件 删除以下几行
"check_panel_msg": check_panel_msg, check_panel_msg,
PluginLoader.daemon_panel() refresh_domain_cache,
- 去除WebRTC连接BTPanel/static/js/public.js 删除stun.start();这一行 task_ssh_error_count,
- 去除首页广告BTPanel/static/js/index.js 文件删除两处index.recommend_paid_version() - [可选]去除各种计算题复制bt.js到 BTPanel/static/ ,在 BTPanel/templates/default/software.html 的 \<script\>window.vite_public_request_token 前面加入
- 去除首页自动检测更新避免频繁请求云端BTPanel/static/js/index.js 文件注释掉bt.system.check_update这一段代码外的setTimeout
- 去除内页广告BTPanel/templates/default/layout.html 删除两处getPaymentStatus();
- 删除问卷调查BTPanel/templates/default/layout.html 删除if(window.localStorage.getItem('panelNPS') == null)以及下面的行
- [可选]去除各种计算题复制bt.js到 BTPanel/static/ ,在 BTPanel/templates/default/layout.html 的\</body\>前面加入
```javascript ```javascript
<script src="/static/bt.js"></script> <script src="/static/bt.js"></script>
@ -109,16 +133,16 @@
这3行及分别接下来的4行代码 这3行及分别接下来的4行代码
def get_view_title_content(self, get): 下面加上 return public.returnMsg(True, '')
- [可选]关闭未绑定域名提示页面在class/panelSite.pyroot /www/server/nginx/html改成return 400 - [可选]关闭未绑定域名提示页面在class/panelSite.pyroot /www/server/nginx/html改成return 400
- [可选]关闭自动生成访问日志:在 BTPanel/\_\_init\_\_.py 删除public.write_request_log这一行 - [可选]关闭自动生成访问日志:在 BTPanel/\_\_init\_\_.py 删除public.write_request_log这一行
- [可选]删除小图标广告在BTPanel/static/js/site.js删除“WAF防火墙”对应的span标签 - [可选]新版vite页面去除需求反馈、各种广告、计算题等执行 php think cleanvitejs <面板BTPanel/static/js路径>
- [可选]上传文件默认选中覆盖在BTPanel/static/js/upload-drog.jsid="all_operation"加checked属性
解压安装包panel6.zip将更新包改好的文件覆盖到里面然后重新打包即可更新安装包。 解压安装包[panel6.zip](http://download.bt.cn/install/src/panel6.zip),将更新包改好的文件覆盖到里面,然后重新打包,即可更新安装包。(
别忘了删除class文件夹里面所有的.so文件 别忘了删除class文件夹里面所有的.so文件

View File

@ -1,78 +1,10 @@
# Windows面板官方更新包修改记录 # Windows面板官方更新包修改记录
查询最新版本号https://www.bt.cn/api/wpanel/get_version?is_version=1 查询最新版本号https://www.bt.cn/api/wpanel/get_version?is_version=1
官方更新包下载链接http://download.bt.cn/win/panel/panel_版本号.zip 官方更新包下载链接http://download.bt.cn/win/panel/panel_版本号.zip
假设搭建的宝塔第三方云端网址是 http://www.example.com - 使用16进制编辑器打开btPanel.exe将 https://api.bt.cn 替换成 http://api.bt.cn/ ,将 https://www.bt.cn 替换成 http://www.bt.cn/ 然后将api.bt.cn替换成任意其他域名将第二个www.bt.cn替换成任意其他域名。
- 批量解密模块文件:执行 php think decrypt classdir <面板class文件夹路径>
Windows版宝塔由于加密文件太多无法全部解密因此无法做到全开源。 - 新版vite页面去除需求反馈、各种广告、计算题等执行 php think cleanvitejs <面板assets/static/js路径>
- 删除PluginLoader.pyd将win/PluginLoader.py复制到class文件夹
- 全局搜索替换 https://api.bt.cn => http://www.example.com
- 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/需排除ipsModel.py
- 全局搜索替换 http://www.bt.cn/api/ => http://www.example.com/api/
- 全局搜索替换 https://download.bt.cn/win/panel/data/setup.py => http://www.example.com/win/panel/data/setup.py
- class/panel_update.py 文件 public.get_url() => 'http://www.example.com'
- class/public.py 在
```python
def GetConfigValue(key):
```
这一行下面加上
```python
if key == 'home': return 'http://www.example.com'
```
在 def is_bind(): 这一行下面加上 return True
在 def check_domain_cloud(domain): 这一行下面加上 return
在 get_update_file() 方法里面 get_url() => GetConfigValue('home')
- class/plugin_deployment.py 文件 get_icon 和 SetupPackage 方法内,替换 public.GetConfigValue('home') => 'https://www.bt.cn'
- 去除无用的定时任务task.py 文件
删除 p = threading.Thread(target=check_files_panel) 以及下面2行
删除 p = threading.Thread(target=check_panel_msg) 以及下面2行
删除 p = threading.Thread(target=update_software_list) 以及下面2行
- 去除面板日志上报script/site_task.py 文件
- 删除最下面 logs_analysis() 这1行
- 去除首页广告BTPanel/static/js/index.js 文件删除最下面index.recommend_paid_version()这一行以及index.consultancy_services()这一行
- 去除首页自动检测更新避免频繁请求云端BTPanel/static/js/index.js 文件注释掉bt.system.check_update这一段代码外的setTimeout
- 去除内页广告BTPanel/templates/default/layout.html 删除getPaymentStatus();这一行
- [可选]去除各种计算题复制win/bt.js到 BTPanel/static/ ,在 BTPanel/templates/default/layout.html 的尾部加入
```javascript
<script src="/static/bt.js"></script>
```
- [可选]去除创建网站自动创建的垃圾文件class/panelSite.py 文件
删除 htaccess = self.sitePath + '/.htaccess' 以及下面2行
删除 index = self.sitePath + '/index.html' 以及下面6行
删除 doc404 = self.sitePath + '/404.html' 以及下面6行
删除 if not os.path.exists(self.sitePath + '/.htaccess') 这一行
- [可选]关闭自动生成访问日志:在 BTPanel/\_\_init\_\_.py 删除public.write_request_log()这一行