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 * * *"
+ }
+ ]
+}