Merge branch 'dev'

This commit is contained in:
Wisp X 2022-03-23 17:28:14 +08:00
commit 317c2ea369
17 changed files with 399 additions and 125 deletions

View File

@ -0,0 +1,9 @@
<?php
namespace App\Enums;
final class PastedAction
{
const Upload = 2; // 直接上传
const Waiting = 1; // 等待上传
}

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

@ -13,6 +13,9 @@ final class UserConfigKey
/** @var string 默认权限 */
const DefaultPermission = 'default_permission';
/** @var string 图片粘贴后动作 */
const PastedAction = 'pasted_action';
/** @var string 上传是否自动清除预览 */
const IsAutoClearPreview = 'is_auto_clear_preview';
}

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'],
'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

@ -19,6 +19,7 @@ class UserSettingRequest extends FormRequest
'configs.default_album' => 'required|numeric',
'configs.default_strategy' => 'required|numeric',
'configs.default_permission' => 'required|in:1,0',
'configs.pasted_action' => 'required|in:1,2',
'configs.is_auto_clear_preview' => 'nullable|boolean'
];
}
@ -37,6 +38,8 @@ class UserSettingRequest extends FormRequest
'configs.default_strategy.numeric' => '默认策略选择错误',
'configs.default_permission.required' => '权限值选择错误',
'configs.default_permission.in' => '权限值不正确',
'configs.pasted_action.required' => '粘贴动作值选择错误',
'configs.pasted_action.in' => '粘贴动作值不正确',
'configs.is_auto_clear_preview.boolean' => '是否自动清除预览选择错误'
];
}

View File

@ -4,6 +4,7 @@ namespace App\Models;
use App\Enums\ConfigKey;
use App\Enums\ImagePermission;
use App\Enums\PastedAction;
use App\Enums\UserConfigKey;
use App\Utils;
use Carbon\Carbon;
@ -100,13 +101,7 @@ class User extends Authenticatable implements MustVerifyEmail
$user->group_id = Group::query()->where('is_default', true)->value('id');
// 初始容量
$user->capacity = Utils::config(ConfigKey::UserInitialCapacity);
$user->configs = collect([
UserConfigKey::DefaultAlbum => 0,
UserConfigKey::DefaultStrategy => 0,
UserConfigKey::DefaultPermission => ImagePermission::Private,
UserConfigKey::IsAutoClearPreview => false,
])->merge($user->configs ?: []);
$user->configs = collect(config('convention.app.user'))->merge($user->configs ?: []);
});
}

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;
@ -208,10 +214,15 @@ class ImageService
throw new UploadException('图片记录保存失败');
}
// 图片检测
if ($configs->get(GroupConfigKey::IsEnableScan)) {
// 图片检测,跳过 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),

View File

@ -10,14 +10,13 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use League\Flysystem\Filesystem;
use League\Flysystem\FilesystemException;
use League\Flysystem\Local\LocalFilesystemAdapter;
class UpgradeService
{
const ApiUrl = 'https://api.lsky.pro/v1';
const ApiUrl = 'https://api.lsky.pro/v2';
/** @var array|array[] 所有版本 */
protected array $versions = [];
@ -34,7 +33,7 @@ class UpgradeService
public function __construct(protected string $version)
{
$this->http = Http::baseUrl(self::ApiUrl)->withOptions(['timeout' => 30])->timeout(30);
$this->http = Http::baseUrl(self::ApiUrl)->withOptions(['timeout' => 1800])->timeout(1800);
$this->filesystem = new Filesystem(new LocalFilesystemAdapter(base_path()));
}
@ -55,7 +54,7 @@ class UpgradeService
public function getVersions(): Collection
{
if (! $this->versions) {
$response = $this->http->timeout(30)->get('/versions');
$response = $this->http->get('/versions');
if (! $response->successful()) {
throw new \Exception('无法请求升级服务器');
}
@ -84,19 +83,21 @@ class UpgradeService
@ini_set('memory_limit', '1G');
@ini_set('max_execution_time', '86400');
// 获取差异信息
$response = $this->http
->withOptions(['timeout' => 1800])
->timeout(1800)
->get('/diff/'.urlencode(Utils::config(ConfigKey::AppVersion)));
if (! $response->successful()) {
$response = $this->http->get('/diff/'.urlencode(Utils::config(ConfigKey::AppVersion)));
if ($response->failed()) {
throw new \Exception('无法请求升级服务器');
}
$files = $response->json();
$result = $response->json();
$files = $result['files'];
$this->setProgress('下载补丁包...');
foreach ($files as $file) {
if ($file['action'] === 'deleted') continue;
$this->filesystem->write($this->temp.'/'.$file['pathname'], base64_decode($file['content']));
$res = $this->http->baseUrl($result['download_url'])->get($file['pathname']);
if ($res->failed()) {
throw new \Exception("补丁文件 {$file['pathname']} 下载失败。");
}
$this->filesystem->write($this->temp.'/'.$file['pathname'], $res->body());
// 校验文件
if ($file['md5'] !== md5_file(base_path($this->temp).'/'.$file['pathname'])) {
throw new \Exception("补丁文件 {$file['pathname']} 校验失败。");

View File

@ -24,6 +24,7 @@
"league/flysystem-webdav": "^3.0",
"overtrue/flysystem-cos": "^5.0",
"overtrue/flysystem-qiniu": "^3.0",
"tencentcloud/ims": "^3.0",
"wispx/flysystem-upyun": "^1.0",
"zing/flysystem-oss": "^2.1"
},

123
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "611dd24a26159f4429cb23742e3ba089",
"content-hash": "0a46ddcea87c0f61efcd32e23425e568",
"packages": [
{
"name": "adbario/php-dot-notation",
@ -417,16 +417,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.215.0",
"version": "3.215.2",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "79c4ffdf93cdcc7be9196ae2e22f0d0323cb2557"
"reference": "8ddbc5242ec59a22b2c4704867e028343c6d8ade"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/79c4ffdf93cdcc7be9196ae2e22f0d0323cb2557",
"reference": "79c4ffdf93cdcc7be9196ae2e22f0d0323cb2557",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/8ddbc5242ec59a22b2c4704867e028343c6d8ade",
"reference": "8ddbc5242ec59a22b2c4704867e028343c6d8ade",
"shasum": ""
},
"require": {
@ -502,9 +502,9 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.215.0"
"source": "https://github.com/aws/aws-sdk-php/tree/3.215.2"
},
"time": "2022-03-18T18:16:01+00:00"
"time": "2022-03-22T19:02:23+00:00"
},
{
"name": "brick/math",
@ -7649,6 +7649,93 @@
],
"time": "2022-03-02T12:58:14+00:00"
},
{
"name": "tencentcloud/common",
"version": "3.0.599",
"source": {
"type": "git",
"url": "https://github.com/tencentcloud-sdk-php/common.git",
"reference": "135a83776d1e3d49394399e8e2eae08984593290"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tencentcloud-sdk-php/common/zipball/135a83776d1e3d49394399e8e2eae08984593290",
"reference": "135a83776d1e3d49394399e8e2eae08984593290",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.3||^7.0",
"php": ">=5.6.0"
},
"type": "library",
"autoload": {
"psr-4": {
"TencentCloud\\": "./src/TencentCloud"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "tencentcloudapi",
"email": "tencentcloudapi@tencent.com",
"homepage": "https://cloud.tencent.com/document/sdk/PHP",
"role": "Developer"
}
],
"description": "TencentCloudApi php sdk",
"homepage": "https://github.com/tencentcloud-sdk-php/common",
"support": {
"issues": "https://github.com/tencentcloud-sdk-php/common/issues",
"source": "https://github.com/tencentcloud-sdk-php/common/tree/3.0.599"
},
"time": "2022-03-23T00:34:04+00:00"
},
{
"name": "tencentcloud/ims",
"version": "3.0.599",
"source": {
"type": "git",
"url": "https://github.com/tencentcloud-sdk-php/ims.git",
"reference": "09e759818ed0cb8d4d2c82db1c6731294ae875f1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tencentcloud-sdk-php/ims/zipball/09e759818ed0cb8d4d2c82db1c6731294ae875f1",
"reference": "09e759818ed0cb8d4d2c82db1c6731294ae875f1",
"shasum": ""
},
"require": {
"tencentcloud/common": "3.0.599"
},
"type": "library",
"autoload": {
"psr-4": {
"TencentCloud\\": "./src/TencentCloud"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "tencentcloudapi",
"email": "tencentcloudapi@tencent.com",
"homepage": "https://github.com/tencentcloud-sdk-php/ims",
"role": "Developer"
}
],
"description": "TencentCloudApi php sdk ims",
"homepage": "https://github.com/tencentcloud-sdk-php/ims",
"support": {
"issues": "https://github.com/tencentcloud-sdk-php/ims/issues",
"source": "https://github.com/tencentcloud-sdk-php/ims/tree/3.0.599"
},
"time": "2022-03-23T00:41:49+00:00"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
"version": "2.2.4",
@ -7967,16 +8054,16 @@
},
{
"name": "zing/flysystem-oss",
"version": "2.2.0",
"version": "2.2.1",
"source": {
"type": "git",
"url": "https://github.com/zingimmick/flysystem-oss.git",
"reference": "168b3b24c6f9466b637dcc0ddb374e2f4f90d011"
"reference": "706cf7309b34dfed73f3051d48ae6adeda6ad205"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zingimmick/flysystem-oss/zipball/168b3b24c6f9466b637dcc0ddb374e2f4f90d011",
"reference": "168b3b24c6f9466b637dcc0ddb374e2f4f90d011",
"url": "https://api.github.com/repos/zingimmick/flysystem-oss/zipball/706cf7309b34dfed73f3051d48ae6adeda6ad205",
"reference": "706cf7309b34dfed73f3051d48ae6adeda6ad205",
"shasum": ""
},
"require": {
@ -8020,7 +8107,7 @@
],
"support": {
"issues": "https://github.com/zingimmick/flysystem-oss/issues",
"source": "https://github.com/zingimmick/flysystem-oss/tree/2.2.0"
"source": "https://github.com/zingimmick/flysystem-oss/tree/2.2.1"
},
"funding": [
{
@ -8028,7 +8115,7 @@
"type": "custom"
}
],
"time": "2022-03-14T15:48:04+00:00"
"time": "2022-03-21T09:15:45+00:00"
}
],
"packages-dev": [
@ -10694,16 +10781,16 @@
},
{
"name": "spatie/laravel-ignition",
"version": "1.1.0",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-ignition.git",
"reference": "5b8c360d1f6bcba339a6d593efa02816c06d17c3"
"reference": "f3243fd99351e0a79df6886a5354d8dd88d6d0d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/5b8c360d1f6bcba339a6d593efa02816c06d17c3",
"reference": "5b8c360d1f6bcba339a6d593efa02816c06d17c3",
"url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/f3243fd99351e0a79df6886a5354d8dd88d6d0d2",
"reference": "f3243fd99351e0a79df6886a5354d8dd88d6d0d2",
"shasum": ""
},
"require": {
@ -10777,7 +10864,7 @@
"type": "github"
}
],
"time": "2022-03-19T17:03:56+00:00"
"time": "2022-03-21T07:13:26+00:00"
},
{
"name": "symfony/debug",

View File

@ -4,15 +4,19 @@
use App\Enums\ConfigKey;
use App\Enums\GroupConfigKey;
use App\Enums\ImagePermission;
use App\Enums\Mail\SmtpOption;
use App\Enums\PastedAction;
use App\Enums\Scan\AliyunOption;
use App\Enums\Scan\TencentOption;
use App\Enums\UserConfigKey;
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 => '',
@ -37,59 +41,73 @@ return [
]
],
],
ConfigKey::Group => [
GroupConfigKey::MaximumFileSize => 5120,
GroupConfigKey::ConcurrentUploadNum => 3,
GroupConfigKey::IsEnableScan => 0,
GroupConfigKey::IsEnableWatermark => 0,
GroupConfigKey::IsEnableOriginalProtection => 0,
GroupConfigKey::ScannedAction => 'mark', // in mark or delete
GroupConfigKey::ScanConfigs => [
'driver' => 'aliyun',
'drivers' => [
'aliyun' => [
AliyunOption::AccessKeyId => '',
AliyunOption::AccessKeySecret => '',
AliyunOption::RegionId => '',
AliyunOption::Scenes => ['porn'],
AliyunOption::BizType => '',
],
],
],
GroupConfigKey::WatermarkConfigs => [
'driver' => 'font',
'drivers' => [
'font' => [
FontOption::Text => 'Lsky Pro',
FontOption::Position => 'bottom-right',
FontOption::Angle => 0,
FontOption::Size => 50,
FontOption::Font => '',
FontOption::Color => '#000000',
FontOption::X => 10,
FontOption::Y => 10,
],
'image' => [
ImageOption::Image => '',
ImageOption::Position => 'bottom-right',
ImageOption::Opacity => 100,
ImageOption::Rotate => 0,
ImageOption::Width => 0,
ImageOption::Height => 0,
ImageOption::X => 10,
ImageOption::Y => 10,
]
],
],
GroupConfigKey::LimitPerMinute => 20,
GroupConfigKey::LimitPerHour => 100,
GroupConfigKey::LimitPerDay => 300,
GroupConfigKey::LimitPerWeek => 600,
GroupConfigKey::LimitPerMonth => 999,
GroupConfigKey::AcceptedFileSuffixes => ['jpeg', 'jpg', 'png', 'gif', 'tif', 'bmp', 'ico', 'psd', 'webp'],
GroupConfigKey::PathNamingRule => '{Y}/{m}/{d}',
GroupConfigKey::FileNamingRule => '{uniqid}',
GroupConfigKey::ImageCacheTtl => 2626560,
],
],
'group' => [
GroupConfigKey::MaximumFileSize => 5120,
GroupConfigKey::ConcurrentUploadNum => 3,
GroupConfigKey::IsEnableScan => 0,
GroupConfigKey::IsEnableWatermark => 0,
GroupConfigKey::IsEnableOriginalProtection => 0,
GroupConfigKey::ScannedAction => 'mark', // in mark or delete
GroupConfigKey::ScanConfigs => [
'driver' => 'tencent',
'drivers' => [
'tencent' => [
TencentOption::Endpoint => 'ims.tencentcloudapi.com',
TencentOption::SecretId => '',
TencentOption::SecretKey => '',
TencentOption::Region => '',
TencentOption::BizType => ''
],
'aliyun' => [
AliyunOption::AccessKeyId => '',
AliyunOption::AccessKeySecret => '',
AliyunOption::RegionId => '',
AliyunOption::Scenes => ['porn'],
AliyunOption::BizType => '',
],
],
],
GroupConfigKey::WatermarkConfigs => [
'driver' => 'font',
'drivers' => [
'font' => [
FontOption::Text => 'Lsky Pro',
FontOption::Position => 'bottom-right',
FontOption::Angle => 0,
FontOption::Size => 50,
FontOption::Font => '',
FontOption::Color => '#000000',
FontOption::X => 10,
FontOption::Y => 10,
],
'image' => [
ImageOption::Image => '',
ImageOption::Position => 'bottom-right',
ImageOption::Opacity => 100,
ImageOption::Rotate => 0,
ImageOption::Width => 0,
ImageOption::Height => 0,
ImageOption::X => 10,
ImageOption::Y => 10,
]
],
],
GroupConfigKey::LimitPerMinute => 20,
GroupConfigKey::LimitPerHour => 100,
GroupConfigKey::LimitPerDay => 300,
GroupConfigKey::LimitPerWeek => 600,
GroupConfigKey::LimitPerMonth => 999,
GroupConfigKey::AcceptedFileSuffixes => ['jpeg', 'jpg', 'png', 'gif', 'tif', 'bmp', 'ico', 'psd', 'webp'],
GroupConfigKey::PathNamingRule => '{Y}/{m}/{d}',
GroupConfigKey::FileNamingRule => '{uniqid}',
GroupConfigKey::ImageCacheTtl => 2626560,
],
'user' => [
UserConfigKey::DefaultAlbum => 0,
UserConfigKey::DefaultStrategy => 0,
UserConfigKey::DefaultPermission => ImagePermission::Private,
UserConfigKey::PastedAction => PastedAction::Waiting,
UserConfigKey::IsAutoClearPreview => false,
]
];

View File

@ -19,9 +19,7 @@ class InstallSeeder extends Seeder
public function run()
{
$date = Carbon::now()->format('Y-m-d H:i:s');
$array = collect(config('convention.app'))->except([
ConfigKey::Group,
])->transform(function ($value, $key) use ($date) {
$array = collect(config('convention.app'))->transform(function ($value, $key) use ($date) {
return [
'name' => $key,
'value' => is_array($value) ? json_encode($value, JSON_UNESCAPED_UNICODE) : $value,

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)

View File

@ -1,6 +1,6 @@
@props(['id' => 'modal'])
<div {{ $attributes->merge(['class' => "fixed z-10 inset-0 overflow-y-auto"]) }} role="dialog" aria-modal="true" x-data x-cloak x-show="$store.modal.isOpen('{{ $id }}')">
<div {{ $attributes->merge(['id' => $id, 'class' => "fixed z-10 inset-0 overflow-y-auto"]) }} role="dialog" aria-modal="true" x-data x-cloak x-show="$store.modal.isOpen('{{ $id }}')">
<div class="flex min-h-screen text-center md:block md:px-2 lg:px-4" style="font-size: 0">
<div x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="transform opacity-0"

File diff suppressed because one or more lines are too long

View File

@ -24,7 +24,7 @@
@if(Auth::user()->group)
<option value="0">未选择</option>
@foreach(Auth::user()->group->strategies as $strategy)
<option value="{{ $strategy->id }}" @selected(Auth::user()->configs->get(\App\Enums\UserConfigKey::DefaultStrategy) == $strategy->id)>{{ $strategy->name }}</option>
<option value="{{ $strategy->id }}" @selected(Auth::user()->configs->get('default_strategy') == $strategy->id)>{{ $strategy->name }}</option>
@endforeach
@else
<option value="0">系统默认</option>
@ -38,7 +38,7 @@
@if(Auth::user()->albums->isNotEmpty())
<option value="0">未选择</option>
@foreach(Auth::user()->albums as $album)
<option value="{{ $album->id }}" @selected(Auth::user()->configs->get(\App\Enums\UserConfigKey::DefaultAlbum) == $album->id)>{{ $album->name }}</option>
<option value="{{ $album->id }}" @selected(Auth::user()->configs->get('default_album') == $album->id)>{{ $album->name }}</option>
@endforeach
@else
<option value="0">没有可用相册</option>
@ -53,20 +53,27 @@
<div class="col-span-6">
<label for="password" class="block text-sm font-medium text-gray-700">密码</label>
<x-input type="password" name="password" id="password" placeholder="不修改请留空" autocomplete="password" />
<x-input type="password" name="password" id="password" placeholder="不修改请留空" autocomplete="new-password" />
</div>
<div class="col-span-6">
<x-fieldset title="是否自动清除预览" faq="设置上传时,文件上传完成以后是否自动清除预览图片">
<x-fieldset-radio id="is_auto_clear_preview_yes" name="configs[is_auto_clear_preview]" value="1" :checked="Auth::user()->configs->get(\App\Enums\UserConfigKey::IsAutoClearPreview)"></x-fieldset-radio>
<x-fieldset-radio id="is_auto_clear_preview_no" name="configs[is_auto_clear_preview]" value="0" :checked="! Auth::user()->configs->get(\App\Enums\UserConfigKey::IsAutoClearPreview)"></x-fieldset-radio>
<x-fieldset-radio id="is_auto_clear_preview_yes" name="configs[is_auto_clear_preview]" value="1" :checked="Auth::user()->configs->get('is_auto_clear_preview')"></x-fieldset-radio>
<x-fieldset-radio id="is_auto_clear_preview_no" name="configs[is_auto_clear_preview]" value="0" :checked="! Auth::user()->configs->get('is_auto_clear_preview')"></x-fieldset-radio>
</x-fieldset>
</div>
<div class="col-span-6">
<x-fieldset title="图片粘贴后动作" faq="设置上传页面粘贴图片后的动作">
<x-fieldset-radio id="pasted_action_upload" name="configs[pasted_action]" value="{{ \App\Enums\PastedAction::Upload }}" :checked="Auth::user()->configs->get('pasted_action') == \App\Enums\PastedAction::Upload">直接上传</x-fieldset-radio>
<x-fieldset-radio id="pasted_action_waiting" name="configs[pasted_action]" value="{{ \App\Enums\PastedAction::Waiting }}" :checked="Auth::user()->configs->get('pasted_action') == \App\Enums\PastedAction::Waiting">等待上传</x-fieldset-radio>
</x-fieldset>
</div>
<div class="col-span-6">
<x-fieldset title="图片默认权限" faq="设置上传的图片默认的权限(公开还是私有,公开的图片将会出现在画廊中,你也可以通过图片管理单独设置权限)">
<x-fieldset-radio id="private" name="configs[default_permission]" value="{{ \App\Enums\ImagePermission::Private }}" :checked="Auth::user()->configs->get(\App\Enums\UserConfigKey::DefaultPermission) == \App\Enums\ImagePermission::Private">私有</x-fieldset-radio>
<x-fieldset-radio id="public" name="configs[default_permission]" value="{{ \App\Enums\ImagePermission::Public }}" :checked="Auth::user()->configs->get(\App\Enums\UserConfigKey::DefaultPermission) == \App\Enums\ImagePermission::Public">公开</x-fieldset-radio>
<x-fieldset-radio id="default_permission_private" name="configs[default_permission]" value="{{ \App\Enums\ImagePermission::Private }}" :checked="Auth::user()->configs->get('default_permission') == \App\Enums\ImagePermission::Private">私有</x-fieldset-radio>
<x-fieldset-radio id="default_permission_public" name="configs[default_permission]" value="{{ \App\Enums\ImagePermission::Public }}" :checked="Auth::user()->configs->get('default_permission') == \App\Enums\ImagePermission::Public">公开</x-fieldset-radio>
</x-fieldset>
</div>
</div>