This commit is contained in:
flucont 2022-06-14 15:50:39 +08:00
parent 4689707987
commit 2ed20aba20
35 changed files with 2433 additions and 106 deletions

View File

@ -36,13 +36,17 @@
- 在`系统基本设置`修改宝塔面板接口设置。你需要一个官方最新脚本安装并绑定账号的宝塔面板,用于获取最新插件列表及插件包。并根据界面提示安装好专用插件。
- 在`定时任务设置`执行所显示的命令从宝塔官方获取最新的插件列表并批量下载插件包(增量更新)。当然你也可以去插件列表,一个一个点击下载。
- 在public/install/src和update文件夹里面分别是bt安装包和更新包解压后源码里面全部的 www.example.com 替换成你自己搭建的云端域名然后重新打包。可使用VSCode等支持批量替换的软件。
- 将bt安装脚本public/install/install_6.0.sh和更新脚本update6.sh里面的 www.example.com 替换成你自己搭建的云端域名。
- 在public/install/src和update文件夹里面分别是Linux面板安装包和更新包解压后源码里面全部的 www.example.com 替换成你自己搭建的云端域名如果云端用了强制https也需要单独改然后重新打包。可使用VSCode等支持批量替换的软件。
- 在public/win/panel/panel_x.x.x.zip是Windows面板的更新包同样方法替换域名。
- Linux面板安装脚本public/install/install_6.0.sh和更新脚本update6.sh里面的 www.example.com 替换成你自己搭建的云端域名。
- Windows面板更新脚本 public/win/install/panel_update.py、public/win/panel/data/setup.py、api.py 里面的 www.example.com 替换成你自己搭建的云端域名。
- 访问网站`/download`查看使用此第三方云端的一键安装脚本
## 其他
- [bt官方更新包修改记录](./wiki/update.md)
- [Linux面板官方更新包修改记录](./wiki/update.md)
- [Windows面板官方更新包修改记录](./wiki/updatewin.md)
- 宝塔面板官方版与此第三方云端版对比:

View File

@ -25,16 +25,27 @@ class UpdateAll extends Command
$res = Db::name('config')->cache('configs',0)->column('value','key');
Config::set($res, 'sys');
if(config_get('bt_url')){
$this->process_plugins($input, $output, 'Linux');
}
if(config_get('wbt_url')){
$this->process_plugins($input, $output, 'Windows');
}
config_set('runtime', date('Y-m-d H:i:s'));
}
private function process_plugins(Input $input, Output $output, $os){
//刷新插件列表
if(!$this->refresh_plugin_list($input, $output)){
if(!$this->refresh_plugin_list($input, $output, $os)){
return;
}
$count = 0;
$type = intval(config_get('updateall_type'));
$type = intval(config_get($os=='Windows'?'updateall_type_win':'updateall_type'));
$json_arr = Plugins::get_plugin_list();
$json_arr = Plugins::get_plugin_list($os);
//循环下载缺少的插件
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;
@ -43,18 +54,18 @@ class UpdateAll extends Command
$ver = $version['m_version'].'.'.$version['version'];
if(isset($version['download'])){
if(!file_exists(get_data_dir().'plugins/other/'.$version['download'])){
if(!$this->download_plugin($input, $output, $plugin['name'], $ver)){
if(!$this->download_plugin($input, $output, $plugin['name'], $ver, $os)){
sleep(1);
$this->download_plugin($input, $output, $plugin['name'], $ver);
$this->download_plugin($input, $output, $plugin['name'], $ver, $os);
}
sleep(1);
$count++;
}
}else{
if(!file_exists(get_data_dir().'plugins/package/'.$plugin['name'].'-'.$ver.'.zip')){
if(!$this->download_plugin($input, $output, $plugin['name'], $ver)){
if(!file_exists(get_data_dir($os).'plugins/package/'.$plugin['name'].'-'.$ver.'.zip')){
if(!$this->download_plugin($input, $output, $plugin['name'], $ver, $os)){
sleep(1);
$this->download_plugin($input, $output, $plugin['name'], $ver);
$this->download_plugin($input, $output, $plugin['name'], $ver, $os);
}
sleep(1);
$count++;
@ -62,29 +73,15 @@ class UpdateAll extends Command
}
}
}
$imgcount = 0;
//循环下载缺少的插件图片
/*foreach($json_arr['list'] as $plugin){
if(isset($plugin['min_image']) && strpos($plugin['min_image'], 'fname=')){
$fname = substr($plugin['min_image'], strpos($plugin['min_image'], '?fname=')+7);
if(!file_exists(get_data_dir().'plugins/other/'.$fname)){
$this->download_plugin_image($input, $output, $fname);
sleep(1);
$imgcount++;
}
}
}*/
$output->writeln('本次成功下载'.$count.'个插件'.($imgcount>0?''.$imgcount.'个图片':''));
config_set('runtime', date('Y-m-d H:i:s'));
$output->writeln($os.'本次成功下载'.$count.'个插件');
}
private function refresh_plugin_list(Input $input, Output $output){
private function refresh_plugin_list(Input $input, Output $output, $os){
try{
Plugins::refresh_plugin_list();
Db::name('log')->insert(['uid' => 1, 'action' => '刷新插件列表', 'data' => '刷新插件列表成功', 'addtime' => date("Y-m-d H:i:s")]);
$output->writeln('刷新插件列表成功');
Plugins::refresh_plugin_list($os);
Db::name('log')->insert(['uid' => 1, 'action' => '刷新插件列表', 'data' => '刷新'.$os.'插件列表成功', 'addtime' => date("Y-m-d H:i:s")]);
$output->writeln('刷新'.$os.'插件列表成功');
return true;
}catch(\Exception $e){
$output->writeln($e->getMessage());
@ -93,12 +90,12 @@ class UpdateAll extends Command
}
}
private function download_plugin(Input $input, Output $output, $plugin_name, $version){
private function download_plugin(Input $input, Output $output, $plugin_name, $version, $os){
$fullname = $plugin_name.'-'.$version;
try{
Plugins::download_plugin($plugin_name, $version);
Db::name('log')->insert(['uid' => 1, 'action' => '下载插件', 'data' => $fullname, 'addtime' => date("Y-m-d H:i:s")]);
$output->writeln('下载插件: '.$fullname.' 成功');
Plugins::download_plugin($plugin_name, $version, $os);
Db::name('log')->insert(['uid' => 1, 'action' => '下载插件', 'data' => $fullname.' os:'.$os, 'addtime' => date("Y-m-d H:i:s")]);
$output->writeln('下载'.$os.'插件: '.$fullname.' 成功');
return true;
}catch(\Exception $e){
$output->writeln($fullname.' '.$e->getMessage());

View File

@ -2,8 +2,8 @@
// 应用公共文件
use think\facade\Db;
function get_data_dir(){
return app()->getRootPath().'data/';
function get_data_dir($os = 'Linux'){
return app()->getRootPath().'data/'.($os == 'Windows' ? 'win/' : '');
}

View File

@ -133,7 +133,7 @@ class Admin extends BaseController
if(!$bt_url || !$bt_key)return json(['code'=>-1, 'msg'=>'参数不能为空']);
$btapi = new Btapi($bt_url, $bt_key);
$result = $btapi->get_config();
if($result && isset($result['status']) && $result['status']==1){
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'=>'面板连接测试成功!']);
@ -157,11 +157,25 @@ class Admin extends BaseController
return view();
}
public function pluginswin(){
$typelist = [];
$json_arr = Plugins::get_plugin_list('Windows');
if($json_arr){
foreach($json_arr['type'] as $type){
$typelist[$type['id']] = $type['title'];
}
}
View::assign('typelist', $typelist);
return view();
}
public function plugins_data(){
$type = input('post.type/d');
$keyword = input('post.keyword', null, 'trim');
$os = input('get.os');
if(!$os) $os = 'Linux';
$json_arr = Plugins::get_plugin_list();
$json_arr = Plugins::get_plugin_list($os);
if(!$json_arr) return json([]);
$typelist = [];
@ -184,7 +198,7 @@ class Admin extends BaseController
$versions[] = ['status'=>$status, 'type'=>1, 'version'=>$ver, 'download'=>$version['download'], 'md5'=>$version['md5']];
}else{
$status = false;
if(file_exists(get_data_dir().'plugins/package/'.$plugin['name'].'-'.$ver.'.zip')){
if(file_exists(get_data_dir($os).'plugins/package/'.$plugin['name'].'-'.$ver.'.zip')){
$status = true;
}
$versions[] = ['status'=>$status, 'type'=>0, 'version'=>$ver];
@ -209,10 +223,12 @@ class Admin extends BaseController
public function download_plugin(){
$name = input('post.name', null, 'trim');
$version = input('post.version', null, 'trim');
$os = input('post.os');
if(!$os) $os = 'Linux';
if(!$name || !$version) return json(['code'=>-1, 'msg'=>'参数不能为空']);
try{
Plugins::download_plugin($name, $version);
Db::name('log')->insert(['uid' => 0, 'action' => '下载插件', 'data' => $name.'-'.$version, 'addtime' => date("Y-m-d H:i:s")]);
Plugins::download_plugin($name, $version, $os);
Db::name('log')->insert(['uid' => 0, 'action' => '下载插件', 'data' => $name.'-'.$version.' os:'.$os, 'addtime' => date("Y-m-d H:i:s")]);
return json(['code'=>0,'msg'=>'下载成功']);
}catch(\Exception $e){
return json(['code'=>-1, 'msg'=>$e->getMessage()]);
@ -220,9 +236,11 @@ class Admin extends BaseController
}
public function refresh_plugins(){
$os = input('get.os');
if(!$os) $os = 'Linux';
try{
Plugins::refresh_plugin_list();
Db::name('log')->insert(['uid' => 0, 'action' => '刷新插件列表', 'data' => '刷新插件列表成功', 'addtime' => date("Y-m-d H:i:s")]);
Plugins::refresh_plugin_list($os);
Db::name('log')->insert(['uid' => 0, 'action' => '刷新插件列表', 'data' => '刷新'.$os.'插件列表成功', 'addtime' => date("Y-m-d H:i:s")]);
return json(['code'=>0,'msg'=>'获取最新插件列表成功!']);
}catch(\Exception $e){
return json(['code'=>-1, 'msg'=>$e->getMessage()]);

View File

@ -22,18 +22,40 @@ class Api extends BaseController
return json($json_arr);
}
//获取插件列表(win)
public function get_plugin_list_win(){
if(!$this->checklist()) return '';
$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_file = get_data_dir('Windows').'config/plugin_list.json';
if(file_exists($json_file)){
$data = file_get_contents($json_file);
$json_arr = json_decode($data, true);
if($json_arr){
return json($json_arr);
}
}
return json((object)[]);
}
//下载插件包
public function download_plugin(){
$plugin_name = input('post.name');
$version = input('post.version');
$os = input('post.os');
if(!$plugin_name || !$version){
return '参数不能为空';
}
if(!in_array($os,['Windows','Linux'])) $os = 'Linux';
if(!preg_match('/^[a-zA-Z0-9_]+$/', $plugin_name) || !preg_match('/^[0-9.]+$/', $version)){
return '参数不正确';
}
if(!$this->checklist()) '你的服务器被禁止使用此云端';
$filepath = get_data_dir().'plugins/package/'.$plugin_name.'-'.$version.'.zip';
$filepath = get_data_dir($os).'plugins/package/'.$plugin_name.'-'.$version.'.zip';
if(file_exists($filepath)){
$filename = $plugin_name.'.zip';
$this->output_file($filepath, $filename);
@ -46,19 +68,21 @@ class Api extends BaseController
public function download_plugin_main(){
$plugin_name = input('post.name');
$version = input('post.version');
$os = input('post.os');
if(!$plugin_name || !$version){
return '参数不能为空';
}
if(!in_array($os,['Windows','Linux'])) $os = 'Linux';
if(!preg_match('/^[a-zA-Z0-9_]+$/', $plugin_name) || !preg_match('/^[0-9.]+$/', $version)){
return '参数不正确';
}
if(!$this->checklist()) '你的服务器被禁止使用此云端';
$filepath = get_data_dir().'plugins/main/'.$plugin_name.'-'.$version.'.dat';
$filepath = get_data_dir($os).'plugins/main/'.$plugin_name.'-'.$version.'.dat';
if(file_exists($filepath)){
$filename = $plugin_name.'_main.py';
$this->output_file($filepath, $filename);
}else{
$filepath = get_data_dir().'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
$filepath = get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
if(file_exists($filepath)){
$filename = $plugin_name.'_main.py';
$this->output_file($filepath, $filename);
@ -86,14 +110,26 @@ class Api extends BaseController
}
public function get_update_logs(){
$version = config_get('new_version');
$data = [
[
'title' => 'Linux面板'.$version,
'body' => config_get('update_msg'),
'addtime' => config_get('update_date')
]
];
$type = input('get.type');
if($type == 'Windows'){
$version = config_get('new_version_win');
$data = [
[
'title' => 'Linux面板'.$version,
'body' => config_get('update_msg_win'),
'addtime' => config_get('update_date_win')
]
];
}else{
$version = config_get('new_version');
$data = [
[
'title' => 'Linux面板'.$version,
'body' => config_get('update_msg'),
'addtime' => config_get('update_date')
]
];
}
return jsonp($data);
}
@ -102,6 +138,11 @@ class Api extends BaseController
return $version;
}
public function get_version_win(){
$version = config_get('new_version_win');
return $version;
}
//安装统计
public function setup_count(){
return 'ok';
@ -130,6 +171,32 @@ class Api extends BaseController
return json($data);
}
//检测更新(win)
public function check_update_win(){
$version = config_get('new_version_win');
$down_url = request()->root(true).'/win/panel/panel_'.$version.'.zip';
$data = [
'force' => false,
'version' => $version,
'downUrl' => $down_url,
'updateMsg' => config_get('update_msg_win'),
'uptime' => config_get('update_date_win'),
'is_beta' => 0,
'py_version' => '3.8.6',
'adviser' => -1,
'is_rec' => -1,
'btb' => '',
'beta' => [
'py_version' => '3.8.6',
'version' => $version,
'downUrl' => $down_url,
'updateMsg' => config_get('update_msg_win'),
'uptime' => config_get('update_date_win'),
]
];
return json($data);
}
//获取内测版更新日志
public function get_beta_logs(){
return json(['beta_ps'=>'当前暂无内测版', 'list'=>[]]);
@ -160,6 +227,11 @@ class Api extends BaseController
return time();
}
//同步时间
public function get_win_date(){
return date("Y-m-d H:i:s");
}
//查询是否专业版(废弃)
public function is_pro(){
return json(['endtime'=>true, 'code'=>1]);
@ -190,6 +262,14 @@ class Api extends BaseController
return json(['status'=>false, 'msg'=>'不支持绑定宝塔官网账号', 'data'=>'5b5d']);
}
//绑定一键部署列表
public function get_deplist(){
$os = input('post.os');
$json_arr = Plugins::get_deplist($os);
if(!$json_arr) return json([]);
return json($json_arr);
}
public function return_success(){
return json(['status'=>true, 'msg'=>1, 'data'=>(object)[]]);
}

View File

@ -8,30 +8,35 @@ use ZipArchive;
class Plugins
{
private static function get_btapi(){
$bt_url = config_get('bt_url');
$bt_key = config_get('bt_key');
private static function get_btapi($os){
if($os == 'Windows'){
$bt_url = config_get('wbt_url');
$bt_key = config_get('wbt_key');
}else{
$bt_url = config_get('bt_url');
$bt_key = config_get('bt_key');
}
if(!$bt_url || !$bt_key) throw new Exception('请先配置好宝塔面板接口信息');
$btapi = new Btapi($bt_url, $bt_key);
return $btapi;
}
//刷新插件列表
public static function refresh_plugin_list(){
$btapi = self::get_btapi();
public static function refresh_plugin_list($os = 'Linux'){
$btapi = self::get_btapi($os);
$result = $btapi->get_plugin_list();
if($result && isset($result['list']) && isset($result['type'])){
if(empty($result['list']) || empty($result['type'])){
throw new Exception('获取插件列表失败:插件列表为空');
}
self::save_plugin_list($result);
self::save_plugin_list($result, $os);
}else{
throw new Exception('获取插件列表失败:'.(isset($result['msg'])?$result['msg']:'面板连接失败'));
}
}
//保存插件列表
private static function save_plugin_list($data){
private static function save_plugin_list($data, $os){
$data['ip'] = '127.0.0.1';
$data['serverid'] = '';
$data['beta'] = 0;
@ -45,15 +50,15 @@ class Plugins
$data['list'] = $list;
if($data['pro']>-1) $data['pro'] = 0;
if($data['ltd']>-1) $data['ltd'] = strtotime('+1 year');
$json_file = get_data_dir().'config/plugin_list.json';
$json_file = get_data_dir($os).'config/plugin_list.json';
if(!file_put_contents($json_file, json_encode($data))){
throw new Exception('保存插件列表失败,文件无写入权限');
}
}
//获取插件列表
public static function get_plugin_list(){
$json_file = get_data_dir().'config/plugin_list.json';
public static function get_plugin_list($os = 'Linux'){
$json_file = get_data_dir($os).'config/plugin_list.json';
if(file_exists($json_file)){
$data = file_get_contents($json_file);
$json_arr = json_decode($data, true);
@ -65,8 +70,8 @@ class Plugins
}
//获取一个插件信息
public static function get_plugin_info($name){
$json_arr = self::get_plugin_list();
public static function get_plugin_info($name, $os = 'Linux'){
$json_arr = self::get_plugin_list($os);
if(!$json_arr) return null;
foreach($json_arr['list'] as $plugin){
if($plugin['name'] == $name){
@ -77,30 +82,30 @@ class Plugins
}
//下载插件(自动判断是否第三方)
public static function download_plugin($plugin_name, $version){
$plugin_info = Plugins::get_plugin_info($plugin_name);
public static function download_plugin($plugin_name, $version, $os = 'Linux'){
$plugin_info = Plugins::get_plugin_info($plugin_name, $os);
if(!$plugin_info) throw new Exception('未找到该插件信息');
if($plugin_info['type'] == 10 && isset($plugin_info['versions'][0]['download'])){
if($plugin_info['price'] == 0){
$btapi = self::get_btapi();
$btapi = self::get_btapi($os);
$btapi->create_plugin_other_order($plugin_info['id']);
}
$fname = $plugin_info['versions'][0]['download'];
$filemd5 = $plugin_info['versions'][0]['md5'];
Plugins::download_plugin_other($fname, $filemd5);
Plugins::download_plugin_other($fname, $filemd5, $os);
if(isset($plugin_info['min_image']) && strpos($plugin_info['min_image'], 'fname=')){
$fname = substr($plugin_info['min_image'], strpos($plugin_info['min_image'], '?fname=')+7);
Plugins::download_plugin_other($fname);
Plugins::download_plugin_other($fname, null, $os);
}
}else{
Plugins::download_plugin_package($plugin_name, $version);
Plugins::download_plugin_package($plugin_name, $version, $os);
}
}
//下载插件包
public static function download_plugin_package($plugin_name, $version){
$filepath = get_data_dir().'plugins/package/'.$plugin_name.'-'.$version.'.zip';
$btapi = self::get_btapi();
public static function download_plugin_package($plugin_name, $version, $os = 'Linux'){
$filepath = get_data_dir($os).'plugins/package/'.$plugin_name.'-'.$version.'.zip';
$btapi = self::get_btapi($os);
$result = $btapi->get_plugin_filename($plugin_name, $version);
if($result && isset($result['status'])){
if($result['status'] == true){
@ -110,12 +115,12 @@ class Plugins
$zip = new ZipArchive;
if ($zip->open($filepath) === true)
{
$zip->extractTo(get_data_dir().'plugins/folder/'.$plugin_name.'-'.$version);
$zip->extractTo(get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version);
$zip->close();
$main_filepath = get_data_dir().'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
$main_filepath = get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
if(file_exists($main_filepath) && filesize($main_filepath)>10){
if(!strpos(file_get_contents($main_filepath), 'import ')){ //加密py文件需要解密
self::decode_plugin_main($plugin_name, $version, $main_filepath);
self::decode_plugin_main($plugin_name, $version, $main_filepath, $os);
self::noauth_plugin_main($main_filepath);
$zip->open($filepath, ZipArchive::CREATE);
$zip->addFile($main_filepath, $plugin_name.'/'.$plugin_name.'_main.py');
@ -138,9 +143,9 @@ class Plugins
}
//下载插件主程序文件
public static function download_plugin_main($plugin_name, $version){
$filepath = get_data_dir().'plugins/main/'.$plugin_name.'-'.$version.'.dat';
$btapi = self::get_btapi();
public static function download_plugin_main($plugin_name, $version, $os = 'Linux'){
$filepath = get_data_dir($os).'plugins/main/'.$plugin_name.'-'.$version.'.dat';
$btapi = self::get_btapi($os);
$result = $btapi->get_plugin_main_filename($plugin_name, $version);
if($result && isset($result['status'])){
if($result['status'] == true){
@ -160,8 +165,8 @@ class Plugins
}
//解密并下载插件主程序文件
public static function decode_plugin_main($plugin_name, $version, $main_filepath){
$btapi = self::get_btapi();
public static function decode_plugin_main($plugin_name, $version, $main_filepath, $os = 'Linux'){
$btapi = self::get_btapi($os);
$result = $btapi->get_decode_plugin_main($plugin_name, $version);
if($result && isset($result['status'])){
if($result['status'] == true){
@ -180,20 +185,29 @@ class Plugins
public static function noauth_plugin_main($main_filepath){
$data = file_get_contents($main_filepath);
if(!$data) return false;
$data = str_replace('\'http://www.bt.cn/api/panel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list_test', $data);
$data = str_replace('\'https://www.bt.cn/api/panel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list_test', $data);
$data = str_replace('\'http://www.bt.cn/api/panel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list', $data);
$data = str_replace('\'https://www.bt.cn/api/panel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list', $data);
$data = str_replace('\'http://www.bt.cn/api/panel/notpro', 'public.GetConfigValue(\'home\')+\'/api/panel/notpro', $data);
$data = str_replace('\'https://www.bt.cn/api/panel/notpro', 'public.GetConfigValue(\'home\')+\'/api/panel/notpro', $data);
$data = str_replace('\'http://www.bt.cn/api/wpanel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list_test', $data);
$data = str_replace('\'https://www.bt.cn/api/wpanel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list_test', $data);
$data = str_replace('\'http://www.bt.cn/api/wpanel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list', $data);
$data = str_replace('\'https://www.bt.cn/api/wpanel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list', $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);
file_put_contents($main_filepath, $data);
}
//下载插件其他文件
public static function download_plugin_other($fname, $filemd5 = null){
public static function download_plugin_other($fname, $filemd5 = null, $os = 'Linux'){
$filepath = get_data_dir().'plugins/other/'.$fname;
@mkdir(dirname($filepath), 0777, true);
$btapi = self::get_btapi();
$btapi = self::get_btapi($os);
$result = $btapi->get_plugin_other_filename($fname);
if($result && isset($result['status'])){
if($result['status'] == true){
@ -244,4 +258,17 @@ class Plugins
}
}
//获取一键部署列表
public static function get_deplist($os = 'Linux'){
$json_file = get_data_dir($os).'config/deployment_list.json';
if(file_exists($json_file)){
$data = file_get_contents($json_file);
$json_arr = json_decode($data, true);
if($json_arr){
return $json_arr;
}
}
return false;
}
}

View File

@ -34,8 +34,12 @@
<li class="{:checkIfActive('index')}">
<a href="/admin"><i class="fa fa-home"></i> 后台首页</a>
</li>
<li class="{:checkIfActive('plugins')}">
<a href="/admin/plugins"><i class="fa fa-cubes"></i> 插件列表</a>
<li class="{:checkIfActive('plugins,pluginswin')}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-cubes"></i> 插件列表<b class="caret"></b></a>
<ul class="dropdown-menu">
<li class="{:checkIfActive('plugins')}"><a href="/admin/plugins">Linux面板</a></li>
<li class="{:checkIfActive('pluginswin')}"><a href="/admin/pluginswin">Windows面板</a></li>
</ul>
</li>
<li class="{:checkIfActive('record')}">
<a href="/admin/record"><i class="fa fa-list"></i> 使用记录</a>

View File

@ -0,0 +1,214 @@
{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/win/plugins/package/软件标识-版本号.zip第三方插件包路径是/data/plugins/other/other/,对于部分包含二次验证的插件可以自行修改。</p>
<p>点击【重新获取】按钮会从宝塔官方获取最新插件列表,但是插件包需要手动点击下载。如果需要批量下载插件包,可查看<a href="/admin/set/type/task">定时任务设置</a></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;
<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="//cdn.staticfile.org/layer/3.5.1/layer.js"></script>
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/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="/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:'Windows'},
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=Windows',
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 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=Windows',
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){
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
}
},
],
})
})
</script>
{/block}

View File

@ -19,19 +19,34 @@
<font color="green">页面地址:<a href="/download" target="_blank">/download</a>,开启后可以公开访问,否则只能管理员访问</font>
</div>
<div class="form-group">
<label>宝塔面板最新版本号:</label>
<label>宝塔Linux面板最新版本号:</label>
<input type="text" name="new_version" value="{:config_get('new_version')}" class="form-control"/>
<font color="green">用于一键更新脚本获取最新版本号,以及检测更新接口。并确保已在/public/install/update/放置对应版本更新包</font>
</div>
<div class="form-group">
<label>宝塔面板最新版更新日志:</label>
<label>宝塔Linux面板更新日志:</label>
<textarea class="form-control" name="update_msg" rows="5" placeholder="支持HTML代码">{:config_get('update_msg')}</textarea>
<font color="green">用于检测更新接口返回</font>
</div>
<div class="form-group">
<label>宝塔面板最新版更新日期:</label>
<label>宝塔Linux面板更新日期:</label>
<input type="date" name="update_date" value="{:config_get('update_date')}" class="form-control"/>
<font color="green">用于检测更新接口返回</font>
</div>
<div class="form-group">
<label>宝塔Windows面板最新版本号</label>
<input type="text" name="new_version_win" value="{:config_get('new_version_win')}" class="form-control"/>
<font color="green">用于一键更新脚本获取最新版本号,以及检测更新接口。并确保已在/public/win/panel/放置对应版本更新包</font>
</div>
<div class="form-group">
<label>宝塔Windows面板更新日志</label>
<textarea class="form-control" name="update_msg_win" rows="5" placeholder="支持HTML代码">{:config_get('update_msg_win')}</textarea>
<font color="green">用于检测更新接口返回</font>
</div>
<div class="form-group">
<label>宝塔Windows面板更新日期</label>
<input type="date" name="update_date_win" value="{:config_get('update_date_win')}" 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"/>
@ -40,7 +55,7 @@
</div>
</div>
<div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">宝塔面板接口设置</h3></div>
<div class="panel-heading"><h3 class="panel-title">宝塔Linux面板接口设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
<p>以下宝塔面板请使用官方最新脚本安装并绑定账号,用于获取最新插件列表及插件包</p>
@ -61,6 +76,29 @@
</form>
</div>
</div>
<div class="panel panel-primary">
<div class="panel-heading"><h3 class="panel-title">宝塔Windows面板接口设置</h3></div>
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
<p>以下宝塔面板请使用官方最新脚本安装并绑定账号,用于获取最新插件列表及插件包</p>
<p><a href="/static/file/win/kaixin.zip">下载专用插件</a>,在面板【软件商店】->【第三方应用】,点击【导入插件】,导入该专用插件。</p>
<p><a href="/static/file/win/__init__.zip">下载py替换文件</a>解压覆盖到C:\Program Files\python\Lib\json\目录下,然后重启面板。</p>
<div class="form-group">
<label>宝塔面板URL</label><br/>
<input type="text" name="wbt_url" value="{:config_get('wbt_url')}" class="form-control"/>
<font color="green">填写规则如:<u>http://192.168.1.1:8888</u> ,不要带其他后缀</font>
</div>
<div class="form-group">
<label>宝塔面板接口密钥:</label>
<input type="text" name="wbt_key" value="{:config_get('wbt_key')}" class="form-control"/>
</div>
<div class="form-group text-center">
<button type="button" class="btn btn-info btn-block" id="testbturl2">测试连接</button>
<input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/>
</div>
</form>
</div>
</div>
{elseif $mod=='task'}
<div class="panel panel-success">
<div class="panel-heading"><h3 class="panel-title">定时任务说明</h3></div>
@ -78,9 +116,13 @@
<div class="panel-body">
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
<div class="form-group">
<label>批量下载插件范围:</label><br/>
<label>Linux面板批量下载插件范围:</label><br/>
<select class="form-control" name="updateall_type" default="{:config_get('updateall_type')}"><option value="0">仅免费插件</option><option value="1">免费插件+专业版插件</option><option value="2">免费插件+专业版插件+企业版插件</option></select><font color="green">(批量下载不包含所有第三方插件,第三方插件需要去手动下载。)</font>
</div>
<div class="form-group">
<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>
</div>
<div class="form-group text-center">
<input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/>
</div>
@ -153,6 +195,38 @@ $(document).ready(function(){
}
});
})
$("#testbturl2").click(function(){
var wbt_url = $("input[name=wbt_url]").val();
var wbt_key = $("input[name=wbt_key]").val();
if(wbt_url == ''){
layer.alert('宝塔面板URL不能为空');return;
}
if(wbt_url.indexOf('http://')==-1 && wbt_url.indexOf('https://')==-1){
layer.alert('宝塔面板URL不正确');return;
}
if(wbt_key == ''){
layer.alert('宝塔面板接口密钥不能为空');return;
}
var ii = layer.load(2, {shade:[0.1,'#fff']});
$.ajax({
type : 'POST',
url : '/admin/testbturl',
data : {bt_url:wbt_url, bt_key:wbt_key},
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('服务器错误');
}
});
})
})
function saveSetting(obj){
var ii = layer.load(2, {shade:[0.1,'#fff']});

View File

@ -20,12 +20,47 @@
<p>2分钟装好面板一键管理服务器</p>
<p>集成LAMP/LNMP环境安装网站、FTP、数据库、文件管理、软件安装等功能</p>
</div>
<div class="mt_30 i1ta wap_mt15">
<a href="https://demo.bt.cn" target="_block" rel="noreferrer">查看演示</a>
<a href="javascript:;" id="goInstallLinux">
<img class="middle mr10" src="/static/images/i1aico_03.png">
立即免费安装
</a>
<div class="disflex flex_lrcenter mt_50 install-list">
<div class="install-box linux">
<div class="img">
<img src="/static/images/prd_1_03.png">
</div>
<div class="cont">
<div class="top">
<div class="title">Linux面板 {:config_get('new_version')}</div>
<div class="desc">
支持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>
</div>
<div class="mark">
<span>2分钟装好</span>
<span>阿里云推荐</span>
<span>腾讯云推荐</span>
</div>
</div>
<div class="bottom">
<a class="btn" href="javascript:;" id="goInstallLinux">查看安装脚本</a>
</div>
</div>
</div>
<div class="install-box windows">
<div class="img">
<img src="/static/images/prd_2_03.png">
</div>
<div class="cont">
<div class="top">
<div class="title">Windows面板 {:config_get('new_version_win')}</div>
<div class="desc">支持Windows Server 2008 R2/2012/2016/201964位系统</div>
<div class="mark">
<span>操作简单</span>
<span>使用方便</span>
</div>
</div>
<div class="bottom">
<a class="btn" href="javascript:;" id="goInstallWindows">查看安装方法</a>
</div>
</div>
</div>
</div>
</div>
</div>
@ -33,7 +68,7 @@
<div class="d2" id="instal-linux">
<div class="wrap">
<div class="wrap-title linux-title">
<div class="text">使用此云端的宝塔面板(版本:{:config_get('new_version')}</div>
<div class="text">Linux面板{:config_get('new_version')}安装脚本</div>
</div>
<div class="desc">
使用 SSH 连接工具,如
@ -70,13 +105,23 @@
</div>
</div>
</div>
<div class="d4" id="instal-cloud">
<div class="d4" id="instal-windows">
<div class="wrap">
<div class="wrap-title">
<div class="text">更新日志</div>
<div class="text">Windows面板{:config_get('new_version_win')}安装方法</div>
</div>
<div class="desc">
<p>宝塔Linux面板更新到{:config_get('new_version')}</p>
<p>1、使用<a class="link" href="https://download.bt.cn/win/panel/BtSoft.zip" target="_blank" rel="noreferrer">官方安装程序</a>进行安装,安装完先不要进入面板。</p>
<p>2、在命令提示符cmd输入以下一键更新命令然后重启面板。</p>
</div>
<div class="install-code">
<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>
<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 class="tips" style="color: orangered; font-weight: 700">
<p>注意仅支持Windows Server 2008 R2/2012/2016/201964位系统中文简体且未安装其它环境</p>
</div>
</div>
</div>
@ -133,8 +178,8 @@
scrollTop('#instal-linux');
});
$('#goOnlineInstall').click(function () {
scrollTop('#online-instal');
$('#goInstallWindows').click(function () {
scrollTop('#instal-windows');
});
$('#goInstallCloud').click(function () {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,9 @@ INSERT INTO `cloud_config` (`key`, `value`) VALUES
('new_version', '7.9.2'),
('update_msg', '暂无更新日志'),
('update_date', '2022-06-01'),
('new_version_win', '7.6.0'),
('update_msg_win', '暂无更新日志'),
('update_date_win', '2022-06-01'),
('updateall_type', '0'),
('syskey', 'UqP94LtI8eWAIgCP');

Binary file not shown.

View File

@ -61,6 +61,187 @@ canvas {
padding: 70px 0;
}
.install-box {
position: relative;
top: 0;
display: flex;
align-items: center;
width: 24%;
padding: 30px 10px 16px 20px;
border-radius: 8px;
background-color: #fff;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 30%);
transition: all 0.3s;
}
.install-box:hover {
box-shadow: 0 6px 16px 0 rgb(0 0 0 / 50%);
top: -10px;
}
.install-box.linux,
.install-box.windows {
align-items: center;
width: 41.5%;
}
.install-box + .install-box {
margin-left: 2%;
}
.install-box::before {
content: '';
position: absolute;
top: 20px;
right: 0;
bottom: 0;
width: 100%;
background-repeat: no-repeat;
background-position: right bottom;
background-size: auto 100%;
z-index: 10;
transition: all 0.25s;
opacity: 0.3;
}
.install-box.linux::before {
background-image: url(../images/downico1_01.png);
}
.install-box.windows::before {
background-image: url(../images/downico2_01.png);
}
.install-box.cloud::before {
background-image: url(../images/downico3_01.png);
}
.install-box.ssh::before {
background-image: url(../images/downico4_01.png);
}
.install-box.app::before {
background-image: url(../images/downico5_01.png);
}
.install-box .img {
position: relative;
flex: 0 0 44%;
z-index: 12;
}
.install-box .img img {
display: block;
width: 100%;
}
.install-box .cont {
flex: 1;
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
margin-left: 5%;
padding-bottom: 12px;
z-index: 12;
}
.install-box.linux .cont,
.install-box.windows .cont {
padding-bottom: 10px;
}
.install-box .bottom {
display: flex;
margin: 0 auto;
}
.install-box .title {
margin-bottom: 14px;
line-height: 1;
font-size: 20px;
font-weight: bold;
transition: color 0.25s;
}
.install-box.linux .title,
.install-box.windows .title {
font-size: 28px;
}
.install-box .desc {
margin-bottom: 14px;
font-size: 12px;
color: #777;
transition: color 0.25s;
}
.install-box.linux .desc,
.install-box.windows .desc {
font-size: 14px;
}
.install-box .mark {
margin-bottom: 20px;
height: 22px;
overflow: hidden;
}
.install-box .mark span {
margin-right: 10px;
line-height: 22px;
height: 22px;
padding: 0 6px;
font-size: 12px;
border-radius: 2px;
display: inline-block;
}
.install-box .mark span:nth-child(1) {
border: #6bdc6b 1px solid;
color: #fff;
background-color: #6bdc6b;
}
.install-box .mark span:nth-child(2) {
border: #ffa565 1px solid;
color: #fff;
background-color: #ffa565;
}
.install-box .mark span:nth-child(3) {
border: #65a7ff 1px solid;
color: #fff;
background-color: #65a7ff;
}
.install-box .mark span:last-child {
margin-right: 0;
}
.install-box .btn {
display: flex;
align-items: center;
justify-content: center;
height: 34px;
padding: 0 16px;
background-image: linear-gradient(#54b891, #22a53d);
border-radius: 6px;
color: #fff;
font-size: 14px;
font-weight: 700;
line-height: 1;
border: none;
}
.install-box .btn.default {
margin-left: 10px;
background: none;
border: 1px solid #777;
color: #777;
}
.install-box .btn:hover {
filter: brightness(120%);
-webkit-filter: brightness(120%);
}
.install-box:hover {
background-image: linear-gradient(#e5f5e5, #fff);
}
.wrap-title {
display: flex;
flex-wrap: wrap;
@ -156,6 +337,7 @@ canvas {
.d4 .desc {
margin-bottom: 16px;
line-height: 30px;
}
.bird {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -0,0 +1,109 @@
#coding: utf-8
# +-------------------------------------------------------------------
# | 宝塔Windows面板
# +-------------------------------------------------------------------
# | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved.
# +-------------------------------------------------------------------
# | Author: 沐落 <cjx@bt.cn>
# | 面板升级安装公共类
# +-------------------------------------------------------------------
import os, sys
panelPath = os.getenv('BT_PANEL')
os.chdir(panelPath)
sys.path.insert(0,panelPath + "/class/")
import public,time,re,shutil,platform
class panel_update:
def __init__(self):
pass
def UpdatePanel(self,version):
"""
更新面板到指定版本
@version 面板版本号
"""
import public
setupPath = os.getenv('BT_SETUP')
loacl_path = setupPath + '/panel.zip'
tmpPath = "{}/temp/panel".format(setupPath)
httpUrl = 'http://www.example.com'
try:
downUrl = httpUrl + '/win/panel/panel_' + version + '.zip';
if os.path.exists(loacl_path): os.remove(loacl_path)
public.downloadFileByWget(downUrl,loacl_path);
if os.path.getsize(loacl_path) < 1048576: return public.returnMsg(False,"PANEL_UPDATE_ERR_DOWN");
except :
print(public.get_error_info())
return public.returnMsg(False,"修复失败,无法连接到下载节点.");
#处理临时文件目录
tcPath = '{}\class'.format(tmpPath)
if os.path.exists(tmpPath): shutil.rmtree(tmpPath,True)
if not os.path.exists(tmpPath): os.makedirs(tmpPath)
import zipfile
zip_file = zipfile.ZipFile(loacl_path)
for names in zip_file.namelist():
zip_file.extract(names,tmpPath)
zip_file.close()
for name in os.listdir(tcPath):
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']
for ff_path in file_list:
if os.path.exists(tmpPath + '/' + ff_path): os.remove(tmpPath + '/' + ff_path)
public.mod_reload(public)
import public
#兼容不同版本工具箱
public.kill('BtTools.exe')
toolPath = tmpPath + '/script/BtTools.exe'
if os.path.exists(toolPath):os.remove(toolPath)
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)
os.system("del /s {}\*.pyc".format(public.to_path(cPath)))
os.system("del /s {}\*.pyt".format(public.to_path(cPath)))
for name in os.listdir(cPath):
try:
if name.find('.pyd') >=0:
oldName = os.path.join(cPath,name)
newName = os.path.join(cPath,public.GetRandomString(8) + '.pyt')
os.rename(oldName,newName)
if name.find('.dll') >= 0:
oldName = os.path.join(cPath,name)
public.rmdir(oldName)
except : pass
#处理面板程序目录文件
os.system("del /s {}\*.pyc".format(public.to_path(cPath)))
os.system("del /s {}\*.pyt".format(public.to_path(cPath)))
os.system("echo f|xcopy /s /c /e /y /r {} {}".format(public.to_path(tmpPath),public.to_path(panelPath)))
return public.returnMsg(True,"升级面板成功,重启面板后生效.");

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,164 @@
# -*- 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)

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -7,25 +7,35 @@ Route::get('/download', 'index/download');
Route::any('/panel/get_plugin_list', 'api/get_plugin_list');
Route::any('/wpanel/get_plugin_list', 'api/get_plugin_list_win');
Route::post('/down/download_plugin', 'api/download_plugin');
Route::post('/down/download_plugin_main', 'api/download_plugin_main');
Route::post('/panel/get_soft_list_status', 'api/return_success');
Route::post('/panel/get_unbinding', 'api/return_success');
Route::post('/bt_cert', 'api/return_error');
Route::post('/Auth/GetAuthToken', 'api/get_auth_token');
Route::post('/Auth/GetBindCode', 'api/return_error');
Route::group('api', function () {
Route::any('/panel/get_soft_list', 'api/get_plugin_list');
Route::any('/panel/get_soft_list_test', 'api/get_plugin_list');
Route::any('/wpanel/get_soft_list', 'api/get_plugin_list_win');
Route::any('/wpanel/get_soft_list_test', 'api/get_plugin_list_win');
Route::get('/getUpdateLogs', 'api/get_update_logs');
Route::get('/panel/get_version', 'api/get_version');
Route::get('/wpanel/get_version', 'api/get_version_win');
Route::get('/SetupCount', 'api/setup_count');
Route::any('/panel/updateLinux', 'api/check_update');
Route::any('/wpanel/updateWindows', 'api/check_update_win');
Route::post('/panel/check_auth_key', 'api/check_auth_key');
Route::post('/panel/check_domain', 'api/check_domain');
Route::post('/panel/check_files', 'api/return_empty');
Route::get('/index/get_time', 'api/get_time');
Route::get('/index/get_win_date', 'api/get_win_date');
Route::get('/panel/is_pro', 'api/is_pro');
Route::get('/getIpAddress', 'api/get_ip_address');
Route::post('/Auth/GetAuthToken', 'api/get_auth_token');
Route::post('/Auth/GetBindCode', 'api/return_error');
Route::get('/Pluginother/get_file', 'api/download_plugin_other');
Route::post('/Pluginother/create_order', 'api/return_error');
@ -53,6 +63,7 @@ Route::group('api', function () {
Route::post('/invite/create_order', 'api/return_error');
Route::post('/panel/get_plugin_remarks', 'api/get_plugin_remarks');
Route::post('/wpanel/get_plugin_remarks', 'api/get_plugin_remarks');
Route::post('/panel/set_user_adviser', 'api/return_success');
Route::post('/wpanel/get_messages', 'api/return_empty_array');
@ -66,12 +77,17 @@ Route::group('api', function () {
Route::post('/wpanel/model_click', 'api/return_empty');
Route::post('/v2/statistics/report_plugin_daily', 'api/return_error');
Route::get('/panel/notpro', 'api/return_empty');
Route::post('/Btdeployment/get_deplist', 'api/get_deplist');
Route::post('/LinuxBeta', 'api/return_error');
Route::post('/panel/apple_beta', 'api/return_error');
Route::post('/wpanel/apple_beta', 'api/return_error');
Route::post('/panel/to_not_beta', 'api/return_error');
Route::post('/wpanel/to_not_beta', 'api/return_error');
Route::post('/panel/to_beta', 'api/return_error');
Route::post('/wpanel/to_beta', 'api/return_error');
Route::get('/panel/get_beta_logs', 'api/get_beta_logs');
Route::get('/wpanel/get_beta_logs', 'api/get_beta_logs');
Route::miss('api/return_error');
});
@ -86,6 +102,7 @@ Route::group('admin', function () {
Route::post('/setaccount', 'admin/setaccount');
Route::post('/testbturl', 'admin/testbturl');
Route::get('/plugins', 'admin/plugins');
Route::get('/pluginswin', 'admin/pluginswin');
Route::post('/plugins_data', 'admin/plugins_data');
Route::post('/download_plugin', 'admin/download_plugin');
Route::get('/refresh_plugins', 'admin/refresh_plugins');

202
wiki/files/win/bt.js Normal file
View File

@ -0,0 +1,202 @@
/*
*宝塔面板去除各种计算题与延时等待
*/
if("undefined" != typeof bt && bt.hasOwnProperty("show_confirm")){
bt.show_confirm = function(title, msg, fun, error) {
if (error == undefined) {
error = ""
}
var mess = layer.open({
type: 1,
title: title,
area: "350px",
closeBtn: 2,
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>"
});
$(".bt-cancel").click(function () {
layer.close(mess);
});
$("#toSubmit").click(function () {
layer.close(mess);
fun();
})
}
}
if("undefined" != typeof bt && bt.hasOwnProperty("prompt_confirm")){
bt.prompt_confirm = function (title, msg, callback) {
layer.open({
type: 1,
title: title,
area: "350px",
closeBtn: 2,
btn: ['确认', '取消'],
content: "<div class='bt-form promptDelete pd20'>\
<p>" + msg + "</p>\
</div>",
yes: function (layers, index) {
layer.close(layers)
if (callback) callback()
}
});
}
}
if("undefined" != typeof database && database.hasOwnProperty("del_database")){
database.del_database = function (wid, dbname,obj, callback) {
var tips = '是否确认【删除数据库】,删除后可能会影响业务使用!';
if(obj && obj.db_type > 0) tips = '远程数据库不支持数据库回收站,删除后将无法恢复,请谨慎操作';
var title = typeof dbname === "function" ?'批量删除数据库':'删除数据库 [ '+ dbname +' ]';
layer.open({
type:1,
title:title,
icon:0,
skin:'delete_site_layer',
area: "530px",
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\' style=\'margin-bottom: 20px;\'>"+tips+"</div>" +
"<div style=\'color:red;margin:18px 0 18px 18px;font-size:14px;font-weight: bold;\'>注意:数据无价,请谨慎操作!!!"+(!recycle_bin_db_open?'<br>风险操作:当前数据库回收站未开启,删除数据库将永久消失!':'')+"</div>" +
"</div>",
btn:[lan.public.ok,lan.public.cancel],
yes:function(indexs){
var data = {id: wid,name: dbname};
if(typeof dbname === "function"){
delete data.id;
delete data.name;
}
layer.close(indexs)
if(typeof dbname === "function"){
dbname(data)
}else{
bt.database.del_database(data, function (rdata) {
layer.closeAll()
if (rdata.status) database_table.$refresh_table_list(true);
if (callback) callback(rdata);
bt.msg(rdata);
})
}
}
})
}
}
if("undefined" != typeof site && site.hasOwnProperty("del_site")){
site.del_site = function(wid, wname, callback) {
var 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 === 'data' && !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)
if(typeof wname === "function"){
console.log(data)
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(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

@ -0,0 +1,60 @@
#coding: utf-8
import public,os,sys,json
class Plugin:
name = False
p_path = None
is_php = False
plu = None
__api_root_url = 'https://api.bt.cn'
__api_url = __api_root_url+ '/wpanel/get_plugin_list'
__cache_file = 'data/plugin_list.json'
def __init__(self, name):
self.name = name
self.p_path = public.get_plugin_path(name)
self.is_php = os.path.exists(self.p_path + '/index.php')
def get_plugin_list(self, force = False):
if force==False and os.path.exists(self.__cache_file):
jsonData = public.readFile(self.__cache_file)
softList = json.loads(jsonData)
else:
try:
jsonData = public.HttpGet(self.__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: raise Exception('云端插件列表获取失败')
public.writeFile(self.__cache_file, jsonData)
return softList
def isdef(self, fun):
if not self.is_php:
sys.path.append(self.p_path)
plugin_main = __import__(self.name + '_main')
try:
from imp import reload
reload(plugin_main)
except:
pass
self.plu = eval('plugin_main.' + self.name + '_main()')
if not hasattr(self.plu, fun):
if self.name == 'btwaf' and fun == 'index':
raise Exception("未购买")
return False
return True
def exec_fun(self, args):
fun = args.s
if not self.is_php:
plu = self.plu
data = eval('plu.' + fun + '(args)')
else:
import panelPHP
args.s = fun
args.name = self.name
data = panelPHP.panelPHP(self.name).exec_php_script(args)
return data

View File

@ -1,4 +1,6 @@
# 官方更新包修改记录
# Linux面板官方更新包修改记录
查询最新版本号https://www.bt.cn/api/panel/get_version?is_version=1
官方更新包下载链接http://download.bt.cn/install/update/LinuxPanel-版本号.zip
@ -17,7 +19,7 @@
- 全局搜索替换 https://api.bt.cn => http://www.example.com
- 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/需排除clearModel.py和plugin_deployment.py
- 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/需排除clearModel.py
- 全局搜索替换 http://download.bt.cn/install/update6.sh => http://www.example.com/install/update6.sh
@ -53,6 +55,8 @@
```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')
```
- install/install_soft.sh 在bash执行之前加入以下代码
@ -70,6 +74,8 @@
删除 p = threading.Thread(target=check_panel_msg) 以及下面2行
删除 p = threading.Thread(target=update_software_list) 以及下面2行
- 去除面板日志上报script/site_task.py 文件
删除最下面 logs_analysis() 这1行

78
wiki/updatewin.md Normal file
View File

@ -0,0 +1,78 @@
# Windows面板官方更新包修改记录
查询最新版本号https://www.bt.cn/api/wpanel/get_version?is_version=1
官方更新包下载链接http://download.bt.cn/win/panel/panel_版本号.zip
假设搭建的宝塔第三方云端网址是 http://www.example.com
Windows版宝塔由于加密文件太多无法全部解密因此无法做到全开源。
- 删除pluginAuth.cp38-win_amd64.pyd将win/pluginAuth.py复制到class文件夹
- 全局搜索替换 https://api.bt.cn => http://www.example.com
- 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/
- 全局搜索替换 http://www.bt.cn/api/ => http://www.example.com/api/
- 全局搜索替换 http://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/webshell_check.py 搜索替换 public.GetConfigValue('home') => 'https://www.bt.cn'
- 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()这一行
- 去除首页自动检测更新避免频繁请求云端BTPanel/static/js/index.js 文件注释掉bt.system.check_update这一段代码外的setTimeout
- [可选]去除各种计算题复制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') 这一行