diff --git a/.github/README.md b/.github/README.md index 8328fc9a..c9f11df5 100644 --- a/.github/README.md +++ b/.github/README.md @@ -43,7 +43,10 @@ We use pnpm and turbo for managing the depende > [!IMPORTANT] > Make sure you have the prerequisites installed before running the following commands and have `.env` file in the root directory. -> `.env.example` file is provided as an example uses Vercel Postgres and Uploadthing. +> Use the format in `.env.example` file to create a `.env` file in the root directory in `apps/web/` as its a monorepo. +> Get an example / temp postgre database from vercel for `.env` file and place it in `packages/db`. +> Get your storage bucket from uploadthing and add it to `.env` file, Will be replaced with amazon s3 bucket later. +> Add a random string in CRON_SECRET in `.env` file for vercel cron jobs (Only works in production). ```bash # Clone the repository diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 05fd49a7..d706a3e9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,9 +53,10 @@ This repository participates in Hacktoberfest! We welcome contributions througho 3. **Set up development environment** ```bash - # Get an example / temp database from vercel for `.env` file and place it in `packages/db`. + # Use the format in `.env.example` file to create a `.env` file in the root directory in `apps/web/` as its a monorepo. + # Get an example / temp postgre database from vercel for `.env` file and place it in `packages/db`. # Get your storage bucket from uploadthing and add it to `.env` file, Will be replaced with amazon s3 bucket later. - # Format is in `.env.example` file + # Add a random string in CRON_SECRET in `.env` file for vercel cron jobs (Only works in production). ``` 4. **Run the development servers** diff --git a/apps/web/.env.example b/apps/web/.env.example index 7ed0884d..15901997 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -3,3 +3,6 @@ UPLOADTHING_TOKEN= UPLOADTHING_SECRET= NEXT_PUBLIC_UPLOADTHING_APP_ID= + +# Other - Can be any random string +CRON_SECRET="" diff --git a/apps/web/src/app/api/clear-uploads/route.ts b/apps/web/src/app/api/clear-uploads/route.ts new file mode 100644 index 00000000..d206340e --- /dev/null +++ b/apps/web/src/app/api/clear-uploads/route.ts @@ -0,0 +1,52 @@ +import { prisma } from "@zephyr/db"; +import { UTApi } from "uploadthing/server"; + +export async function GET(req: Request) { + try { + const authHeader = req.headers.get("Authorization"); + + if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) { + return Response.json( + { message: "Invalid authorization header" }, + { status: 401 } + ); + } + + const unusedMedia = await prisma.media.findMany({ + where: { + postId: null, + ...(process.env.NODE_ENV === "production" + ? { + createdAt: { + lte: new Date(Date.now() - 1000 * 60 * 60 * 24) + } + } + : {}) + }, + select: { + id: true, + url: true + } + }); + + new UTApi().deleteFiles( + unusedMedia.map( + (m) => + m.url.split(`/a/${process.env.NEXT_PUBLIC_UPLOADTHING_APP_ID}/`)[1] + ) + ); + + await prisma.media.deleteMany({ + where: { + id: { + in: unusedMedia.map((m) => m.id) + } + } + }); + + return new Response(); + } catch (error) { + console.error(error); + return Response.json({ error: "Internal server error" }, { status: 500 }); + } +} diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000..74ab2ea9 --- /dev/null +++ b/vercel.json @@ -0,0 +1,8 @@ +{ + "crons": [ + { + "path": "/api/clear-uploads", + "schedule": "0 2 * * *" + } + ] +}