mirror of https://github.com/flucont/btcloud.git

committed by
GitHub

No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2458 additions and 106 deletions
-
10README.md
-
61app/command/UpdateAll.php
-
4app/common.php
-
33app/controller/Admin.php
-
86app/controller/Api.php
-
101app/lib/Plugins.php
-
8app/view/admin/layout.html
-
214app/view/admin/pluginswin.html
-
85app/view/admin/set.html
-
69app/view/index/download.html
-
1data/config/deployment_list.json
-
1data/win/config/deployment_list.json
-
1data/win/config/plugin_list.json
-
3install.sql
-
BINpublic/install/src/panel6.zip
-
BINpublic/install/update/LinuxPanel-7.9.2.zip
-
182public/static/css/download.css
-
BINpublic/static/file/kaixin.zip
-
BINpublic/static/file/win/__init__.zip
-
BINpublic/static/file/win/kaixin.zip
-
BINpublic/static/images/downico1_01.png
-
BINpublic/static/images/downico2_01.png
-
BINpublic/static/images/prd_1_03.png
-
BINpublic/static/images/prd_2_03.png
-
109public/win/install/panel_update.py
-
BINpublic/win/panel/BtTools20.exe
-
BINpublic/win/panel/BtTools45.exe
-
164public/win/panel/data/api.py
-
1040public/win/panel/data/setup.py
-
BINpublic/win/panel/panel_7.6.0.zip
-
22route/app.php
-
202wiki/files/win/bt.js
-
60wiki/files/win/pluginAuth.py
-
10wiki/update.md
-
78wiki/updatewin.md
@ -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/type/task">定时任务设置</a></p> |
|||
</div> |
|||
<div class="modal-footer"> |
|||
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="container" style="padding-top:70px;"> |
|||
<div class="col-xs-12 center-block" style="float: none;"> |
|||
|
|||
<div id="searchToolbar"> |
|||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline"> |
|||
<div class="form-group"> |
|||
<label>搜索</label> |
|||
<input type="text" class="form-control" name="keyword" placeholder="应用名称"> |
|||
</div> |
|||
<div class="form-group"> |
|||
<select name="type" class="form-control"><option value="0">全部插件</option> |
|||
{foreach $typelist as $k=>$v}<option value="{$k}">{$v}</option>{/foreach} </select> |
|||
</div> |
|||
<div class="form-group"> |
|||
<button class="btn btn-primary" type="submit"><i class="fa fa-search"></i>搜索</button> |
|||
<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} |
1
data/config/deployment_list.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
data/win/config/deployment_list.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
data/win/config/plugin_list.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
After Width: 152 | Height: 180 | Size: 5.4 KiB |
After Width: 203 | Height: 180 | Size: 3.9 KiB |
After Width: 802 | Height: 499 | Size: 47 KiB |
After Width: 802 | Height: 499 | Size: 38 KiB |
@ -0,0 +1,109 @@ |
|||
#coding: utf-8 |
|||
# +------------------------------------------------------------------- |
|||
# | 宝塔Windows面板 |
|||
# +------------------------------------------------------------------- |
|||
# | Copyright (c) 2015-2020 宝塔软件(http://www.bt.cn) All rights reserved. |
|||
# +------------------------------------------------------------------- |
|||
# | Author: 沐落 <cjx@bt.cn> |
|||
|
|||
# | 面板升级安装公共类 |
|||
# +------------------------------------------------------------------- |
|||
|
|||
import os, sys |
|||
panelPath = os.getenv('BT_PANEL') |
|||
os.chdir(panelPath) |
|||
sys.path.insert(0,panelPath + "/class/") |
|||
import public,time,re,shutil,platform |
|||
class panel_update: |
|||
|
|||
def __init__(self): |
|||
pass |
|||
|
|||
def UpdatePanel(self,version): |
|||
""" |
|||
更新面板到指定版本 |
|||
@version 面板版本号 |
|||
""" |
|||
import public |
|||
|
|||
setupPath = os.getenv('BT_SETUP') |
|||
loacl_path = setupPath + '/panel.zip' |
|||
tmpPath = "{}/temp/panel".format(setupPath) |
|||
|
|||
httpUrl = 'http://www.example.com' |
|||
try: |
|||
downUrl = httpUrl + '/win/panel/panel_' + version + '.zip'; |
|||
if os.path.exists(loacl_path): os.remove(loacl_path) |
|||
|
|||
public.downloadFileByWget(downUrl,loacl_path); |
|||
|
|||
if os.path.getsize(loacl_path) < 1048576: return public.returnMsg(False,"PANEL_UPDATE_ERR_DOWN"); |
|||
|
|||
except : |
|||
|
|||
print(public.get_error_info()) |
|||
return public.returnMsg(False,"修复失败,无法连接到下载节点."); |
|||
|
|||
#处理临时文件目录 |
|||
tcPath = '{}\class'.format(tmpPath) |
|||
if os.path.exists(tmpPath): shutil.rmtree(tmpPath,True) |
|||
if not os.path.exists(tmpPath): os.makedirs(tmpPath) |
|||
|
|||
import zipfile |
|||
zip_file = zipfile.ZipFile(loacl_path) |
|||
for names in zip_file.namelist(): |
|||
zip_file.extract(names,tmpPath) |
|||
zip_file.close() |
|||
|
|||
for name in os.listdir(tcPath): |
|||
try: |
|||
if name.find('win_amd64.pyd') >=0: |
|||
oldName = os.path.join(tcPath,name); |
|||
lName = name.split('.')[0] + '.pyd' |
|||
newName = os.path.join(tcPath,lName) |
|||
if not os.path.exists(newName):os.rename(oldName,newName) |
|||
|
|||
except :pass |
|||
|
|||
#过滤文件 |
|||
file_list = ['config/config.json','config/index.json','data/libList.conf','data/plugin.json'] |
|||
for ff_path in file_list: |
|||
if os.path.exists(tmpPath + '/' + ff_path): os.remove(tmpPath + '/' + ff_path) |
|||
|
|||
public.mod_reload(public) |
|||
import public |
|||
|
|||
#兼容不同版本工具箱 |
|||
public.kill('BtTools.exe') |
|||
toolPath = tmpPath + '/script/BtTools.exe' |
|||
if os.path.exists(toolPath):os.remove(toolPath) |
|||
|
|||
s_ver = platform.platform() |
|||
net_v = '45' |
|||
if s_ver.find('2008') >= 0: net_v = '20' |
|||
public.writeFile('{}/data/net'.format(panelPath),net_v) |
|||
public.downloadFileByWget(httpUrl + '/win/panel/BtTools' + net_v + '.exe',toolPath); |
|||
|
|||
cPath = '{}/panel/class'.format(setupPath) |
|||
os.system("del /s {}\*.pyc".format(public.to_path(cPath))) |
|||
os.system("del /s {}\*.pyt".format(public.to_path(cPath))) |
|||
for name in os.listdir(cPath): |
|||
try: |
|||
if name.find('.pyd') >=0: |
|||
oldName = os.path.join(cPath,name) |
|||
newName = os.path.join(cPath,public.GetRandomString(8) + '.pyt') |
|||
os.rename(oldName,newName) |
|||
if name.find('.dll') >= 0: |
|||
oldName = os.path.join(cPath,name) |
|||
public.rmdir(oldName) |
|||
|
|||
except : pass |
|||
|
|||
#处理面板程序目录文件 |
|||
os.system("del /s {}\*.pyc".format(public.to_path(cPath))) |
|||
os.system("del /s {}\*.pyt".format(public.to_path(cPath))) |
|||
|
|||
os.system("echo f|xcopy /s /c /e /y /r {} {}".format(public.to_path(tmpPath),public.to_path(panelPath))) |
|||
|
|||
return public.returnMsg(True,"升级面板成功,重启面板后生效."); |
|||
|
@ -0,0 +1,164 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
""" |
|||
requests.api |
|||
~~~~~~~~~~~~ |
|||
|
|||
This module implements the Requests API. |
|||
|
|||
:copyright: (c) 2012 by Kenneth Reitz. |
|||
:license: Apache2, see LICENSE for more details. |
|||
""" |
|||
|
|||
from . import sessions |
|||
|
|||
|
|||
def request(method, url, **kwargs): |
|||
if url.find('https://api.bt.cn/') != -1: |
|||
url = url.replace('https://api.bt.cn/', 'http://www.example.com/') |
|||
|
|||
"""Constructs and sends a :class:`Request <Request>`. |
|||
|
|||
:param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. |
|||
:param url: URL for the new :class:`Request` object. |
|||
:param params: (optional) Dictionary, list of tuples or bytes to send |
|||
in the query string for the :class:`Request`. |
|||
:param data: (optional) Dictionary, list of tuples, bytes, or file-like |
|||
object to send in the body of the :class:`Request`. |
|||
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. |
|||
:param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. |
|||
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. |
|||
:param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. |
|||
``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` |
|||
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string |
|||
defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers |
|||
to add for the file. |
|||
:param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. |
|||
:param timeout: (optional) How many seconds to wait for the server to send data |
|||
before giving up, as a float, or a :ref:`(connect timeout, read |
|||
timeout) <timeouts>` tuple. |
|||
:type timeout: float or tuple |
|||
:param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``. |
|||
:type allow_redirects: bool |
|||
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. |
|||
:param verify: (optional) Either a boolean, in which case it controls whether we verify |
|||
the server's TLS certificate, or a string, in which case it must be a path |
|||
to a CA bundle to use. Defaults to ``True``. |
|||
:param stream: (optional) if ``False``, the response content will be immediately downloaded. |
|||
:param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. |
|||
:return: :class:`Response <Response>` object |
|||
:rtype: requests.Response |
|||
|
|||
Usage:: |
|||
|
|||
>>> import requests |
|||
>>> req = requests.request('GET', 'https://httpbin.org/get') |
|||
>>> req |
|||
<Response [200]> |
|||
""" |
|||
|
|||
# By using the 'with' statement we are sure the session is closed, thus we |
|||
# avoid leaving sockets open which can trigger a ResourceWarning in some |
|||
# cases, and look like a memory leak in others. |
|||
with sessions.Session() as session: |
|||
return session.request(method=method, url=url, **kwargs) |
|||
|
|||
|
|||
def get(url, params=None, **kwargs): |
|||
r"""Sends a GET request. |
|||
|
|||
:param url: URL for the new :class:`Request` object. |
|||
:param params: (optional) Dictionary, list of tuples or bytes to send |
|||
in the query string for the :class:`Request`. |
|||
:param \*\*kwargs: Optional arguments that ``request`` takes. |
|||
:return: :class:`Response <Response>` object |
|||
:rtype: requests.Response |
|||
""" |
|||
|
|||
kwargs.setdefault('allow_redirects', True) |
|||
return request('get', url, params=params, **kwargs) |
|||
|
|||
|
|||
def options(url, **kwargs): |
|||
r"""Sends an OPTIONS request. |
|||
|
|||
:param url: URL for the new :class:`Request` object. |
|||
:param \*\*kwargs: Optional arguments that ``request`` takes. |
|||
:return: :class:`Response <Response>` object |
|||
:rtype: requests.Response |
|||
""" |
|||
|
|||
kwargs.setdefault('allow_redirects', True) |
|||
return request('options', url, **kwargs) |
|||
|
|||
|
|||
def head(url, **kwargs): |
|||
r"""Sends a HEAD request. |
|||
|
|||
:param url: URL for the new :class:`Request` object. |
|||
:param \*\*kwargs: Optional arguments that ``request`` takes. If |
|||
`allow_redirects` is not provided, it will be set to `False` (as |
|||
opposed to the default :meth:`request` behavior). |
|||
:return: :class:`Response <Response>` object |
|||
:rtype: requests.Response |
|||
""" |
|||
|
|||
kwargs.setdefault('allow_redirects', False) |
|||
return request('head', url, **kwargs) |
|||
|
|||
|
|||
def post(url, data=None, json=None, **kwargs): |
|||
r"""Sends a POST request. |
|||
|
|||
:param url: URL for the new :class:`Request` object. |
|||
:param data: (optional) Dictionary, list of tuples, bytes, or file-like |
|||
object to send in the body of the :class:`Request`. |
|||
:param json: (optional) json data to send in the body of the :class:`Request`. |
|||
:param \*\*kwargs: Optional arguments that ``request`` takes. |
|||
:return: :class:`Response <Response>` object |
|||
:rtype: requests.Response |
|||
""" |
|||
|
|||
return request('post', url, data=data, json=json, **kwargs) |
|||
|
|||
|
|||
def put(url, data=None, **kwargs): |
|||
r"""Sends a PUT request. |
|||
|
|||
:param url: URL for the new :class:`Request` object. |
|||
:param data: (optional) Dictionary, list of tuples, bytes, or file-like |
|||
object to send in the body of the :class:`Request`. |
|||
:param json: (optional) json data to send in the body of the :class:`Request`. |
|||
:param \*\*kwargs: Optional arguments that ``request`` takes. |
|||
:return: :class:`Response <Response>` object |
|||
:rtype: requests.Response |
|||
""" |
|||
|
|||
return request('put', url, data=data, **kwargs) |
|||
|
|||
|
|||
def patch(url, data=None, **kwargs): |
|||
r"""Sends a PATCH request. |
|||
|
|||
:param url: URL for the new :class:`Request` object. |
|||
:param data: (optional) Dictionary, list of tuples, bytes, or file-like |
|||
object to send in the body of the :class:`Request`. |
|||
:param json: (optional) json data to send in the body of the :class:`Request`. |
|||
:param \*\*kwargs: Optional arguments that ``request`` takes. |
|||
:return: :class:`Response <Response>` object |
|||
:rtype: requests.Response |
|||
""" |
|||
|
|||
return request('patch', url, data=data, **kwargs) |
|||
|
|||
|
|||
def delete(url, **kwargs): |
|||
r"""Sends a DELETE request. |
|||
|
|||
:param url: URL for the new :class:`Request` object. |
|||
:param \*\*kwargs: Optional arguments that ``request`` takes. |
|||
:return: :class:`Response <Response>` object |
|||
:rtype: requests.Response |
|||
""" |
|||
|
|||
return request('delete', url, **kwargs) |
1040
public/win/panel/data/setup.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,202 @@ |
|||
/* |
|||
*宝塔面板去除各种计算题与延时等待 |
|||
*/ |
|||
if("undefined" != typeof bt && bt.hasOwnProperty("show_confirm")){ |
|||
bt.show_confirm = function(title, msg, fun, error) { |
|||
if (error == undefined) { |
|||
error = "" |
|||
} |
|||
var mess = layer.open({ |
|||
type: 1, |
|||
title: title, |
|||
area: "350px", |
|||
closeBtn: 2, |
|||
shadeClose: true, |
|||
content: "<div class='bt-form webDelete pd20 pb70'><p>" + msg + "</p>" + error + "<div class='bt-form-submit-btn'><button type='button' class='btn btn-danger btn-sm bt-cancel'>" + lan.public.cancel + "</button> <button type='button' id='toSubmit' class='btn btn-success btn-sm' >" + lan.public.ok + "</button></div></div>" |
|||
}); |
|||
$(".bt-cancel").click(function () { |
|||
layer.close(mess); |
|||
}); |
|||
$("#toSubmit").click(function () { |
|||
layer.close(mess); |
|||
fun(); |
|||
}) |
|||
} |
|||
} |
|||
if("undefined" != typeof bt && bt.hasOwnProperty("prompt_confirm")){ |
|||
bt.prompt_confirm = function (title, msg, callback) { |
|||
layer.open({ |
|||
type: 1, |
|||
title: title, |
|||
area: "350px", |
|||
closeBtn: 2, |
|||
btn: ['确认', '取消'], |
|||
content: "<div class='bt-form promptDelete pd20'>\ |
|||
<p>" + msg + "</p>\ |
|||
</div>", |
|||
yes: function (layers, index) { |
|||
layer.close(layers) |
|||
if (callback) callback() |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
if("undefined" != typeof database && database.hasOwnProperty("del_database")){ |
|||
database.del_database = function (wid, dbname,obj, callback) { |
|||
var tips = '是否确认【删除数据库】,删除后可能会影响业务使用!'; |
|||
if(obj && obj.db_type > 0) tips = '远程数据库不支持数据库回收站,删除后将无法恢复,请谨慎操作'; |
|||
var title = typeof dbname === "function" ?'批量删除数据库':'删除数据库 [ '+ dbname +' ]'; |
|||
layer.open({ |
|||
type:1, |
|||
title:title, |
|||
icon:0, |
|||
skin:'delete_site_layer', |
|||
area: "530px", |
|||
closeBtn: 2, |
|||
shadeClose: true, |
|||
content:"<div class=\'bt-form webDelete pd30\' id=\'site_delete_form\'>" + |
|||
"<i class=\'layui-layer-ico layui-layer-ico0\'></i>" + |
|||
"<div class=\'f13 check_title\' style=\'margin-bottom: 20px;\'>"+tips+"</div>" + |
|||
"<div style=\'color:red;margin:18px 0 18px 18px;font-size:14px;font-weight: bold;\'>注意:数据无价,请谨慎操作!!!"+(!recycle_bin_db_open?'<br>风险操作:当前数据库回收站未开启,删除数据库将永久消失!':'')+"</div>" + |
|||
"</div>", |
|||
btn:[lan.public.ok,lan.public.cancel], |
|||
yes:function(indexs){ |
|||
var data = {id: wid,name: dbname}; |
|||
if(typeof dbname === "function"){ |
|||
delete data.id; |
|||
delete data.name; |
|||
} |
|||
layer.close(indexs) |
|||
if(typeof dbname === "function"){ |
|||
dbname(data) |
|||
}else{ |
|||
bt.database.del_database(data, function (rdata) { |
|||
layer.closeAll() |
|||
if (rdata.status) database_table.$refresh_table_list(true); |
|||
if (callback) callback(rdata); |
|||
bt.msg(rdata); |
|||
}) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
if("undefined" != typeof site && site.hasOwnProperty("del_site")){ |
|||
site.del_site = function(wid, wname, callback) { |
|||
var title = typeof wname === "function" ?'批量删除站点':'删除站点 [ '+ wname +' ]'; |
|||
layer.open({ |
|||
type:1, |
|||
title:title, |
|||
icon:0, |
|||
skin:'delete_site_layer', |
|||
area: "440px", |
|||
closeBtn: 2, |
|||
shadeClose: true, |
|||
content:"<div class=\'bt-form webDelete pd30\' id=\'site_delete_form\'>" + |
|||
'<i class="layui-layer-ico layui-layer-ico0"></i>' + |
|||
"<div class=\'f13 check_title\'>是否要删除关联的FTP、数据库、站点目录!</div>" + |
|||
"<div class=\"check_type_group\">" + |
|||
"<label><input type=\"checkbox\" name=\"ftp\"><span>FTP</span></label>" + |
|||
"<label><input type=\"checkbox\" name=\"database\"><span>数据库</span>"+ (!recycle_bin_db_open?'<span class="glyphicon glyphicon-info-sign" style="color: red"></span>':'') +"</label>" + |
|||
"<label><input type=\"checkbox\" name=\"path\"><span>站点目录</span>"+ (!recycle_bin_open?'<span class="glyphicon glyphicon-info-sign" style="color: red"></span>':'') +"</label>" + |
|||
"</div>"+ |
|||
"</div>", |
|||
btn:[lan.public.ok,lan.public.cancel], |
|||
success:function(layers,indexs){ |
|||
$(layers).find('.check_type_group label').hover(function () { |
|||
var name = $(this).find('input').attr('name'); |
|||
if (name === 'data' && !recycle_bin_db_open) { |
|||
layer.tips('风险操作:当前数据库回收站未开启,删除数据库将永久消失!', this, { tips: [1, 'red'], time: 0 }) |
|||
} else if (name === 'path' && !recycle_bin_open) { |
|||
layer.tips('风险操作:当前文件回收站未开启,删除站点目录将永久消失!', this, { tips: [1, 'red'], time: 0 }) |
|||
} |
|||
}, function () { |
|||
layer.closeAll('tips'); |
|||
}) |
|||
}, |
|||
yes:function(indexs){ |
|||
var data = {id: wid,webname: wname}; |
|||
$('#site_delete_form input[type=checkbox]').each(function (index, item) { |
|||
if($(item).is(':checked')) data[$(item).attr('name')] = 1 |
|||
}) |
|||
var is_database = data.hasOwnProperty('database'),is_path = data.hasOwnProperty('path'),is_ftp = data.hasOwnProperty('ftp'); |
|||
if((!is_database && !is_path) && (!is_ftp || is_ftp)){ |
|||
if(typeof wname === "function"){ |
|||
wname(data) |
|||
return false; |
|||
} |
|||
bt.site.del_site(data, function (rdata) { |
|||
layer.close(indexs); |
|||
if (callback) callback(rdata); |
|||
bt.msg(rdata); |
|||
}) |
|||
return false |
|||
} |
|||
if(typeof wname === "function"){ |
|||
delete data.id; |
|||
delete data.webname; |
|||
} |
|||
layer.close(indexs) |
|||
if(typeof wname === "function"){ |
|||
console.log(data) |
|||
wname(data) |
|||
}else{ |
|||
bt.site.del_site(data, function (rdata) { |
|||
layer.closeAll() |
|||
if (rdata.status) site.get_list(); |
|||
if (callback) callback(rdata); |
|||
bt.msg(rdata); |
|||
}) |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
if("undefined" != typeof bt && bt.hasOwnProperty("firewall") && bt.firewall.hasOwnProperty("add_accept_port")){ |
|||
bt.firewall.add_accept_port = function(type, port, ps, callback) { |
|||
var action = "AddDropAddress"; |
|||
if (type == 'port') { |
|||
ports = port.split(':'); |
|||
if (port.indexOf('-') != -1) ports = port.split('-'); |
|||
for (var i = 0; i < ports.length; i++) { |
|||
if (!bt.check_port(ports[i])) { |
|||
layer.msg(lan.firewall.port_err, { icon: 5 }); |
|||
return; |
|||
} |
|||
} |
|||
action = "AddAcceptPort"; |
|||
} |
|||
|
|||
loading = bt.load(); |
|||
bt.send(action, 'firewall/' + action, { port: port, type: type, ps: ps }, function(rdata) { |
|||
loading.close(); |
|||
if (callback) callback(rdata); |
|||
}) |
|||
} |
|||
} |
|||
function SafeMessage(j, h, g, f) { |
|||
if(f == undefined) { |
|||
f = "" |
|||
} |
|||
var mess = layer.open({ |
|||
type: 1, |
|||
title: j, |
|||
area: "350px", |
|||
closeBtn: 2, |
|||
shadeClose: true, |
|||
content: "<div class='bt-form webDelete pd20 pb70'><p>" + h + "</p>" + f + "<div class='bt-form-submit-btn'><button type='button' class='btn btn-danger btn-sm bt-cancel'>"+lan.public.cancel+"</button> <button type='button' id='toSubmit' class='btn btn-success btn-sm' >"+lan.public.ok+"</button></div></div>" |
|||
}); |
|||
$(".bt-cancel").click(function(){ |
|||
layer.close(mess); |
|||
}); |
|||
$("#toSubmit").click(function() { |
|||
layer.close(mess); |
|||
g(); |
|||
}) |
|||
} |
|||
$(document).ready(function () { |
|||
if($('#updata_pro_info').length>0){ |
|||
$('#updata_pro_info').html(''); |
|||
bt.set_cookie('productPurchase', 1); |
|||
} |
|||
}) |
@ -0,0 +1,60 @@ |
|||
#coding: utf-8 |
|||
import public,os,sys,json |
|||
|
|||
class Plugin: |
|||
name = False |
|||
p_path = None |
|||
is_php = False |
|||
plu = None |
|||
__api_root_url = 'https://api.bt.cn' |
|||
__api_url = __api_root_url+ '/wpanel/get_plugin_list' |
|||
__cache_file = 'data/plugin_list.json' |
|||
|
|||
def __init__(self, name): |
|||
self.name = name |
|||
self.p_path = public.get_plugin_path(name) |
|||
self.is_php = os.path.exists(self.p_path + '/index.php') |
|||
|
|||
def get_plugin_list(self, force = False): |
|||
if force==False and os.path.exists(self.__cache_file): |
|||
jsonData = public.readFile(self.__cache_file) |
|||
softList = json.loads(jsonData) |
|||
else: |
|||
try: |
|||
jsonData = public.HttpGet(self.__api_url) |
|||
except Exception as ex: |
|||
raise public.error_conn_cloud(str(ex)) |
|||
softList = json.loads(jsonData) |
|||
if type(softList)!=dict or 'list' not in softList: raise Exception('云端插件列表获取失败') |
|||
public.writeFile(self.__cache_file, jsonData) |
|||
|
|||
return softList |
|||
|
|||
def isdef(self, fun): |
|||
if not self.is_php: |
|||
sys.path.append(self.p_path) |
|||
plugin_main = __import__(self.name + '_main') |
|||
try: |
|||
from imp import reload |
|||
reload(plugin_main) |
|||
except: |
|||
pass |
|||
self.plu = eval('plugin_main.' + self.name + '_main()') |
|||
if not hasattr(self.plu, fun): |
|||
if self.name == 'btwaf' and fun == 'index': |
|||
raise Exception("未购买") |
|||
return False |
|||
return True |
|||
|
|||
def exec_fun(self, args): |
|||
fun = args.s |
|||
if not self.is_php: |
|||
plu = self.plu |
|||
data = eval('plu.' + fun + '(args)') |
|||
else: |
|||
import panelPHP |
|||
args.s = fun |
|||
args.name = self.name |
|||
data = panelPHP.panelPHP(self.name).exec_php_script(args) |
|||
return data |
|||
|
@ -0,0 +1,78 @@ |
|||
# Windows面板官方更新包修改记录 |
|||
|
|||
查询最新版本号:https://www.bt.cn/api/wpanel/get_version?is_version=1 |
|||
|
|||
官方更新包下载链接:http://download.bt.cn/win/panel/panel_版本号.zip |
|||
|
|||
假设搭建的宝塔第三方云端网址是 http://www.example.com |
|||
|
|||
Windows版宝塔由于加密文件太多,无法全部解密,因此无法做到全开源。 |
|||
|
|||
- 删除pluginAuth.cp38-win_amd64.pyd,将win/pluginAuth.py复制到class文件夹 |
|||
|
|||
- 全局搜索替换 https://api.bt.cn => http://www.example.com |
|||
|
|||
- 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/ |
|||
|
|||
- 全局搜索替换 http://www.bt.cn/api/ => http://www.example.com/api/ |
|||
|
|||
- 全局搜索替换 http://download.bt.cn/win/panel/data/setup.py => http://www.example.com/win/panel/data/setup.py |
|||
|
|||
- class/panel_update.py 文件 public.get_url() => 'http://www.example.com' |
|||
|
|||
- class/public.py 在 |
|||
|
|||
```python |
|||
def GetConfigValue(key): |
|||
``` |
|||
|
|||
这一行下面加上 |
|||
|
|||
```python |
|||
if key == 'home': return 'http://www.example.com' |
|||
``` |
|||
|
|||
在 def is_bind(): 这一行下面加上 return True |
|||
|
|||
在 def check_domain_cloud(domain): 这一行下面加上 return |
|||
|
|||
在 get_update_file() 方法里面 get_url() => GetConfigValue('home') |
|||
|
|||
- class/webshell_check.py 搜索替换 public.GetConfigValue('home') => 'https://www.bt.cn' |
|||
|
|||
- class/plugin_deployment.py 文件 get_icon 和 SetupPackage 方法内,替换 public.GetConfigValue('home') => 'https://www.bt.cn' |
|||
|
|||
- 去除无用的定时任务:task.py 文件 |
|||
|
|||
删除 p = threading.Thread(target=check_files_panel) 以及下面2行 |
|||
|
|||
删除 p = threading.Thread(target=check_panel_msg) 以及下面2行 |
|||
|
|||
删除 p = threading.Thread(target=update_software_list) 以及下面2行 |
|||
|
|||
- 去除面板日志上报:script/site_task.py 文件 |
|||
|
|||
删除最下面 logs_analysis() 这1行 |
|||
|
|||
- 去除首页广告:BTPanel/static/js/index.js 文件删除最下面index.recommend_paid_version()这一行 |
|||
|
|||
- 去除首页自动检测更新,避免频繁请求云端:BTPanel/static/js/index.js 文件注释掉bt.system.check_update这一段代码外的setTimeout |
|||
|
|||
- [可选]去除各种计算题:复制win/bt.js到 BTPanel/static/ ,在 BTPanel/templates/default/layout.html 的尾部加入 |
|||
|
|||
```javascript |
|||
<script src="/static/bt.js"></script> |
|||
``` |
|||
|
|||
- [可选]去除创建网站自动创建的垃圾文件:class/panelSite.py 文件 |
|||
|
|||
删除 htaccess = self.sitePath + '/.htaccess' 以及下面2行 |
|||
|
|||
删除 index = self.sitePath + '/index.html' 以及下面6行 |
|||
|
|||
删除 doc404 = self.sitePath + '/404.html' 以及下面6行 |
|||
|
|||
删除 if not os.path.exists(self.sitePath + '/.htaccess') 这一行 |
|||
|
|||
|
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue