diff --git a/app.js b/app.js
index 0a88df8..266cd4a 100644
--- a/app.js
+++ b/app.js
@@ -47,18 +47,17 @@ app.get("/profile", (req, res) => {
if (!req.session.user) {
return res.render("pages/login", { client_id: process.env.GITHUB_CLIENT_ID });
}
- console.log(req.session.user)
return res.render("pages/profile", { userData: req.session.user });
});
+app.get("/user/github/:username", (req, res) => {
+ const username = req.params.username;
+ res.render("pages/user-profile", { username: username })
+});
// Token fetching stuff
app.get("/token/:service", (req, res) => {
const service = req.params.service;
- console.log(
- service.toUpperCase() + "_TOKEN",
- process.env[service.toUpperCase() + "_TOKEN"],
- );
res.send(process.env[service.toUpperCase() + "_TOKEN"] || "No token found");
});
diff --git a/public/pages/gh-username-search.ejs b/public/pages/gh-username-search.ejs
index b0631d8..95511a2 100644
--- a/public/pages/gh-username-search.ejs
+++ b/public/pages/gh-username-search.ejs
@@ -5,16 +5,14 @@
-
-
GitHub Username Search | FetchCV
-
+
-
-
-
Oh no! Token retriavl error
- We could not fetch your data
-
-
-
-
-
-
-
-
-
-
-
![Profile Picture]()
-
-
Name
-
handle
-
-
-
-
-
-
-
-
-
-
followers
-
following
-
stars
-
location:
-
Email
-
Website (ur/)
-
Github
-
-
-
-
-
-
-
-
Created with FetchCV
-
-
-
-
-
-
-
-
+
diff --git a/public/pages/user-profile.ejs b/public/pages/user-profile.ejs
new file mode 100644
index 0000000..cebc0bd
--- /dev/null
+++ b/public/pages/user-profile.ejs
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ My Profile | FetchCV
+
+
+
+
+
+
+
+
+
Oh no! Token retrival error
+ We could not fetch your data
+
+
+
+
+
+
+
+
+
+
+
![Profile Picture]()
+
+
Name
+
handle
+
+
+
+
+
+
+
+
+
+
followers
+
following
+
stars
+
location:
+
Email
+
Website (ur/)
+
Github
+
+
+
+
+
+
+
+
Created with FetchCV
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/scripts-src/github-username-search.ts b/public/scripts-src/github-username-search.ts
new file mode 100644
index 0000000..be799ec
--- /dev/null
+++ b/public/scripts-src/github-username-search.ts
@@ -0,0 +1,21 @@
+//===============================================
+// Username search
+//===============================================
+
+function newUsername() {
+ let usernameElement = document.getElementById("username") as HTMLInputElement;
+ if (usernameElement) {
+ username = usernameElement.value;
+ window.location.href = window.location.origin + "/user/github/" + username;
+ }
+ }
+
+ const usernameInput = document.getElementById("username");
+ if (usernameInput) {
+ usernameInput.addEventListener("keydown", function (event) {
+ if (event.key == "Enter") {
+ newUsername();
+ }
+ });
+ }
+
\ No newline at end of file
diff --git a/public/scripts/gitlab.ts b/public/scripts-src/gitlab.ts
similarity index 100%
rename from public/scripts/gitlab.ts
rename to public/scripts-src/gitlab.ts
diff --git a/public/scripts/profile-github.ts b/public/scripts-src/profile-github.ts
similarity index 100%
rename from public/scripts/profile-github.ts
rename to public/scripts-src/profile-github.ts
diff --git a/public/scripts/profile.ts b/public/scripts-src/profile.ts
similarity index 100%
rename from public/scripts/profile.ts
rename to public/scripts-src/profile.ts
diff --git a/public/scripts-src/user-profile-github.ts b/public/scripts-src/user-profile-github.ts
new file mode 100644
index 0000000..a6db293
--- /dev/null
+++ b/public/scripts-src/user-profile-github.ts
@@ -0,0 +1,243 @@
+interface UserData {
+ avatar_url: string,
+ name: string,
+ login: string,
+ bio: string,
+ public_repos: number,
+ followers: number,
+ following: number,
+ location: string,
+ email: string,
+ blog: string,
+ html_url: string
+
+ }
+
+ let userData: UserData; // global variable
+ let totalStars: number = 0;
+ let languages: [string, number][] = [];
+ let TOKEN: string;
+ let tokenRecieved: boolean = false;
+ let header: RequestInit | undefined = {};
+
+ getToken("github");
+
+ async function getToken(service: string) {
+ console.log("Fetching token...");
+ const response = await fetch(`/token/${service}`);
+ if (response.ok) {
+ const token = await response.text() as string;
+ tokenRecieved = true;
+ console.log("Token received.");
+ TOKEN = token;
+ header = {
+ 'headers': {
+ 'Authorization': `token ${TOKEN}`
+ }
+ }
+ } else {
+ showError("Token Error", "Failed to fetch token. Try reloading the page.", "bg-red-800");
+ console.error('Error fetching token:', response.statusText);
+ }
+ }
+
+
+
+
+ async function getUserInfo() {
+ if (!tokenRecieved) {
+ console.log('Waiting for token to be received...');
+ setTimeout(getUserInfo, 200);
+ return;
+ }
+
+ try {
+ console.log(header);
+ const response = await fetch(`https://api.github.com/users/${username}`, header);
+ if (response.ok) {
+ userData = await response.json();
+ updateData();
+ updateGithubStats();
+ await getRepoData();
+ } else if (response.status === 404) {
+ showError("Oh no!", "User does not exist. Try another username.", "bg-red-800");
+ } else {
+ showError("Connection Error", "Failed to fetch user data. Try reloading the page.", "bg-red-800");
+ console.error("Failed to fetch user data. Status:", response.status, response);
+ }
+ }
+ catch (error) {
+ showError("Connection Error", "Could not request user data.", "bg-red-800");
+ console.error("Failed to request user data. Error - ", error);
+ }
+ }
+
+ async function getRepoData() {
+ try {
+ const response = await fetch(`https://api.github.com/users/${username}/repos`, header);
+ const repos = await response.json();
+
+ getRepoStars(repos);
+ getRepoLangs(repos);
+ } catch (error) {
+ showError("Oh no!", "Could not get user repository data.", "bg-red-800");
+ console.error('Error fetching data:', error);
+ }
+ }
+
+ function updateData() {
+ if (!userData) {
+ showError("Oh no!", "Could get the user data, my bad", "bg-red-800");
+ return;
+ }
+ (document.querySelector(".user-info") as HTMLElement).classList.remove("hidden");
+ (document.querySelector(".profile-picture") as HTMLImageElement).src = userData.avatar_url;
+ (document.querySelector(".profile-name") as HTMLElement).textContent = userData.name;
+ (document.querySelector(".profile-handle") as HTMLElement).textContent = userData.login;
+ (document.querySelector(".profile-desc") as HTMLElement).textContent = userData.bio;
+
+ (document.querySelector(".profile-repos") as HTMLElement).textContent = userData.public_repos.toString();
+
+ (document.querySelector(".profile-followers") as HTMLElement).textContent = userData.followers.toString();
+ (document.querySelector(".profile-following") as HTMLElement).textContent = userData.following.toString();
+
+
+ (document.querySelector(".profile-location") as HTMLElement).textContent = userData.location;
+ (document.querySelector(".profile-email") as HTMLElement).textContent = userData.email;
+ (document.querySelector(".profile-website") as HTMLAnchorElement).textContent = "Personal Website";
+ (document.querySelector(".profile-github") as HTMLAnchorElement).textContent = "Github Profile";
+ (document.querySelector(".profile-website") as HTMLAnchorElement).href = userData.blog;
+ (document.querySelector(".profile-github") as HTMLAnchorElement).href = userData.html_url;
+ }
+
+ async function getRepoStars(repos: any[]) {
+ for (const repo of repos) {
+ const repoResponse = await fetch(`https://api.github.com/repos/${repo.owner.login}/${repo.name}`, header);
+ const repoData = await repoResponse.json();
+ totalStars += repoData.stargazers_count;
+ }
+
+ (document.querySelector(".profile-stars") as HTMLElement).textContent = totalStars.toString();
+ return totalStars;
+ }
+
+ async function getRepoLangs(repos: any[]) {
+ let langs: { [key: string]: number } = {};
+ languages = [];
+
+ for (const repo of repos) {
+ const repoResponse = await fetch(`https://api.github.com/repos/${repo.owner.login}/${repo.name}/languages`, header);
+ const repoData = await repoResponse.json();
+
+ for (const lang in repoData) {
+ if (langs.hasOwnProperty(lang)) {
+ langs[lang] += repoData[lang];
+ } else {
+ langs[lang] = repoData[lang];
+ }
+ }
+ }
+
+ let sortedLangs: [string, number][] = [];
+ for (let lang in langs) {
+ languages.push([lang, langs[lang]] as [string, number]);
+ }
+
+ languages.sort(function (a, b) {
+ return b[1] - a[1];
+ });
+
+ const total = languages.reduce((acc, curr) => acc + curr[1], 0);
+ const percentLanguages: [string, number][] = languages.map(lang => [lang[0], Math.round((lang[1] / total) * 100)]);
+
+ generateLanguageElements(percentLanguages);
+
+ return languages;
+ }
+
+ function generateLanguageElements(languages: [string, number][]) {
+ const langList = document.querySelector(".profile-langs");
+ if (langList) {
+ langList.innerHTML = "";
+ for (const lang of languages) {
+ const langElement = document.createElement("li");
+ langElement.textContent = `${lang[0]}: ${lang[1]}%`;
+ langElement.classList.add("inline-block", "bg-zinc-200", "dark:bg-zinc-700", "px-2", "m-1", "py-1", "rounded-lg", "border-[1px]", "border-zinc-400", "dark:border-zinc-700", "border-t-zinc-300", "dark:border-t-zinc-600", "inline-flex", "items-center");
+ langElement.innerHTML = ` ` + langElement.textContent;
+ langList.appendChild(langElement);
+ }
+ }
+ function getLangIcon(lang: string) {
+ switch (lang) {
+ case "JavaScript":
+ return "javascript";
+ case "TypeScript":
+ return "typescript";
+ case "EJS":
+ case "HTML":
+ return "html5";
+ case "CSS":
+ return "css3";
+ case "Python":
+ return "python";
+ case "Java":
+ return "java";
+ case "C":
+ return "c";
+ case "C++":
+ return "cplusplus";
+ case "Lua":
+ return "lua";
+ case "Shell":
+ return "bash";
+ case "Ruby":
+ return "ruby";
+ case "PHP":
+ return "php";
+ case "Swift":
+ return "swift";
+ case "Go":
+ return "go";
+ case "Rust":
+ return "rust";
+ case "Kotlin":
+ return "kotlin";
+ case "GDScript":
+ return "godot";
+ case "QML":
+ return "Qt";
+ default:
+ return "gimp hidden";
+ }
+ }
+}
+
+ function updateGithubStats() {
+ console.log(`Username: ${username}`);
+ const githubStatsElement = document.querySelector(".github-stats");
+ if (githubStatsElement) {
+ githubStatsElement.innerHTML = `
+
+
+
+ `;
+ }
+ }
+
+ async function getRateLimit() {
+ if (header) {
+ const response = await fetch('https://api.github.com/rate_limit', header);
+
+ if (response.ok) {
+ const data = await response.json();
+ } else {
+ console.error('Error fetching rate limit:', response.statusText);
+ }
+ }
+ }
diff --git a/public/scripts/main.ts b/public/scripts-src/user-profile.ts
similarity index 82%
rename from public/scripts/main.ts
rename to public/scripts-src/user-profile.ts
index 7a31d65..19debb9 100644
--- a/public/scripts/main.ts
+++ b/public/scripts-src/user-profile.ts
@@ -4,14 +4,7 @@
// First check for username
window.addEventListener("load", () => {
- if (localStorage.getItem("savedUsername")) {
- username = JSON.parse(localStorage.getItem("savedUsername") as string);
- getUserInfo();
- }
- else {
- console.log("No username saved.");
- showError("No username", "Please enter a username.", "bg-blue-800");
- }
+ getUserInfo();
});
@@ -24,7 +17,7 @@ function newUsername() {
let usernameElement = document.getElementById("username") as HTMLInputElement;
if (usernameElement) {
username = usernameElement.value;
- localStorage.setItem("savedUsername", JSON.stringify(username));
+ window.location.href = window.location.origin + "/user/github/" + username;
getUserInfo();
}
}
@@ -69,4 +62,4 @@ function hideError() {
if (errorBox) {
errorBox.classList.add("hidden");
}
-}
\ No newline at end of file
+}
diff --git a/public/scripts/github-username-search.js b/public/scripts/github-username-search.js
new file mode 100644
index 0000000..6f51ae8
--- /dev/null
+++ b/public/scripts/github-username-search.js
@@ -0,0 +1,18 @@
+//===============================================
+// Username search
+//===============================================
+function newUsername() {
+ let usernameElement = document.getElementById("username");
+ if (usernameElement) {
+ username = usernameElement.value;
+ window.location.href = window.location.origin + "/user/github/" + username;
+ }
+}
+const usernameInput = document.getElementById("username");
+if (usernameInput) {
+ usernameInput.addEventListener("keydown", function (event) {
+ if (event.key == "Enter") {
+ newUsername();
+ }
+ });
+}
diff --git a/public/scripts/github.ts b/public/scripts/github.ts
deleted file mode 100644
index b1a030a..0000000
--- a/public/scripts/github.ts
+++ /dev/null
@@ -1,242 +0,0 @@
-interface UserData {
- avatar_url: string,
- name: string,
- login: string,
- bio: string,
- public_repos: number,
- followers: number,
- following: number,
- location: string,
- email: string,
- blog: string,
- html_url: string
-
-}
-
-let userData: UserData; // global variable
-let username: string = "";
-let totalStars: number = 0;
-let languages: [string, number][] = [];
-let TOKEN: string;
-let tokenRecieved: boolean = false;
-let header: RequestInit | undefined = {};
-
-getToken("github");
-
-async function getToken(service: string) {
- console.log("Fetching token...");
- const response = await fetch(`/token/${service}`);
- if (response.ok) {
- const token = await response.text() as string;
- tokenRecieved = true;
- console.log("Token received.");
- TOKEN = token;
- header = {
- 'headers': {
- 'Authorization': `token ${TOKEN}`
- }
- }
- } else {
- showError("Token Error", "Failed to fetch token. Try reloading the page.", "bg-red-800");
- console.error('Error fetching token:', response.statusText);
- }
-}
-
-
-
-
-async function getUserInfo() {
- if (!tokenRecieved) {
- console.log('Waiting for token to be received...');
- setTimeout(getUserInfo, 200);
- return;
- }
-
- try {
- console.log(header);
- const response = await fetch(`https://api.github.com/users/${username}`, header);
- if (response.ok) {
- userData = await response.json();
- updateData();
- updateGithubStats();
- await getRepoData();
- } else if (response.status === 404) {
- showError("Oh no!", "User does not exist. Try another username.", "bg-red-800");
- } else {
- showError("Connection Error", "Failed to fetch user data. Try reloading the page.", "bg-red-800");
- console.error("Failed to fetch user data. Status:", response.status, response);
- }
- }
- catch (error) {
- showError("Connection Error", "Could not request user data.", "bg-red-800");
- console.error("Failed to request user data. Error - ", error);
- }
-}
-
-async function getRepoData() {
- try {
- const response = await fetch(`https://api.github.com/users/${username}/repos`, header);
- const repos = await response.json();
-
- getRepoStars(repos);
- getRepoLangs(repos);
- } catch (error) {
- showError("Oh no!", "Could not get user repository data.", "bg-red-800");
- console.error('Error fetching data:', error);
- }
-}
-
-function updateData() {
- if (!userData) {
- showError("Oh no!", "Could get the user data, my bad", "bg-red-800");
- return;
- }
- (document.querySelector(".user-info") as HTMLElement).classList.remove("hidden");
- (document.querySelector(".profile-picture") as HTMLImageElement).src = userData.avatar_url;
- (document.querySelector(".profile-name") as HTMLElement).textContent = userData.name;
- (document.querySelector(".profile-handle") as HTMLElement).textContent = userData.login;
- (document.querySelector(".profile-desc") as HTMLElement).textContent = userData.bio;
-
- (document.querySelector(".profile-repos") as HTMLElement).textContent = userData.public_repos.toString();
-
- (document.querySelector(".profile-followers") as HTMLElement).textContent = userData.followers.toString();
- (document.querySelector(".profile-following") as HTMLElement).textContent = userData.following.toString();
-
-
- (document.querySelector(".profile-location") as HTMLElement).textContent = userData.location;
- (document.querySelector(".profile-email") as HTMLElement).textContent = userData.email;
- (document.querySelector(".profile-website") as HTMLAnchorElement).textContent = "Personal Website";
- (document.querySelector(".profile-github") as HTMLAnchorElement).textContent = "Github Profile";
- (document.querySelector(".profile-website") as HTMLAnchorElement).href = userData.blog;
- (document.querySelector(".profile-github") as HTMLAnchorElement).href = userData.html_url;
-}
-
-async function getRepoStars(repos: any[]) {
- for (const repo of repos) {
- const repoResponse = await fetch(`https://api.github.com/repos/${repo.owner.login}/${repo.name}`, header);
- const repoData = await repoResponse.json();
- totalStars += repoData.stargazers_count;
- }
-
- (document.querySelector(".profile-stars") as HTMLElement).textContent = totalStars.toString();
- return totalStars;
-}
-
-async function getRepoLangs(repos: any[]) {
- let langs: { [key: string]: number } = {};
- languages = [];
-
- for (const repo of repos) {
- const repoResponse = await fetch(`https://api.github.com/repos/${repo.owner.login}/${repo.name}/languages`, header);
- const repoData = await repoResponse.json();
-
- for (const lang in repoData) {
- if (langs.hasOwnProperty(lang)) {
- langs[lang] += repoData[lang];
- } else {
- langs[lang] = repoData[lang];
- }
- }
- }
-
- let sortedLangs: [string, number][] = [];
- for (let lang in langs) {
- languages.push([lang, langs[lang]] as [string, number]);
- }
-
- languages.sort(function (a, b) {
- return b[1] - a[1];
- });
-
- const total = languages.reduce((acc, curr) => acc + curr[1], 0);
- const percentLanguages: [string, number][] = languages.map(lang => [lang[0], Math.round((lang[1] / total) * 100)]);
-
- generateLanguageElements(percentLanguages);
-
- return languages;
-}
-
-function generateLanguageElements(languages: [string, number][]) {
- const langList = document.querySelector(".profile-langs");
- if (langList) {
- langList.innerHTML = "";
- for (const lang of languages) {
- const langElement = document.createElement("li");
- langElement.textContent = `${lang[0]}: ${lang[1]}%`;
- langElement.classList.add("inline-block", "bg-zinc-200", "dark:bg-zinc-700", "px-2", "m-1", "py-1", "rounded-lg", "border-[1px]", "border-zinc-400", "dark:border-zinc-700", "border-t-zinc-300", "dark:border-t-zinc-600", "inline-flex", "items-center");
- langElement.innerHTML = ` ` + langElement.textContent;
- langList.appendChild(langElement);
- }
- }
- function getLangIcon(lang: string) {
- switch (lang) {
- case "JavaScript":
- return "javascript";
- case "TypeScript":
- return "typescript";
- case "EJS":
- case "HTML":
- return "html5";
- case "CSS":
- return "css3";
- case "Python":
- return "python";
- case "Java":
- return "java";
- case "C":
- return "c";
- case "C++":
- return "cplusplus";
- case "Lua":
- return "lua";
- case "Shell":
- return "bash";
- case "Ruby":
- return "ruby";
- case "PHP":
- return "php";
- case "Swift":
- return "swift";
- case "Go":
- return "go";
- case "Rust":
- return "rust";
- case "Kotlin":
- return "kotlin";
- case "GDScript":
- return "godot";
- default:
- return "gimp hidden";
- }
- }
-}
-
-function updateGithubStats() {
- console.log(`Username: ${username}`);
- const githubStatsElement = document.querySelector(".github-stats");
- if (githubStatsElement) {
- githubStatsElement.innerHTML = `
-
-
-
- `;
- }
-}
-
-async function getRateLimit() {
- if (header) {
- const response = await fetch('https://api.github.com/rate_limit', header);
-
- if (response.ok) {
- const data = await response.json();
- } else {
- console.error('Error fetching rate limit:', response.statusText);
- }
- }
-}
\ No newline at end of file
diff --git a/public/scripts/github.js b/public/scripts/user-profile-github.js
similarity index 92%
rename from public/scripts/github.js
rename to public/scripts/user-profile-github.js
index bbf984c..ced6105 100644
--- a/public/scripts/github.js
+++ b/public/scripts/user-profile-github.js
@@ -1,5 +1,4 @@
let userData; // global variable
-let username = "";
let totalStars = 0;
let languages = [];
let TOKEN;
@@ -170,6 +169,8 @@ function generateLanguageElements(languages) {
return "kotlin";
case "GDScript":
return "godot";
+ case "QML":
+ return "Qt";
default:
return "gimp hidden";
}
@@ -180,16 +181,16 @@ function updateGithubStats() {
const githubStatsElement = document.querySelector(".github-stats");
if (githubStatsElement) {
githubStatsElement.innerHTML = `
-
-
-
- `;
+
+
+
+ `;
}
}
async function getRateLimit() {
diff --git a/public/scripts/main.js b/public/scripts/user-profile.js
similarity index 82%
rename from public/scripts/main.js
rename to public/scripts/user-profile.js
index c8b337f..06cb515 100644
--- a/public/scripts/main.js
+++ b/public/scripts/user-profile.js
@@ -3,14 +3,7 @@
//===============================================
// First check for username
window.addEventListener("load", () => {
- if (localStorage.getItem("savedUsername")) {
- username = JSON.parse(localStorage.getItem("savedUsername"));
- getUserInfo();
- }
- else {
- console.log("No username saved.");
- showError("No username", "Please enter a username.", "bg-blue-800");
- }
+ getUserInfo();
});
//===============================================
// Setting username
@@ -20,7 +13,7 @@ function newUsername() {
let usernameElement = document.getElementById("username");
if (usernameElement) {
username = usernameElement.value;
- localStorage.setItem("savedUsername", JSON.stringify(username));
+ window.location.href = window.location.origin + "/user/github/" + username;
getUserInfo();
}
}
diff --git a/public/styles/tailwind.css b/public/styles/tailwind.css
index 526b10c..f66150c 100644
--- a/public/styles/tailwind.css
+++ b/public/styles/tailwind.css
@@ -679,6 +679,14 @@ video {
height: 100%;
}
+.h-\[100v\] {
+ height: 100v;
+}
+
+.h-\[100vw\] {
+ height: 100vw;
+}
+
.w-\[100\%\] {
width: 100%;
}
@@ -699,6 +707,14 @@ video {
width: 100vw;
}
+.w-\[80vw\] {
+ width: 80vw;
+}
+
+.w-\[400px\] {
+ width: 400px;
+}
+
.max-w-\[45vw\] {
max-width: 45vw;
}
@@ -1030,12 +1046,26 @@ video {
}
@media (min-width: 640px) {
+ .sm\:w-\[100vw\] {
+ width: 100vw;
+ }
+
+ .sm\:w-\[80vw\] {
+ width: 80vw;
+ }
+
.sm\:px-0 {
padding-left: 0px;
padding-right: 0px;
}
}
+@media (min-width: 768px) {
+ .md\:w-\[400px\] {
+ width: 400px;
+ }
+}
+
@media (min-width: 1024px) {
.lg\:px-\[20vw\] {
padding-left: 20vw;
diff --git a/tsconfig.json b/tsconfig.json
index 92ff512..af5a1d2 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -7,7 +7,8 @@
"strictNullChecks": true,
"strictFunctionTypes": true,
"noEmit": false,
- "noEmitOnError": false
+ "noEmitOnError": false,
+ "outDir": "public/scripts"
},
"exclude": [
"node_modules",