Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: delete old google drive export files cron job- AI-561 #257

Merged
merged 11 commits into from
Nov 6, 2024
2 changes: 1 addition & 1 deletion apps/nextjs/src/app/api/aila-download-all/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ async function getHandler(req: Request): Promise<Response> {
}

const lessonExport = await prisma.lessonExport.findFirst({
where: { gdriveFileId: fileId, userId },
where: { gdriveFileId: fileId, userId, expiredAt: null },
});

if (!lessonExport) {
Expand Down
1 change: 1 addition & 0 deletions apps/nextjs/src/app/api/aila-download/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ async function getHandler(req: Request): Promise<Response> {
where: {
gdriveFileId: fileId,
userId,
expiredAt: null,
},
cacheStrategy: { ttl: 60 * 5, swr: 60 * 2 },
});
Expand Down
132 changes: 132 additions & 0 deletions apps/nextjs/src/app/api/cron-jobs/expired-exports/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { prisma } from "@oakai/db";
import { googleDrive } from "@oakai/exports/src/gSuite/drive/client";
import { aiLogger } from "@oakai/logger";
import * as Sentry from "@sentry/node";
import type { NextRequest } from "next/server";
import { isTruthy } from "remeda";

const log = aiLogger("cron");

async function updateExpiredAt(fileIds: string[]) {
if (fileIds.length === 0) {
log.info("No file IDs to update.");
return;
}

try {
const result = await prisma.lessonExport.updateMany({
where: {
gdriveFileId: {
in: fileIds,
},
},
data: {
expiredAt: new Date(),
},
});
log.info(`Updated expiredAt for ${fileIds.length} files.`);

if (result.count === fileIds.length) {
log.info("All files updated successfully.");
} else {
throw new Error(
`Expected to update ${fileIds.length} files, but only updated ${result.count}.`,
);
}
} catch (error) {
log.error("Error updating expiredAt field in the database:", error);
throw error;
}
}

async function deleteExpiredExports(fileIds: string[]) {
try {
for (const id of fileIds) {
await googleDrive.files.delete({ fileId: id });
log.info("Deleted:", id);
}
} catch (error) {
log.error("Error deleting old files from folder:", error);
throw error;
}
}

interface FetchExpiredExportsOptions {
folderId: string;
daysAgo: number;
}

async function fetchExpiredExports({
folderId,
daysAgo,
}: FetchExpiredExportsOptions) {
try {
const currentDate = new Date();
const targetDate = new Date(
currentDate.setDate(currentDate.getDate() - daysAgo),
).toISOString();

const query = `modifiedTime < '${targetDate}' and '${folderId}' in parents`;

const res = await googleDrive.files.list({
q: query,
fields: "files(id, name, modifiedTime, ownedByMe )",
pageSize: 1000,
});

const files =
res.data.files?.filter((file) => file.ownedByMe === true) || [];

if (files.length === 0) {
log.info(
"No files found that are older than one month in the specified folder.",
);
return null;
}

log.info(`Found ${files.length} files older than one month in folder:`);
return files;
} catch (error) {
log.error("Error fetching old files from folder:", error);
return null;
}
}

export async function GET(request: NextRequest) {
try {
const authHeader = request.headers.get("authorization");

const cronSecret = process.env.CRON_SECRET;
const folderId = process.env.GOOGLE_DRIVE_OUTPUT_FOLDER_ID;

if (!cronSecret) {
log.error("Missing cron secret");
return new Response("Missing cron secret", { status: 400 });
JBR90 marked this conversation as resolved.
Show resolved Hide resolved
}
if (!folderId) {
log.error("No folder ID provided.");
return new Response("No folder ID provided", { status: 400 });
JBR90 marked this conversation as resolved.
Show resolved Hide resolved
}

if (authHeader !== `Bearer ${cronSecret}`) {
log.error("Authorization failed. Invalid token.");
return new Response("Unauthorized", { status: 401 });
}

const files = await fetchExpiredExports({ folderId, daysAgo: 14 });

if (!files || files.length === 0) {
return new Response("No expired files found", { status: 404 });
}

const validFileIds = files.map((file) => file.id).filter(isTruthy);

await updateExpiredAt(validFileIds);
await deleteExpiredExports(validFileIds);
} catch (error) {
Sentry.captureException(error);
return new Response("Internal Server Error", { status: 500 });
}

return new Response(JSON.stringify({ success: true }), { status: 200 });
JBR90 marked this conversation as resolved.
Show resolved Hide resolved
}
8 changes: 7 additions & 1 deletion apps/nextjs/vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,11 @@
"deploymentEnabled": {
"release": false
}
}
},
"crons": [
{
"path": "/api/cron-jobs/expired-exports",
"schedule": "0 3 * * *"
}
]
}
1 change: 1 addition & 0 deletions packages/api/src/router/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export async function ailaGetExportBySnapshotId({
where: {
lessonSnapshotId: snapshotId,
exportType,
expiredAt: null,
},
orderBy: {
createdAt: "desc",
Expand Down
3 changes: 2 additions & 1 deletion packages/logger/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ type ChildKey =
| "transcripts"
| "trpc"
| "ui"
| "webhooks";
| "webhooks"
| "cron";

const errorLogger =
typeof window === "undefined"
Expand Down
3 changes: 2 additions & 1 deletion turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
"STRICT_CSP",
"TELEMETRY_ENABLED",
"UPSTASH_*",
"WOLFRAM_CLIENT_SECRET"
"WOLFRAM_CLIENT_SECRET",
"CRON_SECRET"
]
}
Loading