Skip to content

Commit

Permalink
feat: Revalidate on reupload
Browse files Browse the repository at this point in the history
  • Loading branch information
richiemcilroy committed Nov 27, 2024
1 parent 638ddae commit b49d7a7
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 7 deletions.
22 changes: 18 additions & 4 deletions apps/web/app/api/playlist/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
generateM3U8Playlist,
generateMasterPlaylist,
} from "@/utils/video/ffmpeg/helpers";
import { getHeaders } from "@/utils/helpers";
import { getHeaders, CACHE_CONTROL_HEADERS } from "@/utils/helpers";
import { createS3Client, getS3Bucket } from "@/utils/s3";

export const revalidate = 3599;
Expand Down Expand Up @@ -88,6 +88,7 @@ export async function GET(request: NextRequest) {
headers: {
...getHeaders(origin),
Location: `https://v.cap.so/${userId}/${videoId}/result.mp4`,
...CACHE_CONTROL_HEADERS,
},
});
}
Expand All @@ -98,6 +99,7 @@ export async function GET(request: NextRequest) {
headers: {
...getHeaders(origin),
Location: `https://v.cap.so/${userId}/${videoId}/output/video_recording_000.m3u8`,
...CACHE_CONTROL_HEADERS,
},
});
}
Expand All @@ -108,6 +110,7 @@ export async function GET(request: NextRequest) {
headers: {
...getHeaders(origin),
Location: playlistUrl,
...CACHE_CONTROL_HEADERS,
},
});
}
Expand All @@ -131,6 +134,7 @@ export async function GET(request: NextRequest) {
status: 200,
headers: {
...getHeaders(origin),
...CACHE_CONTROL_HEADERS,
"Content-Type": "text/vtt",
},
});
Expand Down Expand Up @@ -182,7 +186,10 @@ export async function GET(request: NextRequest) {

return new Response(playlist, {
status: 200,
headers: getHeaders(origin),
headers: {
...getHeaders(origin),
...CACHE_CONTROL_HEADERS,
},
});
}

Expand All @@ -201,6 +208,7 @@ export async function GET(request: NextRequest) {
headers: {
...getHeaders(origin),
Location: playlistUrl,
...CACHE_CONTROL_HEADERS,
},
});
}
Expand Down Expand Up @@ -279,7 +287,10 @@ export async function GET(request: NextRequest) {

return new Response(generatedPlaylist, {
status: 200,
headers: getHeaders(origin),
headers: {
...getHeaders(origin),
...CACHE_CONTROL_HEADERS,
},
});
}

Expand Down Expand Up @@ -317,7 +328,10 @@ export async function GET(request: NextRequest) {

return new Response(generatedPlaylist, {
status: 200,
headers: getHeaders(origin),
headers: {
...getHeaders(origin),
...CACHE_CONTROL_HEADERS,
},
});
} catch (error) {
console.error("Error generating video segment URLs", error);
Expand Down
74 changes: 74 additions & 0 deletions apps/web/app/api/revalidate/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { revalidatePath } from 'next/cache';
import { type NextRequest } from "next/server";
import { getHeaders, CACHE_CONTROL_HEADERS } from "@/utils/helpers";
import { db } from "@cap/database";
import { videos } from "@cap/database/schema";
import { eq } from "drizzle-orm";

export async function POST(request: NextRequest) {
const origin = request.headers.get("origin") as string;

try {
const { videoId } = await request.json();

if (!videoId) {
return new Response(
JSON.stringify({ error: "Missing videoId" }),
{
status: 400,
headers: {
...getHeaders(origin),
...CACHE_CONTROL_HEADERS,
},
}
);
}

const [video] = await db.select().from(videos).where(eq(videos.id, videoId));

if (!video) {
return new Response(
JSON.stringify({ error: "Video not found" }),
{
status: 404,
headers: {
...getHeaders(origin),
...CACHE_CONTROL_HEADERS,
},
}
);
}

// Revalidate the specific video page
revalidatePath(`/s/${videoId}`);

return new Response(
JSON.stringify({
revalidated: true,
now: Date.now(),
path: `/s/${videoId}`
}),
{
headers: {
...getHeaders(origin),
...CACHE_CONTROL_HEADERS,
},
}
);
} catch (err) {
console.error("Revalidation error:", err);
return new Response(
JSON.stringify({
error: "Error revalidating",
details: err instanceof Error ? err.message : String(err)
}),
{
status: 500,
headers: {
...getHeaders(origin),
...CACHE_CONTROL_HEADERS,
},
}
);
}
}
16 changes: 16 additions & 0 deletions apps/web/app/api/upload/signed/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,22 @@ export async function POST(request: NextRequest) {

console.log("Presigned URL created successfully");

// After successful presigned URL creation, trigger revalidation
const videoId = fileKey.split('/')[1]; // Assuming fileKey format is userId/videoId/...
if (videoId) {
try {
await fetch(`${process.env.NEXT_PUBLIC_URL}/api/revalidate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ videoId }),
});
} catch (revalidateError) {
console.error('Failed to revalidate page:', revalidateError);
}
}

return new Response(JSON.stringify({ presignedPostData }), {
headers: {
"Content-Type": "application/json",
Expand Down
11 changes: 9 additions & 2 deletions apps/web/app/api/video/playlistUrl/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { db } from "@cap/database";
import { videos } from "@cap/database/schema";
import { eq } from "drizzle-orm";
import { getHeaders } from "@/utils/helpers";
import { CACHE_CONTROL_HEADERS } from "@/utils/helpers";

export const revalidate = 0;

Expand Down Expand Up @@ -63,7 +64,10 @@ export async function GET(request: NextRequest) {
JSON.stringify({ playlistOne: playlistUrl, playlistTwo: null }),
{
status: 200,
headers: getHeaders(origin),
headers: {
...getHeaders(origin),
...CACHE_CONTROL_HEADERS,
},
}
);
}
Expand All @@ -75,7 +79,10 @@ export async function GET(request: NextRequest) {
}),
{
status: 200,
headers: getHeaders(origin),
headers: {
...getHeaders(origin),
...CACHE_CONTROL_HEADERS,
},
}
);
}
5 changes: 4 additions & 1 deletion apps/web/app/s/[videoId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"use server";
import { Share } from "./Share";
import { db } from "@cap/database";
import { eq } from "drizzle-orm";
Expand All @@ -8,6 +7,10 @@ import type { Metadata, ResolvingMetadata } from "next";
import { notFound } from "next/navigation";
import { ImageViewer } from "./_components/ImageViewer";

export const dynamic = "auto";
export const dynamicParams = true;
export const revalidate = 30;

type Props = {
params: { [key: string]: string | string[] | undefined };
};
Expand Down
6 changes: 6 additions & 0 deletions apps/web/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,9 @@ export function rateLimitMiddleware(

return request;
}

export const CACHE_CONTROL_HEADERS = {
'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate',
'Pragma': 'no-cache',
'Expires': '0',
};

1 comment on commit b49d7a7

@vercel
Copy link

@vercel vercel bot commented on b49d7a7 Nov 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.