Skip to content

Commit

Permalink
Merge branch 'main' into test/chromatic-ci
Browse files Browse the repository at this point in the history
  • Loading branch information
codeincontext authored Nov 6, 2024
2 parents 82efed9 + e07cffb commit 969991c
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 13 deletions.
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
140 changes: 140 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,140 @@
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");

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

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

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: 500 });
}
if (!folderId) {
log.error("No folder ID provided.");
return new Response("No folder ID provided", { status: 500 });
}

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 });
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Meta, StoryObj } from "@storybook/react";
import { within } from "@storybook/test";

import { SidebarActions } from "./sidebar-actions";

Expand Down Expand Up @@ -26,14 +27,24 @@ export const Default: Story = {
},
};

export const SharePending: Story = {
args: {
...Default.args,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const deleteButton = canvas.getByRole("button", { name: "Share" });
deleteButton.click();
},
};

export const RemovePending: Story = {
args: {
...Default.args,
},
play: async ({ canvasElement }) => {
const deleteButton = canvasElement.querySelector("button:nth-child(2)");
if (deleteButton instanceof HTMLElement) {
deleteButton.click();
}
const canvas = within(canvasElement);
const deleteButton = canvas.getByRole("button", { name: "Delete" });
deleteButton.click();
},
};
6 changes: 1 addition & 5 deletions apps/nextjs/src/lib/hooks/use-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ interface SidebarProviderProps {
}

export function SidebarProvider({ children }: SidebarProviderProps) {
const [isSidebarOpen, setSidebarOpen] = React.useState(true);
const [isSidebarOpen, setSidebarOpen] = React.useState(false);
const [isLoading, setLoading] = React.useState(true);

React.useEffect(() => {
Expand All @@ -46,10 +46,6 @@ export function SidebarProvider({ children }: SidebarProviderProps) {
});
};

if (isLoading) {
return null;
}

return (
<SidebarContext.Provider
value={{ isSidebarOpen, toggleSidebar, isLoading }}
Expand Down
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"
]
}

0 comments on commit 969991c

Please sign in to comment.