From afd350ddaa0839d3ce06ff1257e53b354dfc99f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Nov 2025 07:31:59 +0000 Subject: [PATCH 1/5] Initial plan From 95007eab74f28862e96fd61a9178848a8e5ccb6c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Nov 2025 07:41:33 +0000 Subject: [PATCH 2/5] Fix upload API to save files to public/media and add API endpoint to list all files Co-authored-by: enikqi <10384338+enikqi@users.noreply.github.com> --- site/app/api/upload/route.ts | 8 +++--- site/app/upload/page.tsx | 51 ++++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/site/app/api/upload/route.ts b/site/app/api/upload/route.ts index fdf7409..ee02581 100644 --- a/site/app/api/upload/route.ts +++ b/site/app/api/upload/route.ts @@ -15,8 +15,8 @@ export async function POST(request: NextRequest) { const bytes = await file.arrayBuffer() const buffer = Buffer.from(bytes) - // All files go to media directory - const uploadDir = 'media' + // All files go to public/media directory so they're accessible via URLs + const uploadDir = join('public', 'media') // Ensure directory exists const uploadPath = join(process.cwd(), uploadDir) @@ -31,8 +31,8 @@ export async function POST(request: NextRequest) { // Write file await writeFile(filepath, buffer) - // Return file info - const fileUrl = `/${uploadDir}/${filename}` + // Return file info (URLs in Next.js don't include 'public' - files in public/ are served from root) + const fileUrl = `/media/${filename}` return NextResponse.json({ success: true, diff --git a/site/app/upload/page.tsx b/site/app/upload/page.tsx index f2f3dec..7dc554e 100644 --- a/site/app/upload/page.tsx +++ b/site/app/upload/page.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState } from 'react' +import { useState, useEffect } from 'react' import { Upload, File, Video, Image, CheckCircle, AlertCircle } from 'lucide-react' interface UploadedFile { @@ -10,12 +10,14 @@ interface UploadedFile { type: string url: string path: string + uploadedAt?: string } export default function UploadPage() { const [uploading, setUploading] = useState(false) const [uploadedFiles, setUploadedFiles] = useState([]) const [dragActive, setDragActive] = useState(false) + const [loading, setLoading] = useState(true) const handleFileUpload = async (files: FileList) => { setUploading(true) @@ -32,9 +34,7 @@ export default function UploadPage() { const result = await response.json() - if (result.success) { - setUploadedFiles(prev => [...prev, result.file]) - } else { + if (!result.success) { console.error('Upload failed:', result.message) } } catch (error) { @@ -43,6 +43,18 @@ export default function UploadPage() { } setUploading(false) + + // Reload all files after upload completes + try { + const response = await fetch('/api/media') + const data = await response.json() + + if (data.success) { + setUploadedFiles(data.files) + } + } catch (error) { + console.error('Failed to reload files:', error) + } } const handleDrop = (e: React.DragEvent) => { @@ -84,6 +96,27 @@ export default function UploadPage() { return } + // Load all existing files on mount + useEffect(() => { + const loadExistingFiles = async () => { + try { + setLoading(true) + const response = await fetch('/api/media') + const data = await response.json() + + if (data.success) { + setUploadedFiles(data.files) + } + } catch (error) { + console.error('Failed to load existing files:', error) + } finally { + setLoading(false) + } + } + + loadExistingFiles() + }, []) + return (
@@ -144,11 +177,19 @@ export default function UploadPage() {
)} + {/* Loading indicator */} + {loading && uploadedFiles.length === 0 && ( +
+
+

Loading files...

+
+ )} + {/* Uploaded Files */} {uploadedFiles.length > 0 && (

- Uploaded Files ({uploadedFiles.length}) + All Uploaded Files ({uploadedFiles.length})

From 86e1358307a68f714b41e46b90bfd52473a175ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Nov 2025 07:44:23 +0000 Subject: [PATCH 3/5] Add media API endpoint and update gitignore for media directory Co-authored-by: enikqi <10384338+enikqi@users.noreply.github.com> --- .gitignore | 6 ++-- site/app/api/media/route.ts | 67 +++++++++++++++++++++++++++++++++++++ site/public/media/.gitkeep | 0 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 site/app/api/media/route.ts create mode 100644 site/public/media/.gitkeep diff --git a/.gitignore b/.gitignore index b692c2d..3d470f1 100644 --- a/.gitignore +++ b/.gitignore @@ -38,8 +38,10 @@ debug.log *.zip # big local data -site/media/ -media/ +# Allow media directory structure but ignore uploaded files +# Note: Using trailing slash to only match directories, not files/folders containing 'media' in path +site/public/media/* +!site/public/media/.gitkeep chrome-user-data/ .dev.db swapfile diff --git a/site/app/api/media/route.ts b/site/app/api/media/route.ts new file mode 100644 index 0000000..1d7d73e --- /dev/null +++ b/site/app/api/media/route.ts @@ -0,0 +1,67 @@ +import { NextRequest, NextResponse } from 'next/server' +import { readdir, stat } from 'fs/promises' +import { join } from 'path' +import { existsSync } from 'fs' + +export async function GET(request: NextRequest) { + try { + const mediaDir = join(process.cwd(), 'public', 'media') + + // Check if media directory exists + if (!existsSync(mediaDir)) { + return NextResponse.json({ + success: true, + files: [] + }) + } + + // Read all files in the media directory + const files = await readdir(mediaDir) + + // Get file stats for each file + const fileDetails = await Promise.all( + files.map(async (filename) => { + const filepath = join(mediaDir, filename) + const stats = await stat(filepath) + + // Determine file type based on extension + const ext = filename.split('.').pop()?.toLowerCase() || '' + let type = 'application/octet-stream' + + if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(ext)) { + type = `image/${ext === 'jpg' ? 'jpeg' : ext}` + } else if (['mp4', 'webm', 'avi', 'mov'].includes(ext)) { + type = `video/${ext}` + } + + return { + name: filename, + originalName: filename, + size: stats.size, + type: type, + url: `/media/${filename}`, + path: filepath, + uploadedAt: stats.mtime.toISOString() + } + }) + ) + + // Sort by upload date (newest first) + fileDetails.sort((a, b) => + new Date(b.uploadedAt).getTime() - new Date(a.uploadedAt).getTime() + ) + + return NextResponse.json({ + success: true, + files: fileDetails, + total: fileDetails.length + }) + + } catch (error) { + console.error('Error listing media files:', error) + return NextResponse.json( + { success: false, message: 'Failed to list media files' }, + { status: 500 } + ) + } +} diff --git a/site/public/media/.gitkeep b/site/public/media/.gitkeep new file mode 100644 index 0000000..e69de29 From 3cf4a6f9e57e7badb2dcb9a61e06d645aa74bce8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Nov 2025 07:47:00 +0000 Subject: [PATCH 4/5] Filter hidden files from media API response Co-authored-by: enikqi <10384338+enikqi@users.noreply.github.com> --- site/app/api/media/route.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/site/app/api/media/route.ts b/site/app/api/media/route.ts index 1d7d73e..ed39dd5 100644 --- a/site/app/api/media/route.ts +++ b/site/app/api/media/route.ts @@ -18,9 +18,12 @@ export async function GET(request: NextRequest) { // Read all files in the media directory const files = await readdir(mediaDir) + // Filter out .gitkeep and other hidden files + const visibleFiles = files.filter(filename => !filename.startsWith('.')) + // Get file stats for each file const fileDetails = await Promise.all( - files.map(async (filename) => { + visibleFiles.map(async (filename) => { const filepath = join(mediaDir, filename) const stats = await stat(filepath) From ba2f70939d45cec72d61f30cdcb6f92a7077f85b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Nov 2025 07:49:11 +0000 Subject: [PATCH 5/5] Refactor to eliminate code duplication and remove unused parameters Co-authored-by: enikqi <10384338+enikqi@users.noreply.github.com> --- site/app/api/media/route.ts | 4 ++-- site/app/upload/page.tsx | 44 +++++++++++++++---------------------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/site/app/api/media/route.ts b/site/app/api/media/route.ts index ed39dd5..e1ef20d 100644 --- a/site/app/api/media/route.ts +++ b/site/app/api/media/route.ts @@ -1,9 +1,9 @@ -import { NextRequest, NextResponse } from 'next/server' +import { NextResponse } from 'next/server' import { readdir, stat } from 'fs/promises' import { join } from 'path' import { existsSync } from 'fs' -export async function GET(request: NextRequest) { +export async function GET() { try { const mediaDir = join(process.cwd(), 'public', 'media') diff --git a/site/app/upload/page.tsx b/site/app/upload/page.tsx index 7dc554e..d349017 100644 --- a/site/app/upload/page.tsx +++ b/site/app/upload/page.tsx @@ -45,16 +45,7 @@ export default function UploadPage() { setUploading(false) // Reload all files after upload completes - try { - const response = await fetch('/api/media') - const data = await response.json() - - if (data.success) { - setUploadedFiles(data.files) - } - } catch (error) { - console.error('Failed to reload files:', error) - } + await loadAllFiles() } const handleDrop = (e: React.DragEvent) => { @@ -96,25 +87,26 @@ export default function UploadPage() { return } - // Load all existing files on mount - useEffect(() => { - const loadExistingFiles = async () => { - try { - setLoading(true) - const response = await fetch('/api/media') - const data = await response.json() - - if (data.success) { - setUploadedFiles(data.files) - } - } catch (error) { - console.error('Failed to load existing files:', error) - } finally { - setLoading(false) + // Reusable function to load all files from the server + const loadAllFiles = async () => { + try { + setLoading(true) + const response = await fetch('/api/media') + const data = await response.json() + + if (data.success) { + setUploadedFiles(data.files) } + } catch (error) { + console.error('Failed to load files:', error) + } finally { + setLoading(false) } + } - loadExistingFiles() + // Load all existing files on mount + useEffect(() => { + loadAllFiles() }, []) return (