Big redo. should work. i hope

This commit is contained in:
Unknown 2025-01-31 12:31:00 -07:00
parent 3fb3e9e4c6
commit 9c66c93ef3
22 changed files with 881 additions and 400 deletions

3
.gitignore vendored
View file

@ -22,3 +22,6 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.dev.vars
.prod.env
.prod.vars

175
README.md
View file

@ -1,70 +1,169 @@
# Getting Started with Create React App
# Personal Website with Cloudflare Integration
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## 🏗️ Architecture Overview
## Available Scripts
This project implements a modern web application architecture leveraging Cloudflare's edge computing capabilities. The architecture consists of three primary components:
In the project directory, you can run:
1. **React Frontend**: A Single Page Application (SPA) built with Create React App
2. **Cloudflare Workers**: Serverless functions handling API integrations
3. **Cloudflare Pages**: Static site hosting with global CDN distribution
### `npm start`
## 🚀 Getting Started
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
### Prerequisites
The page will reload when you make changes.\
You may also see any lint errors in the console.
- Node.js (v16.0.0 or higher)
- npm (v7.0.0 or higher)
- Cloudflare account
- Spotify Developer account
- Git
### `npm test`
### Environment Setup
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
1. Clone the repository:
```bash
git clone https://github.com/EndofTimee/My-website
cd personal-website
```
### `npm run build`
2. Create a `.env` file in the root directory:
```env
SPOTIFY_CLIENT_ID=your_spotify_client_id
SPOTIFY_CLIENT_SECRET=your_spotify_client_secret
SPOTIFY_REDIRECT_URI=your_redirect_uri
```
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
3. Install dependencies:
```bash
npm install
```
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
## 💻 Local Development
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### Starting the Development Server
### `npm run eject`
```bash
# Start React development server
npm start
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
# In a separate terminal, start the Cloudflare Worker
npx wrangler dev spotify-worker.js
```
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
The application will be available at:
- Frontend: http://localhost:3000
- Worker: http://localhost:8787
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
### Component Structure
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
The project follows a modular component structure:
## Learn More
```
src/
├── components/
│ ├── SpotifyList/ # Spotify integration
│ ├── GithubRepos/ # GitHub repository display
│ ├── LoadingAnimation/ # Loading states
│ └── ParallaxEffect/ # Visual effects
├── App.js # Main application component
└── index.js # Application entry point
```
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
## 🌐 Deployment
To learn React, check out the [React documentation](https://reactjs.org/).
### Automated Deployment
### Code Splitting
The project includes a PowerShell deployment script that handles both frontend and worker deployment:
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
```bash
npm run deploy
```
### Analyzing the Bundle Size
This script:
1. Loads environment variables
2. Installs dependencies
3. Builds the React application
4. Deploys to Cloudflare Pages
5. Deploys the Spotify Worker
6. Sets up environment secrets
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Manual Deployment Steps
### Making a Progressive Web App
If you need to deploy components individually:
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
1. Frontend Deployment:
```bash
npm run build
npx wrangler pages deploy ./build
```
### Advanced Configuration
2. Worker Deployment:
```bash
npx wrangler deploy spotify-worker.js
```
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Environment Configuration
### Deployment
#### Cloudflare Pages Configuration:
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
1. Build settings:
- Build command: `npm run build`
- Build output directory: `build`
- Node.js version: 16 (or higher)
### `npm run build` fails to minify
2. Environment variables:
- Production branch: `main`
- Preview branches: `dev/*`
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
#### Worker Configuration:
Required environment secrets:
- `SPOTIFY_CLIENT_ID`
- `SPOTIFY_CLIENT_SECRET`
- `SPOTIFY_REDIRECT_URI`
## 🐛 Troubleshooting
### Common Issues
1. Worker Deployment Failures:
```bash
# Verify wrangler.toml configuration
npx wrangler config
# Check worker status
npx wrangler tail
```
2. Build Issues:
```bash
# Clear dependency cache
rm -rf node_modules
npm clean-cache --force
npm install
```
3. Environment Variables:
```bash
# Verify environment variables
npx wrangler secret list
```
## 📚 Additional Resources
- [Cloudflare Workers Documentation](https://developers.cloudflare.com/workers/)
- [Cloudflare Pages Documentation](https://developers.cloudflare.com/pages/)
- [Spotify Web API Documentation](https://developer.spotify.com/documentation/web-api/)
- [React Documentation](https://reactjs.org/docs/getting-started.html)
## 🤝 Contributing
1. Fork the repository
2. Create a feature branch
3. Commit changes
4. Push to the branch
5. Open a Pull Request
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

View file

@ -3,9 +3,9 @@ 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';
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = proccess.env.CLIENT_SECRET;
const REDIRECT_URI = proces.env.REDIRECT_URI
let accessToken = null;
// Function to refresh Spotify Access Token

432
deploy-master.ps1 Normal file
View file

@ -0,0 +1,432 @@
# deploy-master.ps1
#Requires -Version 5.1
Set-StrictMode -Version Latest
# Configure warning and error preferences
$WarningPreference = 'Continue'
$ErrorActionPreference = 'Continue'
# Configure output encoding
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::InputEncoding = [System.Text.Encoding]::UTF8
# Script Configuration
$CONFIG = @{
ProjectName = "personal-site"
RequiredFiles = @{
Root = @(
"package.json",
"wrangler.toml",
"spotify-worker.js",
".env",
".dev.vars",
".prod.vars"
)
Src = @(
"App.js",
"App.css",
"index.js",
"App.test.js",
"reportWebVitals.js",
"setupTests.js"
)
Public = @(
"index.html",
"manifest.json",
"robots.txt"
)
}
RequiredDirs = @(
"src",
"public",
"build",
"logs"
)
RequiredEnvVars = @{
".env" = @(
"REACT_APP_SPOTIFY_CLIENT_ID",
"REACT_APP_SPOTIFY_CLIENT_SECRET",
"REACT_APP_SPOTIFY_REDIRECT_URI",
"REACT_APP_WORKER_URL"
)
".prod.vars" = @(
"SPOTIFY_CLIENT_ID",
"SPOTIFY_CLIENT_SECRET",
"SPOTIFY_REDIRECT_URI"
)
}
}
# Helper Functions
function Write-Header {
param([string]$Title)
$border = "=" * 80
Write-Host "`n$border" -ForegroundColor Magenta
Write-Host $Title -ForegroundColor Magenta
Write-Host "$border" -ForegroundColor Magenta
}
function Write-Status {
param(
[string]$Message,
[string]$Type = "Info",
[switch]$NoNewline
)
$colors = @{
Info = "Cyan"
Success = "Green"
Warning = "Yellow"
Error = "Red"
}
$prefix = switch ($Type) {
"Success" { "[+]" }
"Error" { "[-]" }
"Warning" { "[!]" }
"Info" { "[*]" }
}
if ($NoNewline) {
Write-Host "$prefix $Message" -ForegroundColor $colors[$Type] -NoNewline
}
else {
Write-Host "$prefix $Message" -ForegroundColor $colors[$Type]
}
}
function Test-NodeEnvironment {
Write-Header "Checking Node.js Environment"
$errors = @()
try {
$nodeVersion = node --version 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Status "Node.js version: $nodeVersion" "Success"
}
else {
$errors += "Node.js not found"
}
$npmVersion = npm --version 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Status "npm version: $npmVersion" "Success"
}
else {
$errors += "npm not found"
}
}
catch {
Write-Status "Error checking Node.js environment: $_" "Warning"
}
return $errors
}
function Test-Dependencies {
Write-Header "Checking Dependencies"
$errors = @()
try {
# Clean existing installations
if (Test-Path "node_modules") {
Write-Status "Removing existing node_modules..." "Info"
Remove-Item "node_modules" -Recurse -Force -ErrorAction Continue
}
if (Test-Path "package-lock.json") {
Write-Status "Removing package-lock.json..." "Info"
Remove-Item "package-lock.json" -Force -ErrorAction Continue
}
# Fresh install with warning handling
Write-Status "Installing dependencies..." "Info"
$npmOutput = npm install --legacy-peer-deps --no-audit 2>&1
$npmExitCode = $LASTEXITCODE
# Process npm output
$npmOutput | ForEach-Object {
if ($_ -match "ERR!") {
Write-Status $_ "Error"
$errors += $_
}
elseif ($_ -match "WARN") {
Write-Status $_ "Warning"
}
else {
Write-Status $_ "Info"
}
}
if ($npmExitCode -ne 0) {
$errors += "npm install failed with exit code: $npmExitCode"
}
else {
Write-Status "Dependencies installed successfully" "Success"
}
}
catch {
Write-Status "Caught exception during dependency installation: $_" "Warning"
}
return $errors
}
function Test-CloudflareSetup {
Write-Header "Checking Cloudflare Setup"
$errors = @()
try {
$wranglerVersion = npx wrangler --version 2>&1
Write-Status "Wrangler version: $wranglerVersion" "Success"
$whoami = npx wrangler whoami 2>&1
if ($whoami -match "You are logged in") {
Write-Status "Authenticated with Cloudflare" "Success"
}
else {
Write-Status "Not authenticated with Cloudflare. Please log in..." "Warning"
npx wrangler login
if ($LASTEXITCODE -ne 0) {
$errors += "Failed to authenticate with Cloudflare"
}
}
}
catch {
Write-Status "Error checking Cloudflare setup: $_" "Warning"
}
return $errors
}
function Test-ProjectFiles {
Write-Header "Checking Project Files"
$errors = @()
foreach ($category in $CONFIG.RequiredFiles.Keys) {
foreach ($file in $CONFIG.RequiredFiles[$category]) {
$path = switch ($category) {
"Root" { $file }
"Src" { "src/$file" }
"Public" { "public/$file" }
}
if (-not (Test-Path $path)) {
$errors += "Missing required file: $path"
}
else {
Write-Status "Found $path" "Success"
}
}
}
foreach ($dir in $CONFIG.RequiredDirs) {
if (-not (Test-Path $dir)) {
Write-Status "Creating directory: $dir" "Info"
New-Item -Path $dir -ItemType Directory -Force | Out-Null
}
Write-Status "Directory exists: $dir" "Success"
}
return $errors
}
function Test-EnvVars {
Write-Header "Checking Environment Variables"
$errors = @()
foreach ($file in $CONFIG.RequiredEnvVars.Keys) {
if (Test-Path $file) {
$content = Get-Content $file -Raw
foreach ($var in $CONFIG.RequiredEnvVars[$file]) {
if (-not ($content -match $var)) {
$errors += "Missing $var in $file"
}
}
}
else {
$errors += "Missing file: $file"
}
}
return $errors
}
function Build-Project {
Write-Header "Building Project"
$errors = @()
try {
if (Test-Path "build") {
Remove-Item "build" -Recurse -Force -ErrorAction Continue
Write-Status "Cleaned previous build" "Success"
}
Write-Status "Building project..." "Info"
$buildOutput = npm run build 2>&1
# Process build output
$buildOutput | ForEach-Object {
if ($_ -match "ERR!") {
Write-Status $_ "Error"
$errors += $_
}
elseif ($_ -match "WARN") {
Write-Status $_ "Warning"
}
else {
Write-Status $_ "Info"
}
}
if ($LASTEXITCODE -ne 0) {
$errors += "Build failed with exit code: $LASTEXITCODE"
}
else {
Write-Status "Build completed successfully" "Success"
}
}
catch {
Write-Status "Error during build: $_" "Warning"
}
return $errors
}
function Deploy-Project {
Write-Header "Deploying Project"
$errors = @()
try {
# Deploy worker
Write-Status "Deploying Cloudflare Worker..." "Info"
$workerOutput = npx wrangler deploy spotify-worker.js --minify 2>&1
$workerOutput | ForEach-Object {
if ($_ -match "ERR!") {
Write-Status $_ "Error"
$errors += $_
}
elseif ($_ -match "WARN") {
Write-Status $_ "Warning"
}
else {
Write-Status $_ "Info"
}
}
if ($LASTEXITCODE -ne 0) {
$errors += "Worker deployment failed with exit code: $LASTEXITCODE"
}
else {
Write-Status "Worker deployed successfully" "Success"
}
# Deploy pages
Write-Status "Deploying to Cloudflare Pages..." "Info"
$pagesOutput = npx wrangler pages deploy build/ 2>&1
$pagesOutput | ForEach-Object {
if ($_ -match "ERR!") {
Write-Status $_ "Error"
$errors += $_
}
elseif ($_ -match "WARN") {
Write-Status $_ "Warning"
}
else {
Write-Status $_ "Info"
}
}
if ($LASTEXITCODE -ne 0) {
$errors += "Pages deployment failed with exit code: $LASTEXITCODE"
}
else {
Write-Status "Pages deployed successfully" "Success"
}
}
catch {
Write-Status "Error during deployment: $_" "Warning"
}
return $errors
}
# Main execution
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$logFile = "logs/deploy_$timestamp.log"
if (-not (Test-Path "logs")) {
New-Item -ItemType Directory -Path "logs" | Out-Null
}
Start-Transcript -Path $logFile
try {
Write-Header "Starting Deployment Process"
$allErrors = @()
$continueDeployment = $true
$allErrors += Test-NodeEnvironment
$allErrors += Test-Dependencies
$allErrors += Test-CloudflareSetup
$allErrors += Test-ProjectFiles
$allErrors += Test-EnvVars
if ($allErrors.Count -gt 0) {
Write-Header "Validation Warnings/Errors"
foreach ($error in $allErrors) {
Write-Status $error "Warning"
}
Write-Status "Found $($allErrors.Count) issues during validation" "Warning"
$userResponse = Read-Host "Do you want to continue with deployment? (y/n)"
$continueDeployment = $userResponse -eq 'y'
}
if ($continueDeployment) {
$buildErrors = Build-Project
if ($buildErrors) {
Write-Status "Build completed with warnings:" "Warning"
foreach ($error in $buildErrors) {
Write-Status $error "Warning"
}
$userResponse = Read-Host "Do you want to continue with deployment? (y/n)"
$continueDeployment = $userResponse -eq 'y'
}
if ($continueDeployment) {
$deployErrors = Deploy-Project
if ($deployErrors) {
Write-Status "Deployment completed with warnings:" "Warning"
foreach ($error in $deployErrors) {
Write-Status $error "Warning"
}
}
else {
Write-Header "Deployment Successful"
Write-Status "All components deployed successfully!" "Success"
}
}
}
Write-Status "Log file: $logFile" "Info"
}
catch {
Write-Header "Deployment Error"
Write-Status $_.Exception.Message "Error"
Write-Status "Check the log file for details: $logFile" "Info"
Write-Header "Troubleshooting Steps"
Write-Status "1. Check the log file: $logFile" "Info"
Write-Status "2. Verify Node.js and npm installations" "Info"
Write-Status "3. Check Cloudflare authentication: npx wrangler login" "Info"
Write-Status "4. Verify all required files exist" "Info"
Write-Status "5. Check build output" "Info"
exit 1
}
finally {
Stop-Transcript
}

View file

@ -1,17 +0,0 @@
# Step 1: Install Dependencies
Write-Output "Installing dependencies..."
npm install
# Step 2: Build Frontend
Write-Output "Building the React project..."
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-test
# Step 4: Deploy Backend to Cloudflare Workers
Write-Output "Deploying backend worker..."
wrangler deploy spotify-worker.js --name spotify-worker
Write-Output "✅ Deployment complete! Frontend and backend are live."

View file

@ -0,0 +1,83 @@
**********************
Windows PowerShell transcript start
Start time: 20250131121116
Username: ENDOFTIMEE\mason
RunAs User: ENDOFTIMEE\mason
Configuration Name:
Machine: ENDOFTIMEE (Microsoft Windows NT 10.0.26100.0)
Host Application: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -Command Import-Module 'c:\Users\mason\.vscode\extensions\ms-vscode.powershell-2025.0.0\modules\PowerShellEditorServices\PowerShellEditorServices.psd1'; Start-EditorServices -HostName 'Visual Studio Code Host' -HostProfileId 'Microsoft.VSCode' -HostVersion '2025.0.0' -BundledModulesPath 'c:\Users\mason\.vscode\extensions\ms-vscode.powershell-2025.0.0\modules' -EnableConsoleRepl -StartupBanner "PowerShell Extension v2025.0.0
Copyright (c) Microsoft Corporation.
https://aka.ms/vscode-powershell
Type 'help' to get help.
" -LogLevel 'Warning' -LogPath 'c:\Users\mason\AppData\Roaming\Code\logs\20250131T111852\window1\exthost\ms-vscode.powershell' -SessionDetailsPath 'c:\Users\mason\AppData\Roaming\Code\User\globalStorage\ms-vscode.powershell\sessions\PSES-VSCode-31640-527219.json' -FeatureFlags @()
Process ID: 36136
PSVersion: 5.1.26100.2161
PSEdition: Desktop
PSCompatibleVersions: 1.0, 2.0, 3.0, 4.0, 5.0, 5.1.26100.2161
BuildVersion: 10.0.26100.2161
CLRVersion: 4.0.30319.42000
WSManStackVersion: 3.0
PSRemotingProtocolVersion: 2.3
SerializationVersion: 1.1.0.1
**********************
Transcript started, output file is logs/deploy_20250131_121116.log
================================================================================
Starting Deployment Process
================================================================================
================================================================================
Checking Node.js Environment
================================================================================
[+] Node.js version: v22.12.0
[+] npm version: 10.9.0
================================================================================
Checking Dependencies
================================================================================
[*] Installing dependencies...
node.exe : npm warn deprecated @babel/plugin-proposal-numeric-separator@7.18.6: This proposal has been merged to the
ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator
instead.
At C:\Program Files\nodejs\npm.ps1:29 char:3
+ & $NODE_EXE $NPM_CLI_JS $args
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (npm warn deprec...arator instead.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
npm warn deprecated @babel/plugin-proposal-private-methods@7.18.6: This proposal has been merged to the ECMAScript
standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.
npm warn deprecated @babel/plugin-proposal-class-properties@7.18.6: This proposal has been merged to the ECMAScript
standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.
npm warn deprecated @babel/plugin-proposal-nullish-coalescing-operator@7.18.6: This proposal has been merged to the
ECMAScript standard and thus this plugin is no longer maintained. Please use
@babel/plugin-transform-nullish-coalescing-operator instead.
npm warn deprecated @humanwhocodes/config-array@0.13.0: Use @eslint/config-array instead
npm warn deprecated stable@0.1.8: Modern JS already guarantees Array#sort() is a stable sort, so this library is
deprecated. See the compatibility table on MDN:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility
npm warn deprecated @babel/plugin-proposal-private-property-in-object@7.21.11: This proposal has been merged to the
ECMAScript standard and thus this plugin is no longer maintained. Please use
@babel/plugin-transform-private-property-in-object instead.
npm warn deprecated @babel/plugin-proposal-optional-chaining@7.21.0: This proposal has been merged to the ECMAScript
standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.
npm warn deprecated rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
npm warn deprecated rollup-plugin-inject@3.0.2: This package has been deprecated and is no longer maintained. Please
use @rollup/plugin-inject.
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated rollup-plugin-terser@7.0.2: This package has been deprecated and is no longer maintained. Please
use @rollup/plugin-terser
npm warn deprecated abab@2.0.6: Use your platform's native atob() and btoa() methods instead
npm warn deprecated q@1.5.1: You or someone you depend on is using Q, the JavaScript Promise library that gave
JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript
promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.
npm warn deprecated
npm warn deprecated (For a CapTP with native promises, see @endo/eventual-send and @endo/captp)
npm warn deprecated @humanwhocodes/object-schema@2.0.3: Use @eslint/object-schema instead
npm warn deprecated domexception@2.0.1: Use your platform's native DOMException instead
npm warn deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead
npm warn deprecated w3c-hr-time@1.0.2: Use your platform's native performance.now() and performance.timeOrigin.
npm warn deprecated workbox-cacheable-response@6.6.0: workbox-background-sync@6.6.0
npm warn deprecated workbox-google-analytics@6.6.0: It is not compatible with newer versions of GA starting with v4, as
long as you are using GAv3 it should be ok, but the package is not longer being maintained
npm warn deprecated svgo@1.3.2: This SVGO version is no longer supported. Upgrade to v2.x.x.

View file

@ -1,52 +1,33 @@
{
"name": "my-website",
{
"name": "personal-site",
"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",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"@babel/plugin-transform-private-methods": "^7.25.9",
"@babel/plugin-transform-numeric-separator": "^7.25.9",
"@babel/plugin-transform-class-properties": "^7.25.9",
"@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9",
"@babel/plugin-transform-optional-chaining": "^7.25.9",
"@eslint/eslintrc": "^1.4.1",
"@eslint/js": "^8.57.1",
"@jridgewell/sourcemap-codec": "^1.5.0",
"@rollup/plugin-terser": "7.0.2",
"eslint": "^8.57.1",
"rimraf": "^4.4.1",
"glob": "^9.3.5",
"svgo": "^2.8.0"
"web-vitals": "^2.1.4",
"itty-router": "^4.0.27",
"lru-cache": "^10.1.0"
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@cloudflare/workers-types": "^4.20240208.0",
"wrangler": "^3.28.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"eject": "react-scripts eject",
"deploy": "powershell ./deploy-master.ps1"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
"react-app"
]
},
"browserslist": {
@ -60,5 +41,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"overrides": {
"inflight": "npm:lru-cache@^10.1.0"
}
}
}

0
public/pages/system.html Normal file
View file

View file

@ -1,124 +0,0 @@
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}`);
});
app.get('/spotify-data', async (req, res) => {
if (!access_token) {
return res.status(401).json({ error: 'User not authenticated. Please login first.' });
}
try {
const fetch = (await import('node-fetch')).default;
// Fetch user's top tracks
const tracksResponse = await fetch('https://api.spotify.com/v1/me/top/tracks?limit=10', {
headers: { Authorization: `Bearer ${access_token}` }
});
const topTracks = await tracksResponse.json();
// Fetch user's playlists
const playlistsResponse = await fetch('https://api.spotify.com/v1/me/playlists?limit=5', {
headers: { Authorization: `Bearer ${access_token}` }
});
const playlists = await playlistsResponse.json();
res.json({
topTracks: topTracks.items.map(track => ({
name: track.name,
artist: track.artists.map(artist => artist.name).join(', ')
})),
playlists: playlists.items.map(playlist => ({
name: playlist.name,
url: playlist.external_urls.spotify
}))
});
} catch (error) {
console.error('Error fetching Spotify data:', error);
res.status(500).json({ error: 'Failed to fetch Spotify data' });
}
});

View file

@ -1,73 +1,83 @@
import { Router } from 'itty-router';
const router = Router();
const CLIENT_ID = 'YOUR_SPOTIFY_CLIENT_ID';
const CLIENT_SECRET = 'YOUR_SPOTIFY_CLIENT_SECRET';
// CORS headers
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
let accessToken = null;
let tokenExpiry = null;
// Response helper
const jsonResponse = (data, status = 200) => {
return new Response(JSON.stringify(data), {
status,
headers: {
...corsHeaders,
'Content-Type': 'application/json',
},
});
};
// 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'
// Error response helper
const errorResponse = (message, status = 500) => {
return jsonResponse({ error: message }, status);
};
// Spotify credentials
const SPOTIFY_CLIENT_ID = process.env.SPOTIFY_CLIENT_ID;
const SPOTIFY_CLIENT_SECRET = process.env.SPOTIFY_CLIENT_SECRET;
const SPOTIFY_REDIRECT_URI = process.env.SPOTIFY_REDIRECT_URI;
// 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(`${SPOTIFY_CLIENT_ID}:${SPOTIFY_CLIENT_SECRET}`)
},
body: 'grant_type=client_credentials'
});
const data = await authResponse.json();
return data.access_token;
}
// CORS preflight handler
router.options('*', () => new Response(null, { headers: corsHeaders }));
// Health check endpoint
router.get('/health', () => {
return jsonResponse({
status: 'healthy',
timestamp: new Date().toISOString(),
});
});
// Spotify top tracks endpoint
router.get('/top-tracks', async () => {
try {
const accessToken = await refreshAccessToken();
const spotifyResponse = await fetch('https://api.spotify.com/v1/me/top/tracks?limit=10', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
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 });
}
const spotifyData = await spotifyResponse.json();
return jsonResponse(spotifyData);
} catch (error) {
return errorResponse('Failed to fetch Spotify data: ' + error.message);
}
});
// Default route for unmatched paths
router.all('*', () => new Response('Not Found', { status: 404 }));
// 404 handler
router.all('*', () => errorResponse('Not Found', 404));
// Event listener for handling requests
addEventListener('fetch', (event) => {
event.respondWith(router.handle(event.request));
});
// Export handler
export default {
fetch: (request, env, ctx) => router.handle(request, env, ctx)
};

View file

@ -1,33 +1,36 @@
import React, { useEffect } from 'react';
import './rolling-code.css';
import logo from './logo.png';
import './styles/rolling-effects.css'; // Fixed import path
import './App.css';
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);
if (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);
if (particleContainer) {
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="app-container">
<div className="animated-lighting"></div>
<div className="rolling-code-container"></div>
<div className="particle-container"></div>
@ -39,4 +42,4 @@ const App = () => {
);
};
export default App;
export default App;

View file

@ -1,25 +1,80 @@
import React, { useEffect, useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
const WORKER_URL = 'https://spotify-worker.your-worker-subdomain.workers.dev';
function SpotifyList() {
const [tracks, setTracks] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('/spotify-data')
.then(response => response.json())
.then(data => {
setTracks(data.items);
});
const fetchTracks = async () => {
try {
setLoading(true);
const response = await fetch(`${WORKER_URL}/top-tracks`);
if (!response.ok) {
throw new Error('Failed to fetch tracks');
}
const data = await response.json();
setTracks(data.items || []);
} catch (err) {
setError(err.message);
console.error('Error fetching tracks:', err);
} finally {
setLoading(false);
}
};
fetchTracks();
}, []);
if (loading) {
return (
<Card className="w-full max-w-md mx-auto">
<CardContent className="p-6">
<div className="flex justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
</div>
</CardContent>
</Card>
);
}
if (error) {
return (
<Card className="w-full max-w-md mx-auto border-red-200">
<CardContent className="p-6">
<p className="text-red-500">Error: {error}</p>
</CardContent>
</Card>
);
}
return (
<div id="spotify-list">
<h2>My Most Listened to Songs</h2>
<ul>
{tracks.map(track => (
<li key={track.id}>{track.name} by {track.artists.map(artist => artist.name).join(', ')}</li>
))}
</ul>
</div>
<Card className="w-full max-w-md mx-auto">
<CardHeader>
<CardTitle>My Top Tracks</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2">
{tracks.map((track, index) => (
<li
key={track.id}
className="p-2 hover:bg-gray-50 rounded-lg transition-colors"
>
<span className="font-medium">{index + 1}.</span>{' '}
{track.name} by{' '}
<span className="text-gray-600">
{track.artists.map(artist => artist.name).join(', ')}
</span>
</li>
))}
</ul>
</CardContent>
</Card>
);
}

View file

@ -1,6 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import './styles/index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
@ -11,4 +11,4 @@ root.render(
</React.StrictMode>
);
reportWebVitals();
reportWebVitals();

View file

@ -1,88 +0,0 @@
@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;
}

View file

@ -0,0 +1,23 @@
@keyframes rollingCode {
0% { transform: translateY(0); opacity: 1; }
100% { transform: translateY(100vh); opacity: 0; }
}
body {
margin: 0;
overflow: hidden;
background: black;
color: limegreen;
font-family: monospace;
font-size: 1rem;
}
.code-line {
position: absolute;
top: -10%;
width: 100%;
white-space: nowrap;
overflow: hidden;
animation: rollingCode 5s linear infinite;
}

18
wrangler.toml Normal file
View file

@ -0,0 +1,18 @@
name = "personal-site"
main = "spotify-worker.js"
compatibility_date = "2024-01-30"
[build]
command = "npm run build"
watch_dir = "build"
[site]
bucket = "./build"
[env.production]
name = "personal-site"
vars = { ENVIRONMENT = "production" }
[env.development]
name = "personal-site-dev"
vars = { ENVIRONMENT = "development" }