3.0.0 - Dependency updates, improved typesafe config, improve typing
All checks were successful
Docker Deploy / build-and-push (push) Successful in 3m44s
All checks were successful
Docker Deploy / build-and-push (push) Successful in 3m44s
This commit is contained in:
@@ -1,23 +1,23 @@
|
||||
import type { APIRoute } from "astro";
|
||||
import * as TOML from "@iarna/toml";
|
||||
import { siteConfig } from "../../config/data";
|
||||
import { config } from "../../config";
|
||||
|
||||
export const GET: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
// Check if resume TOML content is configured
|
||||
if (!siteConfig.resume.tomlFile || !siteConfig.resume.tomlFile.trim()) {
|
||||
if (!config.resumeConfig.tomlFile || !config.resumeConfig.tomlFile.trim()) {
|
||||
return new Response("Resume not configured", { status: 404 });
|
||||
}
|
||||
|
||||
let tomlContent: string;
|
||||
|
||||
// Check if tomlFile is a path (starts with /) or raw content
|
||||
if (siteConfig.resume.tomlFile.startsWith("/")) {
|
||||
if (config.resumeConfig.tomlFile.startsWith("/")) {
|
||||
// It's a file path - fetch it
|
||||
const url = new URL(request.url);
|
||||
const baseUrl = `${url.protocol}//${url.host}`;
|
||||
|
||||
const response = await fetch(`${baseUrl}${siteConfig.resume.tomlFile}`);
|
||||
const response = await fetch(`${baseUrl}${config.resumeConfig.tomlFile}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
@@ -28,7 +28,7 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
tomlContent = await response.text();
|
||||
} else {
|
||||
// It's raw TOML content
|
||||
tomlContent = siteConfig.resume.tomlFile;
|
||||
tomlContent = config.resumeConfig.tomlFile;
|
||||
}
|
||||
const resumeData = TOML.parse(tomlContent);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { APIRoute } from "astro";
|
||||
import { chromium } from "playwright";
|
||||
import { siteConfig } from "../../../config/data";
|
||||
import { config } from "../../../config";
|
||||
import * as TOML from "@iarna/toml";
|
||||
|
||||
// Helper function to fetch and return SVG icon from Simple Icons CDN
|
||||
@@ -300,7 +300,7 @@ const fetchProfileIcons = async (profiles: any[]) => {
|
||||
};
|
||||
|
||||
const generateResumeHTML = async (data: ResumeData): Promise<string> => {
|
||||
const resumeConfig = siteConfig.resume;
|
||||
const resumeConfig = config.resumeConfig;
|
||||
// Use layout from TOML data, fallback to site config, then to default
|
||||
const layout = data.layout
|
||||
? {
|
||||
@@ -405,19 +405,19 @@ async function generatePDFFromToml(tomlContent: string): Promise<Uint8Array> {
|
||||
|
||||
export const GET: APIRoute = async ({ request }) => {
|
||||
try {
|
||||
if (!siteConfig.resume.tomlFile || !siteConfig.resume.tomlFile.trim()) {
|
||||
if (!config.resumeConfig.tomlFile || !config.resumeConfig.tomlFile.trim()) {
|
||||
return new Response("Resume not configured", { status: 404 });
|
||||
}
|
||||
|
||||
let tomlContent: string;
|
||||
|
||||
// Check if tomlFile is a path (starts with /) or raw content
|
||||
if (siteConfig.resume.tomlFile.startsWith("/")) {
|
||||
if (config.resumeConfig.tomlFile.startsWith("/")) {
|
||||
// It's a file path - fetch it
|
||||
const url = new URL(request.url);
|
||||
const baseUrl = `${url.protocol}//${url.host}`;
|
||||
|
||||
const response = await fetch(`${baseUrl}${siteConfig.resume.tomlFile}`);
|
||||
const response = await fetch(`${baseUrl}${config.resumeConfig.tomlFile}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
@@ -428,7 +428,7 @@ export const GET: APIRoute = async ({ request }) => {
|
||||
tomlContent = await response.text();
|
||||
} else {
|
||||
// It's raw TOML content
|
||||
tomlContent = siteConfig.resume.tomlFile;
|
||||
tomlContent = config.resumeConfig.tomlFile;
|
||||
}
|
||||
|
||||
const pdfBuffer = await generatePDFFromToml(tomlContent);
|
||||
|
||||
@@ -3,13 +3,13 @@ import { Image } from "astro:assets";
|
||||
import SocialLinks from "../components/SocialLinks.astro";
|
||||
import TechLinks from "../components/TechLinks.astro";
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import { personalInfo, homepageSections } from "../config/data";
|
||||
import { config } from "../config";
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<Image
|
||||
src={personalInfo.profileImage.src}
|
||||
alt={personalInfo.profileImage.alt}
|
||||
src={config.personalInfo.profileImage.src}
|
||||
alt={config.personalInfo.profileImage.alt}
|
||||
width={300}
|
||||
height={300}
|
||||
layout="constrained"
|
||||
@@ -18,24 +18,22 @@ import { personalInfo, homepageSections } from "../config/data";
|
||||
style="max-width: 12rem; width: 100%;"
|
||||
/>
|
||||
|
||||
<h1
|
||||
class="text-primary text-4xl sm:text-6xl font-bold text-center"
|
||||
>
|
||||
{personalInfo.name}
|
||||
<h1 class="text-primary text-4xl sm:text-6xl font-bold text-center">
|
||||
{config.personalInfo.name}
|
||||
</h1>
|
||||
|
||||
<h2 class="text-xl sm:text-3xl font-bold text-center mx-6">
|
||||
{personalInfo.tagline}
|
||||
{config.personalInfo.tagline}
|
||||
</h2>
|
||||
|
||||
<h3 class="text-lg sm:text-2xl font-bold">
|
||||
{homepageSections.socialLinks.title}
|
||||
{config.homepageSections.socialLinks.title}
|
||||
</h3>
|
||||
|
||||
<SocialLinks />
|
||||
|
||||
<h3 class="text-lg sm:text-2xl font-bold">
|
||||
{homepageSections.techStack.title}
|
||||
{config.homepageSections.techStack.title}
|
||||
</h3>
|
||||
|
||||
<TechLinks />
|
||||
|
||||
@@ -18,7 +18,7 @@ const { Content } = await post.render();
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div class="min-h-screen p-4 md:p-8">
|
||||
<div class="w-full p-4 md:p-8">
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="p-4 md:p-8">
|
||||
<h1 class="text-4xl md:text-5xl font-bold text-primary mb-6">
|
||||
@@ -45,7 +45,10 @@ const { Content } = await post.render();
|
||||
</div>
|
||||
|
||||
{/* Back button */}
|
||||
<a href="/posts" class="btn btn-outline btn-primary btn-sm font-bold">
|
||||
<a
|
||||
href="/posts"
|
||||
class="btn btn-outline btn-primary btn-sm font-bold"
|
||||
>
|
||||
<Icon name="mdi:arrow-left" class="text-lg" />
|
||||
Back
|
||||
</a>
|
||||
|
||||
@@ -8,30 +8,30 @@ const posts = await getCollection("posts");
|
||||
|
||||
// Sort posts by date, newest first
|
||||
const sortedPosts = posts.sort(
|
||||
(a: CollectionEntry<"posts">, b: CollectionEntry<"posts">) =>
|
||||
new Date(b.data.pubDate).valueOf() - new Date(a.data.pubDate).valueOf(),
|
||||
(a: CollectionEntry<"posts">, b: CollectionEntry<"posts">) =>
|
||||
new Date(b.data.pubDate).valueOf() - new Date(a.data.pubDate).valueOf(),
|
||||
);
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div class="min-h-screen min-w-screen p-4 sm:p-8">
|
||||
<h1
|
||||
class="text-3xl sm:text-4xl font-bold text-primary mb-6 sm:mb-8 text-center"
|
||||
>
|
||||
Posts
|
||||
</h1>
|
||||
<div
|
||||
class="flex flex-row flex-wrap justify-center gap-4 sm:gap-6 max-w-6xl mx-auto"
|
||||
>
|
||||
{sortedPosts.map((post) => <PostCard post={post} />)}
|
||||
</div>
|
||||
<div class="w-full p-4 sm:p-8">
|
||||
<h1
|
||||
class="text-3xl sm:text-4xl font-bold text-primary mb-6 sm:mb-8 text-center"
|
||||
>
|
||||
Posts
|
||||
</h1>
|
||||
<div
|
||||
class="flex flex-row flex-wrap justify-center gap-4 sm:gap-6 max-w-6xl mx-auto"
|
||||
>
|
||||
{sortedPosts.map((post) => <PostCard post={post} />)}
|
||||
</div>
|
||||
|
||||
{
|
||||
sortedPosts.length === 0 && (
|
||||
<p class="text-center text-gray-500 mt-12">
|
||||
No posts available yet. Check back soon!
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
sortedPosts.length === 0 && (
|
||||
<p class="text-center text-gray-500 mt-12">
|
||||
No posts available yet. Check back soon!
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import ProjectCard from "../components/ProjectCard.astro";
|
||||
import { projects } from "../config/data";
|
||||
import { config } from "../config";
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div class="min-h-screen min-w-screen p-4 sm:p-8">
|
||||
<h1
|
||||
class="text-3xl sm:text-4xl font-bold text-primary mb-6 sm:mb-8 text-center"
|
||||
>
|
||||
Projects
|
||||
</h1>
|
||||
<div
|
||||
class="flex flex-row flex-wrap justify-center gap-4 sm:gap-6 max-w-6xl mx-auto"
|
||||
>
|
||||
{projects.map((project) => <ProjectCard project={project} />)}
|
||||
<div class="w-full p-4 sm:p-8">
|
||||
<h1
|
||||
class="text-3xl sm:text-4xl font-bold text-primary mb-6 sm:mb-8 text-center"
|
||||
>
|
||||
Projects
|
||||
</h1>
|
||||
<div
|
||||
class="flex flex-row flex-wrap justify-center gap-4 sm:gap-6 max-w-6xl mx-auto"
|
||||
>
|
||||
{
|
||||
config.projects.map((project) => (
|
||||
<ProjectCard project={project} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
{
|
||||
config.projects.length === 0 && (
|
||||
<p class="text-center text-gray-500 mt-12">
|
||||
No projects available yet. Check back soon!
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{
|
||||
projects.length === 0 && (
|
||||
<p class="text-center text-gray-500 mt-12">
|
||||
No projects available yet. Check back soon!
|
||||
</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import Layout from "../layouts/Layout.astro";
|
||||
import ResumeSkills from "../components/ResumeSkills";
|
||||
import ResumeDownloadButton from "../components/ResumeDownloadButton";
|
||||
import ResumeSettingsModal from "../components/ResumeSettingsModal";
|
||||
import { siteConfig } from "../config/data";
|
||||
import { config } from "../config";
|
||||
import "../styles/global.css";
|
||||
import * as TOML from "@iarna/toml";
|
||||
|
||||
@@ -57,16 +57,18 @@ interface ResumeData {
|
||||
let resumeData: ResumeData | undefined = undefined;
|
||||
let fetchError: string | null = null;
|
||||
|
||||
if (!siteConfig.resume.tomlFile || !siteConfig.resume.tomlFile.trim()) {
|
||||
if (!config.resumeConfig.tomlFile || !config.resumeConfig.tomlFile.trim()) {
|
||||
return Astro.redirect("/");
|
||||
}
|
||||
|
||||
try {
|
||||
let tomlContent: string;
|
||||
|
||||
if (siteConfig.resume.tomlFile.startsWith("/")) {
|
||||
if (config.resumeConfig.tomlFile.startsWith("/")) {
|
||||
const baseUrl = Astro.url.origin;
|
||||
const response = await fetch(`${baseUrl}${siteConfig.resume.tomlFile}`);
|
||||
const response = await fetch(
|
||||
`${baseUrl}${config.resumeConfig.tomlFile}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
@@ -76,7 +78,7 @@ try {
|
||||
|
||||
tomlContent = await response.text();
|
||||
} else {
|
||||
tomlContent = siteConfig.resume.tomlFile;
|
||||
tomlContent = config.resumeConfig.tomlFile;
|
||||
}
|
||||
|
||||
resumeData = TOML.parse(tomlContent) as unknown as ResumeData;
|
||||
@@ -86,7 +88,7 @@ try {
|
||||
}
|
||||
|
||||
const data = resumeData;
|
||||
const resumeConfig = siteConfig.resume;
|
||||
const resumeConfig = config.resumeConfig;
|
||||
|
||||
if (!data) {
|
||||
return Astro.redirect("/");
|
||||
@@ -136,26 +138,29 @@ if (!data) {
|
||||
<ResumeDownloadButton client:load />
|
||||
|
||||
{
|
||||
data.summary && resumeConfig.sections.summary?.enabled && (
|
||||
<div class="card bg-base-300 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.summary.title || "Summary"}
|
||||
</h2>
|
||||
<div>{data.summary.content}</div>
|
||||
data.summary &&
|
||||
resumeConfig.sections.enabled.includes("summary") && (
|
||||
<div class="card bg-base-300 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.summary?.title ||
|
||||
"Summary"}
|
||||
</h2>
|
||||
<div>{data.summary.content}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
data.skills &&
|
||||
data.skills.length > 0 &&
|
||||
resumeConfig.sections.skills?.enabled && (
|
||||
resumeConfig.sections.enabled.includes("skills") && (
|
||||
<div class="card bg-base-300 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.skills.title || "Skills"}
|
||||
{resumeConfig.sections.skills?.title ||
|
||||
"Skills"}
|
||||
</h2>
|
||||
<ResumeSkills
|
||||
skills={data.skills.map((skill, index) => ({
|
||||
@@ -173,11 +178,11 @@ if (!data) {
|
||||
{
|
||||
data.experience &&
|
||||
data.experience.length > 0 &&
|
||||
resumeConfig.sections.experience?.enabled && (
|
||||
resumeConfig.sections.enabled.includes("experience") && (
|
||||
<div class="card bg-base-300 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.experience.title ||
|
||||
{resumeConfig.sections.experience?.title ||
|
||||
"Experience"}
|
||||
</h2>
|
||||
<div class="space-y-4 sm:space-y-6">
|
||||
@@ -222,11 +227,11 @@ if (!data) {
|
||||
{
|
||||
data.education &&
|
||||
data.education.length > 0 &&
|
||||
resumeConfig.sections.education?.enabled && (
|
||||
resumeConfig.sections.enabled.includes("education") && (
|
||||
<div class="card bg-base-300 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.education.title ||
|
||||
{resumeConfig.sections.education?.title ||
|
||||
"Education"}
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
@@ -264,11 +269,11 @@ if (!data) {
|
||||
{
|
||||
data.volunteer &&
|
||||
data.volunteer.length > 0 &&
|
||||
resumeConfig.sections.volunteer?.enabled && (
|
||||
resumeConfig.sections.enabled.includes("volunteer") && (
|
||||
<div class="card bg-base-300 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.volunteer.title ||
|
||||
{resumeConfig.sections.volunteer?.title ||
|
||||
"Volunteer Work"}
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
@@ -296,11 +301,11 @@ if (!data) {
|
||||
{
|
||||
data.awards &&
|
||||
data.awards.length > 0 &&
|
||||
resumeConfig.sections.awards?.enabled && (
|
||||
resumeConfig.sections.enabled.includes("awards") && (
|
||||
<div class="card bg-base-300 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 ||
|
||||
{resumeConfig.sections.awards?.title ||
|
||||
"Awards & Recognition"}
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import TalkCard from "../components/TalkCard.astro";
|
||||
import { talks } from "../config/data";
|
||||
import { config } from "../config";
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div class="min-h-screen min-w-screen p-4 sm:p-8">
|
||||
<div class="w-full p-4 sm:p-8">
|
||||
<h1
|
||||
class="text-3xl sm:text-4xl font-bold text-primary mb-6 sm:mb-8 text-center"
|
||||
>
|
||||
@@ -14,11 +14,11 @@ import { talks } from "../config/data";
|
||||
<div
|
||||
class="flex flex-row flex-wrap justify-center gap-4 sm:gap-6 max-w-6xl mx-auto"
|
||||
>
|
||||
{talks.map((talk) => <TalkCard talk={talk} />)}
|
||||
{config.talks.map((talk) => <TalkCard talk={talk} />)}
|
||||
</div>
|
||||
|
||||
{
|
||||
talks.length === 0 && (
|
||||
config.talks.length === 0 && (
|
||||
<p class="text-center text-gray-500 mt-12">
|
||||
No talks available yet. Check back soon!
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user