Compare commits

...

5 Commits
3.4.3 ... 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
25 changed files with 622 additions and 609 deletions

View File

@ -101,6 +101,7 @@ class CleanViteJs extends Command
} }
private function handlefile($filepath){ private function handlefile($filepath){
//echo $filepath."\n";
$file = file_get_contents($filepath); $file = file_get_contents($filepath);
if(!$file)return; if(!$file)return;
@ -119,8 +120,24 @@ class CleanViteJs extends Command
} }
$flag = true; $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){ //main if(strpos($file, '论坛求助')!==false && strpos($file, '"/other/customer-qrcode.png"')!==false){ //main
$code = $this->getExtendCode($file, '"微信公众号"', 1); $code = $this->getExtendCode($file, '"微信公众号"', 1);
$code = $this->getExtendFunction($file, $code); $code = $this->getExtendFunction($file, $code);
$start = strpos($file, $code) - 1; $start = strpos($file, $code) - 1;
@ -147,12 +164,23 @@ class CleanViteJs extends Command
$file = preg_replace('!computed\(\(\)=>"input"===\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"calc"===\w+\.\w+\.type\}\)!', '!1', $file);
$file = preg_replace('!computed\(function\(\)\{return"input"===\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); $code = $this->getExtendCode($file, '"自动部署"', 2);
if($code){ if($code){
$file = str_replace($code.',', '', $file);
$file = str_replace($code, '', $file); $file = str_replace($code, '', $file);
} }
$flag = true; $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 if(strpos($file, '请冷静几秒钟,确认以下要删除的数据')!==false && strpos($file, '"计算结果:"')!==false){ //site
$code = $this->getExtendCode($file, '"计算结果:"', 1, '[', ']'); $code = $this->getExtendCode($file, '"计算结果:"', 1, '[', ']');
@ -196,29 +224,42 @@ class CleanViteJs extends Command
$code2 = str_replace($this->getExtendFunction($code, '"购买商业证书"'), '', $code2); $code2 = str_replace($this->getExtendFunction($code, '"购买商业证书"'), '', $code2);
$file = str_replace($code, $code2, $file); $file = str_replace($code, $code2, $file);
} }
$file = str_replace('.value="busSslList"', '.value="letsEncryptList"', $file);
$flag = true; $flag = true;
} }
if(strpos($file, '"btSslList"')!==false && strpos($filepath, '/useStore')){ //site-ssl if(strpos($file, '"busSslList"')!==false && strpos($filepath, '/useStore')){ //site-ssl
$file = str_replace('"btSslList"', '"currentCertInfo"', $file); $file = str_replace('"busSslList"', '"currentCertInfo"', $file);
$flag = true; $flag = true;
} }
if(strpos($file, '"商用SSL"')!==false){ //ssl if(strpos($file, '"商用SSL"')!==false){ //ssl
$code = $this->getExtendFunction($file, '"商用SSL"', '{', '}'); $code = $this->getExtendFunction($file, '"商用SSL"', '{', '}');
$file = str_replace($code, '', $file); $file = str_replace($code, '', $file);
$code = $this->getExtendCode($file, ',"联系客服"', 2, '[', ']'); $code = $this->getExtendFunction($file, '"宝塔证书"', '{', '}');
if($code){ if($code){
$file = str_replace($code, '[]', $file); $file = str_replace($code, '', $file);
} }
$code = $this->getExtendCode($file, ',"联系客服"', 2, '[', ']'); for($i=0;$i<3;$i++){
if($code){ $code = $this->getExtendCode($file, ',"联系客服"', 2, '[', ']');
$file = str_replace($code, '[]', $file); if($code){
$file = str_replace($code, '[]', $file);
}
} }
$flag = true;
} }
if(strpos($file, '"SSL-CERTIFICATE-STORE"')!==false){ //ssl if(strpos($file, '"SSL-CERTIFICATE-STORE"')!==false){ //ssl
$file = str_replace('("ssl")', '("encrypt")', $file); $file = str_replace('("ssl")', '("encrypt")', $file);
$flag = true; $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){ if(strpos($file, '如果您希望添加其它Docker应用')!==false){
$code = $this->getExtendCode($file, '如果您希望添加其它Docker应用', 1, '[', ']'); $code = $this->getExtendCode($file, '如果您希望添加其它Docker应用', 1, '[', ']');
@ -234,9 +275,13 @@ class CleanViteJs extends Command
$flag = true; $flag = true;
} }
if(strpos($file, '"打开插件文件目录"')!==false){ //soft.table if(strpos($file, '"打开插件文件目录"')!==false && strpos($file, '"(续费)"')!==false){ //soft.table
$code = $this->getExtendFunction($file, '"(续费)"'); $code = $this->getExtendFunction($file, '"(续费)"');
$file = str_replace($code, '""', $file); $file = str_replace($code, '""', $file);
$code = $this->getExtendCode($file, 'activity_id:47', 2);
if($code){
$file = str_replace($code, '{}', $file);
}
$flag = true; $flag = true;
} }

View File

@ -58,6 +58,7 @@ class UpdateAll extends Command
//循环下载缺少的插件 //循环下载缺少的插件
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

@ -144,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']:'面板地址无法连接']);
} }
} }
} }
@ -170,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();
} }

View File

@ -19,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")]);
@ -33,7 +36,10 @@ 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); return json($json_arr);
} }
@ -47,49 +53,49 @@ 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('en'); $json_arr = Plugins::get_plugin_list('en');
if(!$json_arr) return json((object)[]); 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()) return '你的服务器被禁止使用此云端'; 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 //下载插件包aapanel
public function download_plugin_en(){ public function download_plugin_en(){
$plugin_name = input('post.name'); $plugin_name = input('param.name');
$version = input('post.version'); $version = input('param.version');
if(!$plugin_name || !$version){ if(!$plugin_name || !$version){
return '参数不能为空'; return json(['status'=>false, 'msg'=>'参数不能为空']);
} }
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()) return '你的服务器被禁止使用此云端'; if(!$this->checklist()) return json(['status'=>false, 'msg'=>'你的服务器被禁止使用此云端']);
$filepath = get_data_dir('en').'plugins/package/'.$plugin_name.'-'.$version.'.zip'; $filepath = get_data_dir('en').'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'=>'云端不存在该插件包']);
} }
} }
@ -99,13 +105,13 @@ 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()) return '你的服务器被禁止使用此云端'; 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';
$mainfilepath = get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py'; $mainfilepath = get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
if(file_exists($mainfilepath)){ if(file_exists($mainfilepath)){
@ -116,10 +122,10 @@ class Api extends BaseController
if ($zip->open($filepath) === true){ if ($zip->open($filepath) === true){
echo $zip->getFromName($plugin_name.'/'.$plugin_name.'_main.py'); echo $zip->getFromName($plugin_name.'/'.$plugin_name.'_main.py');
}else{ }else{
return '插件包解压缩失败'; return json(['status'=>false, 'msg'=>'插件包解压缩失败']);
} }
}else{ }else{
return '云端不存在该插件主文件'; return json(['status'=>false, 'msg'=>'云端不存在该插件主文件']);
} }
} }
@ -143,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'){
@ -544,6 +580,17 @@ class Api extends BaseController
return json(json_decode($response, true)); 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)[]]);
} }

View File

@ -9,9 +9,10 @@ class BtPlugins
{ {
private $btapi; private $btapi;
private $os; private $os;
//需屏蔽的插件名称列表 //需屏蔽的插件名称列表
private static $block_plugins = ['dns','bt_boce','ssl_verify']; 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;

View File

@ -26,6 +26,17 @@ class Btapi
return $data; return $data;
} }
public function get_config_go(){
$url = $this->BT_PANEL.'/panel/get_config';
$p_data = $this->GetKeyData();
$result = $this->curl($url,$p_data);
$data = json_decode($result,true);
return $data;
}
//获取已登录用户信息 //获取已登录用户信息
public function get_user_info(){ public function get_user_info(){
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_user_info'; $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_user_info';

View File

@ -200,4 +200,19 @@ class Plugins
return $result; 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

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
Linux_Version="11.2.0" Linux_Version="11.5.0"
Windows_Version="8.2.2" Windows_Version="8.2.2"
Aapanel_Version="7.0.25" Aapanel_Version="7.0.25"
Btm_Version="2.3.3" Btm_Version="2.3.3"
@ -12,8 +12,6 @@ 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

View File

@ -75,7 +75,7 @@ td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:340px;
<script src="{$cdnpublic}bootstrap-table/1.19.1/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 = ['firewall', 'php_filter', 'enterprise_backup', 'tamper_drive', 'KylinOperatingSystem', 'KingdeeApusicDistributedCache', 'BorlandCacheServer', 'GBase8s', 'KingdeeApusicLoadBalancer', 'BorlandWebServer']; 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+'版本插件包?', {

View File

@ -174,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"/>
@ -384,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({
@ -429,7 +429,7 @@ $(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']}); var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({ $.ajax({
@ -474,7 +474,7 @@ $(document).ready(function(){
if(enbt_key == ''){ if(enbt_key == ''){
layer.alert('宝塔面板接口密钥不能为空');return; layer.alert('宝塔面板接口密钥不能为空');return;
} }
var postdata = {bt_type:enbt_type, bt_url:enbt_url, bt_key:enbt_key}; 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({

View File

@ -142,17 +142,18 @@
<div class="text">Windows面板{:config_get('new_version_win')}安装方法</div> <div class="text">Windows面板{:config_get('new_version_win')}安装方法</div>
</div> </div>
<div class="desc"> <div class="desc">
<p>1、使用<a class="link" href="https://download.bt.cn/win/panel/BtSoft.zip" target="_blank" rel="noreferrer">官方安装程序</a>进行安装,安装完先不要进入面板。</p> <p>1、使用<a class="link" href="https://download.bt.cn/win/panel/go/BtSoft.exe" target="_blank" rel="noreferrer">官方安装程序</a>进行安装,安装完先不要进入面板。</p>
<p>2、在命令提示符cmd输入以下一键更新命令然后重启面板。</p> <p>2、在命令提示符cmd输入以下一键更新命令然后重启面板。</p>
<p>3、打开面板后绑定账号时输入任意账号密码即可。</p>
</div> </div>
<div class="install-code"> <div class="install-code">
<div class="code-cont"> <div class="code-cont">
<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 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>
<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> <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>
</div> </div>
</div> </div>
<div class="tips" style="color: orangered; font-weight: 700"> <div class="tips" style="color: orangered; font-weight: 700">
<p>注意:仅支持Windows Server 2008 R2/2012/2016/2019/202264位系统中文简体,且未安装其它环境</p> <p>注意:支持Windows Server 2012/Windows 8及以上系统,且未安装其它环境</p>
</div> </div>
</div> </div>
</div>{/if} </div>{/if}

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,16 +12,16 @@ INSERT INTO `cloud_config` (`key`, `value`) VALUES
('bt_key', ''), ('bt_key', ''),
('whitelist', '0'), ('whitelist', '0'),
('download_page', '1'), ('download_page', '1'),
('new_version', '11.1.0'), ('new_version', '11.5.0'),
('update_msg', '暂无更新日志'), ('update_msg', '暂无更新日志'),
('update_date', '2025-09-21'), ('update_date', '2026-01-19'),
('new_version_win', '8.2.2'), ('new_version_win', '8.5.1'),
('update_msg_win', '暂无更新日志'), ('update_msg_win', '暂无更新日志'),
('update_date_win', '2024-12-30'), ('update_date_win', '2026-01-19'),
('new_version_en', '7.0.25'), ('new_version_en', '7.0.25'),
('update_msg_en', '暂无更新日志'), ('update_msg_en', '暂无更新日志'),
('update_date_en', '2025-09-10'), ('update_date_en', '2025-09-10'),
('new_version_btm', '2.3.2'), ('new_version_btm', '2.3.3'),
('update_msg_btm', '暂无更新日志'), ('update_msg_btm', '暂无更新日志'),
('update_date_btm', '2025-08-12'), ('update_date_btm', '2025-08-12'),
('updateall_type', '0'), ('updateall_type', '0'),

View File

@ -14,6 +14,83 @@ if [ $(whoami) != "root" ];then
exit 1; exit 1;
fi fi
MEM_TOTAL=$(free -m|grep Mem|awk '{print $2}')
if [ "${MEM_TOTAL}" ] ;then
if [ "${MEM_TOTAL}" -lt "450" ];then
echo "====================================================="
free -m
echo "当前服务器内存为:${MEM_TOTAL}MB"
echo "检测到当前服务器内存小于450MB无法安装宝塔面板"
echo "建议更换内存大于等于512MB的服务器安装宝塔面板"
echo "====================================================="
exit 1
fi
fi
Fix_Apt_Lock(){
[ ! -f "/usr/bin/apt-get" ] && return 0
echo "检查 apt/dpkg 锁状态..."
# # 1. 停止自动更新服务
# if systemctl is-active --quiet unattended-upgrades 2>/dev/null; then
# echo "停止 unattended-upgrades 服务..."
# systemctl stop unattended-upgrades 2>/dev/null
# systemctl disable unattended-upgrades 2>/dev/null
# sleep 2
# fi
# 2. 等待其他 apt/dpkg 进程最多等待60秒
local wait=0
while fuser /var/lib/dpkg/lock >/dev/null 2>&1 || \
fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 || \
fuser /var/lib/apt/lists/lock >/dev/null 2>&1 || \
fuser /var/cache/apt/archives/lock >/dev/null 2>&1; do
if [ ${wait} -eq 0 ]; then
echo "检测到 apt/dpkg 正在使用中,等待完成..."
ps aux | grep -E 'apt-get|apt |dpkg|unattended' | grep -v grep | awk '{print " PID "$2": "$11}' || true
fi
[ ${wait} -ge 60 ] && break
sleep 3
wait=$((wait + 3))
done
# 3. 如果还有锁,强制清理
if fuser /var/lib/dpkg/lock >/dev/null 2>&1 || \
fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 || \
fuser /var/lib/apt/lists/lock >/dev/null 2>&1 || \
fuser /var/cache/apt/archives/lock >/dev/null 2>&1; then
echo "强制清理 apt/dpkg 锁..."
# 强制终止进程
pkill -9 unattended-upgr 2>/dev/null
pkill -9 apt-get 2>/dev/null
pkill -9 apt 2>/dev/null
pkill -9 dpkg 2>/dev/null
sleep 1
# 删除所有锁文件
rm -f /var/lib/dpkg/lock-frontend
rm -f /var/lib/dpkg/lock
rm -f /var/lib/apt/lists/lock
rm -f /var/cache/apt/archives/lock
# 修复 dpkg 状态
echo "修复 dpkg 状态..."
dpkg --configure -a 2>/dev/null || true
apt-get install -f -y 2>/dev/null || true
fi
echo "apt/dpkg 锁检查完成"
return 0
}
is64bit=$(getconf LONG_BIT) is64bit=$(getconf LONG_BIT)
if [ "${is64bit}" != '64' ];then if [ "${is64bit}" != '64' ];then
echo "抱歉, 当前面板版本不支持32位系统, 请使用64位系统或安装宝塔5.9!"; echo "抱歉, 当前面板版本不支持32位系统, 请使用64位系统或安装宝塔5.9!";
@ -45,10 +122,10 @@ if [ "${UBUNTU_NO_LTS}" ];then
exit 1 exit 1
fi fi
DEBIAN_9_C=$(cat /etc/issue|grep Debian|grep -E "8 |9 ") DEBIAN_9_C=$(cat /etc/issue|grep Debian|grep -E "7 |8 |9 ")
if [ "${DEBIAN_9_C}" ];then if [ "${DEBIAN_9_C}" ];then
echo "当前您使用的Debian-8/9官方已经停止支持、无法进行宝塔面板的安装" echo "当前您使用的Debian-7/8/9官方已经停止支持、无法进行宝塔面板的安装"
echo "请使用Debian-11/12进行安装宝塔面板" echo "建议使用Debian-11/12/13进行安装宝塔面板"
exit 1 exit 1
fi fi
@ -197,7 +274,7 @@ Add_lib_Install(){
if [ -f "/etc/os-release" ];then if [ -f "/etc/os-release" ];then
. /etc/os-release . /etc/os-release
OS_V=${VERSION_ID%%.*} OS_V=${VERSION_ID%%.*}
if [ "${ID}" == "debian" ] && [[ "${OS_V}" =~ ^(11|12)$ ]];then if [ "${ID}" == "debian" ] && [[ "${OS_V}" =~ ^(11|12|13)$ ]];then
OS_NAME=${ID} OS_NAME=${ID}
elif [ "${ID}" == "ubuntu" ] && [[ "${OS_V}" =~ ^(22|24)$ ]];then elif [ "${ID}" == "ubuntu" ] && [[ "${OS_V}" =~ ^(22|24)$ ]];then
OS_NAME=${ID} OS_NAME=${ID}
@ -278,9 +355,10 @@ Set_Repo_Url(){
NODE_STATUS=$(echo ${NODE_CHECK}|awk '{print $1}') NODE_STATUS=$(echo ${NODE_CHECK}|awk '{print $1}')
TIME_TOTAL=$(echo ${NODE_CHECK}|awk '{print $2 * 1000}'|cut -d '.' -f 1) TIME_TOTAL=$(echo ${NODE_CHECK}|awk '{print $2 * 1000}'|cut -d '.' -f 1)
if { [ "${NODE_STATUS}" != "200" ] && [ "${NODE_STATUS}" != "301" ]; } || [ "${TIME_TOTAL}" -ge "300" ] || [ "${SOURCE_URL_CHECK}" ]; then if { [ "${NODE_STATUS}" != "200" ] && [ "${NODE_STATUS}" != "301" ]; } || [ "${TIME_TOTAL}" -ge "500" ] || [ "${SOURCE_URL_CHECK}" ]; then
\cp -rpa /etc/apt/sources.list /etc/apt/sources.list.btbackup \cp -rpa /etc/apt/sources.list /etc/apt/sources.list.btbackup
apt_lists=(mirrors.cloud.tencent.com mirrors.163.com repo.huaweicloud.com mirrors.tuna.tsinghua.edu.cn mirrors.aliyun.com mirrors.ustc.edu.cn ) apt_lists=(mirrors.cloud.tencent.com mirrors.163.com repo.huaweicloud.com mirrors.tuna.tsinghua.edu.cn mirrors.aliyun.com mirrors.ustc.edu.cn )
apt_lists=(mirrors.cloud.tencent.com repo.huaweicloud.com mirrors.aliyun.com mirrors.ustc.edu.cn mirrors.163.com)
for list in ${apt_lists[@]}; for list in ${apt_lists[@]};
do do
NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${list} -o /dev/null) NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${list} -o /dev/null)
@ -445,6 +523,11 @@ Set_Centos8_Repo(){
yum install unzip tar -y yum install unzip tar -y
if [ "$?" != "0" ] ;then if [ "$?" != "0" ] ;then
if [ -d "/etc/yum.repos.d" ];then
mkdir -p /etc/yum.repos.d
fi
if [ -z "${download_Url}" ];then if [ -z "${download_Url}" ];then
download_Url="http://download.bt.cn" download_Url="http://download.bt.cn"
fi fi
@ -484,19 +567,20 @@ get_node_url(){
echo '---------------------------------------------'; echo '---------------------------------------------';
echo "Selected download node..."; echo "Selected download node...";
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 https://download.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 https://download.bt.cn http://115.231.130.125:5880);
CURL_CHECK=$(which curl) CURL_CHECK=$(which curl)
if [ "$?" == "0" ];then if [ "$?" == "0" ];then
CN_CHECK=$(curl -sS --connect-timeout 10 -m 10 https://api.bt.cn/api/isCN) CN_CHECK=$(curl -sS --connect-timeout 10 -m 10 https://api.bt.cn/api/isCN)
if [ "${CN_CHECK}" == "True" ];then if [ "${CN_CHECK}" == "True" ];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); 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 http://115.231.130.125:5880);
else else
PING6_CHECK=$(ping6 -c 2 -W 2 download.bt.cn &> /dev/null && echo "yes" || echo "no") PING6_CHECK=$(ping6 -c 2 -W 2 download.bt.cn &> /dev/null && echo "yes" || echo "no")
if [ "${PING6_CHECK}" == "yes" ];then if [ "${PING6_CHECK}" == "yes" ];then
nodes=(https://dg2.bt.cn https://download.bt.cn https://cf1-node.aapanel.com); nodes=(https://dg2.bt.cn https://download.bt.cn https://cf1-node.aapanel.com);
else else
nodes=(https://cf1-node.aapanel.com https://download.bt.cn https://na1-node.bt.cn https://jp1-node.bt.cn https://dg2.bt.cn); #nodes=(https://cf1-node.aapanel.com https://download.bt.cn https://na1-node.bt.cn https://jp1-node.bt.cn https://dg2.bt.cn);
nodes=(https://cf1-node.aapanel.com https://cf1-node.aapanel.com https://download.bt.cn https://dg2.bt.cn https://jp1-node.bt.cn);
fi fi
fi fi
fi fi
@ -631,7 +715,7 @@ Install_RPM_Pack(){
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
#yum remove -y python-requests python3-requests python-greenlet python3-greenlet #yum remove -y python-requests python3-requests python-greenlet python3-greenlet
yumPacks="libcurl-devel wget tar gcc make zip unzip openssl openssl-devel gcc libxml2 libxml2-devel libxslt* zlib zlib-devel libjpeg-devel libpng-devel libwebp libwebp-devel freetype freetype-devel lsof pcre pcre-devel vixie-cron crontabs icu libicu-devel c-ares libffi-devel bzip2-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel qrencode at mariadb rsyslog net-tools firewalld" yumPacks="libcurl-devel wget tar gcc make zip unzip openssl openssl-devel gcc libxml2 libxml2-devel libxslt* zlib zlib-devel libjpeg-devel libpng-devel libwebp libwebp-devel freetype freetype-devel lsof pcre pcre-devel vixie-cron crontabs icu libicu-devel c-ares libffi-devel bzip2-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel qrencode at rsyslog net-tools firewalld"
yum install -y ${yumPacks} yum install -y ${yumPacks}
for yumPack in ${yumPacks} for yumPack in ${yumPacks}
@ -646,6 +730,10 @@ Install_RPM_Pack(){
dnf install -y redhat-rpm-config dnf install -y redhat-rpm-config
fi fi
# if [ ! -f "/usr/bin/mysql" ] && [ -f "/usr/sbin/mysql" ];then
# yum install
# fi
ALI_OS=$(cat /etc/redhat-release |grep "Alibaba Cloud Linux release 3") ALI_OS=$(cat /etc/redhat-release |grep "Alibaba Cloud Linux release 3")
if [ -z "${ALI_OS}" ];then if [ -z "${ALI_OS}" ];then
yum install epel-release -y yum install epel-release -y
@ -673,6 +761,7 @@ Install_Deb_Pack(){
apt-get install bash -y apt-get install bash -y
if [ -f "/usr/bin/bash" ];then if [ -f "/usr/bin/bash" ];then
ln -sf /usr/bin/bash /bin/sh ln -sf /usr/bin/bash /bin/sh
ln -sf /usr/bin/bash /usr/bin/sh
fi fi
apt-get install ruby -y apt-get install ruby -y
apt-get install lsb-release -y apt-get install lsb-release -y
@ -693,7 +782,7 @@ Install_Deb_Pack(){
apt-get install curl -y apt-get install curl -y
fi fi
debPacks="wget curl libcurl4-openssl-dev gcc make zip unzip tar openssl libssl-dev gcc libxml2 libxml2-dev zlib1g zlib1g-dev libjpeg-dev libpng-dev lsof libpcre3 libpcre3-dev cron net-tools swig build-essential libffi-dev libbz2-dev libncurses-dev libsqlite3-dev libreadline-dev tk-dev libgdbm-dev libdb-dev libdb++-dev libpcap-dev xz-utils git qrencode sqlite3 at mariadb-client rsyslog net-tools ufw"; debPacks="wget curl libcurl4-openssl-dev gcc make zip unzip tar openssl libssl-dev gcc libxml2 libxml2-dev zlib1g zlib1g-dev libjpeg-dev libpng-dev lsof libpcre3 libpcre3-dev cron net-tools swig build-essential libffi-dev libbz2-dev libncurses-dev libsqlite3-dev libreadline-dev tk-dev libgdbm-dev libdb-dev libdb++-dev libpcap-dev xz-utils git qrencode sqlite3 at rsyslog net-tools ufw";
apt-get install -y $debPacks --force-yes apt-get install -y $debPacks --force-yes
for debPack in ${debPacks} for debPack in ${debPacks}
@ -703,7 +792,11 @@ Install_Deb_Pack(){
apt-get install -y $debPack apt-get install -y $debPack
fi fi
done done
if [ ! -f "/usr/bin/mysql" ] && [ -f "/usr/sbin/mysql" ];then
apt-get install mysql-client -y
fi
if [ ! -d '/etc/letsencrypt' ];then if [ ! -d '/etc/letsencrypt' ];then
mkdir -p /etc/letsencryp mkdir -p /etc/letsencryp
mkdir -p /var/spool/cron mkdir -p /var/spool/cron
@ -713,6 +806,17 @@ Install_Deb_Pack(){
fi fi
fi fi
} }
Install_Other_Pack(){
if [ -f "/sbin/apk" ];then
sed -i 's/dl-cdn.alpinelinux.org/mirrors.tencent.com/g' /etc/apk/repositories
apk update
apk upgrade
apk add openrc openssh curl curl-dev libffi-dev openssl-dev shadow bash zlib-dev g++ make sqlite-dev libpcap-dev jpeg-dev dos2unix libev-dev build-base linux-headers gd-dev bash openssl libxml2-dev libxslt-dev jemalloc-dev luajit luajit-dev
LOCK_PIP="True"
fi
}
Get_Versions(){ Get_Versions(){
redhat_version_file="/etc/redhat-release" redhat_version_file="/etc/redhat-release"
deb_version_file="/etc/issue" deb_version_file="/etc/issue"
@ -812,7 +916,6 @@ Get_Versions(){
} }
Install_Python_Lib(){ Install_Python_Lib(){
if [ -f "/www/server/panel/pyenv/bin/python3.7" ];then if [ -f "/www/server/panel/pyenv/bin/python3.7" ];then
python_file_date=$(date -r /www/server/panel/pyenv/bin/python3.7 +"%Y") python_file_date=$(date -r /www/server/panel/pyenv/bin/python3.7 +"%Y")
if [ "${python_file_date}" -lt "2021" ];then if [ "${python_file_date}" -lt "2021" ];then
@ -821,6 +924,19 @@ Install_Python_Lib(){
fi fi
curl -Ss --connect-timeout 3 -m 60 $download_Url/install/pip_select.sh|bash curl -Ss --connect-timeout 3 -m 60 $download_Url/install/pip_select.sh|bash
if [ "${LOCK_PIP}" ];then
if [ ! -d ~/.pip ];then
mkdir -p ~/.pip
fi
cat > ~/.pip/pip.conf <<EOF
[global]
index-url = https://mirrors.tencent.com/pypi/simple
[install]
trusted-host = mirrors.tencent.com
EOF
fi
pyenv_path="/www/server/panel" pyenv_path="/www/server/panel"
if [ -f $pyenv_path/pyenv/bin/python ];then if [ -f $pyenv_path/pyenv/bin/python ];then
is_ssl=$($python_bin -c "import ssl" 2>&1|grep cannot) is_ssl=$($python_bin -c "import ssl" 2>&1|grep cannot)
@ -1052,6 +1168,7 @@ Install_Bt(){
yum install unzip -y yum install unzip -y
elif [ "${PM}" = "apt-get" ]; then elif [ "${PM}" = "apt-get" ]; then
apt-get update apt-get update
Fix_Apt_Lock
apt-get install unzip -y 2>&1|tee /tmp/apt_install_log.log apt-get install unzip -y 2>&1|tee /tmp/apt_install_log.log
UNZIP_CHECK=$(which unzip) UNZIP_CHECK=$(which unzip)
if [ "$?" != "0" ];then if [ "$?" != "0" ];then
@ -1110,7 +1227,10 @@ Install_Bt(){
chmod +x /etc/init.d/bt chmod +x /etc/init.d/bt
chmod -R 600 ${setup_path}/server/panel chmod -R 600 ${setup_path}/server/panel
chmod -R +x ${setup_path}/server/panel/script chmod -R +x ${setup_path}/server/panel/script
chmod -R 700 $pyenv_path/pyenv/bin
ln -sf /etc/init.d/bt /usr/bin/bt ln -sf /etc/init.d/bt /usr/bin/bt
chmod +x /www/server/panel/script/btcli.py
ln -sf /www/server/panel/script/btcli.py /usr/bin/btcli
echo "${panelPort}" > ${setup_path}/server/panel/data/port.pl echo "${panelPort}" > ${setup_path}/server/panel/data/port.pl
wget -O /etc/init.d/bt ${download_Url}/install/src/bt7.init -T 15 wget -O /etc/init.d/bt ${download_Url}/install/src/bt7.init -T 15
wget -O /www/server/panel/init.sh ${download_Url}/install/src/bt7.init -T 15 wget -O /www/server/panel/init.sh ${download_Url}/install/src/bt7.init -T 15
@ -1163,7 +1283,8 @@ Set_Bt_Panel(){
auth_path=$SAFE_PATH auth_path=$SAFE_PATH
echo "/${auth_path}" > ${admin_auth} echo "/${auth_path}" > ${admin_auth}
fi fi
chmod -R 700 $pyenv_path/pyenv/bin
btpip install asn1crypto==1.5.1 cbor2==5.4.6
if [ ! -f "/www/server/panel/pyenv/n.pl" ];then if [ ! -f "/www/server/panel/pyenv/n.pl" ];then
btpip install docxtpl==0.16.7 btpip install docxtpl==0.16.7
/www/server/panel/pyenv/bin/pip3 install pymongo /www/server/panel/pyenv/bin/pip3 install pymongo
@ -1215,8 +1336,14 @@ Set_Bt_Panel(){
/etc/init.d/bt start /etc/init.d/bt start
sleep 5 sleep 5
isStart=$(ps aux |grep 'BT-Panel'|grep -v grep|awk '{print $2}') isStart=$(ps aux |grep 'BT-Panel'|grep -v grep|awk '{print $2}')
LOCAL_CURL=$(curl 127.0.0.1:${panelPort}/login 2>&1 |grep -i html)
if [ -z "${isStart}" ];then if [ -f "/www/server/panel/data/ssl.pl" ];then
LOCAL_CURL=$(curl -k https://127.0.0.1:${panelPort}/login 2>&1 |grep -i html)
else
LOCAL_CURL=$(curl 127.0.0.1:${panelPort}/login 2>&1 |grep -i html)
fi
if [ -z "${isStart}" ] && [ -z "${LOCAL_CURL}" ];then
/etc/init.d/bt 22 /etc/init.d/bt 22
cd /www/server/panel/pyenv/bin cd /www/server/panel/pyenv/bin
touch t.pl touch t.pl
@ -1240,12 +1367,16 @@ Set_Firewall(){
if [ "${PM}" = "apt-get" ]; then if [ "${PM}" = "apt-get" ]; then
#apt-get install -y ufw #apt-get install -y ufw
if [ -f "/usr/sbin/ufw" ];then if [ -f "/usr/sbin/ufw" ];then
if [ "${PANEL_PORT}" ];then
ufw allow ${PANEL_PORT}/tcp
fi
ufw allow 20/tcp ufw allow 20/tcp
ufw allow 21/tcp ufw allow 21/tcp
ufw allow 22/tcp ufw allow 22/tcp
ufw allow 80/tcp ufw allow 80/tcp
ufw allow 443/tcp ufw allow 443/tcp
ufw allow 888/tcp ufw allow 888/tcp
ufw allow 8888/tcp
ufw allow ${panelPort}/tcp ufw allow ${panelPort}/tcp
ufw allow ${sshPort}/tcp ufw allow ${sshPort}/tcp
ufw allow 39000:40000/tcp ufw allow 39000:40000/tcp
@ -1288,6 +1419,10 @@ Set_Firewall(){
firewall-cmd --permanent --zone=public --add-port=22/tcp > /dev/null 2>&1 firewall-cmd --permanent --zone=public --add-port=22/tcp > /dev/null 2>&1
firewall-cmd --permanent --zone=public --add-port=80/tcp > /dev/null 2>&1 firewall-cmd --permanent --zone=public --add-port=80/tcp > /dev/null 2>&1
firewall-cmd --permanent --zone=public --add-port=443/tcp > /dev/null 2>&1 firewall-cmd --permanent --zone=public --add-port=443/tcp > /dev/null 2>&1
firewall-cmd --permanent --zone=public --add-port=8888/tcp > /dev/null 2>&1
if [ "${PANEL_PORT}" ];then
firewall-cmd --permanent --zone=public --add-port=${PANEL_PORT}/tcp > /dev/null 2>&10
fi
firewall-cmd --permanent --zone=public --add-port=${panelPort}/tcp > /dev/null 2>&1 firewall-cmd --permanent --zone=public --add-port=${panelPort}/tcp > /dev/null 2>&1
firewall-cmd --permanent --zone=public --add-port=${sshPort}/tcp > /dev/null 2>&1 firewall-cmd --permanent --zone=public --add-port=${sshPort}/tcp > /dev/null 2>&1
firewall-cmd --permanent --zone=public --add-port=39000-40000/tcp > /dev/null 2>&1 firewall-cmd --permanent --zone=public --add-port=39000-40000/tcp > /dev/null 2>&1
@ -1378,9 +1513,68 @@ Setup_Count(){
fi fi
echo /www > /var/bt_setupPath.conf echo /www > /var/bt_setupPath.conf
} }
Start_Ip_Cert_Async(){
IP_SSL_PID=""
if [ -z "${ipv4_address}" ];then
return
fi
if [ "$SET_SSL" == "true" ];then
if [ -f "/www/server/panel/script/auto_apply_ip_ssl.py" ];then
acme_connect_url="https://acme-v02.api.letsencrypt.org"
acme_http_code=$(curl -sS --connect-timeout 2 -m 60 -o /dev/null -w "%{http_code}" "$acme_connect_url")
if [ "$acme_http_code" == "200" ];then
echo "正在后台开启受信任宝塔面板ip证书..."
(
timeout 60 $pyenv_path/pyenv/bin/python3.7 /www/server/panel/script/auto_apply_ip_ssl.py -ips ${ipv4_address} -path /www/server/panel/ssl > /tmp/auto_apply_ip_ssl.log 2>&1
echo $? > /tmp/ip_ssl_exit_code.pl
) &
IP_SSL_PID=$!
fi
fi
fi
}
Check_Ip_Cert_Async(){
if [ "$SET_SSL" != "true" ];then
return
fi
if [ -z "$IP_SSL_PID" ]; then
if [ "$acme_http_code" != "200" ];then
echo "受信ip证书申请失败exit code=$acme_http_code"
echo "转为使用默认自签证书后续可手动在面板设置中重新使用Let's encrypt申请ip证书"
return
fi
echo "受信宝塔面板ip证书开启成功"
/etc/init.d/bt restart
return
fi
echo "正在检查受信宝塔面板ip证书开启状态..."
wait $IP_SSL_PID
if [ -f "/tmp/ip_ssl_exit_code.pl" ]; then
rc=$(cat /tmp/ip_ssl_exit_code.pl)
rm -f /tmp/ip_ssl_exit_code.pl
else
rc=1
fi
if [ $rc -eq 0 ]; then
echo "受信宝塔面板ip证书开启成功"
/etc/init.d/bt restart
elif [ $rc -eq 124 ]; then
echo "受信ip证书申请超时60秒"
echo "转为使用默认自签证书后续可手动在面板设置中重新使用Let's encrypt申请ip证书"
else
echo "受信ip证书申请失败exit code=$rc"
echo "转为使用默认自签证书后续可手动在面板设置中重新使用Let's encrypt申请ip证书"
fi
}
Install_Main(){ Install_Main(){
Ready_Check Ready_Check
#Set_Ssl Set_Ssl
startTime=`date +%s` startTime=`date +%s`
Lock_Clear Lock_Clear
System_Check System_Check
@ -1397,17 +1591,21 @@ Install_Main(){
Install_RPM_Pack Install_RPM_Pack
elif [ "${PM}" = "apt-get" ]; then elif [ "${PM}" = "apt-get" ]; then
Install_Deb_Pack Install_Deb_Pack
else
Install_Other_Pack
fi fi
Set_Firewall
Install_Python_Lib Install_Python_Lib
Install_Bt Install_Bt
Get_Ip_Address
Start_Ip_Cert_Async
Set_Bt_Panel Set_Bt_Panel
Service_Add Service_Add
Set_Firewall
Get_Ip_Address Check_Ip_Cert_Async
Setup_Count ${IDC_CODE} Setup_Count ${IDC_CODE}
Add_lib_Install Add_lib_Install
} }

Binary file not shown.

View File

@ -219,6 +219,8 @@ fi
btpip uninstall enum34 -y btpip uninstall enum34 -y
btpip install asn1crypto==1.5.1 cbor2==5.4.6
GEOIP_C=$(echo $pip_list|grep geoip2) GEOIP_C=$(echo $pip_list|grep geoip2)
if [ -z "${GEOIP_C}" ];then if [ -z "${GEOIP_C}" ];then
btpip install geoip2==4.7.0 btpip install geoip2==4.7.0

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,11 +7,14 @@ 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('/wpanel/get_unbinding', 'api/return_success');
Route::post('/bt_cert', 'api/bt_cert'); 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');
@ -103,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');
@ -152,6 +157,7 @@ Route::group('api', function () {
Route::post('/panel/submit_expand_pack_used', 'api/return_success'); Route::post('/panel/submit_expand_pack_used', 'api/return_success');
Route::get('/panel/getLatestOfficialVersion', 'api/get_version_en'); Route::get('/panel/getLatestOfficialVersion', 'api/get_version_en');
Route::post('/cert/user/list', 'api/nps_questions'); 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('/Auth/GetCloudToken', 'api/get_auth_token');
Route::post('/cloudtro/version_info', 'api/cloudc_version_info'); Route::post('/cloudtro/version_info', 'api/cloudc_version_info');
@ -161,6 +167,10 @@ Route::group('api', function () {
Route::miss('api/return_error'); Route::miss('api/return_error');
}); });
Route::group('newapi', function () {
Route::miss('api/return_error');
});
Route::get('/admin/verifycode', 'admin/verifycode')->middleware(\think\middleware\SessionInit::class); Route::get('/admin/verifycode', 'admin/verifycode')->middleware(\think\middleware\SessionInit::class);
Route::any('/admin/login', 'admin/login')->middleware(\think\middleware\SessionInit::class); Route::any('/admin/login', 'admin/login')->middleware(\think\middleware\SessionInit::class);
Route::get('/admin/logout', 'admin/logout'); Route::get('/admin/logout', 'admin/logout');

View File

@ -1,217 +0,0 @@
#coding: utf-8
import public,os,sys,json
#获取插件列表(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.获取失败
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):
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')
if not is_php:
public.package_path_append(p_path)
plugin_main = __import__(plugin_name + '_main')
try:
if sys.version_info[0] == 2:
reload(plugin_main)
else:
from imp import reload
reload(plugin_main)
except:
pass
plu = eval('plugin_main.' + plugin_name + '_main()')
if not hasattr(plu, 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 not is_php:
return getattr(plu, def_name)
else:
return None
else:
if not is_php:
data = eval('plu.' + def_name + '(args)')
else:
import panelPHP
args.s = def_name
args.name = plugin_name
data = panelPHP.panelPHP(plugin_name).exec_php_script(args)
return data
#执行模块方法(模块名,方法名,参数)
def module_run(mod_name, def_name, args):
if not mod_name or not def_name: return public.returnMsg(False,'模块名称和模块方法名称不能为空!')
if not path_check(mod_name) or not path_check(def_name): return public.returnMsg(False,'模块名或方法名不能包含特殊符号!')
if 'model_index' in args:
if args.model_index:
mod_file = "{}/{}Model/{}Model.py".format(public.get_class_path(),args.model_index,mod_name)
else:
mod_file = "{}/projectModel/{}Model.py".format(public.get_class_path(),mod_name)
else:
module_list = get_module_list()
for module_dir in module_list:
mod_file = "{}/{}/{}Model.py".format(public.get_class_path(),module_dir,mod_name)
if os.path.exists(mod_file): break
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:
run_object = getattr(def_object.main(),def_name,None)
except:
return public.returnMsg(False,'模块入口实例化失败' % mod_name)
if not run_object: return public.returnMsg(False,'在[%s]模块中找不到[%s]方法' % (mod_name,def_name))
if 'module_get_object' in args and args.module_get_object == 1:
return run_object
result = run_object(args)
return result
#获取模块文件夹列表
def get_module_list():
list = []
class_path = public.get_class_path()
f_list = os.listdir(class_path)
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
#检查路径是否合法
def path_check(path):
list = ["./","..",",",";",":","?","'","\"","<",">","|","\\","\n","\r","\t","\b","\a","\f","\v","*","%","&","$","#","@","!","~","`","^","(",")","+","=","{","}","[","]"]
for i in path:
if i in list:
return False
return True
#数据加密
def db_encrypt(data):
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):
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')

View File

@ -18,6 +18,8 @@
- 全局搜索替换 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/update6.sh => http://www.example.com/install/update6.sh
@ -98,8 +100,6 @@
- script/upgrade_panel_optimized.py 文件def get_home_node(url): 下面加上return url - script/upgrade_panel_optimized.py 文件def get_home_node(url): 下面加上return url
- tools.py 文件u_input == 16下面的public.get_url()替换成public.GetConfigValue('home')
- install/install_soft.sh 在. 执行之前加入以下代码 - install/install_soft.sh 在. 执行之前加入以下代码
```shell ```shell
@ -111,19 +111,11 @@
- 去除无用的定时任务task.py 文件 删除以下几行 - 去除无用的定时任务task.py 文件 删除以下几行
"check_panel_msg": self.check_panel_msg, check_panel_msg,
"update_software_list": self.update_software_list, refresh_domain_cache,
"refresh_domain_cache": self.refresh_domain_cache, task_ssh_error_count,
PluginLoader.daemon_panel()
self.check_node_status()
self.upload_send_num()
- script/site_task.py 删除flush_ssh_log()
- [可选]去除各种计算题复制bt.js到 BTPanel/static/ ,在 BTPanel/templates/default/software.html 的 \<script\>window.vite_public_request_token 前面加入 - [可选]去除各种计算题复制bt.js到 BTPanel/static/ ,在 BTPanel/templates/default/software.html 的 \<script\>window.vite_public_request_token 前面加入

View File

@ -4,83 +4,7 @@
官方更新包下载链接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替换成任意其他域名。
Windows版宝塔由于加密文件太多无法全部解密因此无法做到全开源。
- 删除PluginLoader.pyd将win/PluginLoader.py复制到class文件夹
- 批量解密模块文件:执行 php think decrypt classdir <面板class文件夹路径> - 批量解密模块文件:执行 php think decrypt classdir <面板class文件夹路径>
- 新版vite页面去除需求反馈、各种广告、计算题等执行 php think cleanvitejs <面板assets/static/js路径>
- 全局搜索替换 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'
- script/reload_check.py 文件在第2行插入sys.exit()
- 去除无用的定时任务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行
- tools.py删除#尝试删除本地hosts文件中的宝塔域名解析
- 去除面板日志上报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()这一行
- [可选]上传文件默认选中覆盖在BTPanel/static/js/upload-drog.jsid="all_operation"加checked属性