This commit is contained in:
37
src/pages/api/resume.json.ts
Normal file
37
src/pages/api/resume.json.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import type { APIRoute } from "astro";
|
||||
import * as TOML from "@iarna/toml";
|
||||
import { siteConfig } from "../../config/data";
|
||||
|
||||
export const GET: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
// Check if resume TOML file is configured
|
||||
if (!siteConfig.resume.tomlFile || !siteConfig.resume.tomlFile.trim()) {
|
||||
return new Response("Resume not configured", { status: 404 });
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
const baseUrl = `${url.protocol}//${url.host}`;
|
||||
|
||||
// Fetch the TOML file from the public directory
|
||||
const response = await fetch(`${baseUrl}${siteConfig.resume.tomlFile}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch resume TOML: ${response.status} ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
|
||||
const tomlContent = await response.text();
|
||||
const resumeData = TOML.parse(tomlContent);
|
||||
|
||||
return new Response(JSON.stringify(resumeData), {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Cache-Control": "public, max-age=300", // Cache for 5 minutes
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error parsing resume TOML:", error);
|
||||
return new Response("Error parsing resume data", { status: 500 });
|
||||
}
|
||||
};
|
@ -41,6 +41,12 @@ interface ResumeData {
|
||||
position: string;
|
||||
date: string;
|
||||
}[];
|
||||
awards: {
|
||||
title: string;
|
||||
organization: string;
|
||||
date: string;
|
||||
description?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
const generateResumeHTML = (data: ResumeData): string => {
|
||||
@ -128,6 +134,23 @@ const generateResumeHTML = (data: ResumeData): string => {
|
||||
})
|
||||
.join("") || "";
|
||||
|
||||
const awardsHTML =
|
||||
data.awards
|
||||
?.map((award) => {
|
||||
return `
|
||||
<div class="mb-2 pl-2 border-l-2 border-yellow-600">
|
||||
<h3 class="text-xs font-semibold text-gray-900 mb-1">${award.title}</h3>
|
||||
<div class="text-xs text-gray-600 mb-1">
|
||||
<span class="font-medium">${award.organization}</span>
|
||||
<span class="mx-1">•</span>
|
||||
<span>${award.date}</span>
|
||||
</div>
|
||||
${award.description ? `<div class="text-xs text-gray-700 leading-tight">${award.description}</div>` : ""}
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
.join("") || "";
|
||||
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@ -217,6 +240,23 @@ const generateResumeHTML = (data: ResumeData): string => {
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
${
|
||||
data.awards &&
|
||||
data.awards.length > 0 &&
|
||||
resumeConfig.sections.awards?.enabled
|
||||
? `
|
||||
<section>
|
||||
<h2 class="text-sm font-semibold text-gray-900 mb-2 pb-1 border-b border-gray-300">
|
||||
${resumeConfig.sections.awards.title || "Awards & Recognition"}
|
||||
</h2>
|
||||
<div class="space-y-2">
|
||||
${awardsHTML}
|
||||
</div>
|
||||
</section>
|
||||
`
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
@ -263,14 +303,14 @@ const generateResumeHTML = (data: ResumeData): string => {
|
||||
|
||||
export const GET: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
if (!siteConfig.resume.jsonFile || !siteConfig.resume.jsonFile.trim()) {
|
||||
if (!siteConfig.resume.tomlFile || !siteConfig.resume.tomlFile.trim()) {
|
||||
return new Response("Resume not configured", { status: 404 });
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
const baseUrl = `${url.protocol}//${url.host}`;
|
||||
|
||||
const response = await fetch(`${baseUrl}${siteConfig.resume.jsonFile}`);
|
||||
const response = await fetch(`${baseUrl}${siteConfig.resume.tomlFile}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
@ -279,7 +319,9 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
}
|
||||
|
||||
const tomlContent = await response.text();
|
||||
const resumeData: ResumeData = TOML.parse(tomlContent) as unknown as ResumeData;
|
||||
const resumeData: ResumeData = TOML.parse(
|
||||
tomlContent,
|
||||
) as unknown as ResumeData;
|
||||
|
||||
const htmlContent = generateResumeHTML(resumeData);
|
||||
|
||||
|
@ -45,13 +45,19 @@ interface ResumeData {
|
||||
position: string;
|
||||
date: string;
|
||||
}[];
|
||||
awards: {
|
||||
title: string;
|
||||
organization: string;
|
||||
date: string;
|
||||
description?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
let resumeData: ResumeData | undefined = undefined;
|
||||
let fetchError: string | null = null;
|
||||
|
||||
// Check if resume JSON file is configured before attempting to fetch
|
||||
if (!siteConfig.resume.jsonFile || !siteConfig.resume.jsonFile.trim()) {
|
||||
// Check if resume TOML file is configured before attempting to fetch
|
||||
if (!siteConfig.resume.tomlFile || !siteConfig.resume.tomlFile.trim()) {
|
||||
return Astro.redirect("/");
|
||||
}
|
||||
|
||||
@ -60,7 +66,7 @@ try {
|
||||
const baseUrl = Astro.url.origin;
|
||||
|
||||
// Fetch the TOML file from the public directory
|
||||
const response = await fetch(`${baseUrl}${siteConfig.resume.jsonFile}`);
|
||||
const response = await fetch(`${baseUrl}${siteConfig.resume.tomlFile}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
@ -331,5 +337,42 @@ if (!data) {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
data.awards &&
|
||||
data.awards.length > 0 &&
|
||||
resumeConfig.sections.awards?.enabled && (
|
||||
<div class="card bg-base-200 shadow-xl mb-4 sm:mb-6">
|
||||
<div class="card-body p-4 sm:p-6 break-words">
|
||||
<h2 class="card-title text-xl sm:text-2xl">
|
||||
{resumeConfig.sections.awards.title ||
|
||||
"Awards & Recognition"}
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
{data.awards.map((award) => (
|
||||
<div class="border-l-2 border-warning pl-4 sm:pl-6">
|
||||
<h3 class="text-lg sm:text-xl font-semibold">
|
||||
{award.title}
|
||||
</h3>
|
||||
<div class="text-sm sm:text-base text-base-content/70 mb-2">
|
||||
<span class="font-medium">
|
||||
{award.organization}
|
||||
</span>
|
||||
<span class="block sm:inline sm:ml-4">
|
||||
{award.date}
|
||||
</span>
|
||||
</div>
|
||||
{award.description && (
|
||||
<div class="text-sm sm:text-base text-base-content/80 mt-2">
|
||||
{award.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Layout>
|
||||
|
Reference in New Issue
Block a user