From 5559e8558881418eec31c89659347c7c90cbddd0 Mon Sep 17 00:00:00 2001 From: Justin Walrath Date: Mon, 12 May 2025 11:26:02 -0400 Subject: [PATCH] Moved the upload folder out of public, an api endpoint to server the files, and a middleware to make it seemlessly pull the uploads from the api without api in the name. Further styling on the expand/collapse podcast card logic to have it show new lines and to fade the collapsed paragraph. Publish page now shows that it's 'loading' in the submit button and alerts the user when done. After success upload it'll redirect to the podcasts listing page. --- .gitignore | 2 +- src/app/[stream]/podcasts/podcastCard.tsx | 12 +++++---- src/app/[stream]/publish/publishForm.tsx | 20 ++++++++++---- src/common/helpers/data.ts | 2 +- src/middleware.ts | 15 +++++++++++ src/pages/api/[stream]/podcasts.ts | 33 +---------------------- src/pages/api/uploads/[file].ts | 16 +++++++++++ 7 files changed, 56 insertions(+), 44 deletions(-) create mode 100644 src/middleware.ts create mode 100644 src/pages/api/uploads/[file].ts diff --git a/.gitignore b/.gitignore index 77398b6..f90bca4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ # production /build -/public/uploads/ +/uploads/ db.json # misc diff --git a/src/app/[stream]/podcasts/podcastCard.tsx b/src/app/[stream]/podcasts/podcastCard.tsx index 8f73fdc..7596e39 100644 --- a/src/app/[stream]/podcasts/podcastCard.tsx +++ b/src/app/[stream]/podcasts/podcastCard.tsx @@ -25,11 +25,13 @@ export default function PodcastCard({

{title}

-
- {description} +
+
+ {description} +
+ {!descriptionExpanded && ( +
+ )}
diff --git a/src/common/helpers/data.ts b/src/common/helpers/data.ts index ec653c6..4969650 100644 --- a/src/common/helpers/data.ts +++ b/src/common/helpers/data.ts @@ -21,7 +21,7 @@ export const convertUrlToPublic = (url?: string) => { }; export const preparePodcastItem = (podcast: PodcastDto) => { - const fileSize = getFileSize(path.join(process.cwd(), `public/uploads/${podcast.url}`)), + const fileSize = getFileSize(path.join(process.cwd(), `uploads/${podcast.url}`)), fileUrl = convertUrlToPublic(podcast.url); return { diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 0000000..420e167 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,15 @@ +import { NextRequest, NextResponse } from 'next/server'; + +export function middleware(req: NextRequest) { + const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''; + const url = req.nextUrl.clone(); + + const adjustedPathname = url.pathname.replace(basePath, ''); + + if (adjustedPathname.startsWith('/uploads/')) { + url.pathname = `/api${adjustedPathname}`; + return NextResponse.rewrite(url); + } + + return NextResponse.next(); +} \ No newline at end of file diff --git a/src/pages/api/[stream]/podcasts.ts b/src/pages/api/[stream]/podcasts.ts index 0ee89bd..3cd3199 100644 --- a/src/pages/api/[stream]/podcasts.ts +++ b/src/pages/api/[stream]/podcasts.ts @@ -27,7 +27,7 @@ const post = async (req: NextApiRequest, res: NextApiResponse) => { return res.status(400).json({ error: 'Invalid stream parameter' }); } - const uploadDir = path.join(process.cwd(), 'public/uploads'); + const uploadDir = path.join(process.cwd(), 'uploads'); await fs.mkdir(uploadDir, { recursive: true }); const form = new multiparty.Form({ uploadDir @@ -80,37 +80,6 @@ const post = async (req: NextApiRequest, res: NextApiResponse) => { res.status(500).json({ error: 'Internal server error' }); } }); - - // try { - // const { title, description, author, image, file } = req.body; - - // const uploadDir = path.join(process.cwd(), 'public/uploads'); - // const imageFileName = `${uuidv4()}${path.extname(image.name)}`; - // const imageFilePath = path.join(uploadDir, imageFileName); - // await fs.writeFile(imageFilePath, Buffer.from(image.data, 'base64')); - - // const podcastFileName = `${uuidv4()}${path.extname(file.name)}`; - // const podcastFilePath = path.join(uploadDir, podcastFileName); - // await fs.writeFile(podcastFilePath, Buffer.from(file.data, 'base64')); - - // const podcastData: PodcastDto = { - // podcastId: uuidv4(), - // streamId: stream, - // title, - // description, - // uploadDate: Date.now().toString(), - // author, - // imageUrl: imageFileName, - // url: podcastFileName, - // }; - - // await publishPodcast(podcastData); - - // res.status(201).json({ message: 'Podcast created successfully' }); - // } catch (error) { - // console.error('Error handling request:', error); - // res.status(500).json({ error: 'Internal server error' }); - // } }; export default function handler( diff --git a/src/pages/api/uploads/[file].ts b/src/pages/api/uploads/[file].ts new file mode 100644 index 0000000..09f0a2e --- /dev/null +++ b/src/pages/api/uploads/[file].ts @@ -0,0 +1,16 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import path from 'path'; +import fs from 'fs'; + +export default function handler(req: NextApiRequest, res: NextApiResponse) { + const { file } = req.query; + + const filePath = path.join(process.cwd(), 'uploads', file as string); + + if (!fs.existsSync(filePath)) { + return res.status(404).json({ error: 'File not found' }); + } + + res.setHeader('Content-Type', 'application/octet-stream'); + fs.createReadStream(filePath).pipe(res); +} \ No newline at end of file