更新
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller\api;
|
||||
|
||||
use app\model\User;
|
||||
use app\model\MembershipLevel;
|
||||
use app\model\Role;
|
||||
use app\service\JwtService;
|
||||
use app\service\SettingsService;
|
||||
use app\service\UserContextService;
|
||||
|
||||
class Auth extends BaseApi
|
||||
{
|
||||
public function guest()
|
||||
{
|
||||
$guestKey = strtolower(trim((string) $this->request->post('guest_key', '')));
|
||||
if (!preg_match('/^[a-f0-9]{64}$/', $guestKey)) {
|
||||
return $this->error('游客标识无效,请刷新页面重试', 422);
|
||||
}
|
||||
|
||||
// Only persist a one-way fingerprint; the random browser key remains the credential.
|
||||
$fingerprint = hash('sha256', $guestKey);
|
||||
$username = 'guest_' . substr($fingerprint, 0, 32);
|
||||
$user = User::where('username', $username)->find();
|
||||
|
||||
if (!$user) {
|
||||
$roleId = Role::where('slug', 'user')->value('id');
|
||||
$membershipId = MembershipLevel::where('slug', 'free')->value('id') ?: 1;
|
||||
|
||||
try {
|
||||
$user = User::create([
|
||||
'username' => $username,
|
||||
'email' => $username . '@guest.local',
|
||||
'password_hash' => password_hash(bin2hex(random_bytes(32)), PASSWORD_BCRYPT),
|
||||
'nickname' => '访客',
|
||||
'role' => 'user',
|
||||
'role_id' => $roleId ?: null,
|
||||
'membership_level_id' => $membershipId,
|
||||
'status' => 'active',
|
||||
]);
|
||||
} catch (\Throwable $exception) {
|
||||
// Two tabs may initialize the same browser guest at the same time.
|
||||
$user = User::where('username', $username)->find();
|
||||
if (!$user) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($user->status !== 'active') {
|
||||
return $this->error('游客访问暂不可用', 403);
|
||||
}
|
||||
|
||||
$token = JwtService::generateToken(['user_id' => $user->id]);
|
||||
|
||||
return $this->success([
|
||||
'token' => $token,
|
||||
'user' => $this->formatUser($user->id),
|
||||
], '已进入游客模式');
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
$allow = SettingsService::get('allow_register', true);
|
||||
if ($allow !== true && $allow !== 'true') {
|
||||
return $this->error('当前不允许注册');
|
||||
}
|
||||
|
||||
$input = $this->request->post();
|
||||
$username = trim($input['username'] ?? '');
|
||||
$email = trim($input['email'] ?? '');
|
||||
$password = $input['password'] ?? '';
|
||||
|
||||
if (strlen($username) < 3 || strlen($username) > 50) {
|
||||
return $this->error('用户名长度需 3-50 个字符');
|
||||
}
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
return $this->error('邮箱格式不正确');
|
||||
}
|
||||
if (strlen($password) < 6) {
|
||||
return $this->error('密码至少 6 位');
|
||||
}
|
||||
|
||||
if (User::where('username', $username)->whereOr('email', $email)->find()) {
|
||||
return $this->error('用户名或邮箱已存在');
|
||||
}
|
||||
|
||||
$defaultRoleId = \app\model\Role::where('slug', 'user')->value('id');
|
||||
|
||||
$user = User::create([
|
||||
'username' => $username,
|
||||
'email' => $email,
|
||||
'password_hash' => password_hash($password, PASSWORD_BCRYPT),
|
||||
'nickname' => $username,
|
||||
'membership_level_id' => 1,
|
||||
'role_id' => $defaultRoleId ?: null,
|
||||
]);
|
||||
|
||||
$token = JwtService::generateToken(['user_id' => $user->id]);
|
||||
|
||||
return $this->success([
|
||||
'token' => $token,
|
||||
'user' => $this->formatUser($user->id),
|
||||
], '注册成功');
|
||||
}
|
||||
|
||||
public function login()
|
||||
{
|
||||
$input = $this->request->post();
|
||||
$account = trim($input['account'] ?? '');
|
||||
$password = $input['password'] ?? '';
|
||||
|
||||
if (!$account || !$password) {
|
||||
return $this->error('请输入账号和密码');
|
||||
}
|
||||
|
||||
$user = User::where(function ($query) use ($account) {
|
||||
$query->where('username', $account)->whereOr('email', $account);
|
||||
})->where('status', 'active')->find();
|
||||
|
||||
if (!$user || !password_verify($password, $user->password_hash)) {
|
||||
return $this->error('账号或密码错误', 401);
|
||||
}
|
||||
|
||||
$user->save(['last_login_at' => date('Y-m-d H:i:s')]);
|
||||
$token = JwtService::generateToken(['user_id' => $user->id]);
|
||||
|
||||
return $this->success([
|
||||
'token' => $token,
|
||||
'user' => $this->formatUser($user->id),
|
||||
], '登录成功');
|
||||
}
|
||||
|
||||
public function me()
|
||||
{
|
||||
return $this->success($this->authUser());
|
||||
}
|
||||
|
||||
public function updateProfile()
|
||||
{
|
||||
$user = $this->authUser();
|
||||
$nickname = trim($this->request->put('nickname', ''));
|
||||
|
||||
if ($nickname) {
|
||||
User::where('id', $user['id'])->update(['nickname' => $nickname]);
|
||||
}
|
||||
|
||||
return $this->success($this->formatUser($user['id']));
|
||||
}
|
||||
|
||||
public function logout()
|
||||
{
|
||||
return $this->success(null, '已退出');
|
||||
}
|
||||
|
||||
private function formatUser(int $userId): array
|
||||
{
|
||||
return UserContextService::formatPublicUser($userId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller\api;
|
||||
|
||||
use app\BaseController;
|
||||
use think\response\Json;
|
||||
|
||||
class BaseApi extends BaseController
|
||||
{
|
||||
protected function success(mixed $data = null, string $message = 'success', int $httpCode = 200): Json
|
||||
{
|
||||
return json([
|
||||
'code' => 0,
|
||||
'message' => $message,
|
||||
'data' => $data,
|
||||
], $httpCode);
|
||||
}
|
||||
|
||||
protected function error(string $message, int $httpCode = 400, int $code = 1): Json
|
||||
{
|
||||
return json([
|
||||
'code' => $code,
|
||||
'message' => $message,
|
||||
'data' => null,
|
||||
], $httpCode);
|
||||
}
|
||||
|
||||
protected function authUser(): array
|
||||
{
|
||||
return $this->request->authUser ?? [];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller\api;
|
||||
|
||||
use app\model\AiModel;
|
||||
use app\model\Conversation as ConversationModel;
|
||||
use app\model\Message;
|
||||
use app\service\ComfyUIService;
|
||||
use app\service\PermissionService;
|
||||
use think\exception\HttpResponseException;
|
||||
|
||||
class Conversation extends BaseApi
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$user = $this->authUser();
|
||||
$page = max(1, (int) $this->request->get('page', 1));
|
||||
$limit = min(50, max(1, (int) $this->request->get('limit', 20)));
|
||||
|
||||
$query = ConversationModel::alias('c')
|
||||
->leftJoin('ai_models m', 'c.model_id = m.id')
|
||||
->where('c.user_id', $user['id'])
|
||||
->whereNull('c.deleted_at')
|
||||
->field('c.*,m.name as model_name,m.provider as model_provider')
|
||||
->order(['c.is_pinned' => 'desc', 'c.updated_at' => 'desc']);
|
||||
|
||||
$total = (clone $query)->count();
|
||||
$list = $query->page($page, $limit)->select();
|
||||
|
||||
return $this->success([
|
||||
'list' => $list,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$user = $this->authUser();
|
||||
PermissionService::checkConversationLimit($user);
|
||||
|
||||
$input = $this->request->post();
|
||||
$modelId = $input['model_id'] ?? null;
|
||||
if ($modelId !== null && $modelId !== '') {
|
||||
$modelId = (int) $modelId;
|
||||
if ($modelId <= 0) {
|
||||
$modelId = null;
|
||||
}
|
||||
} else {
|
||||
$modelId = null;
|
||||
}
|
||||
|
||||
$conversation = ConversationModel::create([
|
||||
'user_id' => $user['id'],
|
||||
'title' => trim($input['title'] ?? '新对话'),
|
||||
'model_id' => $modelId,
|
||||
]);
|
||||
|
||||
return $this->success($this->findConversation((int) $conversation->id, (int) $user['id']), '创建成功');
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$user = $this->authUser();
|
||||
return $this->success($this->findConversation((int) $id, (int) $user['id']));
|
||||
}
|
||||
|
||||
public function update($id)
|
||||
{
|
||||
$user = $this->authUser();
|
||||
$conversation = ConversationModel::where('id', $id)
|
||||
->where('user_id', $user['id'])
|
||||
->whereNull('deleted_at')
|
||||
->find();
|
||||
|
||||
if (!$conversation) {
|
||||
return $this->error('会话不存在', 404);
|
||||
}
|
||||
|
||||
$input = $this->request->put();
|
||||
$data = [];
|
||||
if (isset($input['title'])) {
|
||||
$data['title'] = trim($input['title']);
|
||||
}
|
||||
if (isset($input['is_pinned'])) {
|
||||
$data['is_pinned'] = (int) $input['is_pinned'];
|
||||
}
|
||||
if (array_key_exists('model_id', $input)) {
|
||||
$mid = $input['model_id'];
|
||||
if ($mid === null || $mid === '') {
|
||||
$data['model_id'] = null;
|
||||
} else {
|
||||
$data['model_id'] = (int) $mid;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($data)) {
|
||||
return $this->error('无更新内容');
|
||||
}
|
||||
|
||||
$conversation->save($data);
|
||||
return $this->success($this->findConversation((int) $id, (int) $user['id']));
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
$user = $this->authUser();
|
||||
$conversation = ConversationModel::where('id', $id)
|
||||
->where('user_id', $user['id'])
|
||||
->whereNull('deleted_at')
|
||||
->find();
|
||||
|
||||
if (!$conversation) {
|
||||
return $this->error('会话不存在', 404);
|
||||
}
|
||||
|
||||
$conversation->save(['deleted_at' => date('Y-m-d H:i:s')]);
|
||||
return $this->success(null, '删除成功');
|
||||
}
|
||||
|
||||
public function messages($id)
|
||||
{
|
||||
$user = $this->authUser();
|
||||
$this->findConversation((int) $id, (int) $user['id']);
|
||||
|
||||
$rows = Message::where('conversation_id', $id)
|
||||
->field('id,role,content,content_type,attachments,created_at')
|
||||
->order('created_at', 'asc')
|
||||
->select();
|
||||
|
||||
$list = [];
|
||||
foreach ($rows as $item) {
|
||||
$attachments = $item->attachments;
|
||||
if (is_string($attachments)) {
|
||||
$attachments = json_decode($attachments, true) ?: [];
|
||||
}
|
||||
if (!is_array($attachments)) {
|
||||
$attachments = [];
|
||||
}
|
||||
|
||||
// 刷新后恢复未完成的 Comfy 生图任务
|
||||
$stillPending = false;
|
||||
$pendingJob = ComfyUIService::findPendingJob($attachments);
|
||||
if ($pendingJob && $item->role === 'assistant') {
|
||||
$modelId = (int) ($pendingJob['model_id'] ?? 0);
|
||||
$model = $modelId > 0 ? AiModel::find($modelId) : null;
|
||||
if ($model) {
|
||||
$recovered = ComfyUIService::recoverPendingMessage([
|
||||
'id' => (int) $item->id,
|
||||
'content' => (string) $item->content,
|
||||
'attachments' => $attachments,
|
||||
], $model, (int) $user['id']);
|
||||
|
||||
$item->content = $recovered['content'];
|
||||
$item->attachments = $recovered['attachments'];
|
||||
$attachments = $recovered['attachments'];
|
||||
$stillPending = empty($recovered['finished']);
|
||||
|
||||
Message::where('id', $item->id)->update([
|
||||
'content' => $recovered['content'],
|
||||
'attachments' => json_encode($recovered['attachments'], JSON_UNESCAPED_UNICODE),
|
||||
]);
|
||||
} else {
|
||||
$stillPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($item->role === 'assistant' && trim((string) $item->content) === '') {
|
||||
if (empty($attachments) || !ComfyUIService::hasImageAttachments($attachments)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 前端只展示真实媒体附件;pending 任务信息已体现在 content 文案中
|
||||
$visibleAttachments = array_values(array_filter(
|
||||
$attachments,
|
||||
fn ($a) => is_array($a) && ($a['type'] ?? '') !== ComfyUIService::JOB_TYPE
|
||||
));
|
||||
|
||||
$list[] = [
|
||||
'id' => $item->id,
|
||||
'role' => $item->role,
|
||||
'content' => $item->content,
|
||||
'content_type' => $item->content_type,
|
||||
'attachments' => $visibleAttachments,
|
||||
'pending_job' => $stillPending,
|
||||
'pending_image_count' => $stillPending ? (int) ($pendingJob['image_count'] ?? 0) : 0,
|
||||
'created_at' => $item->created_at,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->success($list);
|
||||
}
|
||||
|
||||
private function findConversation(int $id, int $userId): array
|
||||
{
|
||||
$conversation = ConversationModel::alias('c')
|
||||
->leftJoin('ai_models m', 'c.model_id = m.id')
|
||||
->where('c.id', $id)
|
||||
->where('c.user_id', $userId)
|
||||
->whereNull('c.deleted_at')
|
||||
->field('c.*,m.name as model_name,m.provider as model_provider')
|
||||
->find();
|
||||
|
||||
if (!$conversation) {
|
||||
throw new HttpResponseException(json([
|
||||
'code' => 1,
|
||||
'message' => '会话不存在',
|
||||
'data' => null,
|
||||
], 404));
|
||||
}
|
||||
|
||||
return $conversation->toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller\api;
|
||||
|
||||
use app\model\AiModel;
|
||||
use app\service\AgentCatalog;
|
||||
use app\service\SettingsService;
|
||||
|
||||
class Settings extends BaseApi
|
||||
{
|
||||
public function features()
|
||||
{
|
||||
return $this->success(SettingsService::getFeatures());
|
||||
}
|
||||
|
||||
public function publicSettings()
|
||||
{
|
||||
$allow = SettingsService::get('allow_register', true);
|
||||
return $this->success([
|
||||
'site_name' => SettingsService::get('site_name', 'AI Chat'),
|
||||
'allow_register' => $allow === true || $allow === 'true',
|
||||
'features' => SettingsService::getFeatures(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function models()
|
||||
{
|
||||
$list = AiModel::where('enabled', 1)
|
||||
->field('id,name,provider,model_id,is_default,support_context,support_image')
|
||||
->order('sort_order,id')
|
||||
->select();
|
||||
|
||||
return $this->success($list);
|
||||
}
|
||||
|
||||
public function agents()
|
||||
{
|
||||
return $this->success(AgentCatalog::publicList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller\api;
|
||||
|
||||
use app\model\UploadFile;
|
||||
use app\service\PermissionService;
|
||||
use think\exception\HttpResponseException;
|
||||
|
||||
class Upload extends BaseApi
|
||||
{
|
||||
public function upload()
|
||||
{
|
||||
$user = $this->authUser();
|
||||
$file = $this->request->file('file');
|
||||
|
||||
if (!$file) {
|
||||
return $this->error('请选择文件');
|
||||
}
|
||||
|
||||
try {
|
||||
$ext = strtolower($file->extension() ?: pathinfo($file->getOriginalName(), PATHINFO_EXTENSION));
|
||||
$ext = $ext ?: 'bin';
|
||||
$mime = $this->resolveMime($file, $ext);
|
||||
$fileType = $this->detectFileType($mime, $ext);
|
||||
|
||||
if (!empty($user['is_guest']) && $fileType === 'image') {
|
||||
return $this->error('游客模式不支持发送图片,请登录后再上传', 403);
|
||||
}
|
||||
|
||||
if ($fileType === 'document' && !in_array($mime, config('upload.allowed_documents'), true)) {
|
||||
$mime = self::DOCUMENT_MIME_BY_EXT[$ext] ?? $mime;
|
||||
}
|
||||
|
||||
if (!PermissionService::canUpload($user, $fileType)) {
|
||||
return $this->error('您没有权限上传此类型文件或功能未开启', 403);
|
||||
}
|
||||
|
||||
$maxSize = PermissionService::getMaxUploadSizeMb($user) * 1024 * 1024;
|
||||
if ($file->getSize() > $maxSize) {
|
||||
return $this->error('文件大小超出限制');
|
||||
}
|
||||
|
||||
if (!$this->isAllowedMime($mime, $fileType, $ext)) {
|
||||
return $this->error('不支持的文件类型: ' . $mime);
|
||||
}
|
||||
|
||||
$originalName = $file->getOriginalName();
|
||||
$fileSize = $file->getSize();
|
||||
|
||||
$uploadPath = rtrim(config('upload.path'), '/\\');
|
||||
$this->ensureUploadDir($uploadPath);
|
||||
|
||||
$subdir = date('Y/m/d');
|
||||
$storedBase = uniqid('', true) . '.' . $ext;
|
||||
$storedName = $subdir . '/' . $storedBase;
|
||||
$fullDir = $uploadPath . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $subdir);
|
||||
|
||||
if (!is_dir($fullDir) && !mkdir($fullDir, 0755, true) && !is_dir($fullDir)) {
|
||||
return $this->error('无法创建上传目录,请检查 uploads 权限', 500);
|
||||
}
|
||||
|
||||
if (!is_writable($fullDir)) {
|
||||
return $this->error('上传目录不可写,请执行: chmod -R 775 uploads && chown -R www-data:www-data uploads', 500);
|
||||
}
|
||||
|
||||
$moved = $file->move($fullDir, $storedBase);
|
||||
if (!$moved) {
|
||||
return $this->error('文件保存失败: ' . ($file->getError() ?: '未知错误'), 500);
|
||||
}
|
||||
|
||||
$record = UploadFile::create([
|
||||
'user_id' => $user['id'],
|
||||
'original_name' => $originalName,
|
||||
'stored_name' => $storedBase,
|
||||
'file_path' => $storedName,
|
||||
'mime_type' => $mime,
|
||||
'file_size' => $fileSize,
|
||||
'file_type' => $fileType,
|
||||
]);
|
||||
|
||||
return $this->success([
|
||||
'id' => $record->id,
|
||||
'url' => '/api/uploads/' . rawurlencode($storedBase),
|
||||
'name' => $originalName,
|
||||
'type' => $fileType,
|
||||
'mime' => $mime,
|
||||
'size' => $fileSize,
|
||||
]);
|
||||
} catch (\think\exception\FileException $e) {
|
||||
return $this->error('文件保存失败: ' . $e->getMessage(), 500);
|
||||
} catch (\Throwable $e) {
|
||||
return $this->error('上传失败: ' . $e->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function serve($filename)
|
||||
{
|
||||
$filename = basename($filename);
|
||||
$upload = UploadFile::where('stored_name', $filename)
|
||||
->whereOr('file_path', 'like', '%/' . $filename)
|
||||
->find();
|
||||
|
||||
if (!$upload) {
|
||||
throw new HttpResponseException(json([
|
||||
'code' => 1,
|
||||
'message' => '文件不存在',
|
||||
'data' => null,
|
||||
], 404));
|
||||
}
|
||||
|
||||
$path = rtrim(config('upload.path'), '/\\') . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $upload->file_path);
|
||||
if (!is_file($path)) {
|
||||
throw new HttpResponseException(json([
|
||||
'code' => 1,
|
||||
'message' => '文件不存在',
|
||||
'data' => null,
|
||||
], 404));
|
||||
}
|
||||
|
||||
$mime = $upload->mime_type ?: (@mime_content_type($path) ?: 'application/octet-stream');
|
||||
|
||||
return response(file_get_contents($path), 200, [
|
||||
'Content-Type' => $mime,
|
||||
'Content-Length' => (string) filesize($path),
|
||||
'Cache-Control' => 'public, max-age=604800',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文档扩展名 -> 常见但不同系统上可能检测出不一致的 MIME 兜底表。
|
||||
* .doc/.docx 等 Office 文档在不同服务器 fileinfo 版本下识别出的 MIME 差异很大,
|
||||
* 仅靠 MIME 白名单很容易误判为“不支持的文件类型”,因此这里用扩展名兜底放行。
|
||||
*/
|
||||
private const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'];
|
||||
|
||||
private const VIDEO_EXTENSIONS = ['mp4', 'webm', 'mov'];
|
||||
|
||||
private const DOCUMENT_EXTENSIONS = ['pdf', 'doc', 'docx', 'txt', 'md'];
|
||||
|
||||
private const IMAGE_MIME_BY_EXT = [
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'png' => 'image/png',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
'bmp' => 'image/bmp',
|
||||
];
|
||||
|
||||
private const DOCUMENT_MIME_BY_EXT = [
|
||||
'pdf' => 'application/pdf',
|
||||
'doc' => 'application/msword',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'txt' => 'text/plain',
|
||||
'md' => 'text/markdown',
|
||||
];
|
||||
|
||||
private function resolveMime($file, string $ext): string
|
||||
{
|
||||
$mime = $file->getMime();
|
||||
if (!$mime && is_file($file->getPathname())) {
|
||||
$mime = @mime_content_type($file->getPathname()) ?: '';
|
||||
}
|
||||
|
||||
$mime = strtolower(trim((string) $mime));
|
||||
if ($mime === 'image/jpg') {
|
||||
$mime = 'image/jpeg';
|
||||
}
|
||||
|
||||
if ($mime === '' || $mime === 'application/octet-stream') {
|
||||
$mime = self::IMAGE_MIME_BY_EXT[$ext] ?? self::DOCUMENT_MIME_BY_EXT[$ext] ?? $mime;
|
||||
}
|
||||
|
||||
return $mime ?: 'application/octet-stream';
|
||||
}
|
||||
|
||||
private function ensureUploadDir(string $uploadPath): void
|
||||
{
|
||||
if (is_dir($uploadPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mkdir($uploadPath, 0755, true) && !is_dir($uploadPath)) {
|
||||
throw new \RuntimeException('无法创建 uploads 目录: ' . $uploadPath);
|
||||
}
|
||||
}
|
||||
|
||||
private function detectFileType(string $mime, string $ext = ''): string
|
||||
{
|
||||
$ext = strtolower($ext);
|
||||
|
||||
if (in_array($ext, self::IMAGE_EXTENSIONS, true)) {
|
||||
return 'image';
|
||||
}
|
||||
if (in_array($ext, self::VIDEO_EXTENSIONS, true)) {
|
||||
return 'video';
|
||||
}
|
||||
if (str_starts_with($mime, 'image/')) {
|
||||
return 'image';
|
||||
}
|
||||
if (str_starts_with($mime, 'video/')) {
|
||||
return 'video';
|
||||
}
|
||||
if (str_starts_with($mime, 'audio/')) {
|
||||
return 'audio';
|
||||
}
|
||||
if (in_array($mime, config('upload.allowed_documents'), true)) {
|
||||
return 'document';
|
||||
}
|
||||
if (in_array(strtolower($ext), self::DOCUMENT_EXTENSIONS, true)) {
|
||||
return 'document';
|
||||
}
|
||||
return 'other';
|
||||
}
|
||||
|
||||
private function isAllowedMime(string $mime, string $fileType, string $ext = ''): bool
|
||||
{
|
||||
$config = config('upload');
|
||||
return match ($fileType) {
|
||||
'image' => in_array($mime, $config['allowed_images'], true)
|
||||
|| in_array(strtolower($ext), self::IMAGE_EXTENSIONS, true),
|
||||
'video' => in_array($mime, $config['allowed_videos'], true)
|
||||
|| in_array(strtolower($ext), self::VIDEO_EXTENSIONS, true),
|
||||
'audio' => in_array($mime, $config['allowed_audios'], true),
|
||||
'document' => in_array($mime, $config['allowed_documents'], true)
|
||||
|| in_array(strtolower($ext), self::DOCUMENT_EXTENSIONS, true),
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user