feat: enhance file handling and preview features

- Added new dependencies for file type handling and markdown support in package.json.
- Updated FileGrid component to use file extension for FilePreview.
- Refactored FilePreview component to determine file type based on extension, supporting various media types including images, audio, video, text, archives, and code files.
- Created a utility function getFileType to centralize MIME type determination based on file extensions.
- Updated API route to utilize the new getFileType function for serving files with correct MIME types.
- Added Tailwind CSS configuration with typography plugin for improved styling.
This commit is contained in:
ZareMate 2025-05-04 19:07:59 +02:00
parent af978c98b8
commit ad24c76a70
Signed by: zaremate
GPG Key ID: 369A0E45E03A81C3
7 changed files with 3125 additions and 30 deletions

3030
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -24,15 +24,21 @@
"@auth/prisma-adapter": "^2.7.2",
"@prisma/client": "^6.5.0",
"@t3-oss/env-nextjs": "^0.12.0",
"@tailwindcss/typography": "^0.5.16",
"@tanstack/react-query": "^5.69.0",
"@trpc/client": "^11.0.0",
"@trpc/react-query": "^11.0.0",
"@trpc/server": "^11.0.0",
"dompurify": "^3.2.5",
"mermaid": "^11.6.0",
"next": "^15.2.3",
"next-auth": "5.0.0-beta.25",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hot-toast": "^2.5.2",
"react-markdown": "^10.1.0",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.1",
"server-only": "^0.0.1",
"superjson": "^2.2.1",
"zod": "^3.24.2"

View File

@ -103,7 +103,7 @@ export default function FileGrid({ session }: FileGridProps) {
key={file.id}
className="flex place-content-end max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20"
>
{fileType !== "unknown" && <div className=" self-center max-w-50"><FilePreview fileId={file.id} fileType={fileType} /></div>}
{<div className=" self-center max-w-50"><FilePreview fileId={file.id} fileType={file.extension} /></div>}
<button onClick={() => router.push(pageUrl + file.url)}>
<h3 className="text-2xl font-bold">{file.name}</h3>

View File

@ -51,8 +51,37 @@ export function FilePreview({ fileId, fileType }: FilePreviewProps) {
if (!mediaSrc) {
return <div>Loading...</div>;
}
const getFileType = (extension: string): string => {
const fileTypes: Record<string, string> = {
".mp4": "video/mp4",
".webm": "video/webm",
".ogg": "video/ogg",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".gif": "image/gif",
".svg": "image/svg+xml",
".mp3": "audio/mpeg",
".wav": "audio/wav",
".zip": "archive/zip",
".rar": "archive/rar",
".pdf": "text/pdf",
".txt": "text/plain",
".c" : "code/c",
".cpp" : "code/cpp",
".py" : "code/python",
".js" : "code/javascript",
".html" : "code/html",
".css" : "code/css",
".md" : "markdown/markdown",
".json" : "code/json",
".xml" : "code/xml",
".csv" : "code/csv",
};
return fileTypes[extension] || "unknown";
};
if (fileType.startsWith("video")) {
if (getFileType(fileType).startsWith("video")) {
return (
<video
controls
@ -63,7 +92,7 @@ export function FilePreview({ fileId, fileType }: FilePreviewProps) {
</video>
);
}
if (fileType.startsWith("audio")) {
if (getFileType(fileType).startsWith("audio")) {
return (
<audio
controls
@ -74,6 +103,29 @@ export function FilePreview({ fileId, fileType }: FilePreviewProps) {
</audio>
);
}
if (getFileType(fileType).startsWith("image")) {
return <img src={mediaSrc} alt="Media preview" className="max-w-full max-h-96 rounded-lg shadow-md" />;
}
return <img src={mediaSrc} alt="Media preview" className="max-w-full max-h-96 rounded-lg shadow-md" />;
if (getFileType(fileType).startsWith("text")) {
return (
<img src="/icons/files/text.svg" alt="Text file preview" className="max-w-full max-h-96 rounded-lg invert" />
);
}
if (getFileType(fileType).startsWith("archive")) {
return (
<img src="/icons/files/archive.svg" alt="Archive file preview" className="max-w-full max-h-96 rounded-lg invert" />
);
}
if (getFileType(fileType).startsWith("code")) {
return (
<img src="/icons/files/code.svg" alt="Code file preview" className="max-w-full max-h-96 rounded-lg invert" />
);
}
// if (getFileType(fileType).startsWith("markdown")) {
// return;
// }
return;
}

View File

@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
import path from "path";
import { promises as fs } from "fs";
import { db } from "~/server/db";
import { getFileType } from "~/utils/fileType";
export async function GET(req: Request) {
const url = new URL(req.url);
@ -27,25 +28,8 @@ export async function GET(req: Request) {
// Read the file from the filesystem
const fileBuffer = await fs.readFile(filePath);
const mimeType = file.extension === ".mp4"
? "video/mp4"
: file.extension === ".webm"
? "video/webm"
: file.extension === ".ogg"
? "video/ogg"
: file.extension === ".jpg" || file.extension === ".jpeg"
? "image/jpeg"
: file.extension === ".png"
? "image/png"
: file.extension === ".gif"
? "image/gif"
: file.extension === ".svg"
? "image/svg+xml"
: file.extension === ".mp3"
? "audio/mpeg"
: file.extension === ".wav"
? "audio/wav"
: "application/octet-stream";
const mimeType = getFileType(path.extname(file.name)); // Get the MIME type based on the file extension
// Return the file as a binary response
return new Response(fileBuffer, {

31
src/utils/fileType.ts Normal file
View File

@ -0,0 +1,31 @@
// This function takes a file name as input and returns the file type based on its extension.
export function getFileType(extension: string): string {
const fileTypes: Record<string, string> = {
".mp4": "video/mp4",
".webm": "video/webm",
".ogg": "video/ogg",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".gif": "image/gif",
".svg": "image/svg+xml",
".mp3": "audio/mpeg",
".wav": "audio/wav",
".zip": "archive/zip",
".rar": "archive/rar",
".pdf": "text/pdf",
".txt": "text/plain",
".c": "code/c",
".cpp": "code/cpp",
".py": "code/python",
".js": "code/javascript",
".html": "code/html",
".css": "code/css",
".md": "markdown/markdown",
".json": "code/json",
".xml": "code/xml",
".csv": "code/csv",
};
return fileTypes[extension] || "unknown";
};

6
tailwind.config.ts Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx}", // Ensure your paths are correct
],
plugins: [require("@tailwindcss/typography")],
};