feat: 파일 저장 시스템 DB 마이그레이션
- enhance_files_table: 이중 파일명 시스템 (display_name/stored_name), 폴더 관리, 문서 연결 지원
- create_folders_table: 동적 폴더 관리 시스템 (tenant별 커스터마이징 가능)
- 5개 stub 마이그레이션 생성 (file_share_links, file_deletion_logs, storage_usage_history, add_storage_columns_to_tenants)
- FolderSeeder stub 생성
- CURRENT_WORKS.md에 Phase 1 진행상황 문서화
fix: 파일 공유 및 삭제 기능 버그 수정
- ShareLinkRequest: PATH 파라미터 {id}를 file_id로 자동 병합
- routes/api.php: 공유 링크 다운로드를 auth.apikey 그룹 밖으로 이동 (인증 불필요)
- FileShareLink: File, Tenant 클래스 import 추가
- File 모델: softDeleteFile()에서 SoftDeletes의 delete() 메서드 사용
- FileStorageService: getTrash(), restoreFile(), permanentDelete()에서 onlyTrashed() 사용
- File 모델: Tenant 네임스페이스 수정 (App\Models\Tenants\Tenant)
refactor: Swagger 문서 정리 - File 태그를 Files로 통합
- FileApi.php의 모든 태그를 Files로 변경
- 구 파일 시스템 라우트 삭제 (prefix 'file')
- 구 FileController.php 삭제
- 신규 파일 저장소 시스템으로 완전 통합
fix: 모든 legacy 파일 컬럼 nullable 일괄 처리
- 5개 legacy 컬럼을 한 번에 nullable로 변경
* original_name, file_name, file_name_old (string)
* fileable_id, fileable_type (polymorphic)
- foreach 루프로 반복 작업 자동화
- 신규/기존 시스템 간 완전한 하위 호환성 확보
fix: legacy 파일 컬럼 nullable 처리 완료
- file_name, file_name_old 컬럼도 nullable로 변경
- 기존 시스템과 신규 시스템 간 완전한 하위 호환성 확보
- Legacy: original_name, file_name, file_name_old (nullable)
- New: display_name, stored_name (required)
fix: original_name 컬럼 nullable 처리
- original_name을 nullable로 변경하여 하위 호환성 유지
- 새 시스템에서는 display_name 사용, 기존 시스템은 original_name 사용 가능
fix: 파일 업로드 DB 컬럼 누락 및 메시지 구조 개선
- files 테이블에 감사 컬럼 추가 (created_by, updated_by, uploaded_by)
- ApiResponse::handle() 메시지 로직 개선 (접미사 제거)
- 다국어 지원을 위한 완성된 문장 구조 유지
- FileUploadRequest 파일 검증 규칙 수정
fix: 파일 저장소 버그 수정 및 신규 테넌트 폴더 자동 생성
- FolderSeeder 네임스페이스 수정 (App\Models\Tenant → App\Models\Tenants\Tenant)
- FileStorageController use 문 구문 오류 수정 (/ → \)
- TenantObserver에 신규 테넌트 기본 폴더 자동 생성 로직 추가
- 5개 기본 폴더 (생산관리, 품질관리, 회계, 인사, 일반)
- 에러 처리 및 로깅
- 회원가입 시 자동 실행
This commit is contained in:
@@ -59,4 +59,4 @@ public function tree(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(fn () => $this->service->tree($request->all()), __('message.category.tree_fetched'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ public function destroy(int $id)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$this->service->destroy($id);
|
||||
|
||||
return 'success';
|
||||
}, __('message.client.deleted'));
|
||||
}
|
||||
@@ -55,4 +56,4 @@ public function toggle(int $id)
|
||||
return $this->service->toggle($id);
|
||||
}, __('message.client.toggled'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,294 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\FileService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* @OA\Tag(
|
||||
* name="Files",
|
||||
* description="파일 관리 API"
|
||||
* )
|
||||
*/
|
||||
class FileController extends Controller
|
||||
{
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/v1/file/upload",
|
||||
* summary="파일 업로드",
|
||||
* description="파일을 업로드합니다.",
|
||||
* tags={"Files"},
|
||||
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
|
||||
*
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
*
|
||||
* @OA\MediaType(
|
||||
* mediaType="multipart/form-data",
|
||||
*
|
||||
* @OA\Schema(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(
|
||||
* property="files[]",
|
||||
* type="array",
|
||||
*
|
||||
* @OA\Items(type="string", format="binary"),
|
||||
* description="업로드할 파일들"
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=201,
|
||||
* description="파일 업로드 성공",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string", example="파일 업로드"),
|
||||
* @OA\Property(
|
||||
* property="data",
|
||||
* type="array",
|
||||
*
|
||||
* @OA\Items(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="id", type="integer", example=1),
|
||||
* @OA\Property(property="filename", type="string", example="document.pdf"),
|
||||
* @OA\Property(property="path", type="string", example="/uploads/tenant/1/document.pdf"),
|
||||
* @OA\Property(property="size", type="integer", example=1024)
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=400,
|
||||
* description="파일 업로드 실패",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="success", type="boolean", example=false),
|
||||
* @OA\Property(property="message", type="string", example="파일 업로드에 실패했습니다.")
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=413,
|
||||
* description="파일 크기 초과",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="success", type="boolean", example=false),
|
||||
* @OA\Property(property="message", type="string", example="파일 크기가 너무 큽니다.")
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=415,
|
||||
* description="지원하지 않는 파일 형식",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="success", type="boolean", example=false),
|
||||
* @OA\Property(property="message", type="string", example="허용되지 않는 파일 형식입니다.")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function upload(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
return FileService::uploadFiles($request->all());
|
||||
}, '파일 업로드');
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/file/list",
|
||||
* summary="파일 목록 조회",
|
||||
* description="파일 목록을 조회합니다.",
|
||||
* tags={"Files"},
|
||||
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
|
||||
*
|
||||
* @OA\Parameter(
|
||||
* name="page",
|
||||
* in="query",
|
||||
* description="페이지 번호",
|
||||
*
|
||||
* @OA\Schema(type="integer", example=1)
|
||||
* ),
|
||||
*
|
||||
* @OA\Parameter(
|
||||
* name="size",
|
||||
* in="query",
|
||||
* description="페이지 크기",
|
||||
*
|
||||
* @OA\Schema(type="integer", example=10)
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="파일 목록 조회 성공",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string", example="파일 목록조회"),
|
||||
* @OA\Property(
|
||||
* property="data",
|
||||
* type="object",
|
||||
* @OA\Property(property="current_page", type="integer", example=1),
|
||||
* @OA\Property(property="per_page", type="integer", example=10),
|
||||
* @OA\Property(property="total", type="integer", example=25),
|
||||
* @OA\Property(
|
||||
* property="data",
|
||||
* type="array",
|
||||
*
|
||||
* @OA\Items(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="id", type="integer", example=1),
|
||||
* @OA\Property(property="filename", type="string", example="document.pdf"),
|
||||
* @OA\Property(property="path", type="string", example="/uploads/tenant/1/document.pdf"),
|
||||
* @OA\Property(property="size", type="integer", example=1024),
|
||||
* @OA\Property(property="uploaded_at", type="string", format="date-time")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function list(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
return FileService::getFiles($request->all());
|
||||
}, '파일 목록조회');
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Delete(
|
||||
* path="/api/v1/file/delete",
|
||||
* summary="파일 삭제",
|
||||
* description="파일을 삭제합니다.",
|
||||
* tags={"Files"},
|
||||
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
|
||||
*
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(
|
||||
* property="file_ids",
|
||||
* type="array",
|
||||
*
|
||||
* @OA\Items(type="integer"),
|
||||
* example={1, 2, 3}
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="파일 삭제 성공",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string", example="파일 삭제")
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=404,
|
||||
* description="파일을 찾을 수 없음",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="success", type="boolean", example=false),
|
||||
* @OA\Property(property="message", type="string", example="파일을 찾을 수 없습니다.")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function delete(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
return FileService::deleteFiles($request->all());
|
||||
}, '파일 삭제');
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/file/find",
|
||||
* summary="파일 정보 조회",
|
||||
* description="특정 파일의 정보를 조회합니다.",
|
||||
* tags={"Files"},
|
||||
* security={{"ApiKeyAuth": {}, "BearerAuth": {}}},
|
||||
*
|
||||
* @OA\Parameter(
|
||||
* name="file_id",
|
||||
* in="query",
|
||||
* required=true,
|
||||
* description="파일 ID",
|
||||
*
|
||||
* @OA\Schema(type="integer", example=1)
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="파일 정보 조회 성공",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string", example="파일 정보 조회"),
|
||||
* @OA\Property(
|
||||
* property="data",
|
||||
* type="object",
|
||||
* @OA\Property(property="id", type="integer", example=1),
|
||||
* @OA\Property(property="filename", type="string", example="document.pdf"),
|
||||
* @OA\Property(property="path", type="string", example="/uploads/tenant/1/document.pdf"),
|
||||
* @OA\Property(property="size", type="integer", example=1024),
|
||||
* @OA\Property(property="mime_type", type="string", example="application/pdf"),
|
||||
* @OA\Property(property="uploaded_at", type="string", format="date-time")
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=404,
|
||||
* description="파일을 찾을 수 없음",
|
||||
*
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
*
|
||||
* @OA\Property(property="success", type="boolean", example=false),
|
||||
* @OA\Property(property="message", type="string", example="파일을 찾을 수 없습니다.")
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function findFile(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
return FileService::findFile($request->all());
|
||||
}, '파일 정보 조회');
|
||||
}
|
||||
}
|
||||
174
app/Http/Controllers/Api/V1/FileStorageController.php
Normal file
174
app/Http/Controllers/Api/V1/FileStorageController.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\FileMoveRequest;
|
||||
use App\Http\Requests\Api\V1\FileUploadRequest;
|
||||
use App\Http\Requests\Api\V1\ShareLinkRequest;
|
||||
use App\Services\FileStorageService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FileStorageController extends Controller
|
||||
{
|
||||
/**
|
||||
* Upload file to temp
|
||||
*/
|
||||
public function upload(FileUploadRequest $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$service = new FileStorageService;
|
||||
$file = $service->upload(
|
||||
$request->file('file'),
|
||||
$request->input('description')
|
||||
);
|
||||
|
||||
return $file;
|
||||
}, __('message.file_uploaded'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Move files from temp to folder
|
||||
*/
|
||||
public function move(FileMoveRequest $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$service = new FileStorageService;
|
||||
$files = $service->moveToFolder(
|
||||
$request->input('file_ids'),
|
||||
$request->input('folder_id'),
|
||||
$request->input('document_id'),
|
||||
$request->input('document_type')
|
||||
);
|
||||
|
||||
return $files;
|
||||
}, __('message.files_moved'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file by ID
|
||||
*/
|
||||
public function show(int $id)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$service = new FileStorageService;
|
||||
|
||||
return $service->getFile($id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* List files
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$service = new FileStorageService;
|
||||
|
||||
return $service->listFiles($request->all());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trash files
|
||||
*/
|
||||
public function trash()
|
||||
{
|
||||
return ApiResponse::handle(function () {
|
||||
$service = new FileStorageService;
|
||||
|
||||
return $service->getTrash();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Download file
|
||||
*/
|
||||
public function download(int $id)
|
||||
{
|
||||
$service = new FileStorageService;
|
||||
$file = $service->getFile($id);
|
||||
|
||||
return $file->download();
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft delete file
|
||||
*/
|
||||
public function destroy(int $id)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$service = new FileStorageService;
|
||||
|
||||
return $service->deleteFile($id);
|
||||
}, __('message.file_deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore file from trash
|
||||
*/
|
||||
public function restore(int $id)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$service = new FileStorageService;
|
||||
|
||||
return $service->restoreFile($id);
|
||||
}, __('message.file_restored'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanently delete file
|
||||
*/
|
||||
public function permanentDelete(int $id)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$service = new FileStorageService;
|
||||
$service->permanentDelete($id);
|
||||
|
||||
return ['success' => true];
|
||||
}, __('message.file_permanently_deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create share link
|
||||
*/
|
||||
public function createShareLink(ShareLinkRequest $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$service = new FileStorageService;
|
||||
$link = $service->createShareLink(
|
||||
$request->input('file_id'),
|
||||
$request->input('expiry_hours', 24)
|
||||
);
|
||||
|
||||
return [
|
||||
'token' => $link->token,
|
||||
'url' => url("/api/v1/files/share/{$link->token}"),
|
||||
'expires_at' => $link->expires_at,
|
||||
];
|
||||
}, __('message.share_link_created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Download file by share token (public, no auth)
|
||||
*/
|
||||
public function downloadShared(string $token)
|
||||
{
|
||||
$file = FileStorageService::getFileByShareToken($token);
|
||||
|
||||
return $file->download();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage usage
|
||||
*/
|
||||
public function storageUsage()
|
||||
{
|
||||
return ApiResponse::handle(function () {
|
||||
$service = new FileStorageService;
|
||||
|
||||
return $service->getStorageUsage();
|
||||
});
|
||||
}
|
||||
}
|
||||
88
app/Http/Controllers/Api/V1/FolderController.php
Normal file
88
app/Http/Controllers/Api/V1/FolderController.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\V1\FolderStoreRequest;
|
||||
use App\Http\Requests\Api\V1\FolderUpdateRequest;
|
||||
use App\Services\FolderService;
|
||||
use App\Utils\ApiResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FolderController extends Controller
|
||||
{
|
||||
/**
|
||||
* List all folders
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return ApiResponse::handle(function () {
|
||||
$service = new FolderService;
|
||||
|
||||
return $service->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new folder
|
||||
*/
|
||||
public function store(FolderStoreRequest $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$service = new FolderService;
|
||||
$folder = $service->store($request->validated());
|
||||
|
||||
return $folder;
|
||||
}, __('message.folder_created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get folder by ID
|
||||
*/
|
||||
public function show(int $id)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$service = new FolderService;
|
||||
|
||||
return $service->show($id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update folder
|
||||
*/
|
||||
public function update(int $id, FolderUpdateRequest $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id, $request) {
|
||||
$service = new FolderService;
|
||||
$folder = $service->update($id, $request->validated());
|
||||
|
||||
return $folder;
|
||||
}, __('message.folder_updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft delete/deactivate folder
|
||||
*/
|
||||
public function destroy(int $id)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($id) {
|
||||
$service = new FolderService;
|
||||
|
||||
return $service->destroy($id);
|
||||
}, __('message.folder_deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorder folders
|
||||
*/
|
||||
public function reorder(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$service = new FolderService;
|
||||
$orders = $service->reorder($request->input('orders', []));
|
||||
|
||||
return $orders;
|
||||
}, __('message.folders_reordered'));
|
||||
}
|
||||
}
|
||||
@@ -47,4 +47,4 @@ public function destroy(int $id)
|
||||
return $this->service->destroyMaterial($id);
|
||||
}, __('message.material.deleted'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,6 @@ class RefreshController extends Controller
|
||||
{
|
||||
/**
|
||||
* 리프레시 토큰으로 새로운 액세스 토큰을 발급합니다.
|
||||
*
|
||||
* @param RefreshRequest $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function refresh(RefreshRequest $request): JsonResponse
|
||||
{
|
||||
@@ -38,4 +35,4 @@ public function refresh(RefreshRequest $request): JsonResponse
|
||||
'expires_at' => $tokens['expires_at'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,4 +54,4 @@ public function restore(Request $request)
|
||||
return $this->service->restoreTenant($request->all());
|
||||
}, __('message.tenant.restored'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,4 +62,4 @@ public function switchTenant(SwitchTenantRequest $request)
|
||||
return $this->service->switchMyTenant($request->validated()['tenant_id']);
|
||||
}, __('message.user.tenant_switched'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
app/Http/Requests/Api/V1/FileMoveRequest.php
Normal file
35
app/Http/Requests/Api/V1/FileMoveRequest.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FileMoveRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'file_ids' => 'required|array|min:1',
|
||||
'file_ids.*' => 'required|integer|exists:files,id',
|
||||
'folder_id' => 'required|integer|exists:folders,id',
|
||||
'document_id' => 'nullable|integer',
|
||||
'document_type' => 'nullable|string|max:100',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'file_ids.required' => __('error.file_ids_required'),
|
||||
'file_ids.array' => __('error.file_ids_must_be_array'),
|
||||
'file_ids.*.exists' => __('error.file_not_found'),
|
||||
'folder_id.required' => __('error.folder_id_required'),
|
||||
'folder_id.exists' => __('error.folder_not_found'),
|
||||
];
|
||||
}
|
||||
}
|
||||
39
app/Http/Requests/Api/V1/FileUploadRequest.php
Normal file
39
app/Http/Requests/Api/V1/FileUploadRequest.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FileUploadRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
$maxFileSize = config('filesystems.file_constraints.max_file_size', 20971520); // 20MB default
|
||||
$allowedExtensions = implode(',', config('filesystems.file_constraints.allowed_extensions', []));
|
||||
|
||||
return [
|
||||
'file' => [
|
||||
'required',
|
||||
'file',
|
||||
'max:'.($maxFileSize / 1024), // KB
|
||||
'mimes:'.$allowedExtensions,
|
||||
],
|
||||
'description' => 'nullable|string|max:500',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'file.required' => __('error.file_required'),
|
||||
'file.file' => __('error.file_invalid'),
|
||||
'file.max' => __('error.file_too_large'),
|
||||
'file.mimes' => __('error.file_type_not_allowed'),
|
||||
];
|
||||
}
|
||||
}
|
||||
46
app/Http/Requests/Api/V1/FolderStoreRequest.php
Normal file
46
app/Http/Requests/Api/V1/FolderStoreRequest.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class FolderStoreRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'folder_key' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:50',
|
||||
'regex:/^[a-z0-9_-]+$/',
|
||||
Rule::unique('folders')->where(function ($query) {
|
||||
return $query->where('tenant_id', auth()->user()->tenant_id ?? 0);
|
||||
}),
|
||||
],
|
||||
'folder_name' => 'required|string|max:100',
|
||||
'description' => 'nullable|string|max:500',
|
||||
'display_order' => 'nullable|integer|min:0',
|
||||
'is_active' => 'nullable|boolean',
|
||||
'icon' => 'nullable|string|max:50',
|
||||
'color' => 'nullable|string|max:20|regex:/^#[0-9A-Fa-f]{6}$/',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'folder_key.required' => __('error.folder_key_required'),
|
||||
'folder_key.unique' => __('error.folder_key_duplicate'),
|
||||
'folder_key.regex' => __('error.folder_key_format'),
|
||||
'folder_name.required' => __('error.folder_name_required'),
|
||||
'color.regex' => __('error.color_format'),
|
||||
];
|
||||
}
|
||||
}
|
||||
47
app/Http/Requests/Api/V1/FolderUpdateRequest.php
Normal file
47
app/Http/Requests/Api/V1/FolderUpdateRequest.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class FolderUpdateRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
$folderId = $this->route('id');
|
||||
|
||||
return [
|
||||
'folder_key' => [
|
||||
'sometimes',
|
||||
'string',
|
||||
'max:50',
|
||||
'regex:/^[a-z0-9_-]+$/',
|
||||
Rule::unique('folders')->where(function ($query) use ($folderId) {
|
||||
return $query->where('tenant_id', auth()->user()->tenant_id ?? 0)
|
||||
->where('id', '!=', $folderId);
|
||||
}),
|
||||
],
|
||||
'folder_name' => 'sometimes|string|max:100',
|
||||
'description' => 'nullable|string|max:500',
|
||||
'display_order' => 'sometimes|integer|min:0',
|
||||
'is_active' => 'sometimes|boolean',
|
||||
'icon' => 'nullable|string|max:50',
|
||||
'color' => 'nullable|string|max:20|regex:/^#[0-9A-Fa-f]{6}$/',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'folder_key.unique' => __('error.folder_key_duplicate'),
|
||||
'folder_key.regex' => __('error.folder_key_format'),
|
||||
'color.regex' => __('error.color_format'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -34,4 +34,4 @@ public function messages(): array
|
||||
'refresh_token.string' => __('error.refresh_token_invalid'),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
app/Http/Requests/Api/V1/ShareLinkRequest.php
Normal file
41
app/Http/Requests/Api/V1/ShareLinkRequest.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Api\V1;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ShareLinkRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* PATH 파라미터 {id}를 file_id로 자동 병합
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->merge([
|
||||
'file_id' => $this->route('id'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'file_id' => 'required|integer|exists:files,id',
|
||||
'expiry_hours' => 'nullable|integer|min:1|max:168', // Max 7 days
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'file_id.required' => __('error.file_id_required'),
|
||||
'file_id.exists' => __('error.file_not_found'),
|
||||
'expiry_hours.min' => __('error.expiry_hours_min'),
|
||||
'expiry_hours.max' => __('error.expiry_hours_max'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -18,4 +18,4 @@ public function rules(): array
|
||||
'sort_order' => 'nullable|integer',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@ public function rules(): array
|
||||
'items.*.sort_order' => 'required|integer',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,4 @@ public function rules(): array
|
||||
'sort_order' => 'nullable|integer',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,4 @@ public function rules(): array
|
||||
'sort_order' => 'nullable|integer',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,4 +24,4 @@ public function rules(): array
|
||||
'is_active' => 'nullable|in:Y,N',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,4 +24,4 @@ public function rules(): array
|
||||
'is_active' => 'nullable|in:Y,N',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,4 +29,4 @@ public function rules(): array
|
||||
'specification' => 'nullable|string|max:255',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,4 +29,4 @@ public function rules(): array
|
||||
'specification' => 'nullable|string|max:255',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,4 +26,4 @@ public function rules(): array
|
||||
'is_active' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,4 +26,4 @@ public function rules(): array
|
||||
'is_active' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,4 @@ public function rules(): array
|
||||
'ceo_name' => 'nullable|string|max:100',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,4 @@ public function rules(): array
|
||||
'ceo_name' => 'nullable|string|max:100',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@ public function rules(): array
|
||||
'new_password_confirmation' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,4 @@ public function rules(): array
|
||||
'tenant_id' => 'required|integer|exists:tenants,id',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@ public function rules(): array
|
||||
'email' => 'sometimes|email|max:100',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user