mirror of
https://github.com/System-End/My-website.git
synced 2026-04-19 19:45:10 +00:00
o
This commit is contained in:
parent
40605d69ae
commit
aea8ec2bd4
14 changed files with 477 additions and 47 deletions
50
cloudflare-worker.js
Normal file
50
cloudflare-worker.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
import { Router } from 'itty-router';
|
||||
|
||||
const router = Router();
|
||||
|
||||
const CLIENT_ID = 'YOUR_SPOTIFY_CLIENT_ID';
|
||||
const CLIENT_SECRET = 'YOUR_SPOTIFY_CLIENT_SECRET';
|
||||
const REDIRECT_URI = 'YOUR_REDIRECT_URI';
|
||||
let accessToken = null;
|
||||
|
||||
// Function to refresh Spotify Access Token
|
||||
async function refreshAccessToken() {
|
||||
const authResponse = await fetch('https://accounts.spotify.com/api/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Basic ' + btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)
|
||||
},
|
||||
body: 'grant_type=client_credentials'
|
||||
});
|
||||
|
||||
const data = await authResponse.json();
|
||||
accessToken = data.access_token;
|
||||
}
|
||||
|
||||
// Spotify Data Fetch Endpoint
|
||||
router.get('/spotify-data', async (request) => {
|
||||
if (!accessToken) {
|
||||
await refreshAccessToken();
|
||||
}
|
||||
|
||||
const spotifyResponse = await fetch('https://api.spotify.com/v1/me/top/tracks?limit=10', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
}
|
||||
});
|
||||
|
||||
const spotifyData = await spotifyResponse.json();
|
||||
return new Response(JSON.stringify(spotifyData), {
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
});
|
||||
|
||||
// Default Route
|
||||
router.all('*', () => new Response('Not Found', { status: 404 }));
|
||||
|
||||
// Event Listener for Worker Requests
|
||||
addEventListener('fetch', (event) => {
|
||||
event.respondWith(router.handle(event.request));
|
||||
});
|
||||
|
|
@ -8,7 +8,7 @@ npm run build
|
|||
|
||||
# Step 3: Deploy Frontend to Cloudflare Pages
|
||||
Write-Output "Deploying frontend to Cloudflare Pages..."
|
||||
wrangler pages deploy ./build --project-name personal-site
|
||||
wrangler pages deploy ./build --project-name personal-site-test
|
||||
|
||||
# Step 4: Deploy Backend to Cloudflare Workers
|
||||
Write-Output "Deploying backend worker..."
|
||||
|
|
|
|||
16
package.json
16
package.json
|
|
@ -3,6 +3,22 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-class-properties": "^7.25.9",
|
||||
"@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9",
|
||||
"@babel/plugin-transform-numeric-separator": "^7.25.9",
|
||||
"@babel/plugin-transform-optional-chaining": "^7.25.9",
|
||||
"@babel/plugin-transform-private-methods": "^7.25.9",
|
||||
"@eslint/eslintrc": "^1.4.1",
|
||||
"@eslint/js": "^8.57.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
"@babel/plugin-transform-class-properties": "^7.25.9",
|
||||
"@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9",
|
||||
"@babel/plugin-transform-numeric-separator": "^7.25.9",
|
||||
"@babel/plugin-transform-optional-chaining": "^7.25.9",
|
||||
"@babel/plugin-transform-private-methods": "^7.25.9",
|
||||
"@eslint/eslintrc": "^1.4.1",
|
||||
"@eslint/js": "^8.57.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
"cra-template": "1.2.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"short_name": "Personal Website",
|
||||
"name": "My Personal Website",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
|
|
@ -8,12 +8,12 @@
|
|||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"src": "logo.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"src": "logo.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
|
|
|
|||
73
spotify-worker.js
Normal file
73
spotify-worker.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
|
||||
import { Router } from 'itty-router';
|
||||
|
||||
const router = Router();
|
||||
|
||||
const CLIENT_ID = 'YOUR_SPOTIFY_CLIENT_ID';
|
||||
const CLIENT_SECRET = 'YOUR_SPOTIFY_CLIENT_SECRET';
|
||||
|
||||
let accessToken = null;
|
||||
let tokenExpiry = null;
|
||||
|
||||
// Helper function to fetch a new access token
|
||||
async function fetchAccessToken() {
|
||||
const response = await fetch('https://accounts.spotify.com/api/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': 'Basic ' + btoa(`${CLIENT_ID}:${CLIENT_SECRET}`)
|
||||
},
|
||||
body: 'grant_type=client_credentials'
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch Spotify token: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
accessToken = data.access_token;
|
||||
tokenExpiry = Date.now() + (data.expires_in * 1000);
|
||||
console.log('New access token fetched');
|
||||
}
|
||||
|
||||
// Middleware to ensure a valid access token
|
||||
async function ensureAccessToken() {
|
||||
if (!accessToken || Date.now() >= tokenExpiry) {
|
||||
await fetchAccessToken();
|
||||
}
|
||||
}
|
||||
|
||||
// Route to fetch Spotify data (Top Tracks)
|
||||
router.get('/spotify-data', async () => {
|
||||
try {
|
||||
await ensureAccessToken();
|
||||
|
||||
const response = await fetch('https://api.spotify.com/v1/me/top/tracks?limit=10', {
|
||||
headers: { 'Authorization': `Bearer ${accessToken}` }
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
return new Response(JSON.stringify({ error: 'Failed to fetch Spotify data' }), { status: response.status });
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return new Response(JSON.stringify({
|
||||
topTracks: data.items.map(track => ({
|
||||
name: track.name,
|
||||
artist: track.artists.map(artist => artist.name).join(', ')
|
||||
}))
|
||||
}), { headers: { 'Content-Type': 'application/json' } });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching Spotify data:', error);
|
||||
return new Response(JSON.stringify({ error: 'Internal server error' }), { status: 500 });
|
||||
}
|
||||
});
|
||||
|
||||
// Default route for unmatched paths
|
||||
router.all('*', () => new Response('Not Found', { status: 404 }));
|
||||
|
||||
// Event listener for handling requests
|
||||
addEventListener('fetch', (event) => {
|
||||
event.respondWith(router.handle(event.request));
|
||||
});
|
||||
|
|
@ -5,6 +5,9 @@
|
|||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
background-image: url('/logo.png');
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
|
|
|
|||
57
src/App.js
57
src/App.js
|
|
@ -1,21 +1,42 @@
|
|||
import React from 'react';
|
||||
import Navbar from './components/Navbar';
|
||||
import SpotifyList from './components/SpotifyList';
|
||||
import GitHubRepos from './components/GitHubRepos';
|
||||
import './index.css';
|
||||
import React, { useEffect } from 'react';
|
||||
import './rolling-code.css';
|
||||
import logo from './logo.png';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<Navbar />
|
||||
<main>
|
||||
<h1>Welcome to my resume website</h1>
|
||||
<p>This is the homepage. It is currently a WIP.</p>
|
||||
</main>
|
||||
<SpotifyList />
|
||||
<GitHubRepos />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const App = () => {
|
||||
useEffect(() => {
|
||||
// Generate rolling code lines
|
||||
const container = document.querySelector('.rolling-code-container');
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const line = document.createElement('div');
|
||||
line.className = 'code-line';
|
||||
line.style.animationDelay = `${Math.random() * 5}s`;
|
||||
line.textContent = Math.random().toString(36).substr(2, 80);
|
||||
container.appendChild(line);
|
||||
}
|
||||
|
||||
// Generate particles
|
||||
const particleContainer = document.querySelector('.particle-container');
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const particle = document.createElement('div');
|
||||
particle.className = 'particle';
|
||||
particle.style.left = `${Math.random() * 100}vw`;
|
||||
particle.style.animationDelay = `${Math.random() * 10}s`;
|
||||
particleContainer.appendChild(particle);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<img src={logo} alt="Logo" style="width: 100px; height: auto;"></img>
|
||||
<div className="animated-lighting"></div>
|
||||
<div className="rolling-code-container"></div>
|
||||
<div className="particle-container"></div>
|
||||
<div className="content">
|
||||
<h1>Welcome to My Website</h1>
|
||||
<p>Enhanced Background with Lighting Effects</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
|
|
|||
54
src/components/GithubRepos.css
Normal file
54
src/components/GithubRepos.css
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
.github-repos-container {
|
||||
padding: 2rem;
|
||||
background-color: #f9f9f9;
|
||||
font-family: Arial, sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.repos-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.repo-card {
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
padding: 1rem;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.repo-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.repo-name {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
color: #0077cc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.repo-name:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.repo-description {
|
||||
color: #555;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.repo-language {
|
||||
display: inline-block;
|
||||
margin-top: 1rem;
|
||||
padding: 0.3rem 0.6rem;
|
||||
background-color: #eee;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
color: #333;
|
||||
}
|
||||
|
|
@ -1,29 +1,44 @@
|
|||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import './GithubRepos.css';
|
||||
|
||||
function GitHubRepos() {
|
||||
const [repos, setRepos] = useState([]);
|
||||
const GithubRepos = () => {
|
||||
const [repos, setRepos] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/github-repos')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
setRepos(data);
|
||||
});
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const fetchRepos = async () => {
|
||||
try {
|
||||
const response = await fetch('https://api.github.com/users/EndofTimee/repos');
|
||||
const data = await response.json();
|
||||
setRepos(data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching GitHub repos:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div id="github-repos">
|
||||
<h2>My GitHub Repositories</h2>
|
||||
<ul>
|
||||
{repos.map(repo => (
|
||||
<li key={repo.id}>
|
||||
<h3>{repo.name}</h3>
|
||||
<p>{repo.description}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
fetchRepos();
|
||||
}, []);
|
||||
|
||||
export default GitHubRepos;
|
||||
return (
|
||||
<div className="github-repos-container">
|
||||
<h1>My GitHub Repositories</h1>
|
||||
<div className="repos-grid">
|
||||
{repos.map((repo) => (
|
||||
<div key={repo.id} className="repo-card">
|
||||
<a href={repo.html_url} target="_blank" rel="noopener noreferrer" className="repo-name">
|
||||
{repo.name}
|
||||
</a>
|
||||
<p className="repo-description">
|
||||
{repo.description || 'No description provided.'}
|
||||
</p>
|
||||
{repo.language && (
|
||||
<span className="repo-language">{repo.language}</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GithubRepos;
|
||||
|
|
|
|||
14
src/components/LoadingScreen.js
Normal file
14
src/components/LoadingScreen.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
import React from 'react';
|
||||
import './LoadingAnimation.css';
|
||||
|
||||
const LoadingAnimation = () => {
|
||||
return (
|
||||
<div className="loading-container">
|
||||
<div className="loading-spinner"></div>
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingAnimation;
|
||||
20
src/components/ParallaxEffect.js
Normal file
20
src/components/ParallaxEffect.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
import React from 'react';
|
||||
import './parallax.css';
|
||||
|
||||
const ParallaxEffect = () => {
|
||||
return (
|
||||
<div className="parallax-container">
|
||||
<div className="parallax-layer layer-1"></div>
|
||||
<div className="parallax-layer layer-2"></div>
|
||||
<div className="content-layer">
|
||||
<div className="parallax-text">
|
||||
<h1>Welcome to the Parallax Effect</h1>
|
||||
<p>Scroll down to see the magic happen!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParallaxEffect;
|
||||
24
src/components/loading-screen.css
Normal file
24
src/components/loading-screen.css
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid rgba(255, 255, 255, 0.3);
|
||||
border-top: 5px solid white;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
52
src/parallax-effect.css
Normal file
52
src/parallax-effect.css
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.parallax-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
perspective: 2px; /* Creates the parallax effect */
|
||||
}
|
||||
|
||||
.parallax-layer {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
transform: translateZ(0);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.layer-1 {
|
||||
background-image: url('https://source.unsplash.com/1600x900/?nature');
|
||||
transform: translateZ(-1px) scale(1.5);
|
||||
}
|
||||
|
||||
.layer-2 {
|
||||
background-image: url('https://source.unsplash.com/1600x900/?forest');
|
||||
transform: translateZ(-0.5px) scale(1.2);
|
||||
}
|
||||
|
||||
.content-layer {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 2rem;
|
||||
padding-top: 40vh;
|
||||
}
|
||||
|
||||
.parallax-text {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
padding: 1rem 2rem;
|
||||
display: inline-block;
|
||||
border-radius: 8px;
|
||||
}
|
||||
88
src/rolling-effects.css
Normal file
88
src/rolling-effects.css
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
@keyframes rollingCode {
|
||||
0% { transform: translateY(0); opacity: 1; }
|
||||
100% { transform: translateY(100vh); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes particleMove {
|
||||
0% { transform: translateY(0) translateX(0); opacity: 0.5; }
|
||||
50% { opacity: 1; }
|
||||
100% { transform: translateY(100vh) translateX(10px); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes lightAnimation {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 100%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: black;
|
||||
font-family: monospace;
|
||||
font-size: 1rem;
|
||||
color: limegreen;
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.animated-lighting {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: radial-gradient(circle, rgba(0, 255, 0, 0.3), transparent 60%);
|
||||
background-size: 300% 300%;
|
||||
animation: lightAnimation 12s infinite linear;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.code-line {
|
||||
position: absolute;
|
||||
top: -10%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
animation: rollingCode 10s linear infinite;
|
||||
opacity: 0.7;
|
||||
z-index: 1;
|
||||
color: #00ff00;
|
||||
}
|
||||
|
||||
.particle {
|
||||
position: absolute;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 50%;
|
||||
animation: particleMove 8s linear infinite;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.particle:nth-child(odd) {
|
||||
animation-duration: 12s;
|
||||
}
|
||||
|
||||
.particle:nth-child(even) {
|
||||
animation-duration: 10s;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
color: white;
|
||||
text-align: center;
|
||||
margin-top: 20vh;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue