mirror of
https://github.com/System-End/My-website.git
synced 2026-04-19 22:05:07 +00:00
feat: add GitHub projects and Spotify songs
This commit is contained in:
parent
db3a264d8a
commit
20bccba46c
12 changed files with 245 additions and 1 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
.env
|
||||
14
index.html
14
index.html
|
|
@ -24,6 +24,18 @@
|
|||
<h1>Welcome to my resume website</h1>
|
||||
<p>This is the homepage. It is currently a WIP.</p>
|
||||
</main>
|
||||
|
||||
<div id="spotify-list">
|
||||
<h2>My Most Listened to Songs</h2>
|
||||
<ul id="spotify-list"></ul>
|
||||
</div>
|
||||
|
||||
<div id="github-repos">
|
||||
<h2>My GitHub Repositories</h2>
|
||||
<ul id="repo-list"></ul>
|
||||
</div>
|
||||
<!-- scripts -->
|
||||
<script src="/scripts/theme-toggle.js"></script>
|
||||
<script src="/scripts/spotify.js"></script>
|
||||
<script src="/scripts/github-repos.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
0
pages/HEID.html
Normal file
0
pages/HEID.html
Normal file
0
pages/POE.html
Normal file
0
pages/POE.html
Normal file
0
pages/about.html
Normal file
0
pages/about.html
Normal file
32
pages/github-repos.html
Normal file
32
pages/github-repos.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>GitHub Projects</title>
|
||||
<link rel="stylesheet" href="/pages/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="loading">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<div id="navbar"></div>
|
||||
<script>
|
||||
fetch('/pages/navbar.html')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
document.getElementById('navbar').innerHTML = data;
|
||||
});
|
||||
</script>
|
||||
<main>
|
||||
<h1>My GitHub Projects</h1>
|
||||
<ul id="repo-list"></ul>
|
||||
</main>
|
||||
<script>
|
||||
window.addEventListener('load', () => {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
});
|
||||
</script>
|
||||
<script src="/scripts/github-repos.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -2,5 +2,7 @@
|
|||
<ul>
|
||||
<li><a href="../index.html">Home</a></li>
|
||||
<li><a href="/pages/APCSP.html">APCSP Project</a></li>
|
||||
<li><a href="/pages/github-projects.html">GitHub Projects</a></li>
|
||||
</ul>
|
||||
<input type="color" id="theme-color-picker" title="Choose your color">
|
||||
</nav>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,15 @@ body {
|
|||
--accent-color: #ff5722;
|
||||
}
|
||||
|
||||
[data-theme="light"] {
|
||||
--background-color: var(--background-color-light);
|
||||
--text-color: var(--text-color-light);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--background-color: var(--background-color-dark);
|
||||
--text-color: var(--text-color-dark);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--background-color);
|
||||
|
|
@ -48,6 +57,14 @@ nav ul li a {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Theme toggle button */
|
||||
.theme-toggle {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5em;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* Header styling */
|
||||
|
|
@ -90,3 +107,59 @@ nav ul li a {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Light/Dark Mode Toggle */
|
||||
[data-theme="dark"] body {
|
||||
background-color: var(--background-color-dark);
|
||||
color: var(--text-color-dark);
|
||||
}
|
||||
|
||||
[data-theme="light"] body {
|
||||
background-color: var(--background-color-light);
|
||||
color: var(--text-color-light);
|
||||
}
|
||||
|
||||
/* responsive design */
|
||||
@media (max-width: 768px) {
|
||||
nav ul {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main-content { padding: 10px; }
|
||||
}
|
||||
|
||||
/* Loading animation */
|
||||
#loading {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#loading .spinner {
|
||||
border: 8px solid #f3f3f3;
|
||||
border-top: 8px solid var(--primary-color);
|
||||
border-radius: 50%;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
#spotify-list {
|
||||
position:fixed;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 200px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 10px;
|
||||
box-sizing: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
10
scripts/github-repos.js
Normal file
10
scripts/github-repos.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fetch('/github-repos')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const repoList = document.getElementById('repo-list');
|
||||
data.forEach(repo => {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.innerHTML = `<h3>${repo.name}</h3><p>${repo.readme}</p>`;
|
||||
repoList.appendChild(listItem);
|
||||
});
|
||||
});
|
||||
10
scripts/spotify.js
Normal file
10
scripts/spotify.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
fetch('/spotify-data')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const spotifyList = document.getElementById('spotify-list');
|
||||
data.items.forEach(track => {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.textContent = `${track.name} by ${track.artists.map(artist => artist.name).join(', ')}`;
|
||||
spotifyList.appendChild(listItem);
|
||||
});
|
||||
});
|
||||
13
scripts/theme-toggle.js
Normal file
13
scripts/theme-toggle.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
const colorPicker = document.getElementById('theme-color-picker'); // Get the color picker element
|
||||
|
||||
// set intial color
|
||||
const currentColor = localStorage.getItem('theme-color'); || '#3f10ad';
|
||||
document.documentElement.style.setProperty('--primary-color', currentColor);
|
||||
colorPicker.value = currentColor;
|
||||
|
||||
// update theme color and sae in local storage
|
||||
colorPicker.addEventListener('input', (event) => {
|
||||
const newColor = event.target.value;
|
||||
document.documentElement.style.setProperty('--primary-color', newColor);
|
||||
localStorage.setItem('theme-color', newColor);
|
||||
})
|
||||
90
server.js
Normal file
90
server.js
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const querystring = require('querystring');
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3000;
|
||||
|
||||
const client_id = process.env.SPOTIFY_CLIENT_ID;
|
||||
const client_secret = process.env.SPOTIFY_CLIENT_SECRET;
|
||||
const redirect_uri = process.env.SPOTIFY_REDIRECT_URI;
|
||||
|
||||
let access_token = '';
|
||||
let refresh_token = '';
|
||||
|
||||
app.use(express.static('public'));
|
||||
|
||||
app.get('/login', (req, res) => {
|
||||
const scope = 'user-top-read';
|
||||
const authUrl = 'https://accounts.spotify.com/authorize?' +
|
||||
querystring.stringify({
|
||||
response_type: 'code',
|
||||
client_id: client_id,
|
||||
scope: scope,
|
||||
redirect_uri: redirect_uri
|
||||
});
|
||||
res.redirect(authUrl);
|
||||
});
|
||||
|
||||
app.get('/callback', async (req, res) => {
|
||||
const code = req.query.code || null;
|
||||
const fetch = (await import('node-fetch')).default;
|
||||
const tokenResponse = await fetch('https://accounts.spotify.com/api/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + Buffer.from(`${client_id}:${client_secret}`).toString('base64'),
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: querystring.stringify({
|
||||
code: code,
|
||||
redirect_uri: redirect_uri,
|
||||
grant_type: 'authorization_code'
|
||||
})
|
||||
});
|
||||
const tokenData = await tokenResponse.json();
|
||||
access_token = tokenData.access_token;
|
||||
refresh_token = tokenData.refresh_token;
|
||||
|
||||
res.redirect('/');
|
||||
});
|
||||
|
||||
async function refreshAccessToken() {
|
||||
const fetch = (await import('node-fetch')).default;
|
||||
const tokenResponse = await fetch('https://accounts.spotify.com/api/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + Buffer.from(`${client_id}:${client_secret}`).toString('base64'),
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: querystring.stringify({
|
||||
grant_type: 'refresh_token',
|
||||
refresh_token: refresh_token
|
||||
})
|
||||
});
|
||||
const tokenData = await tokenResponse.json();
|
||||
access_token = tokenData.access_token;
|
||||
}
|
||||
|
||||
app.get('/spotify-data', async (req, res) => {
|
||||
if (!access_token) {
|
||||
await refreshAccessToken();
|
||||
}
|
||||
const fetch = (await import('node-fetch')).default;
|
||||
const response = await fetch('https://api.spotify.com/v1/me/top/tracks', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${access_token}`
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
app.get('/github-repos', async (req, res) => {
|
||||
const fetch = (await import('node-fetch')).default;
|
||||
const response = await fetch(`https://api.github.com/users/${process.env.GITHUB_USERNAME}/repos`);
|
||||
const data = await response.json();
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Server running at http://localhost:${port}`);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue