Fixed term!
Some checks failed
Docker Deploy / build-and-push (push) Has been cancelled

This commit is contained in:
2025-06-26 16:19:52 -06:00
parent f89d32d6ce
commit 43c96c73b2
7 changed files with 534 additions and 160 deletions

View File

@@ -1,18 +1,27 @@
import type { FileSystemNode, ResumeData } from './types';
import { talks, projects, socialLinks, techLinks } from '../../config/data';
import type { FileSystemNode, ResumeData } from "./types";
import { talks, projects, socialLinks, techLinks } from "../../config/data";
export async function buildFileSystem(): Promise<{ [key: string]: FileSystemNode }> {
export async function buildFileSystem(): Promise<{
[key: string]: FileSystemNode;
}> {
try {
const response = await fetch('/files/resume.json');
const resumeData: ResumeData = await response.json();
const response = await fetch("/api/resume.json");
if (!response.ok) {
throw new Error(
`Failed to fetch resume data: ${response.status} ${response.statusText}`,
);
}
const resumeData: any = await response.json();
// Fetch blog posts
const postsResponse = await fetch('/api/posts.json');
const postsResponse = await fetch("/api/posts.json");
let postsData = [];
try {
postsData = await postsResponse.json();
} catch (error) {
console.log('Could not fetch posts data:', error);
console.log("Could not fetch posts data:", error);
}
// Build resume files from rxresume json
@@ -25,117 +34,140 @@ export async function buildFileSystem(): Promise<{ [key: string]: FileSystemNode
const contactContent = buildContactContent(resumeData);
const fs: { [key: string]: FileSystemNode } = {
'/': {
type: 'directory',
name: '/',
"/": {
type: "directory",
name: "/",
children: {
'about.txt': {
type: 'file',
name: 'about.txt',
content: `${resumeData.basics.name}\nResearcher, Full-Stack Developer, and IT Professional.\n\nExplore the directories:\n- /resume - Professional experience and skills\n- /posts - Blog posts and articles\n- /talks - Conference presentations\n- /projects - Personal and professional projects\n- /social - Social media and contact links\n- /tech - Technologies and tools I use\n\nType "ls" to see all available files and directories.`
"about.txt": {
type: "file",
name: "about.txt",
content: `${resumeData.basics.name}\nResearcher, Full-Stack Developer, and IT Professional.\n\nExplore the directories:\n- /resume - Professional experience and skills\n- /posts - Blog posts and articles\n- /talks - Conference presentations\n- /projects - Personal and professional projects\n- /social - Social media and contact links\n- /tech - Technologies and tools I use\n\nType "ls" to see all available files and directories.`,
},
'resume': {
type: 'directory',
name: 'resume',
children: resumeFiles
resume: {
type: "directory",
name: "resume",
children: resumeFiles,
},
'posts': {
type: 'directory',
name: 'posts',
children: postsFiles
posts: {
type: "directory",
name: "posts",
children: postsFiles,
},
'talks': {
type: 'directory',
name: 'talks',
children: talksFiles
talks: {
type: "directory",
name: "talks",
children: talksFiles,
},
'projects': {
type: 'directory',
name: 'projects',
children: projectsFiles
projects: {
type: "directory",
name: "projects",
children: projectsFiles,
},
'social': {
type: 'directory',
name: 'social',
children: socialFiles
social: {
type: "directory",
name: "social",
children: socialFiles,
},
'tech': {
type: 'directory',
name: 'tech',
children: techFiles
tech: {
type: "directory",
name: "tech",
children: techFiles,
},
'contact.txt': {
type: 'file',
name: 'contact.txt',
content: contactContent
}
}
}
"contact.txt": {
type: "file",
name: "contact.txt",
content: contactContent,
},
},
},
};
return fs;
} catch (error) {
console.error('Error loading resume data:', error);
console.error("Error loading resume data:", error);
return buildFallbackFileSystem();
}
}
function buildResumeFiles(resumeData: ResumeData): { [key: string]: FileSystemNode } {
function buildResumeFiles(resumeData: any): { [key: string]: FileSystemNode } {
const resumeFiles: { [key: string]: FileSystemNode } = {};
if (resumeData.sections.summary) {
resumeFiles['summary.txt'] = {
type: 'file',
name: 'summary.txt',
content: resumeData.sections.summary.content.replace(/<[^>]*>/g, '')
};
}
try {
if (resumeData.summary) {
resumeFiles["summary.txt"] = {
type: "file",
name: "summary.txt",
content: resumeData.summary.content,
};
}
if (resumeData.sections.skills?.items) {
const skillsContent = resumeData.sections.skills.items
.map(skill => `${skill.name} (Level: ${skill.level}/5)`)
.join('\n');
resumeFiles['skills.txt'] = {
type: 'file',
name: 'skills.txt',
content: skillsContent
};
}
if (resumeData.skills && Array.isArray(resumeData.skills)) {
const skillsContent = resumeData.skills
.map((skill: any) => `${skill.name} (Level: ${skill.level}/5)`)
.join("\n");
resumeFiles["skills.txt"] = {
type: "file",
name: "skills.txt",
content: skillsContent,
};
}
if (resumeData.sections.experience?.items) {
const experienceContent = resumeData.sections.experience.items
.map(exp => {
const summary = exp.summary.replace(/<[^>]*>/g, '').replace(/&nbsp;/g, ' ');
return `${exp.position} at ${exp.company}\n${exp.date} | ${exp.location}\n${summary}\n${exp.url?.href ? `URL: ${exp.url.href}` : ''}\n`;
})
.join('\n---\n\n');
resumeFiles['experience.txt'] = {
type: 'file',
name: 'experience.txt',
content: experienceContent
};
}
if (resumeData.experience && Array.isArray(resumeData.experience)) {
const experienceContent = resumeData.experience
.map((exp: any) => {
const description = Array.isArray(exp.description)
? exp.description.join("\n• ")
: "";
return `${exp.position} at ${exp.company}\n${exp.date} | ${exp.location}\n• ${description}\n${exp.url ? `URL: ${exp.url}` : ""}\n`;
})
.join("\n---\n\n");
resumeFiles["experience.txt"] = {
type: "file",
name: "experience.txt",
content: experienceContent,
};
}
if (resumeData.sections.education?.items) {
const educationContent = resumeData.sections.education.items
.map(edu => `${edu.institution}\n${edu.studyType} - ${edu.area}\n${edu.date}\n${edu.summary ? edu.summary.replace(/<[^>]*>/g, '') : ''}`)
.join('\n\n---\n\n');
resumeFiles['education.txt'] = {
type: 'file',
name: 'education.txt',
content: educationContent
};
}
if (resumeData.education && Array.isArray(resumeData.education)) {
const educationContent = resumeData.education
.map(
(edu: any) =>
`${edu.institution}\n${edu.degree} - ${edu.field}\n${edu.date}\n${edu.details && Array.isArray(edu.details) ? edu.details.join("\n• ") : ""}`,
)
.join("\n\n---\n\n");
resumeFiles["education.txt"] = {
type: "file",
name: "education.txt",
content: educationContent,
};
}
if (resumeData.sections.volunteer?.items) {
const volunteerContent = resumeData.sections.volunteer.items
.map(vol => `${vol.organization}\n${vol.position}\n${vol.date}`)
.join('\n\n---\n\n');
resumeFiles['volunteer.txt'] = {
type: 'file',
name: 'volunteer.txt',
content: volunteerContent
};
if (resumeData.volunteer && Array.isArray(resumeData.volunteer)) {
const volunteerContent = resumeData.volunteer
.map((vol: any) => `${vol.organization}\n${vol.position}\n${vol.date}`)
.join("\n\n---\n\n");
resumeFiles["volunteer.txt"] = {
type: "file",
name: "volunteer.txt",
content: volunteerContent,
};
}
if (resumeData.awards && Array.isArray(resumeData.awards)) {
const awardsContent = resumeData.awards
.map(
(award: any) =>
`${award.title}\n${award.organization}\n${award.date}\n${award.description || ""}`,
)
.join("\n\n---\n\n");
resumeFiles["awards.txt"] = {
type: "file",
name: "awards.txt",
content: awardsContent,
};
}
} catch (error) {
console.error("Error building resume files:", error);
}
return resumeFiles;
@@ -150,15 +182,15 @@ function buildPostsFiles(postsData: any[]): { [key: string]: FileSystemNode } {
title: "${post.title}"
description: "${post.description}"
pubDate: "${post.pubDate}"
tags: [${post.tags.map((tag: string) => `"${tag}"`).join(', ')}]
tags: [${post.tags.map((tag: string) => `"${tag}"`).join(", ")}]
---
${post.content}`;
postsFiles[fileName] = {
type: 'file',
type: "file",
name: fileName,
content
content,
};
});
@@ -168,18 +200,17 @@ ${post.content}`;
function buildTalksFiles(): { [key: string]: FileSystemNode } {
const talksFiles: { [key: string]: FileSystemNode } = {};
talks.forEach(talk => {
talks.forEach((talk) => {
const fileName = `${talk.id}.txt`;
let content = `${talk.name}
${talk.description}
${talk.venue || ''}
${talk.date || ''}
${talk.date || ""}
${talk.link}`;
talksFiles[fileName] = {
type: 'file',
type: "file",
name: fileName,
content
content,
};
});
@@ -189,18 +220,18 @@ ${talk.link}`;
function buildProjectsFiles(): { [key: string]: FileSystemNode } {
const projectsFiles: { [key: string]: FileSystemNode } = {};
projects.forEach(project => {
projects.forEach((project) => {
const fileName = `${project.id}.txt`;
let content = `${project.name}
${project.description}
${project.status || ''}
${project.technologies ? project.technologies.join(', ') : ''}
${project.status || ""}
${project.technologies ? project.technologies.join(", ") : ""}
${project.link}`;
projectsFiles[fileName] = {
type: 'file',
type: "file",
name: fileName,
content
content,
};
});
@@ -210,15 +241,15 @@ ${project.link}`;
function buildSocialFiles(): { [key: string]: FileSystemNode } {
const socialFiles: { [key: string]: FileSystemNode } = {};
socialLinks.forEach(link => {
socialLinks.forEach((link) => {
const fileName = `${link.id}.txt`;
let content = `${link.name}
${link.url}`;
socialFiles[fileName] = {
type: 'file',
type: "file",
name: fileName,
content
content,
};
});
@@ -228,76 +259,126 @@ ${link.url}`;
function buildTechFiles(): { [key: string]: FileSystemNode } {
const techFiles: { [key: string]: FileSystemNode } = {};
techLinks.forEach(link => {
techLinks.forEach((link) => {
const fileName = `${link.id}.txt`;
let content = `${link.name}
${link.url}`;
techFiles[fileName] = {
type: 'file',
type: "file",
name: fileName,
content
content,
};
});
return techFiles;
}
function buildContactContent(resumeData: ResumeData): string {
return [
`Email: ${resumeData.basics.email}`,
'',
'Social Profiles:',
...resumeData.sections.profiles.items.map(profile =>
`${profile.network}: ${profile.url.href}`
)
].join('\n');
function buildContactContent(resumeData: any): string {
try {
const basics = resumeData.basics || {};
const email = basics.email || "Not provided";
const profiles = basics.profiles || [];
return [
`Email: ${email}`,
"",
"Social Profiles:",
...profiles.map((profile: any) => `${profile.network}: ${profile.url}`),
].join("\n");
} catch (error) {
console.error("Error building contact content:", error);
return "Contact information unavailable";
}
}
function buildFallbackFileSystem(): { [key: string]: FileSystemNode } {
const talksFiles = buildTalksFiles();
const projectsFiles = buildProjectsFiles();
const socialFiles = buildSocialFiles();
const techFiles = buildTechFiles();
return {
'/': {
type: 'directory',
name: '/',
"/": {
type: "directory",
name: "/",
children: {
'about.txt': {
type: 'file',
name: 'about.txt',
content: 'Atridad Lahiji\nResearcher, Full-Stack Developer, and IT Professional.\n\nError loading detailed information. Please check the website directly.'
}
}
}
"about.txt": {
type: "file",
name: "about.txt",
content:
"Atridad Lahiji\nResearcher, Full-Stack Developer, and IT Professional.\n\nError loading resume data. Basic navigation still available.\n\nExplore the directories:\n- /talks - Conference presentations\n- /projects - Personal and professional projects\n- /social - Social media and contact links\n- /tech - Technologies and tools I use\n\nType 'ls' to see all available files and directories.",
},
talks: {
type: "directory",
name: "talks",
children: talksFiles,
},
projects: {
type: "directory",
name: "projects",
children: projectsFiles,
},
social: {
type: "directory",
name: "social",
children: socialFiles,
},
tech: {
type: "directory",
name: "tech",
children: techFiles,
},
"help.txt": {
type: "file",
name: "help.txt",
content:
"Available commands:\n- ls - list files\n- cd <directory> - change directory\n- cat <file> - view file contents\n- pwd - show current directory\n- clear - clear terminal\n- help - show this help\n- train - run the train animation",
},
},
},
};
}
export function getCurrentDirectory(fileSystem: { [key: string]: FileSystemNode }, currentPath: string): FileSystemNode {
const pathParts = currentPath.split('/').filter((part: string) => part !== '');
let current = fileSystem['/'];
export function getCurrentDirectory(
fileSystem: { [key: string]: FileSystemNode },
currentPath: string,
): FileSystemNode {
const pathParts = currentPath
.split("/")
.filter((part: string) => part !== "");
let current = fileSystem["/"];
for (const part of pathParts) {
if (current?.children && current.children[part] && current.children[part].type === 'directory') {
if (
current?.children &&
current.children[part] &&
current.children[part].type === "directory"
) {
current = current.children[part];
}
}
return current;
}
export function resolvePath(currentPath: string, path: string): string {
if (path.startsWith('/')) {
if (path.startsWith("/")) {
return path;
}
const currentParts = currentPath.split('/').filter((part: string) => part !== '');
const pathParts = path.split('/');
const currentParts = currentPath
.split("/")
.filter((part: string) => part !== "");
const pathParts = path.split("/");
for (const part of pathParts) {
if (part === '..') {
if (part === "..") {
currentParts.pop();
} else if (part !== '.' && part !== '') {
} else if (part !== "." && part !== "") {
currentParts.push(part);
}
}
return '/' + currentParts.join('/');
}
return "/" + currentParts.join("/");
}