Some checks failed
Docker Deploy / build-and-push (push) Has been cancelled
385 lines
11 KiB
TypeScript
385 lines
11 KiB
TypeScript
import type { FileSystemNode, ResumeData } from "./types";
|
|
import { talks, projects, socialLinks, techLinks } from "../../config/data";
|
|
|
|
export async function buildFileSystem(): Promise<{
|
|
[key: string]: FileSystemNode;
|
|
}> {
|
|
try {
|
|
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");
|
|
let postsData = [];
|
|
try {
|
|
postsData = await postsResponse.json();
|
|
} catch (error) {
|
|
console.log("Could not fetch posts data:", error);
|
|
}
|
|
|
|
// Build resume files from rxresume json
|
|
const resumeFiles = buildResumeFiles(resumeData);
|
|
const postsFiles = buildPostsFiles(postsData);
|
|
const talksFiles = buildTalksFiles();
|
|
const projectsFiles = buildProjectsFiles();
|
|
const socialFiles = buildSocialFiles();
|
|
const techFiles = buildTechFiles();
|
|
const contactContent = buildContactContent(resumeData);
|
|
|
|
const fs: { [key: string]: FileSystemNode } = {
|
|
"/": {
|
|
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.`,
|
|
},
|
|
resume: {
|
|
type: "directory",
|
|
name: "resume",
|
|
children: resumeFiles,
|
|
},
|
|
posts: {
|
|
type: "directory",
|
|
name: "posts",
|
|
children: postsFiles,
|
|
},
|
|
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,
|
|
},
|
|
"contact.txt": {
|
|
type: "file",
|
|
name: "contact.txt",
|
|
content: contactContent,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
return fs;
|
|
} catch (error) {
|
|
console.error("Error loading resume data:", error);
|
|
return buildFallbackFileSystem();
|
|
}
|
|
}
|
|
|
|
function buildResumeFiles(resumeData: any): { [key: string]: FileSystemNode } {
|
|
const resumeFiles: { [key: string]: FileSystemNode } = {};
|
|
|
|
try {
|
|
if (resumeData.summary) {
|
|
resumeFiles["summary.txt"] = {
|
|
type: "file",
|
|
name: "summary.txt",
|
|
content: resumeData.summary.content,
|
|
};
|
|
}
|
|
|
|
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.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.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.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;
|
|
}
|
|
|
|
function buildPostsFiles(postsData: any[]): { [key: string]: FileSystemNode } {
|
|
const postsFiles: { [key: string]: FileSystemNode } = {};
|
|
|
|
postsData.forEach((post: any) => {
|
|
const fileName = `${post.slug}.md`;
|
|
let content = `---
|
|
title: "${post.title}"
|
|
description: "${post.description}"
|
|
pubDate: "${post.pubDate}"
|
|
tags: [${post.tags.map((tag: string) => `"${tag}"`).join(", ")}]
|
|
---
|
|
|
|
${post.content}`;
|
|
|
|
postsFiles[fileName] = {
|
|
type: "file",
|
|
name: fileName,
|
|
content,
|
|
};
|
|
});
|
|
|
|
return postsFiles;
|
|
}
|
|
|
|
function buildTalksFiles(): { [key: string]: FileSystemNode } {
|
|
const talksFiles: { [key: string]: FileSystemNode } = {};
|
|
|
|
talks.forEach((talk) => {
|
|
const fileName = `${talk.id}.txt`;
|
|
let content = `${talk.name}
|
|
${talk.description}
|
|
${talk.date || ""}
|
|
${talk.link}`;
|
|
|
|
talksFiles[fileName] = {
|
|
type: "file",
|
|
name: fileName,
|
|
content,
|
|
};
|
|
});
|
|
|
|
return talksFiles;
|
|
}
|
|
|
|
function buildProjectsFiles(): { [key: string]: FileSystemNode } {
|
|
const projectsFiles: { [key: string]: FileSystemNode } = {};
|
|
|
|
projects.forEach((project) => {
|
|
const fileName = `${project.id}.txt`;
|
|
let content = `${project.name}
|
|
${project.description}
|
|
${project.status || ""}
|
|
${project.technologies ? project.technologies.join(", ") : ""}
|
|
${project.link}`;
|
|
|
|
projectsFiles[fileName] = {
|
|
type: "file",
|
|
name: fileName,
|
|
content,
|
|
};
|
|
});
|
|
|
|
return projectsFiles;
|
|
}
|
|
|
|
function buildSocialFiles(): { [key: string]: FileSystemNode } {
|
|
const socialFiles: { [key: string]: FileSystemNode } = {};
|
|
|
|
socialLinks.forEach((link) => {
|
|
const fileName = `${link.id}.txt`;
|
|
let content = `${link.name}
|
|
${link.url}`;
|
|
|
|
socialFiles[fileName] = {
|
|
type: "file",
|
|
name: fileName,
|
|
content,
|
|
};
|
|
});
|
|
|
|
return socialFiles;
|
|
}
|
|
|
|
function buildTechFiles(): { [key: string]: FileSystemNode } {
|
|
const techFiles: { [key: string]: FileSystemNode } = {};
|
|
|
|
techLinks.forEach((link) => {
|
|
const fileName = `${link.id}.txt`;
|
|
let content = `${link.name}
|
|
${link.url}`;
|
|
|
|
techFiles[fileName] = {
|
|
type: "file",
|
|
name: fileName,
|
|
content,
|
|
};
|
|
});
|
|
|
|
return techFiles;
|
|
}
|
|
|
|
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: "/",
|
|
children: {
|
|
"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["/"];
|
|
|
|
for (const part of pathParts) {
|
|
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("/")) {
|
|
return path;
|
|
}
|
|
|
|
const currentParts = currentPath
|
|
.split("/")
|
|
.filter((part: string) => part !== "");
|
|
const pathParts = path.split("/");
|
|
|
|
for (const part of pathParts) {
|
|
if (part === "..") {
|
|
currentParts.pop();
|
|
} else if (part !== "." && part !== "") {
|
|
currentParts.push(part);
|
|
}
|
|
}
|
|
|
|
return "/" + currentParts.join("/");
|
|
}
|