├── .env.example ├── .gitignore ├── LICENSE ├── README.md ├── app ├── .htaccess ├── AppService.php ├── BaseController.php ├── ExceptionHandle.php ├── Request.php ├── command │ ├── Clean.php │ ├── CleanViteJs.php │ ├── DecryptFile.php │ └── UpdateAll.php ├── common.php ├── controller │ ├── Admin.php │ ├── Api.php │ ├── Index.php │ └── Install.php ├── event.php ├── lib │ ├── BtPlugins.php │ ├── Btapi.php │ ├── Plugins.php │ ├── ThirdPlugins.php │ └── cn.json ├── middleware.php ├── middleware │ ├── AuthAdmin.php │ ├── CheckAdmin.php │ ├── LoadConfig.php │ └── RefererCheck.php ├── provider.php ├── script │ ├── cacert.sh │ └── convert.sh ├── service.php └── view │ ├── admin │ ├── deplist.html │ ├── index.html │ ├── layout.html │ ├── list.html │ ├── log.html │ ├── login.html │ ├── plugins.html │ ├── pluginsen.html │ ├── pluginswin.html │ ├── record.html │ ├── set.html │ └── ssl.html │ ├── dispatch_jump.html │ ├── index │ └── download.html │ └── install │ └── index.html ├── composer.json ├── config ├── app.php ├── cache.php ├── captcha.php ├── console.php ├── cookie.php ├── database.php ├── filesystem.php ├── lang.php ├── log.php ├── middleware.php ├── route.php ├── session.php ├── trace.php └── view.php ├── data ├── config │ └── .gitkeep ├── en │ ├── config │ │ └── .gitkeep │ └── plugins │ │ ├── folder │ │ └── .gitkeep │ │ ├── main │ │ └── .gitkeep │ │ └── package │ │ └── .gitkeep ├── plugins │ ├── folder │ │ └── .gitkeep │ ├── main │ │ └── .gitkeep │ ├── other │ │ └── other │ │ │ └── .gitkeep │ └── package │ │ └── .gitkeep └── win │ ├── config │ └── .gitkeep │ └── plugins │ ├── folder │ └── .gitkeep │ ├── main │ └── .gitkeep │ └── package │ └── .gitkeep ├── install.sql ├── public ├── .htaccess ├── index.php ├── install │ ├── install_7.0_en.sh │ ├── install_btmonitor.sh │ ├── install_panel.sh │ ├── public.sh │ ├── src │ │ ├── bt-monitor-2.3.0.zip │ │ ├── panel6.zip │ │ └── panel_7_en.zip │ ├── update │ │ ├── LinuxPanel-9.5.0.pl │ │ ├── LinuxPanel-9.6.0.pl │ │ ├── LinuxPanel-9.6.0.zip │ │ └── LinuxPanel_EN-7.0.16.zip │ ├── update6.sh │ ├── update_7.x_en.sh │ ├── update_btmonitor.sh │ └── update_panel.sh ├── robots.txt ├── router.php ├── static │ ├── css │ │ ├── bootstrap-table.css │ │ ├── download.css │ │ ├── sanren.css │ │ └── style.css │ ├── file │ │ ├── en │ │ │ └── kaixin.zip │ │ ├── kaixin.zip │ │ └── win │ │ │ └── kaixin.zip │ ├── images │ │ ├── aapanel.png │ │ ├── account.png │ │ ├── addc.png │ │ ├── bt.png │ │ ├── bt_monitor.png │ │ ├── bto_copy.png │ │ ├── close.png │ │ ├── downico1_01.png │ │ ├── downico2_01.png │ │ ├── eye.png │ │ ├── eye_close.png │ │ ├── footbg_02.jpg │ │ ├── hot_03.png │ │ ├── i1abg_03.png │ │ ├── i1aico_03.png │ │ ├── i1ico_03.png │ │ ├── ico-copy.png │ │ ├── install_type_select.png │ │ ├── logo.svg │ │ ├── navicon.png │ │ ├── prd_1_03.png │ │ ├── prd_2_03.png │ │ └── wave.svg │ └── js │ │ ├── custom.js │ │ └── dx.js └── win │ ├── install │ └── panel_update.py │ └── panel │ ├── BtTools20.exe │ ├── BtTools45.exe │ ├── data │ ├── api.py │ └── setup.py │ └── panel_8.2.2.zip ├── route └── app.php ├── runtime └── .gitignore ├── think └── wiki ├── aapanel.md ├── btmonitor.md ├── files ├── aapanel │ ├── PluginLoader.py │ └── bt.js ├── btmonitor │ └── PluginLoader.py ├── linux │ ├── PluginLoader.py │ └── bt.js └── win │ ├── PluginLoader.py │ └── bt.js ├── update.md └── updatewin.md /.env.example: -------------------------------------------------------------------------------- 1 | APP_DEBUG = false 2 | 3 | [APP] 4 | DEFAULT_TIMEZONE = Asia/Shanghai 5 | 6 | [DATABASE] 7 | TYPE = mysql 8 | HOSTNAME = {dbhost} 9 | DATABASE = {dbname} 10 | USERNAME = {dbuser} 11 | PASSWORD = {dbpwd} 12 | HOSTPORT = {dbport} 13 | CHARSET = utf8mb4 14 | PREFIX = {dbprefix} 15 | DEBUG = false 16 | 17 | [LANG] 18 | default_lang = zh-cn -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.vscode 3 | /vendor 4 | *.log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Flucont 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 宝塔面板第三方云端 2 | 这是一个用php开发的宝塔面板第三方云端站点程序。 3 | 4 | 你可以使用此程序搭建属于自己的宝塔面板第三方云端,实现最新版宝塔面板私有化部署,不与宝塔官方接口通信,满足隐私安全合规需求。同时还可以去除面板强制绑定账号,DIY面板功能等。 5 | 6 | 网站后台管理可一键同步宝塔官方的插件列表与增量更新插件包,还有云端使用记录、IP黑白名单、操作日志、定时任务等功能。 7 | 8 | 本项目自带 **宝塔Linux面板**、**宝塔Windows面板**、**aaPanel面板**、**宝塔云监控** 的最新版安装包和更新包,已修改适配此第三方云端,并且全开源,无.so等加密文件。 9 | 10 | 觉得该项目不错的可以给个Star~ 11 | 12 | ## 声明 13 | 14 | 1.此项目只能以自用为目的,不得侵犯堡塔公司及其他第三方的知识产权和其他合法权利。 15 | 16 | 2.搭建使用此项目必须有一定的编程和Linux运维基础,纯小白不建议使用。 17 | 18 | ## 环境要求 19 | 20 | * `PHP` >= 7.4 21 | * `MySQL` >= 5.6 22 | * `fileinfo`扩展 23 | * `ZipArchive`扩展 24 | 25 | ## 部署方法 26 | 27 | - [下载最新版的Release包](https://github.com/flucont/btcloud/releases) 28 | - 如果是下载的源码包,需要执行 `composer install --no-dev` 安装依赖,如果是下载的Release包,则不需要 29 | - 设置网站运行目录为`public` 30 | - 设置伪静态为`ThinkPHP` 31 | - 访问网站,会自动跳转到安装页面,根据提示安装完成 32 | 33 | ## 使用方法 34 | 35 | - 在`批量替换工具`,执行页面显示的命令,可将bt安装包、更新包和脚本文件里面的`http://www.example.com`批量替换成当前网站的网址。 36 | - 在`系统基本设置`修改宝塔面板接口设置。你需要准备一个使用官方最新脚本安装并绑定账号的宝塔面板,用于获取最新插件列表及插件包。并根据界面提示安装好专用插件。 37 | - 在`定时任务设置`执行所显示的命令从宝塔官方获取最新的插件列表并批量下载插件包(增量更新)。当然你也可以去插件列表,一个一个点击下载。 38 | - 访问网站`/download`查看使用此第三方云端的一键安装脚本。 39 | 40 | ## 更新方法 41 | 42 | - [下载最新版的Release包](https://github.com/flucont/btcloud/releases) 43 | - 上传覆盖除data文件夹以外的全部文件 44 | - 后台使用批量替换工具->获取最新插件列表->修改软件版本设置里面的版本号 45 | 46 | ## 其他 47 | 48 | - [Linux面板官方更新包修改记录](./wiki/update.md) 49 | 50 | - [Windows面板官方更新包修改记录](./wiki/updatewin.md) 51 | 52 | - [aaPanel面板官方更新包修改记录](./wiki/aapanel.md) 53 | 54 | - [宝塔云监控安装包修改记录](./wiki/btmonitor.md) 55 | 56 | - 宝塔面板官方版与此第三方云端版对比: 57 | 58 | | | 官方版 | 此第三方云端版 | 59 | | ---------- | ------------------------------------------------------------ | -------------------------------------------------- | 60 | | 版本更新 | 支持 | 支持 | 61 | | 面板广告 | 有广告 | 无广告 | 62 | | 是否全开源 | 没有全开源 | 全开源 | 63 | | 资源占用 | 各种统计上报等任务,资源占用略高 | 去除了很多无用的定时任务,资源占较少 | 64 | | 兼容性 | 由于编译的so文件有系统架构限制,兼容的系统仅限已编译的so对应的系统架构 | 由于全开源,没有已编译的so文件,因此无系统架构限制 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | deny from all -------------------------------------------------------------------------------- /app/AppService.php: -------------------------------------------------------------------------------- 1 | app = $app; 50 | $this->request = $this->app->request; 51 | 52 | // 控制器初始化 53 | $this->initialize(); 54 | } 55 | 56 | // 初始化 57 | protected function initialize() 58 | { 59 | $this->clientip = real_ip(); 60 | } 61 | 62 | /** 63 | * 验证数据 64 | * @access protected 65 | * @param array $data 数据 66 | * @param string|array $validate 验证器名或者验证规则数组 67 | * @param array $message 提示信息 68 | * @param bool $batch 是否批量验证 69 | * @return array|string|true 70 | * @throws ValidateException 71 | */ 72 | protected function validate(array $data, $validate, array $message = [], bool $batch = false) 73 | { 74 | if (is_array($validate)) { 75 | $v = new Validate(); 76 | $v->rule($validate); 77 | } else { 78 | if (strpos($validate, '.')) { 79 | // 支持场景 80 | [$validate, $scene] = explode('.', $validate); 81 | } 82 | $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate); 83 | $v = new $class(); 84 | if (!empty($scene)) { 85 | $v->scene($scene); 86 | } 87 | } 88 | 89 | $v->message($message); 90 | 91 | // 是否批量验证 92 | if ($batch || $this->batchValidate) { 93 | $v->batch(true); 94 | } 95 | 96 | return $v->failException(true)->check($data); 97 | } 98 | 99 | protected function alert($code, $msg = '', $url = null, $wait = 3) 100 | { 101 | if ($url) { 102 | $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : (string)$this->app->route->buildUrl($url); 103 | } 104 | if(empty($msg)) $msg = '未知错误'; 105 | 106 | View::assign([ 107 | 'code' => $code, 108 | 'msg' => $msg, 109 | 'url' => $url, 110 | 'wait' => $wait, 111 | ]); 112 | return View::fetch(app()->getAppPath().'view/dispatch_jump.html'); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /app/ExceptionHandle.php: -------------------------------------------------------------------------------- 1 | setName('clean') 20 | ->setDescription('the clean command'); 21 | } 22 | 23 | protected function execute(Input $input, Output $output) 24 | { 25 | $res = Db::name('config')->cache('configs',0)->column('value','key'); 26 | Config::set($res, 'sys'); 27 | 28 | if(config_get('bt_url')){ 29 | $this->clean_plugins($input, $output, 'Linux'); 30 | } 31 | if(config_get('wbt_url')){ 32 | $this->clean_plugins($input, $output, 'Windows'); 33 | } 34 | 35 | config_set('cleantime', date('Y-m-d H:i:s')); 36 | } 37 | 38 | private function clean_plugins(Input $input, Output $output, $os){ 39 | $data_dir = get_data_dir($os) . 'plugins/'; 40 | $file_list = []; 41 | $json_arr = Plugins::get_plugin_list($os); 42 | if(count($json_arr['list']) == 0) return; 43 | foreach($json_arr['list'] as $plugin){ 44 | foreach($plugin['versions'] as $version){ 45 | $ver = $version['m_version'].'.'.$version['version']; 46 | if(!isset($version['download'])){ 47 | $file_list[] = $plugin['name'].'-'.$ver; 48 | } 49 | } 50 | } 51 | 52 | $count = 0; 53 | $dir = opendir($data_dir.'package'); 54 | while(false !== ( $file = readdir($dir)) ) { 55 | if($file == '.' || $file == '..') continue; 56 | $name = str_replace('.zip', '', $file); 57 | if(!in_array($name, $file_list)){ 58 | $filepath = $data_dir . 'package/' . $file; 59 | unlink($filepath); 60 | $count++; 61 | } 62 | } 63 | $output->writeln($os.'成功清理'.$count.'个历史版本插件包'); 64 | 65 | $count = 0; 66 | $dir = opendir($data_dir.'folder'); 67 | while(false !== ( $file = readdir($dir)) ) { 68 | if($file == '.' || $file == '..') continue; 69 | if(!in_array($file, $file_list)){ 70 | $filepath = $data_dir . 'folder/' . $file; 71 | deleteDir($filepath); 72 | $count++; 73 | } 74 | } 75 | $output->writeln($os.'成功清理'.$count.'个历史版本插件目录'); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/command/DecryptFile.php: -------------------------------------------------------------------------------- 1 | setName('decrypt') 20 | ->addArgument('type', Argument::REQUIRED, '文件类型,plugin:插件文件,module:模块文件,classdir:宝塔class目录,all:所有py文件') 21 | ->addArgument('file', Argument::REQUIRED, '文件路径') 22 | ->addArgument('os', Argument::OPTIONAL, '操作系统:Windows/Linux') 23 | ->setDescription('解密宝塔面板python文件'); 24 | } 25 | 26 | protected function execute(Input $input, Output $output) 27 | { 28 | $type = trim($input->getArgument('type')); 29 | $file = trim($input->getArgument('file')); 30 | 31 | if(!file_exists($file)){ 32 | $output->writeln('文件不存在'); 33 | return; 34 | } 35 | 36 | if($type == 'plugin'){ 37 | $os = trim($input->getArgument('os')); 38 | try{ 39 | if(Plugins::decode_plugin_main_local($file, $os)){ 40 | $output->writeln('文件解密成功!'); 41 | }else{ 42 | $output->writeln('文件解密失败!'); 43 | } 44 | }catch(\Exception $e){ 45 | $output->writeln($e->getMessage()); 46 | } 47 | }elseif($type == 'module'){ 48 | $this->decode_module_file($output, $file); 49 | }elseif($type == 'classdir'){ 50 | $file = rtrim($file, '/'); 51 | if(file_exists($file.'/common.py')){ 52 | $class_v = 1; 53 | }elseif(file_exists($file.'/common_v2.py')){ 54 | $class_v = 2; 55 | }else{ 56 | $output->writeln('当前路径非宝塔面板class目录'); 57 | return; 58 | } 59 | $dirs = glob($file.'/*Model'.($class_v == 2 ? 'V2' : '')); 60 | foreach($dirs as $dir){ 61 | if(!is_dir($dir))continue; 62 | $files = glob($dir.'/*Model.py'); 63 | foreach($files as $filepath){ 64 | $this->decode_module_file($output, $filepath); 65 | } 66 | } 67 | if($class_v == 2){ 68 | $filepath = $file.'/wp_toolkit/core.py'; 69 | if(file_exists($filepath)){ 70 | $this->decode_module_file($output, $filepath); 71 | } 72 | }else{ 73 | $filepath = $file.'/public/authorization.py'; 74 | if(file_exists($filepath)){ 75 | $this->decode_module_file($output, $filepath); 76 | } 77 | } 78 | }elseif($type == 'all'){ 79 | $file = rtrim($file, '/'); 80 | $this->scan_all_file($input, $output, $file); 81 | }else{ 82 | $output->writeln('未知文件类型'); 83 | } 84 | } 85 | 86 | private function scan_all_file(Input $input, Output $output, $path) { 87 | $dir = opendir($path); 88 | while(false !== ( $file = readdir($dir)) ) { 89 | if (( $file != '.' ) && ( $file != '..' )) { 90 | $filepath = $path . '/' . $file; 91 | if ( is_dir($filepath) ) { 92 | $this->scan_all_file($input, $output, $filepath); 93 | } 94 | elseif(substr($filepath, -3) == '.py') { 95 | $this->decode_module_file($output, $filepath); 96 | } 97 | } 98 | } 99 | closedir($dir); 100 | } 101 | 102 | private function decode_module_file(Output $output, $filepath){ 103 | try{ 104 | $res = Plugins::decode_module_file($filepath); 105 | if($res == 2){ 106 | $output->writeln('文件解密失败:'.$filepath); 107 | }elseif($res == 1){ 108 | $output->writeln('文件解密成功:'.$filepath); 109 | } 110 | }catch(\Exception $e){ 111 | $output->writeln($e->getMessage().':'.$filepath); 112 | } 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /app/command/UpdateAll.php: -------------------------------------------------------------------------------- 1 | setName('updateall') 20 | ->setDescription('the updateall command'); 21 | } 22 | 23 | protected function execute(Input $input, Output $output) 24 | { 25 | $res = Db::name('config')->cache('configs',0)->column('value','key'); 26 | Config::set($res, 'sys'); 27 | 28 | if(!config_get('bt_type') && config_get('bt_url') || config_get('bt_type')==1 && config_get('bt_surl')){ 29 | $this->process_plugins($input, $output, 'Linux'); 30 | } 31 | if(!config_get('wbt_type') && config_get('wbt_url') || config_get('wbt_type')==1 && config_get('wbt_surl')){ 32 | $this->process_plugins($input, $output, 'Windows'); 33 | } 34 | if(!config_get('enbt_type') && config_get('enbt_url') || config_get('enbt_type')==1 && config_get('enbt_surl')){ 35 | $this->process_plugins($input, $output, 'en'); 36 | } 37 | 38 | config_set('runtime', date('Y-m-d H:i:s')); 39 | } 40 | 41 | private function process_plugins(Input $input, Output $output, $os){ 42 | //刷新插件列表 43 | if(!$this->refresh_plugin_list($input, $output, $os)){ 44 | return; 45 | } 46 | 47 | $count = 0; 48 | 49 | if($os=='Windows'){ 50 | $type = intval(config_get('updateall_type_win')); 51 | }elseif($os=='en'){ 52 | $type = intval(config_get('updateall_type_en')); 53 | }else{ 54 | $type = intval(config_get('updateall_type')); 55 | } 56 | 57 | $json_arr = Plugins::get_plugin_list($os); 58 | //循环下载缺少的插件 59 | foreach($json_arr['list'] as $plugin){ 60 | if($type == 0 && ($plugin['type']==8 || $plugin['type']==12) || $type == 1 && $plugin['type']==12 || $plugin['type']==10 || $plugin['type']==5) continue; 61 | 62 | foreach($plugin['versions'] as $version){ 63 | $ver = $version['m_version'].'.'.$version['version']; 64 | if(isset($version['download'])){ 65 | if(!file_exists(get_data_dir().'plugins/other/'.$version['download'])){ 66 | if(!$this->download_plugin($input, $output, $plugin['name'], $ver, $os)){ 67 | sleep(1); 68 | $this->download_plugin($input, $output, $plugin['name'], $ver, $os); 69 | } 70 | sleep(1); 71 | $count++; 72 | } 73 | }else{ 74 | if(!file_exists(get_data_dir($os).'plugins/package/'.$plugin['name'].'-'.$ver.'.zip')){ 75 | if(!$this->download_plugin($input, $output, $plugin['name'], $ver, $os)){ 76 | sleep(1); 77 | $this->download_plugin($input, $output, $plugin['name'], $ver, $os); 78 | } 79 | sleep(1); 80 | $count++; 81 | } 82 | } 83 | } 84 | } 85 | 86 | $output->writeln($os.'本次成功下载'.$count.'个插件'); 87 | } 88 | 89 | private function refresh_plugin_list(Input $input, Output $output, $os){ 90 | try{ 91 | Plugins::refresh_plugin_list($os); 92 | Db::name('log')->insert(['uid' => 1, 'action' => '刷新插件列表', 'data' => '刷新'.$os.'插件列表成功', 'addtime' => date("Y-m-d H:i:s")]); 93 | $output->writeln('刷新'.$os.'插件列表成功'); 94 | return true; 95 | }catch(\Exception $e){ 96 | $output->writeln($e->getMessage()); 97 | errorlog($e->getMessage()); 98 | return false; 99 | } 100 | } 101 | 102 | private function download_plugin(Input $input, Output $output, $plugin_name, $version, $os){ 103 | $fullname = $plugin_name.'-'.$version; 104 | try{ 105 | Plugins::download_plugin($plugin_name, $version, $os); 106 | Db::name('log')->insert(['uid' => 1, 'action' => '下载插件', 'data' => $fullname.' os:'.$os, 'addtime' => date("Y-m-d H:i:s")]); 107 | $output->writeln('下载'.$os.'插件: '.$fullname.' 成功'); 108 | return true; 109 | }catch(\Exception $e){ 110 | $output->writeln($fullname.' '.$e->getMessage()); 111 | errorlog($fullname.' '.$e->getMessage()); 112 | return false; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /app/controller/Index.php: -------------------------------------------------------------------------------- 1 | islogin){ 17 | return 'need login'; 18 | } 19 | View::assign('siteurl', request()->root(true)); 20 | return view(); 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/controller/Install.php: -------------------------------------------------------------------------------- 1 | getRootPath().'.env')){ 15 | return '当前已经安装成功,如果需要重新安装,请手动删除根目录.env文件'; 16 | } 17 | if(request()->isPost()){ 18 | $mysql_host = input('post.mysql_host', null, 'trim'); 19 | $mysql_port = intval(input('post.mysql_port', '3306')); 20 | $mysql_user = input('post.mysql_user', null, 'trim'); 21 | $mysql_pwd = input('post.mysql_pwd', null, 'trim'); 22 | $mysql_name = input('post.mysql_name', null, 'trim'); 23 | $mysql_prefix = input('post.mysql_prefix', 'cloud_', 'trim'); 24 | $admin_username = input('post.admin_username', null, 'trim'); 25 | $admin_password = input('post.admin_password', null, 'trim'); 26 | 27 | if(!$mysql_host || !$mysql_user || !$mysql_pwd || !$mysql_name || !$admin_username || !$admin_password){ 28 | return json(['code'=>0, 'msg'=>'必填项不能为空']); 29 | } 30 | 31 | $configdata = file_get_contents(app()->getRootPath().'.env.example'); 32 | $configdata = str_replace(['{dbhost}','{dbname}','{dbuser}','{dbpwd}','{dbport}','{dbprefix}'], [$mysql_host, $mysql_name, $mysql_user, $mysql_pwd, $mysql_port, $mysql_prefix], $configdata); 33 | 34 | try{ 35 | $DB=new PDO("mysql:host=".$mysql_host.";dbname=".$mysql_name.";port=".$mysql_port,$mysql_user,$mysql_pwd); 36 | }catch(Exception $e){ 37 | if($e->getCode() == 2002){ 38 | $errorMsg='连接数据库失败:数据库地址填写错误!'; 39 | }elseif($e->getCode() == 1045){ 40 | $errorMsg='连接数据库失败:数据库用户名或密码填写错误!'; 41 | }elseif($e->getCode() == 1049){ 42 | $errorMsg='连接数据库失败:数据库名不存在!'; 43 | }else{ 44 | $errorMsg='连接数据库失败:'.$e->getMessage(); 45 | } 46 | return json(['code'=>0, 'msg'=>$errorMsg]); 47 | } 48 | $DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); 49 | $DB->exec("set sql_mode = ''"); 50 | $DB->exec("set names utf8"); 51 | 52 | $sqls=file_get_contents(app()->getRootPath().'install.sql'); 53 | $sqls=explode(';', $sqls); 54 | $sqls[]="REPLACE INTO `".$mysql_prefix."config` VALUES ('syskey', '".random(16)."')"; 55 | $sqls[]="REPLACE INTO `".$mysql_prefix."config` VALUES ('admin_username', '".addslashes($admin_username)."')"; 56 | $sqls[]="REPLACE INTO `".$mysql_prefix."config` VALUES ('admin_password', '".addslashes($admin_password)."')"; 57 | $success=0;$error=0;$errorMsg=null; 58 | foreach ($sqls as $value) { 59 | $value=trim($value); 60 | if(empty($value))continue; 61 | $value = str_replace('cloud_',$mysql_prefix,$value); 62 | if($DB->exec($value)===false){ 63 | $error++; 64 | $dberror=$DB->errorInfo(); 65 | $errorMsg.=$dberror[2]."\n"; 66 | }else{ 67 | $success++; 68 | } 69 | } 70 | if(empty($errorMsg)){ 71 | if(!file_put_contents(app()->getRootPath().'.env', $configdata)){ 72 | return json(['code'=>0, 'msg'=>'保存失败,请确保网站根目录有写入权限']); 73 | } 74 | Cache::clear(); 75 | return json(['code'=>1, 'msg'=>'安装完成!成功执行SQL语句'.$success.'条']); 76 | }else{ 77 | return json(['code'=>0, 'msg'=>$errorMsg]); 78 | } 79 | } 80 | return view(); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/event.php: -------------------------------------------------------------------------------- 1 | [ 5 | ], 6 | 7 | 'listen' => [ 8 | 'AppInit' => [], 9 | 'HttpRun' => [], 10 | 'HttpEnd' => [], 11 | 'LogLevel' => [], 12 | 'LogWrite' => [], 13 | ], 14 | 15 | 'subscribe' => [ 16 | ], 17 | ]; 18 | -------------------------------------------------------------------------------- /app/lib/Btapi.php: -------------------------------------------------------------------------------- 1 | BT_PANEL = $bt_panel; 14 | $this->BT_KEY = $bt_key; 15 | } 16 | 17 | //获取面板配置信息 18 | public function get_config(){ 19 | $url = $this->BT_PANEL.'/config?action=get_config'; 20 | 21 | $p_data = $this->GetKeyData(); 22 | 23 | $result = $this->curl($url,$p_data); 24 | 25 | $data = json_decode($result,true); 26 | return $data; 27 | } 28 | 29 | //获取已登录用户信息 30 | public function get_user_info(){ 31 | $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_user_info'; 32 | 33 | $p_data = $this->GetKeyData(); 34 | 35 | $result = $this->curl($url,$p_data); 36 | 37 | $data = json_decode($result,true); 38 | return $data; 39 | } 40 | 41 | //从云端获取插件列表 42 | public function get_plugin_list(){ 43 | $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_plugin_list'; 44 | 45 | $p_data = $this->GetKeyData(); 46 | 47 | $result = $this->curl($url,$p_data); 48 | 49 | $data = json_decode($result,true); 50 | return $data; 51 | } 52 | 53 | //下载插件包,返回文件路径 54 | public function get_plugin_filename($plugin_name, $version){ 55 | $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin'; 56 | 57 | $p_data = $this->GetKeyData(); 58 | $p_data['plugin_name'] = $plugin_name; 59 | $p_data['version'] = $version; 60 | 61 | $result = $this->curl($url,$p_data); 62 | 63 | $data = json_decode($result,true); 64 | return $data; 65 | } 66 | 67 | //下载插件主程序文件,返回文件路径 68 | public function get_plugin_main_filename($plugin_name, $version){ 69 | $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin_main'; 70 | 71 | $p_data = $this->GetKeyData(); 72 | $p_data['plugin_name'] = $plugin_name; 73 | $p_data['version'] = $version; 74 | 75 | $result = $this->curl($url,$p_data); 76 | 77 | $data = json_decode($result,true); 78 | return $data; 79 | } 80 | 81 | //解密插件主程序py代码,返回文件路径 82 | public function get_decode_plugin_main($plugin_name, $version){ 83 | $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=decode_plugin_main'; 84 | 85 | $p_data = $this->GetKeyData(); 86 | $p_data['plugin_name'] = $plugin_name; 87 | $p_data['version'] = $version; 88 | 89 | $result = $this->curl($url,$p_data); 90 | 91 | $data = json_decode($result,true); 92 | return $data; 93 | } 94 | 95 | //下载插件其他文件,返回文件路径 96 | public function get_plugin_other_filename($fname){ 97 | $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin_other'; 98 | 99 | $p_data = $this->GetKeyData(); 100 | $p_data['fname'] = $fname; 101 | 102 | $result = $this->curl($url,$p_data); 103 | 104 | $data = json_decode($result,true); 105 | return $data; 106 | } 107 | 108 | //下载文件 109 | public function download($filename, $localpath){ 110 | $url = $this->BT_PANEL.'/download'; 111 | 112 | $p_data = $this->GetKeyData(); 113 | $p_data['filename'] = $filename; 114 | 115 | $result = $this->curl_download($url.'?'.http_build_query($p_data), $localpath); 116 | 117 | return $result; 118 | } 119 | 120 | //获取文件base64 121 | public function get_file($filename){ 122 | $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_file'; 123 | 124 | $p_data = $this->GetKeyData(); 125 | $p_data['filename'] = $filename; 126 | 127 | $result = $this->curl($url,$p_data); 128 | 129 | $data = json_decode($result,true); 130 | return $data; 131 | } 132 | 133 | //购买第三方插件 134 | public function create_plugin_other_order($pid){ 135 | $url = $this->BT_PANEL.'/auth?action=create_plugin_other_order'; 136 | 137 | $p_data = $this->GetKeyData(); 138 | $p_data['pid'] = $pid; 139 | $p_data['cycle'] = '999'; 140 | $p_data['type'] = '0'; 141 | 142 | $result = $this->curl($url,$p_data); 143 | 144 | $data = json_decode($result,true); 145 | return $data; 146 | } 147 | 148 | //获取一键部署列表 149 | public function get_deplist(){ 150 | $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_deplist'; 151 | 152 | $p_data = $this->GetKeyData(); 153 | 154 | $result = $this->curl($url,$p_data); 155 | 156 | $data = json_decode($result,true); 157 | return $data; 158 | } 159 | 160 | //BTWAF-获取蜘蛛列表 161 | public function btwaf_getspiders(){ 162 | $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=btwaf_getspiders'; 163 | 164 | $p_data = $this->GetKeyData(); 165 | 166 | $result = $this->curl($url,$p_data); 167 | $result = str_replace("\u0000", '', $result); 168 | 169 | $data = json_decode($result,true); 170 | return $data; 171 | } 172 | 173 | //BTWAF-获取堡塔恶意情报IP库 174 | public function btwaf_getmalicious(){ 175 | $url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=btwaf_getmalicious'; 176 | 177 | $p_data = $this->GetKeyData(); 178 | 179 | $result = $this->curl($url,$p_data); 180 | 181 | $data = json_decode($result,true); 182 | return $data; 183 | } 184 | 185 | 186 | private function GetKeyData(){ 187 | $now_time = time(); 188 | $p_data = array( 189 | 'request_token' => md5($now_time.''.md5($this->BT_KEY)), 190 | 'request_time' => $now_time 191 | ); 192 | return $p_data; 193 | } 194 | 195 | 196 | private function curl($url, $data = null, $timeout = 60) 197 | { 198 | //定义cookie保存位置 199 | $cookie_file=app()->getRuntimePath().md5($this->BT_PANEL).'.cookie'; 200 | if(!file_exists($cookie_file)){ 201 | touch($cookie_file); 202 | } 203 | 204 | $ch = curl_init(); 205 | curl_setopt($ch, CURLOPT_URL, $url); 206 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); 207 | if($data){ 208 | curl_setopt($ch, CURLOPT_POST, 1); 209 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 210 | } 211 | curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); 212 | curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file); 213 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 214 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 215 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 216 | $output = curl_exec($ch); 217 | curl_close($ch); 218 | return $output; 219 | } 220 | 221 | private function curl_download($url, $localpath, $timeout = 300) 222 | { 223 | //定义cookie保存位置 224 | $cookie_file=app()->getRuntimePath().md5($this->BT_PANEL).'.cookie'; 225 | if(!file_exists($cookie_file)){ 226 | touch($cookie_file); 227 | } 228 | 229 | $ch = curl_init(); 230 | curl_setopt($ch, CURLOPT_URL, $url); 231 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); 232 | curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file); 233 | curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file); 234 | $fp = fopen($localpath, 'w+'); 235 | curl_setopt($ch, CURLOPT_FILE, $fp); 236 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 237 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 238 | curl_exec($ch); 239 | if (curl_errno($ch)) { 240 | $message = curl_error($ch); 241 | curl_close($ch); 242 | fclose($fp); 243 | throw new Exception('下载文件失败:'.$message); 244 | } 245 | $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 246 | if($httpcode>299){ 247 | curl_close($ch); 248 | fclose($fp); 249 | throw new Exception('下载文件失败:HTTPCODE-'.$httpcode); 250 | } 251 | curl_close($ch); 252 | fclose($fp); 253 | return true; 254 | } 255 | } -------------------------------------------------------------------------------- /app/lib/Plugins.php: -------------------------------------------------------------------------------- 1 | get_plugin_list(); 34 | self::save_plugin_list($result, $os); 35 | } 36 | 37 | //保存插件列表 38 | private static function save_plugin_list($data, $os){ 39 | $data['ip'] = '127.0.0.1'; 40 | if($os == 'en'){ 41 | $data['serverId'] = ''; 42 | $data['aln'] = self::get_aln(); 43 | $data['pro'] = 0; 44 | $data['pro_authorization_sn'] = '0'; 45 | if(!empty($data['authorization_map'])){ 46 | foreach($data['authorization_map'] as $code => &$plugin){ 47 | if($code != '0' && isset($plugin['end_time'])) $plugin['end_time'] = 0; 48 | } 49 | } 50 | if(isset($data['expansions']['mail'])){ 51 | $data['expansions']['mail']['total'] = 2000000; 52 | $data['expansions']['mail']['available'] = 2000000; 53 | } 54 | }else{ 55 | $data['serverid'] = ''; 56 | $data['aln'] = self::get_aln(); 57 | $data['beta'] = 0; 58 | $data['uid'] = 1; 59 | $data['skey'] = ''; 60 | $data['pro'] = -1; 61 | $data['ltd'] = strtotime('+10 year'); 62 | } 63 | foreach($data['list'] as &$plugin){ 64 | if(isset($plugin['endtime'])) $plugin['endtime'] = 0; 65 | } 66 | $json_file = get_data_dir($os).'config/plugin_list.json'; 67 | if(!file_put_contents($json_file, json_encode($data))){ 68 | throw new Exception('保存插件列表失败,文件无写入权限'); 69 | } 70 | } 71 | 72 | //多账号数量 73 | private static function get_aln($count = '9999'){ 74 | $key = 'FB8upo8XMgP5by54'; 75 | $iv = 'lOrrq3lNEURZNdK7'; 76 | return openssl_encrypt($count, 'aes-128-cbc', $key, 0, $iv); 77 | } 78 | 79 | //获取插件列表 80 | public static function get_plugin_list($os = 'Linux'){ 81 | $json_file = get_data_dir($os).'config/plugin_list.json'; 82 | if(file_exists($json_file)){ 83 | $data = file_get_contents($json_file); 84 | $json_arr = json_decode($data, true); 85 | if($json_arr){ 86 | return $json_arr; 87 | } 88 | } 89 | return false; 90 | } 91 | 92 | //获取一个插件信息 93 | public static function get_plugin_info($name, $os = 'Linux'){ 94 | $json_arr = self::get_plugin_list($os); 95 | if(!$json_arr) return null; 96 | foreach($json_arr['list'] as $plugin){ 97 | if($plugin['name'] == $name){ 98 | return $plugin; 99 | } 100 | } 101 | return null; 102 | } 103 | 104 | //下载插件(自动判断是否第三方) 105 | public static function download_plugin($plugin_name, $version, $os = 'Linux'){ 106 | $plugin_info = Plugins::get_plugin_info($plugin_name, $os); 107 | if(!$plugin_info) throw new Exception('未找到该插件信息'); 108 | $btapi = self::get_btapi($os); 109 | $btapi->download_plugin($plugin_name, $version, $plugin_info); 110 | } 111 | 112 | //下载插件主程序文件 113 | public static function download_plugin_main($plugin_name, $version, $os = 'Linux'){ 114 | $btapi = self::get_btapi($os); 115 | $btapi->download_plugin_main($plugin_name, $version); 116 | } 117 | 118 | //本地解密插件主程序文件 119 | public static function decode_plugin_main_local($main_filepath, $os = 'Linux'){ 120 | $btapi = new BtPlugins($os); 121 | return $btapi->decode_plugin_main_local($main_filepath); 122 | } 123 | 124 | //本地解密模块文件 125 | public static function decode_module_file($filepath){ 126 | $src = file_get_contents($filepath); 127 | if($src===false)throw new Exception('文件打开失败'); 128 | if(!$src || strpos($src, 'import ')!==false)return 0; 129 | $key = 'Z2B87NEAS2BkxTrh'; 130 | $iv = 'WwadH66EGWpeeTT6'; 131 | $data_arr = explode("\n", $src); 132 | $de_text = ''; 133 | foreach($data_arr as $data){ 134 | $data = trim($data); 135 | if(!empty($data)){ 136 | $tmp = openssl_decrypt($data, 'aes-128-cbc', $key, 0, $iv); 137 | if($tmp !== false) $de_text .= $tmp; 138 | } 139 | } 140 | if(!empty($de_text) && strpos($de_text, 'import ')!==false){ 141 | file_put_contents($filepath, $de_text); 142 | return 1; 143 | } 144 | return 2; 145 | } 146 | 147 | //刷新一键部署列表 148 | public static function refresh_deplist($os = 'Linux'){ 149 | $btapi = self::get_btapi($os); 150 | $result = $btapi->get_deplist(); 151 | $json_file = get_data_dir($os).'config/deployment_list.json'; 152 | if(!file_put_contents($json_file, json_encode($result))){ 153 | throw new Exception('保存一键部署列表失败,文件无写入权限'); 154 | } 155 | } 156 | 157 | //获取一键部署列表 158 | public static function get_deplist($os = 'Linux'){ 159 | $json_file = get_data_dir($os).'config/deployment_list.json'; 160 | if(file_exists($json_file)){ 161 | $data = file_get_contents($json_file); 162 | $json_arr = json_decode($data, true); 163 | if($json_arr){ 164 | return $json_arr; 165 | } 166 | } 167 | return false; 168 | } 169 | 170 | //获取蜘蛛IP列表 171 | public static function btwaf_getspiders(){ 172 | $result = cache('btwaf_getspiders'); 173 | if($result){ 174 | return $result; 175 | } 176 | $btapi = self::get_btapi('Linux'); 177 | $result = $btapi->btwaf_getspiders(); 178 | cache('btwaf_getspiders', $result, 3600 * 24 * 3); 179 | return $result; 180 | } 181 | 182 | //分类获取蜘蛛IP列表 183 | public static function get_spider($type){ 184 | $result = cache('get_spider_'.$type); 185 | if($result){ 186 | return $result; 187 | } 188 | $url = 'https://www.bt.cn/api/panel/get_spider?spider='.$type; 189 | $data = get_curl($url); 190 | $result = json_decode($data, true); 191 | if(!$result) return []; 192 | cache('get_spider_'.$type, $result, 3600 * 24); 193 | return $result; 194 | } 195 | 196 | //获取堡塔恶意情报IP库 197 | public static function btwaf_getmalicious(){ 198 | $btapi = self::get_btapi('Linux'); 199 | $result = $btapi->btwaf_getmalicious(); 200 | return $result; 201 | } 202 | 203 | } -------------------------------------------------------------------------------- /app/lib/ThirdPlugins.php: -------------------------------------------------------------------------------- 1 | os = $os; 16 | if($os == 'en'){ 17 | $url = config_get('enbt_surl'); 18 | }elseif($os == 'Windows'){ 19 | $url = config_get('wbt_surl'); 20 | }else{ 21 | $url = config_get('bt_surl'); 22 | } 23 | if(!$url) throw new Exception('请先配置好第三方云端首页URL'); 24 | $this->url = $url; 25 | } 26 | 27 | //获取插件列表 28 | public function get_plugin_list() 29 | { 30 | if($this->os == 'en'){ 31 | $url = $this->url . 'api/panel/getSoftListEn'; 32 | }elseif($this->os == 'Windows'){ 33 | $url = $this->url . 'api/wpanel/get_soft_list'; 34 | }else{ 35 | $url = $this->url . 'api/panel/get_soft_list'; 36 | } 37 | $res = $this->curl($url); 38 | $result = json_decode($res, true); 39 | if($result && isset($result['list']) && isset($result['type'])){ 40 | if(empty($result['list']) || empty($result['type'])){ 41 | throw new Exception('获取插件列表失败:插件列表为空'); 42 | } 43 | return $result; 44 | }else{ 45 | throw new Exception('获取插件列表失败:'.(isset($result['msg'])?$result['msg']:'第三方云端连接失败')); 46 | } 47 | } 48 | 49 | //下载插件(自动判断是否第三方) 50 | public function download_plugin($plugin_name, $version, $plugin_info){ 51 | if($plugin_info['type'] == 10 && isset($plugin_info['versions'][0]['download'])){ 52 | $fname = $plugin_info['versions'][0]['download']; 53 | $filemd5 = $plugin_info['versions'][0]['md5']; 54 | $this->download_plugin_other($fname, $filemd5); 55 | if(isset($plugin_info['min_image']) && strpos($plugin_info['min_image'], 'fname=')){ 56 | $fname = substr($plugin_info['min_image'], strpos($plugin_info['min_image'], '?fname=')+7); 57 | $this->download_plugin_other($fname); 58 | } 59 | }else{ 60 | $this->download_plugin_package($plugin_name, $version); 61 | } 62 | } 63 | 64 | //下载插件包 65 | private function download_plugin_package($plugin_name, $version){ 66 | $filepath = get_data_dir($this->os).'plugins/package/'.$plugin_name.'-'.$version.'.zip'; 67 | 68 | $url = $this->url . 'down/download_plugin'; 69 | $post = ['name'=>$plugin_name, 'version'=>$version, 'os'=>$this->os]; 70 | $this->curl_download($url, $post, $filepath); 71 | 72 | if(file_exists($filepath)){ 73 | $handle = fopen($filepath, "rb"); 74 | $file_head = fread($handle, 4); 75 | fclose($handle); 76 | if(bin2hex($file_head) === '504b0304'){ 77 | $zip = new ZipArchive; 78 | if ($zip->open($filepath) === true) 79 | { 80 | $zip->close(); 81 | return true; 82 | }else{ 83 | @unlink($filepath); 84 | throw new Exception('插件包解压缩失败'); 85 | } 86 | }else{ 87 | $handle = fopen($filepath, "rb"); 88 | $errmsg = htmlspecialchars(fgets($handle)); 89 | fclose($handle); 90 | @unlink($filepath); 91 | throw new Exception('下载插件包失败:'.($errmsg?$errmsg:'未知错误')); 92 | } 93 | }else{ 94 | throw new Exception('下载插件包失败,本地文件不存在'); 95 | } 96 | } 97 | 98 | //下载插件主程序文件 99 | public function download_plugin_main($plugin_name, $version){ 100 | $filepath = get_data_dir($this->os).'plugins/main/'.$plugin_name.'-'.$version.'.dat'; 101 | 102 | $url = $this->url . 'down/download_plugin_main'; 103 | $post = ['name'=>$plugin_name, 'version'=>$version, 'os'=>$this->os]; 104 | $this->curl_download($url, $post, $filepath); 105 | 106 | if(file_exists($filepath)){ 107 | $line = count(file($filepath)); 108 | if($line > 3) return true; 109 | 110 | $handle = fopen($filepath, "rb"); 111 | $errmsg = htmlspecialchars(fgets($handle)); 112 | fclose($handle); 113 | @unlink($filepath); 114 | throw new Exception('下载插件主程序文件失败:'.($errmsg?$errmsg:'未知错误')); 115 | }else{ 116 | throw new Exception('下载插件主程序文件失败,本地文件不存在'); 117 | } 118 | } 119 | 120 | //下载插件其他文件 121 | private function download_plugin_other($fname, $filemd5 = null){ 122 | $filepath = get_data_dir().'plugins/other/'.$fname; 123 | @mkdir(dirname($filepath), 0777, true); 124 | 125 | $url = $this->url . 'api/Pluginother/get_file?fname='.urlencode($fname); 126 | $this->curl_download($url, false, $filepath); 127 | 128 | if(file_exists($filepath)){ 129 | $handle = fopen($filepath, "rb"); 130 | $file_head = fread($handle, 15); 131 | fclose($handle); 132 | if($file_head === '{"status":false'){ 133 | $res = file_get_contents($filepath); 134 | $result = json_decode($res, true); 135 | @unlink($filepath); 136 | throw new Exception('下载插件文件失败:'.($result?$result['msg']:'未知错误')); 137 | } 138 | if($filemd5 && md5_file($filepath) != $filemd5){ 139 | $msg = filesize($filepath) < 300 ? file_get_contents($filepath) : '插件文件MD5校验失败'; 140 | @unlink($filepath); 141 | throw new Exception($msg); 142 | } 143 | return true; 144 | }else{ 145 | throw new Exception('下载插件文件失败,本地文件不存在'); 146 | } 147 | } 148 | 149 | //获取一键部署列表 150 | public function get_deplist(){ 151 | $url = $this->url . 'api/panel/get_deplist'; 152 | $post = ['os' => $this->os]; 153 | $res = $this->curl($url, http_build_query($post)); 154 | $result = json_decode($res, true); 155 | if($result && isset($result['list']) && isset($result['type'])){ 156 | if(empty($result['list']) || empty($result['type'])){ 157 | throw new Exception('获取一键部署列表失败:一键部署列表为空'); 158 | } 159 | return $result; 160 | }else{ 161 | throw new Exception('获取一键部署列表失败:'.(isset($result['msg'])?$result['msg']:'第三方云端连接失败')); 162 | } 163 | } 164 | 165 | //获取蜘蛛IP列表 166 | public function btwaf_getspiders(){ 167 | $url = $this->url . 'api/bt_waf/getSpiders'; 168 | $res = $this->curl($url); 169 | $result = json_decode($res, true); 170 | if(isset($result['status']) && !$result['status']){ 171 | throw new Exception(isset($result['msg'])?$result['msg']:'获取失败'); 172 | }else{ 173 | return $result; 174 | } 175 | } 176 | 177 | //获取堡塔恶意情报IP库 178 | public function btwaf_getmalicious(){ 179 | $url = $this->url . 'api/bt_waf/get_malicious'; 180 | $res = $this->curl($url); 181 | $result = json_decode($res, true); 182 | if(isset($result['success'])){ 183 | return $result; 184 | }else{ 185 | throw new Exception(isset($result['res'])?$result['res']:'获取失败'); 186 | } 187 | } 188 | 189 | private function curl($url, $post = 0){ 190 | $ua = "Mozilla/5.0 (BtCloud; ".request()->root(true).")"; 191 | return get_curl($url, $post, 0, 0, 0, $ua); 192 | } 193 | 194 | private function curl_download($url, $post, $localpath, $timeout = 300) 195 | { 196 | $ch = curl_init(); 197 | curl_setopt($ch, CURLOPT_URL, $url); 198 | curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); 199 | $fp = fopen($localpath, 'w+'); 200 | curl_setopt($ch, CURLOPT_FILE, $fp); 201 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 202 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 203 | curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (BtCloud; ".request()->root(true).")"); 204 | if($post){ 205 | curl_setopt($ch, CURLOPT_POST, 1); 206 | curl_setopt($ch, CURLOPT_POSTFIELDS, $post); 207 | } 208 | curl_exec($ch); 209 | if (curl_errno($ch)) { 210 | $message = curl_error($ch); 211 | curl_close($ch); 212 | fclose($fp); 213 | throw new Exception('下载文件失败:'.$message); 214 | } 215 | $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 216 | if($httpcode>299){ 217 | curl_close($ch); 218 | fclose($fp); 219 | throw new Exception('下载文件失败:HTTPCODE-'.$httpcode); 220 | } 221 | curl_close($ch); 222 | fclose($fp); 223 | return true; 224 | } 225 | 226 | } -------------------------------------------------------------------------------- /app/middleware.php: -------------------------------------------------------------------------------- 1 | time()) { 19 | $islogin = true; 20 | } 21 | } 22 | } 23 | request()->islogin = $islogin; 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/middleware/CheckAdmin.php: -------------------------------------------------------------------------------- 1 | islogin) { 12 | if ($request->isAjax() || !$request->isGet()) { 13 | return json(['code'=>-1, 'msg'=>'未登录'])->code(401); 14 | } 15 | return redirect((string)url('/admin/login')); 16 | } 17 | return $next($request); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/middleware/LoadConfig.php: -------------------------------------------------------------------------------- 1 | getRootPath().'.env')){ 22 | if(strpos(request()->url(),'/installapp')===false){ 23 | return redirect((string)url('/installapp'))->header([ 24 | 'Cache-Control' => 'no-store, no-cache, must-revalidate', 25 | 'Pragma' => 'no-cache', 26 | ]); 27 | }else{ 28 | return $next($request); 29 | } 30 | } 31 | 32 | $res = Db::name('config')->cache('configs',0)->column('value','key'); 33 | Config::set($res, 'sys'); 34 | 35 | View::assign('cdnpublic', '//lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/'); 36 | return $next($request)->header([ 37 | 'Cache-Control' => 'no-store, no-cache, must-revalidate', 38 | 'Pragma' => 'no-cache', 39 | ]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/middleware/RefererCheck.php: -------------------------------------------------------------------------------- 1 | Request::class, 8 | 'think\exception\Handle' => ExceptionHandle::class, 9 | ]; 10 | -------------------------------------------------------------------------------- /app/script/cacert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OPENSSL_CHECK=$(which openssl) 4 | if [ "$?" != "0" ]; then 5 | echo "未安装OpenSSL" 6 | exit 1 7 | fi 8 | 9 | if [ ! -f ca.key ] && [ ! -f ca.crt ]; then 10 | openssl genrsa -out ca.key 2048 11 | openssl req -new -x509 -utf8 -days 3650 -extensions v3_ca -subj "/C=CN/O=宝塔面板/CN=宝塔面板" -key ca.key -out ca.crt 12 | fi 13 | 14 | openssl genrsa -out server.key 2048 15 | openssl req -new -nodes -key server.key -subj "/C=CN/O=BTPanel/CN=BTPanel" -out server.csr 16 | openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -extensions req_ext 17 | 18 | cat ca.crt >> server.crt 19 | 20 | openssl pkcs12 -export -out baota_root.pfx -inkey server.key -in server.crt -password pass: 21 | if [ "$?" != "0" ]; then 22 | echo "生成CA根证书失败" 23 | exit 1 24 | fi 25 | 26 | mkdir -p ../../public/ssl 27 | \cp baota_root.pfx ../../public/ssl/baota_root.pfx 28 | \cp ca.crt ../../public/ssl/baota_root.crt 29 | rm -f server.crt server.key server.csr 30 | 31 | echo "生成CA根证书成功" 32 | -------------------------------------------------------------------------------- /app/script/convert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | Linux_Version="9.6.0" 4 | Windows_Version="8.2.2" 5 | Aapanel_Version="7.0.16" 6 | Btm_Version="2.3.0" 7 | 8 | FILES=( 9 | public/install/src/panel6.zip 10 | public/install/update/LinuxPanel-${Linux_Version}.zip 11 | public/install/install_panel.sh 12 | public/install/update_panel.sh 13 | public/install/update6.sh 14 | public/win/install/panel_update.py 15 | public/win/panel/panel_${Windows_Version}.zip 16 | public/win/panel/data/api.py 17 | public/win/panel/data/setup.py 18 | public/install/src/bt-monitor-${Btm_Version}.zip 19 | public/install/install_btmonitor.sh 20 | public/install/update_btmonitor.sh 21 | public/install/src/panel_7_en.zip 22 | public/install/update/LinuxPanel_EN-${Aapanel_Version}.zip 23 | public/install/install_7.0_en.sh 24 | public/install/update_7.x_en.sh 25 | ) 26 | 27 | DIR=$1 28 | SITEURL=$2 29 | 30 | if [ ! -d "$DIR" ]; then 31 | echo "网站目录不存在" 32 | exit 1 33 | fi 34 | if [ "$SITEURL" = "" ]; then 35 | echo "网站URL不正确" 36 | exit 1 37 | fi 38 | 39 | function handleFile() 40 | { 41 | Filename=$1 42 | if [ "${Filename##*.}" = "zip" ]; then 43 | handleZipFile $Filename 44 | else 45 | handleTextFile $Filename 46 | fi 47 | } 48 | 49 | function handleZipFile() 50 | { 51 | Filename=$1 52 | mkdir -p /tmp/package 53 | unzip -o -q $Filename -d /tmp/package 54 | grep -rl --include=\*.py --include=\*.sh --include=index.js 'http://www.example.com' /tmp/package | xargs -I @ sed -i "s|http://www.example.com|${SITEURL}|g" @ 55 | Sprit_SITEURK=${SITEURL//\//\\\\\/} 56 | grep -rl --include=\*.sh 'http:\\\/\\\/www.example.com' /tmp/package | xargs -I @ sed -i "s|http:\\\/\\\/www.example.com|${Sprit_SITEURK}|g" @ 57 | rm -f $Filename 58 | cd /tmp/package && zip -9 -q -r $Filename * && cd - 59 | rm -rf /tmp/package 60 | } 61 | 62 | function handleTextFile() 63 | { 64 | sed -i "s|http://www.example.com|${SITEURL}|g" $1 65 | } 66 | 67 | 68 | echo "==========================" 69 | echo "正在处理中..." 70 | echo "==========================" 71 | 72 | for File in ${FILES[@]} 73 | do 74 | Filename="${DIR}${File}" 75 | if [ -f "$Filename" ]; then 76 | handleFile $Filename 77 | echo -e "成功处理文件:\033[32m${Filename}\033[0m" 78 | else 79 | echo -e "文件不存在:\033[33m${Filename}\033[0m" 80 | fi 81 | done 82 | 83 | echo "==========================" 84 | echo "处理完成" 85 | echo "==========================" 86 | -------------------------------------------------------------------------------- /app/service.php: -------------------------------------------------------------------------------- 1 | 5 |
6 |
7 |

一键部署列表

8 |
9 |
10 |
Linux面板
11 |
列表文件更新时间:{$deplist_linux_time}重新获取
12 |
13 |
14 |
Windows面板
15 |
列表文件更新时间:{$deplist_win_time}重新获取
16 |
17 |
18 |
19 | 20 | 49 | {/block} -------------------------------------------------------------------------------- /app/view/admin/index.html: -------------------------------------------------------------------------------- 1 | {extend name="admin/layout" /} 2 | {block name="title"}宝塔第三方云端管理中心{/block} 3 | {block name="main"} 4 | 14 |
15 |
16 |
17 |

后台管理首页

18 |
19 |
宝塔插件统计:共有 {$stat.total} 个,其中免费插件 {$stat.free} 个,专业版插件 {$stat.pro} 个,企业版插件 {$stat.ltd} 个,第三方插件 {$stat.third} 个
20 |
使用记录统计:历史总共数量:{$stat.record_total},正在使用数量:{$stat.record_isuse}
21 |
任务运行情况:上次运行时间:{$stat.runtime|raw}  查看详情
22 | 23 |
24 |
25 |
26 |
27 |

服务器信息

28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
框架版本{$info.framework_version}
PHP版本{$info.php_version}
MySQL版本{$info.mysql_version}
WEB软件{$info.software}
操作系统{$info.os}
服务器时间{$info.date}
57 |
58 |
59 |
60 | {/block} -------------------------------------------------------------------------------- /app/view/admin/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {block name="title"}标题{/block} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 76 | {block name="main"}主内容{/block} 77 | 78 | -------------------------------------------------------------------------------- /app/view/admin/list.html: -------------------------------------------------------------------------------- 1 | {extend name="admin/layout" /} 2 | {block name="title"}黑白名单{/block} 3 | {block name="main"} 4 | 7 |
8 |
9 | 10 | 13 | 14 | {if $type=='black' && config_get('whitelist')=='1'} 15 |
提示:当前为白名单模式,黑名单列表里面的记录不会生效。
16 | {/if} 17 | {if $type=='white' && config_get('whitelist')=='0'} 18 |
提示:当前未开启白名单模式,白名单列表里面的记录不会生效。
19 | {/if} 20 | {if $type=='black'} 21 |
添加到黑名单列表中的服务器IP将无法使用此云端
22 | {/if} 23 | {if $type=='white'} 24 |
只有添加到白名单列表中的服务器IP才可以使用此云端
25 | {/if} 26 | 27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |   35 | 重置  36 | 添加  37 |
38 |
39 |
40 | 41 | 42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | 209 | {/block} -------------------------------------------------------------------------------- /app/view/admin/log.html: -------------------------------------------------------------------------------- 1 | {extend name="admin/layout" /} 2 | {block name="title"}操作日志{/block} 3 | {block name="main"} 4 | 6 |
7 |
8 | 9 |
10 |
11 |
12 | 13 | 14 |
15 |
16 |   17 | 重置  18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 |
26 | 27 | 28 | 29 | 30 | 74 | {/block} -------------------------------------------------------------------------------- /app/view/admin/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 管理员登录 8 | 9 | 10 | 11 | 15 | 16 | 17 | 37 |
38 |
39 |
40 |

管理员登录

41 |
42 |
43 |
44 | 45 | 46 |

47 |
48 | 49 | 50 |

51 |
52 | 53 | 54 | 55 | 56 | 57 |

58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | 67 | 99 | 100 | -------------------------------------------------------------------------------- /app/view/admin/pluginswin.html: -------------------------------------------------------------------------------- 1 | {extend name="admin/layout" /} 2 | {block name="title"}插件列表{/block} 3 | {block name="main"} 4 | 26 | 45 |
46 |
47 | 48 |
49 |
50 |
51 | 52 | 53 |
54 |
55 | 57 |
58 |
59 |   60 | 重置  61 | 刷新列表  62 |   63 | 64 |
65 |
66 |
67 | 68 | 69 |
70 |
71 |
72 | 73 | 74 | 75 | 76 | 276 | {/block} -------------------------------------------------------------------------------- /app/view/admin/record.html: -------------------------------------------------------------------------------- 1 | {extend name="admin/layout" /} 2 | {block name="title"}使用记录{/block} 3 | {block name="main"} 4 | 6 |
7 |
8 | 9 |
10 |
11 |
12 | 13 | 14 |
15 |
16 |   17 | 重置  18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 |
26 | 27 | 28 | 29 | 30 | 67 | {/block} -------------------------------------------------------------------------------- /app/view/admin/ssl.html: -------------------------------------------------------------------------------- 1 | {extend name="admin/layout" /} 2 | {block name="title"}自签名SSL证书生成{/block} 3 | {block name="main"} 4 | 11 |
12 |
13 |
14 |

自签名SSL证书生成

15 |
16 | {if $isca} 17 |
下载CA证书并导入,可解决浏览器不安全提醒。
Windows:baota_root.pfx(密码为空),Mac/Linux:baota_root.crt
18 |
19 |
20 | 21 | 22 |
23 |
24 | 25 | 26 |
27 |
28 | 29 | 30 |
31 |
32 | 33 |
34 | 44 |
45 | {else} 46 | 47 |
执行以下命令,生成自签名CA证书。然后,可通过接口或当前页面生成SSL证书,用于面板访问。
48 |
cd {:app()->getRootPath()}app/script && chmod +x cacert.sh && ./cacert.sh

49 | {/if} 50 |
51 |
52 | 53 | 86 | {/block} -------------------------------------------------------------------------------- /app/view/dispatch_jump.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 温馨提示 6 | 7 | 8 | 28 | 29 | 30 |
31 |
32 | 33 |
34 |

{$msg}

35 | {if $url} 36 |

37 | 页面将在 {$wait} 秒后自动跳转 38 |

39 | {/if} 40 |

41 | 返回上一页 42 | {if $url} 43 | 立即跳转 44 | {/if} 45 |

46 |
47 | 59 | 60 | -------------------------------------------------------------------------------- /app/view/install/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 宝塔第三方云端 - 安装程序 7 | 8 | 9 | 151 | 152 | 153 | 154 |
155 |

156 | 157 | 158 | 159 |

160 |

宝塔第三方云端 - 安装程序

161 |
162 | 163 |
164 | 165 | 166 | 167 | 168 |
169 |
170 | 171 | 172 |
173 | 174 |
175 | 176 | 177 |
178 | 179 |
180 | 181 | 182 |
183 | 184 |
185 | 186 | 187 |
188 | 189 |
190 | 191 | 192 |
193 | 194 |
195 | 196 | 197 |
198 |
199 | 200 |
201 |
202 | 203 | 204 |
205 | 206 |
207 | 208 | 209 |
210 |
211 | 212 |
213 | 214 | 215 | 216 |
217 |
218 |
219 |
220 | 221 | 267 | 268 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "btpanel/cloud", 3 | "description": "BTPanel third cloud site", 4 | "type": "project", 5 | "keywords": [ 6 | "btpanel", 7 | "baota", 8 | "aapanel" 9 | ], 10 | "homepage": "https://www.bt.cn/", 11 | "license": "Apache-2.0", 12 | "authors": [], 13 | "require": { 14 | "php": ">=7.2.5", 15 | "topthink/framework": "^6.0.0", 16 | "topthink/think-orm": "^2.0", 17 | "topthink/think-view": "^1.0", 18 | "topthink/think-captcha": "^3.0" 19 | }, 20 | "require-dev": { 21 | "symfony/var-dumper": "^4.2", 22 | "topthink/think-trace":"^1.0" 23 | }, 24 | "autoload": { 25 | "psr-4": { 26 | "app\\": "app" 27 | }, 28 | "psr-0": { 29 | "": "extend/" 30 | } 31 | }, 32 | "config": { 33 | "preferred-install": "dist" 34 | }, 35 | "scripts": { 36 | "post-autoload-dump": [ 37 | "@php think service:discover", 38 | "@php think vendor:publish" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /config/app.php: -------------------------------------------------------------------------------- 1 | env('app.host', ''), 9 | // 应用的命名空间 10 | 'app_namespace' => '', 11 | // 是否启用路由 12 | 'with_route' => true, 13 | // 默认应用 14 | 'default_app' => 'index', 15 | // 默认时区 16 | 'default_timezone' => 'Asia/Shanghai', 17 | 18 | // 应用映射(自动多应用模式有效) 19 | 'app_map' => [], 20 | // 域名绑定(自动多应用模式有效) 21 | 'domain_bind' => [], 22 | // 禁止URL访问的应用列表(自动多应用模式有效) 23 | 'deny_app_list' => [], 24 | 25 | // 异常页面的模板文件 26 | 'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl', 27 | 28 | // 错误显示信息,非调试模式有效 29 | 'error_message' => '页面错误!请稍后再试~', 30 | // 显示错误信息 31 | 'show_error_msg' => true, 32 | ]; 33 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('cache.driver', 'file'), 10 | 11 | // 缓存连接方式配置 12 | 'stores' => [ 13 | 'file' => [ 14 | // 驱动方式 15 | 'type' => 'File', 16 | // 缓存保存目录 17 | 'path' => '', 18 | // 缓存前缀 19 | 'prefix' => '', 20 | // 缓存有效期 0表示永久缓存 21 | 'expire' => 0, 22 | // 缓存标签前缀 23 | 'tag_prefix' => 'tag:', 24 | // 序列化机制 例如 ['serialize', 'unserialize'] 25 | 'serialize' => [], 26 | ], 27 | 'redis' => [ 28 | // 驱动方式 29 | 'type' => 'Redis', 30 | 'host' => '127.0.0.1', 31 | 'port' => 6379, 32 | 'password' => '', 33 | 'select' => 0, 34 | // 缓存有效期 0表示永久缓存 35 | 'expire' => 3600, 36 | 'prefix' => '', 37 | ], 38 | // 更多的缓存连接 39 | ], 40 | ]; 41 | -------------------------------------------------------------------------------- /config/captcha.php: -------------------------------------------------------------------------------- 1 | 4, 9 | // 验证码字符集合 10 | 'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY', 11 | // 验证码过期时间 12 | 'expire' => 1800, 13 | // 是否使用中文验证码 14 | 'useZh' => false, 15 | // 是否使用算术验证码 16 | 'math' => false, 17 | // 是否使用背景图 18 | 'useImgBg' => false, 19 | //验证码字符大小 20 | 'fontSize' => 25, 21 | // 是否使用混淆曲线 22 | 'useCurve' => true, 23 | //是否添加杂点 24 | 'useNoise' => true, 25 | // 验证码字体 不设置则随机 26 | 'fontttf' => '', 27 | //背景颜色 28 | 'bg' => [243, 251, 254], 29 | // 验证码图片高度 30 | 'imageH' => 0, 31 | // 验证码图片宽度 32 | 'imageW' => 0, 33 | 34 | // 添加额外的验证码设置 35 | // verify => [ 36 | // 'length'=>4, 37 | // ... 38 | //], 39 | ]; 40 | -------------------------------------------------------------------------------- /config/console.php: -------------------------------------------------------------------------------- 1 | [ 8 | 'updateall' => 'app\command\UpdateAll', 9 | 'decrypt' => 'app\command\DecryptFile', 10 | 'clean' => 'app\command\Clean', 11 | 'cleanvitejs' => 'app\command\CleanViteJs', 12 | ], 13 | ]; 14 | -------------------------------------------------------------------------------- /config/cookie.php: -------------------------------------------------------------------------------- 1 | 0, 8 | // cookie 保存路径 9 | 'path' => '/', 10 | // cookie 有效域名 11 | 'domain' => '', 12 | // cookie 启用安全传输 13 | 'secure' => false, 14 | // httponly设置 15 | 'httponly' => false, 16 | // 是否使用 setcookie 17 | 'setcookie' => true, 18 | // samesite 设置,支持 'strict' 'lax' 19 | 'samesite' => '', 20 | ]; 21 | -------------------------------------------------------------------------------- /config/database.php: -------------------------------------------------------------------------------- 1 | env('database.driver', 'mysql'), 6 | 7 | // 自定义时间查询规则 8 | 'time_query_rule' => [], 9 | 10 | // 自动写入时间戳字段 11 | // true为自动识别类型 false关闭 12 | // 字符串则明确指定时间字段类型 支持 int timestamp datetime date 13 | 'auto_timestamp' => true, 14 | 15 | // 时间字段取出后的默认时间格式 16 | 'datetime_format' => 'Y-m-d H:i:s', 17 | 18 | // 时间字段配置 配置格式:create_time,update_time 19 | 'datetime_field' => '', 20 | 21 | // 数据库连接配置信息 22 | 'connections' => [ 23 | 'mysql' => [ 24 | // 数据库类型 25 | 'type' => env('database.type', 'mysql'), 26 | // 服务器地址 27 | 'hostname' => env('database.hostname', '127.0.0.1'), 28 | // 数据库名 29 | 'database' => env('database.database', ''), 30 | // 用户名 31 | 'username' => env('database.username', 'root'), 32 | // 密码 33 | 'password' => env('database.password', ''), 34 | // 端口 35 | 'hostport' => env('database.hostport', '3306'), 36 | // 数据库连接参数 37 | 'params' => [], 38 | // 数据库编码默认采用utf8 39 | 'charset' => env('database.charset', 'utf8mb4'), 40 | // 数据库表前缀 41 | 'prefix' => env('database.prefix', ''), 42 | 43 | // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) 44 | 'deploy' => 0, 45 | // 数据库读写是否分离 主从式有效 46 | 'rw_separate' => false, 47 | // 读写分离后 主服务器数量 48 | 'master_num' => 1, 49 | // 指定从服务器序号 50 | 'slave_no' => '', 51 | // 是否严格检查字段是否存在 52 | 'fields_strict' => true, 53 | // 是否需要断线重连 54 | 'break_reconnect' => false, 55 | // 监听SQL 56 | 'trigger_sql' => env('app_debug', true), 57 | // 开启字段缓存 58 | 'fields_cache' => false, 59 | ], 60 | 61 | // 更多的数据库配置信息 62 | ], 63 | ]; 64 | -------------------------------------------------------------------------------- /config/filesystem.php: -------------------------------------------------------------------------------- 1 | env('filesystem.driver', 'local'), 6 | // 磁盘列表 7 | 'disks' => [ 8 | 'local' => [ 9 | 'type' => 'local', 10 | 'root' => app()->getRuntimePath() . 'storage', 11 | ], 12 | 'public' => [ 13 | // 磁盘类型 14 | 'type' => 'local', 15 | // 磁盘路径 16 | 'root' => app()->getRootPath() . 'public/storage', 17 | // 磁盘路径对应的外部URL路径 18 | 'url' => '/storage', 19 | // 可见性 20 | 'visibility' => 'public', 21 | ], 22 | // 更多的磁盘配置信息 23 | ], 24 | ]; 25 | -------------------------------------------------------------------------------- /config/lang.php: -------------------------------------------------------------------------------- 1 | env('lang.default_lang', 'zh-cn'), 9 | // 允许的语言列表 10 | 'allow_lang_list' => [], 11 | // 多语言自动侦测变量名 12 | 'detect_var' => 'lang', 13 | // 是否使用Cookie记录 14 | 'use_cookie' => true, 15 | // 多语言cookie变量 16 | 'cookie_var' => 'think_lang', 17 | // 多语言header变量 18 | 'header_var' => 'think-lang', 19 | // 扩展语言包 20 | 'extend_list' => [], 21 | // Accept-Language转义为对应语言包名称 22 | 'accept_language' => [ 23 | 'zh-hans-cn' => 'zh-cn', 24 | ], 25 | // 是否支持语言分组 26 | 'allow_group' => false, 27 | ]; 28 | -------------------------------------------------------------------------------- /config/log.php: -------------------------------------------------------------------------------- 1 | env('log.channel', 'file'), 9 | // 日志记录级别 10 | 'level' => [], 11 | // 日志类型记录的通道 ['error'=>'email',...] 12 | 'type_channel' => [], 13 | // 关闭全局日志写入 14 | 'close' => false, 15 | // 全局日志处理 支持闭包 16 | 'processor' => null, 17 | 18 | // 日志通道列表 19 | 'channels' => [ 20 | 'file' => [ 21 | // 日志记录方式 22 | 'type' => 'File', 23 | // 日志保存目录 24 | 'path' => '', 25 | // 单文件日志写入 26 | 'single' => false, 27 | // 独立日志级别 28 | 'apart_level' => [], 29 | // 最大日志文件数量 30 | 'max_files' => 0, 31 | // 使用JSON格式记录 32 | 'json' => false, 33 | // 日志处理 34 | 'processor' => null, 35 | // 关闭通道日志写入 36 | 'close' => false, 37 | // 日志输出格式化 38 | 'format' => '[%s][%s] %s', 39 | // 是否实时写入 40 | 'realtime_write' => false, 41 | ], 42 | // 其它日志通道配置 43 | ], 44 | 45 | ]; 46 | -------------------------------------------------------------------------------- /config/middleware.php: -------------------------------------------------------------------------------- 1 | [], 6 | // 优先级设置,此数组中的中间件会按照数组中的顺序优先执行 7 | 'priority' => [], 8 | ]; 9 | -------------------------------------------------------------------------------- /config/route.php: -------------------------------------------------------------------------------- 1 | '/', 9 | // URL伪静态后缀 10 | 'url_html_suffix' => '', 11 | // URL普通方式参数 用于自动生成 12 | 'url_common_param' => true, 13 | // 是否开启路由延迟解析 14 | 'url_lazy_route' => false, 15 | // 是否强制使用路由 16 | 'url_route_must' => true, 17 | // 合并路由规则 18 | 'route_rule_merge' => false, 19 | // 路由是否完全匹配 20 | 'route_complete_match' => false, 21 | // 访问控制器层名称 22 | 'controller_layer' => 'controller', 23 | // 空控制器名 24 | 'empty_controller' => 'Error', 25 | // 是否使用控制器后缀 26 | 'controller_suffix' => false, 27 | // 默认的路由变量规则 28 | 'default_route_pattern' => '[\w\.]+', 29 | // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则 30 | 'request_cache_key' => false, 31 | // 请求缓存有效期 32 | 'request_cache_expire' => null, 33 | // 全局请求缓存排除规则 34 | 'request_cache_except' => [], 35 | // 默认控制器名 36 | 'default_controller' => 'Index', 37 | // 默认操作名 38 | 'default_action' => 'index', 39 | // 操作方法后缀 40 | 'action_suffix' => '', 41 | // 默认JSONP格式返回的处理方法 42 | 'default_jsonp_handler' => 'jsonpReturn', 43 | // 默认JSONP处理方法 44 | 'var_jsonp_handler' => 'callback', 45 | ]; 46 | -------------------------------------------------------------------------------- /config/session.php: -------------------------------------------------------------------------------- 1 | 'PHPSESSID', 9 | // SESSION_ID的提交变量,解决flash上传跨域 10 | 'var_session_id' => '', 11 | // 驱动方式 支持file cache 12 | 'type' => 'file', 13 | // 存储连接标识 当type使用cache的时候有效 14 | 'store' => null, 15 | // 过期时间 16 | 'expire' => 1440, 17 | // 前缀 18 | 'prefix' => '', 19 | ]; 20 | -------------------------------------------------------------------------------- /config/trace.php: -------------------------------------------------------------------------------- 1 | 'Html', 8 | // 读取的日志通道名 9 | 'channel' => '', 10 | ]; 11 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | 'Think', 9 | // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法 10 | 'auto_rule' => 1, 11 | // 模板目录名 12 | 'view_dir_name' => 'view', 13 | // 模板后缀 14 | 'view_suffix' => 'html', 15 | // 模板文件名分隔符 16 | 'view_depr' => DIRECTORY_SEPARATOR, 17 | // 模板引擎普通标签开始标记 18 | 'tpl_begin' => '{', 19 | // 模板引擎普通标签结束标记 20 | 'tpl_end' => '}', 21 | // 标签库标签开始标记 22 | 'taglib_begin' => '{', 23 | // 标签库标签结束标记 24 | 'taglib_end' => '}', 25 | ]; 26 | -------------------------------------------------------------------------------- /data/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/config/.gitkeep -------------------------------------------------------------------------------- /data/en/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/en/config/.gitkeep -------------------------------------------------------------------------------- /data/en/plugins/folder/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/en/plugins/folder/.gitkeep -------------------------------------------------------------------------------- /data/en/plugins/main/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/en/plugins/main/.gitkeep -------------------------------------------------------------------------------- /data/en/plugins/package/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/en/plugins/package/.gitkeep -------------------------------------------------------------------------------- /data/plugins/folder/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/plugins/folder/.gitkeep -------------------------------------------------------------------------------- /data/plugins/main/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/plugins/main/.gitkeep -------------------------------------------------------------------------------- /data/plugins/other/other/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/plugins/other/other/.gitkeep -------------------------------------------------------------------------------- /data/plugins/package/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/plugins/package/.gitkeep -------------------------------------------------------------------------------- /data/win/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/win/config/.gitkeep -------------------------------------------------------------------------------- /data/win/plugins/folder/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/win/plugins/folder/.gitkeep -------------------------------------------------------------------------------- /data/win/plugins/main/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/win/plugins/main/.gitkeep -------------------------------------------------------------------------------- /data/win/plugins/package/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/data/win/plugins/package/.gitkeep -------------------------------------------------------------------------------- /install.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS `cloud_config`; 2 | CREATE TABLE `cloud_config` ( 3 | `key` varchar(32) NOT NULL, 4 | `value` varchar(255) DEFAULT NULL, 5 | PRIMARY KEY (`key`) 6 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 7 | 8 | INSERT INTO `cloud_config` (`key`, `value`) VALUES 9 | ('admin_username', 'admin'), 10 | ('admin_password', '123456'), 11 | ('bt_url', ''), 12 | ('bt_key', ''), 13 | ('whitelist', '0'), 14 | ('download_page', '1'), 15 | ('new_version', '9.4.0'), 16 | ('update_msg', '暂无更新日志'), 17 | ('update_date', '2025-01-09'), 18 | ('new_version_win', '8.2.2'), 19 | ('update_msg_win', '暂无更新日志'), 20 | ('update_date_win', '2024-12-30'), 21 | ('new_version_en', '7.0.13'), 22 | ('update_msg_en', '暂无更新日志'), 23 | ('update_date_en', '2024-11-17'), 24 | ('new_version_btm', '2.3.0'), 25 | ('update_msg_btm', '暂无更新日志'), 26 | ('update_date_btm', '2024-04-24'), 27 | ('updateall_type', '0'), 28 | ('syskey', 'UqP94LtI8eWAIgCP'); 29 | 30 | 31 | DROP TABLE IF EXISTS `cloud_black`; 32 | CREATE TABLE `cloud_black` ( 33 | `id` int(11) NOT NULL AUTO_INCREMENT, 34 | `ip` varchar(50) NOT NULL, 35 | `enable` tinyint(1) NOT NULL DEFAULT '1', 36 | `addtime` datetime NOT NULL, 37 | PRIMARY KEY (`id`), 38 | UNIQUE KEY `ip`(`ip`) 39 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 40 | 41 | DROP TABLE IF EXISTS `cloud_white`; 42 | CREATE TABLE `cloud_white` ( 43 | `id` int(11) NOT NULL AUTO_INCREMENT, 44 | `ip` varchar(50) NOT NULL, 45 | `enable` tinyint(1) NOT NULL DEFAULT '1', 46 | `addtime` datetime NOT NULL, 47 | PRIMARY KEY (`id`), 48 | UNIQUE KEY `ip`(`ip`) 49 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 50 | 51 | DROP TABLE IF EXISTS `cloud_record`; 52 | CREATE TABLE `cloud_record` ( 53 | `id` int(11) NOT NULL AUTO_INCREMENT, 54 | `ip` varchar(50) NOT NULL, 55 | `addtime` datetime NOT NULL, 56 | `usetime` datetime NOT NULL, 57 | PRIMARY KEY (`id`), 58 | UNIQUE KEY `ip`(`ip`) 59 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 60 | 61 | DROP TABLE IF EXISTS `cloud_log`; 62 | CREATE TABLE `cloud_log` ( 63 | `id` int(11) NOT NULL AUTO_INCREMENT, 64 | `uid` tinyint(4) NOT NULL DEFAULT '1', 65 | `action` varchar(40) NOT NULL, 66 | `data` varchar(150) DEFAULT NULL, 67 | `addtime` datetime NOT NULL, 68 | PRIMARY KEY (`id`) 69 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 70 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | Options +FollowSymlinks -Multiviews 3 | RewriteEngine On 4 | 5 | RewriteCond %{REQUEST_FILENAME} !-d 6 | RewriteCond %{REQUEST_FILENAME} !-f 7 | RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] 8 | 9 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | 12 | // [ 应用入口文件 ] 13 | namespace think; 14 | 15 | require __DIR__ . '/../vendor/autoload.php'; 16 | 17 | // 执行HTTP应用并响应 18 | $http = (new App())->http; 19 | 20 | $response = $http->run(); 21 | 22 | $response->send(); 23 | 24 | $http->end($response); 25 | -------------------------------------------------------------------------------- /public/install/public.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin 3 | pyenv_bin=/www/server/panel/pyenv/bin 4 | rep_path=${pyenv_bin}:$PATH 5 | if [ -d "$pyenv_bin" ];then 6 | PATH=$rep_path 7 | fi 8 | export PATH 9 | export LANG=en_US.UTF-8 10 | export LANGUAGE=en_US:en 11 | 12 | NODE_FILE_CHECK=$(cat /www/server/panel/data/node.json |grep 125.88.182.172) 13 | if [ "${NODE_FILE_CHECK}" ];then 14 | rm -f /www/server/panel/data/node.json 15 | fi 16 | 17 | if [ -f "/www/server/panel/install/d_node.pl" ];then 18 | LOCAL_DATE=$(date +%Y-%m-%d) 19 | FILE_DATE=$(stat /www/server/panel/install/d_node.pl|grep Change|awk '{print $2}') 20 | if [ "${LOCAL_DATE}" != "${FILE_DATE}" ];then 21 | rm -f /www/server/panel/install/d_node.pl 22 | else 23 | test_url=$(cat /www/server/panel/install/d_node.pl) 24 | HTTP_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${test_url}/net_test|xargs|awk '{print $2}') 25 | if [ "${HTTP_CHECK}" == "200" ];then 26 | NODE_URL=$test_url 27 | fi 28 | fi 29 | fi 30 | 31 | get_node_url(){ 32 | 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); 33 | 34 | if [ -f "/www/server/panel/data/domestic_ip.pl" ];then 35 | 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); 36 | fi 37 | 38 | if [ -f "/www/server/panel/data/foreign_ip.pl" ];then 39 | nodes=(https://cf1-node.aapanel.com https://dg2.bt.cn https://na1-node.bt.cn https://jp1-node.bt.cn https://download.bt.cn https://ctcc1-node.bt.cn https://ctcc2-node.bt.cn https://hk1-node.bt.cn); 40 | fi 41 | 42 | if [ "$1" ];then 43 | nodes=($(echo ${nodes[*]}|sed "s#${1}##")) 44 | fi 45 | 46 | tmp_file1=/dev/shm/net_test1.pl 47 | tmp_file2=/dev/shm/net_test2.pl 48 | [ -f "${tmp_file1}" ] && rm -f ${tmp_file1} 49 | [ -f "${tmp_file2}" ] && rm -f ${tmp_file2} 50 | touch $tmp_file1 51 | touch $tmp_file2 52 | for node in ${nodes[@]}; 53 | do 54 | if [ "${node}" == "https://cf1-node.aapanel.com" ];then 55 | NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${node}/1net_test|xargs) 56 | else 57 | NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${node}/net_test|xargs) 58 | fi 59 | RES=$(echo ${NODE_CHECK}|awk '{print $1}') 60 | NODE_STATUS=$(echo ${NODE_CHECK}|awk '{print $2}') 61 | TIME_TOTAL=$(echo ${NODE_CHECK}|awk '{print $3 * 1000 - 500 }'|cut -d '.' -f 1) 62 | if [ "${NODE_STATUS}" == "200" ];then 63 | if [ $TIME_TOTAL -lt 300 ];then 64 | if [ $RES -ge 1500 ];then 65 | echo "$RES $node" >> $tmp_file1 66 | fi 67 | else 68 | if [ $RES -ge 1500 ];then 69 | echo "$TIME_TOTAL $node" >> $tmp_file2 70 | fi 71 | fi 72 | 73 | i=$(($i+1)) 74 | if [ $TIME_TOTAL -lt 300 ];then 75 | if [ $RES -ge 2390 ];then 76 | break; 77 | fi 78 | fi 79 | fi 80 | done 81 | 82 | NODE_URL=$(cat $tmp_file1|sort -r -g -t " " -k 1|head -n 1|awk '{print $2}') 83 | if [ -z "$NODE_URL" ];then 84 | NODE_URL=$(cat $tmp_file2|sort -g -t " " -k 1|head -n 1|awk '{print $2}') 85 | if [ -z "$NODE_URL" ];then 86 | NODE_URL='https://download.bt.cn'; 87 | fi 88 | fi 89 | rm -f $tmp_file1 90 | rm -f $tmp_file2 91 | } 92 | 93 | GetCpuStat(){ 94 | time1=$(cat /proc/stat |grep 'cpu ') 95 | sleep 1 96 | time2=$(cat /proc/stat |grep 'cpu ') 97 | cpuTime1=$(echo ${time1}|awk '{print $2+$3+$4+$5+$6+$7+$8}') 98 | cpuTime2=$(echo ${time2}|awk '{print $2+$3+$4+$5+$6+$7+$8}') 99 | runTime=$((${cpuTime2}-${cpuTime1})) 100 | idelTime1=$(echo ${time1}|awk '{print $5}') 101 | idelTime2=$(echo ${time2}|awk '{print $5}') 102 | idelTime=$((${idelTime2}-${idelTime1})) 103 | useTime=$(((${runTime}-${idelTime})*3)) 104 | [ ${useTime} -gt ${runTime} ] && cpuBusy="true" 105 | if [ "${cpuBusy}" == "true" ]; then 106 | cpuCore=$((${cpuInfo}/2)) 107 | else 108 | cpuCore=$((${cpuInfo}-1)) 109 | fi 110 | } 111 | GetPackManager(){ 112 | if [ -f "/usr/bin/yum" ] && [ -f "/etc/yum.conf" ]; then 113 | PM="yum" 114 | elif [ -f "/usr/bin/apt-get" ] && [ -f "/usr/bin/dpkg" ]; then 115 | PM="apt-get" 116 | fi 117 | } 118 | 119 | bt_check(){ 120 | p_path=/www/server/panel/class/panelPlugin.py 121 | if [ -f $p_path ];then 122 | is_ext=$(cat $p_path|grep btwaf) 123 | if [ "$is_ext" != "" ];then 124 | send_check 125 | fi 126 | fi 127 | 128 | p_path=/www/server/panel/BTPanel/templates/default/index.html 129 | if [ -f $p_path ];then 130 | is_ext=$(cat $p_path|grep fbi) 131 | if [ "$is_ext" != "" ];then 132 | send_check 133 | fi 134 | fi 135 | } 136 | 137 | send_check(){ 138 | chattr -i /etc/init.d/bt 139 | chmod +x /etc/init.d/bt 140 | p_path2=/www/server/panel/class/common.py 141 | p_version=$(cat $p_path2|grep "version = "|awk '{print $3}'|tr -cd [0-9.]) 142 | curl -sS --connect-timeout 3 -m 60 http://www.example.com/api/panel/notpro?version=$p_version 143 | NODE_URL="" 144 | exit 0; 145 | } 146 | GetSysInfo(){ 147 | if [ "${PM}" = "yum" ]; then 148 | SYS_VERSION=$(cat /etc/redhat-release) 149 | elif [ "${PM}" = "apt-get" ]; then 150 | SYS_VERSION=$(cat /etc/issue) 151 | fi 152 | SYS_INFO=$(uname -msr) 153 | SYS_BIT=$(getconf LONG_BIT) 154 | MEM_TOTAL=$(free -m|grep Mem|awk '{print $2}') 155 | CPU_INFO=$(getconf _NPROCESSORS_ONLN) 156 | GCC_VER=$(gcc -v 2>&1|grep "gcc version"|awk '{print $3}') 157 | CMAKE_VER=$(cmake --version|grep version|awk '{print $3}') 158 | 159 | echo -e ${SYS_VERSION} 160 | echo -e Bit:${SYS_BIT} Mem:${MEM_TOTAL}M Core:${CPU_INFO} gcc:${GCC_VER} cmake:${CMAKE_VER} 161 | echo -e ${SYS_INFO} 162 | } 163 | cpuInfo=$(getconf _NPROCESSORS_ONLN) 164 | if [ "${cpuInfo}" -ge "4" ];then 165 | GetCpuStat 166 | else 167 | cpuCore="1" 168 | fi 169 | GetPackManager 170 | 171 | if [ ! $NODE_URL ];then 172 | EN_CHECK=$(cat /www/server/panel/config/config.json |grep English) 173 | if [ -z "${EN_CHECK}" ];then 174 | echo '正在选择下载节点...'; 175 | else 176 | echo "selecting download node..."; 177 | fi 178 | get_node_url 179 | fi 180 | 181 | 182 | -------------------------------------------------------------------------------- /public/install/src/bt-monitor-2.3.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/install/src/bt-monitor-2.3.0.zip -------------------------------------------------------------------------------- /public/install/src/panel6.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/install/src/panel6.zip -------------------------------------------------------------------------------- /public/install/src/panel_7_en.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/install/src/panel_7_en.zip -------------------------------------------------------------------------------- /public/install/update/LinuxPanel-9.5.0.pl: -------------------------------------------------------------------------------- 1 | {"hash": "c097e387cb3f2baf34a222191de7130bd878215ffa4ba8cef2d337e3466e6963", "update_time": "1747723203"} -------------------------------------------------------------------------------- /public/install/update/LinuxPanel-9.6.0.pl: -------------------------------------------------------------------------------- 1 | {"hash": "d869ee4b8bdcfae324c4fdef9ec647db4a1529e7f6890b8cb4693f40b8a24626", "update_time": "1748242787"} -------------------------------------------------------------------------------- /public/install/update/LinuxPanel-9.6.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/install/update/LinuxPanel-9.6.0.zip -------------------------------------------------------------------------------- /public/install/update/LinuxPanel_EN-7.0.16.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/install/update/LinuxPanel_EN-7.0.16.zip -------------------------------------------------------------------------------- /public/install/update6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin 3 | export PATH 4 | LANG=en_US.UTF-8 5 | 6 | Btapi_Url='http://www.example.com' 7 | 8 | if [ ! -d /www/server/panel/BTPanel ];then 9 | echo "=============================================" 10 | echo "错误, 5.x不可以使用此命令升级!" 11 | echo "5.9平滑升级到6.0的命令:curl http://download.bt.cn/install/update_to_6.sh|bash" 12 | exit 0; 13 | fi 14 | 15 | if [ ! -f "/www/server/panel/pyenv/bin/python3" ];then 16 | echo "=============================================" 17 | echo "错误, 当前面板过旧/py-2.7/无pyenv环境,无法升级至最新版面板" 18 | echo "请截图发帖至论坛www.bt.cn/bbs求助" 19 | exit 0; 20 | fi 21 | 22 | if [ -f "/etc/redhat-release" ];then 23 | Centos6Check=$(cat /etc/redhat-release | grep ' 6.' | grep -iE 'centos|Red Hat') 24 | if [ "${Centos6Check}" ];then 25 | echo "Centos6不支持升级宝塔面板,建议备份数据重装更换Centos7/8安装宝塔面板" 26 | exit 1 27 | fi 28 | Centos8Check=$(cat /etc/redhat-release | grep ' 8.' | grep -iE 'centos|Red Hat') 29 | if [ "${Centos8Check}" ];then 30 | if [ ! -f "/usr/bin/python" ] && [ -f "/usr/bin/python3" ] && [ ! -d "/www/server/panel/pyenv" ]; then 31 | ln -sf /usr/bin/python3 /usr/bin/python 32 | fi 33 | fi 34 | fi 35 | 36 | mypip="pip" 37 | env_path=/www/server/panel/pyenv/bin/activate 38 | if [ -f $env_path ];then 39 | mypip="/www/server/panel/pyenv/bin/pip" 40 | fi 41 | 42 | if [ -f "/www/server/panel/data/down_url.pl" ];then 43 | D_NODE_URL=$(cat /www/server/panel/data/down_url.pl|grep bt.cn) 44 | fi 45 | 46 | if [ -z "${D_NODE_URL}" ];then 47 | D_NODE_URL="download.bt.cn" 48 | fi 49 | 50 | download_Url=$D_NODE_URL 51 | 52 | 53 | Set_Centos7_Repo(){ 54 | if [ -f "/etc/yum.repos.d/docker-ce.repo" ];then 55 | mv /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce.repo_backup 56 | fi 57 | MIRROR_CHECK=$(cat /etc/yum.repos.d/CentOS-Base.repo |grep "[^#]mirror.centos.org") 58 | if [ "${MIRROR_CHECK}" ] && [ "${is64bit}" == "64" ];then 59 | \cp -rpa /etc/yum.repos.d/ /etc/yumBak 60 | sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*.repo 61 | sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.epel.cloud|g' /etc/yum.repos.d/CentOS-*.repo 62 | fi 63 | 64 | TSU_MIRROR_CHECK=$(cat /etc/yum.repos.d/CentOS-Base.repo |grep "tuna.tsinghua.edu.cn") 65 | if [ "${TSU_MIRROR_CHECK}" ];then 66 | \cp -rpa /etc/yum.repos.d/ /etc/yumBak 67 | sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*.repo 68 | sed -i 's|#baseurl=https://mirrors.tuna.tsinghua.edu.cn|baseurl=http://vault.epel.cloud|g' /etc/yum.repos.d/CentOS-*.repo 69 | sed -i 's|#baseurl=http://mirrors.tuna.tsinghua.edu.cn|baseurl=http://vault.epel.cloud|g' /etc/yum.repos.d/CentOS-*.repo 70 | fi 71 | 72 | ALI_CLOUD_CHECK=$(grep Alibaba /etc/motd) 73 | Tencent_Cloud=$(cat /etc/hostname |grep -E VM-[0-9]+-[0-9]+) 74 | if [ "${ALI_CLOUD_CHECK}" ] || [ "${Tencent_Cloud}" ];then 75 | return 76 | fi 77 | 78 | yum install tree -y 79 | if [ "$?" != "0" ] ;then 80 | TAR_CHECK=$(which tree) 81 | if [ "$?" == "0" ] ;then 82 | \cp -rpa /etc/yum.repos.d/ /etc/yumBak 83 | if [ -z "${download_Url}" ];then 84 | download_Url="http://download.bt.cn" 85 | fi 86 | curl -Ss --connect-timeout 5 -m 60 -O ${download_Url}/src/el7repo.tar.gz 87 | rm -f /etc/yum.repos.d/*.repo 88 | tar -xvzf el7repo.tar.gz -C /etc/yum.repos.d/ 89 | fi 90 | fi 91 | 92 | yum install tree -y 93 | if [ "$?" != "0" ] ;then 94 | sed -i "s/vault.epel.cloud/mirrors.cloud.tencent.com/g" /etc/yum.repos.d/*.repo 95 | fi 96 | } 97 | if [ -f "/etc/redhat-release" ];then 98 | Centos7Check=$(cat /etc/redhat-release | grep ' 7.' | grep -iE 'centos|Red Hat') 99 | if [ "${Centos7Check}" ];then 100 | Set_Centos7_Repo 101 | fi 102 | fi 103 | setup_path=/www 104 | version=$(curl -Ss --connect-timeout 5 -m 2 $Btapi_Url/api/panel/get_version) 105 | if [ -z "$version" ];then 106 | version='9.5.0' 107 | fi 108 | armCheck=$(uname -m|grep arm) 109 | if [ "${armCheck}" ];then 110 | version='7.7.0' 111 | fi 112 | 113 | if [ "$1" ];then 114 | version=$1 115 | fi 116 | wget -T 5 -O /tmp/panel.zip $Btapi_Url/install/update/LinuxPanel-${version}.zip 117 | dsize=$(du -b /tmp/panel.zip|awk '{print $1}') 118 | if [ $dsize -lt 10240 ];then 119 | echo "获取更新包失败,请稍后更新或联系宝塔运维" 120 | exit; 121 | fi 122 | 123 | unzip -o /tmp/panel.zip -d $setup_path/server/ > /dev/null 124 | rm -f /tmp/panel.zip 125 | cd $setup_path/server/panel/ 126 | check_bt=`cat /etc/init.d/bt` 127 | if [ "${check_bt}" = "" ];then 128 | rm -f /etc/init.d/bt 129 | wget -O /etc/init.d/bt $download_Url/install/src/bt7.init -T 20 130 | chmod +x /etc/init.d/bt 131 | fi 132 | echo "==============================================================" 133 | echo "正在修复面板依赖问题" 134 | rm -f /www/server/panel/*.pyc 135 | rm -f /www/server/panel/class/*.pyc 136 | #pip install flask_sqlalchemy 137 | #pip install itsdangerous==0.24 138 | 139 | if [ -f "/www/server/panel/pyenv/bin/pip3" ];then 140 | btpip_path="/www/server/panel/pyenv/bin/pip3" 141 | FlaskV=$($btpip_path list 2>/dev/null|grep "Flask " |awk '{print $2}'|cut -f 1 -d .) 142 | piplist_count=$($btpip_path list 2>/dev/null|wc -l) 143 | if [ "${FlaskV}" -le "2" ] && [ "${piplist_count}" -le "118" ];then 144 | echo "检测到面板运行环境过老,正常尝试修复面板依赖" 145 | pyenv_path="/www/server/panel" 146 | wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip-3.7.16.txt -T 5 147 | $pyenv_path/pyenv/bin/pip install -U pip 148 | $pyenv_path/pyenv/bin/pip install -U setuptools==65.5.0 149 | $pyenv_path/pyenv/bin/pip install -U wheel==0.34.2 150 | $pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt 151 | echo "依赖修复完成,如面板仍无法正常访问,请联系宝塔官方人员进行求助" 152 | fi 153 | 154 | #wget -O pip-packs.txt $download_Url/install/pyenv/pip-packs.txt 155 | #PIP_PACKS=$(cat pip-packs.txt) 156 | #for P_PACK in ${PIP_PACKS}; 157 | #do 158 | # btpip show ${P_PACK} > /dev/null 2>&1 159 | # if [ "$?" == "1" ];then 160 | # btpip install ${P_PACK} 161 | # fi 162 | #done 163 | fi 164 | 165 | 166 | pip_list=$($mypip list 2>&1) 167 | request_v=$(btpip list 2>/dev/null|grep "requests "|awk '{print $2}'|cut -d '.' -f 2) 168 | if [ "$request_v" = "" ] || [ "${request_v}" -gt "28" ];then 169 | $mypip install requests==2.27.1 170 | fi 171 | 172 | NATSORT_C=$(echo $pip_list|grep natsort) 173 | if [ -z "${NATSORT_C}" ];then 174 | btpip install natsort 175 | fi 176 | 177 | openssl_v=$(echo "$pip_list"|grep pyOpenSSL) 178 | if [ "$openssl_v" = "" ];then 179 | $mypip install pyOpenSSL 180 | fi 181 | 182 | #cffi_v=$(echo "$pip_list"|grep cffi|grep 1.12.) 183 | #if [ "$cffi_v" = "" ];then 184 | # $mypip install cffi==1.12.3 185 | #fi 186 | 187 | pymysql=$(echo "$pip_list"|grep pymysql) 188 | if [ "$pymysql" = "" ];then 189 | $mypip install pymysql 190 | fi 191 | GEVENT_V=$(btpip list 2> /dev/null|grep "gevent "|awk '{print $2}'|cut -f 1 -d '.') 192 | if [ "${GEVENT_V}" -le "1" ];then 193 | /www/server/panel/pyenv/bin/pip3 install -I gevent 194 | fi 195 | 196 | BROTLI_C=$(btpip list 2> /dev/null |grep Brotli) 197 | if [ -z "$BROTLI_C" ]; then 198 | btpip install brotli 199 | fi 200 | 201 | PYMYSQL_C=$(btpip list 2> /dev/null |grep PyMySQL) 202 | if [ -z "$PYMYSQL_C" ]; then 203 | btpip install PyMySQL 204 | fi 205 | 206 | 207 | PY_CRPYT=$(btpip list 2> /dev/null |grep cryptography|awk '{print $2}'|cut -f 1 -d '.') 208 | if [ "${PY_CRPYT}" -le "10" ];then 209 | btpip install pyOpenSSL==24.1.0 210 | btpip install cryptography==42.0.5 211 | fi 212 | 213 | PYMYSQL_SSL_CHECK=$(btpython -c "import pymysql" 2>&1|grep "AttributeError: module 'cryptography.hazmat.bindings._rust.openssl'") 214 | if [ "${PYMYSQL_SSL_CHECK}" ];then 215 | btpip uninstall pyopenssl cryptography -y 216 | btpip install pyopenssl cryptography 217 | fi 218 | 219 | btpip uninstall enum34 -y 220 | 221 | GEOIP_C=$(echo $pip_list|grep geoip2) 222 | if [ -z "${GEOIP_C}" ];then 223 | btpip install geoip2==4.7.0 224 | fi 225 | 226 | # PANDAS_C=$(echo $pip_list|grep pandas) 227 | # if [ -z "${PANDAS_C}" ];then 228 | # btpip install pandas 229 | # fi 230 | 231 | pymysql=$(echo "$pip_list"|grep pycryptodome) 232 | if [ "$pymysql" = "" ];then 233 | $mypip install pycryptodome 234 | fi 235 | 236 | echo "修复面板依赖完成!" 237 | echo "===========================================" 238 | 239 | RE_UPDATE=$(cat /www/server/panel/data/db/update) 240 | if [ "$RE_UPDATE" -ge "4" ];then 241 | echo "2" > /www/server/panel/data/db/update 242 | fi 243 | 244 | #psutil=$(echo "$pip_list"|grep psutil|awk '{print $2}'|grep '5.7.') 245 | #if [ "$psutil" = "" ];then 246 | # $mypip install -U psutil 247 | #fi 248 | 249 | if [ -d /www/server/panel/class/BTPanel ];then 250 | rm -rf /www/server/panel/class/BTPanel 251 | fi 252 | rm -f /www/server/panel/class/*.so 253 | if [ ! -f /www/server/panel/data/not_workorder.pl ]; then 254 | echo "True" > /www/server/panel/data/not_workorder.pl 255 | fi 256 | if [ ! -f /www/server/panel/data/userInfo.json ]; then 257 | echo "{\"uid\":1,\"username\":\"Administrator\",\"address\":\"127.0.0.1\",\"access_key\":\"test\",\"secret_key\":\"123456\",\"ukey\":\"123456\",\"state\":1}" > /www/server/panel/data/userInfo.json 258 | fi 259 | if [ ! -f /www/server/panel/data/panel_nps.pl ]; then 260 | echo "" > /www/server/panel/data/panel_nps.pl 261 | fi 262 | if [ ! -f /www/server/panel/data/btwaf_nps.pl ]; then 263 | echo "" > /www/server/panel/data/btwaf_nps.pl 264 | fi 265 | if [ ! -f /www/server/panel/data/tamper_proof_nps.pl ]; then 266 | echo "" > /www/server/panel/data/tamper_proof_nps.pl 267 | fi 268 | if [ ! -f /www/server/panel/data/total_nps.pl ]; then 269 | echo "" > /www/server/panel/data/total_nps.pl 270 | fi 271 | 272 | echo "===========================================" 273 | echo "正在更新面板文件..............." 274 | sleep 1 275 | echo "更新完成!" 276 | echo "===========================================" 277 | 278 | chattr -i /etc/init.d/bt 279 | chmod +x /etc/init.d/bt 280 | echo "=====================================" 281 | rm -f /dev/shm/bt_sql_tips.pl 282 | kill $(ps aux|grep -E "task.pyc|main.py"|grep -v grep|awk '{print $2}') 283 | /etc/init.d/bt restart 284 | echo 'True' > /www/server/panel/data/restart.pl 285 | pkill -9 gunicorn & 286 | echo "已成功升级到[$version]${Ver}"; 287 | 288 | 289 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | -------------------------------------------------------------------------------- /public/router.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | // $Id$ 12 | 13 | if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) { 14 | return false; 15 | } else { 16 | $_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php'; 17 | 18 | require __DIR__ . "/index.php"; 19 | } 20 | -------------------------------------------------------------------------------- /public/static/css/bootstrap-table.css: -------------------------------------------------------------------------------- 1 | .bootstrap-table .fixed-table-toolbar::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-toolbar .bs-bars,.bootstrap-table .fixed-table-toolbar .columns,.bootstrap-table .fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group>.btn{border-radius:0}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu{text-align:left;max-height:300px;overflow:auto;-ms-overflow-style:scrollbar;z-index:1001}.bootstrap-table .fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.4286}.bootstrap-table .fixed-table-toolbar .columns-left{margin-right:5px}.bootstrap-table .fixed-table-toolbar .columns-right{margin-left:5px}.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu{right:0;left:auto}.bootstrap-table .fixed-table-container{position:relative;clear:both}.bootstrap-table .fixed-table-container .table{width:100%;margin-bottom:0!important}.bootstrap-table .fixed-table-container .table td,.bootstrap-table .fixed-table-container .table th{vertical-align:middle;box-sizing:border-box}.bootstrap-table .fixed-table-container .table thead th{vertical-align:bottom;padding:0;margin:0}.bootstrap-table .fixed-table-container .table thead th:focus{outline:0 solid transparent}.bootstrap-table .fixed-table-container .table thead th.detail{width:30px}.bootstrap-table .fixed-table-container .table thead th .th-inner{padding:.75rem;vertical-align:bottom;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bootstrap-table .fixed-table-container .table thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px!important}.bootstrap-table .fixed-table-container .table thead th .both{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC")}.bootstrap-table .fixed-table-container .table thead th .asc{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==")}.bootstrap-table .fixed-table-container .table thead th .desc{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ")}.bootstrap-table .fixed-table-container .table tbody tr.selected td{background-color:rgba(0,0,0,.075)}.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td{text-align:center}.bootstrap-table .fixed-table-container .table tbody tr .card-view{display:flex}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title{font-weight:700;display:inline-block;min-width:30%;width:auto!important;text-align:left!important}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-value{width:100%!important;text-align:left!important}.bootstrap-table .fixed-table-container .table .bs-checkbox{text-align:center}.bootstrap-table .fixed-table-container .table .bs-checkbox label{margin-bottom:0}.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=checkbox],.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=radio]{margin:0 auto!important}.bootstrap-table .fixed-table-container .table.table-sm .th-inner{padding:.3rem}.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer){border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height.has-card-view{border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border{border-left:1px solid #dee2e6;border-right:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table thead th{border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th{border-bottom:1px solid #32383e}.bootstrap-table .fixed-table-container .fixed-table-header{overflow:hidden}.bootstrap-table .fixed-table-container .fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading{align-items:center;background:#fff;display:flex;justify-content:center;position:absolute;bottom:0;width:100%;max-width:100%;z-index:1000;transition:visibility 0s,opacity .15s ease-in-out;opacity:0;visibility:hidden}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.open{visibility:visible;opacity:1}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap{align-items:baseline;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text{margin-right:6px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap{align-items:center;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before{content:"";animation-duration:1.5s;animation-iteration-count:infinite;animation-name:loading;background:#212529;border-radius:50%;display:block;height:5px;margin:0 4px;opacity:0;width:5px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot{animation-delay:.3s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after{animation-delay:.6s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark{background:#212529}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before{background:#fff}.bootstrap-table .fixed-table-container .fixed-table-footer{overflow:hidden}.bootstrap-table .fixed-table-pagination::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-pagination>.pagination,.bootstrap-table .fixed-table-pagination>.pagination-detail{margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-pagination>.pagination-detail .pagination-info{line-height:34px;margin-right:5px}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list{display:inline-block}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group{position:relative;display:inline-block;vertical-align:middle}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group .dropdown-menu{margin-bottom:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination{margin:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a{color:#c8c8c8}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::before{content:"\2B05"}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::after{content:"\27A1"}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.disabled a{pointer-events:none;cursor:default}.bootstrap-table.fullscreen{position:fixed;top:0;left:0;z-index:1050;width:100%!important;background:#fff;height:calc(100vh);overflow-y:scroll}.bootstrap-table.bootstrap4 .pagination-lg .page-link,.bootstrap-table.bootstrap5 .pagination-lg .page-link{padding:.5rem 1rem}.bootstrap-table.bootstrap5 .float-left{float:left}.bootstrap-table.bootstrap5 .float-right{float:right}div.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden}@keyframes loading{0%{opacity:0}50%{opacity:1}to{opacity:0}} 2 | .table-bottom-border{border-bottom: 2px solid #ddd;} 3 | @media screen and (max-width:767px){.fixed-table-body{border:1px solid #ddd}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{white-space:nowrap}.columns-right{display:none;}} 4 | .bootstrap-table.bootstrap3 .fixed-table-pagination>.pagination .page-jump-to{display:inline-block}.bootstrap-table.bootstrap3 .fixed-table-pagination>.pagination .input-group-btn{width:auto}.bootstrap-table .fixed-table-pagination>.pagination .page-jump-to input{width:70px;margin-left:5px;text-align:center;float:left} 5 | .form-inline .form-control{display:inline-block;width:auto;vertical-align:middle} 6 | .form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle} 7 | -------------------------------------------------------------------------------- /public/static/file/en/kaixin.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/file/en/kaixin.zip -------------------------------------------------------------------------------- /public/static/file/kaixin.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/file/kaixin.zip -------------------------------------------------------------------------------- /public/static/file/win/kaixin.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/file/win/kaixin.zip -------------------------------------------------------------------------------- /public/static/images/aapanel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/aapanel.png -------------------------------------------------------------------------------- /public/static/images/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/account.png -------------------------------------------------------------------------------- /public/static/images/addc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/addc.png -------------------------------------------------------------------------------- /public/static/images/bt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/bt.png -------------------------------------------------------------------------------- /public/static/images/bt_monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/bt_monitor.png -------------------------------------------------------------------------------- /public/static/images/bto_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/bto_copy.png -------------------------------------------------------------------------------- /public/static/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/close.png -------------------------------------------------------------------------------- /public/static/images/downico1_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/downico1_01.png -------------------------------------------------------------------------------- /public/static/images/downico2_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/downico2_01.png -------------------------------------------------------------------------------- /public/static/images/eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/eye.png -------------------------------------------------------------------------------- /public/static/images/eye_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/eye_close.png -------------------------------------------------------------------------------- /public/static/images/footbg_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/footbg_02.jpg -------------------------------------------------------------------------------- /public/static/images/hot_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/hot_03.png -------------------------------------------------------------------------------- /public/static/images/i1abg_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/i1abg_03.png -------------------------------------------------------------------------------- /public/static/images/i1aico_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/i1aico_03.png -------------------------------------------------------------------------------- /public/static/images/i1ico_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/i1ico_03.png -------------------------------------------------------------------------------- /public/static/images/ico-copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/ico-copy.png -------------------------------------------------------------------------------- /public/static/images/install_type_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/install_type_select.png -------------------------------------------------------------------------------- /public/static/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 115 | -------------------------------------------------------------------------------- /public/static/images/navicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/navicon.png -------------------------------------------------------------------------------- /public/static/images/prd_1_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/prd_1_03.png -------------------------------------------------------------------------------- /public/static/images/prd_2_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/static/images/prd_2_03.png -------------------------------------------------------------------------------- /public/static/images/wave.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /public/static/js/custom.js: -------------------------------------------------------------------------------- 1 | var location_url = window.location.href; 2 | var parameter_str = location_url.split('?')[1]; 3 | if (parameter_str !== undefined) { 4 | parameter_str = parameter_str.split('#')[0]; 5 | var $_GET = {}; 6 | var parameter_arr = parameter_str.split('&'); 7 | var tmp_arr; 8 | for (var i = 0, len = parameter_arr.length; i <= len - 1; i++) { 9 | tmp_arr = parameter_arr[i].split('='); 10 | $_GET[tmp_arr[0]] = decodeURIComponent(tmp_arr[1]); 11 | } 12 | window.$_GET = $_GET; 13 | } else { 14 | window.$_GET = []; 15 | } 16 | 17 | function searchSubmit(){ 18 | $('#listTable').bootstrapTable('refresh'); 19 | return false; 20 | } 21 | function searchClear(){ 22 | $('#searchToolbar').find('input[name]').each(function() { 23 | $(this).val(''); 24 | }); 25 | $('#searchToolbar').find('select[name]').each(function() { 26 | $(this).find('option:first').prop("selected", 'selected'); 27 | }); 28 | $('#listTable').bootstrapTable('refresh'); 29 | } 30 | function updateToolbar(){ 31 | $('#searchToolbar').find(':input[name]').each(function() { 32 | var name = $(this).attr('name'); 33 | if(typeof window.$_GET[name] != 'undefined') 34 | $(this).val(window.$_GET[name]); 35 | }) 36 | } 37 | function updateQueryStr(obj){ 38 | var arr = []; 39 | for (var p in obj){ 40 | if (obj.hasOwnProperty(p) && typeof obj[p] != 'undefined' && obj[p] != '') { 41 | arr.push(p + "=" + encodeURIComponent(obj[p])); 42 | } 43 | } 44 | history.replaceState({}, null, '?'+arr.join("&")); 45 | } 46 | 47 | if (typeof $.fn.bootstrapTable !== "undefined") { 48 | $.fn.bootstrapTable.custom = { 49 | method: 'post', 50 | contentType: "application/x-www-form-urlencoded", 51 | sortable: true, 52 | pagination: true, 53 | sidePagination: 'server', 54 | pageNumber: 1, 55 | pageSize: 20, 56 | pageList: [10, 15, 20, 30, 50, 100], 57 | loadingFontSize: '18px', 58 | toolbar: '#searchToolbar', 59 | showColumns: true, 60 | minimumCountColumns: 2, 61 | showToggle: true, 62 | showFullscreen: true, 63 | paginationPreText: '前页', 64 | paginationNextText: '后页', 65 | showJumpTo: true, 66 | paginationLoop: false, 67 | queryParamsType: '', 68 | queryParams: function(params) { 69 | $('#searchToolbar').find(':input[name]').each(function() { 70 | params[$(this).attr('name')] = $(this).val() 71 | }) 72 | updateQueryStr(params); 73 | params.offset = params.pageSize * (params.pageNumber-1); 74 | params.limit = params.pageSize; 75 | return params; 76 | }, 77 | formatLoadingMessage: function(){ 78 | return ''; 79 | }, 80 | formatShowingRows: function(t,n,r,e){ 81 | return '显示第 '+t+' 到第 '+n+' 条, 总共 '+r+' 条'; 82 | }, 83 | formatRecordsPerPage: function(t){ 84 | return '每页显示 '+t+' 条'; 85 | }, 86 | formatNoMatches: function(){ 87 | return '没有找到匹配的记录'; 88 | } 89 | }; 90 | $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.custom); 91 | } 92 | 93 | function httpGet(url, callback){ 94 | $.ajax({ 95 | url: url, 96 | type: 'get', 97 | dataType: 'json', 98 | success: function (res) { 99 | callback(res) 100 | }, 101 | error: function () { 102 | if (typeof layer !== "undefined") { 103 | layer.closeAll(); 104 | layer.msg('服务器错误'); 105 | } 106 | } 107 | }); 108 | } 109 | 110 | function httpPost(url, data, callback){ 111 | $.ajax({ 112 | url: url, 113 | type: 'post', 114 | data: data, 115 | dataType: 'json', 116 | success: function (res) { 117 | callback(res) 118 | }, 119 | error: function () { 120 | if (typeof layer !== "undefined") { 121 | layer.closeAll(); 122 | layer.msg('服务器错误'); 123 | } 124 | } 125 | }); 126 | } 127 | 128 | var isMobile = function(){ 129 | if( /Android|SymbianOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Windows Phone|Midp/i.test(navigator.userAgent)) { 130 | return true; 131 | } 132 | return false; 133 | } -------------------------------------------------------------------------------- /public/static/js/dx.js: -------------------------------------------------------------------------------- 1 | // JS is for dynamically creating and moving the birds across the screen. 2 | // The actual bird flapping and flight wave is CSS animation. 3 | 4 | // Adjust these options here to customize the scene. 5 | let options = { 6 | delay: 500, 7 | speedRange: [2, 5], 8 | angleRange: [-30, 30], 9 | sizeRange: [15, 30], 10 | }; 11 | 12 | let bird = document.createElement('span'); 13 | bird.className = 'bird'; 14 | let particles = []; 15 | let length = 12; 16 | let isLeave = false; 17 | 18 | init(); 19 | 20 | function init() { 21 | for (let i = 0; i < length; i++) { 22 | let particle = initParticle(); 23 | particle.move(); 24 | particles.push(particle); 25 | } 26 | } 27 | 28 | function initPos() { 29 | var top = $('.d1').offset().top + 50; 30 | var bottom = $('.d1').height() / 1.8 + top; 31 | return [rand(50, window.innerWidth / 2), rand(top, bottom)]; 32 | } 33 | 34 | function initParticle() { 35 | let newBird = bird.cloneNode(); 36 | const size = rand(options.sizeRange[0], options.sizeRange[1]); 37 | newBird.style.width = size + 'px'; 38 | newBird.style.height = size / 5 + 'px'; 39 | 40 | document.querySelector('.animate-bg').appendChild(newBird); 41 | 42 | let pos = initPos(); 43 | 44 | return new Particle(newBird, { 45 | speed: rand(options.speedRange[0], options.speedRange[1]), 46 | angle: rand(options.angleRange[0], options.angleRange[1]), 47 | pos: pos, 48 | }); 49 | } 50 | 51 | window.requestAnimationFrame(draw); 52 | 53 | function draw() { 54 | particles.forEach((particle, i, arr) => { 55 | if (particle.element.style.display == 'none') { 56 | particle.element.style.display = 'inline-block'; 57 | particle.pos = initPos(); 58 | } 59 | 60 | if (particle.pos[0] > window.innerWidth || particle.pos[1] > window.innerHeight || particle.pos[0] < 0 - window.innerWidth || particle.pos[1] < 0 - window.innerHeight) { 61 | particle.element.style.display = 'none'; 62 | } else { 63 | particle.move(); 64 | } 65 | }); 66 | 67 | window.requestAnimationFrame(draw); 68 | } 69 | 70 | function Particle(element, options) { 71 | this.size = 1; 72 | this.speed = 1; 73 | this.angle = 90; 74 | this.pos = [0, 0]; 75 | this.element = element; 76 | 77 | this.constructor = function (options) { 78 | for (let i in options) { 79 | this[i] = options[i]; 80 | } 81 | }; 82 | 83 | this.move = function () { 84 | var radians = (this.angle * Math.PI) / 180; 85 | this.pos[0] += Math.cos(radians) * this.speed; 86 | this.pos[1] += Math.sin(radians) * this.speed; 87 | // console.log(this.pos) 88 | this.draw(); 89 | }; 90 | 91 | this.draw = function () { 92 | this.element.style.left = this.pos[0] + 'px'; 93 | this.element.style.top = this.pos[1] + 'px'; 94 | }; 95 | 96 | this.constructor(options); 97 | } 98 | 99 | function rand(min, max) { 100 | return Math.random() * (max - min) + min; 101 | } 102 | -------------------------------------------------------------------------------- /public/win/install/panel_update.py: -------------------------------------------------------------------------------- 1 | #coding: utf-8 2 | # +------------------------------------------------------------------- 3 | # | 宝塔Windows面板 4 | # +------------------------------------------------------------------- 5 | # | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved. 6 | # +------------------------------------------------------------------- 7 | # | Author: 沐落 8 | 9 | # | 面板升级安装公共类 10 | # +------------------------------------------------------------------- 11 | 12 | import os, sys 13 | panelPath = os.getenv('BT_PANEL') 14 | os.chdir(panelPath) 15 | sys.path.insert(0,panelPath + "/class/") 16 | import public,time,re,shutil,platform 17 | class panel_update: 18 | 19 | def __init__(self): 20 | pass 21 | 22 | def UpdatePanel(self,version): 23 | """ 24 | 更新面板到指定版本 25 | @version 面板版本号 26 | """ 27 | import public 28 | 29 | setupPath = os.getenv('BT_SETUP') 30 | loacl_path = setupPath + '/panel.zip' 31 | tmpPath = "{}/temp/panel".format(setupPath) 32 | 33 | httpUrl = 'http://www.example.com' 34 | try: 35 | downUrl = httpUrl + '/win/panel/panel_' + version + '.zip'; 36 | if os.path.exists(loacl_path): os.remove(loacl_path) 37 | 38 | public.downloadFileByWget(downUrl,loacl_path); 39 | 40 | if os.path.getsize(loacl_path) < 1048576: return public.returnMsg(False,"PANEL_UPDATE_ERR_DOWN"); 41 | 42 | except : 43 | 44 | print(public.get_error_info()) 45 | return public.returnMsg(False,"修复失败,无法连接到下载节点."); 46 | 47 | #处理临时文件目录 48 | tcPath = '{}\class'.format(tmpPath) 49 | if os.path.exists(tmpPath): shutil.rmtree(tmpPath,True) 50 | if not os.path.exists(tmpPath): os.makedirs(tmpPath) 51 | 52 | import zipfile 53 | zip_file = zipfile.ZipFile(loacl_path) 54 | for names in zip_file.namelist(): 55 | zip_file.extract(names,tmpPath) 56 | zip_file.close() 57 | 58 | for name in os.listdir(tcPath): 59 | try: 60 | if name.find('win_amd64.pyd') >=0: 61 | oldName = os.path.join(tcPath,name); 62 | lName = name.split('.')[0] + '.pyd' 63 | newName = os.path.join(tcPath,lName) 64 | if not os.path.exists(newName):os.rename(oldName,newName) 65 | 66 | except :pass 67 | 68 | #过滤文件 69 | file_list = ['config/config.json','config/index.json','data/libList.conf','data/plugin.json'] 70 | for ff_path in file_list: 71 | if os.path.exists(tmpPath + '/' + ff_path): os.remove(tmpPath + '/' + ff_path) 72 | 73 | public.mod_reload(public) 74 | import public 75 | 76 | #兼容不同版本工具箱 77 | public.kill('BtTools.exe') 78 | toolPath = tmpPath + '/script/BtTools.exe' 79 | if os.path.exists(toolPath):os.remove(toolPath) 80 | 81 | s_ver = platform.platform() 82 | net_v = '45' 83 | if s_ver.find('2008') >= 0: net_v = '20' 84 | public.writeFile('{}/data/net'.format(panelPath),net_v) 85 | public.downloadFileByWget(httpUrl + '/win/panel/BtTools' + net_v + '.exe',toolPath); 86 | 87 | cPath = '{}/panel/class'.format(setupPath) 88 | os.system("del /s {}\*.pyc".format(public.to_path(cPath))) 89 | os.system("del /s {}\*.pyt".format(public.to_path(cPath))) 90 | for name in os.listdir(cPath): 91 | try: 92 | if name.find('.pyd') >=0: 93 | oldName = os.path.join(cPath,name) 94 | newName = os.path.join(cPath,public.GetRandomString(8) + '.pyt') 95 | os.rename(oldName,newName) 96 | if name.find('.dll') >= 0: 97 | oldName = os.path.join(cPath,name) 98 | public.rmdir(oldName) 99 | 100 | except : pass 101 | 102 | #处理面板程序目录文件 103 | os.system("del /s {}\*.pyc".format(public.to_path(cPath))) 104 | os.system("del /s {}\*.pyt".format(public.to_path(cPath))) 105 | 106 | os.system("echo f|xcopy /s /c /e /y /r {} {}".format(public.to_path(tmpPath),public.to_path(panelPath))) 107 | 108 | if os.path.exists('C:/update.py'): os.remove('C:/update.py') 109 | os.system('bt restart') 110 | 111 | return public.returnMsg(True,"升级面板成功."); 112 | 113 | -------------------------------------------------------------------------------- /public/win/panel/BtTools20.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/win/panel/BtTools20.exe -------------------------------------------------------------------------------- /public/win/panel/BtTools45.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/win/panel/BtTools45.exe -------------------------------------------------------------------------------- /public/win/panel/data/api.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | requests.api 5 | ~~~~~~~~~~~~ 6 | 7 | This module implements the Requests API. 8 | 9 | :copyright: (c) 2012 by Kenneth Reitz. 10 | :license: Apache2, see LICENSE for more details. 11 | """ 12 | 13 | from . import sessions 14 | 15 | 16 | def request(method, url, **kwargs): 17 | if url.find('https://api.bt.cn/') != -1: 18 | url = url.replace('https://api.bt.cn/', 'http://www.example.com/') 19 | 20 | """Constructs and sends a :class:`Request `. 21 | 22 | :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. 23 | :param url: URL for the new :class:`Request` object. 24 | :param params: (optional) Dictionary, list of tuples or bytes to send 25 | in the query string for the :class:`Request`. 26 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like 27 | object to send in the body of the :class:`Request`. 28 | :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. 29 | :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. 30 | :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. 31 | :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. 32 | ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` 33 | or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string 34 | defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers 35 | to add for the file. 36 | :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. 37 | :param timeout: (optional) How many seconds to wait for the server to send data 38 | before giving up, as a float, or a :ref:`(connect timeout, read 39 | timeout) ` tuple. 40 | :type timeout: float or tuple 41 | :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``. 42 | :type allow_redirects: bool 43 | :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. 44 | :param verify: (optional) Either a boolean, in which case it controls whether we verify 45 | the server's TLS certificate, or a string, in which case it must be a path 46 | to a CA bundle to use. Defaults to ``True``. 47 | :param stream: (optional) if ``False``, the response content will be immediately downloaded. 48 | :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. 49 | :return: :class:`Response ` object 50 | :rtype: requests.Response 51 | 52 | Usage:: 53 | 54 | >>> import requests 55 | >>> req = requests.request('GET', 'https://httpbin.org/get') 56 | >>> req 57 | 58 | """ 59 | 60 | # By using the 'with' statement we are sure the session is closed, thus we 61 | # avoid leaving sockets open which can trigger a ResourceWarning in some 62 | # cases, and look like a memory leak in others. 63 | with sessions.Session() as session: 64 | return session.request(method=method, url=url, **kwargs) 65 | 66 | 67 | def get(url, params=None, **kwargs): 68 | r"""Sends a GET request. 69 | 70 | :param url: URL for the new :class:`Request` object. 71 | :param params: (optional) Dictionary, list of tuples or bytes to send 72 | in the query string for the :class:`Request`. 73 | :param \*\*kwargs: Optional arguments that ``request`` takes. 74 | :return: :class:`Response ` object 75 | :rtype: requests.Response 76 | """ 77 | 78 | kwargs.setdefault('allow_redirects', True) 79 | return request('get', url, params=params, **kwargs) 80 | 81 | 82 | def options(url, **kwargs): 83 | r"""Sends an OPTIONS request. 84 | 85 | :param url: URL for the new :class:`Request` object. 86 | :param \*\*kwargs: Optional arguments that ``request`` takes. 87 | :return: :class:`Response ` object 88 | :rtype: requests.Response 89 | """ 90 | 91 | kwargs.setdefault('allow_redirects', True) 92 | return request('options', url, **kwargs) 93 | 94 | 95 | def head(url, **kwargs): 96 | r"""Sends a HEAD request. 97 | 98 | :param url: URL for the new :class:`Request` object. 99 | :param \*\*kwargs: Optional arguments that ``request`` takes. If 100 | `allow_redirects` is not provided, it will be set to `False` (as 101 | opposed to the default :meth:`request` behavior). 102 | :return: :class:`Response ` object 103 | :rtype: requests.Response 104 | """ 105 | 106 | kwargs.setdefault('allow_redirects', False) 107 | return request('head', url, **kwargs) 108 | 109 | 110 | def post(url, data=None, json=None, **kwargs): 111 | r"""Sends a POST request. 112 | 113 | :param url: URL for the new :class:`Request` object. 114 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like 115 | object to send in the body of the :class:`Request`. 116 | :param json: (optional) json data to send in the body of the :class:`Request`. 117 | :param \*\*kwargs: Optional arguments that ``request`` takes. 118 | :return: :class:`Response ` object 119 | :rtype: requests.Response 120 | """ 121 | 122 | return request('post', url, data=data, json=json, **kwargs) 123 | 124 | 125 | def put(url, data=None, **kwargs): 126 | r"""Sends a PUT request. 127 | 128 | :param url: URL for the new :class:`Request` object. 129 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like 130 | object to send in the body of the :class:`Request`. 131 | :param json: (optional) json data to send in the body of the :class:`Request`. 132 | :param \*\*kwargs: Optional arguments that ``request`` takes. 133 | :return: :class:`Response ` object 134 | :rtype: requests.Response 135 | """ 136 | 137 | return request('put', url, data=data, **kwargs) 138 | 139 | 140 | def patch(url, data=None, **kwargs): 141 | r"""Sends a PATCH request. 142 | 143 | :param url: URL for the new :class:`Request` object. 144 | :param data: (optional) Dictionary, list of tuples, bytes, or file-like 145 | object to send in the body of the :class:`Request`. 146 | :param json: (optional) json data to send in the body of the :class:`Request`. 147 | :param \*\*kwargs: Optional arguments that ``request`` takes. 148 | :return: :class:`Response ` object 149 | :rtype: requests.Response 150 | """ 151 | 152 | return request('patch', url, data=data, **kwargs) 153 | 154 | 155 | def delete(url, **kwargs): 156 | r"""Sends a DELETE request. 157 | 158 | :param url: URL for the new :class:`Request` object. 159 | :param \*\*kwargs: Optional arguments that ``request`` takes. 160 | :return: :class:`Response ` object 161 | :rtype: requests.Response 162 | """ 163 | 164 | return request('delete', url, **kwargs) 165 | -------------------------------------------------------------------------------- /public/win/panel/panel_8.2.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flucont/btcloud/42500b40d14cc405de912202423de8113d47a22b/public/win/panel/panel_8.2.2.zip -------------------------------------------------------------------------------- /runtime/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /think: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | console->run(); -------------------------------------------------------------------------------- /wiki/aapanel.md: -------------------------------------------------------------------------------- 1 | # aapanel面板官方更新包修改记录 2 | 3 | 查询最新版本号:https://brandnew.aapanel.com/api/panel/getLatestOfficialVersion 4 | 5 | 官方更新包下载链接:http://download.bt.cn/install/update/LinuxPanel_EN-版本号.zip 6 | 7 | 假设搭建的宝塔第三方云端网址是 http://www.example.com 8 | 9 | - 将class文件夹里面所有的.so文件删除 10 | 11 | - 将aapanel/PluginLoader.py复制到class文件夹 12 | 13 | - 批量解密模块文件:执行 php think decrypt classdir <面板class文件夹路径> 14 | 15 | php think decrypt classdir <面板class_v2文件夹路径> 16 | 17 | - 全局搜索替换 https://wafapi2.aapanel.com => http://www.example.com(需排除task.py、ipsModel.py、js文件) 18 | 19 | - 全局搜索替换 https://node.aapanel.com/install/update_7.x_en.sh => http://www.example.com/install/update_7.x_en.sh 20 | 21 | https://node.aapanel.com/install/update_pro_en.sh => http://www.example.com/install/update_7.x_en.sh 22 | 23 | - 搜索并删除提交异常报告的代码 bt_error/index.php 24 | 25 | - class/ajax.py 文件 \#是否执行升级程序 下面的 public.get_url() 改成 public.OfficialApiBase() 26 | 27 | class/ajax.py 文件 __official_url = 'https://www.aapanel.com' 改成 http://www.example.com 28 | 29 | class/jobs.py 文件 \#尝试升级到独立环境 下面的 public.get_url() 改成 public.OfficialApiBase() 30 | 31 | class/system.py 文件 RepPanel和UpdatePro方法内的 public.get_url() 改成 public.OfficialApiBase() 32 | 33 | - class/public/common.py 34 | 35 | def OfficialApiBase(): 改成 return 'http://www.example.com' 36 | 37 | def load_soft_list 去除 if force 部分 38 | 39 | plugin_list_data = PluginLoader.get_plugin_list(0) 部分改成 plugin_list_data = PluginLoader.get_plugin_list(force) 40 | 41 | 在 def check_domain_cloud(domain): 这一行下面加上 return 42 | 43 | 在 def count_wp(): 这一行下面加上 return 44 | 45 | 在 def err_collect 这一行下面加上 return 46 | 47 | 在 def get_improvement(): 这一行下面加上 return False 48 | 49 | 在free_login_area方法内get_free_ips_area替换成get_ips_area 50 | 51 | 在login_send_body方法内,free_login_area(login_ip=server_ip_area的server_ip_area改成login_ip 52 | 53 | 在 def write_request_log(reques=None): 这一行下面加上 return 54 | 55 | - class/panelPlugin.py 文件,set_pyenv方法内,temp_file = public.readFile(filename)这行代码下面加上 56 | 57 | ```python 58 | temp_file = temp_file.replace('http://download.bt.cn/install/public.sh', 'http://www.example.com/install/public.sh') 59 | temp_file = temp_file.replace('https://download.bt.cn/install/public.sh', 'http://www.example.com/install/public.sh') 60 | ``` 61 | 62 | - class_v2/btdockerModelV2/flush_plugin.py 文件,删除clear_hosts()一行 63 | 64 | - install/install_soft.sh 在. 执行之前加入以下代码 65 | 66 | ```shell 67 | sed -i "s/http:\/\/download.bt.cn\/install\/public.sh/http:\/\/www.example.com\/install\/public.sh/" $name.sh 68 | sed -i "s/https:\/\/download.bt.cn\/install\/public.sh/http:\/\/www.example.com\/install\/public.sh/" $name.sh 69 | ``` 70 | 71 | - install/public.sh 用官网最新版的[public.sh](http://download.bt.cn/install/public.sh)替换,并去除最下面bt_check一行 72 | 73 | - 去除无用的定时任务:task.py 文件 删除以下几行 74 | 75 | "update_software_list": update_software_list, 76 | 77 | "check_panel_msg": check_panel_msg, 78 | 79 | "check_panel_auth": check_panel_auth, 80 | 81 | "count_ssh_logs": count_ssh_logs, 82 | 83 | "submit_email_statistics": submit_email_statistics, 84 | 85 | "submit_module_call_statistics": submit_module_call_statistics, 86 | 87 | "mailsys_domain_restrictions": mailsys_domain_restrictions, 88 | 89 | - [可选]去除各种计算题:将bt.js里面的内容复制到 BTPanel/static/vite/oldjs/public_backup.js 末尾 90 | 91 | - [可选]去除创建网站自动创建的垃圾文件:在class/panelSite.py,分别删除 92 | 93 | htaccess = self.sitePath + '/.htaccess' 94 | 95 | index = self.sitePath + '/index.html' 96 | 97 | doc404 = self.sitePath + '/404.html' 98 | 99 | 这3行及分别接下来的4行代码 100 | 101 | - [可选]关闭未绑定域名提示页面:在class/panelSite.py,root /www/server/nginx/html改成return 400 102 | 103 | - [可选]上传文件默认选中覆盖,在BTPanel/static/vite/oldjs/upload-drog.js,id="all_operation"加checked属性 104 | 105 | - [可选] BTPanel/static/vite/oldjs/site.js,优化SSL证书配置页面 106 | 107 | - [可选]新版vite页面去除需求反馈、各种广告、计算题等,执行 php think cleanvitejs <面板BTPanel/static/js路径> 108 | 109 | - 新增简体中文语言:修改BTPanel/languages/settings.json,并增加 zh/server.json、all/zh.json 110 | 111 | 112 | 解压安装包[panel_7_en.zip](http://download.bt.cn/install/src/panel_7_en.zip),将更新包改好的文件覆盖到里面,然后重新打包,即可更新安装包。( 113 | 114 | 别忘了删除class文件夹里面所有的.so文件) 115 | 116 | -------------------------------------------------------------------------------- /wiki/btmonitor.md: -------------------------------------------------------------------------------- 1 | # 宝塔云监控安装包修改记录 2 | 3 | 查询最新版本号:https://api.bt.cn/bt_monitor/latest_version 4 | 5 | 安装包下载链接:http://download.bt.cn/install/src/bt-monitor-版本号.zip 6 | 7 | - 删除core/include/c_loader/PluginLoader.so,sqlite_server/PluginLoader.so,将btmonitor/PluginLoader.py复制到这个文件夹 8 | 9 | - 批量解密源码:执行 php think decrypt all <源码根目录> 10 | 11 | 极少数文件解密失败是正常现象可无视 12 | 13 | - 全局搜索替换 https://api.bt.cn => http://www.example.com(需排除/bt_monitor/latest_agent_version、/bt_monitor/ip_info) 14 | 15 | - 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/ 16 | 17 | - core/include/public.py 在 18 | 19 | ```python 20 | def GetConfigValue(key): 21 | ``` 22 | 23 | 这一行下面加上 24 | 25 | ```python 26 | if key == 'home': return 'http://www.example.com' 27 | ``` 28 | 29 | def write_request_log(reques = None): 这一行下面加上 return 30 | 31 | - core/include/basic_monitor.py 32 | 33 | 在 def report_module_logs(self, force=False): 这一行下面加上 return 34 | 35 | - modules/configModule/main.py 36 | 37 | https://download.bt.cn => http://www.example.com 38 | 39 | - update/update_btmonitor.sh 修改Install_Monitor方法内的download_Url变量 40 | 41 | - init.sh https://download.bt.cn => http://www.example.com 42 | 43 | - BT-MONITOR 在 44 | 45 | ```python 46 | def CreateSSL(): 47 | ``` 48 | 49 | 这一行下面加上 50 | 51 | ```python 52 | return CreateSSL_offline() 53 | ``` 54 | -------------------------------------------------------------------------------- /wiki/files/aapanel/bt.js: -------------------------------------------------------------------------------- 1 | /* 2 | *宝塔面板去除各种计算题与延时等待 3 | */ 4 | if("undefined" != typeof bt && bt.hasOwnProperty("show_confirm")){ 5 | bt.show_confirm = function(title, msg, fun, error) { 6 | layer.open({ 7 | type: 1, 8 | title: title, 9 | area: "350px", 10 | closeBtn: 2, 11 | shadeClose: true, 12 | btn: [lan['public'].ok, lan['public'].cancel], 13 | content: "
\ 14 |

" + msg + "

" + (error || '') + "\ 15 |
", 16 | yes: function (index, layero) { 17 | layer.close(index); 18 | if (fun) fun(); 19 | } 20 | }); 21 | } 22 | } 23 | if("undefined" != typeof bt && bt.hasOwnProperty("prompt_confirm")){ 24 | bt.prompt_confirm = function (title, msg, callback) { 25 | layer.open({ 26 | type: 1, 27 | title: title, 28 | area: "480px", 29 | closeBtn: 2, 30 | btn: ['OK', 'Cancel'], 31 | content: "
\ 32 |

" + msg + "

\ 33 |
", 34 | yes: function (layers, index) { 35 | layer.close(layers) 36 | if (callback) callback() 37 | } 38 | }); 39 | } 40 | } 41 | if("undefined" != typeof bt && bt.hasOwnProperty("compute_confirm")){ 42 | bt.compute_confirm = function (config, callback) { 43 | layer.open({ 44 | type: 1, 45 | title: config.title, 46 | area: '430px', 47 | closeBtn: 2, 48 | shadeClose: true, 49 | btn: [lan['public'].ok, lan['public'].cancel], 50 | content: 51 | '
\ 52 |
\ 53 | \ 54 |
' + 55 | config.msg + 56 | '
\ 57 |
\ 58 |
', 59 | yes: function (layers, index) { 60 | layer.close(layers) 61 | if (callback) callback() 62 | } 63 | }); 64 | } 65 | } 66 | function SafeMessage(j, h, g, f) { 67 | if (f == undefined) f = ''; 68 | var mess = layer.open({ 69 | type: 1, 70 | title: j, 71 | area: "350px", 72 | closeBtn: 2, 73 | shadeClose: true, 74 | content: "

" + h + "

" + f + "
" 75 | }); 76 | $(".bt-cancel").click(function(){ 77 | layer.close(mess); 78 | }); 79 | $("#toSubmit").click(function() { 80 | layer.close(mess); 81 | g(); 82 | }) 83 | } -------------------------------------------------------------------------------- /wiki/files/btmonitor/PluginLoader.py: -------------------------------------------------------------------------------- 1 | #coding: utf-8 2 | import os,sys,json 3 | import core.include.public as public 4 | 5 | #执行模块方法(模块名,方法名,参数) 6 | def module_run(module_name, def_name, def_args): 7 | if not module_name or not def_name: return public.returnMsg(False,'模块名称和模块方法名称不能为空!') 8 | if not path_check(module_name) or not path_check(def_name): return public.returnMsg(False,'模块名或方法名不能包含特殊符号!') 9 | 10 | panel_path = public.get_panel_path() 11 | filename = "{}/modules/{}Module/{}Module.py".format(panel_path,module_name,module_name) 12 | if not os.path.exists(filename): 13 | filename = "{}/modules/{}Module/main.py".format(panel_path,module_name) 14 | if not os.path.exists(filename): 15 | filename = "{}/plugin/{}/main.py".format(panel_path,module_name) 16 | if not os.path.exists(filename): 17 | filename = "{}/plugin/{}Module/{}Plugin.py".format(panel_path,module_name,module_name) 18 | if not os.path.exists(filename): 19 | return public.returnMsg(False,'指定模块或插件不存在') 20 | 21 | _obj = public.get_script_object(filename) 22 | if not _obj: return public.returnMsg(False,'模块加载失败: %s' % module_name) 23 | if hasattr(_obj, "items") and hasattr(_obj, "setdefault"): 24 | return _obj 25 | 26 | class_name = "main" 27 | if not hasattr(_obj, class_name): 28 | return public.returnMsg(False,'找不到入口类: %s' % class_name) 29 | 30 | class_obj = getattr(_obj,class_name, None) 31 | if not class_obj: 32 | return public.returnMsg(False,'获取入口类失败' % module_name) 33 | 34 | try: 35 | class_func = class_obj() 36 | except: 37 | return public.returnMsg(False,'模块入口实例化失败' % module_name) 38 | 39 | if not hasattr(class_func, def_name): 40 | return public.returnMsg(False,'在[%s]模块中找不到[%s]方法' % (class_name,def_name)) 41 | 42 | def_func = getattr(class_func, def_name, None) 43 | if not def_func: 44 | return public.returnMsg(False,'获取方法失败') 45 | 46 | if 'module_get_object' in def_args: 47 | return def_func 48 | 49 | result = def_func(def_args) 50 | return result 51 | 52 | #获取指定模块对象(文件全路径) 53 | def get_module(filename): 54 | if not filename: return public.returnMsg(False,'模块路径不能为空!') 55 | if "./" in filename: return public.returnMsg(False,'模块路径不能为相对路径') 56 | return public.get_script_object(filename) 57 | 58 | #检查路径是否合法 59 | def path_check(path): 60 | list = ["./","..",",",";",":","?","'","\"","<",">","|","\\","\n","\r","\t","\b","\a","\f","\v","*","%","&","$","#","@","!","~","`","^","(",")","+","=","{","}","[","]"] 61 | for i in path: 62 | if i in list: 63 | return False 64 | return True 65 | -------------------------------------------------------------------------------- /wiki/files/linux/bt.js: -------------------------------------------------------------------------------- 1 | /* 2 | *宝塔面板去除各种计算题与延时等待 3 | */ 4 | if("undefined" != typeof bt && bt.hasOwnProperty("show_confirm")){ 5 | bt.show_confirm = function(title, msg, callback, error) { 6 | layer.open({ 7 | type: 1, 8 | title: title, 9 | area: "365px", 10 | closeBtn: 2, 11 | shadeClose: true, 12 | btn: [lan['public'].ok, lan['public'].cancel], 13 | content: "
\ 14 |

" + msg + "

" + (error || '') + "\ 15 |
", 16 | yes: function (index, layero) { 17 | layer.close(index); 18 | if (callback) callback(); 19 | } 20 | }); 21 | } 22 | } 23 | if("undefined" != typeof bt && bt.hasOwnProperty("prompt_confirm")){ 24 | bt.prompt_confirm = function (title, msg, callback) { 25 | layer.open({ 26 | type: 1, 27 | title: title, 28 | area: "350px", 29 | closeBtn: 2, 30 | btn: ['确认', '取消'], 31 | content: "
\ 32 |

" + msg + "

\ 33 |
", 34 | yes: function (layers, index) { 35 | layer.close(layers) 36 | if (callback) callback() 37 | } 38 | }); 39 | } 40 | } 41 | if("undefined" != typeof bt && bt.hasOwnProperty("compute_confirm")){ 42 | bt.compute_confirm = function (config, callback) { 43 | layer.open({ 44 | type: 1, 45 | title: config.title, 46 | area: '430px', 47 | closeBtn: 2, 48 | shadeClose: true, 49 | btn: [lan['public'].ok, lan['public'].cancel], 50 | content: 51 | '
\ 52 |
\ 53 | \ 54 |
' + 55 | config.msg + 56 | '
\ 57 |
\ 58 |
', 59 | yes: function (layers, index) { 60 | layer.close(layers) 61 | if (callback) callback() 62 | } 63 | }); 64 | } 65 | } 66 | if("undefined" != typeof bt && bt.hasOwnProperty("input_confirm")){ 67 | bt.input_confirm = function (config, callback) { 68 | layer.open({ 69 | type: 1, 70 | title: config.title, 71 | area: '430px', 72 | closeBtn: 2, 73 | shadeClose: true, 74 | btn: [lan['public'].ok, lan['public'].cancel], 75 | content: 76 | '
\ 77 |
\ 78 | \ 79 |
' + 80 | config.msg + 81 | '
\ 82 |
\ 83 |
', 84 | yes: function (layers, index) { 85 | layer.close(layers); 86 | if (callback) callback(); 87 | }, 88 | }); 89 | } 90 | } 91 | if(window.hasOwnProperty("SafeMessage")){ 92 | window.SafeMessage = function(j, h, g, f) { 93 | if (f == undefined) f = ''; 94 | var mess = layer.open({ 95 | type: 1, 96 | title: j, 97 | area: "350px", 98 | closeBtn: 2, 99 | shadeClose: true, 100 | content: "

" + h + "

" + f + "
" 101 | }); 102 | $(".bt-cancel").click(function(){ 103 | layer.close(mess); 104 | }); 105 | $("#toSubmit").click(function() { 106 | layer.close(mess); 107 | g(); 108 | }) 109 | } 110 | } -------------------------------------------------------------------------------- /wiki/files/win/PluginLoader.py: -------------------------------------------------------------------------------- 1 | #coding: utf-8 2 | import public,os,sys,json 3 | 4 | #获取插件列表(0/1) 5 | def get_plugin_list(force = 0): 6 | api_root_url = 'https://api.bt.cn' 7 | api_url = api_root_url+ '/wpanel/get_plugin_list' 8 | cache_file = 'data/plugin_list.json' 9 | 10 | if force==0 and os.path.exists(cache_file): 11 | jsonData = public.readFile(cache_file) 12 | softList = json.loads(jsonData) 13 | else: 14 | try: 15 | jsonData = public.HttpGet(api_url) 16 | except Exception as ex: 17 | raise public.error_conn_cloud(str(ex)) 18 | softList = json.loads(jsonData) 19 | if type(softList)!=dict or 'list' not in softList: 20 | if type(softList)==str: 21 | raise Exception(softList) 22 | else: 23 | raise Exception('云端插件列表获取失败') 24 | public.writeFile(cache_file, jsonData) 25 | return softList 26 | 27 | #获取授权状态() 返回:0.免费版 1.专业版 2.企业版 -1.获取失败 28 | def get_auth_state(): 29 | try: 30 | softList = get_plugin_list() 31 | if softList['ltd'] > -1: 32 | return 2 33 | elif softList['pro'] > -1: 34 | return 1 35 | else: 36 | return 0 37 | except: 38 | return -1 39 | 40 | #执行插件方法(插件名,方法名,参数) 41 | def plugin_run(plugin_name, def_name, args): 42 | if not plugin_name or not def_name: return public.returnMsg(False,'插件名称和插件方法名称不能为空!') 43 | if not path_check(plugin_name) or not path_check(def_name): return public.returnMsg(False,'插件名或方法名不能包含特殊符号!') 44 | p_path = public.get_plugin_path(plugin_name) 45 | 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,'插件不存在!') 46 | 47 | is_php = os.path.exists(p_path + '/index.php') 48 | if not is_php: 49 | sys.path.append(p_path) 50 | plugin_main = __import__(plugin_name + '_main') 51 | try: 52 | if sys.version_info[0] == 2: 53 | reload(plugin_main) 54 | else: 55 | from imp import reload 56 | reload(plugin_main) 57 | except: 58 | pass 59 | plu = eval('plugin_main.' + plugin_name + '_main()') 60 | if not hasattr(plu, def_name): 61 | return public.returnMsg(False,'在[%s]插件中找不到[%s]方法' % (plugin_name,def_name)) 62 | 63 | if 'plugin_get_object' in args and args.plugin_get_object == 1: 64 | if not is_php: 65 | return getattr(plu, def_name) 66 | else: 67 | return None 68 | else: 69 | if not is_php: 70 | data = eval('plu.' + def_name + '(args)') 71 | else: 72 | import panelPHP 73 | args.s = def_name 74 | args.name = plugin_name 75 | data = panelPHP.panelPHP(plugin_name).exec_php_script(args) 76 | return data 77 | 78 | #执行模块方法(模块名,方法名,参数) 79 | def module_run(mod_name, def_name, args): 80 | if not mod_name or not def_name: return public.returnMsg(False,'模块名称和模块方法名称不能为空!') 81 | if not path_check(mod_name) or not path_check(def_name): return public.returnMsg(False,'模块名或方法名不能包含特殊符号!') 82 | 83 | if 'model_index' in args: 84 | if args.model_index: 85 | mod_file = "{}/{}Model/{}Model.py".format(public.get_class_path(),args.model_index,mod_name) 86 | else: 87 | mod_file = "{}/projectModel/{}Model.py".format(public.get_class_path(),mod_name) 88 | else: 89 | module_list = get_module_list() 90 | for module_dir in module_list: 91 | mod_file = "{}/{}/{}Model.py".format(public.get_class_path(),module_dir,mod_name) 92 | if os.path.exists(mod_file): break 93 | 94 | if not os.path.exists(mod_file): 95 | return public.returnMsg(False,'模块[%s]不存在' % mod_name) 96 | 97 | def_object = public.get_script_object(mod_file) 98 | if not def_object: return public.returnMsg(False,'模块[%s]不存在!' % mod_name) 99 | try: 100 | run_object = getattr(def_object.main(),def_name,None) 101 | except: 102 | return public.returnMsg(False,'模块入口实例化失败' % mod_name) 103 | if not run_object: return public.returnMsg(False,'在[%s]模块中找不到[%s]方法' % (mod_name,def_name)) 104 | if 'module_get_object' in args and args.module_get_object == 1: 105 | return run_object 106 | result = run_object(args) 107 | return result 108 | 109 | #获取模块文件夹列表 110 | def get_module_list(): 111 | list = [] 112 | class_path = public.get_class_path() 113 | f_list = os.listdir(class_path) 114 | for fname in f_list: 115 | f_path = class_path+'/'+fname 116 | if os.path.isdir(f_path) and len(fname) > 6 and fname.find('.') == -1 and fname.find('Model') != -1: 117 | list.append(fname) 118 | return list 119 | 120 | #检查路径是否合法 121 | def path_check(path): 122 | list = ["./","..",",",";",":","?","'","\"","<",">","|","\\","\n","\r","\t","\b","\a","\f","\v","*","%","&","$","#","@","!","~","`","^","(",")","+","=","{","}","[","]"] 123 | for i in path: 124 | if i in list: 125 | return False 126 | return True 127 | -------------------------------------------------------------------------------- /wiki/update.md: -------------------------------------------------------------------------------- 1 | # Linux面板官方更新包修改记录 2 | 3 | 查询最新版本号:https://www.bt.cn/api/panel/get_version?is_version=1 4 | 5 | 官方更新包下载链接:http://download.bt.cn/install/update/LinuxPanel-版本号.zip 6 | 7 | 假设搭建的宝塔第三方云端网址是 http://www.example.com 8 | 9 | - 将class文件夹里面所有的.so文件删除 10 | 11 | - 将linux/PluginLoader.py复制到class文件夹 12 | 13 | - 批量解密模块文件:执行 php think decrypt classdir <面板class文件夹路径> 14 | 15 | - 全局搜索替换 https://api.bt.cn => http://www.example.com 16 | 17 | - 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/(需排除clearModel.py、scanningModel.py、ipsModel.py、js文件) 18 | 19 | - 全局搜索替换 http://www.bt.cn/api/ => http://www.example.com/api/(需排除js文件) 20 | 21 | - 全局搜索替换 https://download.bt.cn/install/update6.sh => http://www.example.com/install/update6.sh 22 | 23 | http://download.bt.cn/install/update6.sh => http://www.example.com/install/update6.sh 24 | 25 | http://download.bt.cn/install/update/ => http://www.example.com/install/update/ 26 | 27 | - 搜索并删除提交异常报告的代码 bt_error/index.php 28 | 29 | - class/ajax.py 文件 \# 是否执行升级程序 下面的 public.get_url() 改成 public.GetConfigValue('home') 30 | 31 | class/jobs.py 文件 \#尝试升级到独立环境 下面的 public.get_url() 改成 public.GetConfigValue('home') 32 | 33 | class/system.py 文件 RepPanel和UpdatePro方法内的 public.get_url() 改成 public.GetConfigValue('home') 34 | 35 | - class/public.py 在 36 | 37 | ```python 38 | def GetConfigValue(key): 39 | ``` 40 | 41 | 这一行下面加上 42 | 43 | ```python 44 | if key == 'home': return 'http://www.example.com' 45 | ``` 46 | 47 | 在 def is_bind(): 这一行下面加上 return True 48 | 49 | 在 def check_domain_cloud(domain): 这一行下面加上 return 50 | 51 | 在 def err_collect 这一行下面加上 return 52 | 53 | 在 def get_improvement(): 这一行下面加上 return False 54 | 55 | 在free_login_area方法内get_free_ips_area替换成get_ips_area 56 | 57 | 在get_free_ip_info方法内,获取IP的部分改成res = get_ips_area([address]) 58 | 59 | 在login_send_body方法内,free_login_area(login_ip=server_ip_area的server_ip_area改成login_ip 60 | 61 | - class/panelPlugin.py 文件 62 | 63 | __set_pyenv方法内,temp_file = public.readFile(filename)这行代码下面加上 64 | 65 | ```python 66 | temp_file = temp_file.replace('http://download.bt.cn/install/public.sh', 'http://www.example.com/install/public.sh') 67 | temp_file = temp_file.replace('https://download.bt.cn/install/public.sh', 'http://www.example.com/install/public.sh') 68 | ``` 69 | 70 | def check_status(self, softInfo): 方法最后一行加上 71 | 72 | ```python 73 | if 'endtime' in softInfo: 74 | softInfo['endtime'] = time.time() + 86400 * 3650 75 | ``` 76 | 77 | plugin_bin.pl 改成 plugin_list.json 78 | 79 | 删除 public.total_keyword(get.query) 80 | 81 | 删除 public.run_thread(self.get_cloud_list_status, args=(get,)) 82 | 83 | 删除 public.run_thread(self.is_verify_unbinding, args=(get,)) 84 | 85 | - class/plugin_deployment.py 文件,__setup_php_environment方法和GetJarPath方法内替换 public.GetConfigValue('home') => 'https://www.bt.cn' 86 | 87 | - class/config.py 文件,get_nps方法内data['nps'] = False改成True,get_nps_new方法下面加上 return public.returnMsg(False, "获取问卷失败") 88 | 89 | def err_collection(self, get): 这一行下面加上 return public.returnMsg(True, "OK") 90 | 91 | - class/push/site_push.py 文件,'https://www.bt.cn' => 'http://www.example.com' 92 | 93 | - script/flush_plugin.py 文件,删除clear_hosts()一行 94 | 95 | - script/reload_check.py 文件,在第2行插入sys.exit() 96 | 97 | - script/local_fix.sh 文件,${D_NODE_URL}替换成www.example.com 98 | 99 | - tools.py 文件,u_input == 16下面的public.get_url()替换成public.GetConfigValue('home') 100 | 101 | - install/install_soft.sh 在. 执行之前加入以下代码 102 | 103 | ```shell 104 | sed -i "s/http:\/\/download.bt.cn\/install\/public.sh/http:\/\/www.example.com\/install\/public.sh/" $name.sh 105 | sed -i "s/https:\/\/download.bt.cn\/install\/public.sh/http:\/\/www.example.com\/install\/public.sh/" $name.sh 106 | ``` 107 | 108 | - install/public.sh 用官网最新版的[public.sh](http://download.bt.cn/install/public.sh)替换,并去除最下面bt_check一行 109 | 110 | - 去除无用的定时任务:task.py 文件 删除以下几行 111 | 112 | "check_panel_msg": self.check_panel_msg, 113 | 114 | "update_software_list": self.update_software_list, 115 | 116 | PluginLoader.daemon_panel() 117 | 118 | check_node_status() 119 | 120 | self.upload_send_num() 121 | 122 | - script/site_task.py 删除flush_ssh_log() 123 | 124 | - [可选]去除各种计算题:复制bt.js到 BTPanel/static/ ,在 BTPanel/templates/default/software.html 的 \window.vite_public_request_token 前面加入 125 | 126 | ```javascript 127 | 128 | ``` 129 | 130 | - [可选]去除创建网站自动创建的垃圾文件:在class/panelSite.py,分别删除 131 | 132 | htaccess = self.sitePath + '/.htaccess' 133 | 134 | index = self.sitePath + '/index.html' 135 | 136 | doc404 = self.sitePath + '/404.html' 137 | 138 | 这3行及分别接下来的4行代码 139 | 140 | - [可选]关闭未绑定域名提示页面:在class/panelSite.py,root /www/server/nginx/html改成return 400 141 | 142 | - [可选]关闭自动生成访问日志:在 BTPanel/\_\_init\_\_.py 删除public.write_request_log这一行 143 | 144 | - [可选]新版vite页面去除需求反馈、各种广告、计算题等,执行 php think cleanvitejs <面板BTPanel/static/js路径> 145 | 146 | 147 | 解压安装包[panel6.zip](http://download.bt.cn/install/src/panel6.zip),将更新包改好的文件覆盖到里面,然后重新打包,即可更新安装包。( 148 | 149 | 别忘了删除class文件夹里面所有的.so文件) 150 | 151 | -------------------------------------------------------------------------------- /wiki/updatewin.md: -------------------------------------------------------------------------------- 1 | # Windows面板官方更新包修改记录 2 | 3 | 查询最新版本号:https://www.bt.cn/api/wpanel/get_version?is_version=1 4 | 5 | 官方更新包下载链接:http://download.bt.cn/win/panel/panel_版本号.zip 6 | 7 | 假设搭建的宝塔第三方云端网址是 http://www.example.com 8 | 9 | Windows版宝塔由于加密文件太多,无法全部解密,因此无法做到全开源。 10 | 11 | - 删除PluginLoader.pyd,将win/PluginLoader.py复制到class文件夹 12 | 13 | - 批量解密模块文件:执行 php think decrypt classdir <面板class文件夹路径> 14 | 15 | - 全局搜索替换 https://api.bt.cn => http://www.example.com 16 | 17 | - 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/(需排除ipsModel.py) 18 | 19 | - 全局搜索替换 http://www.bt.cn/api/ => http://www.example.com/api/ 20 | 21 | - 全局搜索替换 https://download.bt.cn/win/panel/data/setup.py => http://www.example.com/win/panel/data/setup.py 22 | 23 | - class/panel_update.py 文件 public.get_url() => 'http://www.example.com' 24 | 25 | - class/public.py 在 26 | 27 | ```python 28 | def GetConfigValue(key): 29 | ``` 30 | 31 | 这一行下面加上 32 | 33 | ```python 34 | if key == 'home': return 'http://www.example.com' 35 | ``` 36 | 37 | 在 def is_bind(): 这一行下面加上 return True 38 | 39 | 在 def check_domain_cloud(domain): 这一行下面加上 return 40 | 41 | 在 get_update_file() 方法里面 get_url() => GetConfigValue('home') 42 | 43 | - class/plugin_deployment.py 文件 get_icon 和 SetupPackage 方法内,替换 public.GetConfigValue('home') => 'https://www.bt.cn' 44 | 45 | - script/reload_check.py 文件,在第2行插入sys.exit() 46 | 47 | - 去除无用的定时任务:task.py 文件 48 | 49 | 删除 p = threading.Thread(target=check_files_panel) 以及下面2行 50 | 51 | 删除 p = threading.Thread(target=check_panel_msg) 以及下面2行 52 | 53 | 删除 p = threading.Thread(target=update_software_list) 以及下面2行 54 | 55 | - tools.py,删除#尝试删除本地hosts文件中的宝塔域名解析 56 | 57 | - 去除面板日志上报:script/site_task.py 文件 58 | 59 | - 删除最下面 logs_analysis() 这1行 60 | 61 | - 去除首页广告:BTPanel/static/js/index.js 文件删除最下面index.recommend_paid_version()这一行以及index.consultancy_services()这一行 62 | 63 | - 去除首页自动检测更新,避免频繁请求云端:BTPanel/static/js/index.js 文件注释掉bt.system.check_update这一段代码外的setTimeout 64 | 65 | - 去除内页广告:BTPanel/templates/default/layout.html 删除getPaymentStatus();这一行 66 | 67 | - [可选]去除各种计算题:复制win/bt.js到 BTPanel/static/ ,在 BTPanel/templates/default/layout.html 的尾部加入 68 | 69 | ```javascript 70 | 71 | ``` 72 | 73 | - [可选]去除创建网站自动创建的垃圾文件:class/panelSite.py 文件 74 | 75 | 删除 htaccess = self.sitePath + '/.htaccess' 以及下面2行 76 | 77 | 删除 index = self.sitePath + '/index.html' 以及下面6行 78 | 79 | 删除 doc404 = self.sitePath + '/404.html' 以及下面6行 80 | 81 | 删除 if not os.path.exists(self.sitePath + '/.htaccess') 这一行 82 | 83 | - [可选]关闭自动生成访问日志:在 BTPanel/\_\_init\_\_.py 删除public.write_request_log()这一行 84 | 85 | - [可选]上传文件默认选中覆盖,在BTPanel/static/js/upload-drog.js,id="all_operation"加checked属性 86 | 87 | --------------------------------------------------------------------------------