Files
Ken Yasue 60fef5c0c9 feat(m10): file sharing with upload, lightbox, permission, tests, e2e
- FileRepository (CRUD, soft-delete, pagination, project isolation) +
  FileStorageService (local FS save data/uploads/<projectId>/<uuid>.<ext>,
  MIME allowlist, filename sanitize, unique uuid name, member permission,
  file_shared notification to other members, file_uploaded activity log,
  SSE file.uploaded, uploader/admin delete + FS unlink)
- APIs: files list/upload(multipart), download (membership), delete
- Screens: files page + Uploader(multipart) + FileList(image Lightbox,
  PDF link, delete), ProjectNav ファイル link
- Unit tests (FileRepository, FileStorageService) + e2e file-sharing
2026-06-25 02:02:39 +02:00

33 lines
1.0 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import fs from 'node:fs';
import { getCurrentUser } from '@/lib/auth/getCurrentUser';
import { createFileStorageService } from '@/lib/api/services';
import { UnauthorizedError } from '@/lib/errors';
import { handleApiError } from '@/lib/api/handleError';
export const runtime = 'nodejs';
export async function GET(
_request: NextRequest,
{ params }: { params: Promise<{ fileId: string }> }
) {
const user = await getCurrentUser();
if (!user) return handleApiError(new UnauthorizedError());
const { fileId } = await params;
const service = createFileStorageService();
try {
const file = service.getFileInfo(user.id, Number(fileId));
const body = fs.readFileSync(file.path);
return new NextResponse(body, {
headers: {
'Content-Type': file.mimeType,
'Content-Disposition': `inline; filename="${file.originalName}"`,
'Content-Length': String(file.size),
},
});
} catch (error) {
return handleApiError(error);
}
}