Files
sam-manage/app/Http/Controllers/Additional/PptxController.php

185 lines
6.0 KiB
PHP
Raw Normal View History

<?php
namespace App\Http\Controllers\Additional;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
/**
* 추가기능 > PPTX 관리 컨트롤러
* 파일시스템 스캔 기반으로 PPTX 파일 목록 조회 다운로드
*/
class PptxController extends Controller
{
private array $scanPaths = [
'/var/www/docs/pptx-output' => ['label' => '산출물', 'source' => 'docs'],
'/var/www/docs/rules' => ['label' => '정책/규칙', 'source' => 'docs'],
'/var/www/docs/guides' => ['label' => '가이드', 'source' => 'docs'],
'/var/www/docs/projects' => ['label' => '프로젝트', 'source' => 'docs'],
'/var/www/docs/plans' => ['label' => '계획', 'source' => 'docs'],
'/var/www/mng/docs/pptx-output' => ['label' => '산출물', 'source' => 'mng'],
'/var/www/mng/docs' => ['label' => '교육/문서', 'source' => 'mng'],
'/var/www/mng/public/docs' => ['label' => '공개 문서', 'source' => 'mng'],
'/var/www/sales-docs/pptx-output' => ['label' => '영업 산출물', 'source' => 'sales'],
'/var/www/sales-docs/plan/pptx' => ['label' => '영업 기획', 'source' => 'sales'],
'/var/www/sales-docs/plan' => ['label' => '영업 기획', 'source' => 'sales'],
];
/**
* PPTX 목록 페이지
*/
public function index(Request $request): View|Response
{
if ($request->header('HX-Request')) {
return response('', 200)->header('HX-Redirect', route('additional.pptx.index'));
}
$files = $this->scanPptxFiles();
$categories = collect($files)->pluck('category')->unique()->sort()->values()->all();
$totalSize = collect($files)->sum('size_bytes');
return view('additional.pptx.index', [
'files' => $files,
'categories' => $categories,
'totalCount' => count($files),
'totalSize' => $this->formatFileSize($totalSize),
]);
}
/**
* PPTX 파일 다운로드
*/
public function download(Request $request): BinaryFileResponse|Response
{
$filePath = $request->query('file');
if (! $filePath) {
abort(400, '파일 경로가 지정되지 않았습니다.');
}
// 경로 트래버설 방지
if (str_contains($filePath, '..')) {
abort(403, '잘못된 파일 경로입니다.');
}
// .pptx 확장자만 허용
if (strtolower(pathinfo($filePath, PATHINFO_EXTENSION)) !== 'pptx') {
abort(403, '허용되지 않는 파일 형식입니다.');
}
// 허용된 base path에 속하는지 검증
$allowed = false;
foreach (array_keys($this->scanPaths) as $basePath) {
if (str_starts_with($filePath, $basePath.'/')) {
$allowed = true;
break;
}
}
if (! $allowed) {
abort(403, '허용되지 않는 경로입니다.');
}
if (! file_exists($filePath)) {
abort(404, '파일을 찾을 수 없습니다.');
}
return response()->download($filePath, basename($filePath), [
'Content-Type' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
]);
}
/**
* 모든 스캔 경로에서 PPTX 파일 수집
*/
private function scanPptxFiles(): array
{
$files = [];
$seenPaths = [];
foreach ($this->scanPaths as $basePath => $meta) {
if (! is_dir($basePath)) {
continue;
}
$this->findPptxInDirectory($basePath, $basePath, $meta, $files, $seenPaths);
}
// 수정일 내림차순 정렬
usort($files, fn ($a, $b) => $b['modified_at'] <=> $a['modified_at']);
return $files;
}
/**
* 디렉토리에서 PPTX 파일 재귀 검색
*/
private function findPptxInDirectory(string $dir, string $basePath, array $meta, array &$files, array &$seenPaths): void
{
$items = scandir($dir);
if ($items === false) {
return;
}
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$fullPath = $dir.'/'.$item;
if (is_dir($fullPath)) {
// mng/docs 경로에서 하위 디렉토리 중 다른 scanPath에 해당하는 것은 건너뜀
if (isset($this->scanPaths[$fullPath])) {
continue;
}
$this->findPptxInDirectory($fullPath, $basePath, $meta, $files, $seenPaths);
continue;
}
if (strtolower(pathinfo($item, PATHINFO_EXTENSION)) !== 'pptx') {
continue;
}
// 중복 방지 (realpath 기반)
$realPath = realpath($fullPath);
if ($realPath && isset($seenPaths[$realPath])) {
continue;
}
if ($realPath) {
$seenPaths[$realPath] = true;
}
$relativePath = str_replace($basePath.'/', '', $fullPath);
$sizeBytes = filesize($fullPath);
$modifiedAt = filemtime($fullPath);
$files[] = [
'name' => $item,
'path' => $fullPath,
'relative_path' => $relativePath,
'category' => $meta['label'],
'source' => $meta['source'],
'source_dir' => str_replace('/var/www/', '', $basePath),
'size_bytes' => $sizeBytes,
'size' => $this->formatFileSize($sizeBytes),
'modified_at' => $modifiedAt,
'modified_date' => date('Y-m-d H:i', $modifiedAt),
];
}
}
private function formatFileSize(int $bytes): string
{
if ($bytes >= 1048576) {
return number_format($bytes / 1048576, 1).' MB';
}
return number_format($bytes / 1024, 0).' KB';
}
}