Skip to content

Commit

Permalink
refactor(frontend): use api ,add newrelic, cache invalidation pubsub,…
Browse files Browse the repository at this point in the history
… opengraph, next 15
  • Loading branch information
leo-the-nardo committed Dec 5, 2024
1 parent ede2f64 commit cde0280
Show file tree
Hide file tree
Showing 55 changed files with 2,662 additions and 2,089 deletions.
88 changes: 0 additions & 88 deletions backend/sst.config.ts

This file was deleted.

2 changes: 1 addition & 1 deletion frontend/.eslintrc.json
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"]
}
76 changes: 76 additions & 0 deletions frontend/app/api/events/posts-updated/route.ts
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
}
96 changes: 96 additions & 0 deletions frontend/app/api/hard-sync/route.ts
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 removed frontend/app/apple-icon.png
Binary file not shown.
96 changes: 0 additions & 96 deletions frontend/app/blog/[slug]/bup.tsx

This file was deleted.

Loading

0 comments on commit cde0280

Please sign in to comment.