update
18
.env.example
Normal file
@ -0,0 +1,18 @@
|
||||
APP_DEBUG = false
|
||||
|
||||
[APP]
|
||||
DEFAULT_TIMEZONE = Asia/Shanghai
|
||||
|
||||
[DATABASE]
|
||||
TYPE = mysql
|
||||
HOSTNAME = {dbhost}
|
||||
DATABASE = {dbname}
|
||||
USERNAME = {dbuser}
|
||||
PASSWORD = {dbpwd}
|
||||
HOSTPORT = {dbport}
|
||||
CHARSET = utf8mb4
|
||||
PREFIX = {dbprefix}
|
||||
DEBUG = false
|
||||
|
||||
[LANG]
|
||||
default_lang = zh-cn
|
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/.idea
|
||||
/.vscode
|
||||
/vendor
|
||||
*.log
|
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Flucont
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
56
README.md
Normal file
@ -0,0 +1,56 @@
|
||||
# 宝塔面板第三方云端
|
||||
这是一个用php开发的宝塔面板第三方云端站点程序。
|
||||
|
||||
你可以使用此程序搭建属于自己的宝塔面板第三方云端,实现最新版宝塔面板私有化部署,不与宝塔官方接口通信,满足隐私安全合规需求。同时还可以去除面板强制绑定账号,DIY面板功能等。
|
||||
|
||||
网站后台管理可一键同步宝塔官方的插件列表与增量更新插件包,还有云端使用记录、IP黑白名单、操作日志、定时任务等功能。
|
||||
|
||||
本项目自带的宝塔安装包和更新包是7.9.8最新版,已修改适配此第三方云端,并且全开源,无so等加密文件。
|
||||
|
||||
觉得该项目不错的可以给个Star~
|
||||
|
||||
## 声明
|
||||
|
||||
1.此项目只能以自用为目的,不得侵犯堡塔公司及其他第三方的知识产权和其他合法权利。
|
||||
|
||||
2.搭建使用此项目必须有一定的编程和Linux运维基础,纯小白不建议使用。
|
||||
|
||||
## 环境要求
|
||||
|
||||
* `PHP` >= 7.4
|
||||
* `MySQL` >= 5.6
|
||||
* `fileinfo`扩展
|
||||
* `ZipArchive`扩展
|
||||
|
||||
## 部署方法
|
||||
|
||||
- [下载最新版的Release包](https://github.com/flucont/btcloud/releases)
|
||||
- 如果是下载的源码包,需要执行 `composer install --no-dev` 安装依赖,如果是下载的Release包,则不需要
|
||||
- 设置网站运行目录为`public`
|
||||
- 设置伪静态为`ThinkPHP`
|
||||
- 访问网站,会自动跳转到安装页面,根据提示安装完成
|
||||
|
||||
## 使用方法
|
||||
|
||||
- 在`批量替换工具`,执行页面显示的命令,可将bt安装包、更新包和脚本文件里面的`http://www.example.com`批量替换成当前网站的网址。
|
||||
- 在`系统基本设置`修改宝塔面板接口设置。你需要准备一个使用官方最新脚本安装并绑定账号的宝塔面板,用于获取最新插件列表及插件包。并根据界面提示安装好专用插件。
|
||||
- 在`定时任务设置`执行所显示的命令从宝塔官方获取最新的插件列表并批量下载插件包(增量更新)。当然你也可以去插件列表,一个一个点击下载。
|
||||
- 访问网站`/download`查看使用此第三方云端的一键安装脚本。
|
||||
|
||||
## 其他
|
||||
|
||||
- [Linux面板官方更新包修改记录](./wiki/update.md)
|
||||
|
||||
- [Windows面板官方更新包修改记录](./wiki/updatewin.md)
|
||||
|
||||
- 宝塔面板官方版与此第三方云端版对比:
|
||||
|
||||
| | 官方版 | 此第三方云端版 |
|
||||
| ---------- | ------------------------------------------------------------ | -------------------------------------------------- |
|
||||
| 版本更新 | 支持 | 支持 |
|
||||
| 面板广告 | 有广告 | 无广告 |
|
||||
| 是否全开源 | 没有全开源 | 全开源 |
|
||||
| 资源占用 | 各种统计上报等任务,资源占用略高 | 去除了很多无用的定时任务,资源占较少 |
|
||||
| 兼容性 | 由于编译的so文件有系统架构限制,兼容的系统仅限已编译的so对应的系统架构 | 由于全开源,没有已编译的so文件,因此无系统架构限制 |
|
||||
|
||||
|
1
app/.htaccess
Normal file
@ -0,0 +1 @@
|
||||
deny from all
|
22
app/AppService.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app;
|
||||
|
||||
use think\Service;
|
||||
|
||||
/**
|
||||
* 应用服务类
|
||||
*/
|
||||
class AppService extends Service
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
// 服务注册
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
// 服务启动
|
||||
}
|
||||
}
|
115
app/BaseController.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app;
|
||||
|
||||
use think\App;
|
||||
use think\exception\ValidateException;
|
||||
use think\Validate;
|
||||
use think\facade\View;
|
||||
|
||||
/**
|
||||
* 控制器基础类
|
||||
*/
|
||||
abstract class BaseController
|
||||
{
|
||||
/**
|
||||
* Request实例
|
||||
* @var \think\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* 应用实例
|
||||
* @var \think\App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 是否批量验证
|
||||
* @var bool
|
||||
*/
|
||||
protected $batchValidate = false;
|
||||
|
||||
/**
|
||||
* 控制器中间件
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [];
|
||||
|
||||
protected $clientip;
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
* @param App $app 应用对象
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->request = $this->app->request;
|
||||
|
||||
// 控制器初始化
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
// 初始化
|
||||
protected function initialize()
|
||||
{
|
||||
$this->clientip = real_ip();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证数据
|
||||
* @access protected
|
||||
* @param array $data 数据
|
||||
* @param string|array $validate 验证器名或者验证规则数组
|
||||
* @param array $message 提示信息
|
||||
* @param bool $batch 是否批量验证
|
||||
* @return array|string|true
|
||||
* @throws ValidateException
|
||||
*/
|
||||
protected function validate(array $data, $validate, array $message = [], bool $batch = false)
|
||||
{
|
||||
if (is_array($validate)) {
|
||||
$v = new Validate();
|
||||
$v->rule($validate);
|
||||
} else {
|
||||
if (strpos($validate, '.')) {
|
||||
// 支持场景
|
||||
[$validate, $scene] = explode('.', $validate);
|
||||
}
|
||||
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
|
||||
$v = new $class();
|
||||
if (!empty($scene)) {
|
||||
$v->scene($scene);
|
||||
}
|
||||
}
|
||||
|
||||
$v->message($message);
|
||||
|
||||
// 是否批量验证
|
||||
if ($batch || $this->batchValidate) {
|
||||
$v->batch(true);
|
||||
}
|
||||
|
||||
return $v->failException(true)->check($data);
|
||||
}
|
||||
|
||||
protected function alert($code, $msg = '', $url = null, $wait = 3)
|
||||
{
|
||||
if ($url) {
|
||||
$url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : (string)$this->app->route->buildUrl($url);
|
||||
}
|
||||
if(empty($msg)) $msg = '未知错误';
|
||||
|
||||
View::assign([
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'url' => $url,
|
||||
'wait' => $wait,
|
||||
]);
|
||||
return View::fetch(app()->getAppPath().'view/dispatch_jump.html');
|
||||
}
|
||||
|
||||
}
|
58
app/ExceptionHandle.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace app;
|
||||
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\Handle;
|
||||
use think\exception\HttpException;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\exception\ValidateException;
|
||||
use think\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 应用异常处理类
|
||||
*/
|
||||
class ExceptionHandle extends Handle
|
||||
{
|
||||
/**
|
||||
* 不需要记录信息(日志)的异常类列表
|
||||
* @var array
|
||||
*/
|
||||
protected $ignoreReport = [
|
||||
HttpException::class,
|
||||
HttpResponseException::class,
|
||||
ModelNotFoundException::class,
|
||||
DataNotFoundException::class,
|
||||
ValidateException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* 记录异常信息(包括日志或者其它方式记录)
|
||||
*
|
||||
* @access public
|
||||
* @param Throwable $exception
|
||||
* @return void
|
||||
*/
|
||||
public function report(Throwable $exception): void
|
||||
{
|
||||
// 使用内置的方式记录异常日志
|
||||
parent::report($exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @access public
|
||||
* @param \think\Request $request
|
||||
* @param Throwable $e
|
||||
* @return Response
|
||||
*/
|
||||
public function render($request, Throwable $e): Response
|
||||
{
|
||||
// 添加自定义异常处理机制
|
||||
|
||||
// 其他错误交给系统处理
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
}
|
8
app/Request.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace app;
|
||||
|
||||
// 应用请求对象类
|
||||
class Request extends \think\Request
|
||||
{
|
||||
|
||||
}
|
115
app/command/DecryptFile.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\facade\Db;
|
||||
use think\facade\Config;
|
||||
use app\lib\Plugins;
|
||||
|
||||
class DecryptFile extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('decrypt')
|
||||
->addArgument('type', Argument::REQUIRED, '文件类型,plugin:插件文件,module:模块文件,classdir:宝塔class目录,all:所有py文件')
|
||||
->addArgument('file', Argument::REQUIRED, '文件路径')
|
||||
->addArgument('os', Argument::OPTIONAL, '操作系统:Windows/Linux')
|
||||
->setDescription('解密宝塔面板python文件');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$type = trim($input->getArgument('type'));
|
||||
$file = trim($input->getArgument('file'));
|
||||
|
||||
if(!file_exists($file)){
|
||||
$output->writeln('文件不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
if($type == 'plugin'){
|
||||
$os = trim($input->getArgument('os'));
|
||||
try{
|
||||
if(Plugins::decode_plugin_main_local($file, $os)){
|
||||
$output->writeln('文件解密成功!');
|
||||
}else{
|
||||
$output->writeln('文件解密失败!');
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
$output->writeln($e->getMessage());
|
||||
}
|
||||
}elseif($type == 'module'){
|
||||
try{
|
||||
$res = Plugins::decode_module_file($file);
|
||||
if($res == 2){
|
||||
$output->writeln('文件解密失败!');
|
||||
}elseif($res == 1){
|
||||
$output->writeln('文件解密成功!');
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
$output->writeln($e->getMessage());
|
||||
}
|
||||
}elseif($type == 'classdir'){
|
||||
$file = rtrim($file, '/');
|
||||
if(!file_exists($file.'/common.py')){
|
||||
$output->writeln('当前路径非宝塔面板class目录');
|
||||
return;
|
||||
}
|
||||
$dirs = glob($file.'/*Model');
|
||||
foreach($dirs as $dir){
|
||||
if(!is_dir($dir))continue;
|
||||
$files = glob($dir.'/*Model.py');
|
||||
foreach($files as $file){
|
||||
try{
|
||||
$res = Plugins::decode_module_file($file);
|
||||
if($res == 2){
|
||||
$output->writeln('文件解密失败:'.$file);
|
||||
}elseif($res == 1){
|
||||
$output->writeln('文件解密成功:'.$file);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
$output->writeln($e->getMessage().':'.$file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}elseif($type == 'all'){
|
||||
$file = rtrim($file, '/');
|
||||
$this->scan_all_file($input, $output, $file);
|
||||
}else{
|
||||
$output->writeln('未知文件类型');
|
||||
}
|
||||
}
|
||||
|
||||
private function scan_all_file(Input $input, Output $output, $path) {
|
||||
$dir = opendir($path);
|
||||
while(false !== ( $file = readdir($dir)) ) {
|
||||
if (( $file != '.' ) && ( $file != '..' )) {
|
||||
$filepath = $path . '/' . $file;
|
||||
if ( is_dir($filepath) ) {
|
||||
$this->scan_all_file($input, $output, $filepath);
|
||||
}
|
||||
elseif(substr($filepath, -3) == '.py') {
|
||||
try{
|
||||
$res = Plugins::decode_module_file($filepath);
|
||||
if($res == 2){
|
||||
$output->writeln('文件解密失败:'.$filepath);
|
||||
}elseif($res == 1){
|
||||
$output->writeln('文件解密成功:'.$filepath);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
$output->writeln($e->getMessage().':'.$filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
}
|
||||
|
||||
|
||||
}
|
118
app/command/UpdateAll.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\facade\Db;
|
||||
use think\facade\Config;
|
||||
use app\lib\Plugins;
|
||||
|
||||
class UpdateAll extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('updateall')
|
||||
->setDescription('the updateall command');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$res = Db::name('config')->cache('configs',0)->column('value','key');
|
||||
Config::set($res, 'sys');
|
||||
|
||||
if(config_get('bt_url')){
|
||||
$this->process_plugins($input, $output, 'Linux');
|
||||
}
|
||||
if(config_get('wbt_url')){
|
||||
$this->process_plugins($input, $output, 'Windows');
|
||||
}
|
||||
|
||||
config_set('runtime', date('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
private function process_plugins(Input $input, Output $output, $os){
|
||||
//刷新插件列表
|
||||
if(!$this->refresh_plugin_list($input, $output, $os)){
|
||||
return;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
|
||||
$type = intval(config_get($os=='Windows'?'updateall_type_win':'updateall_type'));
|
||||
|
||||
$json_arr = Plugins::get_plugin_list($os);
|
||||
//循环下载缺少的插件
|
||||
foreach($json_arr['list'] as $plugin){
|
||||
if($type == 0 && ($plugin['type']==8 || $plugin['type']==12) || $type == 1 && $plugin['type']==12 || $plugin['type']==10 || $plugin['type']==5) continue;
|
||||
|
||||
foreach($plugin['versions'] as $version){
|
||||
$ver = $version['m_version'].'.'.$version['version'];
|
||||
if(isset($version['download'])){
|
||||
if(!file_exists(get_data_dir().'plugins/other/'.$version['download'])){
|
||||
if(!$this->download_plugin($input, $output, $plugin['name'], $ver, $os)){
|
||||
sleep(1);
|
||||
$this->download_plugin($input, $output, $plugin['name'], $ver, $os);
|
||||
}
|
||||
sleep(1);
|
||||
$count++;
|
||||
}
|
||||
}else{
|
||||
if(!file_exists(get_data_dir($os).'plugins/package/'.$plugin['name'].'-'.$ver.'.zip')){
|
||||
if(!$this->download_plugin($input, $output, $plugin['name'], $ver, $os)){
|
||||
sleep(1);
|
||||
$this->download_plugin($input, $output, $plugin['name'], $ver, $os);
|
||||
}
|
||||
sleep(1);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln($os.'本次成功下载'.$count.'个插件');
|
||||
}
|
||||
|
||||
private function refresh_plugin_list(Input $input, Output $output, $os){
|
||||
try{
|
||||
Plugins::refresh_plugin_list($os);
|
||||
Db::name('log')->insert(['uid' => 1, 'action' => '刷新插件列表', 'data' => '刷新'.$os.'插件列表成功', 'addtime' => date("Y-m-d H:i:s")]);
|
||||
$output->writeln('刷新'.$os.'插件列表成功');
|
||||
return true;
|
||||
}catch(\Exception $e){
|
||||
$output->writeln($e->getMessage());
|
||||
errorlog($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function download_plugin(Input $input, Output $output, $plugin_name, $version, $os){
|
||||
$fullname = $plugin_name.'-'.$version;
|
||||
try{
|
||||
Plugins::download_plugin($plugin_name, $version, $os);
|
||||
Db::name('log')->insert(['uid' => 1, 'action' => '下载插件', 'data' => $fullname.' os:'.$os, 'addtime' => date("Y-m-d H:i:s")]);
|
||||
$output->writeln('下载'.$os.'插件: '.$fullname.' 成功');
|
||||
return true;
|
||||
}catch(\Exception $e){
|
||||
$output->writeln($fullname.' '.$e->getMessage());
|
||||
errorlog($fullname.' '.$e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function download_plugin_image(Input $input, Output $output, $fname){
|
||||
try{
|
||||
Plugins::download_plugin_other($fname);
|
||||
$output->writeln('下载图片: '.$fname.' 成功');
|
||||
return true;
|
||||
}catch(\Exception $e){
|
||||
$output->writeln($fname.' '.$e->getMessage());
|
||||
errorlog($fname.' '.$e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
187
app/common.php
Normal file
@ -0,0 +1,187 @@
|
||||
<?php
|
||||
// 应用公共文件
|
||||
use think\facade\Db;
|
||||
|
||||
function get_data_dir($os = 'Linux'){
|
||||
return app()->getRootPath().'data/'.($os == 'Windows' ? 'win/' : '');
|
||||
}
|
||||
|
||||
|
||||
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
|
||||
$ckey_length = 4;
|
||||
$key = md5($key);
|
||||
$keya = md5(substr($key, 0, 16));
|
||||
$keyb = md5(substr($key, 16, 16));
|
||||
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
|
||||
$cryptkey = $keya.md5($keya.$keyc);
|
||||
$key_length = strlen($cryptkey);
|
||||
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
|
||||
$string_length = strlen($string);
|
||||
$result = '';
|
||||
$box = range(0, 255);
|
||||
$rndkey = array();
|
||||
for($i = 0; $i <= 255; $i++) {
|
||||
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
|
||||
}
|
||||
for($j = $i = 0; $i < 256; $i++) {
|
||||
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
|
||||
$tmp = $box[$i];
|
||||
$box[$i] = $box[$j];
|
||||
$box[$j] = $tmp;
|
||||
}
|
||||
for($a = $j = $i = 0; $i < $string_length; $i++) {
|
||||
$a = ($a + 1) % 256;
|
||||
$j = ($j + $box[$a]) % 256;
|
||||
$tmp = $box[$a];
|
||||
$box[$a] = $box[$j];
|
||||
$box[$j] = $tmp;
|
||||
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
|
||||
}
|
||||
if($operation == 'DECODE') {
|
||||
if(((int)substr($result, 0, 10) == 0 || (int)substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
|
||||
return substr($result, 26);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
} else {
|
||||
return $keyc.str_replace('=', '', base64_encode($result));
|
||||
}
|
||||
}
|
||||
|
||||
function random($length, $numeric = 0) {
|
||||
$seed = base_convert(md5(microtime().$_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
|
||||
$seed = $numeric ? (str_replace('0', '', $seed).'012340567890') : ($seed.'zZ'.strtoupper($seed));
|
||||
$hash = '';
|
||||
$max = strlen($seed) - 1;
|
||||
for($i = 0; $i < $length; $i++) {
|
||||
$hash .= $seed[mt_rand(0, $max)];
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
function get_curl($url, $post=0, $referer=0, $cookie=0, $header=0, $ua=0, $nobody=0, $addheader=0)
|
||||
{
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$httpheader[] = "Accept: */*";
|
||||
$httpheader[] = "Accept-Encoding: gzip,deflate,sdch";
|
||||
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
|
||||
$httpheader[] = "Connection: close";
|
||||
if($addheader){
|
||||
$httpheader = array_merge($httpheader, $addheader);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
|
||||
if ($post) {
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
|
||||
}
|
||||
if ($header) {
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
}
|
||||
if ($cookie) {
|
||||
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
|
||||
}
|
||||
if($referer){
|
||||
curl_setopt($ch, CURLOPT_REFERER, $referer);
|
||||
}
|
||||
if ($ua) {
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, $ua);
|
||||
}
|
||||
else {
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36");
|
||||
}
|
||||
if ($nobody) {
|
||||
curl_setopt($ch, CURLOPT_NOBODY, 1);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
$ret = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function jsonp_decode($jsonp, $assoc = false)
|
||||
{
|
||||
$jsonp = trim($jsonp);
|
||||
if(isset($jsonp[0]) && $jsonp[0] !== '[' && $jsonp[0] !== '{') {
|
||||
$begin = strpos($jsonp, '(');
|
||||
if(false !== $begin)
|
||||
{
|
||||
$end = strrpos($jsonp, ')');
|
||||
if(false !== $end)
|
||||
{
|
||||
$jsonp = substr($jsonp, $begin + 1, $end - $begin - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return json_decode($jsonp, $assoc);
|
||||
}
|
||||
|
||||
function config_get($key, $default = null)
|
||||
{
|
||||
$value = config('sys.'.$key);
|
||||
return $value!==null ? $value : $default;
|
||||
}
|
||||
|
||||
function config_set($key, $value)
|
||||
{
|
||||
$res = Db::name('config')->replace()->insert(['key'=>$key, 'value'=>$value]);
|
||||
return $res!==false;
|
||||
}
|
||||
|
||||
function real_ip($type=0){
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
if($type<=0 && isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
|
||||
foreach ($matches[0] AS $xip) {
|
||||
if (filter_var($xip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
|
||||
$ip = $xip;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} elseif ($type<=0 && isset($_SERVER['HTTP_CLIENT_IP']) && filter_var($_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
|
||||
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
||||
} elseif ($type<=1 && isset($_SERVER['HTTP_CF_CONNECTING_IP']) && filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
|
||||
$ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
|
||||
} elseif ($type<=1 && isset($_SERVER['HTTP_X_REAL_IP']) && filter_var($_SERVER['HTTP_X_REAL_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
|
||||
$ip = $_SERVER['HTTP_X_REAL_IP'];
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
|
||||
function getSubstr($str, $leftStr, $rightStr)
|
||||
{
|
||||
$left = strpos($str, $leftStr);
|
||||
$start = $left+strlen($leftStr);
|
||||
$right = strpos($str, $rightStr, $start);
|
||||
if($left < 0) return '';
|
||||
if($right>0){
|
||||
return substr($str, $start, $right-$start);
|
||||
}else{
|
||||
return substr($str, $start);
|
||||
}
|
||||
}
|
||||
|
||||
function checkRefererHost(){
|
||||
if(!request()->header('referer'))return false;
|
||||
$url_arr = parse_url(request()->header('referer'));
|
||||
$http_host = request()->header('host');
|
||||
if(strpos($http_host,':'))$http_host = substr($http_host, 0, strpos($http_host, ':'));
|
||||
return $url_arr['host'] === $http_host;
|
||||
}
|
||||
|
||||
function checkIfActive($string) {
|
||||
$array=explode(',',$string);
|
||||
$action = request()->action();
|
||||
if (in_array($action,$array)){
|
||||
return 'active';
|
||||
}else
|
||||
return null;
|
||||
}
|
||||
|
||||
function errorlog($msg){
|
||||
$handle = fopen(app()->getRootPath()."record.txt", 'a');
|
||||
fwrite($handle, date('Y-m-d H:i:s')."\t".$msg."\r\n");
|
||||
fclose($handle);
|
||||
}
|
388
app/controller/Admin.php
Normal file
@ -0,0 +1,388 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Request;
|
||||
use think\facade\Cache;
|
||||
use app\lib\Btapi;
|
||||
use app\lib\Plugins;
|
||||
|
||||
class Admin extends BaseController
|
||||
{
|
||||
public function verifycode()
|
||||
{
|
||||
return captcha();
|
||||
}
|
||||
|
||||
public function login(){
|
||||
if(request()->islogin){
|
||||
return redirect('/admin');
|
||||
}
|
||||
if(request()->isAjax()){
|
||||
$username = input('post.username',null,'trim');
|
||||
$password = input('post.password',null,'trim');
|
||||
$code = input('post.code',null,'trim');
|
||||
|
||||
if(empty($username) || empty($password)){
|
||||
return json(['code'=>-1, 'msg'=>'用户名或密码不能为空']);
|
||||
}
|
||||
if(!captcha_check($code)){
|
||||
return json(['code'=>-1, 'msg'=>'验证码错误']);
|
||||
}
|
||||
if($username == config_get('admin_username') && $password == config_get('admin_password')){
|
||||
Db::name('log')->insert(['uid' => 0, 'action' => '登录后台', 'data' => 'IP:'.$this->clientip, 'addtime' => date("Y-m-d H:i:s")]);
|
||||
$session = md5($username.config_get('admin_password'));
|
||||
$expiretime = time()+2562000;
|
||||
$token = authcode("{$username}\t{$session}\t{$expiretime}", 'ENCODE', config_get('syskey'));
|
||||
cookie('admin_token', $token, ['expire' => $expiretime, 'httponly' => true]);
|
||||
config_set('admin_lastlogin', date('Y-m-d H:i:s'));
|
||||
return json(['code'=>0]);
|
||||
}else{
|
||||
return json(['code'=>-1, 'msg'=>'用户名或密码错误']);
|
||||
}
|
||||
}
|
||||
return view();
|
||||
}
|
||||
|
||||
public function logout()
|
||||
{
|
||||
cookie('admin_token', null);
|
||||
return redirect('/admin/login');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$stat = ['total'=>0, 'free'=>0, 'pro'=>0, 'ltd'=>0, 'third'=>0];
|
||||
$json_arr = Plugins::get_plugin_list();
|
||||
if($json_arr){
|
||||
foreach($json_arr['list'] as $plugin){
|
||||
$stat['total']++;
|
||||
if($plugin['type']==10) $stat['third']++;
|
||||
elseif($plugin['type']==12) $stat['ltd']++;
|
||||
elseif($plugin['type']==8) $stat['pro']++;
|
||||
elseif($plugin['type']==5 || $plugin['type']==6 || $plugin['type']==7) $stat['free']++;
|
||||
}
|
||||
}
|
||||
$stat['runtime'] = Db::name('config')->where('key','runtime')->value('value') ?? '<font color="red">未运行</font>';
|
||||
$stat['record_total'] = Db::name('record')->count();
|
||||
$stat['record_isuse'] = Db::name('record')->whereTime('usetime', '>=', strtotime('-7 days'))->count();
|
||||
View::assign('stat', $stat);
|
||||
|
||||
$tmp = 'version()';
|
||||
$mysqlVersion = Db::query("select version()")[0][$tmp];
|
||||
$info = [
|
||||
'framework_version' => app()::VERSION,
|
||||
'php_version' => PHP_VERSION,
|
||||
'mysql_version' => $mysqlVersion,
|
||||
'software' => $_SERVER['SERVER_SOFTWARE'],
|
||||
'os' => php_uname(),
|
||||
'date' => date("Y-m-d H:i:s"),
|
||||
];
|
||||
View::assign('info', $info);
|
||||
return view();
|
||||
}
|
||||
|
||||
public function set(){
|
||||
if(request()->isAjax()){
|
||||
$params = Request::param();
|
||||
|
||||
foreach ($params as $key => $value) {
|
||||
config_set($key, $value);
|
||||
}
|
||||
cache('configs', NULL);
|
||||
return json(['code'=>0]);
|
||||
}
|
||||
$mod = input('param.mod', 'sys');
|
||||
View::assign('mod', $mod);
|
||||
View::assign('conf', config('sys'));
|
||||
$runtime = Db::name('config')->where('key','runtime')->value('value') ?? '<font color="red">未运行</font>';
|
||||
View::assign('runtime', $runtime);
|
||||
return view();
|
||||
}
|
||||
|
||||
public function setaccount(){
|
||||
$params = Request::param();
|
||||
if(isset($params['username']))$params['username']=trim($params['username']);
|
||||
if(isset($params['oldpwd']))$params['oldpwd']=trim($params['oldpwd']);
|
||||
if(isset($params['newpwd']))$params['newpwd']=trim($params['newpwd']);
|
||||
if(isset($params['newpwd2']))$params['newpwd2']=trim($params['newpwd2']);
|
||||
|
||||
if(empty($params['username'])) return json(['code'=>-1, 'msg'=>'用户名不能为空']);
|
||||
|
||||
config_set('admin_username', $params['username']);
|
||||
|
||||
if(!empty($params['oldpwd']) && !empty($params['newpwd']) && !empty($params['newpwd2'])){
|
||||
if(config_get('admin_password') != $params['oldpwd']){
|
||||
return json(['code'=>-1, 'msg'=>'旧密码不正确']);
|
||||
}
|
||||
if($params['newpwd'] != $params['newpwd2']){
|
||||
return json(['code'=>-1, 'msg'=>'两次新密码输入不一致']);
|
||||
}
|
||||
config_set('admin_password', $params['newpwd']);
|
||||
}
|
||||
cache('configs', NULL);
|
||||
cookie('admin_token', null);
|
||||
return json(['code'=>0]);
|
||||
}
|
||||
|
||||
public function testbturl(){
|
||||
$bt_url = input('post.bt_url');
|
||||
$bt_key = input('post.bt_key');
|
||||
if(!$bt_url || !$bt_key)return json(['code'=>-1, 'msg'=>'参数不能为空']);
|
||||
$btapi = new Btapi($bt_url, $bt_key);
|
||||
$result = $btapi->get_config();
|
||||
if($result && isset($result['status']) && ($result['status']==1 || isset($result['sites_path']))){
|
||||
$result = $btapi->get_user_info();
|
||||
if($result && isset($result['username'])){
|
||||
return json(['code'=>0, 'msg'=>'面板连接测试成功!']);
|
||||
}else{
|
||||
return json(['code'=>-1, 'msg'=>'面板连接测试成功,但未安装专用插件']);
|
||||
}
|
||||
}else{
|
||||
return json(['code'=>-1, 'msg'=>isset($result['msg'])?$result['msg']:'面板地址无法连接']);
|
||||
}
|
||||
}
|
||||
|
||||
public function plugins(){
|
||||
$typelist = [];
|
||||
$json_arr = Plugins::get_plugin_list();
|
||||
if($json_arr){
|
||||
foreach($json_arr['type'] as $type){
|
||||
$typelist[$type['id']] = $type['title'];
|
||||
}
|
||||
}
|
||||
View::assign('typelist', $typelist);
|
||||
return view();
|
||||
}
|
||||
|
||||
public function pluginswin(){
|
||||
$typelist = [];
|
||||
$json_arr = Plugins::get_plugin_list('Windows');
|
||||
if($json_arr){
|
||||
foreach($json_arr['type'] as $type){
|
||||
$typelist[$type['id']] = $type['title'];
|
||||
}
|
||||
}
|
||||
View::assign('typelist', $typelist);
|
||||
return view();
|
||||
}
|
||||
|
||||
public function plugins_data(){
|
||||
$type = input('post.type/d');
|
||||
$keyword = input('post.keyword', null, 'trim');
|
||||
$os = input('get.os');
|
||||
if(!$os) $os = 'Linux';
|
||||
|
||||
$json_arr = Plugins::get_plugin_list($os);
|
||||
if(!$json_arr) return json([]);
|
||||
|
||||
$typelist = [];
|
||||
foreach($json_arr['type'] as $row){
|
||||
$typelist[$row['id']] = $row['title'];
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach($json_arr['list'] as $plugin){
|
||||
if($type > 0 && $plugin['type']!=$type) continue;
|
||||
if(!empty($keyword) && $keyword != $plugin['name'] && stripos($plugin['title'], $keyword)===false) continue;
|
||||
$versions = [];
|
||||
foreach($plugin['versions'] as $version){
|
||||
$ver = $version['m_version'].'.'.$version['version'];
|
||||
if(isset($version['download'])){
|
||||
$status = false;
|
||||
if(file_exists(get_data_dir().'plugins/other/'.$version['download'])){
|
||||
$status = true;
|
||||
}
|
||||
$versions[] = ['status'=>$status, 'type'=>1, 'version'=>$ver, 'download'=>$version['download'], 'md5'=>$version['md5']];
|
||||
}else{
|
||||
$status = false;
|
||||
if(file_exists(get_data_dir($os).'plugins/package/'.$plugin['name'].'-'.$ver.'.zip')){
|
||||
$status = true;
|
||||
}
|
||||
$versions[] = ['status'=>$status, 'type'=>0, 'version'=>$ver];
|
||||
}
|
||||
}
|
||||
if($plugin['name'] == 'obs') $plugin['ps'] = substr($plugin['ps'],0,strpos($plugin['ps'],'<a '));
|
||||
$list[] = [
|
||||
'id' => $plugin['id'],
|
||||
'name' => $plugin['name'],
|
||||
'title' => $plugin['title'],
|
||||
'type' => $plugin['type'],
|
||||
'typename' => $typelist[$plugin['type']],
|
||||
'desc' => str_replace('target="_blank"','target="_blank" rel="noopener noreferrer"',$plugin['ps']),
|
||||
'price' => $plugin['price'],
|
||||
'author' => isset($plugin['author']) ? $plugin['author'] : '官方',
|
||||
'versions' => $versions
|
||||
];
|
||||
}
|
||||
return json($list);
|
||||
}
|
||||
|
||||
public function download_plugin(){
|
||||
$name = input('post.name', null, 'trim');
|
||||
$version = input('post.version', null, 'trim');
|
||||
$os = input('post.os');
|
||||
if(!$os) $os = 'Linux';
|
||||
if(!$name || !$version) return json(['code'=>-1, 'msg'=>'参数不能为空']);
|
||||
try{
|
||||
Plugins::download_plugin($name, $version, $os);
|
||||
Db::name('log')->insert(['uid' => 0, 'action' => '下载插件', 'data' => $name.'-'.$version.' os:'.$os, 'addtime' => date("Y-m-d H:i:s")]);
|
||||
return json(['code'=>0,'msg'=>'下载成功']);
|
||||
}catch(\Exception $e){
|
||||
return json(['code'=>-1, 'msg'=>$e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function refresh_plugins(){
|
||||
$os = input('get.os');
|
||||
if(!$os) $os = 'Linux';
|
||||
try{
|
||||
Plugins::refresh_plugin_list($os);
|
||||
Db::name('log')->insert(['uid' => 0, 'action' => '刷新插件列表', 'data' => '刷新'.$os.'插件列表成功', 'addtime' => date("Y-m-d H:i:s")]);
|
||||
return json(['code'=>0,'msg'=>'获取最新插件列表成功!']);
|
||||
}catch(\Exception $e){
|
||||
return json(['code'=>-1, 'msg'=>$e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function record(){
|
||||
return view();
|
||||
}
|
||||
|
||||
public function record_data(){
|
||||
$ip = input('post.ip', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('record');
|
||||
if(!empty($ip)){
|
||||
$select->where('ip', $ip);
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->order('id','desc')->limit($offset, $limit)->select();
|
||||
|
||||
return json(['total'=>$total, 'rows'=>$rows]);
|
||||
}
|
||||
|
||||
public function log(){
|
||||
return view();
|
||||
}
|
||||
|
||||
public function log_data(){
|
||||
$action = input('post.action', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('log');
|
||||
if(!empty($action)){
|
||||
$select->where('action', $action);
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->order('id','desc')->limit($offset, $limit)->select();
|
||||
|
||||
return json(['total'=>$total, 'rows'=>$rows]);
|
||||
}
|
||||
|
||||
public function list(){
|
||||
$type = input('param.type', 'black');
|
||||
View::assign('type', $type);
|
||||
View::assign('typename', $type=='white'?'白名单':'黑名单');
|
||||
return view();
|
||||
}
|
||||
|
||||
public function list_data(){
|
||||
$type = input('param.type', 'black');
|
||||
$ip = input('post.ip', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$tablename = $type == 'black' ? 'black' : 'white';
|
||||
$select = Db::name($tablename);
|
||||
if(!empty($ip)){
|
||||
$select->where('ip', $ip);
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->order('id','desc')->limit($offset, $limit)->select();
|
||||
|
||||
return json(['total'=>$total, 'rows'=>$rows]);
|
||||
}
|
||||
|
||||
public function list_op(){
|
||||
$type = input('param.type', 'black');
|
||||
$tablename = $type == 'black' ? 'black' : 'white';
|
||||
$act = input('post.act', null);
|
||||
if($act == 'get'){
|
||||
$id = input('post.id/d');
|
||||
if(!$id) return json(['code'=>-1, 'msg'=>'no id']);
|
||||
$data = Db::name($tablename)->where('id', $id)->find();
|
||||
return json(['code'=>0, 'data'=>$data]);
|
||||
}elseif($act == 'add'){
|
||||
$ip = input('post.ip', null, 'trim');
|
||||
if(!$ip) return json(['code'=>-1, 'msg'=>'IP不能为空']);
|
||||
if(Db::name($tablename)->where('ip', $ip)->find()){
|
||||
return json(['code'=>-1, 'msg'=>'该IP已存在']);
|
||||
}
|
||||
Db::name($tablename)->insert([
|
||||
'ip' => $ip,
|
||||
'enable' => 1,
|
||||
'addtime' => date("Y-m-d H:i:s")
|
||||
]);
|
||||
return json(['code'=>0, 'msg'=>'succ']);
|
||||
}elseif($act == 'edit'){
|
||||
$id = input('post.id/d');
|
||||
$ip = input('post.ip', null, 'trim');
|
||||
if(!$id || !$ip) return json(['code'=>-1, 'msg'=>'IP不能为空']);
|
||||
if(Db::name($tablename)->where('ip', $ip)->where('id', '<>', $id)->find()){
|
||||
return json(['code'=>-1, 'msg'=>'该IP已存在']);
|
||||
}
|
||||
Db::name($tablename)->where('id', $id)->update([
|
||||
'ip' => $ip
|
||||
]);
|
||||
return json(['code'=>0, 'msg'=>'succ']);
|
||||
}elseif($act == 'enable'){
|
||||
$id = input('post.id/d');
|
||||
$enable = input('post.enable/d');
|
||||
if(!$id) return json(['code'=>-1, 'msg'=>'no id']);
|
||||
Db::name($tablename)->where('id', $id)->update([
|
||||
'enable' => $enable
|
||||
]);
|
||||
return json(['code'=>0, 'msg'=>'succ']);
|
||||
}elseif($act == 'del'){
|
||||
$id = input('post.id/d');
|
||||
if(!$id) return json(['code'=>-1, 'msg'=>'no id']);
|
||||
Db::name($tablename)->where('id', $id)->delete();
|
||||
return json(['code'=>0, 'msg'=>'succ']);
|
||||
}
|
||||
return json(['code'=>-1, 'msg'=>'no act']);
|
||||
}
|
||||
|
||||
public function deplist(){
|
||||
$deplist_linux = get_data_dir().'config/deployment_list.json';
|
||||
$deplist_win = get_data_dir('Windows').'config/deployment_list.json';
|
||||
$deplist_linux_time = file_exists($deplist_linux) ? date("Y-m-d H:i:s", filemtime($deplist_linux)) : '不存在';
|
||||
$deplist_win_time = file_exists($deplist_win) ? date("Y-m-d H:i:s", filemtime($deplist_win)) : '不存在';
|
||||
View::assign('deplist_linux_time', $deplist_linux_time);
|
||||
View::assign('deplist_win_time', $deplist_win_time);
|
||||
return view();
|
||||
}
|
||||
|
||||
public function refresh_deplist(){
|
||||
$os = input('get.os');
|
||||
if(!$os) $os = 'Linux';
|
||||
try{
|
||||
Plugins::refresh_deplist($os);
|
||||
Db::name('log')->insert(['uid' => 0, 'action' => '刷新一键部署列表', 'data' => '刷新'.$os.'一键部署列表成功', 'addtime' => date("Y-m-d H:i:s")]);
|
||||
return json(['code'=>0,'msg'=>'获取最新一键部署列表成功!']);
|
||||
}catch(\Exception $e){
|
||||
return json(['code'=>-1, 'msg'=>$e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function cleancache(){
|
||||
Cache::clear();
|
||||
return json(['code'=>0,'msg'=>'succ']);
|
||||
}
|
||||
}
|
387
app/controller/Api.php
Normal file
@ -0,0 +1,387 @@
|
||||
<?php
|
||||
namespace app\controller;
|
||||
|
||||
use think\facade\Db;
|
||||
use app\BaseController;
|
||||
use app\lib\Plugins;
|
||||
|
||||
class Api extends BaseController
|
||||
{
|
||||
|
||||
//获取插件列表
|
||||
public function get_plugin_list(){
|
||||
if(!$this->checklist()) return '';
|
||||
$record = Db::name('record')->where('ip',$this->clientip)->find();
|
||||
if($record){
|
||||
Db::name('record')->where('id',$record['id'])->update(['usetime'=>date("Y-m-d H:i:s")]);
|
||||
}else{
|
||||
Db::name('record')->insert(['ip'=>$this->clientip, 'addtime'=>date("Y-m-d H:i:s"), 'usetime'=>date("Y-m-d H:i:s")]);
|
||||
}
|
||||
$json_arr = Plugins::get_plugin_list();
|
||||
if(!$json_arr) return json((object)[]);
|
||||
return json($json_arr);
|
||||
}
|
||||
|
||||
//获取插件列表(win)
|
||||
public function get_plugin_list_win(){
|
||||
if(!$this->checklist()) return '';
|
||||
$record = Db::name('record')->where('ip',$this->clientip)->find();
|
||||
if($record){
|
||||
Db::name('record')->where('id',$record['id'])->update(['usetime'=>date("Y-m-d H:i:s")]);
|
||||
}else{
|
||||
Db::name('record')->insert(['ip'=>$this->clientip, 'addtime'=>date("Y-m-d H:i:s"), 'usetime'=>date("Y-m-d H:i:s")]);
|
||||
}
|
||||
$json_file = get_data_dir('Windows').'config/plugin_list.json';
|
||||
if(file_exists($json_file)){
|
||||
$data = file_get_contents($json_file);
|
||||
$json_arr = json_decode($data, true);
|
||||
if($json_arr){
|
||||
return json($json_arr);
|
||||
}
|
||||
}
|
||||
return json((object)[]);
|
||||
}
|
||||
|
||||
//下载插件包
|
||||
public function download_plugin(){
|
||||
$plugin_name = input('post.name');
|
||||
$version = input('post.version');
|
||||
$os = input('post.os');
|
||||
if(!$plugin_name || !$version){
|
||||
return '参数不能为空';
|
||||
}
|
||||
if(!in_array($os,['Windows','Linux'])) $os = 'Linux';
|
||||
if(!preg_match('/^[a-zA-Z0-9_]+$/', $plugin_name) || !preg_match('/^[0-9.]+$/', $version)){
|
||||
return '参数不正确';
|
||||
}
|
||||
if(!$this->checklist()) '你的服务器被禁止使用此云端';
|
||||
$filepath = get_data_dir($os).'plugins/package/'.$plugin_name.'-'.$version.'.zip';
|
||||
if(file_exists($filepath)){
|
||||
$filename = $plugin_name.'.zip';
|
||||
$this->output_file($filepath, $filename);
|
||||
}else{
|
||||
return '云端不存在该插件包';
|
||||
}
|
||||
}
|
||||
|
||||
//下载插件主文件
|
||||
public function download_plugin_main(){
|
||||
$plugin_name = input('post.name');
|
||||
$version = input('post.version');
|
||||
$os = input('post.os');
|
||||
if(!$plugin_name || !$version){
|
||||
return '参数不能为空';
|
||||
}
|
||||
if(!in_array($os,['Windows','Linux'])) $os = 'Linux';
|
||||
if(!preg_match('/^[a-zA-Z0-9_]+$/', $plugin_name) || !preg_match('/^[0-9.]+$/', $version)){
|
||||
return '参数不正确';
|
||||
}
|
||||
if(!$this->checklist()) '你的服务器被禁止使用此云端';
|
||||
$filepath = get_data_dir($os).'plugins/main/'.$plugin_name.'-'.$version.'.dat';
|
||||
if(file_exists($filepath)){
|
||||
$filename = $plugin_name.'_main.py';
|
||||
$this->output_file($filepath, $filename);
|
||||
}else{
|
||||
$filepath = get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
|
||||
if(file_exists($filepath)){
|
||||
$filename = $plugin_name.'_main.py';
|
||||
$this->output_file($filepath, $filename);
|
||||
}else{
|
||||
return '云端不存在该插件主文件';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//下载插件其他文件
|
||||
public function download_plugin_other(){
|
||||
$fname = input('get.fname');
|
||||
if(!$fname){
|
||||
return json(['status'=>false, 'msg'=>'参数不能为空']);
|
||||
}
|
||||
if(strpos(dirname($fname),'.')!==false)return json(['status'=>false, 'msg'=>'参数不正确']);
|
||||
if(!$this->checklist()) return json(['status'=>false, 'msg'=>'你的服务器被禁止使用此云端']);
|
||||
$filepath = get_data_dir().'plugins/other/'.$fname;
|
||||
if(file_exists($filepath)){
|
||||
$filename = basename($fname);
|
||||
$this->output_file($filepath, $filename);
|
||||
}else{
|
||||
return json(['status'=>false, 'msg'=>'云端不存在该插件文件']);
|
||||
}
|
||||
}
|
||||
|
||||
public function get_update_logs(){
|
||||
$type = input('get.type');
|
||||
if($type == 'Windows'){
|
||||
$version = config_get('new_version_win');
|
||||
$data = [
|
||||
[
|
||||
'title' => 'Linux面板'.$version,
|
||||
'body' => config_get('update_msg_win'),
|
||||
'addtime' => config_get('update_date_win')
|
||||
]
|
||||
];
|
||||
}else{
|
||||
$version = config_get('new_version');
|
||||
$data = [
|
||||
[
|
||||
'title' => 'Linux面板'.$version,
|
||||
'body' => config_get('update_msg'),
|
||||
'addtime' => config_get('update_date')
|
||||
]
|
||||
];
|
||||
}
|
||||
return jsonp($data);
|
||||
}
|
||||
|
||||
public function get_version(){
|
||||
$version = config_get('new_version');
|
||||
return $version;
|
||||
}
|
||||
|
||||
public function get_version_win(){
|
||||
$version = config_get('new_version_win');
|
||||
return $version;
|
||||
}
|
||||
|
||||
//安装统计
|
||||
public function setup_count(){
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
//检测更新
|
||||
public function check_update(){
|
||||
$version = config_get('new_version');
|
||||
$down_url = request()->root(true).'/install/update/LinuxPanel-'.$version.'.zip';
|
||||
$data = [
|
||||
'force' => false,
|
||||
'version' => $version,
|
||||
'downUrl' => $down_url,
|
||||
'updateMsg' => config_get('update_msg'),
|
||||
'uptime' => config_get('update_date'),
|
||||
'is_beta' => 0,
|
||||
'adviser' => -1,
|
||||
'btb' => '',
|
||||
'beta' => [
|
||||
'version' => $version,
|
||||
'downUrl' => $down_url,
|
||||
'updateMsg' => config_get('update_msg'),
|
||||
'uptime' => config_get('update_date'),
|
||||
]
|
||||
];
|
||||
return json($data);
|
||||
}
|
||||
|
||||
//检测更新(win)
|
||||
public function check_update_win(){
|
||||
$version = config_get('new_version_win');
|
||||
$down_url = request()->root(true).'/win/panel/panel_'.$version.'.zip';
|
||||
$data = [
|
||||
'force' => false,
|
||||
'version' => $version,
|
||||
'downUrl' => $down_url,
|
||||
'updateMsg' => config_get('update_msg_win'),
|
||||
'uptime' => config_get('update_date_win'),
|
||||
'is_beta' => 0,
|
||||
'py_version' => '3.8.6',
|
||||
'adviser' => -1,
|
||||
'is_rec' => -1,
|
||||
'btb' => '',
|
||||
'beta' => [
|
||||
'py_version' => '3.8.6',
|
||||
'version' => $version,
|
||||
'downUrl' => $down_url,
|
||||
'updateMsg' => config_get('update_msg_win'),
|
||||
'uptime' => config_get('update_date_win'),
|
||||
]
|
||||
];
|
||||
return json($data);
|
||||
}
|
||||
|
||||
//宝塔云监控获取最新版本
|
||||
public function btm_latest_version(){
|
||||
$data = [
|
||||
'version' => config_get('new_version_btm'),
|
||||
'description' => config_get('update_msg_btm'),
|
||||
'create_time' => config_get('update_date_btm')
|
||||
];
|
||||
return json($data);
|
||||
}
|
||||
|
||||
//宝塔云监控更新日志
|
||||
public function btm_update_history(){
|
||||
$data = [
|
||||
[
|
||||
'version' => config_get('new_version_btm'),
|
||||
'description' => config_get('update_msg_btm'),
|
||||
'create_time' => config_get('update_date_btm')
|
||||
]
|
||||
];
|
||||
return json($data);
|
||||
}
|
||||
|
||||
//获取内测版更新日志
|
||||
public function get_beta_logs(){
|
||||
return json(['beta_ps'=>'当前暂无内测版', 'list'=>[]]);
|
||||
}
|
||||
|
||||
//检查用户绑定是否正确
|
||||
public function check_auth_key(){
|
||||
return '1';
|
||||
}
|
||||
|
||||
//从云端验证域名是否可访问
|
||||
public function check_domain(){
|
||||
$domain = input('post.domain',null,'trim');
|
||||
$ssl = input('post.ssl/d');
|
||||
if(!$domain) return json(['status'=>false, 'msg'=>'域名不能为空']);
|
||||
if(!strpos($domain,'.')) return json(['status'=>false, 'msg'=>'域名格式不正确']);
|
||||
$domain = str_replace('*.','',$domain);
|
||||
$ip = gethostbyname($domain);
|
||||
if(!$ip || $ip == $domain){
|
||||
return json(['status'=>false, 'msg'=>'无法访问']);
|
||||
}else{
|
||||
return json(['status'=>true, 'msg'=>'访问正常']);
|
||||
}
|
||||
}
|
||||
|
||||
//同步时间
|
||||
public function get_time(){
|
||||
return time();
|
||||
}
|
||||
|
||||
//同步时间
|
||||
public function get_win_date(){
|
||||
return date("Y-m-d H:i:s");
|
||||
}
|
||||
|
||||
//查询是否专业版(废弃)
|
||||
public function is_pro(){
|
||||
return json(['endtime'=>true, 'code'=>1]);
|
||||
}
|
||||
|
||||
//获取产品推荐信息
|
||||
public function get_plugin_remarks(){
|
||||
return json(['list'=>[], 'pro_list'=>[], 'kfqq'=>'', 'kf'=>'', 'qun'=>'']);
|
||||
}
|
||||
|
||||
//获取指定插件评分
|
||||
public function get_plugin_socre(){
|
||||
return json(['total'=>0, 'split'=>[0,0,0,0,0],'page'=>"<div><span class='Pcurrent'>1</span><span class='Pcount'>共计0条数据</span></div>",'data'=>[]]);
|
||||
}
|
||||
|
||||
//提交插件评分
|
||||
public function plugin_score(){
|
||||
return json(['status'=>true, 'msg'=>'您的评分已成功提交,感谢您的支持!']);
|
||||
}
|
||||
|
||||
//获取IP地址
|
||||
public function get_ip_address(){
|
||||
return $this->clientip;
|
||||
}
|
||||
|
||||
//绑定账号
|
||||
public function get_auth_token(){
|
||||
if(!$_POST['data']) return json(['status'=>false, 'msg'=>'参数不能为空']);
|
||||
$reqData = hex2bin($_POST['data']);
|
||||
parse_str($reqData, $arr);
|
||||
$serverid = $arr['serverid'];
|
||||
$userinfo = ['uid'=>1, 'username'=>'Administrator', 'address'=>'127.0.0.1', 'serverid'=>$serverid, 'access_key'=>random(32), 'secret_key'=>random(48), 'ukey'=>md5(time()), 'state'=>1];
|
||||
$data = bin2hex(urlencode(json_encode($userinfo)));
|
||||
return json(['status'=>true, 'msg'=>'登录成功!', 'data'=>$data]);
|
||||
}
|
||||
|
||||
//绑定账号新
|
||||
public function authorization_login(){
|
||||
if(!$_POST['data']) return json(['status'=>false, 'msg'=>'参数不能为空']);
|
||||
$reqData = hex2bin($_POST['data']);
|
||||
parse_str($reqData, $arr);
|
||||
$serverid = $arr['serverid'];
|
||||
$userinfo = ['uid'=>1, 'username'=>'Administrator', 'ip'=>'127.0.0.1', 'server_id'=>$serverid, 'access_key'=>random(32), 'secret_key'=>random(48)];
|
||||
$data = bin2hex(urlencode(json_encode($userinfo)));
|
||||
return json(['status'=>true, 'msg'=>'登录成功!', 'data'=>$data]);
|
||||
}
|
||||
|
||||
//刷新授权信息
|
||||
public function authorization_info(){
|
||||
if(!$_POST['data']) return json(['status'=>false, 'msg'=>'参数不能为空']);
|
||||
$reqData = hex2bin($_POST['data']);
|
||||
parse_str($reqData, $arr);
|
||||
$id = isset($arr['id'])&&$arr['id']>0?$arr['id']:1;
|
||||
$userinfo = ['id'=>$id, 'product'=>$arr['product'], 'status'=>2, 'clients'=>9999, 'durations'=>0, 'end_time'=>strtotime('+10 year')];
|
||||
$data = bin2hex(urlencode(json_encode($userinfo)));
|
||||
return json(['status'=>true, 'data'=>$data]);
|
||||
}
|
||||
|
||||
//一键部署列表
|
||||
public function get_deplist(){
|
||||
$os = input('post.os');
|
||||
$json_arr = Plugins::get_deplist($os);
|
||||
if(!$json_arr) return json([]);
|
||||
return json($json_arr);
|
||||
}
|
||||
|
||||
//获取宝塔SSL列表
|
||||
public function get_ssl_list(){
|
||||
$data = bin2hex('[]');
|
||||
return json(['status'=>true, 'msg'=>'', 'data'=>$data]);
|
||||
}
|
||||
|
||||
public function return_success(){
|
||||
return json(['status'=>true, 'msg'=>1, 'data'=>(object)[]]);
|
||||
}
|
||||
|
||||
public function return_error(){
|
||||
return json(['status'=>false, 'msg'=>'不支持当前操作']);
|
||||
}
|
||||
|
||||
public function return_empty(){
|
||||
return '';
|
||||
}
|
||||
|
||||
public function return_empty_array(){
|
||||
return json([]);
|
||||
}
|
||||
|
||||
public function return_page_data(){
|
||||
return json(['page'=>"<div><span class='Pcurrent'>1</span><span class='Pnumber'>1/0</span><span class='Pline'>从1-1000条</span><span class='Pcount'>共计0条数据</span></div>", 'data'=>[]]);
|
||||
}
|
||||
|
||||
//检查黑白名单
|
||||
private function checklist(){
|
||||
if(config_get('whitelist') == 1){
|
||||
if(Db::name('white')->where('ip', $this->clientip)->where('enable', 1)->find()){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}else{
|
||||
if(Db::name('black')->where('ip', $this->clientip)->where('enable', 1)->find()){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//下载大文件
|
||||
private function output_file($filepath, $filename){
|
||||
$filesize = filesize($filepath);
|
||||
$filemd5 = md5_file($filepath);
|
||||
|
||||
ob_clean();
|
||||
header("Content-Type: application/octet-stream");
|
||||
header("Content-Disposition: attachment; filename={$filename}.zip");
|
||||
header("Content-Length: {$filesize}");
|
||||
header("File-size: {$filesize}");
|
||||
header("Content-md5: {$filemd5}");
|
||||
|
||||
$read_buffer = 1024 * 100;
|
||||
$handle = fopen($filepath, 'rb');
|
||||
$sum_buffer = 0;
|
||||
while(!feof($handle) && $sum_buffer<$filesize) {
|
||||
echo fread($handle, min($read_buffer, ($filesize - $sum_buffer) + 1));
|
||||
$sum_buffer += $read_buffer;
|
||||
flush();
|
||||
}
|
||||
fclose($handle);
|
||||
exit;
|
||||
}
|
||||
}
|
24
app/controller/Index.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use think\facade\View;
|
||||
|
||||
class Index extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return 'Server is ok';
|
||||
}
|
||||
|
||||
public function download()
|
||||
{
|
||||
if(config_get('download_page') == '0' && !request()->islogin){
|
||||
return redirect('/admin/login');
|
||||
}
|
||||
View::assign('siteurl', request()->root(true));
|
||||
return view();
|
||||
}
|
||||
|
||||
|
||||
}
|
83
app/controller/Install.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
namespace app\controller;
|
||||
|
||||
use PDO;
|
||||
use Exception;
|
||||
use app\BaseController;
|
||||
use think\facade\View;
|
||||
use think\facade\Cache;
|
||||
|
||||
class Install extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
if (file_exists(app()->getRootPath().'.env')){
|
||||
return '当前已经安装成功,如果需要重新安装,请手动删除根目录.env文件';
|
||||
}
|
||||
if(request()->isPost()){
|
||||
$mysql_host = input('post.mysql_host', null, 'trim');
|
||||
$mysql_port = intval(input('post.mysql_port', '3306'));
|
||||
$mysql_user = input('post.mysql_user', null, 'trim');
|
||||
$mysql_pwd = input('post.mysql_pwd', null, 'trim');
|
||||
$mysql_name = input('post.mysql_name', null, 'trim');
|
||||
$mysql_prefix = input('post.mysql_prefix', 'cloud_', 'trim');
|
||||
$admin_username = input('post.admin_username', null, 'trim');
|
||||
$admin_password = input('post.admin_password', null, 'trim');
|
||||
|
||||
if(!$mysql_host || !$mysql_user || !$mysql_pwd || !$mysql_name || !$admin_username || !$admin_password){
|
||||
return json(['code'=>0, 'msg'=>'必填项不能为空']);
|
||||
}
|
||||
|
||||
$configdata = file_get_contents(app()->getRootPath().'.env.example');
|
||||
$configdata = str_replace(['{dbhost}','{dbname}','{dbuser}','{dbpwd}','{dbport}','{dbprefix}'], [$mysql_host, $mysql_name, $mysql_user, $mysql_pwd, $mysql_port, $mysql_prefix], $configdata);
|
||||
|
||||
try{
|
||||
$DB=new PDO("mysql:host=".$mysql_host.";dbname=".$mysql_name.";port=".$mysql_port,$mysql_user,$mysql_pwd);
|
||||
}catch(Exception $e){
|
||||
if($e->getCode() == 2002){
|
||||
$errorMsg='连接数据库失败:数据库地址填写错误!';
|
||||
}elseif($e->getCode() == 1045){
|
||||
$errorMsg='连接数据库失败:数据库用户名或密码填写错误!';
|
||||
}elseif($e->getCode() == 1049){
|
||||
$errorMsg='连接数据库失败:数据库名不存在!';
|
||||
}else{
|
||||
$errorMsg='连接数据库失败:'.$e->getMessage();
|
||||
}
|
||||
return json(['code'=>0, 'msg'=>$errorMsg]);
|
||||
}
|
||||
$DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
|
||||
$DB->exec("set sql_mode = ''");
|
||||
$DB->exec("set names utf8");
|
||||
|
||||
$sqls=file_get_contents(app()->getRootPath().'install.sql');
|
||||
$sqls=explode(';', $sqls);
|
||||
$sqls[]="REPLACE INTO `".$mysql_prefix."config` VALUES ('syskey', '".random(16)."')";
|
||||
$sqls[]="REPLACE INTO `".$mysql_prefix."config` VALUES ('admin_username', '".addslashes($admin_username)."')";
|
||||
$sqls[]="REPLACE INTO `".$mysql_prefix."config` VALUES ('admin_password', '".addslashes($admin_password)."')";
|
||||
$success=0;$error=0;$errorMsg=null;
|
||||
foreach ($sqls as $value) {
|
||||
$value=trim($value);
|
||||
if(empty($value))continue;
|
||||
$value = str_replace('cloud_',$mysql_prefix,$value);
|
||||
if($DB->exec($value)===false){
|
||||
$error++;
|
||||
$dberror=$DB->errorInfo();
|
||||
$errorMsg.=$dberror[2]."\n";
|
||||
}else{
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
if(empty($errorMsg)){
|
||||
if(!file_put_contents(app()->getRootPath().'.env', $configdata)){
|
||||
return json(['code'=>0, 'msg'=>'保存失败,请确保网站根目录有写入权限']);
|
||||
}
|
||||
Cache::clear();
|
||||
return json(['code'=>1, 'msg'=>'安装完成!成功执行SQL语句'.$success.'条']);
|
||||
}else{
|
||||
return json(['code'=>0, 'msg'=>$errorMsg]);
|
||||
}
|
||||
}
|
||||
return view();
|
||||
}
|
||||
|
||||
}
|
17
app/event.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
// 事件定义文件
|
||||
return [
|
||||
'bind' => [
|
||||
],
|
||||
|
||||
'listen' => [
|
||||
'AppInit' => [],
|
||||
'HttpRun' => [],
|
||||
'HttpEnd' => [],
|
||||
'LogLevel' => [],
|
||||
'LogWrite' => [],
|
||||
],
|
||||
|
||||
'subscribe' => [
|
||||
],
|
||||
];
|
230
app/lib/Btapi.php
Normal file
@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Btapi
|
||||
{
|
||||
private $BT_KEY; //接口密钥
|
||||
private $BT_PANEL; //面板地址
|
||||
|
||||
public function __construct($bt_panel, $bt_key){
|
||||
$this->BT_PANEL = $bt_panel;
|
||||
$this->BT_KEY = $bt_key;
|
||||
}
|
||||
|
||||
//获取面板配置信息
|
||||
public function get_config(){
|
||||
$url = $this->BT_PANEL.'/config?action=get_config';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
|
||||
$result = $this->curl($url,$p_data);
|
||||
|
||||
$data = json_decode($result,true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//获取已登录用户信息
|
||||
public function get_user_info(){
|
||||
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_user_info';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
|
||||
$result = $this->curl($url,$p_data);
|
||||
|
||||
$data = json_decode($result,true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//从云端获取插件列表
|
||||
public function get_plugin_list(){
|
||||
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_plugin_list';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
|
||||
$result = $this->curl($url,$p_data);
|
||||
|
||||
$data = json_decode($result,true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//下载插件包,返回文件路径
|
||||
public function get_plugin_filename($plugin_name, $version){
|
||||
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
$p_data['plugin_name'] = $plugin_name;
|
||||
$p_data['version'] = $version;
|
||||
|
||||
$result = $this->curl($url,$p_data);
|
||||
|
||||
$data = json_decode($result,true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//下载插件主程序文件,返回文件路径
|
||||
public function get_plugin_main_filename($plugin_name, $version){
|
||||
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin_main';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
$p_data['plugin_name'] = $plugin_name;
|
||||
$p_data['version'] = $version;
|
||||
|
||||
$result = $this->curl($url,$p_data);
|
||||
|
||||
$data = json_decode($result,true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//解密插件主程序py代码,返回文件路径
|
||||
public function get_decode_plugin_main($plugin_name, $version){
|
||||
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=decode_plugin_main';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
$p_data['plugin_name'] = $plugin_name;
|
||||
$p_data['version'] = $version;
|
||||
|
||||
$result = $this->curl($url,$p_data);
|
||||
|
||||
$data = json_decode($result,true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//下载插件其他文件,返回文件路径
|
||||
public function get_plugin_other_filename($fname){
|
||||
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=download_plugin_other';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
$p_data['fname'] = $fname;
|
||||
|
||||
$result = $this->curl($url,$p_data);
|
||||
|
||||
$data = json_decode($result,true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//下载文件
|
||||
public function download($filename, $localpath){
|
||||
$url = $this->BT_PANEL.'/download';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
$p_data['filename'] = $filename;
|
||||
|
||||
$result = $this->curl_download($url.'?'.http_build_query($p_data), $localpath);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//获取文件base64
|
||||
public function get_file($filename){
|
||||
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_file';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
$p_data['filename'] = $filename;
|
||||
|
||||
$result = $this->curl($url,$p_data);
|
||||
|
||||
$data = json_decode($result,true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//购买第三方插件
|
||||
public function create_plugin_other_order($pid){
|
||||
$url = $this->BT_PANEL.'/auth?action=create_plugin_other_order';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
$p_data['pid'] = $pid;
|
||||
$p_data['cycle'] = '999';
|
||||
$p_data['type'] = '0';
|
||||
|
||||
$result = $this->curl($url,$p_data);
|
||||
|
||||
$data = json_decode($result,true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//获取一键部署列表
|
||||
public function get_deplist(){
|
||||
$url = $this->BT_PANEL.'/plugin?action=a&name=kaixin&s=get_deplist';
|
||||
|
||||
$p_data = $this->GetKeyData();
|
||||
|
||||
$result = $this->curl($url,$p_data);
|
||||
|
||||
$data = json_decode($result,true);
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
private function GetKeyData(){
|
||||
$now_time = time();
|
||||
$p_data = array(
|
||||
'request_token' => md5($now_time.''.md5($this->BT_KEY)),
|
||||
'request_time' => $now_time
|
||||
);
|
||||
return $p_data;
|
||||
}
|
||||
|
||||
|
||||
private function curl($url, $data = null, $timeout = 60)
|
||||
{
|
||||
//定义cookie保存位置
|
||||
$cookie_file=app()->getRuntimePath().md5($this->BT_PANEL).'.cookie';
|
||||
if(!file_exists($cookie_file)){
|
||||
touch($cookie_file);
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
if($data){
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
$output = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function curl_download($url, $localpath, $timeout = 300)
|
||||
{
|
||||
//定义cookie保存位置
|
||||
$cookie_file=app()->getRuntimePath().md5($this->BT_PANEL).'.cookie';
|
||||
if(!file_exists($cookie_file)){
|
||||
touch($cookie_file);
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_file);
|
||||
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_file);
|
||||
$fp = fopen($localpath, 'w+');
|
||||
curl_setopt($ch, CURLOPT_FILE, $fp);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_exec($ch);
|
||||
if (curl_errno($ch)) {
|
||||
$message = curl_error($ch);
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
throw new Exception('下载文件失败:'.$message);
|
||||
}
|
||||
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if($httpcode>299){
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
throw new Exception('下载文件失败:HTTPCODE-'.$httpcode);
|
||||
}
|
||||
curl_close($ch);
|
||||
fclose($fp);
|
||||
return true;
|
||||
}
|
||||
}
|
347
app/lib/Plugins.php
Normal file
@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
use Exception;
|
||||
use ZipArchive;
|
||||
|
||||
class Plugins
|
||||
{
|
||||
|
||||
private static function get_btapi($os){
|
||||
if($os == 'Windows'){
|
||||
$bt_url = config_get('wbt_url');
|
||||
$bt_key = config_get('wbt_key');
|
||||
}else{
|
||||
$bt_url = config_get('bt_url');
|
||||
$bt_key = config_get('bt_key');
|
||||
}
|
||||
if(!$bt_url || !$bt_key) throw new Exception('请先配置好宝塔面板接口信息');
|
||||
$btapi = new Btapi($bt_url, $bt_key);
|
||||
return $btapi;
|
||||
}
|
||||
|
||||
//刷新插件列表
|
||||
public static function refresh_plugin_list($os = 'Linux'){
|
||||
$btapi = self::get_btapi($os);
|
||||
$result = $btapi->get_plugin_list();
|
||||
if($result && isset($result['list']) && isset($result['type'])){
|
||||
if(empty($result['list']) || empty($result['type'])){
|
||||
throw new Exception('获取插件列表失败:插件列表为空');
|
||||
}
|
||||
self::save_plugin_list($result, $os);
|
||||
}else{
|
||||
throw new Exception('获取插件列表失败:'.(isset($result['msg'])?$result['msg']:'面板连接失败'));
|
||||
}
|
||||
}
|
||||
|
||||
//保存插件列表
|
||||
private static function save_plugin_list($data, $os){
|
||||
$data['ip'] = '127.0.0.1';
|
||||
$data['serverid'] = '';
|
||||
$data['beta'] = 0;
|
||||
$data['uid'] = 1;
|
||||
$data['skey'] = '';
|
||||
$list = [];
|
||||
foreach($data['list'] as $plugin){
|
||||
if(isset($plugin['endtime'])) $plugin['endtime'] = 0;
|
||||
$list[] = $plugin;
|
||||
}
|
||||
$data['list'] = $list;
|
||||
$data['ltd'] = strtotime('+10 year');
|
||||
$json_file = get_data_dir($os).'config/plugin_list.json';
|
||||
if(!file_put_contents($json_file, json_encode($data))){
|
||||
throw new Exception('保存插件列表失败,文件无写入权限');
|
||||
}
|
||||
}
|
||||
|
||||
//获取插件列表
|
||||
public static function get_plugin_list($os = 'Linux'){
|
||||
$json_file = get_data_dir($os).'config/plugin_list.json';
|
||||
if(file_exists($json_file)){
|
||||
$data = file_get_contents($json_file);
|
||||
$json_arr = json_decode($data, true);
|
||||
if($json_arr){
|
||||
return $json_arr;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取一个插件信息
|
||||
public static function get_plugin_info($name, $os = 'Linux'){
|
||||
$json_arr = self::get_plugin_list($os);
|
||||
if(!$json_arr) return null;
|
||||
foreach($json_arr['list'] as $plugin){
|
||||
if($plugin['name'] == $name){
|
||||
return $plugin;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//下载插件(自动判断是否第三方)
|
||||
public static function download_plugin($plugin_name, $version, $os = 'Linux'){
|
||||
$plugin_info = Plugins::get_plugin_info($plugin_name, $os);
|
||||
if(!$plugin_info) throw new Exception('未找到该插件信息');
|
||||
if($plugin_info['type'] == 10 && isset($plugin_info['versions'][0]['download'])){
|
||||
if($plugin_info['price'] == 0){
|
||||
$btapi = self::get_btapi($os);
|
||||
$btapi->create_plugin_other_order($plugin_info['id']);
|
||||
}
|
||||
$fname = $plugin_info['versions'][0]['download'];
|
||||
$filemd5 = $plugin_info['versions'][0]['md5'];
|
||||
Plugins::download_plugin_other($fname, $filemd5, $os);
|
||||
if(isset($plugin_info['min_image']) && strpos($plugin_info['min_image'], 'fname=')){
|
||||
$fname = substr($plugin_info['min_image'], strpos($plugin_info['min_image'], '?fname=')+7);
|
||||
Plugins::download_plugin_other($fname, null, $os);
|
||||
}
|
||||
}else{
|
||||
Plugins::download_plugin_package($plugin_name, $version, $os);
|
||||
}
|
||||
}
|
||||
|
||||
//下载插件包
|
||||
public static function download_plugin_package($plugin_name, $version, $os = 'Linux'){
|
||||
$filepath = get_data_dir($os).'plugins/package/'.$plugin_name.'-'.$version.'.zip';
|
||||
$btapi = self::get_btapi($os);
|
||||
$result = $btapi->get_plugin_filename($plugin_name, $version);
|
||||
if($result && isset($result['status'])){
|
||||
if($result['status'] == true){
|
||||
$filename = $result['filename'];
|
||||
self::download_file($btapi, $filename, $filepath);
|
||||
if(file_exists($filepath)){
|
||||
$zip = new ZipArchive;
|
||||
if ($zip->open($filepath) === true)
|
||||
{
|
||||
$zip->extractTo(get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version);
|
||||
$zip->close();
|
||||
$main_filepath = get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
|
||||
if(file_exists($main_filepath) && filesize($main_filepath)>10){
|
||||
if(!strpos(file_get_contents($main_filepath), 'import ')){ //加密py文件,需要解密
|
||||
self::decode_plugin_main($plugin_name, $version, $main_filepath, $os);
|
||||
self::noauth_plugin_main($main_filepath);
|
||||
$zip->open($filepath, ZipArchive::CREATE);
|
||||
$zip->addFile($main_filepath, $plugin_name.'/'.$plugin_name.'_main.py');
|
||||
$zip->close();
|
||||
}
|
||||
}
|
||||
}else{
|
||||
throw new Exception('插件包解压缩失败');
|
||||
}
|
||||
return true;
|
||||
}else{
|
||||
throw new Exception('下载插件包失败,本地文件不存在');
|
||||
}
|
||||
}else{
|
||||
throw new Exception('下载插件包失败:'.($result['msg']?$result['msg']:'未知错误'));
|
||||
}
|
||||
}else{
|
||||
throw new Exception('下载插件包失败,接口返回错误');
|
||||
}
|
||||
}
|
||||
|
||||
//下载插件主程序文件
|
||||
public static function download_plugin_main($plugin_name, $version, $os = 'Linux'){
|
||||
$filepath = get_data_dir($os).'plugins/main/'.$plugin_name.'-'.$version.'.dat';
|
||||
$btapi = self::get_btapi($os);
|
||||
$result = $btapi->get_plugin_main_filename($plugin_name, $version);
|
||||
if($result && isset($result['status'])){
|
||||
if($result['status'] == true){
|
||||
$filename = $result['filename'];
|
||||
self::download_file($btapi, $filename, $filepath);
|
||||
if(file_exists($filepath)){
|
||||
return true;
|
||||
}else{
|
||||
throw new Exception('下载插件主程序文件失败,本地文件不存在');
|
||||
}
|
||||
}else{
|
||||
throw new Exception('下载插件主程序文件失败:'.($result['msg']?$result['msg']:'未知错误'));
|
||||
}
|
||||
}else{
|
||||
throw new Exception('下载插件主程序文件失败,接口返回错误');
|
||||
}
|
||||
}
|
||||
|
||||
//解密并下载插件主程序文件
|
||||
public static function decode_plugin_main($plugin_name, $version, $main_filepath, $os = 'Linux'){
|
||||
if(self::decode_plugin_main_local($main_filepath, $os)) return true;
|
||||
$btapi = self::get_btapi($os);
|
||||
$result = $btapi->get_decode_plugin_main($plugin_name, $version);
|
||||
if($result && isset($result['status'])){
|
||||
if($result['status'] == true){
|
||||
$filename = $result['filename'];
|
||||
self::download_file($btapi, $filename, $main_filepath);
|
||||
return true;
|
||||
}else{
|
||||
throw new Exception('解密插件主程序文件失败:'.($result['msg']?$result['msg']:'未知错误'));
|
||||
}
|
||||
}else{
|
||||
throw new Exception('解密插件主程序文件失败,接口返回错误');
|
||||
}
|
||||
}
|
||||
|
||||
//本地解密插件主程序文件
|
||||
public static function decode_plugin_main_local($main_filepath, $os = 'Linux'){
|
||||
$btapi = self::get_btapi($os);
|
||||
$userinfo = $btapi->get_user_info();
|
||||
if(isset($userinfo['uid'])){
|
||||
$src = file_get_contents($main_filepath);
|
||||
if($src===false)throw new Exception('文件打开失败');
|
||||
if(!$src || strpos($src, 'import ')!==false)return true;
|
||||
$uid = $userinfo['uid'];
|
||||
$serverid = $userinfo['serverid'];
|
||||
$key = md5(substr($serverid, 10, 16).$uid.$serverid);
|
||||
$iv = md5($key.$serverid);
|
||||
$key = substr($key, 8, 16);
|
||||
$iv = substr($iv, 8, 16);
|
||||
$data_arr = explode("\n", $src);
|
||||
$de_text = '';
|
||||
foreach($data_arr as $data){
|
||||
$data = trim($data);
|
||||
if(!empty($data) && strlen($data)!=24){
|
||||
$tmp = openssl_decrypt($data, 'aes-128-cbc', $key, 0, $iv);
|
||||
if($tmp) $de_text .= $tmp;
|
||||
}
|
||||
}
|
||||
if(!empty($de_text) && strpos($de_text, 'import ')!==false){
|
||||
file_put_contents($main_filepath, $de_text);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}else{
|
||||
throw new Exception('解密插件主程序文件失败,获取用户信息失败');
|
||||
}
|
||||
}
|
||||
|
||||
public static function decode_module_file($filepath){
|
||||
$src = file_get_contents($filepath);
|
||||
if($src===false)throw new Exception('文件打开失败');
|
||||
if(!$src || strpos($src, 'import ')!==false)return 0;
|
||||
$key = 'Z2B87NEAS2BkxTrh';
|
||||
$iv = 'WwadH66EGWpeeTT6';
|
||||
$data_arr = explode("\n", $src);
|
||||
$de_text = '';
|
||||
foreach($data_arr as $data){
|
||||
$data = trim($data);
|
||||
if(!empty($data)){
|
||||
$tmp = openssl_decrypt($data, 'aes-128-cbc', $key, 0, $iv);
|
||||
if($tmp) $de_text .= $tmp;
|
||||
}
|
||||
}
|
||||
if(!empty($de_text) && strpos($de_text, 'import ')!==false){
|
||||
file_put_contents($filepath, $de_text);
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
//去除插件主程序文件授权校验
|
||||
public static function noauth_plugin_main($main_filepath){
|
||||
$data = file_get_contents($main_filepath);
|
||||
if(!$data) return false;
|
||||
|
||||
$data = str_replace('\'http://www.bt.cn/api/panel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list_test', $data);
|
||||
$data = str_replace('\'https://www.bt.cn/api/panel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list_test', $data);
|
||||
$data = str_replace('\'http://www.bt.cn/api/panel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list', $data);
|
||||
$data = str_replace('\'https://www.bt.cn/api/panel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list', $data);
|
||||
$data = str_replace('\'http://www.bt.cn/api/panel/notpro', 'public.GetConfigValue(\'home\')+\'/api/panel/notpro', $data);
|
||||
$data = str_replace('\'https://www.bt.cn/api/panel/notpro', 'public.GetConfigValue(\'home\')+\'/api/panel/notpro', $data);
|
||||
|
||||
$data = str_replace('\'http://www.bt.cn/api/wpanel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list_test', $data);
|
||||
$data = str_replace('\'https://www.bt.cn/api/wpanel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list_test', $data);
|
||||
$data = str_replace('\'http://www.bt.cn/api/wpanel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list', $data);
|
||||
$data = str_replace('\'https://www.bt.cn/api/wpanel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list', $data);
|
||||
$data = str_replace('\'http://www.bt.cn/api/wpanel/notpro', 'public.GetConfigValue(\'home\')+\'/api/wpanel/notpro', $data);
|
||||
$data = str_replace('\'https://www.bt.cn/api/wpanel/notpro', 'public.GetConfigValue(\'home\')+\'/api/wpanel/notpro', $data);
|
||||
|
||||
file_put_contents($main_filepath, $data);
|
||||
}
|
||||
|
||||
//下载插件其他文件
|
||||
public static function download_plugin_other($fname, $filemd5 = null, $os = 'Linux'){
|
||||
$filepath = get_data_dir().'plugins/other/'.$fname;
|
||||
@mkdir(dirname($filepath), 0777, true);
|
||||
$btapi = self::get_btapi($os);
|
||||
$result = $btapi->get_plugin_other_filename($fname);
|
||||
if($result && isset($result['status'])){
|
||||
if($result['status'] == true){
|
||||
$filename = $result['filename'];
|
||||
self::download_file($btapi, $filename, $filepath);
|
||||
if(file_exists($filepath)){
|
||||
if($filemd5 && md5_file($filepath) != $filemd5){
|
||||
$msg = filesize($filepath) < 300 ? file_get_contents($filepath) : '插件文件MD5校验失败';
|
||||
@unlink($filepath);
|
||||
throw new Exception($msg);
|
||||
}
|
||||
return true;
|
||||
}else{
|
||||
throw new Exception('下载插件文件失败,本地文件不存在');
|
||||
}
|
||||
}else{
|
||||
throw new Exception('下载插件文件失败:'.($result['msg']?$result['msg']:'未知错误'));
|
||||
}
|
||||
}else{
|
||||
throw new Exception('下载插件文件失败,接口返回错误');
|
||||
}
|
||||
}
|
||||
|
||||
//下载文件
|
||||
private static function download_file($btapi, $filename, $filepath){
|
||||
try{
|
||||
$btapi->download($filename, $filepath);
|
||||
}catch(Exception $e){
|
||||
@unlink($filepath);
|
||||
//宝塔bug小文件下载失败,改用base64下载
|
||||
$result = $btapi->get_file($filename);
|
||||
if($result && isset($result['status']) && $result['status']==true){
|
||||
$filedata = base64_decode($result['data']);
|
||||
if(strlen($filedata) < 4096 && substr($filedata,0,1)=='{' && substr($filedata,-1,1)=='}'){
|
||||
$arr = json_decode($filedata, true);
|
||||
if($arr){
|
||||
throw new Exception('获取文件失败:'.($arr['msg']?$arr['msg']:'未知错误'));
|
||||
}
|
||||
}
|
||||
if(!$filedata){
|
||||
throw new Exception('获取文件失败:文件内容为空');
|
||||
}
|
||||
file_put_contents($filepath, $filedata);
|
||||
}elseif($result){
|
||||
throw new Exception('获取文件失败:'.($result['msg']?$result['msg']:'未知错误'));
|
||||
}else{
|
||||
throw new Exception('获取文件失败:未知错误');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//刷新一键部署列表
|
||||
public static function refresh_deplist($os = 'Linux'){
|
||||
$btapi = self::get_btapi($os);
|
||||
$result = $btapi->get_deplist();
|
||||
if($result && isset($result['list']) && isset($result['type'])){
|
||||
if(empty($result['list']) || empty($result['type'])){
|
||||
throw new Exception('获取一键部署列表失败:一键部署列表为空');
|
||||
}
|
||||
$json_file = get_data_dir($os).'config/deployment_list.json';
|
||||
if(!file_put_contents($json_file, json_encode($result))){
|
||||
throw new Exception('保存一键部署列表失败,文件无写入权限');
|
||||
}
|
||||
}else{
|
||||
throw new Exception('获取一键部署列表失败:'.(isset($result['msg'])?$result['msg']:'面板连接失败'));
|
||||
}
|
||||
}
|
||||
|
||||
//获取一键部署列表
|
||||
public static function get_deplist($os = 'Linux'){
|
||||
$json_file = get_data_dir($os).'config/deployment_list.json';
|
||||
if(file_exists($json_file)){
|
||||
$data = file_get_contents($json_file);
|
||||
$json_arr = json_decode($data, true);
|
||||
if($json_arr){
|
||||
return $json_arr;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
12
app/middleware.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
// 全局中间件定义文件
|
||||
return [
|
||||
// 全局请求缓存
|
||||
// \think\middleware\CheckRequestCache::class,
|
||||
// 多语言加载
|
||||
// \think\middleware\LoadLangPack::class,
|
||||
// Session初始化
|
||||
// \think\middleware\SessionInit::class,
|
||||
\app\middleware\LoadConfig::class,
|
||||
\app\middleware\AuthAdmin::class
|
||||
];
|
26
app/middleware/AuthAdmin.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
|
||||
class AuthAdmin
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
$islogin = false;
|
||||
$cookie = cookie('admin_token');
|
||||
if($cookie){
|
||||
$token=authcode($cookie, 'DECODE', config_get('syskey'));
|
||||
if($token){
|
||||
list($user, $sid, $expiretime) = explode("\t", $token);
|
||||
$session=md5(config_get('admin_username').config_get('admin_password'));
|
||||
if($session==$sid && $expiretime>time()) {
|
||||
$islogin = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
request()->islogin = $islogin;
|
||||
return $next($request);
|
||||
}
|
||||
}
|
19
app/middleware/CheckAdmin.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
|
||||
class CheckAdmin
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
if (!request()->islogin) {
|
||||
if ($request->isAjax() || !$request->isGet()) {
|
||||
return json(['code'=>-1, 'msg'=>'未登录'])->code(401);
|
||||
}
|
||||
return redirect((string)url('/admin/login'));
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
39
app/middleware/LoadConfig.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use think\facade\Db;
|
||||
use think\facade\Config;
|
||||
|
||||
class LoadConfig
|
||||
{
|
||||
/**
|
||||
* 处理请求
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @param \Closure $next
|
||||
* @return Response
|
||||
*/
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
if (!file_exists(app()->getRootPath().'.env')){
|
||||
if(strpos(request()->url(),'/installapp')===false){
|
||||
return redirect((string)url('/installapp'))->header([
|
||||
'Cache-Control' => 'no-store, no-cache, must-revalidate',
|
||||
'Pragma' => 'no-cache',
|
||||
]);
|
||||
}else{
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
$res = Db::name('config')->cache('configs',0)->column('value','key');
|
||||
Config::set($res, 'sys');
|
||||
|
||||
return $next($request)->header([
|
||||
'Cache-Control' => 'no-store, no-cache, must-revalidate',
|
||||
'Pragma' => 'no-cache',
|
||||
]);
|
||||
}
|
||||
}
|
24
app/middleware/RefererCheck.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use think\facade\View;
|
||||
|
||||
class RefererCheck
|
||||
{
|
||||
/**
|
||||
* 处理请求
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @param \Closure $next
|
||||
* @return Response
|
||||
*/
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
if(!checkRefererHost()){
|
||||
return response('Access Denied', 403);
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
9
app/provider.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
use app\ExceptionHandle;
|
||||
use app\Request;
|
||||
|
||||
// 容器Provider定义文件
|
||||
return [
|
||||
'think\Request' => Request::class,
|
||||
'think\exception\Handle' => ExceptionHandle::class,
|
||||
];
|
80
app/script/convert.sh
Normal file
@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
|
||||
Linux_Version="8.0.1"
|
||||
Windows_Version="7.9.0"
|
||||
Btm_Version="2.2.5"
|
||||
|
||||
FILES=(
|
||||
public/install/src/panel6.zip
|
||||
public/install/update/LinuxPanel-${Linux_Version}.zip
|
||||
public/install/install_6.0.sh
|
||||
public/install/update_panel.sh
|
||||
public/install/update6.sh
|
||||
public/win/install/panel_update.py
|
||||
public/win/panel/panel_${Windows_Version}.zip
|
||||
public/win/panel/data/api.py
|
||||
public/win/panel/data/setup.py
|
||||
public/install/src/bt-monitor-${Btm_Version}.zip
|
||||
public/install/install_btmonitor.sh
|
||||
public/install/update_btmonitor.sh
|
||||
)
|
||||
|
||||
DIR=$1
|
||||
SITEURL=$2
|
||||
|
||||
if [ ! -d "$DIR" ]; then
|
||||
echo "网站目录不存在"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$SITEURL" = "" ]; then
|
||||
echo "网站URL不正确"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function handleFile()
|
||||
{
|
||||
Filename=$1
|
||||
if [ "${Filename##*.}" = "zip" ]; then
|
||||
handleZipFile $Filename
|
||||
else
|
||||
handleTextFile $Filename
|
||||
fi
|
||||
}
|
||||
|
||||
function handleZipFile()
|
||||
{
|
||||
Filename=$1
|
||||
mkdir -p /tmp/package
|
||||
unzip -o -q $Filename -d /tmp/package
|
||||
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" @
|
||||
Sprit_SITEURK=${SITEURL//\//\\\\\/}
|
||||
grep -rl --include=\*.sh 'http:\\\/\\\/www.example.com' /tmp/package | xargs -I @ sed -i "s|http:\\\/\\\/www.example.com|${Sprit_SITEURK}|g" @
|
||||
rm -f $Filename
|
||||
cd /tmp/package && zip -9 -q -r $Filename * && cd -
|
||||
rm -rf /tmp/package
|
||||
}
|
||||
|
||||
function handleTextFile()
|
||||
{
|
||||
sed -i "s|http://www.example.com|${SITEURL}|g" $1
|
||||
}
|
||||
|
||||
|
||||
echo "=========================="
|
||||
echo "正在处理中..."
|
||||
echo "=========================="
|
||||
|
||||
for File in ${FILES[@]}
|
||||
do
|
||||
Filename="${DIR}${File}"
|
||||
if [ -f "$Filename" ]; then
|
||||
handleFile $Filename
|
||||
echo -e "成功处理文件:\033[32m${Filename}\033[0m"
|
||||
else
|
||||
echo -e "文件不存在:\033[33m${Filename}\033[0m"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "=========================="
|
||||
echo "处理完成"
|
||||
echo "=========================="
|
9
app/service.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
use app\AppService;
|
||||
|
||||
// 系统服务定义文件
|
||||
// 服务在完成全局初始化之后执行
|
||||
return [
|
||||
AppService::class,
|
||||
];
|
49
app/view/admin/deplist.html
Normal file
@ -0,0 +1,49 @@
|
||||
{extend name="admin/layout" /}
|
||||
{block name="title"}一键部署列表{/block}
|
||||
{block name="main"}
|
||||
<div class="container" style="padding-top:70px;">
|
||||
<div class="col-sm-12 col-md-10 col-lg-8 center-block" style="float: none;">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading"><h3 class="panel-title">一键部署列表</h3></div>
|
||||
<div class="panel-body">
|
||||
<div class="list-group">
|
||||
<div class="list-group-item list-group-item-warning">Linux面板</div>
|
||||
<div class="list-group-item" style="line-height:35px">列表文件更新时间:<font color="blue">{$deplist_linux_time}</font><a href="javascript:refresh_deplist('Linux')" class="btn btn-success pull-right"><i class="fa fa-refresh"></i>重新获取</a></div>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item list-group-item-warning">Windows面板</div>
|
||||
<div class="list-group-item" style="line-height:35px">列表文件更新时间:<font color="blue">{$deplist_win_time}</font><a href="javascript:refresh_deplist('Windows')" class="btn btn-success pull-right"><i class="fa fa-refresh"></i>重新获取</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script>
|
||||
<script>
|
||||
function refresh_deplist(os){
|
||||
var confirm = layer.confirm('是否确定从宝塔官方获取最新一键部署列表?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
layer.close(confirm)
|
||||
var ii = layer.msg('正在获取一键部署列表,请稍候...', {icon: 16, shade:0.1, time: 0});
|
||||
$.ajax({
|
||||
type : 'GET',
|
||||
url : '/admin/refresh_deplist?os='+os,
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii)
|
||||
if(data.code == 0){
|
||||
layer.alert(data.msg, {icon:1}, function(){window.location.reload()});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon:2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii)
|
||||
layer.msg('服务器错误', {icon:2});
|
||||
}
|
||||
});
|
||||
}, function(){
|
||||
layer.close(confirm)
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{/block}
|
60
app/view/admin/index.html
Normal file
@ -0,0 +1,60 @@
|
||||
{extend name="admin/layout" /}
|
||||
{block name="title"}宝塔第三方云端管理中心{/block}
|
||||
{block name="main"}
|
||||
<style>
|
||||
.table>tbody>tr>td{white-space: normal;}
|
||||
.query-title {
|
||||
background-color:#f5fafe;
|
||||
word-break: keep-all;
|
||||
}
|
||||
.query-result{
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
<div class="container" style="padding-top:70px;">
|
||||
<div class="col-xs-12 col-sm-10 col-md-8 center-block" style="float: none;">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading"><h3 class="panel-title">后台管理首页</h3></div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item"><span class="glyphicon glyphicon-stats"></span> <b>宝塔插件统计:</b>共有 {$stat.total} 个,其中免费插件 {$stat.free} 个,专业版插件 {$stat.pro} 个,企业版插件 {$stat.ltd} 个,第三方插件 {$stat.third} 个</div>
|
||||
<div class="list-group-item"><span class="glyphicon glyphicon-tint"></span> <b>使用记录统计:</b>历史总共数量:{$stat.record_total},正在使用数量:{$stat.record_isuse}</div>
|
||||
<div class="list-group-item"><span class="glyphicon glyphicon-time"></span> <b>任务运行情况:</b>上次运行时间:{$stat.runtime|raw} <a href="/admin/set/mod/task" class="btn btn-xs btn-info">查看详情</a></div>
|
||||
<div class="list-group-item"><span class="glyphicon glyphicon-cog"></span> <b>常用功能入口:</b><a href="/admin/plugins" class="btn btn-xs btn-default">插件列表</a> <a href="/admin/record" class="btn btn-xs btn-default">使用记录</a> <a href="/admin/list" class="btn btn-xs btn-default">黑白名单</a> <a href="/download" class="btn btn-xs btn-default" target="_blank">安装脚本</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">服务器信息</h3>
|
||||
</div>
|
||||
<table class="table table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="query-title">框架版本</td>
|
||||
<td class="query-result">{$info.framework_version}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="query-title">PHP版本</td>
|
||||
<td class="query-result">{$info.php_version}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="query-title">MySQL版本</td>
|
||||
<td class="query-result">{$info.mysql_version}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="query-title">WEB软件</td>
|
||||
<td class="query-result">{$info.software}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="query-title">操作系统</td>
|
||||
<td class="query-result">{$info.os}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="query-title">服务器时间</td>
|
||||
<td class="query-result">{$info.date}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
73
app/view/admin/layout.html
Normal file
@ -0,0 +1,73 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<title>{block name="title"}标题{/block}</title>
|
||||
<link href="//cdn.staticfile.org/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="//cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
|
||||
<link href="/static/css/bootstrap-table.css" rel="stylesheet" />
|
||||
<script src="//cdn.staticfile.org/modernizr/2.8.3/modernizr.min.js"></script>
|
||||
<script src="//cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script>
|
||||
<script src="//cdn.staticfile.org/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdn.staticfile.org/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="//cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-fixed-top navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
|
||||
aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">导航按钮</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="./">宝塔第三方云端管理中心</a>
|
||||
</div><!-- /.navbar-header -->
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="{:checkIfActive('index')}">
|
||||
<a href="/admin"><i class="fa fa-home"></i> 后台首页</a>
|
||||
</li>
|
||||
<li class="{:checkIfActive('plugins,pluginswin,deplist')}">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-cubes"></i> 插件列表<b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="{:checkIfActive('plugins')}"><a href="/admin/plugins">Linux面板</a></li>
|
||||
<li class="{:checkIfActive('pluginswin')}"><a href="/admin/pluginswin">Windows面板</a></li>
|
||||
<li class="{:checkIfActive('deplist')}"><a href="/admin/deplist">一键部署列表</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="{:checkIfActive('record')}">
|
||||
<a href="/admin/record"><i class="fa fa-list"></i> 使用记录</a>
|
||||
</li>
|
||||
<li class="{:checkIfActive('list')}">
|
||||
<a href="/admin/list"><i class="fa fa-globe"></i> 黑白名单</a>
|
||||
</li>
|
||||
<li class="{:checkIfActive('log')}">
|
||||
<a href="/admin/log"><i class="fa fa-calendar"></i> 操作日志</a>
|
||||
</li>
|
||||
<li class="{:checkIfActive('set')}">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-cog"></i> 系统设置<b
|
||||
class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/admin/set">系统基本设置</a></li>
|
||||
<li><a href="/admin/set/mod/task">定时任务设置</a></li>
|
||||
<li><a href="/admin/set/mod/tools">批量替换工具</a></li>
|
||||
<li><a href="/admin/set/mod/account">管理账号设置</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/logout" onclick="return confirm('确定退出登录吗?')"><i class="fa fa-power-off"></i> 退出登录</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div><!-- /.container -->
|
||||
</nav><!-- /.navbar -->
|
||||
{block name="main"}主内容{/block}
|
||||
</body>
|
||||
</html>
|
209
app/view/admin/list.html
Normal file
@ -0,0 +1,209 @@
|
||||
{extend name="admin/layout" /}
|
||||
{block name="title"}黑白名单{/block}
|
||||
{block name="main"}
|
||||
<style>
|
||||
.alert{margin-bottom: 5px;}
|
||||
</style>
|
||||
<div class="container" style="padding-top:70px;">
|
||||
<div class="col-xs-12 col-md-10 center-block" style="float: none;">
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="{if $type=='black'}active{/if}"><a href="/admin/list">黑名单列表</a></li><li class="{if $type=='white'}active{/if}"><a href="/admin/list/type/white">白名单列表</a></li>
|
||||
</ul>
|
||||
|
||||
{if $type=='black' && config_get('whitelist')=='1'}
|
||||
<div class="alert alert-warning">提示:当前为白名单模式,黑名单列表里面的记录不会生效。</div>
|
||||
{/if}
|
||||
{if $type=='white' && config_get('whitelist')=='0'}
|
||||
<div class="alert alert-warning">提示:当前未开启白名单模式,白名单列表里面的记录不会生效。</div>
|
||||
{/if}
|
||||
{if $type=='black'}
|
||||
<div class="alert alert-info">添加到黑名单列表中的服务器IP将无法使用此云端</div>
|
||||
{/if}
|
||||
{if $type=='white'}
|
||||
<div class="alert alert-info">只有添加到白名单列表中的服务器IP才可以使用此云端</div>
|
||||
{/if}
|
||||
|
||||
<div id="searchToolbar">
|
||||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline">
|
||||
<div class="form-group">
|
||||
<label>搜索</label>
|
||||
<input type="text" class="form-control" name="ip" placeholder="服务器IP">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary" type="submit"><i class="fa fa-search"></i>搜索</button>
|
||||
<a href="javascript:searchClear()" class="btn btn-default"><i class="fa fa-repeat"></i>重置</a>
|
||||
<a href="javascript:add_item()" class="btn btn-success"><i class="fa fa-plus"></i>添加</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<table id="listTable">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script>
|
||||
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
|
||||
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
|
||||
<script src="/static/js/custom.js"></script>
|
||||
<script>
|
||||
function setEnable(id,enable) {
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/list_op/type/{$type}',
|
||||
data: { act:'enable', id:id, enable:enable},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
if(data.code == 0){
|
||||
searchSubmit();
|
||||
}else{
|
||||
layer.msg(data.msg, {icon:2, time:1500});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function add_item(){
|
||||
layer.open({
|
||||
area: ['360px'],
|
||||
title: '添加IP{$typename}',
|
||||
content: '<div class="form-group"><input type="text" class="form-control" name="item_input" placeholder="请输入服务器IP" value=""></div>',
|
||||
yes: function(){
|
||||
var ip = $("input[name='item_input']").val();
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/list_op/type/{$type}',
|
||||
data: { act:'add', ip:ip},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
if(data.code == 0){
|
||||
layer.msg('添加成功', {icon:1, time:800});
|
||||
searchSubmit();
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
},
|
||||
shadeClose: true
|
||||
});
|
||||
}
|
||||
|
||||
function edit_item(id){
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/list_op/type/{$type}',
|
||||
data: { act:'get', id:id},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
if(data.code == 0){
|
||||
layer.open({
|
||||
area: ['360px'],
|
||||
title: '编辑IP{$typename}',
|
||||
content: '<div class="form-group"><input type="text" class="form-control" name="item_input" placeholder="请输入服务器IP" value="'+data.data.ip+'"></div>',
|
||||
yes: function(){
|
||||
var ip = $("input[name='item_input']").val();
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/list_op/type/{$type}',
|
||||
data: { act:'edit', id:id, ip:ip},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
if(data.code == 0){
|
||||
layer.msg('修改成功', {icon:1, time:800});
|
||||
searchSubmit();
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
},
|
||||
shadeClose: true
|
||||
});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function del_item(id) {
|
||||
if(confirm('是否确定删除此记录?')){
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/list_op/type/{$type}',
|
||||
data: { act:'del', id:id},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
if(data.code == 0){
|
||||
layer.msg('删除成功!', {icon:1, time:800});
|
||||
searchSubmit();
|
||||
}else{
|
||||
layer.alert(data.msg, {icon:2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
updateToolbar();
|
||||
const defaultPageSize = 15;
|
||||
const pageNumber = typeof window.$_GET['pageNumber'] != 'undefined' ? parseInt(window.$_GET['pageNumber']) : 1;
|
||||
const pageSize = typeof window.$_GET['pageSize'] != 'undefined' ? parseInt(window.$_GET['pageSize']) : defaultPageSize;
|
||||
|
||||
$("#listTable").bootstrapTable({
|
||||
url: '/admin/list_data/type/{$type}',
|
||||
pageNumber: pageNumber,
|
||||
pageSize: pageSize,
|
||||
classes: 'table table-striped table-hover table-bottom-border',
|
||||
columns: [
|
||||
{
|
||||
field: 'id',
|
||||
title: 'ID',
|
||||
formatter: function(value, row, index) {
|
||||
return '<b>'+value+'</b>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'ip',
|
||||
title: '服务器IP'
|
||||
},
|
||||
{
|
||||
field: 'enable',
|
||||
title: '是否生效',
|
||||
formatter: function(value, row, index) {
|
||||
return value?'<a href="javascript:setEnable('+row.id+',0)"><font color=green><i class="fa fa-check-circle"></i>已生效</font></a>':'<a href="javascript:setEnable('+row.id+',1)"><font color=red><i class="fa fa-times-circle"></i>未生效</font></a>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'addtime',
|
||||
title: '添加时间'
|
||||
},
|
||||
{
|
||||
field: '',
|
||||
title: '操作',
|
||||
formatter: function(value, row, index) {
|
||||
return '<a href="javascript:edit_item('+row.id+')" class="btn btn-xs btn-info">编辑</a> <a href="javascript:del_item('+row.id+')" class="btn btn-xs btn-danger">删除</a>';
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{/block}
|
74
app/view/admin/log.html
Normal file
@ -0,0 +1,74 @@
|
||||
{extend name="admin/layout" /}
|
||||
{block name="title"}操作日志{/block}
|
||||
{block name="main"}
|
||||
<style>
|
||||
</style>
|
||||
<div class="container" style="padding-top:70px;">
|
||||
<div class="col-xs-12 col-md-10 center-block" style="float: none;">
|
||||
|
||||
<div id="searchToolbar">
|
||||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline">
|
||||
<div class="form-group">
|
||||
<label>搜索</label>
|
||||
<input type="text" class="form-control" name="action" placeholder="操作类型">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary" type="submit"><i class="fa fa-search"></i>搜索</button>
|
||||
<a href="javascript:searchClear()" class="btn btn-default"><i class="fa fa-repeat"></i>重置</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<table id="listTable">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script>
|
||||
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
|
||||
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
|
||||
<script src="/static/js/custom.js"></script>
|
||||
<script>
|
||||
|
||||
$(document).ready(function(){
|
||||
updateToolbar();
|
||||
const defaultPageSize = 20;
|
||||
const pageNumber = typeof window.$_GET['pageNumber'] != 'undefined' ? parseInt(window.$_GET['pageNumber']) : 1;
|
||||
const pageSize = typeof window.$_GET['pageSize'] != 'undefined' ? parseInt(window.$_GET['pageSize']) : defaultPageSize;
|
||||
|
||||
$("#listTable").bootstrapTable({
|
||||
url: '/admin/log_data',
|
||||
pageNumber: pageNumber,
|
||||
pageSize: pageSize,
|
||||
classes: 'table table-striped table-hover table-bottom-border',
|
||||
columns: [
|
||||
{
|
||||
field: 'id',
|
||||
title: 'ID',
|
||||
formatter: function(value, row, index) {
|
||||
return '<b>'+value+'</b>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'uid',
|
||||
title: '操作人',
|
||||
formatter: function(value, row, index) {
|
||||
return value==1?'<font color="green">定时任务</font>':'<font color="blue">管理员</font>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
title: '操作类型'
|
||||
},
|
||||
{
|
||||
field: 'data',
|
||||
title: '操作详情',
|
||||
},
|
||||
{
|
||||
field: 'addtime',
|
||||
title: '操作时间'
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{/block}
|
100
app/view/admin/login.html
Normal file
@ -0,0 +1,100 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-cn">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>管理员登录</title>
|
||||
<link href="//cdn.staticfile.org/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"/>
|
||||
<script src="//cdn.staticfile.org/modernizr/2.8.3/modernizr.min.js"></script>
|
||||
<script src="//cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdn.staticfile.org/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="//cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-fixed-top navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">导航按钮</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="./">宝塔第三方云端管理中心</a>
|
||||
</div><!-- /.navbar-header -->
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="active">
|
||||
<a href="#"><span class="glyphicon glyphicon-user"></span> 登录</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div><!-- /.navbar-collapse -->
|
||||
</div><!-- /.container -->
|
||||
</nav><!-- /.navbar -->
|
||||
<div class="container" style="padding-top:70px;">
|
||||
<div class="col-xs-12 col-sm-10 col-md-8 col-lg-6 center-block" style="float: none;">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading"><h3 class="panel-title">管理员登录</h3></div>
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" role="form" onsubmit="return submitlogin()">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
|
||||
<input type="text" name="user" value="" class="form-control input-lg" placeholder="用户名" required="required"/>
|
||||
</div><br/>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>
|
||||
<input type="password" name="pass" class="form-control input-lg" placeholder="密码" required="required"/>
|
||||
</div><br/>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-adjust"></span></span>
|
||||
<input type="text" class="form-control input-lg" name="code" placeholder="输入验证码" autocomplete="off" required>
|
||||
<span class="input-group-addon" style="padding: 0">
|
||||
<img src="/admin/verifycode" height="45" id="verifycode" onclick="this.src='/admin/verifycode?r='+Math.random();" title="点击更换验证码">
|
||||
</span>
|
||||
</div><br/>
|
||||
<div class="form-group">
|
||||
<div class="col-xs-12"><input type="submit" value="立即登录" class="btn btn-primary btn-block btn-lg"/></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script>
|
||||
<script>
|
||||
function submitlogin(){
|
||||
var user = $("input[name='user']").val();
|
||||
var pass = $("input[name='pass']").val();
|
||||
var code = $("input[name='code']").val();
|
||||
if(user=='' || pass==''){layer.alert('用户名或密码不能为空!');return false;}
|
||||
var ii = layer.load(2);
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '{:request()->url()}',
|
||||
data: {username:user, password:pass, code:code},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.msg('登录成功,正在跳转', {icon: 1,shade: 0.01,time: 15000});
|
||||
window.location.href='/admin';
|
||||
}else{
|
||||
if(data.msg.indexOf('验证码')==-1){
|
||||
$("#verifycode").attr('src', '/admin/verifycode?r='+Math.random())
|
||||
}
|
||||
layer.alert(data.msg, {icon: 2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii);
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
214
app/view/admin/plugins.html
Normal file
@ -0,0 +1,214 @@
|
||||
{extend name="admin/layout" /}
|
||||
{block name="title"}插件列表{/block}
|
||||
{block name="main"}
|
||||
<style>
|
||||
td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:340px;}
|
||||
.bt-ico-ask {
|
||||
border: 1px solid #fb7d00;
|
||||
border-radius: 8px;
|
||||
color: #fb7d00;
|
||||
cursor: help;
|
||||
display: inline-block;
|
||||
font-family: arial;
|
||||
font-size: 11px;
|
||||
font-style: normal;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
margin-left: 5px;
|
||||
text-align: center;
|
||||
width: 16px
|
||||
}
|
||||
.bt-ico-ask:hover {
|
||||
background-color: #fb7d00;
|
||||
color: #fff
|
||||
}
|
||||
</style>
|
||||
<div class="modal fade" id="help" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">帮助</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>“版本与状态”一列中,红色的按钮代表本地不存在该版本插件包,需要点击下载;绿色的按钮代表已存在。</p>
|
||||
<p>官方插件包本地存储路径是/data/plugins/package/软件标识-版本号.zip,第三方插件包路径是/data/plugins/other/other/,对于部分包含二次验证的插件可以自行修改。</p>
|
||||
<p>点击【重新获取】按钮会从宝塔官方获取最新插件列表,但是插件包需要手动点击下载。如果需要批量下载插件包,可查看<a href="/admin/set/mod/task">定时任务设置</a></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container" style="padding-top:70px;">
|
||||
<div class="col-xs-12 center-block" style="float: none;">
|
||||
|
||||
<div id="searchToolbar">
|
||||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline">
|
||||
<div class="form-group">
|
||||
<label>搜索</label>
|
||||
<input type="text" class="form-control" name="keyword" placeholder="应用名称">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select name="type" class="form-control"><option value="0">全部插件</option>
|
||||
{foreach $typelist as $k=>$v}<option value="{$k}">{$v}</option>{/foreach} </select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary" type="submit"><i class="fa fa-search"></i>搜索</button>
|
||||
<a href="javascript:searchClear()" class="btn btn-default"><i class="fa fa-repeat"></i>重置</a>
|
||||
<a href="javascript:refresh_plugins()" class="btn btn-success"><i class="fa fa-refresh"></i>重新获取</a>
|
||||
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#help"><i class="fa fa-info-circle"></i>帮助</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<table id="listTable">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script>
|
||||
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
|
||||
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
|
||||
<script src="/static/js/custom.js"></script>
|
||||
<script>
|
||||
|
||||
function download_version(name, version, status){
|
||||
if(status == true){
|
||||
var confirm = layer.confirm('是否确定重新下载'+version+'版本插件包?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
download_plugin(name, version)
|
||||
}, function(){
|
||||
layer.close(confirm)
|
||||
});
|
||||
}else{
|
||||
download_plugin(name, version)
|
||||
}
|
||||
}
|
||||
|
||||
function download_plugin(name, version){
|
||||
var ii = layer.msg('正在下载,请稍候...', {icon: 16, shade:0.1, time: 0});
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/download_plugin',
|
||||
data: { name:name, version:version},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii)
|
||||
if(data.code == 0){
|
||||
layer.alert(data.msg, {icon:1}, function(){layer.closeAll();searchSubmit();});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon:2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii)
|
||||
layer.msg('服务器错误', {icon:2});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function refresh_plugins(){
|
||||
var confirm = layer.confirm('是否确定从宝塔官方获取最新插件列表?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
layer.close(confirm)
|
||||
var ii = layer.msg('正在获取插件列表,请稍候...', {icon: 16, shade:0.1, time: 0});
|
||||
$.ajax({
|
||||
type : 'GET',
|
||||
url : '/admin/refresh_plugins',
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii)
|
||||
if(data.code == 0){
|
||||
layer.alert(data.msg, {icon:1}, function(){layer.closeAll();searchSubmit();});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon:2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii)
|
||||
layer.msg('服务器错误', {icon:2});
|
||||
}
|
||||
});
|
||||
}, function(){
|
||||
layer.close(confirm)
|
||||
});
|
||||
}
|
||||
|
||||
function searchByType(type){
|
||||
$("input[name=keyword]").val('');
|
||||
$("select[name=type]").val(type);
|
||||
searchSubmit()
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
updateToolbar();
|
||||
const defaultPageSize = 20;
|
||||
|
||||
$("#listTable").bootstrapTable({
|
||||
url: '/admin/plugins_data',
|
||||
pageNumber: 1,
|
||||
pageSize: 15,
|
||||
sidePagination: 'client',
|
||||
classes: 'table table-striped table-hover table-bottom-border',
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '软件标识',
|
||||
formatter: function(value, row, index) {
|
||||
return '<b>'+value+'</b>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
title: '软件名称'
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
title: '软件分类',
|
||||
formatter: function(value, row, index) {
|
||||
return '<a href="javascript:searchByType('+value+')" title="查看该分类下的插件">'+row.typename+'</a>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'desc',
|
||||
title: '说明',
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '价格',
|
||||
formatter: function(value, row, index) {
|
||||
return value > 0 ? '<span style="color:#fc6d26">¥'+value+'</span>' : '免费';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
title: '开发商'
|
||||
},
|
||||
{
|
||||
field: 'versions',
|
||||
title: '版本与状态',
|
||||
formatter: function(value, row, index) {
|
||||
var html = '';
|
||||
if(row.type == 5){
|
||||
html += '<a href="javascript:" class="btn btn-xs btn-success" disabled>无需下载</a>';
|
||||
}else{
|
||||
$.each(value, function(index,item){
|
||||
if(item.status)
|
||||
html += '<a href="javascript:download_version(\''+row.name+'\',\''+item.version+'\','+item.status+')" class="btn btn-xs btn-success">'+item.version+'</a> ';
|
||||
else
|
||||
html += '<a href="javascript:download_version(\''+row.name+'\',\''+item.version+'\','+item.status+')" class="btn btn-xs btn-danger">'+item.version+'</a> ';
|
||||
})
|
||||
}
|
||||
return html
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{/block}
|
214
app/view/admin/pluginswin.html
Normal file
@ -0,0 +1,214 @@
|
||||
{extend name="admin/layout" /}
|
||||
{block name="title"}插件列表{/block}
|
||||
{block name="main"}
|
||||
<style>
|
||||
td{overflow: hidden;text-overflow: ellipsis;white-space: nowrap;max-width:340px;}
|
||||
.bt-ico-ask {
|
||||
border: 1px solid #fb7d00;
|
||||
border-radius: 8px;
|
||||
color: #fb7d00;
|
||||
cursor: help;
|
||||
display: inline-block;
|
||||
font-family: arial;
|
||||
font-size: 11px;
|
||||
font-style: normal;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
margin-left: 5px;
|
||||
text-align: center;
|
||||
width: 16px
|
||||
}
|
||||
.bt-ico-ask:hover {
|
||||
background-color: #fb7d00;
|
||||
color: #fff
|
||||
}
|
||||
</style>
|
||||
<div class="modal fade" id="help" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">帮助</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>“版本与状态”一列中,红色的按钮代表本地不存在该版本插件包,需要点击下载;绿色的按钮代表已存在。</p>
|
||||
<p>官方插件包本地存储路径是/data/win/plugins/package/软件标识-版本号.zip,第三方插件包路径是/data/plugins/other/other/,对于部分包含二次验证的插件可以自行修改。</p>
|
||||
<p>点击【重新获取】按钮会从宝塔官方获取最新插件列表,但是插件包需要手动点击下载。如果需要批量下载插件包,可查看<a href="/admin/set/mod/task">定时任务设置</a></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container" style="padding-top:70px;">
|
||||
<div class="col-xs-12 center-block" style="float: none;">
|
||||
|
||||
<div id="searchToolbar">
|
||||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline">
|
||||
<div class="form-group">
|
||||
<label>搜索</label>
|
||||
<input type="text" class="form-control" name="keyword" placeholder="应用名称">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select name="type" class="form-control"><option value="0">全部插件</option>
|
||||
{foreach $typelist as $k=>$v}<option value="{$k}">{$v}</option>{/foreach} </select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary" type="submit"><i class="fa fa-search"></i>搜索</button>
|
||||
<a href="javascript:searchClear()" class="btn btn-default"><i class="fa fa-repeat"></i>重置</a>
|
||||
<a href="javascript:refresh_plugins()" class="btn btn-success"><i class="fa fa-refresh"></i>重新获取</a>
|
||||
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#help"><i class="fa fa-info-circle"></i>帮助</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<table id="listTable">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script>
|
||||
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
|
||||
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
|
||||
<script src="/static/js/custom.js"></script>
|
||||
<script>
|
||||
|
||||
function download_version(name, version, status){
|
||||
if(status == true){
|
||||
var confirm = layer.confirm('是否确定重新下载'+version+'版本插件包?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
download_plugin(name, version)
|
||||
}, function(){
|
||||
layer.close(confirm)
|
||||
});
|
||||
}else{
|
||||
download_plugin(name, version)
|
||||
}
|
||||
}
|
||||
|
||||
function download_plugin(name, version){
|
||||
var ii = layer.msg('正在下载,请稍候...', {icon: 16, shade:0.1, time: 0});
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/download_plugin',
|
||||
data: { name:name, version:version, os:'Windows'},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii)
|
||||
if(data.code == 0){
|
||||
layer.alert(data.msg, {icon:1}, function(){layer.closeAll();searchSubmit();});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon:2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii)
|
||||
layer.msg('服务器错误', {icon:2});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function refresh_plugins(){
|
||||
var confirm = layer.confirm('是否确定从宝塔官方获取最新插件列表?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
layer.close(confirm)
|
||||
var ii = layer.msg('正在获取插件列表,请稍候...', {icon: 16, shade:0.1, time: 0});
|
||||
$.ajax({
|
||||
type : 'GET',
|
||||
url : '/admin/refresh_plugins?os=Windows',
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii)
|
||||
if(data.code == 0){
|
||||
layer.alert(data.msg, {icon:1}, function(){layer.closeAll();searchSubmit();});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon:2});
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii)
|
||||
layer.msg('服务器错误', {icon:2});
|
||||
}
|
||||
});
|
||||
}, function(){
|
||||
layer.close(confirm)
|
||||
});
|
||||
}
|
||||
|
||||
function searchByType(type){
|
||||
$("input[name=keyword]").val('');
|
||||
$("select[name=type]").val(type);
|
||||
searchSubmit()
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
updateToolbar();
|
||||
const defaultPageSize = 20;
|
||||
|
||||
$("#listTable").bootstrapTable({
|
||||
url: '/admin/plugins_data?os=Windows',
|
||||
pageNumber: 1,
|
||||
pageSize: 15,
|
||||
sidePagination: 'client',
|
||||
classes: 'table table-striped table-hover table-bottom-border',
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '软件标识',
|
||||
formatter: function(value, row, index) {
|
||||
return '<b>'+value+'</b>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
title: '软件名称'
|
||||
},
|
||||
{
|
||||
field: 'type',
|
||||
title: '软件分类',
|
||||
formatter: function(value, row, index) {
|
||||
return '<a href="javascript:searchByType('+value+')" title="查看该分类下的插件">'+row.typename+'</a>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'desc',
|
||||
title: '说明',
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '价格',
|
||||
formatter: function(value, row, index) {
|
||||
return value > 0 ? '<span style="color:#fc6d26">¥'+value+'</span>' : '免费';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
title: '开发商'
|
||||
},
|
||||
{
|
||||
field: 'versions',
|
||||
title: '版本与状态',
|
||||
formatter: function(value, row, index) {
|
||||
var html = '';
|
||||
if(row.type == 5){
|
||||
html += '<a href="javascript:" class="btn btn-xs btn-success" disabled>无需下载</a>';
|
||||
}else{
|
||||
$.each(value, function(index,item){
|
||||
if(item.status)
|
||||
html += '<a href="javascript:download_version(\''+row.name+'\',\''+item.version+'\','+item.status+')" class="btn btn-xs btn-success">'+item.version+'</a> ';
|
||||
else
|
||||
html += '<a href="javascript:download_version(\''+row.name+'\',\''+item.version+'\','+item.status+')" class="btn btn-xs btn-danger">'+item.version+'</a> ';
|
||||
})
|
||||
}
|
||||
return html
|
||||
}
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{/block}
|
67
app/view/admin/record.html
Normal file
@ -0,0 +1,67 @@
|
||||
{extend name="admin/layout" /}
|
||||
{block name="title"}使用记录{/block}
|
||||
{block name="main"}
|
||||
<style>
|
||||
</style>
|
||||
<div class="container" style="padding-top:70px;">
|
||||
<div class="col-xs-12 col-md-10 center-block" style="float: none;">
|
||||
|
||||
<div id="searchToolbar">
|
||||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline">
|
||||
<div class="form-group">
|
||||
<label>搜索</label>
|
||||
<input type="text" class="form-control" name="ip" placeholder="服务器IP">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button class="btn btn-primary" type="submit"><i class="fa fa-search"></i>搜索</button>
|
||||
<a href="javascript:searchClear()" class="btn btn-default"><i class="fa fa-repeat"></i>重置</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<table id="listTable">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script>
|
||||
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/bootstrap-table.min.js"></script>
|
||||
<script src="//cdn.staticfile.org/bootstrap-table/1.20.2/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js"></script>
|
||||
<script src="/static/js/custom.js"></script>
|
||||
<script>
|
||||
|
||||
$(document).ready(function(){
|
||||
updateToolbar();
|
||||
const defaultPageSize = 15;
|
||||
const pageNumber = typeof window.$_GET['pageNumber'] != 'undefined' ? parseInt(window.$_GET['pageNumber']) : 1;
|
||||
const pageSize = typeof window.$_GET['pageSize'] != 'undefined' ? parseInt(window.$_GET['pageSize']) : defaultPageSize;
|
||||
|
||||
$("#listTable").bootstrapTable({
|
||||
url: '/admin/record_data',
|
||||
pageNumber: pageNumber,
|
||||
pageSize: pageSize,
|
||||
classes: 'table table-striped table-hover table-bottom-border',
|
||||
columns: [
|
||||
{
|
||||
field: 'id',
|
||||
title: 'ID',
|
||||
formatter: function(value, row, index) {
|
||||
return '<b>'+value+'</b>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'ip',
|
||||
title: '服务器IP'
|
||||
},
|
||||
{
|
||||
field: 'addtime',
|
||||
title: '首次安装时间',
|
||||
},
|
||||
{
|
||||
field: 'usetime',
|
||||
title: '最后使用时间'
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
</script>
|
||||
{/block}
|
335
app/view/admin/set.html
Normal file
@ -0,0 +1,335 @@
|
||||
{extend name="admin/layout" /}
|
||||
{block name="title"}系统设置{/block}
|
||||
{block name="main"}
|
||||
<div class="container" style="padding-top:70px;">
|
||||
{if $mod=='sys'}
|
||||
<div class="col-sm-12 col-md-6 center-block">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading"><h3 class="panel-title">系统基本设置</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
|
||||
<div class="form-group">
|
||||
<label>是否开启白名单模式:</label><br/>
|
||||
<select class="form-control" name="whitelist" default="{:config_get('whitelist')}"><option value="0">关闭</option><option value="1">开启</option></select>
|
||||
<font color="green">开启白名单模式后,只有在<a href="/admin/list/type/white" target="_blank">白名单列表</a>中的服务器IP才能使用此云端</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>安装脚本展示页面开关:</label>
|
||||
<select class="form-control" name="download_page" default="{:config_get('download_page')}"><option value="0">关闭</option><option value="1">开启</option></select>
|
||||
<font color="green">页面地址:<a href="/download" target="_blank">/download</a>,开启后可以公开访问,否则只能管理员访问</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔Linux面板最新版本号:</label>
|
||||
<input type="text" name="new_version" value="{:config_get('new_version')}" class="form-control"/>
|
||||
<font color="green">用于一键更新脚本获取最新版本号,以及检测更新接口。并确保已在/public/install/update/放置对应版本更新包</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔Linux面板更新日志:</label>
|
||||
<textarea class="form-control" name="update_msg" rows="5" placeholder="支持HTML代码">{:config_get('update_msg')}</textarea>
|
||||
<font color="green">用于检测更新接口返回</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔Linux面板更新日期:</label>
|
||||
<input type="date" name="update_date" value="{:config_get('update_date')}" class="form-control"/>
|
||||
<font color="green">用于检测更新接口返回</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔Windows面板最新版本号:</label>
|
||||
<input type="text" name="new_version_win" value="{:config_get('new_version_win')}" class="form-control"/>
|
||||
<font color="green">用于一键更新脚本获取最新版本号,以及检测更新接口。并确保已在/public/win/panel/放置对应版本更新包</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔Windows面板更新日志:</label>
|
||||
<textarea class="form-control" name="update_msg_win" rows="5" placeholder="支持HTML代码">{:config_get('update_msg_win')}</textarea>
|
||||
<font color="green">用于检测更新接口返回</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔Windows面板更新日期:</label>
|
||||
<input type="date" name="update_date_win" value="{:config_get('update_date_win')}" class="form-control"/>
|
||||
<font color="green">用于检测更新接口返回</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔云监控最新版本号:</label>
|
||||
<input type="text" name="new_version_btm" value="{:config_get('new_version_btm')}" class="form-control"/>
|
||||
<font color="green">用于一键更新脚本获取最新版本号,以及检测更新接口。并确保已在/public/install/src/放置对应版本更新包</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔云监控更新日志:</label>
|
||||
<textarea class="form-control" name="update_msg_btm" rows="3" placeholder="支持HTML代码">{:config_get('update_msg_btm')}</textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔云监控更新日期:</label>
|
||||
<input type="date" name="update_date_btm" value="{:config_get('update_date_btm')}" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 center-block">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading"><h3 class="panel-title">宝塔Linux面板接口设置</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
|
||||
<p>以下宝塔面板请使用官方最新脚本安装并绑定账号,用于获取最新插件列表及插件包</p>
|
||||
<p><a href="/static/file/kaixin.zip">下载专用插件(Linux)</a>,在面板【软件商店】->【第三方应用】,点击【导入插件】,导入该专用插件。</p>
|
||||
<div class="form-group">
|
||||
<label>宝塔面板URL:</label><br/>
|
||||
<input type="text" name="bt_url" value="{:config_get('bt_url')}" class="form-control"/>
|
||||
<font color="green">填写规则如:<u>http://192.168.1.1:8888</u> ,不要带其他后缀</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔面板接口密钥:</label>
|
||||
<input type="text" name="bt_key" value="{:config_get('bt_key')}" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<button type="button" class="btn btn-info btn-block" id="testbturl">测试连接</button>
|
||||
<input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading"><h3 class="panel-title">宝塔Windows面板接口设置</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
|
||||
<p>以下宝塔面板请使用官方最新脚本安装并绑定账号,用于获取最新插件列表及插件包</p>
|
||||
<p><a href="/static/file/win/kaixin.zip">下载专用插件(Win)</a>,在面板【软件商店】->【第三方应用】,点击【导入插件】,导入该专用插件。</p>
|
||||
<div class="form-group">
|
||||
<label>宝塔面板URL:</label><br/>
|
||||
<input type="text" name="wbt_url" value="{:config_get('wbt_url')}" class="form-control"/>
|
||||
<font color="green">填写规则如:<u>http://192.168.1.1:8888</u> ,不要带其他后缀</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>宝塔面板接口密钥:</label>
|
||||
<input type="text" name="wbt_key" value="{:config_get('wbt_key')}" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<button type="button" class="btn btn-info btn-block" id="testbturl2">测试连接</button>
|
||||
<input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{elseif $mod=='task'}
|
||||
<div class="col-sm-12 col-md-6 center-block">
|
||||
<div class="panel panel-success">
|
||||
<div class="panel-heading"><h3 class="panel-title">定时任务说明</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
|
||||
<div class="alert alert-info">使用以下命令可以从宝塔官方获取最新的插件列表并批量下载插件包(增量更新)。<br/>你也可以将此命令添加到crontab以使此云端的插件保持最新,建议1天执行1次。</div>
|
||||
<div class="alert alert-danger">使用命令执行之后,可能会导致 /data 目录下文件权限不对,后台插件列表下载插件覆盖会报错,需要手动循环设置 /data 目录权限。</div>
|
||||
<div class="alert alert-warning">上次运行时间:{$runtime|raw}</div>
|
||||
<div class="list-group-item">php {:app()->getRootPath()}think updateall</div><br/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 center-block">
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading"><h3 class="panel-title">定时任务设置</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return saveSetting(this)" method="post" class="form" role="form">
|
||||
<div class="form-group">
|
||||
<label>Linux面板批量下载插件范围:</label><br/>
|
||||
<select class="form-control" name="updateall_type" default="{:config_get('updateall_type')}"><option value="0">仅免费插件</option><option value="1">免费插件+专业版插件</option><option value="2">免费插件+专业版插件+企业版插件</option></select><font color="green">(批量下载不包含所有第三方插件,第三方插件需要去手动下载。)</font>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Windows面板批量下载插件范围:</label><br/>
|
||||
<select class="form-control" name="updateall_type_win" default="{:config_get('updateall_type_win')}"><option value="0">仅免费插件</option><option value="1">免费插件+专业版插件</option><option value="2">免费插件+专业版插件+企业版插件</option></select><font color="green">(批量下载不包含所有第三方插件,第三方插件需要去手动下载。)</font>
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{elseif $mod=='account'}
|
||||
<div class="col-xs-12 col-sm-8 col-lg-6 center-block" style="float: none;">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading"><h3 class="panel-title">管理账号设置</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return saveAccount(this)" method="post" class="form" role="form">
|
||||
<div class="form-group">
|
||||
<label>用户名:</label><br/>
|
||||
<input type="text" name="username" value="{:config_get('admin_username')}" class="form-control" required/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>旧密码:</label>
|
||||
<input type="password" name="oldpwd" value="" class="form-control" placeholder="请输入当前的管理员密码"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>新密码:</label>
|
||||
<input type="password" name="newpwd" value="" class="form-control" placeholder="不修改请留空"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>重输密码:</label>
|
||||
<input type="password" name="newpwd2" value="" class="form-control" placeholder="不修改请留空"/>
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<input type="submit" name="submit" value="保存" class="btn btn-success btn-block"/>
|
||||
</div>
|
||||
<a href="javascript:cleancache()" class="btn btn-default btn-sm btn-block">清理缓存</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{elseif $mod=='tools'}
|
||||
<div class="col-sm-12 col-md-10 col-lg-8 center-block" style="float: none;">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading"><h3 class="panel-title">批量替换工具</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return saveAccount(this)" method="post" class="form" role="form">
|
||||
<div class="alert alert-info" style="word-break:break-all;">使用以下命令可以将bt安装包、更新包和脚本文件里面的<code>http://www.example.com</code>批量替换成当前网址<code>{:request()->root(true)}</code>,每次更新版本后只需要执行一次即可。</div>
|
||||
<div class="list-group-item" style="word-break:break-all;">cd {:app()->getRootPath()}app/script && chmod +x convert.sh && ./convert.sh {:app()->getRootPath()} {:request()->root(true)}</div><br/>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js"></script>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
var items = $("select[default]");
|
||||
for (i = 0; i < items.length; i++) {
|
||||
$(items[i]).val($(items[i]).attr("default")||0);
|
||||
}
|
||||
$("#testbturl").click(function(){
|
||||
var bt_url = $("input[name=bt_url]").val();
|
||||
var bt_key = $("input[name=bt_key]").val();
|
||||
if(bt_url == ''){
|
||||
layer.alert('宝塔面板URL不能为空');return;
|
||||
}
|
||||
if(bt_url.indexOf('http://')==-1 && bt_url.indexOf('https://')==-1){
|
||||
layer.alert('宝塔面板URL不正确');return;
|
||||
}
|
||||
if(bt_key == ''){
|
||||
layer.alert('宝塔面板接口密钥不能为空');return;
|
||||
}
|
||||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/testbturl',
|
||||
data : {bt_url:bt_url, bt_key:bt_key},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.msg(data.msg, {icon: 1, time:1000})
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2})
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii);
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
})
|
||||
$("#testbturl2").click(function(){
|
||||
var wbt_url = $("input[name=wbt_url]").val();
|
||||
var wbt_key = $("input[name=wbt_key]").val();
|
||||
if(wbt_url == ''){
|
||||
layer.alert('宝塔面板URL不能为空');return;
|
||||
}
|
||||
if(wbt_url.indexOf('http://')==-1 && wbt_url.indexOf('https://')==-1){
|
||||
layer.alert('宝塔面板URL不正确');return;
|
||||
}
|
||||
if(wbt_key == ''){
|
||||
layer.alert('宝塔面板接口密钥不能为空');return;
|
||||
}
|
||||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/testbturl',
|
||||
data : {bt_url:wbt_url, bt_key:wbt_key},
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.msg(data.msg, {icon: 1, time:1000})
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2})
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii);
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
function saveSetting(obj){
|
||||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/set',
|
||||
data : $(obj).serialize(),
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.alert('设置保存成功!', {
|
||||
icon: 1,
|
||||
closeBtn: false
|
||||
}, function(){
|
||||
window.location.reload()
|
||||
});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2})
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii);
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
function saveAccount(obj){
|
||||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : '/admin/setaccount',
|
||||
data : $(obj).serialize(),
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.alert('管理账号保存成功!请重新登录。', {
|
||||
icon: 1,
|
||||
closeBtn: false
|
||||
}, function(){
|
||||
window.location.reload()
|
||||
});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2})
|
||||
}
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii);
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
function cleancache(){
|
||||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||||
$.ajax({
|
||||
type : 'GET',
|
||||
url : '/admin/cleancache',
|
||||
dataType : 'json',
|
||||
success : function(data) {
|
||||
layer.close(ii);
|
||||
layer.msg('清理缓存成功', {icon: 1});
|
||||
},
|
||||
error:function(data){
|
||||
layer.close(ii);
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{/block}
|
60
app/view/dispatch_jump.html
Normal file
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>温馨提示</title>
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
|
||||
<meta name="renderer" content="webkit"/>
|
||||
<style type="text/css">
|
||||
*{box-sizing:border-box;margin:0;padding:0;font-family:Lantinghei SC,Open Sans,Arial,Hiragino Sans GB,Microsoft YaHei,"微软雅黑",STHeiti,WenQuanYi Micro Hei,SimSun,sans-serif;-webkit-font-smoothing:antialiased}
|
||||
body{padding:70px 0;background:#edf1f4;font-weight:400;font-size:1pc;-webkit-text-size-adjust:none;color:#333}
|
||||
a{outline:0;color:#3498db;text-decoration:none;cursor:pointer}
|
||||
.system-message{margin:20px 5%;padding:40px 20px;background:#fff;box-shadow:1px 1px 1px hsla(0,0%,39%,.1);text-align:center}
|
||||
.system-message h1{margin:0;margin-bottom:9pt;color:#444;font-weight:400;font-size:40px}
|
||||
.system-message .jump,.system-message .image{margin:20px 0;padding:0;padding:10px 0;font-weight:400}
|
||||
.system-message .jump{font-size:14px}
|
||||
.system-message .jump a{color:#333}
|
||||
.system-message p{font-size:9pt;line-height:20px}
|
||||
.system-message .btn{display:inline-block;margin-right:10px;width:138px;height:2pc;border:1px solid #44a0e8;border-radius:30px;color:#44a0e8;text-align:center;font-size:1pc;line-height:2pc;margin-bottom:5px;}
|
||||
.success .btn{border-color:#69bf4e;color:#69bf4e}
|
||||
.error .btn{border-color:#ff8992;color:#ff8992}
|
||||
.info .btn{border-color:#3498db;color:#3498db}
|
||||
.copyright p{width:100%;color:#919191;text-align:center;font-size:10px}
|
||||
.system-message .btn-grey{border-color:#bbb;color:#bbb}
|
||||
.clearfix:after{clear:both;display:block;visibility:hidden;height:0;content:"."}
|
||||
@media (max-width:768px){body {padding:20px 0;}}
|
||||
@media (max-width:480px){.system-message h1{font-size:30px;}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="system-message {$code}">
|
||||
<div class="image">
|
||||
<img src="/static/images/{$code}.svg" alt="" width="150" />
|
||||
</div>
|
||||
<h1>{$msg}</h1>
|
||||
{if $url}
|
||||
<p class="jump">
|
||||
页面将在 <span id="wait">{$wait}</span> 秒后自动跳转
|
||||
</p>
|
||||
{/if}
|
||||
<p class="clearfix">
|
||||
<a href="javascript:history.go(-1);" class="btn btn-grey">返回上一页</a>
|
||||
{if $url}
|
||||
<a href="{$url}" class="btn btn-primary">立即跳转</a>
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
(function () {
|
||||
var wait = document.getElementById('wait');
|
||||
var interval = setInterval(function () {
|
||||
var time = --wait.innerHTML;
|
||||
if (time <= 0) {
|
||||
location.href = "{$url}";
|
||||
clearInterval(interval);
|
||||
}
|
||||
}, 1000);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
261
app/view/index/download.html
Normal file
@ -0,0 +1,261 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="force-rendering" content="webkit" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<title>宝塔面板安装脚本</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/sanren.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/download.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="down-main">
|
||||
<div class="d1">
|
||||
<div class="wrap">
|
||||
<div class="i1t textcenter">
|
||||
<h1 class="disflex flex_center flex_lrcenter textcenter">宝塔面板安装脚本<img class="ml10" src="/static/images/i1ico_03.png"></h1>
|
||||
<div class="text20 mt_25 wap_mt15 textcenter cl8">
|
||||
<p>2分钟装好面板,一键管理服务器</p>
|
||||
<p>集成LAMP/LNMP环境安装,网站、FTP、数据库、文件管理、软件安装等功能</p>
|
||||
</div>
|
||||
<div class="disflex flex_lrcenter mt_50 install-list">
|
||||
<div class="install-box linux">
|
||||
<div class="img">
|
||||
<img src="/static/images/prd_1_03.png">
|
||||
</div>
|
||||
<div class="cont">
|
||||
<div class="top">
|
||||
<div class="title">Linux面板 {:config_get('new_version')}</div>
|
||||
<div class="desc">
|
||||
支持Centos、Ubuntu、Deepin、Debian、Fedora等Linux系统。
|
||||
<a class="link" href="https://demo.bt.cn/login" target="_blank" style="margin-left: 5px; font-weight: 700" rel="noreferrer">查看演示</a>
|
||||
</div>
|
||||
<div class="mark">
|
||||
<span>2分钟装好</span>
|
||||
<span>阿里云推荐</span>
|
||||
<span>腾讯云推荐</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<a class="btn" href="javascript:;" id="goInstallLinux">查看安装脚本</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-box windows">
|
||||
<div class="img">
|
||||
<img src="/static/images/prd_2_03.png">
|
||||
</div>
|
||||
<div class="cont">
|
||||
<div class="top">
|
||||
<div class="title">Windows面板 {:config_get('new_version_win')}</div>
|
||||
<div class="desc">支持Windows Server 2008 R2/2012/2016/2019/2022,64位系统</div>
|
||||
<div class="mark">
|
||||
<span>操作简单</span>
|
||||
<span>使用方便</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<a class="btn" href="javascript:;" id="goInstallWindows">查看安装方法</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{if config_get('new_version_btm')}<div class="disflex flex_lrcenter mt_30 install-list">
|
||||
<div class="install-box monitor">
|
||||
<div class="img">
|
||||
<img src="/static/images/bt_monitor.png" />
|
||||
</div>
|
||||
<div class="cont">
|
||||
<div class="top">
|
||||
<div class="title">堡塔云监控</div>
|
||||
<div class="desc">多服务监控和异常告警通知</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<a class="btn" href="javascript:;" id="goInstallMonitor">查看安装脚本</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d2" id="instal-linux">
|
||||
<div class="wrap">
|
||||
<div class="wrap-title linux-title">
|
||||
<div class="text">Linux面板{:config_get('new_version')}安装脚本</div>
|
||||
</div>
|
||||
<div class="desc">
|
||||
使用 SSH 连接工具,如
|
||||
<a class="link" href="https://www.putty.org/" target="_blank" rel="noreferrer">PUTTY</a>
|
||||
连接到您的 Linux 服务器后,
|
||||
<a class="link" href="https://www.bt.cn/bbs/thread-50002-1-1.html" target="_blank" rel="noreferrer">挂载磁盘</a>
|
||||
,根据系统执行相应命令开始安装:
|
||||
</div>
|
||||
<div class="install-code">
|
||||
<span class="osname">Centos安装脚本</span>
|
||||
<div class="code-cont">
|
||||
<div class="command" title="点击复制Centos安装脚本">yum install -y wget && wget -O install.sh {$siteurl}/install/install_6.0.sh && sh install.sh</div>
|
||||
<span class="ico-copy" title="点击复制Centos安装脚本" data-clipboard-text="yum install -y wget && wget -O install.sh {$siteurl}/install/install_6.0.sh && sh install.sh">复制</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-code">
|
||||
<span class="osname">Ubuntu/Debian安装脚本</span>
|
||||
<div class="code-cont">
|
||||
<div class="command" title="点击复制Ubuntu/Debian安装脚本">wget -O install.sh {$siteurl}/install/install_6.0.sh && bash install.sh</div>
|
||||
<span class="ico-copy" title="点击复制Ubuntu/Debian安装脚本" data-clipboard-text="wget -O install.sh {$siteurl}/install/install_6.0.sh && bash install.sh">复制</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-code">
|
||||
<span class="osname">一键更新脚本</span>
|
||||
<div class="code-cont">
|
||||
<div class="command" title="点击复制一键更新脚本">curl {$siteurl}/install/update6.sh|bash</div>
|
||||
<span class="ico-copy" title="点击复制一键更新脚本" data-clipboard-text="curl {$siteurl}/install/update6.sh|bash">复制</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tips" style="color: orangered; font-weight: 700">
|
||||
<p>注意:必须为没装过其它环境如Apache/Nginx/php/MySQL的新系统,推荐使用centos 7.X的系统安装宝塔面板</p>
|
||||
<p style="text-indent: 3em">推荐使用Chrome、火狐、edge浏览器,国产浏览器请使用极速模式访问面板登录地址</p>
|
||||
<p style="text-indent: 3em">如果使用过官方版或其他第三方云端的版本,使用一键更新脚本即可切换到此云端</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d4" id="instal-windows" style="background-color: #edf6ef;">
|
||||
<div class="wrap">
|
||||
<div class="wrap-title">
|
||||
<div class="text">Windows面板{:config_get('new_version_win')}安装方法</div>
|
||||
</div>
|
||||
<div class="desc">
|
||||
<p>1、使用<a class="link" href="https://download.bt.cn/win/panel/BtSoft.zip" target="_blank" rel="noreferrer">官方安装程序</a>进行安装,安装完先不要进入面板。</p>
|
||||
<p>2、在命令提示符(cmd)输入以下一键更新命令,然后重启面板。</p>
|
||||
</div>
|
||||
<div class="install-code">
|
||||
<div class="code-cont">
|
||||
<div class="command" title="点击复制一键更新命令">wget {$siteurl}/win/panel/data/setup.py -O C:/update.py && "C:\Program Files\python\python.exe" C:/update.py update_panel {:config_get('new_version_win')}</div>
|
||||
<span class="ico-copy" title="点击复制一键更新命令" data-clipboard-text="wget {$siteurl}/win/panel/data/setup.py -O C:/update.py && "C:\Program Files\python\python.exe" C:/update.py update_panel {:config_get('new_version_win')}">复制</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tips" style="color: orangered; font-weight: 700">
|
||||
<p>注意:仅支持Windows Server 2008 R2/2012/2016/2019/2022,64位系统(中文简体),且未安装其它环境</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{if config_get('new_version_btm')}
|
||||
<div class="d4" id="instal-monitor">
|
||||
<div class="wrap">
|
||||
<div class="wrap-title">
|
||||
<div class="text" style="margin-right: 12px;">堡塔云监控{:config_get('new_version_btm')}安装脚本</div>
|
||||
</div>
|
||||
<div class="desc">
|
||||
使用 SSH 连接工具,如
|
||||
<a class="link" href="https://www.putty.org/" target="_blank" rel="noreferrer">PUTTY</a>
|
||||
连接到您的 Linux 服务器后,根据系统执行相应命令开始安装:
|
||||
</div>
|
||||
<div class="install-code">
|
||||
<span class="osname">堡塔云监控安装脚本</span>
|
||||
<div class="code-cont">
|
||||
<div class="command" title="点击复制安装脚本">curl -sS {$siteurl}/install/install_btmonitor.sh -o /tmp/install_btmonitor.sh && bash /tmp/install_btmonitor.sh</div>
|
||||
<span class="ico-copy" title="点击复制安装脚本" data-clipboard-text="curl -sS {$siteurl}/install/install_btmonitor.sh -o /tmp/install_btmonitor.sh && bash /tmp/install_btmonitor.sh">复制</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-code">
|
||||
<span class="osname">堡塔云监控更新脚本</span>
|
||||
<div class="code-cont">
|
||||
<div class="command" title="点击复制更新脚本">curl {$siteurl}/install/update_btmonitor.sh|bash</div>
|
||||
<span class="ico-copy" title="点击复制更新脚本" data-clipboard-text="curl {$siteurl}/install/update_btmonitor.sh|bash">复制</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tips" style="color: orangered; font-weight: 700">
|
||||
<p>注意:推荐使用Chrome、火狐、edge浏览器,国产浏览器(极速模式)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>{/if}
|
||||
<div class="animate-bg"></div>
|
||||
</div>
|
||||
|
||||
<div class="foot">
|
||||
<div class="wrap">
|
||||
<div class="fb textcenter">
|
||||
<div class="fb1 textcenter">
|
||||
<a href="http://www.bt.cn/new/agreement_open.html" target="_blank" rel="noreferrer">《开源许可协议》</a>
|
||||
<i>丨</i>
|
||||
<a href="http://www.bt.cn/new/agreement_user.html" target="_blank" rel="noreferrer">《用户协议》</a>
|
||||
<i>丨</i>
|
||||
<a href="http://www.bt.cn/new/agreement_privacy.html" target="_blank" rel="noreferrer">《隐私声明》</a>
|
||||
</div>
|
||||
<div class="fb2 mt_15">
|
||||
<p>
|
||||
Copyright © {:date('Y')} 宝塔面板安装脚本
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="//cdn.staticfile.org/jquery/3.6.0/jquery.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="//cdn.staticfile.org/layer/3.5.1/layer.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<script type="text/javascript" src="//cdn.staticfile.org/clipboard.js/1.7.1/clipboard.min.js"></script>
|
||||
<script type="text/javascript" src="/static/js/dx.js"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
var userId = '';
|
||||
|
||||
// 复制安装命令
|
||||
var clipboard = new Clipboard('.ico-copy', {
|
||||
text: function (element) {
|
||||
return $(element).prev().text();
|
||||
},
|
||||
});
|
||||
clipboard
|
||||
.on('success', function (e) {
|
||||
layer.msg(e.trigger.title + '成功', { icon: 1 });
|
||||
})
|
||||
.on('error', function (e) {
|
||||
layer.msg('复制失败,请手动选中文本Ctrl+c复制内容', { icon: 2 });
|
||||
});
|
||||
|
||||
$('.install-code .command').click(function () {
|
||||
$(this).next('.ico-copy').click();
|
||||
});
|
||||
|
||||
$('#goInstallLinux').click(function () {
|
||||
scrollTop('#instal-linux');
|
||||
});
|
||||
|
||||
$('#goInstallWindows').click(function () {
|
||||
scrollTop('#instal-windows');
|
||||
});
|
||||
|
||||
$('#goInstallCloud').click(function () {
|
||||
scrollTop('#instal-cloud');
|
||||
});
|
||||
|
||||
$('#goInstallMonitor').click(function () {
|
||||
scrollTop('#instal-monitor');
|
||||
});
|
||||
|
||||
function GetRequest() {
|
||||
var url = location.search;
|
||||
//获取url中"?"符后的字串
|
||||
var theRequest = new Object();
|
||||
if (url.indexOf('?') != -1) {
|
||||
var str = url.substr(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
if (GetRequest() == 'bt') {
|
||||
scrollTop('#instal-linux');
|
||||
}
|
||||
|
||||
// 滚动到指定位置
|
||||
function scrollTop(el) {
|
||||
var headHeight = 0;
|
||||
$('html, body').animate({ scrollTop: $(el).offset().top - headHeight }, { duration: 200, easing: 'swing' });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
268
app/view/install/index.html
Normal file
@ -0,0 +1,268 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>宝塔第三方云端 - 安装程序</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="renderer" content="webkit">
|
||||
<style>
|
||||
body {
|
||||
background: #f1f6fd;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body, input, button {
|
||||
font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, 'Microsoft Yahei', Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #7E96B3;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #4e73df;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 28px;
|
||||
font-weight: normal;
|
||||
color: #3C5675;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group .form-field:first-child input {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.form-group .form-field:last-child input {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.form-field input {
|
||||
background: #fff;
|
||||
margin: 0 0 2px;
|
||||
border: 2px solid transparent;
|
||||
transition: background 0.2s, border-color 0.2s, color 0.2s;
|
||||
width: 100%;
|
||||
padding: 15px 15px 15px 180px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-field input:focus {
|
||||
border-color: #4e73df;
|
||||
background: #fff;
|
||||
color: #444;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-field label {
|
||||
float: left;
|
||||
width: 160px;
|
||||
text-align: right;
|
||||
margin-right: -160px;
|
||||
position: relative;
|
||||
margin-top: 15px;
|
||||
font-size: 14px;
|
||||
pointer-events: none;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
button, .btn {
|
||||
background: #3C5675;
|
||||
color: #fff;
|
||||
border: 0;
|
||||
font-weight: bold;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
padding: 15px 30px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.form-buttons {
|
||||
height: 52px;
|
||||
line-height: 52px;
|
||||
}
|
||||
|
||||
.form-buttons .btn {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#error, .error, #success, .success, #warmtips, .warmtips {
|
||||
background: #D83E3E;
|
||||
color: #fff;
|
||||
padding: 15px 20px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#success {
|
||||
background: #3C5675;
|
||||
}
|
||||
|
||||
#error a, .error a {
|
||||
color: white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#warmtips {
|
||||
background: #fff;
|
||||
font-size: 14px;
|
||||
color: #3C5675;
|
||||
border: 2px solid #4e73df;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>
|
||||
<svg t="1660545699809" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4887" width="100px" height="100px">
|
||||
<path d="M811.4 418.7C765.6 297.9 648.9 212 512.2 212S258.8 297.8 213 418.6C127.3 441.1 64 519.1 64 612c0 110.5 89.5 200 199.9 200h496.2C870.5 812 960 722.5 960 612c0-92.7-63.1-170.7-148.6-193.3z m36.3 281c-23.4 23.4-54.5 36.3-87.6 36.3H263.9c-33.1 0-64.2-12.9-87.6-36.3-23.4-23.4-36.3-54.6-36.3-87.7 0-28 9.1-54.3 26.2-76.3 16.7-21.3 40.2-36.8 66.1-43.7l37.9-9.9 13.9-36.6c8.6-22.8 20.6-44.1 35.7-63.4 14.9-19.2 32.6-35.9 52.4-49.9 41.1-28.9 89.5-44.2 140-44.2s98.9 15.3 140 44.2c19.9 14 37.5 30.8 52.4 49.9 15.1 19.3 27.1 40.7 35.7 63.4l13.8 36.5 37.8 10c54.3 14.5 92.1 63.8 92.1 120 0 33.1-12.9 64.3-36.3 87.7z" p-id="4888" fill="#4e73df"></path>
|
||||
</svg>
|
||||
</h1>
|
||||
<h2>宝塔第三方云端 - 安装程序</h2>
|
||||
<div>
|
||||
|
||||
<form method="post">
|
||||
<div id="error" style="display:none"></div>
|
||||
<div id="success" style="display:none"></div>
|
||||
<div id="warmtips" style="display:none"><p>安装完成后,你还需要进行以下操作:</p><p>1、在后台使用批量替换工具,执行命令一键替换压缩包与安装脚本中的域名。</p><p></p>2、在后台配置面板对接,同步插件列表与插件包。</p></div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-field">
|
||||
<label>MySQL 数据库地址</label>
|
||||
<input type="text" name="mysql_host" value="localhost" required="">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>MySQL 数据库端口</label>
|
||||
<input type="number" name="mysql_port" value="3306">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>MySQL 用户名</label>
|
||||
<input type="text" name="mysql_user" value="" required="">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>MySQL 密码</label>
|
||||
<input type="text" name="mysql_pwd" value="" required="">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>MySQL 数据库名</label>
|
||||
<input type="text" name="mysql_name" value="" required="">
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>MySQL 数据表前缀</label>
|
||||
<input type="text" name="mysql_prefix" value="cloud_">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-field">
|
||||
<label>管理员用户名</label>
|
||||
<input type="text" name="admin_username" value="admin" required=""/>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>管理员密码</label>
|
||||
<input type="text" name="admin_password" value="123456" required="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-buttons">
|
||||
<!--@formatter:off-->
|
||||
<button type="submit" >点击安装</button>
|
||||
<!--@formatter:on-->
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script src="//cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
$('form').on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
var form = this;
|
||||
var $error = $("#error");
|
||||
var $success = $("#success");
|
||||
var $button = $(this).find('button')
|
||||
.text("安装中...")
|
||||
.prop('disabled', true);
|
||||
$.ajax({
|
||||
url: "",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: $(this).serialize(),
|
||||
success: function (ret) {
|
||||
if (ret.code == 1) {
|
||||
$error.hide();
|
||||
$(".form-group", form).remove();
|
||||
$button.remove();
|
||||
$("#success").text(ret.msg).show();
|
||||
$("#warmtips").show();
|
||||
|
||||
$buttons = $(".form-buttons", form);
|
||||
$('<a class="btn" href="/admin" style="background:#4e73df">进入后台</a>').appendTo($buttons);
|
||||
|
||||
} else {
|
||||
$error.show().text(ret.msg);
|
||||
$button.prop('disabled', false).text("点击安装");
|
||||
$("html,body").animate({
|
||||
scrollTop: 0
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
error: function (xhr) {
|
||||
$error.show().text(xhr.responseText);
|
||||
$button.prop('disabled', false).text("点击安装");
|
||||
$("html,body").animate({
|
||||
scrollTop: 0
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
41
composer.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "btpanel/cloud",
|
||||
"description": "BTPanel third cloud site",
|
||||
"type": "project",
|
||||
"keywords": [
|
||||
"btpanel",
|
||||
"baota",
|
||||
"aapanel"
|
||||
],
|
||||
"homepage": "https://www.bt.cn/",
|
||||
"license": "Apache-2.0",
|
||||
"authors": [],
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"topthink/framework": "^6.0.0",
|
||||
"topthink/think-orm": "^2.0",
|
||||
"topthink/think-view": "^1.0",
|
||||
"topthink/think-captcha": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/var-dumper": "^4.2",
|
||||
"topthink/think-trace":"^1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"app\\": "app"
|
||||
},
|
||||
"psr-0": {
|
||||
"": "extend/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist"
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"@php think service:discover",
|
||||
"@php think vendor:publish"
|
||||
]
|
||||
}
|
||||
}
|
32
config/app.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 应用设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 应用地址
|
||||
'app_host' => env('app.host', ''),
|
||||
// 应用的命名空间
|
||||
'app_namespace' => '',
|
||||
// 是否启用路由
|
||||
'with_route' => true,
|
||||
// 默认应用
|
||||
'default_app' => 'index',
|
||||
// 默认时区
|
||||
'default_timezone' => 'Asia/Shanghai',
|
||||
|
||||
// 应用映射(自动多应用模式有效)
|
||||
'app_map' => [],
|
||||
// 域名绑定(自动多应用模式有效)
|
||||
'domain_bind' => [],
|
||||
// 禁止URL访问的应用列表(自动多应用模式有效)
|
||||
'deny_app_list' => [],
|
||||
|
||||
// 异常页面的模板文件
|
||||
'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl',
|
||||
|
||||
// 错误显示信息,非调试模式有效
|
||||
'error_message' => '页面错误!请稍后再试~',
|
||||
// 显示错误信息
|
||||
'show_error_msg' => true,
|
||||
];
|
40
config/cache.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 缓存设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 默认缓存驱动
|
||||
'default' => env('cache.driver', 'file'),
|
||||
|
||||
// 缓存连接方式配置
|
||||
'stores' => [
|
||||
'file' => [
|
||||
// 驱动方式
|
||||
'type' => 'File',
|
||||
// 缓存保存目录
|
||||
'path' => '',
|
||||
// 缓存前缀
|
||||
'prefix' => '',
|
||||
// 缓存有效期 0表示永久缓存
|
||||
'expire' => 0,
|
||||
// 缓存标签前缀
|
||||
'tag_prefix' => 'tag:',
|
||||
// 序列化机制 例如 ['serialize', 'unserialize']
|
||||
'serialize' => [],
|
||||
],
|
||||
'redis' => [
|
||||
// 驱动方式
|
||||
'type' => 'Redis',
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'password' => '',
|
||||
'select' => 0,
|
||||
// 缓存有效期 0表示永久缓存
|
||||
'expire' => 3600,
|
||||
'prefix' => '',
|
||||
],
|
||||
// 更多的缓存连接
|
||||
],
|
||||
];
|
39
config/captcha.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | Captcha配置文件
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
//验证码位数
|
||||
'length' => 4,
|
||||
// 验证码字符集合
|
||||
'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY',
|
||||
// 验证码过期时间
|
||||
'expire' => 1800,
|
||||
// 是否使用中文验证码
|
||||
'useZh' => false,
|
||||
// 是否使用算术验证码
|
||||
'math' => false,
|
||||
// 是否使用背景图
|
||||
'useImgBg' => false,
|
||||
//验证码字符大小
|
||||
'fontSize' => 25,
|
||||
// 是否使用混淆曲线
|
||||
'useCurve' => true,
|
||||
//是否添加杂点
|
||||
'useNoise' => true,
|
||||
// 验证码字体 不设置则随机
|
||||
'fontttf' => '',
|
||||
//背景颜色
|
||||
'bg' => [243, 251, 254],
|
||||
// 验证码图片高度
|
||||
'imageH' => 0,
|
||||
// 验证码图片宽度
|
||||
'imageW' => 0,
|
||||
|
||||
// 添加额外的验证码设置
|
||||
// verify => [
|
||||
// 'length'=>4,
|
||||
// ...
|
||||
//],
|
||||
];
|
11
config/console.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 控制台配置
|
||||
// +----------------------------------------------------------------------
|
||||
return [
|
||||
// 指令定义
|
||||
'commands' => [
|
||||
'updateall' => 'app\command\UpdateAll',
|
||||
'decrypt' => 'app\command\DecryptFile',
|
||||
],
|
||||
];
|
20
config/cookie.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | Cookie设置
|
||||
// +----------------------------------------------------------------------
|
||||
return [
|
||||
// cookie 保存时间
|
||||
'expire' => 0,
|
||||
// cookie 保存路径
|
||||
'path' => '/',
|
||||
// cookie 有效域名
|
||||
'domain' => '',
|
||||
// cookie 启用安全传输
|
||||
'secure' => false,
|
||||
// httponly设置
|
||||
'httponly' => false,
|
||||
// 是否使用 setcookie
|
||||
'setcookie' => true,
|
||||
// samesite 设置,支持 'strict' 'lax'
|
||||
'samesite' => '',
|
||||
];
|
63
config/database.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// 默认使用的数据库连接配置
|
||||
'default' => env('database.driver', 'mysql'),
|
||||
|
||||
// 自定义时间查询规则
|
||||
'time_query_rule' => [],
|
||||
|
||||
// 自动写入时间戳字段
|
||||
// true为自动识别类型 false关闭
|
||||
// 字符串则明确指定时间字段类型 支持 int timestamp datetime date
|
||||
'auto_timestamp' => true,
|
||||
|
||||
// 时间字段取出后的默认时间格式
|
||||
'datetime_format' => 'Y-m-d H:i:s',
|
||||
|
||||
// 时间字段配置 配置格式:create_time,update_time
|
||||
'datetime_field' => '',
|
||||
|
||||
// 数据库连接配置信息
|
||||
'connections' => [
|
||||
'mysql' => [
|
||||
// 数据库类型
|
||||
'type' => env('database.type', 'mysql'),
|
||||
// 服务器地址
|
||||
'hostname' => env('database.hostname', '127.0.0.1'),
|
||||
// 数据库名
|
||||
'database' => env('database.database', ''),
|
||||
// 用户名
|
||||
'username' => env('database.username', 'root'),
|
||||
// 密码
|
||||
'password' => env('database.password', ''),
|
||||
// 端口
|
||||
'hostport' => env('database.hostport', '3306'),
|
||||
// 数据库连接参数
|
||||
'params' => [],
|
||||
// 数据库编码默认采用utf8
|
||||
'charset' => env('database.charset', 'utf8mb4'),
|
||||
// 数据库表前缀
|
||||
'prefix' => env('database.prefix', ''),
|
||||
|
||||
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
|
||||
'deploy' => 0,
|
||||
// 数据库读写是否分离 主从式有效
|
||||
'rw_separate' => false,
|
||||
// 读写分离后 主服务器数量
|
||||
'master_num' => 1,
|
||||
// 指定从服务器序号
|
||||
'slave_no' => '',
|
||||
// 是否严格检查字段是否存在
|
||||
'fields_strict' => true,
|
||||
// 是否需要断线重连
|
||||
'break_reconnect' => false,
|
||||
// 监听SQL
|
||||
'trigger_sql' => env('app_debug', true),
|
||||
// 开启字段缓存
|
||||
'fields_cache' => false,
|
||||
],
|
||||
|
||||
// 更多的数据库配置信息
|
||||
],
|
||||
];
|
24
config/filesystem.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
// 默认磁盘
|
||||
'default' => env('filesystem.driver', 'local'),
|
||||
// 磁盘列表
|
||||
'disks' => [
|
||||
'local' => [
|
||||
'type' => 'local',
|
||||
'root' => app()->getRuntimePath() . 'storage',
|
||||
],
|
||||
'public' => [
|
||||
// 磁盘类型
|
||||
'type' => 'local',
|
||||
// 磁盘路径
|
||||
'root' => app()->getRootPath() . 'public/storage',
|
||||
// 磁盘路径对应的外部URL路径
|
||||
'url' => '/storage',
|
||||
// 可见性
|
||||
'visibility' => 'public',
|
||||
],
|
||||
// 更多的磁盘配置信息
|
||||
],
|
||||
];
|
27
config/lang.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 多语言设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 默认语言
|
||||
'default_lang' => env('lang.default_lang', 'zh-cn'),
|
||||
// 允许的语言列表
|
||||
'allow_lang_list' => [],
|
||||
// 多语言自动侦测变量名
|
||||
'detect_var' => 'lang',
|
||||
// 是否使用Cookie记录
|
||||
'use_cookie' => true,
|
||||
// 多语言cookie变量
|
||||
'cookie_var' => 'think_lang',
|
||||
// 多语言header变量
|
||||
'header_var' => 'think-lang',
|
||||
// 扩展语言包
|
||||
'extend_list' => [],
|
||||
// Accept-Language转义为对应语言包名称
|
||||
'accept_language' => [
|
||||
'zh-hans-cn' => 'zh-cn',
|
||||
],
|
||||
// 是否支持语言分组
|
||||
'allow_group' => false,
|
||||
];
|
45
config/log.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | 日志设置
|
||||
// +----------------------------------------------------------------------
|
||||
return [
|
||||
// 默认日志记录通道
|
||||
'default' => env('log.channel', 'file'),
|
||||
// 日志记录级别
|
||||
'level' => [],
|
||||
// 日志类型记录的通道 ['error'=>'email',...]
|
||||
'type_channel' => [],
|
||||
// 关闭全局日志写入
|
||||
'close' => false,
|
||||
// 全局日志处理 支持闭包
|
||||
'processor' => null,
|
||||
|
||||
// 日志通道列表
|
||||
'channels' => [
|
||||
'file' => [
|
||||
// 日志记录方式
|
||||
'type' => 'File',
|
||||
// 日志保存目录
|
||||
'path' => '',
|
||||
// 单文件日志写入
|
||||
'single' => false,
|
||||
// 独立日志级别
|
||||
'apart_level' => [],
|
||||
// 最大日志文件数量
|
||||
'max_files' => 0,
|
||||
// 使用JSON格式记录
|
||||
'json' => false,
|
||||
// 日志处理
|
||||
'processor' => null,
|
||||
// 关闭通道日志写入
|
||||
'close' => false,
|
||||
// 日志输出格式化
|
||||
'format' => '[%s][%s] %s',
|
||||
// 是否实时写入
|
||||
'realtime_write' => false,
|
||||
],
|
||||
// 其它日志通道配置
|
||||
],
|
||||
|
||||
];
|
8
config/middleware.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
// 中间件配置
|
||||
return [
|
||||
// 别名或分组
|
||||
'alias' => [],
|
||||
// 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
|
||||
'priority' => [],
|
||||
];
|
45
config/route.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 路由设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// pathinfo分隔符
|
||||
'pathinfo_depr' => '/',
|
||||
// URL伪静态后缀
|
||||
'url_html_suffix' => '',
|
||||
// URL普通方式参数 用于自动生成
|
||||
'url_common_param' => true,
|
||||
// 是否开启路由延迟解析
|
||||
'url_lazy_route' => false,
|
||||
// 是否强制使用路由
|
||||
'url_route_must' => true,
|
||||
// 合并路由规则
|
||||
'route_rule_merge' => false,
|
||||
// 路由是否完全匹配
|
||||
'route_complete_match' => false,
|
||||
// 访问控制器层名称
|
||||
'controller_layer' => 'controller',
|
||||
// 空控制器名
|
||||
'empty_controller' => 'Error',
|
||||
// 是否使用控制器后缀
|
||||
'controller_suffix' => false,
|
||||
// 默认的路由变量规则
|
||||
'default_route_pattern' => '[\w\.]+',
|
||||
// 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
|
||||
'request_cache_key' => false,
|
||||
// 请求缓存有效期
|
||||
'request_cache_expire' => null,
|
||||
// 全局请求缓存排除规则
|
||||
'request_cache_except' => [],
|
||||
// 默认控制器名
|
||||
'default_controller' => 'Index',
|
||||
// 默认操作名
|
||||
'default_action' => 'index',
|
||||
// 操作方法后缀
|
||||
'action_suffix' => '',
|
||||
// 默认JSONP格式返回的处理方法
|
||||
'default_jsonp_handler' => 'jsonpReturn',
|
||||
// 默认JSONP处理方法
|
||||
'var_jsonp_handler' => 'callback',
|
||||
];
|
19
config/session.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 会话设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// session name
|
||||
'name' => 'PHPSESSID',
|
||||
// SESSION_ID的提交变量,解决flash上传跨域
|
||||
'var_session_id' => '',
|
||||
// 驱动方式 支持file cache
|
||||
'type' => 'file',
|
||||
// 存储连接标识 当type使用cache的时候有效
|
||||
'store' => null,
|
||||
// 过期时间
|
||||
'expire' => 1440,
|
||||
// 前缀
|
||||
'prefix' => '',
|
||||
];
|
10
config/trace.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | Trace设置 开启调试模式后有效
|
||||
// +----------------------------------------------------------------------
|
||||
return [
|
||||
// 内置Html和Console两种方式 支持扩展
|
||||
'type' => 'Html',
|
||||
// 读取的日志通道名
|
||||
'channel' => '',
|
||||
];
|
25
config/view.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | 模板设置
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
// 模板引擎类型使用Think
|
||||
'type' => 'Think',
|
||||
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
|
||||
'auto_rule' => 1,
|
||||
// 模板目录名
|
||||
'view_dir_name' => 'view',
|
||||
// 模板后缀
|
||||
'view_suffix' => 'html',
|
||||
// 模板文件名分隔符
|
||||
'view_depr' => DIRECTORY_SEPARATOR,
|
||||
// 模板引擎普通标签开始标记
|
||||
'tpl_begin' => '{',
|
||||
// 模板引擎普通标签结束标记
|
||||
'tpl_end' => '}',
|
||||
// 标签库标签开始标记
|
||||
'taglib_begin' => '{',
|
||||
// 标签库标签结束标记
|
||||
'taglib_end' => '}',
|
||||
];
|
1
data/config/deployment_list.json
Normal file
1
data/config/plugin_list.json
Normal file
0
data/plugins/folder/.gitkeep
Normal file
0
data/plugins/main/.gitkeep
Normal file
0
data/plugins/other/other/.gitkeep
Normal file
0
data/plugins/package/.gitkeep
Normal file
1
data/win/config/deployment_list.json
Normal file
1
data/win/config/plugin_list.json
Normal file
0
data/win/plugins/folder/.gitkeep
Normal file
0
data/win/plugins/main/.gitkeep
Normal file
0
data/win/plugins/package/.gitkeep
Normal file
66
install.sql
Normal file
@ -0,0 +1,66 @@
|
||||
DROP TABLE IF EXISTS `cloud_config`;
|
||||
CREATE TABLE `cloud_config` (
|
||||
`key` varchar(32) NOT NULL,
|
||||
`value` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
INSERT INTO `cloud_config` (`key`, `value`) VALUES
|
||||
('admin_username', 'admin'),
|
||||
('admin_password', '123456'),
|
||||
('bt_url', ''),
|
||||
('bt_key', ''),
|
||||
('whitelist', '0'),
|
||||
('download_page', '1'),
|
||||
('new_version', '8.0.1'),
|
||||
('update_msg', '暂无更新日志'),
|
||||
('update_date', '2023-07-20'),
|
||||
('new_version_win', '7.9.0'),
|
||||
('update_msg_win', '暂无更新日志'),
|
||||
('update_date_win', '2023-07-20'),
|
||||
('new_version_btm', '2.2.5'),
|
||||
('update_msg_btm', '暂无更新日志'),
|
||||
('update_date_btm', '2023-07-06'),
|
||||
('updateall_type', '0'),
|
||||
('syskey', 'UqP94LtI8eWAIgCP');
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `cloud_black`;
|
||||
CREATE TABLE `cloud_black` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`ip` varchar(20) NOT NULL,
|
||||
`enable` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`addtime` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `ip`(`ip`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `cloud_white`;
|
||||
CREATE TABLE `cloud_white` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`ip` varchar(20) NOT NULL,
|
||||
`enable` tinyint(1) NOT NULL DEFAULT '1',
|
||||
`addtime` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `ip`(`ip`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `cloud_record`;
|
||||
CREATE TABLE `cloud_record` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`ip` varchar(20) NOT NULL,
|
||||
`addtime` datetime NOT NULL,
|
||||
`usetime` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `ip`(`ip`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `cloud_log`;
|
||||
CREATE TABLE `cloud_log` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`uid` tinyint(4) NOT NULL DEFAULT '1',
|
||||
`action` varchar(40) NOT NULL,
|
||||
`data` varchar(150) DEFAULT NULL,
|
||||
`addtime` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
8
public/.htaccess
Normal file
@ -0,0 +1,8 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
Options +FollowSymlinks -Multiviews
|
||||
RewriteEngine On
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
|
||||
</IfModule>
|
24
public/index.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
// [ 应用入口文件 ]
|
||||
namespace think;
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// 执行HTTP应用并响应
|
||||
$http = (new App())->http;
|
||||
|
||||
$response = $http->run();
|
||||
|
||||
$response->send();
|
||||
|
||||
$http->end($response);
|
1024
public/install/install_6.0.sh
Normal file
880
public/install/install_btmonitor.sh
Normal file
@ -0,0 +1,880 @@
|
||||
#!/bin/bash
|
||||
#########################
|
||||
|
||||
# 广东堡塔安全技术有限公司
|
||||
# author: 赤井秀一
|
||||
# mail: 1021266737@qq.com
|
||||
|
||||
#########################
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
LANG=en_US.UTF-8
|
||||
|
||||
Btapi_Url='http://www.example.com'
|
||||
|
||||
is64bit=$(getconf LONG_BIT)
|
||||
if [ "${is64bit}" != '64' ];then
|
||||
echo -e "\033[31m 抱歉, 堡塔云监控系统不支持32位系统, 请使用64位系统! \033[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
S390X_CHECK=$(uname -a|grep s390x)
|
||||
if [ "${S390X_CHECK}" ];then
|
||||
echo -e "\033[31m 抱歉, 堡塔云监控系统不支持s390x架构进行安装,请使用x86_64服务器架构 \033[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
is_aarch64=$(uname -a|grep aarch64)
|
||||
if [ "${is_aarch64}" != "" ];then
|
||||
echo -e "\033[31m 抱歉, 堡塔云监控系统暂不支持aarch64架构进行安装,请使用x86_64服务器架构 \033[0m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Command_Exists() {
|
||||
command -v "$@" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
|
||||
GetSysInfo(){
|
||||
if [ -s "/etc/redhat-release" ];then
|
||||
SYS_VERSION=$(cat /etc/redhat-release)
|
||||
elif [ -s "/etc/issue" ]; then
|
||||
SYS_VERSION=$(cat /etc/issue)
|
||||
fi
|
||||
SYS_INFO=$(uname -a)
|
||||
SYS_BIT=$(getconf LONG_BIT)
|
||||
MEM_TOTAL=$(free -m|grep Mem|awk '{print $2}')
|
||||
CPU_INFO=$(getconf _NPROCESSORS_ONLN)
|
||||
|
||||
echo -e ${SYS_VERSION}
|
||||
echo -e Bit:${SYS_BIT} Mem:${MEM_TOTAL}M Core:${CPU_INFO}
|
||||
echo -e ${SYS_INFO}
|
||||
echo -e "请截图以上报错信息发帖至论坛www.bt.cn/bbs求助"
|
||||
|
||||
}
|
||||
|
||||
Red_Error(){
|
||||
echo '=================================================';
|
||||
printf '\033[1;31;40m%b\033[0m\n' "$@";
|
||||
GetSysInfo
|
||||
exit 1;
|
||||
}
|
||||
|
||||
|
||||
monitor_path="/www/server/bt-monitor"
|
||||
run_bin="/www/server/bt-monitor/BT-MONITOR"
|
||||
if [ ! -d "/www/server" ];then
|
||||
mkdir -p /www/server
|
||||
fi
|
||||
old_dir="/www/server/old_btmonitor"
|
||||
|
||||
cd ~
|
||||
setup_path="/www"
|
||||
python_bin=$setup_path/server/bt-monitor/pyenv/bin/python
|
||||
cpu_cpunt=$(cat /proc/cpuinfo|grep processor|wc -l)
|
||||
|
||||
get_node_url(){
|
||||
if [ ! -f /bin/curl ];then
|
||||
if [ "${PM}" = "yum" ]; then
|
||||
yum install curl -y
|
||||
elif [ "${PM}" = "apt-get" ]; then
|
||||
apt-get install curl -y
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "/www/node.pl" ];then
|
||||
download_Url=$(cat /www/node.pl)
|
||||
echo "Download node: $download_Url";
|
||||
echo '---------------------------------------------';
|
||||
return
|
||||
fi
|
||||
|
||||
echo '---------------------------------------------';
|
||||
echo "Selected download node...";
|
||||
# nodes=(http://dg2.bt.cn http://dg1.bt.cn http://125.90.93.52:5880 http://36.133.1.8:5880 http://123.129.198.197 http://38.34.185.130 http://116.213.43.206:5880 http://128.1.164.196);
|
||||
#nodes=(http://dg2.bt.cn http://dg1.bt.cn http://125.90.93.52:5880 http://36.133.1.8:5880 http://123.129.198.197 http://116.213.43.206:5880 http://128.1.164.196);
|
||||
nodes=(https://dg2.bt.cn https://dg1.bt.cn https://download.bt.cn);
|
||||
tmp_file1=/dev/shm/net_test1.pl
|
||||
tmp_file2=/dev/shm/net_test2.pl
|
||||
[ -f "${tmp_file1}" ] && rm -f ${tmp_file1}
|
||||
[ -f "${tmp_file2}" ] && rm -f ${tmp_file2}
|
||||
touch $tmp_file1
|
||||
touch $tmp_file2
|
||||
for node in ${nodes[@]};
|
||||
do
|
||||
NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${node}/net_test|xargs)
|
||||
RES=$(echo ${NODE_CHECK}|awk '{print $1}')
|
||||
NODE_STATUS=$(echo ${NODE_CHECK}|awk '{print $2}')
|
||||
TIME_TOTAL=$(echo ${NODE_CHECK}|awk '{print $3 * 1000 - 500 }'|cut -d '.' -f 1)
|
||||
if [ "${NODE_STATUS}" == "200" ];then
|
||||
if [ $TIME_TOTAL -lt 100 ];then
|
||||
if [ $RES -ge 1500 ];then
|
||||
echo "$RES $node" >> $tmp_file1
|
||||
fi
|
||||
else
|
||||
if [ $RES -ge 1500 ];then
|
||||
echo "$TIME_TOTAL $node" >> $tmp_file2
|
||||
fi
|
||||
fi
|
||||
|
||||
i=$(($i+1))
|
||||
if [ $TIME_TOTAL -lt 100 ];then
|
||||
if [ $RES -ge 3000 ];then
|
||||
break;
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
NODE_URL=$(cat $tmp_file1|sort -r -g -t " " -k 1|head -n 1|awk '{print $2}')
|
||||
if [ -z "$NODE_URL" ];then
|
||||
NODE_URL=$(cat $tmp_file2|sort -g -t " " -k 1|head -n 1|awk '{print $2}')
|
||||
if [ -z "$NODE_URL" ];then
|
||||
NODE_URL='https://download.bt.cn';
|
||||
fi
|
||||
fi
|
||||
rm -f $tmp_file1
|
||||
rm -f $tmp_file2
|
||||
download_Url=$NODE_URL
|
||||
echo "Download node: $download_Url";
|
||||
echo '---------------------------------------------';
|
||||
}
|
||||
|
||||
Get_Versions(){
|
||||
redhat_version_file="/etc/redhat-release"
|
||||
deb_version_file="/etc/issue"
|
||||
if [ -f $redhat_version_file ];then
|
||||
os_type='el'
|
||||
is_aliyunos=$(cat $redhat_version_file|grep Aliyun)
|
||||
if [ "$is_aliyunos" != "" ];then
|
||||
return
|
||||
fi
|
||||
os_version=$(cat $redhat_version_file|grep CentOS|grep -Eo '([0-9]+\.)+[0-9]+'|grep -Eo '^[0-9]')
|
||||
if [ "${os_version}" = "5" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ -z "${os_version}" ];then
|
||||
os_version=$(cat /etc/redhat-release |grep Stream|grep -oE 8)
|
||||
fi
|
||||
else
|
||||
os_type='ubuntu'
|
||||
os_version=$(cat $deb_version_file|grep Ubuntu|grep -Eo '([0-9]+\.)+[0-9]+'|grep -Eo '^[0-9]+')
|
||||
if [ "${os_version}" = "" ];then
|
||||
os_type='debian'
|
||||
os_version=$(cat $deb_version_file|grep Debian|grep -Eo '([0-9]+\.)+[0-9]+'|grep -Eo '[0-9]+')
|
||||
if [ "${os_version}" = "" ];then
|
||||
os_version=$(cat $deb_version_file|grep Debian|grep -Eo '[0-9]+')
|
||||
fi
|
||||
if [ "${os_version}" = "8" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "${is64bit}" = '32' ];then
|
||||
os_version=""
|
||||
fi
|
||||
else
|
||||
if [ "$os_version" = "14" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "$os_version" = "12" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "$os_version" = "19" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "$os_version" = "21" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "$os_version" = "20" ];then
|
||||
os_version2004=$(cat /etc/issue|grep 20.04)
|
||||
if [ -z "${os_version2004}" ];then
|
||||
os_version=""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
Install_Python_Lib(){
|
||||
curl -Ss --connect-timeout 3 -m 60 $download_Url/install/pip_select.sh|bash
|
||||
pyenv_path="/www/server/bt-monitor"
|
||||
if [ -f $pyenv_path/pyenv/bin/python ];then
|
||||
is_ssl=$($python_bin -c "import ssl" 2>&1|grep cannot)
|
||||
$pyenv_path/pyenv/bin/python3.7 -V
|
||||
if [ $? -eq 0 ] && [ -z "${is_ssl}" ];then
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
is_package=$($python_bin -m psutil 2>&1|grep package)
|
||||
if [ "$is_package" = "" ];then
|
||||
wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip.txt -t 5 -T 10
|
||||
$pyenv_path/pyenv/bin/pip install -U pip
|
||||
$pyenv_path/pyenv/bin/pip install -U setuptools
|
||||
$pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt
|
||||
$pyenv_path/pyenv/bin/pip install -U flask==2.2.0
|
||||
$pyenv_path/pyenv/bin/pip install flask_sock
|
||||
$pyenv_path/pyenv/bin/pip install cachelib
|
||||
$pyenv_path/pyenv/bin/pip install py7zr
|
||||
$pyenv_path/pyenv/bin/pip install backports.lzma
|
||||
$pyenv_path/pyenv/bin/pip install pandas
|
||||
$pyenv_path/pyenv/bin/pip install msgpack
|
||||
fi
|
||||
source $pyenv_path/pyenv/bin/activate
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
return
|
||||
else
|
||||
rm -rf $pyenv_path/pyenv
|
||||
fi
|
||||
fi
|
||||
|
||||
py_version="3.7.9"
|
||||
if [ ! -d "$pyenv_path" ]; then
|
||||
mkdir -p $pyenv_path
|
||||
fi
|
||||
echo "True" > /www/disk.pl
|
||||
if [ ! -w /www/disk.pl ];then
|
||||
Red_Error "ERROR: Install python env fielded." "ERROR: /www目录无法写入,请检查目录/用户/磁盘权限!"
|
||||
fi
|
||||
os_type='el'
|
||||
os_version='7'
|
||||
is_export_openssl=0
|
||||
Get_Versions
|
||||
|
||||
echo "OS: $os_type - $os_version"
|
||||
is_aarch64=$(uname -a|grep aarch64)
|
||||
if [ "$is_aarch64" != "" ];then
|
||||
is64bit="aarch64"
|
||||
fi
|
||||
|
||||
if [ -f "/www/server/bt-monitor/pymake.pl" ];then
|
||||
os_version=""
|
||||
rm -f /www/server/bt-monitor/pymake.pl
|
||||
fi
|
||||
|
||||
if [[ $os_type =~ "debian" ]] || [[ $os_type =~ "ubuntu" ]]; then
|
||||
isbtm="-btm"
|
||||
fi
|
||||
|
||||
if [ "${os_version}" != "" ];then
|
||||
pyenv_file="/www/pyenv.tar.gz"
|
||||
wget -O $pyenv_file $download_Url/install/pyenv/pyenv-${os_type}${os_version}-x${is64bit}${isbtm}.tar.gz -t 5 -T 10
|
||||
|
||||
tmp_size=$(du -b $pyenv_file|awk '{print $1}')
|
||||
if [ $tmp_size -lt 703460 ];then
|
||||
rm -f $pyenv_file
|
||||
echo "ERROR: Download python env fielded."
|
||||
else
|
||||
echo "Install python env..."
|
||||
tar zxvf $pyenv_file -C $pyenv_path/ > /dev/null
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
rm -rf $pyenv_path/pyenv/bin/python
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 $pyenv_path/pyenv/bin/python
|
||||
$pyenv_path/pyenv/bin/python -m pip install --upgrade --force-reinstall pip
|
||||
$pyenv_path/pyenv/bin/pip install -U flask==2.2.0
|
||||
$pyenv_path/pyenv/bin/pip install flask_sock
|
||||
$pyenv_path/pyenv/bin/pip install cachelib
|
||||
$pyenv_path/pyenv/bin/pip install py7zr
|
||||
$pyenv_path/pyenv/bin/pip install backports.lzma
|
||||
$pyenv_path/pyenv/bin/pip install pandas
|
||||
$pyenv_path/pyenv/bin/pip install msgpack
|
||||
if [ ! -f $pyenv_path/pyenv/bin/python ];then
|
||||
rm -f $pyenv_file
|
||||
Red_Error "ERROR: Install python env fielded." "ERROR: 下载堡塔云监控主控端运行环境失败,请尝试重新安装!"
|
||||
fi
|
||||
$pyenv_path/pyenv/bin/python3.7 -V
|
||||
if [ $? -eq 0 ];then
|
||||
rm -f $pyenv_file
|
||||
ln -sf $pyenv_path/pyenv/bin/pip3.7 /usr/bin/btmpip
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 /usr/bin/btmpython
|
||||
source $pyenv_path/pyenv/bin/activate
|
||||
return
|
||||
else
|
||||
rm -f $pyenv_file
|
||||
rm -rf $pyenv_path/pyenv
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
cd /www
|
||||
python_src='/www/python_src.tar.xz'
|
||||
python_src_path="/www/Python-${py_version}"
|
||||
wget -O $python_src $download_Url/src/Python-${py_version}.tar.xz -t 5 -T 10
|
||||
tmp_size=$(du -b $python_src|awk '{print $1}')
|
||||
if [ $tmp_size -lt 10703460 ];then
|
||||
rm -f $python_src
|
||||
Red_Error "ERROR: Download python source code fielded." "ERROR: 下载堡塔云监控主控端运行环境失败,请尝试重新安装!"
|
||||
fi
|
||||
tar xvf $python_src
|
||||
rm -f $python_src
|
||||
cd $python_src_path
|
||||
./configure --prefix=$pyenv_path/pyenv
|
||||
make -j$cpu_cpunt
|
||||
make install
|
||||
if [ ! -f $pyenv_path/pyenv/bin/python3.7 ];then
|
||||
rm -rf $python_src_path
|
||||
Red_Error "ERROR: Make python env fielded." "ERROR: 编译堡塔云监控主控端运行环境失败!"
|
||||
fi
|
||||
cd ~
|
||||
rm -rf $python_src_path
|
||||
wget -O $pyenv_path/pyenv/bin/activate $download_Url/install/pyenv/activate.panel -t 5 -T 10
|
||||
wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip-3.7.8.txt -t 5 -T 10
|
||||
ln -sf $pyenv_path/pyenv/bin/pip3.7 $pyenv_path/pyenv/bin/pip
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 $pyenv_path/pyenv/bin/python
|
||||
ln -sf $pyenv_path/pyenv/bin/pip3.7 /usr/bin/btmpip
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 /usr/bin/btmpython
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
$pyenv_path/pyenv/bin/pip install -U pip
|
||||
$pyenv_path/pyenv/bin/pip install -U setuptools
|
||||
$pyenv_path/pyenv/bin/pip install -U wheel==0.34.2
|
||||
$pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt
|
||||
$pyenv_path/pyenv/bin/pip install -U flask==2.2.0
|
||||
$pyenv_path/pyenv/bin/pip install flask_sock
|
||||
$pyenv_path/pyenv/bin/pip install cachelib
|
||||
$pyenv_path/pyenv/bin/pip install py7zr
|
||||
$pyenv_path/pyenv/bin/pip install backports.lzma
|
||||
$pyenv_path/pyenv/bin/pip install pandas
|
||||
$pyenv_path/pyenv/bin/pip install msgpack
|
||||
source $pyenv_path/pyenv/bin/activate
|
||||
|
||||
is_gevent=$($python_bin -m gevent 2>&1|grep -oE package)
|
||||
is_psutil=$($python_bin -m psutil 2>&1|grep -oE package)
|
||||
if [ "${is_gevent}" != "${is_psutil}" ];then
|
||||
Red_Error "ERROR: psutil/gevent install failed!"
|
||||
fi
|
||||
}
|
||||
|
||||
Install_Monitor(){
|
||||
ulimit -n 1000001
|
||||
tee -a /etc/security/limits.conf << EOF
|
||||
* hard nofile 1000001
|
||||
* soft nofile 1000001
|
||||
root hard nofile 1000001
|
||||
root soft nofile 1000001
|
||||
EOF
|
||||
sysctl -p
|
||||
panelPort="806"
|
||||
|
||||
if [ ! -d "/etc/init.d" ];then
|
||||
mkdir -p /etc/init.d
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/btm" ]; then
|
||||
/etc/init.d/btm stop
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
if [ -f "/www/server/bt-monitor/sqlite-server.sh" ]; then
|
||||
chmod +x /www/server/bt-monitor/sqlite-server.sh
|
||||
/www/server/bt-monitor/sqlite-server.sh stop
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
version="2.1.7"
|
||||
file_name="bt-monitor"
|
||||
agent_src="bt-monitor.zip"
|
||||
|
||||
cd ~
|
||||
version=`curl -sf ${Btapi_Url}/bt_monitor/latest_version |awk -F '\"version\"' '{print $2}'|awk -F ':' '{print $2}'|awk -F '"' '{print $2}'`
|
||||
if [ -z $version ]; then
|
||||
version="2.0.6"
|
||||
fi
|
||||
if [ "$re_install" == "1" ]; then
|
||||
new_dir="/www/server/new_btmonitor"
|
||||
if [ ! -d "$new_dir" ];then
|
||||
mkdir -p $new_dir
|
||||
fi
|
||||
wget -O $agent_src ${Btapi_Url}/install/src/$file_name-$version.zip -t 5 -T 10
|
||||
unzip -o $agent_src -d $new_dir/ > /dev/null
|
||||
if [ ! -f $new_dir/BT-MONITOR ];then
|
||||
ls -lh $agent_src
|
||||
Red_Error "ERROR: Failed to download, please try install again!" "ERROR: 下载堡塔云监控主控端失败,请尝试重新安装!"
|
||||
fi
|
||||
|
||||
rm -rf $new_dir/config
|
||||
rm -rf $new_dir/data
|
||||
rm -rf $new_dir/ssl
|
||||
\cp -r $new_dir/* $monitor_path/
|
||||
rm -rf $new_dir
|
||||
else
|
||||
wget -O $agent_src ${Btapi_Url}/install/src/$file_name-$version.zip -t 5 -T 10
|
||||
if [ ! -d "$monitor_path" ]; then
|
||||
mkdir -p $monitor_path
|
||||
fi
|
||||
unzip -o $agent_src -d $monitor_path/ > /dev/null
|
||||
if [ ! -f $run_bin ];then
|
||||
ls -lh $agent_src
|
||||
Red_Error "ERROR: Failed to download, please try install again!" "ERROR: 下载堡塔云监控主控端失败,请尝试重新安装!"
|
||||
fi
|
||||
fi
|
||||
rm -rf $agent_src
|
||||
chmod +x $monitor_path/BT-MONITOR
|
||||
chmod +x $monitor_path/tools.py
|
||||
wget -O /etc/init.d/btm ${download_Url}/init/btmonitor.init -t 5 -T 10
|
||||
# \cp -r $monitor_path/init.sh /etc/init.d/btm
|
||||
|
||||
chmod +x /etc/init.d/btm
|
||||
ln -sf /etc/init.d/btm /usr/bin/btm
|
||||
|
||||
if [ ! -f $monitor_path/data/user.json ]; then
|
||||
echo "{\"uid\":1,\"username\":\"Administrator\",\"ip\":\"127.0.0.1\",\"server_id\":\"1\",\"access_key\":\"test\",\"secret_key\":\"123456\"}" > $monitor_path/data/user.json
|
||||
fi
|
||||
if [ -f $monitor_path/core/include/c_loader/PluginLoader.so ]; then
|
||||
rm -f $monitor_path/core/include/c_loader/PluginLoader.so
|
||||
fi
|
||||
}
|
||||
|
||||
Start_Monitor(){
|
||||
/etc/init.d/btm start
|
||||
if [ "$?" != "0" ]; then
|
||||
#echo "堡塔云监控主控端启动失败!"
|
||||
tail $monitor_path/logs/error.log
|
||||
Red_Error "堡塔云监控主控端启动失败!"
|
||||
fi
|
||||
|
||||
echo "正在初始化云监控主控端..."
|
||||
if [ "$re_install" == "1" ] || [ "$re_install" == "2" ]; then
|
||||
user_pass=`$setup_path/server/bt-monitor/tools.py reset_pwd`
|
||||
password=`echo $user_pass |awk '{print $3}'`
|
||||
else
|
||||
user_pass=`$monitor_path/tools.py create_admin`
|
||||
password=`echo $user_pass |awk -F " " '{print $5}'`
|
||||
for ((i=1; i<=5; i++));do
|
||||
if [ -z "$password" ]; then
|
||||
sleep 7
|
||||
user_pass=`$monitor_path/tools.py create_admin`
|
||||
password=`echo $user_pass |awk -F " " '{print $5}'`
|
||||
else
|
||||
i=5
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [[ "$password" == "" ]];then
|
||||
Red_Error "ERROR: 初始化云监控主控端失败,请尝试重新安装!"
|
||||
fi
|
||||
|
||||
c_path=$(cat /www/server/bt-monitor/config/config.json |awk -F '\"admin_path\"' '{print $2}'|awk -F ":" '{print $2}'|awk -F '"' '{print $2}')
|
||||
adminpath=$(echo $c_path|awk -F ',' '{print $1}')
|
||||
|
||||
if [ -d "/usr/bin/btmonitoragent" ];then
|
||||
rm -rf /usr/bin/btmonitoragent
|
||||
fi
|
||||
|
||||
date_f=`date '+%Y%m%d_%H%M%S'`
|
||||
md5_pl=`echo $date_f | md5sum | head -c 32`
|
||||
token_pl=`cat $monitor_path/config/token.pl 2>&1`
|
||||
if [ "$token_pl" == ' ' ] || [ ! -f $monitor_path/config/token.pl ]; then
|
||||
echo "$md5_pl" > $monitor_path/config/token.pl
|
||||
fi
|
||||
|
||||
echo "正在给本机安装云监控被控端,请等待..."
|
||||
sleep 15
|
||||
curl -sSO ${download_Url}/install/btmonitoragent.sh && bash btmonitoragent.sh https://127.0.0.1:806 $md5_pl
|
||||
target_dir="/usr/local/btmonitoragent"
|
||||
if [ ! -f "$target_dir/BT-MonitorAgent" ];then
|
||||
tail -n 10 ${monitor_path}/logs/error.log
|
||||
echo ""
|
||||
ps aux|grep -v grep|grep ${monitor_path}
|
||||
netstat -tulnp|grep ${panelPort}
|
||||
/etc/init.d/btm restart
|
||||
if [ "$?" -eq 0 ]; then
|
||||
echo -e "\033[31m安装云监控被控端失败,正在尝试重新安装!\033[0m"
|
||||
sleep 15
|
||||
curl -sSO ${download_Url}/install/btmonitoragent.sh && bash btmonitoragent.sh https://127.0.0.1:806 $md5_pl
|
||||
if [ ! -f "$target_dir/BT-MonitorAgent" ];then
|
||||
Red_Error "ERROR: 安装云监控被控端失败,请尝试重新安装!"
|
||||
fi
|
||||
else
|
||||
Red_Error "ERROR: 安装云监控被控端失败,请尝试重新安装!"
|
||||
fi
|
||||
fi
|
||||
/etc/init.d/btm restart > /dev/null 2>&1
|
||||
|
||||
}
|
||||
|
||||
Set_Firewall(){
|
||||
sshPort=$(cat /etc/ssh/sshd_config | grep 'Port '|awk '{print $2}')
|
||||
if [ "${PM}" = "apt-get" ]; then
|
||||
apt-get install -y ufw
|
||||
if [ -f "/usr/sbin/ufw" ];then
|
||||
ufw allow 22/tcp
|
||||
ufw allow ${panelPort}/tcp
|
||||
ufw allow ${sshPort}/tcp
|
||||
ufw_status=`ufw status`
|
||||
echo y|ufw enable
|
||||
ufw default deny
|
||||
ufw reload
|
||||
fi
|
||||
else
|
||||
if [ -f "/etc/init.d/iptables" ];then
|
||||
iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
|
||||
iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ${panelPort} -j ACCEPT
|
||||
iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ${sshPort} -j ACCEPT
|
||||
iptables -A INPUT -p icmp --icmp-type any -j ACCEPT
|
||||
iptables -A INPUT -s localhost -d localhost -j ACCEPT
|
||||
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
iptables -P INPUT DROP
|
||||
service iptables save
|
||||
sed -i "s#IPTABLES_MODULES=\"\"#IPTABLES_MODULES=\"ip_conntrack_netbios_ns ip_conntrack_ftp ip_nat_ftp\"#" /etc/sysconfig/iptables-config
|
||||
iptables_status=$(service iptables status | grep 'not running')
|
||||
if [ "${iptables_status}" == '' ];then
|
||||
service iptables restart
|
||||
fi
|
||||
else
|
||||
AliyunCheck=$(cat /etc/redhat-release|grep "Aliyun Linux")
|
||||
[ "${AliyunCheck}" ] && return
|
||||
yum install firewalld -y
|
||||
[ "${Centos8Check}" ] && yum reinstall python3-six -y
|
||||
systemctl enable firewalld
|
||||
systemctl start firewalld
|
||||
firewall-cmd --set-default-zone=public > /dev/null 2>&1
|
||||
firewall-cmd --permanent --zone=public --add-port=22/tcp > /dev/null 2>&1
|
||||
firewall-cmd --permanent --zone=public --add-port=${panelPort}/tcp > /dev/null 2>&1
|
||||
firewall-cmd --permanent --zone=public --add-port=${sshPort}/tcp > /dev/null 2>&1
|
||||
firewall-cmd --reload
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
Service_Add(){
|
||||
if [ $Command_Exists systemctl ]; then
|
||||
wget -O /usr/lib/systemd/system/btm.service ${download_Url}/init/systemd/btmonitor.service -t 5 -T 10
|
||||
systemctl daemon-reload
|
||||
systemctl enable btm
|
||||
else
|
||||
if [ "${PM}" == "yum" ] || [ "${PM}" == "dnf" ]; then
|
||||
chkconfig --add btm
|
||||
chkconfig --level 2345 btm on
|
||||
elif [ "${PM}" == "apt-get" ]; then
|
||||
update-rc.d btm defaults
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
Service_Del(){
|
||||
if [ $Command_Exists systemctl ]; then
|
||||
rm -rf /usr/lib/systemd/system/btm.service
|
||||
systemctl disable btm
|
||||
else
|
||||
if [ "${PM}" == "yum" ] || [ "${PM}" == "dnf" ]; then
|
||||
chkconfig --del btm
|
||||
chkconfig --level 2345 btm off
|
||||
elif [ "${PM}" == "apt-get" ]; then
|
||||
update-rc.d btm remove
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
Get_Ip_Address(){
|
||||
getIpAddress=""
|
||||
getIpAddress=$(curl -sS --connect-timeout 10 -m 60 https://www.bt.cn/Api/getIpAddress)
|
||||
if [ -z "${getIpAddress}" ] || [ "${getIpAddress}" = "0.0.0.0" ]; then
|
||||
isHosts=$(cat /etc/hosts|grep 'www.bt.cn')
|
||||
if [ -z "${isHosts}" ];then
|
||||
echo "" >> /etc/hosts
|
||||
echo "116.213.43.206 www.bt.cn" >> /etc/hosts
|
||||
getIpAddress=$(curl -sS --connect-timeout 10 -m 60 https://www.bt.cn/Api/getIpAddress)
|
||||
if [ -z "${getIpAddress}" ];then
|
||||
sed -i "/bt.cn/d" /etc/hosts
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
ipv4Check=$($python_bin -c "import re; print(re.match('^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$','${getIpAddress}'))")
|
||||
if [ "${ipv4Check}" == "None" ];then
|
||||
ipv6Address=$(echo ${getIpAddress}|tr -d "[]")
|
||||
ipv6Check=$($python_bin -c "import re; print(re.match('^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$','${ipv6Address}'))")
|
||||
if [ "${ipv6Check}" == "None" ]; then
|
||||
getIpAddress="SERVER_IP"
|
||||
else
|
||||
echo "True" > ${setup_path}/server/bt-monitor/data/ipv6.pl
|
||||
sleep 1
|
||||
/etc/init.d/btm restart
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${getIpAddress}" != "SERVER_IP" ];then
|
||||
echo "${getIpAddress}" > ${setup_path}/server/bt-monitor/data/iplist.txt
|
||||
fi
|
||||
LOCAL_IP=$(ip addr | grep -E -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -E -v "^127\.|^255\.|^0\." | head -n 1)
|
||||
}
|
||||
|
||||
System_Check(){
|
||||
if [ -f "$monitor_path/BT-MONITOR" ] || [ -f "$monitor_path/tools.py" ] || [ -f "/etc/init.d/btm" ];then
|
||||
Install_Check
|
||||
elif [ -d "$old_dir" ];then
|
||||
Rev_Install_Check
|
||||
fi
|
||||
}
|
||||
|
||||
Install_Check(){
|
||||
echo -e "----------------------------------------------------"
|
||||
echo -e "检测到已存在堡塔云监控系统,请按照选项选择安装方式!"
|
||||
echo -e "1) 覆盖安装:保存原有监控配置及数据并安装堡塔云监控"
|
||||
echo -e "\033[33m2) 全新安装:清空原有监控配置及数据并安装堡塔云监控\033[0m"
|
||||
echo -e "----------------------------------------------------"
|
||||
read -p "请输入对应选项[1|2]进行安装或输入任意内容退出安装: " yes;
|
||||
if [ "$yes" == "1" ]; then
|
||||
re_install="1"
|
||||
echo "即将卸载并重装本机的堡塔云监控被控端..."
|
||||
Uninstall_agent
|
||||
elif [ "$yes" == "2" ]; then
|
||||
Backup_Monitor
|
||||
echo "即将卸载并重装本机的堡塔云监控被控端..."
|
||||
Uninstall_agent
|
||||
else
|
||||
echo -e "------------"
|
||||
echo "取消安装"
|
||||
exit;
|
||||
fi
|
||||
}
|
||||
|
||||
Rev_Install_Check(){
|
||||
echo -e "----------------------------------------------------"
|
||||
echo -e "\033[33m检测到上一次卸载云监控时保留的旧数据,请按照选项选择安装方式!\033[0m"
|
||||
echo -e "1) 还原以前的备份并安装堡塔云监控系统!"
|
||||
echo -e "2) 不使用原有备份,全新安装堡塔云监控系统!"
|
||||
echo -e "----------------------------------------------------"
|
||||
read -p "请输入对应选项[1|2]进行安装或输入任意内容退出安装: " yes;
|
||||
if [ "$yes" == "1" ]; then
|
||||
re_install="2"
|
||||
echo "开始安装堡塔云监控系统并还原数据..."
|
||||
elif [ "$yes" == "2" ]; then
|
||||
echo "开始全新安装堡塔云监控系统..."
|
||||
else
|
||||
echo -e "------------"
|
||||
echo "取消安装"
|
||||
exit;
|
||||
fi
|
||||
}
|
||||
|
||||
Backup_Monitor(){
|
||||
if [ -f "/etc/init.d/btm" ]; then
|
||||
/etc/init.d/btm stop
|
||||
sleep 1
|
||||
fi
|
||||
if [ ! -d "${old_dir}" ];then
|
||||
mkdir -p ${old_dir}
|
||||
else
|
||||
mv ${old_dir} ${old_dir}_$(date +%Y_%m_%d_%H_%M_%S)
|
||||
mkdir -p ${old_dir}
|
||||
fi
|
||||
|
||||
mv ${monitor_path}/data ${old_dir}/data
|
||||
mv ${monitor_path}/config ${old_dir}/config
|
||||
mv ${monitor_path}/ssl ${old_dir}/ssl
|
||||
}
|
||||
|
||||
Reinstall_Monitor(){
|
||||
rm -rf $monitor_path/data
|
||||
rm -rf $monitor_path/config
|
||||
rm -rf $monitor_path/ssl
|
||||
mv $old_dir/data $monitor_path/data
|
||||
mv $old_dir/config $monitor_path/config
|
||||
mv $old_dir/ssl $monitor_path/ssl
|
||||
rm -rf $old_dir
|
||||
}
|
||||
|
||||
Get_Pack_Manager(){
|
||||
if [ -f "/usr/bin/yum" ] && [ -d "/etc/yum.repos.d" ]; then
|
||||
PM="yum"
|
||||
elif [ -f "/usr/bin/apt-get" ] && [ -f "/usr/bin/dpkg" ]; then
|
||||
PM="apt-get"
|
||||
fi
|
||||
}
|
||||
|
||||
Install_RPM_Pack(){
|
||||
yumPacks="wget curl unzip gcc gcc-c++ make libcurl-devel openssl-devel xz-devel python-backports-lzma xz crontabs zlib zlib-devel sqlite-devel libffi-devel bzip2-devel lsof net-tools"
|
||||
yum install -y ${yumPacks}
|
||||
|
||||
for yumPack in ${yumPacks}
|
||||
do
|
||||
rpmPack=$(rpm -q ${yumPack})
|
||||
packCheck=$(echo ${rpmPack}|grep not)
|
||||
if [ "${packCheck}" ]; then
|
||||
yum install ${yumPack} -y
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
Install_Deb_Pack(){
|
||||
debPacks="wget curl unzip gcc g++ make cron libcurl4-openssl-dev libssl-dev liblzma-dev xz-utils libffi-dev libbz2-dev libsqlite3-dev libreadline-dev libgdbm-dev python3-bsddb3 tk-dev ncurses-dev uuid-dev zlib1g zlib1g-dev lsof net-tools";
|
||||
apt-get update -y
|
||||
apt-get install -y $debPacks --force-yes
|
||||
|
||||
for debPack in ${debPacks}
|
||||
do
|
||||
packCheck=$(dpkg -l ${debPack})
|
||||
if [ "$?" -ne "0" ] ;then
|
||||
apt-get install -y $debPack
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
Check_Sys_Write(){
|
||||
echo "正在检测系统关键目录是否可写"
|
||||
if [ ! -d "/etc/init.d" ];then
|
||||
mkdir -p /etc/init.d
|
||||
fi
|
||||
if [ -f /usr/bin/which ];then
|
||||
Get_Pack_Manager
|
||||
if [ "$PM" == "yum" ]; then
|
||||
read_dir="/usr/lib/systemd/system/ /etc/init.d/ /var/spool/cron/"
|
||||
else
|
||||
read_dir="/usr/lib/systemd/system/ /etc/init.d/ /var/spool/cron/crontabs/"
|
||||
fi
|
||||
|
||||
for dir in ${read_dir[@]}
|
||||
do
|
||||
if [[ -d "$dir" ]]; then
|
||||
touch $dir/btm_install_test_111.pl
|
||||
state=$(echo $?)
|
||||
if [[ "$state" != "0" ]];then
|
||||
echo -e "\033[31m错误:检测到系统关键目录不可写! $read_dir \033[0m"
|
||||
echo "1、如果安装了[宝塔系统加固],请先临时关闭"
|
||||
echo "2、如果安装了云锁,请临时关闭[系统加固]功能"
|
||||
echo "3、如果安装了安全狗,请临时关闭[系统防护]功能"
|
||||
echo "4、如果使用了其它安全软件,请先卸载 "
|
||||
echo -e "5、如果使用了禁止写入命令,请执行命令取消禁止写入:\n chattr -iaR $read_dir "
|
||||
echo ""
|
||||
echo -e "\033[31m解决以上问题后,请尝试重新安装! \033[0m"
|
||||
echo -e "如果无法解决请截图以上报错信息发帖至论坛www.bt.cn/bbs求助"
|
||||
exit 1
|
||||
else
|
||||
rm -f $dir/btm_install_test_111.pl
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
Check_Sys_Packs(){
|
||||
echo "正在检查系统中是否存在必备的依赖包"
|
||||
Packs="wget curl unzip gcc make"
|
||||
for pack in ${Packs[@]}
|
||||
do
|
||||
check_pack=$(which $pack)
|
||||
#echo $check_pack
|
||||
if [[ "$check_pack" == "" ]]; then
|
||||
echo -e "\033[31mERROR: $pack 命令不存在,尝试以下解决方法:\033[0m"
|
||||
if [ "$PM" == "yum" ]; then
|
||||
echo 1、使用命令重新安装依赖包:yum reinstall -y ${Packs}
|
||||
else
|
||||
echo 1、使用命令重新安装依赖包:apt-get reinstall -y ${Packs}
|
||||
fi
|
||||
echo -e "2、检查系统源是否可用?尝试更换可用的源参考教程:\n https://www.bt.cn/bbs/thread-58005-1-1.html "
|
||||
echo ""
|
||||
echo -e "\033[31m解决以上问题后,请尝试重新安装! \033[0m"
|
||||
echo -e "如果无法解决请截图以上报错信息发帖至论坛www.bt.cn/bbs求助"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
Install_Main(){
|
||||
startTime=`date +%s`
|
||||
Check_Sys_Write
|
||||
System_Check
|
||||
Get_Pack_Manager
|
||||
get_node_url
|
||||
|
||||
if [ "$PM" == "yum" ]; then
|
||||
Install_RPM_Pack
|
||||
else
|
||||
Install_Deb_Pack
|
||||
fi
|
||||
Check_Sys_Packs
|
||||
Install_Python_Lib
|
||||
Install_Monitor
|
||||
Set_Firewall
|
||||
Get_Ip_Address
|
||||
Service_Add
|
||||
if [ "$re_install" == "2" ]; then
|
||||
Reinstall_Monitor
|
||||
fi
|
||||
Start_Monitor
|
||||
}
|
||||
|
||||
Uninstall_Monitor(){
|
||||
pkill BT-MONITOR
|
||||
/etc/init.d/btm stop
|
||||
|
||||
if [ -f "/www/server/bt-monitor/sqlite-server.sh" ]; then
|
||||
chmod +x /www/server/bt-monitor/sqlite-server.sh
|
||||
/www/server/bt-monitor/sqlite-server.sh stop
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
Service_Del
|
||||
|
||||
rm -rf $monitor_path
|
||||
rm -rf /usr/bin/btm
|
||||
rm -rf /etc/init.d/btm
|
||||
|
||||
echo -e "堡塔云监控主控端卸载成功!"
|
||||
}
|
||||
|
||||
Uninstall_agent(){
|
||||
get_node_url
|
||||
if [ -f "/tmp/btmonitoragent.sh" ];then
|
||||
rm -rf /tmp/btmonitoragent.sh
|
||||
fi
|
||||
curl -o /tmp/btmonitoragent.sh -sSO ${download_Url}/install/btmonitoragent.sh && bash /tmp/btmonitoragent.sh uninstall
|
||||
}
|
||||
|
||||
action="${1}"
|
||||
if [ "$action" == "uninstall" ];then
|
||||
echo -e "----------------------------------------------------"
|
||||
echo -e "\033[33m检测到您正在卸载堡塔云监控系统,请按照选项选择卸载方式!\033[0m"
|
||||
echo -e "1) 备份数据后卸载:保存原有监控配置及数据并卸载堡塔云监控系统"
|
||||
echo -e "2) 完全卸载:清空原有监控配置及数据并卸载堡塔云监控系统"
|
||||
echo -e "----------------------------------------------------"
|
||||
read -p "请输入对应选项[1|2]进行卸载或输入任意内容退出卸载: " yes;
|
||||
if [ "$yes" == "1" ]; then
|
||||
Backup_Monitor
|
||||
echo -e "----------------------------------------------------"
|
||||
echo -e "\033[33m已备份原有监控数据至: ${old_dir}\033[0m"
|
||||
elif [ "$yes" == "2" ]; then
|
||||
echo "正在清空堡塔云监控系统数据..."
|
||||
else
|
||||
echo -e "------------"
|
||||
echo "取消卸载"
|
||||
exit;
|
||||
fi
|
||||
Uninstall_agent
|
||||
Uninstall_Monitor
|
||||
exit 0
|
||||
else
|
||||
echo "
|
||||
+----------------------------------------------------------------------
|
||||
| Bt-Monitor FOR CentOS/Ubuntu/Debian
|
||||
+----------------------------------------------------------------------
|
||||
| Copyright © 2015-2099 BT-SOFT(https://www.bt.cn) All rights reserved.
|
||||
+----------------------------------------------------------------------
|
||||
| The Monitor URL will be https://SERVER_IP:806 when installed.
|
||||
+----------------------------------------------------------------------
|
||||
"
|
||||
while [ "$go" != 'y' ] && [ "$go" != 'n' ]
|
||||
do
|
||||
read -p "Do you want to install Bt-Monitor to the $setup_path directory now?(y/n): " go;
|
||||
done
|
||||
|
||||
if [ "$go" == 'n' ];then
|
||||
exit;
|
||||
fi
|
||||
Install_Main
|
||||
#curl -o /dev/null -fsSL --connect-time 10 "https://api.bt.cn/bt_monitor/setup_count?cloud_type=1&token=$md5_pl&src_code=$1"
|
||||
#curl -o /dev/null -fsSL --connect-time 10 "https://api.bt.cn/bt_monitor/setup_count?cloud_type=1&token=$md5_pl&src_code=$1&status=1"
|
||||
|
||||
fi
|
||||
echo -e "=================================================================="
|
||||
echo -e "\033[32m堡塔云监控主控端安装完成! Installed successfully!\033[0m"
|
||||
echo -e "=================================================================="
|
||||
echo "外网访问地址: https://${getIpAddress}:${panelPort}${adminpath}"
|
||||
echo "内网访问地址: https://${LOCAL_IP}:${panelPort}${adminpath}"
|
||||
echo -e "username: admin"
|
||||
echo -e "password: $password"
|
||||
echo -e "\033[33mIf you cannot access the Monitor,\033[0m"
|
||||
echo -e "\033[33mrelease the following Monitor port [${panelPort}] in the security group\033[0m"
|
||||
echo -e "\033[33m若无法访问堡塔云监控主控端,请检查防火墙/安全组是否有放行[${panelPort}]端口\033[0m"
|
||||
echo -e "=================================================================="
|
||||
|
||||
endTime=`date +%s`
|
||||
((outTime=($endTime-$startTime)/60))
|
||||
echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
|
150
public/install/public.sh
Normal file
@ -0,0 +1,150 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
pyenv_bin=/www/server/panel/pyenv/bin
|
||||
rep_path=${pyenv_bin}:$PATH
|
||||
if [ -d "$pyenv_bin" ];then
|
||||
PATH=$rep_path
|
||||
fi
|
||||
export PATH
|
||||
export LANG=en_US.UTF-8
|
||||
export LANGUAGE=en_US:en
|
||||
|
||||
get_node_url(){
|
||||
nodes=(https://dg2.bt.cn https://download.bt.cn https://ctcc1-node.bt.cn https://cmcc1-node.bt.cn https://ctcc2-node.bt.cn https://hk1-node.bt.cn https://na1-node.bt.cn https://jp1-node.bt.cn);
|
||||
|
||||
if [ "$1" ];then
|
||||
nodes=($(echo ${nodes[*]}|sed "s#${1}##"))
|
||||
fi
|
||||
|
||||
tmp_file1=/dev/shm/net_test1.pl
|
||||
tmp_file2=/dev/shm/net_test2.pl
|
||||
[ -f "${tmp_file1}" ] && rm -f ${tmp_file1}
|
||||
[ -f "${tmp_file2}" ] && rm -f ${tmp_file2}
|
||||
touch $tmp_file1
|
||||
touch $tmp_file2
|
||||
for node in ${nodes[@]};
|
||||
do
|
||||
NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${node}/net_test|xargs)
|
||||
RES=$(echo ${NODE_CHECK}|awk '{print $1}')
|
||||
NODE_STATUS=$(echo ${NODE_CHECK}|awk '{print $2}')
|
||||
TIME_TOTAL=$(echo ${NODE_CHECK}|awk '{print $3 * 1000 - 500 }'|cut -d '.' -f 1)
|
||||
if [ "${NODE_STATUS}" == "200" ];then
|
||||
if [ $TIME_TOTAL -lt 300 ];then
|
||||
if [ $RES -ge 1500 ];then
|
||||
echo "$RES $node" >> $tmp_file1
|
||||
fi
|
||||
else
|
||||
if [ $RES -ge 1500 ];then
|
||||
echo "$TIME_TOTAL $node" >> $tmp_file2
|
||||
fi
|
||||
fi
|
||||
|
||||
i=$(($i+1))
|
||||
if [ $TIME_TOTAL -lt 200 ];then
|
||||
if [ $RES -ge 3000 ];then
|
||||
break;
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
NODE_URL=$(cat $tmp_file1|sort -r -g -t " " -k 1|head -n 1|awk '{print $2}')
|
||||
if [ -z "$NODE_URL" ];then
|
||||
NODE_URL=$(cat $tmp_file2|sort -g -t " " -k 1|head -n 1|awk '{print $2}')
|
||||
if [ -z "$NODE_URL" ];then
|
||||
NODE_URL='https://download.bt.cn';
|
||||
fi
|
||||
fi
|
||||
rm -f $tmp_file1
|
||||
rm -f $tmp_file2
|
||||
}
|
||||
|
||||
GetCpuStat(){
|
||||
time1=$(cat /proc/stat |grep 'cpu ')
|
||||
sleep 1
|
||||
time2=$(cat /proc/stat |grep 'cpu ')
|
||||
cpuTime1=$(echo ${time1}|awk '{print $2+$3+$4+$5+$6+$7+$8}')
|
||||
cpuTime2=$(echo ${time2}|awk '{print $2+$3+$4+$5+$6+$7+$8}')
|
||||
runTime=$((${cpuTime2}-${cpuTime1}))
|
||||
idelTime1=$(echo ${time1}|awk '{print $5}')
|
||||
idelTime2=$(echo ${time2}|awk '{print $5}')
|
||||
idelTime=$((${idelTime2}-${idelTime1}))
|
||||
useTime=$(((${runTime}-${idelTime})*3))
|
||||
[ ${useTime} -gt ${runTime} ] && cpuBusy="true"
|
||||
if [ "${cpuBusy}" == "true" ]; then
|
||||
cpuCore=$((${cpuInfo}/2))
|
||||
else
|
||||
cpuCore=$((${cpuInfo}-1))
|
||||
fi
|
||||
}
|
||||
GetPackManager(){
|
||||
if [ -f "/usr/bin/yum" ] && [ -f "/etc/yum.conf" ]; then
|
||||
PM="yum"
|
||||
elif [ -f "/usr/bin/apt-get" ] && [ -f "/usr/bin/dpkg" ]; then
|
||||
PM="apt-get"
|
||||
fi
|
||||
}
|
||||
|
||||
bt_check(){
|
||||
p_path=/www/server/panel/class/panelPlugin.py
|
||||
if [ -f $p_path ];then
|
||||
is_ext=$(cat $p_path|grep btwaf)
|
||||
if [ "$is_ext" != "" ];then
|
||||
send_check
|
||||
fi
|
||||
fi
|
||||
|
||||
p_path=/www/server/panel/BTPanel/templates/default/index.html
|
||||
if [ -f $p_path ];then
|
||||
is_ext=$(cat $p_path|grep fbi)
|
||||
if [ "$is_ext" != "" ];then
|
||||
send_check
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
send_check(){
|
||||
chattr -i /etc/init.d/bt
|
||||
chmod +x /etc/init.d/bt
|
||||
p_path2=/www/server/panel/class/common.py
|
||||
p_version=$(cat $p_path2|grep "version = "|awk '{print $3}'|tr -cd [0-9.])
|
||||
curl -sS --connect-timeout 3 -m 60 https://www.bt.cn/api/panel/notpro?version=$p_version
|
||||
NODE_URL=""
|
||||
exit 0;
|
||||
}
|
||||
GetSysInfo(){
|
||||
if [ "${PM}" = "yum" ]; then
|
||||
SYS_VERSION=$(cat /etc/redhat-release)
|
||||
elif [ "${PM}" = "apt-get" ]; then
|
||||
SYS_VERSION=$(cat /etc/issue)
|
||||
fi
|
||||
SYS_INFO=$(uname -msr)
|
||||
SYS_BIT=$(getconf LONG_BIT)
|
||||
MEM_TOTAL=$(free -m|grep Mem|awk '{print $2}')
|
||||
CPU_INFO=$(getconf _NPROCESSORS_ONLN)
|
||||
GCC_VER=$(gcc -v 2>&1|grep "gcc version"|awk '{print $3}')
|
||||
CMAKE_VER=$(cmake --version|grep version|awk '{print $3}')
|
||||
|
||||
echo -e ${SYS_VERSION}
|
||||
echo -e Bit:${SYS_BIT} Mem:${MEM_TOTAL}M Core:${CPU_INFO} gcc:${GCC_VER} cmake:${CMAKE_VER}
|
||||
echo -e ${SYS_INFO}
|
||||
}
|
||||
cpuInfo=$(getconf _NPROCESSORS_ONLN)
|
||||
if [ "${cpuInfo}" -ge "4" ];then
|
||||
GetCpuStat
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
GetPackManager
|
||||
|
||||
if [ ! $NODE_URL ];then
|
||||
EN_CHECK=$(cat /www/server/panel/config/config.json |grep English)
|
||||
if [ -z "${EN_CHECK}" ];then
|
||||
echo '正在选择下载节点...';
|
||||
else
|
||||
echo "selecting download node...";
|
||||
fi
|
||||
get_node_url
|
||||
fi
|
||||
|
||||
|
BIN
public/install/src/bt-monitor-2.2.5.zip
Normal file
BIN
public/install/src/panel6.zip
Normal file
BIN
public/install/update/LinuxPanel-8.0.1.zip
Normal file
127
public/install/update6.sh
Normal file
@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
LANG=en_US.UTF-8
|
||||
|
||||
Btapi_Url='http://www.example.com'
|
||||
|
||||
if [ ! -d /www/server/panel/BTPanel ];then
|
||||
echo "============================================="
|
||||
echo "错误, 5.x不可以使用此命令升级!"
|
||||
echo "5.9平滑升级到6.0的命令:curl http://download.bt.cn/install/update_to_6.sh|bash"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
if [ ! -f "/www/server/panel/pyenv/bin/python3" ];then
|
||||
echo "============================================="
|
||||
echo "错误, 当前面板过旧/py-2.7/无pyenv环境,无法升级至最新版面板"
|
||||
echo "请截图发帖至论坛www.bt.cn/bbs求助"
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
public_file=/www/server/panel/install/public.sh
|
||||
if [ ! -f $public_file ];then
|
||||
wget -O Tpublic.sh $Btapi_Url/install/public.sh -T 20;
|
||||
fi
|
||||
. $public_file
|
||||
|
||||
Centos8Check=$(cat /etc/redhat-release | grep ' 8.' | grep -iE 'centos|Red Hat')
|
||||
if [ "${Centos8Check}" ];then
|
||||
if [ ! -f "/usr/bin/python" ] && [ -f "/usr/bin/python3" ] && [ ! -d "/www/server/panel/pyenv" ]; then
|
||||
ln -sf /usr/bin/python3 /usr/bin/python
|
||||
fi
|
||||
fi
|
||||
|
||||
mypip="pip"
|
||||
env_path=/www/server/panel/pyenv/bin/activate
|
||||
if [ -f $env_path ];then
|
||||
mypip="/www/server/panel/pyenv/bin/pip"
|
||||
fi
|
||||
|
||||
download_Url=$NODE_URL
|
||||
setup_path=/www
|
||||
version=$(curl -Ss --connect-timeout 5 -m 2 $Btapi_Url/api/panel/get_version)
|
||||
if [ "$version" = '' ];then
|
||||
version='7.9.9'
|
||||
fi
|
||||
armCheck=$(uname -m|grep arm)
|
||||
if [ "${armCheck}" ];then
|
||||
version='7.7.0'
|
||||
fi
|
||||
|
||||
if [ "$1" ];then
|
||||
version=$1
|
||||
fi
|
||||
wget -T 5 -O /tmp/panel.zip $Btapi_Url/install/update/LinuxPanel-${version}.zip
|
||||
dsize=$(du -b /tmp/panel.zip|awk '{print $1}')
|
||||
if [ $dsize -lt 10240 ];then
|
||||
echo "获取更新包失败,请稍后更新或联系宝塔运维"
|
||||
exit;
|
||||
fi
|
||||
unzip -o /tmp/panel.zip -d $setup_path/server/ > /dev/null
|
||||
rm -f /tmp/panel.zip
|
||||
cd $setup_path/server/panel/
|
||||
check_bt=`cat /etc/init.d/bt`
|
||||
if [ "${check_bt}" = "" ];then
|
||||
rm -f /etc/init.d/bt
|
||||
wget -O /etc/init.d/bt $download_Url/install/src/bt7.init -T 20
|
||||
chmod +x /etc/init.d/bt
|
||||
fi
|
||||
rm -f /www/server/panel/*.pyc
|
||||
rm -f /www/server/panel/class/*.pyc
|
||||
#pip install flask_sqlalchemy
|
||||
#pip install itsdangerous==0.24
|
||||
|
||||
pip_list=$($mypip list)
|
||||
request_v=$(btpip list 2>/dev/null|grep "requests "|awk '{print $2}'|cut -d '.' -f 2)
|
||||
if [ "$request_v" = "" ] || [ "${request_v}" -gt "28" ];then
|
||||
$mypip install requests==2.27.1
|
||||
fi
|
||||
|
||||
openssl_v=$(echo "$pip_list"|grep pyOpenSSL)
|
||||
if [ "$openssl_v" = "" ];then
|
||||
$mypip install pyOpenSSL
|
||||
fi
|
||||
|
||||
#cffi_v=$(echo "$pip_list"|grep cffi|grep 1.12.)
|
||||
#if [ "$cffi_v" = "" ];then
|
||||
# $mypip install cffi==1.12.3
|
||||
#fi
|
||||
|
||||
pymysql=$(echo "$pip_list"|grep pymysql)
|
||||
if [ "$pymysql" = "" ];then
|
||||
$mypip install pymysql
|
||||
fi
|
||||
|
||||
pymysql=$(echo "$pip_list"|grep pycryptodome)
|
||||
if [ "$pymysql" = "" ];then
|
||||
$mypip install pycryptodome
|
||||
fi
|
||||
|
||||
#psutil=$(echo "$pip_list"|grep psutil|awk '{print $2}'|grep '5.7.')
|
||||
#if [ "$psutil" = "" ];then
|
||||
# $mypip install -U psutil
|
||||
#fi
|
||||
|
||||
if [ -d /www/server/panel/class/BTPanel ];then
|
||||
rm -rf /www/server/panel/class/BTPanel
|
||||
fi
|
||||
rm -f /www/server/panel/class/*.so
|
||||
if [ ! -f /www/server/panel/data/not_workorder.pl ]; then
|
||||
echo "True" > /www/server/panel/data/not_workorder.pl
|
||||
fi
|
||||
if [ ! -f /www/server/panel/data/userInfo.json ]; then
|
||||
echo "{\"uid\":1,\"username\":\"Administrator\",\"address\":\"127.0.0.1\",\"serverid\":\"1\",\"access_key\":\"test\",\"secret_key\":\"123456\",\"ukey\":\"123456\",\"state\":1}" > /www/server/panel/data/userInfo.json
|
||||
fi
|
||||
|
||||
chattr -i /etc/init.d/bt
|
||||
chmod +x /etc/init.d/bt
|
||||
echo "====================================="
|
||||
rm -f /dev/shm/bt_sql_tips.pl
|
||||
kill $(ps aux|grep -E "task.pyc|main.py"|grep -v grep|awk '{print $2}')
|
||||
/etc/init.d/bt restart
|
||||
echo 'True' > /www/server/panel/data/restart.pl
|
||||
pkill -9 gunicorn &
|
||||
echo "已成功升级到[$version]${Ver}";
|
||||
|
||||
|
446
public/install/update_btmonitor.sh
Normal file
@ -0,0 +1,446 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
LANG=en_US.UTF-8
|
||||
|
||||
Btapi_Url='http://www.example.com'
|
||||
|
||||
monitor_path="/www/server/bt-monitor"
|
||||
run_bin="/www/server/bt-monitor/BT-MONITOR"
|
||||
is64bit=$(getconf LONG_BIT)
|
||||
|
||||
if [ ! -d $monitor_path ]; then
|
||||
echo "没有安装云监控,请执行下面的命令安装堡塔云监控!"
|
||||
echo "curl -sSO ${Btapi_Url}/install/install_btmonitor.sh && bash install_btmonitor.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd ~
|
||||
setup_path="/www"
|
||||
|
||||
if [ -f "/etc/init.d/btm" ]; then
|
||||
/etc/init.d/btm stop
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
if [ -f "/www/server/bt-monitor/sqlite-server.sh" ]; then
|
||||
chmod +x /www/server/bt-monitor/sqlite-server.sh
|
||||
/www/server/bt-monitor/sqlite-server.sh stop
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
get_node_url(){
|
||||
if [ ! -f /bin/curl ];then
|
||||
if [ "${PM}" = "yum" ]; then
|
||||
yum install curl -y
|
||||
elif [ "${PM}" = "apt-get" ]; then
|
||||
apt-get install curl -y
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "/www/node.pl" ];then
|
||||
download_Url=$(cat /www/node.pl)
|
||||
echo "Download node: $download_Url";
|
||||
echo '---------------------------------------------';
|
||||
return
|
||||
fi
|
||||
|
||||
echo '---------------------------------------------';
|
||||
echo "Selected download node...";
|
||||
# nodes=(http://dg2.bt.cn http://dg1.bt.cn http://125.90.93.52:5880 http://36.133.1.8:5880 http://123.129.198.197 http://38.34.185.130 http://116.213.43.206:5880 http://128.1.164.196);
|
||||
#nodes=(http://dg2.bt.cn http://dg1.bt.cn http://125.90.93.52:5880 http://36.133.1.8:5880 http://123.129.198.197 http://116.213.43.206:5880);
|
||||
nodes=(https://dg2.bt.cn https://dg1.bt.cn https://download.bt.cn);
|
||||
tmp_file1=/dev/shm/net_test1.pl
|
||||
tmp_file2=/dev/shm/net_test2.pl
|
||||
[ -f "${tmp_file1}" ] && rm -f ${tmp_file1}
|
||||
[ -f "${tmp_file2}" ] && rm -f ${tmp_file2}
|
||||
touch $tmp_file1
|
||||
touch $tmp_file2
|
||||
for node in ${nodes[@]};
|
||||
do
|
||||
NODE_CHECK=$(curl --connect-timeout 3 -m 3 2>/dev/null -w "%{http_code} %{time_total}" ${node}/net_test|xargs)
|
||||
RES=$(echo ${NODE_CHECK}|awk '{print $1}')
|
||||
NODE_STATUS=$(echo ${NODE_CHECK}|awk '{print $2}')
|
||||
TIME_TOTAL=$(echo ${NODE_CHECK}|awk '{print $3 * 1000 - 500 }'|cut -d '.' -f 1)
|
||||
if [ "${NODE_STATUS}" == "200" ];then
|
||||
if [ $TIME_TOTAL -lt 100 ];then
|
||||
if [ $RES -ge 1500 ];then
|
||||
echo "$RES $node" >> $tmp_file1
|
||||
fi
|
||||
else
|
||||
if [ $RES -ge 1500 ];then
|
||||
echo "$TIME_TOTAL $node" >> $tmp_file2
|
||||
fi
|
||||
fi
|
||||
|
||||
i=$(($i+1))
|
||||
if [ $TIME_TOTAL -lt 100 ];then
|
||||
if [ $RES -ge 3000 ];then
|
||||
break;
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
NODE_URL=$(cat $tmp_file1|sort -r -g -t " " -k 1|head -n 1|awk '{print $2}')
|
||||
if [ -z "$NODE_URL" ];then
|
||||
NODE_URL=$(cat $tmp_file2|sort -g -t " " -k 1|head -n 1|awk '{print $2}')
|
||||
if [ -z "$NODE_URL" ];then
|
||||
NODE_URL='https://download.bt.cn';
|
||||
fi
|
||||
fi
|
||||
rm -f $tmp_file1
|
||||
rm -f $tmp_file2
|
||||
download_Url=$NODE_URL
|
||||
echo "Download node: $download_Url";
|
||||
echo '---------------------------------------------';
|
||||
}
|
||||
|
||||
Get_Versions(){
|
||||
redhat_version_file="/etc/redhat-release"
|
||||
deb_version_file="/etc/issue"
|
||||
if [ -f $redhat_version_file ];then
|
||||
os_type='el'
|
||||
is_aliyunos=$(cat $redhat_version_file|grep Aliyun)
|
||||
if [ "$is_aliyunos" != "" ];then
|
||||
return
|
||||
fi
|
||||
os_version=$(cat $redhat_version_file|grep CentOS|grep -Eo '([0-9]+\.)+[0-9]+'|grep -Eo '^[0-9]')
|
||||
if [ "${os_version}" = "5" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ -z "${os_version}" ];then
|
||||
os_version=$(cat /etc/redhat-release |grep Stream|grep -oE 8)
|
||||
fi
|
||||
else
|
||||
os_type='ubuntu'
|
||||
os_version=$(cat $deb_version_file|grep Ubuntu|grep -Eo '([0-9]+\.)+[0-9]+'|grep -Eo '^[0-9]+')
|
||||
if [ "${os_version}" = "" ];then
|
||||
os_type='debian'
|
||||
os_version=$(cat $deb_version_file|grep Debian|grep -Eo '([0-9]+\.)+[0-9]+'|grep -Eo '[0-9]+')
|
||||
if [ "${os_version}" = "" ];then
|
||||
os_version=$(cat $deb_version_file|grep Debian|grep -Eo '[0-9]+')
|
||||
fi
|
||||
if [ "${os_version}" = "8" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "${is64bit}" = '32' ];then
|
||||
os_version=""
|
||||
fi
|
||||
else
|
||||
if [ "$os_version" = "14" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "$os_version" = "12" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "$os_version" = "19" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "$os_version" = "21" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "$os_version" = "20" ];then
|
||||
os_version2004=$(cat /etc/issue|grep 20.04)
|
||||
if [ -z "${os_version2004}" ];then
|
||||
os_version=""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
Install_Python_Lib(){
|
||||
curl -Ss --connect-timeout 3 -m 60 $download_Url/install/pip_select.sh|bash
|
||||
pyenv_path="/www/server/bt-monitor"
|
||||
if [ -f $pyenv_path/pyenv/bin/python ];then
|
||||
is_ssl=$($python_bin -c "import ssl" 2>&1|grep cannot)
|
||||
$pyenv_path/pyenv/bin/python3.7 -V
|
||||
if [ $? -eq 0 ] && [ -z "${is_ssl}" ];then
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
is_package=$($python_bin -m psutil 2>&1|grep package)
|
||||
if [ "$is_package" = "" ];then
|
||||
wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip.txt -t 5 -T 10
|
||||
$pyenv_path/pyenv/bin/pip install -U pip
|
||||
$pyenv_path/pyenv/bin/pip install -U setuptools
|
||||
$pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt
|
||||
$pyenv_path/pyenv/bin/pip install -U flask==2.2.0
|
||||
$pyenv_path/pyenv/bin/pip install flask_sock
|
||||
$pyenv_path/pyenv/bin/pip install cachelib
|
||||
$pyenv_path/pyenv/bin/pip install py7zr
|
||||
$pyenv_path/pyenv/bin/pip install backports.lzma
|
||||
fi
|
||||
source $pyenv_path/pyenv/bin/activate
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
return
|
||||
else
|
||||
rm -rf $pyenv_path/pyenv
|
||||
fi
|
||||
fi
|
||||
|
||||
py_version="3.7.9"
|
||||
if [ ! -d "$pyenv_path" ]; then
|
||||
mkdir -p $pyenv_path
|
||||
fi
|
||||
echo "True" > /www/disk.pl
|
||||
if [ ! -w /www/disk.pl ];then
|
||||
Red_Error "ERROR: Install python env fielded." "ERROR: /www目录无法写入,请检查目录/用户/磁盘权限!"
|
||||
fi
|
||||
os_type='el'
|
||||
os_version='7'
|
||||
is_export_openssl=0
|
||||
Get_Versions
|
||||
|
||||
echo "OS: $os_type - $os_version"
|
||||
is_aarch64=$(uname -a|grep aarch64)
|
||||
if [ "$is_aarch64" != "" ];then
|
||||
is64bit="aarch64"
|
||||
fi
|
||||
|
||||
if [ -f "/www/server/bt-monitor/pymake.pl" ];then
|
||||
os_version=""
|
||||
rm -f /www/server/bt-monitor/pymake.pl
|
||||
fi
|
||||
|
||||
if [[ $os_type =~ "debian" ]] || [[ $os_type =~ "ubuntu" ]]; then
|
||||
isbtm="-btm"
|
||||
fi
|
||||
|
||||
if [ "${os_version}" != "" ];then
|
||||
pyenv_file="/www/pyenv.tar.gz"
|
||||
wget -O $pyenv_file $download_Url/install/pyenv/pyenv-${os_type}${os_version}-x${is64bit}${isbtm}.tar.gz -t 5 -T 10
|
||||
tmp_size=$(du -b $pyenv_file|awk '{print $1}')
|
||||
if [ $tmp_size -lt 703460 ];then
|
||||
rm -f $pyenv_file
|
||||
echo "ERROR: Download python env fielded."
|
||||
else
|
||||
echo "Install python env..."
|
||||
tar zxvf $pyenv_file -C $pyenv_path/ > /dev/null
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
rm -rf $pyenv_path/pyenv/bin/python
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 $pyenv_path/pyenv/bin/python
|
||||
$pyenv_path/pyenv/bin/python -m pip install --upgrade --force-reinstall pip
|
||||
$pyenv_path/pyenv/bin/pip install -U flask==2.2.0
|
||||
$pyenv_path/pyenv/bin/pip install flask_sock
|
||||
$pyenv_path/pyenv/bin/pip install cachelib
|
||||
$pyenv_path/pyenv/bin/pip install py7zr
|
||||
$pyenv_path/pyenv/bin/pip install backports.lzma
|
||||
if [ ! -f $pyenv_path/pyenv/bin/python ];then
|
||||
rm -f $pyenv_file
|
||||
Red_Error "ERROR: Install python env fielded." "ERROR: 下载堡塔云监控运行环境失败,请尝试重新安装!"
|
||||
fi
|
||||
$pyenv_path/pyenv/bin/python3.7 -V
|
||||
if [ $? -eq 0 ];then
|
||||
rm -f $pyenv_file
|
||||
ln -sf $pyenv_path/pyenv/bin/pip3.7 /usr/bin/btmpip
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 /usr/bin/btmpython
|
||||
source $pyenv_path/pyenv/bin/activate
|
||||
return
|
||||
else
|
||||
rm -f $pyenv_file
|
||||
rm -rf $pyenv_path/pyenv
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
cd /www
|
||||
python_src='/www/python_src.tar.xz'
|
||||
python_src_path="/www/Python-${py_version}"
|
||||
wget -O $python_src $download_Url/src/Python-${py_version}.tar.xz -t 5 -T 10
|
||||
tmp_size=$(du -b $python_src|awk '{print $1}')
|
||||
if [ $tmp_size -lt 10703460 ];then
|
||||
rm -f $python_src
|
||||
Red_Error "ERROR: Download python source code fielded." "ERROR: 下载堡塔云监控运行环境失败,请尝试重新安装!"
|
||||
fi
|
||||
tar xvf $python_src
|
||||
rm -f $python_src
|
||||
cd $python_src_path
|
||||
./configure --prefix=$pyenv_path/pyenv
|
||||
make -j$cpu_cpunt
|
||||
make install
|
||||
if [ ! -f $pyenv_path/pyenv/bin/python3.7 ];then
|
||||
rm -rf $python_src_path
|
||||
Red_Error "ERROR: Make python env fielded." "ERROR: 编译堡塔云监控运行环境失败!"
|
||||
fi
|
||||
cd ~
|
||||
rm -rf $python_src_path
|
||||
wget -O $pyenv_path/pyenv/bin/activate $download_Url/install/pyenv/activate.panel -t 5 -T 10
|
||||
wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip-3.7.8.txt -t 5 -T 10
|
||||
ln -sf $pyenv_path/pyenv/bin/pip3.7 $pyenv_path/pyenv/bin/pip
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 $pyenv_path/pyenv/bin/python
|
||||
ln -sf $pyenv_path/pyenv/bin/pip3.7 /usr/bin/btmpip
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 /usr/bin/btmpython
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
$pyenv_path/pyenv/bin/pip install -U pip
|
||||
$pyenv_path/pyenv/bin/pip install -U setuptools
|
||||
$pyenv_path/pyenv/bin/pip install -U wheel==0.34.2
|
||||
$pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt
|
||||
$pyenv_path/pyenv/bin/pip install -U flask==2.2.0
|
||||
$pyenv_path/pyenv/bin/pip install flask_sock
|
||||
$pyenv_path/pyenv/bin/pip install cachelib
|
||||
$pyenv_path/pyenv/bin/pip install py7zr
|
||||
$pyenv_path/pyenv/bin/pip install backports.lzma
|
||||
source $pyenv_path/pyenv/bin/activate
|
||||
|
||||
is_gevent=$($python_bin -m gevent 2>&1|grep -oE package)
|
||||
is_psutil=$($python_bin -m psutil 2>&1|grep -oE package)
|
||||
if [ "${is_gevent}" != "${is_psutil}" ];then
|
||||
Red_Error "ERROR: psutil/gevent install failed!"
|
||||
fi
|
||||
}
|
||||
|
||||
Install_Monitor(){
|
||||
version="1.0.2"
|
||||
file_name="bt-monitor"
|
||||
agent_src="bt-monitor.zip"
|
||||
|
||||
cd ~
|
||||
version=`curl -sf ${Btapi_Url}/bt_monitor/latest_version |awk -F '\"version\"' '{print $2}'|awk -F ':' '{print $2}'|awk -F '"' '{print $2}'`
|
||||
if [ -z $version ]; then
|
||||
version="1.0.2"
|
||||
fi
|
||||
new_dir="/www/server/new_btmonitor"
|
||||
if [ ! -d "$new_dir" ];then
|
||||
mkdir -p $new_dir
|
||||
fi
|
||||
if [ ! -z "$action" ]; then
|
||||
# 例如:sh update_btmonitor.sh /root/demo.zip
|
||||
if [[ "$action" =~ "zip" ]]; then
|
||||
version="指定版本"
|
||||
unzip -o $action -d $new_dir/
|
||||
else
|
||||
wget -O $agent_src ${Btapi_Url}/install/src/$file_name-$version.zip -t 5 -T 10
|
||||
unzip -o $agent_src -d $new_dir/ > /dev/null
|
||||
fi
|
||||
else
|
||||
wget -O $agent_src ${Btapi_Url}/install/src/$file_name-$version.zip -t 5 -T 10
|
||||
unzip -o $agent_src -d $new_dir/ > /dev/null
|
||||
fi
|
||||
if [ ! -f $new_dir/BT-MONITOR ];then
|
||||
ls -lh $agent_src
|
||||
Red_Error "ERROR: Failed to download, please try install again!" "ERROR: 下载堡塔云监控失败,请尝试重新安装!"
|
||||
fi
|
||||
|
||||
rm -rf $new_dir/config
|
||||
rm -rf $new_dir/data
|
||||
rm -rf $new_dir/ssl
|
||||
\cp -r $new_dir/* $monitor_path/
|
||||
rm -rf $agent_src
|
||||
rm -rf $new_dir
|
||||
chmod +x $monitor_path/BT-MONITOR
|
||||
chmod +x $monitor_path/tools.py
|
||||
wget -O /etc/init.d/btm ${download_Url}/init/btmonitor.init -t 5 -T 10
|
||||
chmod +x /etc/init.d/btm
|
||||
ln -sf /etc/init.d/btm /usr/bin/btm
|
||||
|
||||
if [ ! -f $monitor_path/data/user.json ]; then
|
||||
echo "{\"uid\":1,\"username\":\"Administrator\",\"ip\":\"127.0.0.1\",\"server_id\":\"1\",\"access_key\":\"test\",\"secret_key\":\"123456\"}" > $monitor_path/data/user.json
|
||||
fi
|
||||
if [ -f $monitor_path/core/include/c_loader/PluginLoader.so ]; then
|
||||
rm -f $monitor_path/core/include/c_loader/PluginLoader.so
|
||||
fi
|
||||
}
|
||||
|
||||
Service_Add(){
|
||||
if [ $Command_Exists systemctl ]; then
|
||||
wget -O /usr/lib/systemd/system/btm.service ${download_Url}/init/systemd/btmonitor.service -t 5 -T 10
|
||||
systemctl daemon-reload
|
||||
systemctl enable btm
|
||||
else
|
||||
if [ "${PM}" == "yum" ] || [ "${PM}" == "dnf" ]; then
|
||||
chkconfig --add btm
|
||||
chkconfig --level 2345 btm on
|
||||
elif [ "${PM}" == "apt-get" ]; then
|
||||
update-rc.d btm defaults
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
Start_Monitor(){
|
||||
/etc/init.d/btm start
|
||||
if [ "$?" != "0" ]; then
|
||||
echo "堡塔云监控启动失败!"
|
||||
tail $monitor_path/logs/error.log
|
||||
exit 1
|
||||
fi
|
||||
echo "已成功升级到[$version]${Ver}";
|
||||
}
|
||||
|
||||
GetSysInfo(){
|
||||
if [ -s "/etc/redhat-release" ];then
|
||||
SYS_VERSION=$(cat /etc/redhat-release)
|
||||
elif [ -s "/etc/issue" ]; then
|
||||
SYS_VERSION=$(cat /etc/issue)
|
||||
fi
|
||||
SYS_INFO=$(uname -a)
|
||||
SYS_BIT=$(getconf LONG_BIT)
|
||||
MEM_TOTAL=$(free -m|grep Mem|awk '{print $2}')
|
||||
CPU_INFO=$(getconf _NPROCESSORS_ONLN)
|
||||
|
||||
echo -e ${SYS_VERSION}
|
||||
echo -e Bit:${SYS_BIT} Mem:${MEM_TOTAL}M Core:${CPU_INFO}
|
||||
echo -e ${SYS_INFO}
|
||||
echo -e "请截图以上报错信息发帖至论坛www.bt.cn/bbs求助"
|
||||
}
|
||||
|
||||
Red_Error(){
|
||||
echo '=================================================';
|
||||
printf '\033[1;31;40m%b\033[0m\n' "$@";
|
||||
GetSysInfo
|
||||
exit 1;
|
||||
}
|
||||
|
||||
Install_RPM_Pack(){
|
||||
yumPacks="wget curl unzip gcc gcc-c++ make libcurl-devel openssl-devel xz-devel python-backports-lzma xz"
|
||||
yum install -y ${yumPacks}
|
||||
|
||||
for yumPack in ${yumPacks}
|
||||
do
|
||||
rpmPack=$(rpm -q ${yumPack})
|
||||
packCheck=$(echo ${rpmPack}|grep not)
|
||||
if [ "${packCheck}" ]; then
|
||||
yum install ${yumPack} -y
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
Install_Deb_Pack(){
|
||||
apt-get update -y
|
||||
debPacks="wget curl unzip gcc g++ make libcurl4-openssl-dev libssl-dev liblzma-dev xz-utils libffi-dev libbz2-dev libsqlite3-dev libreadline-dev libgdbm-dev python3-bsddb3 tk-dev ncurses-dev uuid-dev";
|
||||
apt-get install -y $debPacks --force-yes
|
||||
|
||||
for debPack in ${debPacks}
|
||||
do
|
||||
packCheck=$(dpkg -l ${debPack})
|
||||
if [ "$?" -ne "0" ] ;then
|
||||
apt-get install -y $debPack
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
Get_Pack_Manager(){
|
||||
if [ -f "/usr/bin/yum" ] && [ -d "/etc/yum.repos.d" ]; then
|
||||
PM="yum"
|
||||
elif [ -f "/usr/bin/apt-get" ] && [ -f "/usr/bin/dpkg" ]; then
|
||||
PM="apt-get"
|
||||
fi
|
||||
}
|
||||
|
||||
Update_Monitor(){
|
||||
Get_Pack_Manager
|
||||
get_node_url
|
||||
if [ $PM = "yum" ]; then
|
||||
Install_RPM_Pack
|
||||
else
|
||||
Install_Deb_Pack
|
||||
fi
|
||||
if [ "$action" == "update_py" ]; then
|
||||
Install_Python_Lib
|
||||
fi
|
||||
Install_Monitor
|
||||
Service_Add
|
||||
Start_Monitor
|
||||
}
|
||||
|
||||
action=${1}
|
||||
Update_Monitor
|
367
public/install/update_panel.sh
Normal file
@ -0,0 +1,367 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
pyenv_bin=/www/server/panel/pyenv/bin
|
||||
rep_path=${pyenv_bin}:$PATH
|
||||
if [ -d "$pyenv_bin" ];then
|
||||
PATH=$rep_path
|
||||
fi
|
||||
export PATH
|
||||
LANG=en_US.UTF-8
|
||||
setup_path=/www
|
||||
is64bit=$(getconf LONG_BIT)
|
||||
if [ "${is64bit}" != '64' ];then
|
||||
echo "抱歉, 面板新版本不再支持32位系统, 无法进行升级";
|
||||
echo "退出、不做任何操作"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Btapi_Url='http://www.example.com'
|
||||
|
||||
|
||||
up_plugin=0
|
||||
|
||||
download_file(){
|
||||
dst_file=$1
|
||||
tmp_file=/tmp/bt_tmp_file.temp
|
||||
if [ -f $tmp_file ];then
|
||||
rm -f $tmp_file
|
||||
fi
|
||||
wget -O ${tmp_file} $2 -T 20
|
||||
tmp_size=$(du -b $tmp_file|awk '{print $1}')
|
||||
if [ $tmp_size -lt 10 ];then
|
||||
echo "|-文件下载失败 $dst_file"
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -f $dst_file ];then
|
||||
rm -f $dst_file
|
||||
fi
|
||||
|
||||
mv -f $tmp_file $dst_file
|
||||
|
||||
if [ -f $tmp_file ];then
|
||||
rm -f $tmp_file
|
||||
fi
|
||||
}
|
||||
|
||||
Red_Error(){
|
||||
echo '=================================================';
|
||||
printf '\033[1;31;40m%b\033[0m\n' "$1";
|
||||
exit 0;
|
||||
}
|
||||
|
||||
check_panel(){
|
||||
if [ ! -d /www/server/panel/BTPanel ];then
|
||||
up_plugin=1
|
||||
fi
|
||||
}
|
||||
|
||||
select_node(){
|
||||
public_file=/www/server/panel/install/public.sh
|
||||
if [ ! -f $public_file ];then
|
||||
download_file $public_file $Btapi_Url/install/public.sh
|
||||
fi
|
||||
|
||||
. $public_file
|
||||
|
||||
download_Url=$NODE_URL
|
||||
}
|
||||
|
||||
get_version(){
|
||||
version=$(curl -Ss --connect-timeout 5 -m 2 $Btapi_Url/api/panel/get_version)
|
||||
if [ "$version" = '' ];then
|
||||
version='7.9.9'
|
||||
fi
|
||||
}
|
||||
|
||||
install_pack(){
|
||||
if [ -f /usr/bin/yum ];then
|
||||
yum install libcurl-devel libffi-devel zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel -y
|
||||
else
|
||||
apt install libcurl4-openssl-dev net-tools swig build-essential libffi-dev zlib1g.dev libbz2-dev libssl-dev libncurses-dev libsqlite3-dev libreadline-dev tk-dev libgdbm-dev libdb-dev libdb++-dev libpcap-dev xz-utils -y
|
||||
fi
|
||||
}
|
||||
|
||||
install_python(){
|
||||
curl -Ss --connect-timeout 3 -m 60 $download_Url/install/pip_select.sh|bash
|
||||
pyenv_path="/www/server/panel"
|
||||
python_bin=$pyenv_path/pyenv/bin/python
|
||||
if [ -f $pyenv_path/pyenv/bin/python ];then
|
||||
is_err=$($pyenv_path/pyenv/bin/python3.7 -V 2>&1|grep 'Could not find platform')
|
||||
if [ "$is_err" = "" ];then
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
is_package=$($python_bin -m psutil 2>&1|grep package)
|
||||
if [ "$is_package" = "" ];then
|
||||
wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip.txt -T 5
|
||||
$pyenv_path/pyenv/bin/pip install -U pip
|
||||
$pyenv_path/pyenv/bin/pip install -U setuptools
|
||||
$pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt
|
||||
fi
|
||||
source $pyenv_path/pyenv/bin/activate
|
||||
return
|
||||
else
|
||||
rm -rf $pyenv_path/pyenv
|
||||
fi
|
||||
fi
|
||||
install_pack
|
||||
py_version="3.7.9"
|
||||
mkdir -p $pyenv_path
|
||||
os_type='el'
|
||||
os_version='7'
|
||||
is_export_openssl=0
|
||||
Get_Versions
|
||||
Centos6_Openssl
|
||||
Other_Openssl
|
||||
echo "OS: $os_type - $os_version"
|
||||
is_aarch64=$(uname -a|grep aarch64)
|
||||
if [ "$is_aarch64" != "" ];then
|
||||
os_version="aarch64"
|
||||
fi
|
||||
up_plugin=1
|
||||
|
||||
if [ -f "/www/server/panel/pymake.pl" ];then
|
||||
os_version=""
|
||||
rm -f /www/server/panel/pymake.pl
|
||||
fi
|
||||
|
||||
if [ "${os_version}" != "" ];then
|
||||
pyenv_file="/www/pyenv.tar.gz"
|
||||
wget -O $pyenv_file $download_Url/install/pyenv/pyenv-${os_type}${os_version}-x${is64bit}.tar.gz -T 10
|
||||
tmp_size=$(du -b $pyenv_file|awk '{print $1}')
|
||||
if [ $tmp_size -lt 703460 ];then
|
||||
rm -f $pyenv_file
|
||||
echo "ERROR: Download python env fielded."
|
||||
else
|
||||
echo "Install python env..."
|
||||
tar zxvf $pyenv_file -C $pyenv_path/ &> /dev/null
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
if [ ! -f $pyenv_path/pyenv/bin/python ];then
|
||||
rm -f $pyenv_file
|
||||
Red_Error "ERROR: Install python env fielded."
|
||||
fi
|
||||
is_err=$($pyenv_path/pyenv/bin/python3.7 -V 2>&1|grep 'Could not find platform')
|
||||
if [ "$is_err" = "" ];then
|
||||
rm -f $pyenv_file
|
||||
ln -sf $pyenv_path/pyenv/bin/pip3.7 /usr/bin/btpip
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 /usr/bin/btpython
|
||||
sync_python_lib
|
||||
source $pyenv_path/pyenv/bin/activate
|
||||
return
|
||||
else
|
||||
rm -rf $pyenv_path/pyenv
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [ -f /usr/local/openssl/lib/libssl.so ];then
|
||||
export LDFLAGS="-L/usr/local/openssl/lib"
|
||||
export CPPFLAGS="-I/usr/local/openssl/include"
|
||||
export PKG_CONFIG_PATH="/usr/local/openssl/lib/pkgconfig"
|
||||
echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/openssl/lib" >> /etc/profile
|
||||
source /etc/profile
|
||||
fi
|
||||
cd /www
|
||||
python_src='/www/python_src.tar.xz'
|
||||
python_src_path="/www/Python-${py_version}"
|
||||
wget -O $python_src $download_Url/src/Python-${py_version}.tar.xz -T 5
|
||||
tmp_size=$(du -b $python_src|awk '{print $1}')
|
||||
if [ $tmp_size -lt 10703460 ];then
|
||||
rm -f $python_src
|
||||
Red_Error "ERROR: Download python source code fielded."
|
||||
fi
|
||||
tar xvf $python_src
|
||||
rm -f $python_src
|
||||
cd $python_src_path
|
||||
./configure --prefix=$pyenv_path/pyenv
|
||||
make -j$cpu_cpunt
|
||||
make install
|
||||
if [ ! -f $pyenv_path/pyenv/bin/python3.7 ];then
|
||||
rm -rf $python_src_path
|
||||
Red_Error "ERROR: Make python env fielded."
|
||||
fi
|
||||
cd ~
|
||||
rm -rf $python_src_path
|
||||
wget -O $pyenv_path/pyenv/bin/activate $download_Url/install/pyenv/activate.panel -T 5
|
||||
wget -O $pyenv_path/pyenv/pip.txt $download_Url/install/pyenv/pip.txt -T 5
|
||||
ln -sf $pyenv_path/pyenv/bin/pip3.7 $pyenv_path/pyenv/bin/pip
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 $pyenv_path/pyenv/bin/python
|
||||
ln -sf $pyenv_path/pyenv/bin/pip3.7 /usr/bin/btpip
|
||||
ln -sf $pyenv_path/pyenv/bin/python3.7 /usr/bin/btpython
|
||||
chmod -R 700 $pyenv_path/pyenv/bin
|
||||
$pyenv_path/pyenv/bin/pip install -U pip
|
||||
$pyenv_path/pyenv/bin/pip install -U setuptools
|
||||
$pyenv_path/pyenv/bin/pip install -r $pyenv_path/pyenv/pip.txt
|
||||
sync_python_lib
|
||||
source $pyenv_path/pyenv/bin/activate
|
||||
}
|
||||
|
||||
sync_python_lib(){
|
||||
pip_list=$(pip list 2>/dev/null|grep -v Package|grep -v '\-\-\-\-\-\-'|awk '{print $1}'|xargs)
|
||||
$pyenv_path/pyenv/bin/pip install -U pip setuptools
|
||||
$pyenv_path/pyenv/bin/pip install $pip_list
|
||||
}
|
||||
|
||||
Other_Openssl(){
|
||||
openssl_version=$(openssl version|grep -Eo '[0-9]\.[0-9]\.[0-9]')
|
||||
if [ "$openssl_version" = '1.0.1' ] || [ "$openssl_version" = '1.0.0' ];then
|
||||
opensslVersion="1.0.2r"
|
||||
if [ ! -f "/usr/local/openssl/lib/libssl.so" ];then
|
||||
cd /www
|
||||
openssl_src_file=/www/openssl.tar.gz
|
||||
wget -O $openssl_src_file ${download_Url}/src/openssl-${opensslVersion}.tar.gz
|
||||
tmp_size=$(du -b $openssl_src_file|awk '{print $1}')
|
||||
if [ $tmp_size -lt 703460 ];then
|
||||
rm -f $openssl_src_file
|
||||
Red_Error "ERROR: Download openssl-1.0.2 source code fielded."
|
||||
fi
|
||||
tar -zxf $openssl_src_file
|
||||
rm -f $openssl_src_file
|
||||
cd openssl-${opensslVersion}
|
||||
./config --openssldir=/usr/local/openssl zlib-dynamic shared
|
||||
make -j${cpuCore}
|
||||
make install
|
||||
echo "/usr/local/openssl/lib" > /etc/ld.so.conf.d/zopenssl.conf
|
||||
ldconfig
|
||||
cd ..
|
||||
rm -rf openssl-${opensslVersion}
|
||||
is_export_openssl=1
|
||||
cd ~
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
Insatll_Libressl(){
|
||||
openssl_version=$(openssl version|grep -Eo '[0-9]\.[0-9]\.[0-9]')
|
||||
if [ "$openssl_version" = '1.0.1' ] || [ "$openssl_version" = '1.0.0' ];then
|
||||
opensslVersion="3.0.2"
|
||||
cd /www
|
||||
openssl_src_file=/www/openssl.tar.gz
|
||||
wget -O $openssl_src_file ${download_Url}/install/pyenv/libressl-${opensslVersion}.tar.gz
|
||||
tmp_size=$(du -b $openssl_src_file|awk '{print $1}')
|
||||
if [ $tmp_size -lt 703460 ];then
|
||||
rm -f $openssl_src_file
|
||||
Red_Error "ERROR: Download libressl-$opensslVersion source code fielded."
|
||||
fi
|
||||
tar -zxf $openssl_src_file
|
||||
rm -f $openssl_src_file
|
||||
cd libressl-${opensslVersion}
|
||||
./config –prefix=/usr/local/lib
|
||||
make -j${cpuCore}
|
||||
make install
|
||||
ldconfig
|
||||
ldconfig -v
|
||||
cd ..
|
||||
rm -rf libressl-${opensslVersion}
|
||||
is_export_openssl=1
|
||||
cd ~
|
||||
fi
|
||||
}
|
||||
|
||||
Centos6_Openssl(){
|
||||
if [ "$os_type" != 'el' ];then
|
||||
return
|
||||
fi
|
||||
if [ "$os_version" != '6' ];then
|
||||
return
|
||||
fi
|
||||
echo 'Centos6 install openssl-1.0.2...'
|
||||
openssl_rpm_file="/www/openssl.rpm"
|
||||
wget -O $openssl_rpm_file $download_Url/rpm/centos6/${is64bit}/bt-openssl102.rpm -T 10
|
||||
tmp_size=$(du -b $openssl_rpm_file|awk '{print $1}')
|
||||
if [ $tmp_size -lt 102400 ];then
|
||||
rm -f $openssl_rpm_file
|
||||
Red_Error "ERROR: Download python env fielded."
|
||||
fi
|
||||
rpm -ivh $openssl_rpm_file
|
||||
rm -f $openssl_rpm_file
|
||||
is_export_openssl=1
|
||||
}
|
||||
|
||||
Get_Versions(){
|
||||
redhat_version_file="/etc/redhat-release"
|
||||
deb_version_file="/etc/issue"
|
||||
if [ -f $redhat_version_file ];then
|
||||
os_type='el'
|
||||
is_aliyunos=$(cat $redhat_version_file|grep Aliyun)
|
||||
if [ "$is_aliyunos" != "" ];then
|
||||
return
|
||||
fi
|
||||
os_version=$(cat $redhat_version_file|grep CentOS|grep -Eo '([0-9]+\.)+[0-9]+'|grep -Eo '^[0-9]')
|
||||
if [ "${os_version}" = "5" ];then
|
||||
os_version=""
|
||||
fi
|
||||
else
|
||||
os_type='ubuntu'
|
||||
os_version=$(cat $deb_version_file|grep Ubuntu|grep -Eo '([0-9]+\.)+[0-9]+'|grep -Eo '^[0-9]+')
|
||||
if [ "${os_version}" = "" ];then
|
||||
os_type='debian'
|
||||
os_version=$(cat $deb_version_file|grep Debian|grep -Eo '([0-9]+\.)+[0-9]+'|grep -Eo '[0-9]+')
|
||||
if [ "${os_version}" = "" ];then
|
||||
os_version=$(cat $deb_version_file|grep Debian|grep -Eo '[0-9]+')
|
||||
fi
|
||||
if [ "${os_version}" = "8" ];then
|
||||
os_version=""
|
||||
fi
|
||||
if [ "${is64bit}" = '32' ];then
|
||||
os_version=""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
update_panel(){
|
||||
wget -T 5 -O /tmp/panel.zip $Btapi_Url/install/update/LinuxPanel-${version}.zip
|
||||
dsize=$(du -b /tmp/panel.zip|awk '{print $1}')
|
||||
if [ $dsize -lt 10240 ];then
|
||||
echo "获取更新包失败,请稍后更新或联系宝塔运维"
|
||||
exit;
|
||||
fi
|
||||
unzip -o /tmp/panel.zip -d $setup_path/server/ > /dev/null
|
||||
rm -f /tmp/panel.zip
|
||||
cd $setup_path/server/panel/
|
||||
check_bt=`cat /etc/init.d/bt|grep BT-Task`
|
||||
if [ "${check_bt}" = "" ];then
|
||||
rm -f /etc/init.d/bt
|
||||
wget -O /etc/init.d/bt $download_Url/install/src/bt7.init -T 20
|
||||
chmod +x /etc/init.d/bt
|
||||
fi
|
||||
rm -f /www/server/panel/*.pyc
|
||||
rm -f /www/server/panel/class/*.pyc
|
||||
if [ ! -f $setup_path/server/panel/config/config.json ];then
|
||||
wget -T 5 -O $setup_path/server/panel/config/config.json $download_Url/install/pyenv/config/config.json
|
||||
wget -T 5 -O $setup_path/server/panel/config/dns_api.json $download_Url/install/pyenv/config/dns_api.json
|
||||
fi
|
||||
|
||||
chattr -i /etc/init.d/bt
|
||||
chmod +x /etc/init.d/bt
|
||||
# if [ $up_plugin = 1 ];then
|
||||
# $pyenv_bin/python /www/server/panel/tools.py update_to6
|
||||
# fi
|
||||
}
|
||||
|
||||
update_start(){
|
||||
echo "====================================="
|
||||
echo "开始升级宝塔Linux面板,请稍候..."
|
||||
echo "====================================="
|
||||
}
|
||||
|
||||
|
||||
update_end(){
|
||||
echo "====================================="
|
||||
rm -f /dev/shm/bt_sql_tips.pl
|
||||
kill $(ps aux|grep -E "task.py|main.py"|grep -v grep|awk '{print $2}') &>/dev/null
|
||||
bash /www/server/panel/init.sh start
|
||||
echo 'True' > /www/server/panel/data/restart.pl
|
||||
pkill -9 gunicorn &>/dev/null &
|
||||
echo "已成功升级到[$version]${Ver}";
|
||||
}
|
||||
rm -rf /www/server/phpmyadmin/pma
|
||||
|
||||
update_start
|
||||
check_panel
|
||||
select_node
|
||||
install_python
|
||||
get_version
|
||||
update_panel
|
||||
update_end
|
||||
|
||||
|
2
public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /
|
19
public/router.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
// $Id$
|
||||
|
||||
if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
|
||||
return false;
|
||||
} else {
|
||||
$_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
|
||||
|
||||
require __DIR__ . "/index.php";
|
||||
}
|
6
public/static/css/bootstrap-table.css
vendored
Normal file
716
public/static/css/download.css
Normal file
@ -0,0 +1,716 @@
|
||||
.nav .new,
|
||||
.wapnava .new,
|
||||
.nava.bbs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.nava.bbs .icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 6px;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAg1JREFUWEftlz9IG1Ecx7+/d63dOwqV6GhXm1i6mM1SyB+sYCfzZ5WCg1BxMF26OHQodGoucXATw6WC2eKYswiC6NAqdhBxFLpU7L1fueKFmF4uL3eSZMiDW+5+fz7v+37v/d4Rejyox/nRfwDPtjNj2o0cCarMb+DgIFm8ahenrkDEyM6B+TUIM+2cFL//AiFvxvRFL/s6QNjI7BAwrRhc2YykjNaSxd1WDg0KZFg5ageGDOT24vr7AYBfBU5AfK6sOFMIgP3Uh/8lIN43Y4UJ5eQAJkupEAtx5gYQLqffCIseWUOy8u1V8dKx8SxCBjYA/FCFEMBTBmbdACLldBVMUwB+gmjNjOU/23Zd2wXPK9nHfM0vJfCRiI/MWCHaVQBHFUcJM67/m7yHAnRELLO1RMF0W4Lb9a42F12zbXMRdgAAEOEtLHnoBsCaNg7mFQDDXjUSCEC1+AYAAwU8FLgC8wszUTj2sQ2DlSATfRUWb9WSerExkvI2ZKBkSWtpP7l+GgzlrrcSgFsHsw8eKcS8HxgBnNTiut1X0BqgnK4yiydgXtpL6KXmRGEjs0pAzg/Arc+m9vDBO+vPzRe7Kf13FKsEniyl7G7W8WBNzIORAnAB4u++ATrO3OAQMdIfAFp2XvlSIAiA7Rs2sgsE/mTfCcy4PnqnGwYNrurvLKNzVe+/XzPVmdyXXc8V+Av1tCwwyQQB+wAAAABJRU5ErkJggg==');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
border-radius: 2px;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
display: none;
|
||||
}
|
||||
.wapnava .new .icon,
|
||||
.nav .new .icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-right: 4px;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
|
||||
|
||||
.head_pc {
|
||||
background-color: #fff;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
canvas {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 0;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.top-tips {
|
||||
z-index: 1;
|
||||
}
|
||||
.wrap {
|
||||
padding: 0;
|
||||
}
|
||||
.d1 {
|
||||
background: linear-gradient(0deg, #fff, #d8efdb, #edf7ef);
|
||||
padding-bottom: 60px;
|
||||
position: relative;
|
||||
}
|
||||
.d1 .wrap {
|
||||
padding-top: 60px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.d2 .wrap,
|
||||
.d3 .wrap,
|
||||
.d4 .wrap {
|
||||
padding: 70px 0;
|
||||
}
|
||||
|
||||
.install-box {
|
||||
position: relative;
|
||||
top: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 24%;
|
||||
padding: 30px 10px 16px 20px;
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 30%);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.install-box:hover {
|
||||
box-shadow: 0 6px 16px 0 rgb(0 0 0 / 50%);
|
||||
top: -10px;
|
||||
}
|
||||
.install-box.linux,
|
||||
.install-box.windows {
|
||||
align-items: center;
|
||||
width: 41.5%;
|
||||
}
|
||||
|
||||
.install-box + .install-box {
|
||||
margin-left: 2%;
|
||||
}
|
||||
|
||||
.install-box::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right bottom;
|
||||
background-size: auto 100%;
|
||||
z-index: 10;
|
||||
transition: all 0.25s;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.install-box.linux::before {
|
||||
background-image: url(../images/downico1_01.png);
|
||||
}
|
||||
|
||||
.install-box.windows::before {
|
||||
background-image: url(../images/downico2_01.png);
|
||||
}
|
||||
|
||||
.install-box.cloud::before {
|
||||
background-image: url(../images/downico3_01.png);
|
||||
}
|
||||
|
||||
.install-box.ssh::before {
|
||||
background-image: url(../images/downico4_01.png);
|
||||
}
|
||||
|
||||
.install-box.app::before {
|
||||
background-image: url(../images/downico5_01.png);
|
||||
}
|
||||
|
||||
.install-box .img {
|
||||
position: relative;
|
||||
flex: 0 0 44%;
|
||||
z-index: 12;
|
||||
}
|
||||
.install-box .img img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.install-box .cont {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
margin-left: 5%;
|
||||
padding-bottom: 12px;
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
.install-box.linux .cont,
|
||||
.install-box.windows .cont {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.install-box .bottom {
|
||||
display: flex;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.install-box .title {
|
||||
margin-bottom: 14px;
|
||||
line-height: 1;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.install-box.linux .title,
|
||||
.install-box.windows .title {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.install-box .desc {
|
||||
margin-bottom: 14px;
|
||||
font-size: 12px;
|
||||
color: #777;
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.install-box.linux .desc,
|
||||
.install-box.windows .desc {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.install-box .mark {
|
||||
margin-bottom: 20px;
|
||||
height: 22px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.install-box .mark span {
|
||||
margin-right: 10px;
|
||||
line-height: 22px;
|
||||
height: 22px;
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
}
|
||||
.install-box .mark span:nth-child(1) {
|
||||
border: #6bdc6b 1px solid;
|
||||
color: #fff;
|
||||
background-color: #6bdc6b;
|
||||
}
|
||||
.install-box .mark span:nth-child(2) {
|
||||
border: #ffa565 1px solid;
|
||||
color: #fff;
|
||||
background-color: #ffa565;
|
||||
}
|
||||
.install-box .mark span:nth-child(3) {
|
||||
border: #65a7ff 1px solid;
|
||||
color: #fff;
|
||||
background-color: #65a7ff;
|
||||
}
|
||||
.install-box .mark span:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.install-box .btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 34px;
|
||||
padding: 0 16px;
|
||||
background-image: linear-gradient(#54b891, #22a53d);
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.install-box .btn.default {
|
||||
margin-left: 10px;
|
||||
background: none;
|
||||
border: 1px solid #777;
|
||||
color: #777;
|
||||
}
|
||||
.install-box .btn:hover {
|
||||
filter: brightness(120%);
|
||||
-webkit-filter: brightness(120%);
|
||||
}
|
||||
|
||||
.install-box:hover {
|
||||
background-image: linear-gradient(#e5f5e5, #fff);
|
||||
}
|
||||
|
||||
|
||||
.wrap-title {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.wrap-title::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 6px;
|
||||
height: 36px;
|
||||
margin-right: 20px;
|
||||
background-color: #20a53a;
|
||||
}
|
||||
|
||||
.wrap-title .text {
|
||||
margin-right: 20px;
|
||||
line-height: 36px;
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.wrap-title a {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.d2 .desc,
|
||||
.d2 .tips,
|
||||
.d4 .tips {
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.d2 .desc {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.d2 .tips,
|
||||
.d4 .tips {
|
||||
margin-top: 16px;
|
||||
/* color: #20a53a; */
|
||||
}
|
||||
|
||||
.install-code {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
line-height: 30px;
|
||||
}
|
||||
.install-code .osname {
|
||||
display: inline-block;
|
||||
width: 160px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.install-code + .install-code {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.install-code .code-cont {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.install-code .command {
|
||||
position: relative;
|
||||
display: flex;
|
||||
margin-left: 12px;
|
||||
margin-right: 15px;
|
||||
padding: 7px 10px;
|
||||
border-radius: 3px;
|
||||
background-color: #20202f;
|
||||
box-shadow: 0 0 5px #ececec;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
font-family: SimSun;
|
||||
width: 950px;
|
||||
}
|
||||
|
||||
.install-code .ico-copy {
|
||||
display: block;
|
||||
width: 26px;
|
||||
height: 30px;
|
||||
background: url(../images/ico-copy.png) no-repeat left center;
|
||||
cursor: pointer;
|
||||
width: 65px;
|
||||
text-indent: 2.4em;
|
||||
font-weight: bold;
|
||||
color: #20a53a;
|
||||
}
|
||||
|
||||
.d4 .desc {
|
||||
margin-bottom: 16px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.bird {
|
||||
width: 25px;
|
||||
height: 5px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
transform: skew(20deg, 20deg);
|
||||
animation: wave 2.5s ease-in-out infinite;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.bird:before,
|
||||
.bird:after {
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #1aa837;
|
||||
position: absolute;
|
||||
border-radius: 20%;
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.bird:before {
|
||||
right: 49%;
|
||||
transform-origin: right;
|
||||
transform: rotate(-40deg);
|
||||
animation: flap-left 0.75s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.bird:after {
|
||||
left: 49%;
|
||||
transform-origin: left;
|
||||
transform: rotate(40deg);
|
||||
animation: flap-right 0.75s linear infinite;
|
||||
}
|
||||
|
||||
/* <20><>װ<EFBFBD>ű<EFBFBD> */
|
||||
.layui-layer .install-code {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
line-height: 30px;
|
||||
}
|
||||
.layui-layer .install-code .osname {
|
||||
display: inline-block;
|
||||
width: 160px;
|
||||
font-weight: 700;
|
||||
margin: 20px auto;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.layui-layer .install-code + .install-code {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.layui-layer .install-code .code-cont {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.layui-layer .install-code .command {
|
||||
position: relative;
|
||||
display: flex;
|
||||
margin-right: 15px;
|
||||
padding: 7px 10px;
|
||||
border-radius: 3px;
|
||||
background-color: #20202f;
|
||||
box-shadow: 0 0 5px #ececec;
|
||||
color: #fff;
|
||||
font-family: SimSun;
|
||||
width: 950px;
|
||||
}
|
||||
|
||||
.layui-layer .install-code .ico-copy {
|
||||
display: block;
|
||||
width: 26px;
|
||||
height: 30px;
|
||||
background: url(../images/ico-copy.png) no-repeat left center;
|
||||
cursor: pointer;
|
||||
width: 65px;
|
||||
text-indent: 2.4em;
|
||||
font-weight: bold;
|
||||
color: #20a53a;
|
||||
}
|
||||
.layui-layer .install-code {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.layui-layer .install-code .code-cont {
|
||||
width: 100%;
|
||||
}
|
||||
.layui-layer .install-code .command {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
margin-left: 0;
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
@keyframes wave {
|
||||
40% {
|
||||
transform: translateY(40px) skew(20deg, 20deg);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(50px) skew(20deg, 20deg);
|
||||
}
|
||||
60% {
|
||||
transform: translateY(40px) skew(20deg, 20deg);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0) skew(20deg, 20deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes flap-left {
|
||||
60% {
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
75% {
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(-40deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes flap-right {
|
||||
60% {
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
75% {
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(40deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1440px) {
|
||||
.wrap {
|
||||
width: 97%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1299px) {
|
||||
.d2 .wrap,
|
||||
.d3 .wrap,
|
||||
.d4 .wrap {
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
}
|
||||
@media only screen and (max-width: 980px) {
|
||||
.install-code {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.install-code .code-cont {
|
||||
width: 100%;
|
||||
}
|
||||
.install-code .command {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
margin-left: 0;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.d2 .desc,
|
||||
.d2 .tips {
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.d2 .wrap,
|
||||
.d3 .wrap,
|
||||
.d4 .wrap {
|
||||
padding: 40px 0;
|
||||
}
|
||||
.d1 .wrap {
|
||||
padding-top: 40px;
|
||||
}
|
||||
.wrap-title {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.wrap-title::before {
|
||||
width: 3px;
|
||||
height: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.wrap-title .text {
|
||||
font-size: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.wrap-title a {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1440px) {
|
||||
.wrap {
|
||||
width: 97%;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1299px) {
|
||||
.d2 .wrap,
|
||||
.d3 .wrap,
|
||||
.d4 .wrap {
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
.install-box {
|
||||
padding: 24px 20px 10px;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.install-box.linux .title,
|
||||
.install-box.windows .title,
|
||||
.install-box .title {
|
||||
margin-bottom: 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.install-box .img {
|
||||
flex: 0 0 40%;
|
||||
}
|
||||
|
||||
.install-box.linux .title,
|
||||
.install-box.windows .title {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.install-box .desc {
|
||||
font-size: 13px;
|
||||
}
|
||||
.install-box .btn {
|
||||
padding: 0 12px;
|
||||
height: 30px;
|
||||
border-radius: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 1200px) {
|
||||
.install-box .title {
|
||||
font-size: 16px;
|
||||
}
|
||||
.install-box .desc {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 980px) {
|
||||
.install-list {
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.install-box,
|
||||
.install-box.linux,
|
||||
.install-box.windows {
|
||||
width: 48%;
|
||||
padding: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
.install-box:nth-child(2n) {
|
||||
margin-left: 4%;
|
||||
}
|
||||
.install-box.app {
|
||||
margin-top: 30px;
|
||||
margin-left: 0;
|
||||
}
|
||||
.install-box.monitor {
|
||||
margin-top: 30px;
|
||||
}
|
||||
.install-box.linux .title,
|
||||
.install-box.windows .title,
|
||||
.install-box .title {
|
||||
font-size: 16px;
|
||||
}
|
||||
.install-box .btn {
|
||||
height: 24px;
|
||||
padding: 0 10px;
|
||||
border-radius: 6px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.install-box .btn + .btn {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.install-code {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.install-code .code-cont {
|
||||
width: 100%;
|
||||
}
|
||||
.install-code .command {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
margin-left: 0;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
}
|
||||
.online-install-cont {
|
||||
flex-direction: column;
|
||||
}
|
||||
.online-install-cont .form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.online-install-cont .pr-70 {
|
||||
padding-right: 0;
|
||||
}
|
||||
.online-install-cont .pl-10 {
|
||||
padding-left: 0;
|
||||
}
|
||||
.online-install-cont .first-line {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.online-install-cont .server-line {
|
||||
width: 100%;
|
||||
flex: none;
|
||||
}
|
||||
.online-install-cont .port-line {
|
||||
flex: none;
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.d2 .desc,
|
||||
.d2 .tips {
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.d2 .wrap,
|
||||
.d3 .wrap,
|
||||
.d4 .wrap {
|
||||
padding: 40px 0;
|
||||
}
|
||||
.d1 .wrap {
|
||||
padding-top: 40px;
|
||||
}
|
||||
.install-box {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.install-box .img,
|
||||
.install-box .cont {
|
||||
flex: 0 0 100%;
|
||||
}
|
||||
.install-box .cont {
|
||||
margin-top: 10px;
|
||||
margin-left: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.install-box .title,
|
||||
.install-box.linux .title,
|
||||
.install-box.windows .title {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.install-box .desc {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.wrap-title {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.wrap-title::before {
|
||||
width: 3px;
|
||||
height: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.wrap-title .text {
|
||||
font-size: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.wrap-title a {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
1057
public/static/css/sanren.css
Normal file
1491
public/static/css/style.css
Normal file
BIN
public/static/file/kaixin.zip
Normal file
BIN
public/static/file/win/kaixin.zip
Normal file
BIN
public/static/images/account.png
Normal file
After Width: | Height: | Size: 586 B |
BIN
public/static/images/addc.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
public/static/images/bt.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/static/images/bt_monitor.png
Normal file
After Width: | Height: | Size: 194 KiB |
BIN
public/static/images/bto_copy.png
Normal file
After Width: | Height: | Size: 265 B |
BIN
public/static/images/close.png
Normal file
After Width: | Height: | Size: 888 B |
BIN
public/static/images/downico1_01.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
public/static/images/downico2_01.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
public/static/images/eye.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
public/static/images/eye_close.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
public/static/images/footbg_02.jpg
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
public/static/images/hot_03.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
public/static/images/i1abg_03.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
public/static/images/i1aico_03.png
Normal file
After Width: | Height: | Size: 1.3 KiB |