'modules', 'plugin' => 'plugins', 'template' => 'templates', ]; public function __construct( private CustomAssetService $service, ) { parent::__construct(); } /** * 사용자 추가 에셋 목록 조회. * * @param string $type 확장 타입 (`module` | `plugin` | `template`) * @param string $identifier 확장 식별자 * @return JsonResponse 파일 목록 + 편집기 메타(허용 확장자·크기 상한) */ public function index(string $type, string $identifier): JsonResponse { try { $files = $this->service->list($this->resolveType($type), $identifier); } catch (CustomAssetOperationException $e) { return $this->error($e->errorKey, 422, null, $e->params); } catch (\Throwable $e) { Log::error('사용자 추가 에셋 목록 조회 실패', [ 'type' => $type, 'identifier' => $identifier, 'error' => $e->getMessage(), ]); return $this->error('custom_assets.errors.read_failed', 500, $e, ['path' => 'custom/']); } return $this->success('custom_assets.messages.listed', [ 'files' => $files, 'editable_extensions' => CustomAssetService::EDITABLE_EXTENSIONS, 'uploadable_extensions' => AllowedTemplateFileType::getAllowedExtensions(), 'max_text_bytes' => CustomAssetService::MAX_TEXT_BYTES, 'max_upload_bytes' => CustomAssetService::MAX_UPLOAD_BYTES, ]); } /** * 텍스트 파일 본문 조회. * * @param ReadExtensionCustomAssetRequest $request 검증된 요청 (`path`) * @param string $type 확장 타입 * @param string $identifier 확장 식별자 * @return JsonResponse 본문 응답 */ public function show(ReadExtensionCustomAssetRequest $request, string $type, string $identifier): JsonResponse { $path = (string) $request->validated('path'); try { $file = $this->service->read($this->resolveType($type), $identifier, $path); } catch (CustomAssetOperationException $e) { return $this->error($e->errorKey, 422, null, $e->params); } catch (\Throwable $e) { Log::error('사용자 추가 에셋 본문 조회 실패', [ 'type' => $type, 'identifier' => $identifier, 'error' => $e->getMessage(), ]); return $this->error('custom_assets.errors.read_failed', 500, $e, ['path' => $path]); } return $this->success('custom_assets.messages.listed', $file); } /** * 텍스트 파일 본문 저장 (없으면 생성). * * @param SaveExtensionCustomAssetRequest $request 검증된 요청 (`path`, `content`) * @param string $type 확장 타입 * @param string $identifier 확장 식별자 * @return JsonResponse 저장 결과 */ public function store(SaveExtensionCustomAssetRequest $request, string $type, string $identifier): JsonResponse { $path = (string) $request->validated('path'); try { $saved = $this->service->save( $this->resolveType($type), $identifier, $path, (string) $request->validated('content'), ); } catch (CustomAssetOperationException $e) { return $this->error($e->errorKey, 422, null, $e->params); } catch (\Throwable $e) { Log::error('사용자 추가 에셋 저장 실패', [ 'type' => $type, 'identifier' => $identifier, 'error' => $e->getMessage(), ]); return $this->error('custom_assets.errors.write_failed', 500, $e, ['path' => $path]); } return $this->success('custom_assets.messages.saved', $saved); } /** * 파일 업로드 (폰트·이미지 등 바이너리 포함). * * @param UploadExtensionCustomAssetRequest $request 검증된 요청 (`file`, `directory`) * @param string $type 확장 타입 * @param string $identifier 확장 식별자 * @return JsonResponse 업로드 결과 */ public function upload(UploadExtensionCustomAssetRequest $request, string $type, string $identifier): JsonResponse { $directory = $request->validated('directory'); try { $uploaded = $this->service->upload( $this->resolveType($type), $identifier, $request->file('file'), is_string($directory) ? $directory : null, ); } catch (CustomAssetOperationException $e) { return $this->error($e->errorKey, 422, null, $e->params); } catch (\Throwable $e) { Log::error('사용자 추가 에셋 업로드 실패', [ 'type' => $type, 'identifier' => $identifier, 'error' => $e->getMessage(), ]); return $this->error('custom_assets.errors.write_failed', 500, $e, ['path' => (string) $directory]); } return $this->success('custom_assets.messages.uploaded', $uploaded); } /** * 파일 삭제. * * @param ReadExtensionCustomAssetRequest $request 검증된 요청 (`path`) * @param string $type 확장 타입 * @param string $identifier 확장 식별자 * @return JsonResponse 삭제 결과 */ public function destroy(ReadExtensionCustomAssetRequest $request, string $type, string $identifier): JsonResponse { $path = (string) $request->validated('path'); try { $this->service->delete($this->resolveType($type), $identifier, $path); } catch (CustomAssetOperationException $e) { return $this->error($e->errorKey, 422, null, $e->params); } catch (\Throwable $e) { Log::error('사용자 추가 에셋 삭제 실패', [ 'type' => $type, 'identifier' => $identifier, 'error' => $e->getMessage(), ]); return $this->error('custom_assets.errors.delete_failed', 500, $e, ['path' => $path]); } return $this->success('custom_assets.messages.deleted', ['path' => $path]); } /** * 라우트 타입 파라미터를 해석기 어휘로 바꿉니다. * * 라우트 정규식이 이미 세 값으로 제한하지만, 매핑에 없으면 그대로 넘긴다 — * 해석기가 알 수 없는 타입을 무효로 판정해 422 를 만든다(빈 목록으로 조용히 * 통과시키지 않는다). * * @param string $type 라우트 파라미터 * @return string 해석기 어휘 */ private function resolveType(string $type): string { return self::TYPE_MAP[$type] ?? $type; } }