Skip to content

Commit

Permalink
feat: cron job for google drive storage quote
Browse files Browse the repository at this point in the history
  • Loading branch information
JBR90 committed Dec 4, 2024
1 parent 9e00290 commit 90addb1
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 2 deletions.
114 changes: 114 additions & 0 deletions apps/nextjs/src/app/api/cron-jobs/google-drive-size-quota/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import {
slackAiOpsNotificationChannelId,
slackWebClient,
} from "@oakai/core/src/utils/slack";
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";

const log = aiLogger("cron");

const requiredEnvVars = ["CRON_SECRET", "SLACK_AI_OPS_NOTIFICATION_CHANNEL_ID"];

requiredEnvVars.forEach((envVar) => {
if (!process.env[envVar]) {
throw new Error(`Environment variable ${envVar} is not set.`);
}
});

async function fetchDriveUsage() {
try {
const res = await googleDrive.about.get({
fields: "storageQuota, user(emailAddress)",
});

const storageQuota = res.data.storageQuota;
const userEmail = res.data.user?.emailAddress;

if (!storageQuota) {
throw new Error("Unable to fetch storage quota information.");
}

const usage = {
limit: parseInt(storageQuota.limit ?? "0", 10),
usage: parseInt(storageQuota.usage ?? "0", 10),
userEmail,
};

log.info(
`Drive usage retrieved: ${usage.usage} bytes used of ${usage.limit} bytes total, ${userEmail}.`,
);

return usage;
} catch (error) {
log.error("Failed to fetch Google Drive usage details:", error);
throw error;
}
}

async function checkDriveUsageThreshold(thresholdPercentage: number = 80) {
try {
const usage = await fetchDriveUsage();

if (usage.limit === 0) {
throw new Error("Storage limit is reported as zero, which is invalid.");
}

const usagePercentage = (usage.usage / usage.limit) * 100;

log.info(
`Drive usage percentage: ${usagePercentage.toFixed(
2,
)}%. Threshold is set at ${thresholdPercentage}%.`,
);

if (usagePercentage > thresholdPercentage) {
const errorMessage = `Drive usage is at ${usagePercentage.toFixed(
2,
)}% of the total limit, exceeding the threshold of ${thresholdPercentage}% : ${usage.userEmail}`;
log.error(errorMessage);
Sentry.captureMessage(errorMessage);
await slackWebClient.chat.postMessage({
channel: slackAiOpsNotificationChannelId,
text: errorMessage,
});
}
} catch (error) {
log.error("Error during Drive usage check:", error);
Sentry.captureException(error);
throw error;
}
}

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

if (!cronSecret) {
log.error("Missing cron secret");
return new Response("Missing cron secret", { status: 500 });
}

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

log.info("Starting Google Drive usage check...");

await checkDriveUsageThreshold(80);

return new Response("Drive usage check completed successfully.", {
status: 200,
});
} catch (error) {
log.error(
"An error occurred during the Drive usage check cron job:",
error,
);
Sentry.captureException(error);
return new Response("Internal Server Error", { status: 500 });
}
}
1 change: 1 addition & 0 deletions apps/nextjs/src/middlewares/auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const publicRoutes = [
"/api/trpc/main/health.prismaCheck",
"/api/trpc/chat/chat.health.check",
"/api/cron-jobs/expired-exports",
"/api/cron-jobs/google-drive-size-quota",
/**
* The inngest route is protected using a signing key
* @see https://www.inngest.com/docs/faq#my-app-s-serve-endpoint-requires-authentication-what-should-i-do
Expand Down
4 changes: 4 additions & 0 deletions apps/nextjs/vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
{
"path": "/api/cron-jobs/expired-exports",
"schedule": "0 3 * * *"
},
{
"path": "/api/cron-jobs/google-drive-size-quota",
"schedule": "0 4 * * *"
}
]
}
10 changes: 8 additions & 2 deletions packages/core/src/utils/slack.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ActionsBlock, SectionBlock} from "@slack/web-api";
import type { ActionsBlock, SectionBlock } from "@slack/web-api";
import { WebClient } from "@slack/web-api";
import {
uniqueNamesGenerator,
Expand All @@ -9,12 +9,18 @@ import {

import { getExternalFacingUrl } from "../functions/slack/getExternalFacingUrl";

if (!process.env.SLACK_NOTIFICATION_CHANNEL_ID) {
if (
!process.env.SLACK_NOTIFICATION_CHANNEL_ID ||
!process.env.SLACK_AI_OPS_NOTIFICATION_CHANNEL_ID
) {
throw new Error("Missing env var SLACK_NOTIFICATION_CHANNEL_ID");
}
export const slackNotificationChannelId =
process.env.SLACK_NOTIFICATION_CHANNEL_ID;

export const slackAiOpsNotificationChannelId =
process.env.SLACK_AI_OPS_NOTIFICATION_CHANNEL_ID;

export const slackWebClient = new WebClient(
process.env.SLACK_BOT_USER_OAUTH_TOKEN,
);
Expand Down

0 comments on commit 90addb1

Please sign in to comment.