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
-
102app/controller/Api.php
-
105app/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