-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(frontend): use api ,add newrelic, cache invalidation pubsub,…
… opengraph, next 15
- Loading branch information
1 parent
ede2f64
commit cde0280
Showing
55 changed files
with
2,662 additions
and
2,089 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
{ | ||
"extends": ["next/core-web-vitals", "next/typescript"] | ||
"extends": ["next/core-web-vitals"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { NextRequest, NextResponse } from "next/server"; | ||
import { OAuth2Client } from "google-auth-library"; | ||
import { revalidatePath } from "next/cache"; | ||
|
||
const client = new OAuth2Client(); | ||
type EventPostUpdatedRequest = { | ||
message: { | ||
data: string; // base64 of PostData | ||
message_id: string; | ||
publish_time: string; | ||
attributes: { | ||
eventType: | ||
| "POST_CREATED" | ||
| "CONTENT_UPDATED" | ||
| "POST_DELETED" | ||
| "META_UPDATED"; | ||
slug: string; | ||
}; | ||
}; | ||
subscription: string; | ||
}; | ||
type PostData = { | ||
title: string; | ||
tags: string[]; | ||
created_at: string; | ||
description: string; | ||
slug: string; | ||
}; | ||
export async function POST(req: NextRequest) { | ||
const authenticated = await validatePubSubAuth(req); | ||
if (!authenticated) { | ||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); | ||
} | ||
const { message }: EventPostUpdatedRequest = await req.json(); | ||
const { attributes, data } = message; | ||
const { eventType, slug } = attributes; | ||
// const postData: PostData = JSON.parse(Buffer.from(data, 'base64').toString()); | ||
switch (eventType) { | ||
case "POST_DELETED": | ||
revalidatePath(`/posts/${slug}`); | ||
break; | ||
case "POST_CREATED": | ||
revalidatePath("/"); | ||
break; | ||
case "CONTENT_UPDATED": | ||
revalidatePath(`/posts/${slug}`); | ||
break; | ||
case "META_UPDATED": | ||
revalidatePath("/"); | ||
break; | ||
// default | ||
default: | ||
return NextResponse.json({ error: "Unknown eventType" },{ status: 400 }); | ||
} | ||
return NextResponse.json({ message: "ok" }, { status: 200 }); | ||
// return just 200 | ||
} | ||
|
||
async function validatePubSubAuth(req: NextRequest) { | ||
//VALIDATE AUTH HEADER AND ENVIRONMENT GCP PUB SUB | ||
if (process.env.ENVIRONMENT === "dev") { | ||
return true; | ||
} | ||
const authHeader = req.headers.get("authorization"); | ||
if (!authHeader) { | ||
return false; | ||
} | ||
const idToken = authHeader.split(" ")[1]; // Extract the token from "Bearer <token>" | ||
const audience = process.env.GCP_PROJECT_ID; | ||
try { | ||
await client.verifyIdToken({ idToken, audience }); | ||
} catch (error) { | ||
return false; | ||
} | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { NextRequest, NextResponse } from "next/server"; | ||
import { revalidatePath } from "next/cache"; | ||
import { getPostData } from "../../../lib/posts"; | ||
type PostData = { | ||
title: string; | ||
tags: string[]; | ||
created_at: string; | ||
description: string | null; | ||
slug: string; | ||
} | ||
|
||
export async function POST(req: NextRequest) { | ||
const authenticated = await validateApiToken(req); | ||
if (!authenticated) { | ||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); | ||
} | ||
try { | ||
await performHardSync() | ||
revalidatePath("/*") | ||
}catch (e){ | ||
return NextResponse.error() | ||
} | ||
return NextResponse.json({ message: "ok" }, { status: 200 }); | ||
// return just 200 | ||
} | ||
|
||
async function performHardSync(): Promise<void> { | ||
const GITHUB_REPO = process.env.CONTENT_GITHUB_REPO; | ||
const GITHUB_BRANCH = process.env.CONTENT_GITHUB_BRANCH; | ||
const GITHUB_MDX_PATH = process.env.CONTENT_GITHUB_MDX_PATH; | ||
|
||
// Step 1: Fetch all MDX files (list slugs) | ||
const postsUrl = `https://api.github.com/repos/${GITHUB_REPO}/contents/${GITHUB_MDX_PATH}?ref=${GITHUB_BRANCH}`; | ||
const postsResponse = await fetch(postsUrl); | ||
if (!postsResponse.ok) { | ||
throw new Error('Failed to fetch posts directory.'); | ||
} | ||
|
||
const files = await postsResponse.json(); | ||
const slugs = files | ||
.filter((file: { type: string, name: string }) => file.type === 'file' && file.name.endsWith('.mdx')) | ||
.map((file: { name: string }) => file.name.replace('.mdx', '')); | ||
|
||
// Step 2: Fetch and transform each post into the required format | ||
const posts: PostData[] = []; | ||
|
||
for (const slug of slugs) { | ||
const postData = await getPostData(slug); | ||
if (postData) { | ||
const { frontmatter } = postData; | ||
posts.push({ | ||
title: frontmatter.title, | ||
tags: frontmatter.tags || [], | ||
created_at: frontmatter.date, | ||
description: frontmatter.description!, | ||
slug: slug, | ||
}); | ||
} | ||
} | ||
|
||
// Step 3: Make the POST request with the data | ||
console.log("posts", posts) | ||
const syncResponse = await fetch(process.env.NEXT_PUBLIC_BACKEND_URL + "/blog/hardsync", { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({posts}), | ||
// mode: "cors" | ||
// mode: "no-cors" | ||
}); | ||
|
||
if (!syncResponse.ok) { | ||
const errorDetails = await syncResponse.text(); | ||
console.error(syncResponse.status, errorDetails) | ||
throw new Error(`Failed to sync posts: ${syncResponse.status} ${errorDetails}`); | ||
} | ||
|
||
console.log('Posts successfully synced.'); | ||
} | ||
|
||
async function validateApiToken(req: NextRequest) { | ||
//VALIDATE AUTH HEADER AND ENVIRONMENT GCP PUB SUB | ||
if (process.env.ENVIRONMENT === "dev") { | ||
return true; | ||
} | ||
const authHeader = req.headers.get("api-key"); | ||
if (!authHeader) { | ||
return false; | ||
} | ||
const expectedToken = process.env.API_KEY; | ||
if (authHeader != expectedToken) { | ||
return false | ||
} | ||
return true | ||
} |
Binary file not shown.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.