支持腾讯云图片审核

This commit is contained in:
Wisp X 2022-03-23 17:00:47 +08:00
parent b33c309a56
commit 71924b0397
6 changed files with 151 additions and 17 deletions

View File

@ -0,0 +1,21 @@
<?php
namespace App\Enums\Scan;
final class TencentOption
{
/** @var string SecretId */
const SecretId = 'secret_id';
/** @var string SecretKey */
const SecretKey = 'secret_key';
/** @var string Region */
const Region = 'region';
/** @var string Endpoint */
const Endpoint = 'endpoint';
/** @var string 业务场景 */
const BizType = 'biz_type';
}

View File

@ -43,11 +43,17 @@ class GroupRequest extends FormRequest
'exclude_if:configs.is_enable_scan,false',
'in:mark,delete',
],
'configs.scan_configs.driver' => ['exclude_if:configs.is_enable_scan,false', 'in:aliyun'],
'configs.scan_configs.driver' => ['exclude_if:configs.is_enable_scan,false', 'in:tencent,aliyun,baidu'],
'configs.scan_configs.drivers.tencent.endpoint' => [$requiredIfReview('tencent')],
'configs.scan_configs.drivers.tencent.secret_id' => [$requiredIfReview('tencent')],
'configs.scan_configs.drivers.tencent.secret_key' => [$requiredIfReview('tencent')],
'configs.scan_configs.drivers.tencent.region' => [$requiredIfReview('tencent')],
'configs.scan_configs.drivers.tencent.biz_type' => '',
'configs.scan_configs.drivers.aliyun.access_key_id' => [$requiredIfReview('aliyun')],
'configs.scan_configs.drivers.aliyun.access_key_secret' => [$requiredIfReview('aliyun')],
'configs.scan_configs.drivers.aliyun.biz_type' => [$requiredIfReview('aliyun')],
'configs.scan_configs.drivers.aliyun.region_id' => [$requiredIfReview('aliyun')],
'configs.scan_configs.drivers.aliyun.biz_type' => '',
'configs.scan_configs.drivers.aliyun.scenes' => [$requiredIfReview('aliyun'), 'array'],
'configs.is_enable_original_protection' => 'boolean',
@ -110,10 +116,15 @@ class GroupRequest extends FormRequest
'configs.is_enable_scan' => '是否启用图片审核',
'configs.scanned_action' => '图片审核动作',
'configs.scan_configs.driver' => '图片审核驱动',
'configs.scan_configs.drivers.tencent.endpoint' => 'Endpoint',
'configs.scan_configs.drivers.tencent.secret_id' => 'SecretId',
'configs.scan_configs.drivers.tencent.secret_key' => 'SecretKey',
'configs.scan_configs.drivers.tencent.region' => '地域节点',
'configs.scan_configs.drivers.tencent.biz_type' => '业务场景',
'configs.scan_configs.drivers.aliyun.access_key_id' => 'AccessKeyId',
'configs.scan_configs.drivers.aliyun.access_key_secret' => 'AccessKeySecret',
'configs.scan_configs.drivers.aliyun.biz_type' => '场景名称',
'configs.scan_configs.drivers.aliyun.region_id' => '地域节点',
'configs.scan_configs.drivers.aliyun.biz_type' => '场景名称',
'configs.scan_configs.drivers.aliyun.scenes' => '审核场景',
'configs.is_enable_original_protection' => '是否启用原图保护功能',

View File

@ -8,6 +8,7 @@ use App\Enums\ConfigKey;
use App\Enums\GroupConfigKey;
use App\Enums\ImagePermission;
use App\Enums\Scan\AliyunOption;
use App\Enums\Scan\TencentOption;
use App\Enums\Strategy\CosOption;
use App\Enums\Strategy\FtpOption;
use App\Enums\Strategy\KodoOption;
@ -54,6 +55,11 @@ use League\Flysystem\WebDAV\WebDAVAdapter;
use Overtrue\Flysystem\Cos\CosAdapter;
use Overtrue\Flysystem\Qiniu\QiniuAdapter;
use Sabre\DAV\Client;
use TencentCloud\Common\Credential;
use TencentCloud\Common\Profile\ClientProfile;
use TencentCloud\Common\Profile\HttpProfile;
use TencentCloud\Ims\V20201229\ImsClient;
use TencentCloud\Ims\V20201229\Models\ImageModerationRequest;
use WispX\Flysystem\Upyun\UpyunAdapter;
use Zing\Flysystem\Oss\OssAdapter;
use OSS\OssClient;
@ -211,7 +217,12 @@ class ImageService
// 图片检测,跳过 tif 以及 psd 格式
if ($configs->get(GroupConfigKey::IsEnableScan) && ! in_array($extension, ['psd', 'tif'])) {
$scanConfigs = $configs->get(GroupConfigKey::ScanConfigs);
if ($this->scan($scanConfigs['driver'], collect($scanConfigs['drivers'][$scanConfigs['driver']]), $image)) {
if ($this->scan(
driver: $scanConfigs['driver'],
configs: collect($scanConfigs['drivers'][$scanConfigs['driver']]),
image: $image,
file: $file,
)) {
// 标记 or 删除
if ($configs->get(GroupConfigKey::ScannedAction) === 'delete') {
$image->delete();
@ -354,14 +365,47 @@ class ImageService
* @param $driver
* @param Collection $configs
* @param Image $image
* @param UploadedFile $file
* @return bool true=违规
* @throws UploadException
*/
public function scan($driver, Collection $configs, Image $image): bool
public function scan($driver, Collection $configs, Image $image, UploadedFile $file): bool
{
$flag = false;
try {
if ($driver === 'tencent') {
// 图片大小不得超过 5mb
if ($file->getSize() >= 5242880) {
return false;
}
$cred = new Credential($configs->get(TencentOption::SecretId), $configs->get(TencentOption::SecretKey));
$httpProfile = new HttpProfile();
$httpProfile->setEndpoint($configs->get(TencentOption::Endpoint));
$clientProfile = new ClientProfile();
$clientProfile->setHttpProfile($httpProfile);
$client = new ImsClient($cred, $configs->get(TencentOption::Region), $clientProfile);
$req = new ImageModerationRequest();
$params = [
"FileContent" => base64_encode($file->getContent()),
];
if ($configs->get(TencentOption::BizType)) {
$params['BizType'] = $configs->get(TencentOption::BizType);
}
$req->fromJsonString(json_encode($params));
$resp = $client->ImageModeration($req);
if ($resp->getSuggestion() === 'Block') {
$flag = true;
}
}
if ($driver === 'aliyun') {
// 20 mb以内、宽高不超过 30000px
if ($file->getSize() >= 20971520 || $image->width >= 30000 || $image->height >= 30000) {
return false;
}
AlibabaCloud::accessKeyClient(
$configs->get(AliyunOption::AccessKeyId),
$configs->get(AliyunOption::AccessKeySecret),
@ -386,7 +430,7 @@ class ImageService
}
}
if ($driver === 'tencent') {
if ($driver === 'baidu') {
}
} catch (\Throwable $e) {

View File

@ -6,13 +6,14 @@ use App\Enums\ConfigKey;
use App\Enums\GroupConfigKey;
use App\Enums\Mail\SmtpOption;
use App\Enums\Scan\AliyunOption;
use App\Enums\Scan\TencentOption;
use App\Enums\Watermark\FontOption;
use App\Enums\Watermark\ImageOption;
return [
'app' => [
ConfigKey::AppName => 'Lsky Pro',
ConfigKey::AppVersion => 'V 2.0.1',
ConfigKey::AppVersion => 'V 2.0.2',
ConfigKey::SiteKeywords => 'Lsky Pro,lsky,兰空图床',
ConfigKey::SiteDescription => 'Lsky Pro, Your photo album on the cloud.',
ConfigKey::SiteNotice => '',
@ -45,8 +46,15 @@ return [
GroupConfigKey::IsEnableOriginalProtection => 0,
GroupConfigKey::ScannedAction => 'mark', // in mark or delete
GroupConfigKey::ScanConfigs => [
'driver' => 'aliyun',
'driver' => 'tencent',
'drivers' => [
'tencent' => [
TencentOption::Endpoint => 'ims.tencentcloudapi.com',
TencentOption::SecretId => '',
TencentOption::SecretKey => '',
TencentOption::Region => '',
TencentOption::BizType => ''
],
'aliyun' => [
AliyunOption::AccessKeyId => '',
AliyunOption::AccessKeySecret => '',

View File

@ -116,9 +116,34 @@
</div>
<div class="col-span-6 mb-4">
<x-fieldset title="审核驱动">
<x-fieldset-radio id="configs[scan_configs][driver]" name="configs[scan_configs][driver]" data-select="scan" value="aliyun" checked>阿里云</x-fieldset-radio>
<x-fieldset-radio id="configs[scan_configs][driver]_tencent" name="configs[scan_configs][driver]" data-select="scan" value="tencent" checked>腾讯云</x-fieldset-radio>
<x-fieldset-radio id="configs[scan_configs][driver]_aliyun" name="configs[scan_configs][driver]" data-select="scan" value="aliyun">阿里云</x-fieldset-radio>
</x-fieldset>
</div>
<div class="hidden mb-4" data-scan-driver="tencent">
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][tencent][endpoint]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>Endpoint</label>
<x-input type="text" name="configs[scan_configs][drivers][tencent][endpoint]" id="configs[scan_configs][drivers][tencent][endpoint]" autocomplete="endpoint" placeholder="请输入 Endpoint" value="ims.tencentcloudapi.com" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][tencent][secret_id]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>SecretId</label>
<x-input type="text" name="configs[scan_configs][drivers][tencent][secret_id]" id="configs[scan_configs][drivers][tencent][secret_id]" autocomplete="secret_id" placeholder="请输入 SecretId" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][tencent][secret_key]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>SecretKey</label>
<x-input type="password" name="configs[scan_configs][drivers][tencent][secret_key]" id="configs[scan_configs][drivers][tencent][secret_key]" autocomplete="secret_key" placeholder="请输入 SecretKey" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][tencent][region]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>地域</label>
<x-input type="text" name="configs[scan_configs][drivers][tencent][region]" id="configs[scan_configs][drivers][tencent][region]" autocomplete="region" placeholder="请输入地域节点例如ap-beijing" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][tencent][biz_type]" class="block text-sm font-medium text-gray-700">场景名称</label>
<x-input type="text" name="configs[scan_configs][drivers][tencent][biz_type]" id="configs[scan_configs][drivers][tencent][biz_type]" autocomplete="biz_type" placeholder="业务场景名称,可为空" />
</div>
</div>
<div class="hidden mb-4" data-scan-driver="aliyun">
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][aliyun][access_key_id]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>AccessKeyId</label>
@ -128,14 +153,14 @@
<label for="configs[scan_configs][drivers][aliyun][access_key_secret]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>AccessKeySecret</label>
<x-input type="password" name="configs[scan_configs][drivers][aliyun][access_key_secret]" id="configs[scan_configs][drivers][aliyun][access_key_secret]" autocomplete="access_key_id" placeholder="请输入 AccessKeySecret" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][aliyun][biz_type]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>场景名称</label>
<x-input type="text" name="configs[scan_configs][drivers][aliyun][biz_type]" id="configs[scan_configs][drivers][aliyun][biz_type]" autocomplete="biz_type" placeholder="请输入业务场景名称" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][aliyun][region_id]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>地域节点</label>
<x-input type="text" name="configs[scan_configs][drivers][aliyun][region_id]" id="configs[scan_configs][drivers][aliyun][region_id]" autocomplete="region_id" placeholder="请输入地域节点例如cn-shanghai" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][aliyun][biz_type]" class="block text-sm font-medium text-gray-700">场景名称</label>
<x-input type="text" name="configs[scan_configs][drivers][aliyun][biz_type]" id="configs[scan_configs][drivers][aliyun][biz_type]" autocomplete="biz_type" placeholder="请输入业务场景名称" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<x-fieldset title="审核场景">
@foreach($scenes as $key => $scene)

View File

@ -112,9 +112,34 @@
</div>
<div class="col-span-6 mb-4">
<x-fieldset title="审核驱动">
<x-fieldset-radio id="configs[scan_configs][driver]_tencent" name="configs[scan_configs][driver]" data-select="scan" value="tencent" :checked="($group->configs['scan_configs']['driver'] ?? '') === 'tencent'">腾讯云</x-fieldset-radio>
<x-fieldset-radio id="configs[scan_configs][driver]" name="configs[scan_configs][driver]" data-select="scan" value="aliyun" :checked="($group->configs['scan_configs']['driver'] ?? '') === 'aliyun'">阿里云</x-fieldset-radio>
</x-fieldset>
</div>
<div class="hidden mb-4" data-scan-driver="tencent">
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][tencent][endpoint]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>Endpoint</label>
<x-input type="text" name="configs[scan_configs][drivers][tencent][endpoint]" id="configs[scan_configs][drivers][tencent][endpoint]" autocomplete="endpoint" placeholder="请输入 Endpoint" value="{{ $group->configs['scan_configs']['drivers']['tencent']['endpoint'] ?? '' }}" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][tencent][secret_id]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>SecretId</label>
<x-input type="text" name="configs[scan_configs][drivers][tencent][secret_id]" id="configs[scan_configs][drivers][tencent][secret_id]" autocomplete="secret_id" placeholder="请输入 SecretId" value="{{ $group->configs['scan_configs']['drivers']['tencent']['secret_id'] ?? '' }}" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][tencent][secret_key]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>SecretKey</label>
<x-input type="password" name="configs[scan_configs][drivers][tencent][secret_key]" id="configs[scan_configs][drivers][tencent][secret_key]" autocomplete="secret_key" placeholder="请输入 SecretKey" value="{{ $group->configs['scan_configs']['drivers']['tencent']['secret_key'] ?? '' }}" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][tencent][region]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>地域</label>
<x-input type="text" name="configs[scan_configs][drivers][tencent][region]" id="configs[scan_configs][drivers][tencent][region]" autocomplete="region" placeholder="请输入地域节点例如ap-beijing" value="{{ $group->configs['scan_configs']['drivers']['tencent']['region'] ?? '' }}" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][tencent][biz_type]" class="block text-sm font-medium text-gray-700">场景名称</label>
<x-input type="text" name="configs[scan_configs][drivers][tencent][biz_type]" id="configs[scan_configs][drivers][tencent][biz_type]" autocomplete="biz_type" placeholder="业务场景名称,可为空" value="{{ $group->configs['scan_configs']['drivers']['tencent']['biz_type'] ?? '' }}" />
</div>
</div>
<div class="hidden mb-4" data-scan-driver="aliyun">
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][aliyun][access_key_id]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>AccessKeyId</label>
@ -124,14 +149,14 @@
<label for="configs[scan_configs][drivers][aliyun][access_key_secret]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>AccessKeySecret</label>
<x-input type="password" name="configs[scan_configs][drivers][aliyun][access_key_secret]" id="configs[scan_configs][drivers][aliyun][access_key_secret]" autocomplete="access_key_id" placeholder="请输入 AccessKeySecret" value="{{ $group->configs['scan_configs']['drivers']['aliyun']['access_key_secret'] ?? '' }}" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][aliyun][biz_type]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>场景名称</label>
<x-input type="text" name="configs[scan_configs][drivers][aliyun][biz_type]" id="configs[scan_configs][drivers][aliyun][biz_type]" autocomplete="biz_type" placeholder="请输入业务场景名称" value="{{ $group->configs['scan_configs']['drivers']['aliyun']['biz_type'] ?? '' }}" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][aliyun][region_id]" class="block text-sm font-medium text-gray-700"><span class="text-red-600">*</span>地域节点</label>
<x-input type="text" name="configs[scan_configs][drivers][aliyun][region_id]" id="configs[scan_configs][drivers][aliyun][region_id]" autocomplete="region_id" placeholder="请输入地域节点例如cn-shanghai" value="{{ $group->configs['scan_configs']['drivers']['aliyun']['region_id'] ?? '' }}" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<label for="configs[scan_configs][drivers][aliyun][biz_type]" class="block text-sm font-medium text-gray-700">场景名称</label>
<x-input type="text" name="configs[scan_configs][drivers][aliyun][biz_type]" id="configs[scan_configs][drivers][aliyun][biz_type]" autocomplete="biz_type" placeholder="请输入业务场景名称" value="{{ $group->configs['scan_configs']['drivers']['aliyun']['biz_type'] ?? '' }}" />
</div>
<div class="col-span-6 sm:col-span-3 mb-4">
<x-fieldset title="审核场景">
@foreach($scenes as $key => $scene)