Skip to content

Commit 4d0f5ee

Browse files
committed
feat: migrate to pnpm, add Turnstile anti-spam, fix performance issues
- Migrate from npm to pnpm for better performance and dependency management - Add Cloudflare Turnstile to contact form for anti-spam protection - Add email icon to social links section - Fix ScrollTrigger "Element not found: body" error - Reduce will-change memory consumption warnings - Fix font preload warnings with proper configuration - Update GitHub Actions workflow to support pnpm - Update Dockerfile to use pnpm and Node 22 - Fix ESLint warnings across multiple components - Update metadata to reflect Rust & Web Developer focus
1 parent c0a694c commit 4d0f5ee

18 files changed

Lines changed: 5084 additions & 4158 deletions

.env example

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
NEXT_PUBLIC_BACKEND_URL=https://github-api-backend.netlify.app/api/github
1+
NEXT_PUBLIC_BACKEND_URL=https://your-backend-url/api/github
22
NEXT_PUBLIC_LOCAL_BACKEND_URL=http://localhost:8000/api/github
3-
GITHUB_TOKEN=
4-
NEXT_PUBLIC_FORMSPREE_FORM_ID=
3+
NEXT_PUBLIC_FORMSPREE_FORM_ID=your-formspree-form-id
4+
NEXT_PUBLIC_TURNSTILE_SITE_KEY=your-turnstile-site-key
5+
NEXT_PUBLIC_EMAIL=your@email.com

.github/workflows/nextjs.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ jobs:
1616
env:
1717
NEXT_PUBLIC_LOCAL_BACKEND_URL: ${{ secrets.BACKEND_URL }}
1818
NEXT_PUBLIC_FORMSPREE_FORM_ID: ${{ secrets.FORMSPREE_FORM_ID }}
19+
NEXT_PUBLIC_TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}
20+
NEXT_PUBLIC_EMAIL: ${{ secrets.EMAIL }}
1921
steps:
2022
- name: Checkout
2123
uses: actions/checkout@v4
@@ -27,6 +29,11 @@ jobs:
2729
echo "command=install" >> $GITHUB_OUTPUT
2830
echo "runner=yarn" >> $GITHUB_OUTPUT
2931
exit 0
32+
elif [ -f "${{ github.workspace }}/pnpm-lock.yaml" ]; then
33+
echo "manager=pnpm" >> $GITHUB_OUTPUT
34+
echo "command=install" >> $GITHUB_OUTPUT
35+
echo "runner=pnpm" >> $GITHUB_OUTPUT
36+
exit 0
3037
elif [ -f "${{ github.workspace }}/package.json" ]; then
3138
echo "manager=npm" >> $GITHUB_OUTPUT
3239
echo "command=ci" >> $GITHUB_OUTPUT
@@ -39,8 +46,13 @@ jobs:
3946
- name: Setup Node
4047
uses: actions/setup-node@v4
4148
with:
42-
node-version: "20"
49+
node-version: "22"
4350
cache: ${{ steps.detect-package-manager.outputs.manager }}
51+
- name: Setup pnpm
52+
uses: pnpm/action-setup@v4
53+
with:
54+
version: latest
55+
if: steps.detect-package-manager.outputs.manager == 'pnpm'
4456
- name: Setup Pages
4557
uses: actions/configure-pages@v5
4658
with:
@@ -55,6 +67,8 @@ jobs:
5567
${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-
5668
- name: Install dependencies
5769
run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
70+
- name: Lint with ESLint
71+
run: ${{ steps.detect-package-manager.outputs.runner }} next lint
5872
- name: Build with Next.js
5973
run: ${{ steps.detect-package-manager.outputs.runner }} next build
6074
- name: Upload artifact

Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Install dependencies only when needed
2+
FROM node:22-alpine AS deps
3+
WORKDIR /app
4+
COPY package.json pnpm-lock.yaml ./
5+
RUN npm install -g pnpm && pnpm install --frozen-lockfile
6+
7+
# Rebuild the source code only when needed
8+
FROM node:22-alpine AS builder
9+
WORKDIR /app
10+
COPY --from=deps /app/node_modules ./node_modules
11+
COPY . .
12+
RUN npm install -g pnpm && pnpm run build
13+
14+
# Production image, copy necessary files and run next
15+
FROM node:22-alpine AS runner
16+
WORKDIR /app
17+
ENV NODE_ENV production
18+
19+
COPY .env .
20+
21+
COPY --from=builder /app/.next ./.next
22+
COPY --from=builder /app/node_modules ./node_modules
23+
COPY --from=builder /app/package.json ./package.json
24+
25+
EXPOSE 3000
26+
27+
CMD ["npm", "install", "-g", "pnpm", "&&", "pnpm", "start"]

README.md

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,22 @@ This is my personal developer portfolio built using **Next.js**, styled with **T
1111
```bash
1212
git clone [https://github.com/amitminer/amitminer.github.io.git](https://github.com/amitminer/amitminer.github.io.git)
1313
cd amitminer.github.io
14-
npm install
15-
````
14+
pnpm install
15+
```
1616

17-
### 2\. Set Up Environment Variables
17+
### 2. Set Up Environment Variables
1818

1919
Create the `.env.local` file:
2020

2121
```bash
2222
cp .env.example .env.local
2323
```
2424

25-
Then edit `.env.local` with your values:
26-
27-
```
28-
NEXT_PUBLIC_BACKEND_URL=[https://your-backend-domain.com/api/github](https://your-backend-domain.com/api/github)
29-
NEXT_PUBLIC_FORMSPREE_FORM_ID=your_formspree_id_here
30-
```
25+
Then edit `.env.local` with your values. See the [Environment Variables](#environment-variables) section below for the complete list of required variables.
3126

3227
-----
3328

34-
### 3\. Backend Setup (Required)
29+
### 3. Backend Setup (Required)
3530

3631
> 📦 GitHub Stats & Projects require a backend API.
3732
@@ -49,20 +44,56 @@ Once deployed, use that backend URL as `NEXT_PUBLIC_BACKEND_URL` in `.env.local`
4944

5045
-----
5146

52-
### 4\. Run Development Server
47+
### 4. Run Development Server
5348

5449
```bash
55-
npm run dev
50+
pnpm dev
5651
```
5752

5853
Visit your local site at:
5954
📍 http://localhost:3000
6055

6156
-----
6257

58+
## Environment Variables
59+
60+
Create a `.env` file in the root directory with the following variables:
61+
62+
```env
63+
# Backend API URLs
64+
NEXT_PUBLIC_BACKEND_URL=https://github-api-backend.netlify.app/api/github
65+
NEXT_PUBLIC_LOCAL_BACKEND_URL=http://localhost:8000/api/github
66+
67+
# GitHub API Token (optional, for enhanced rate limits)
68+
GITHUB_TOKEN=
69+
70+
# Formspree Form ID for contact form
71+
NEXT_PUBLIC_FORMSPREE_FORM_ID=
72+
73+
# Cloudflare Turnstile Site Key for anti-spam protection
74+
NEXT_PUBLIC_TURNSTILE_SITE_KEY=
75+
```
76+
6377
## 📡 API Backend
6478

6579
Backend Repo → [github-api-backend](https://github.com/amitxd75/github-api-backend)
6680
Set it up properly, deploy to your server or Netlify, and configure `NEXT_PUBLIC_BACKEND_URL` in `.env.local`.
6781

6882
This enables real-time GitHub stats, repo data, and caching.
83+
84+
## 🚀 Docker
85+
86+
You can now run this app using Docker for easy deployment and local development.
87+
88+
### Build the Docker image
89+
```sh
90+
docker build -t my-portfolio-app .
91+
```
92+
93+
### Run the Docker container
94+
```sh
95+
docker run -p 3000:3000 --env-file .env my-portfolio-app
96+
```
97+
98+
- The app will be available at [http://localhost:3000](http://localhost:3000)
99+
- Make sure to set up your `.env` file with the required environment variables before running.

app/components/icons/EmailIcon.jsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const EmailIcon = ({ className = "w-4 h-4", ...props }) => (
2+
<svg
3+
role="img"
4+
viewBox="0 0 24 24"
5+
xmlns="http://www.w3.org/2000/svg"
6+
className={className}
7+
fill="currentColor"
8+
{...props}
9+
>
10+
<title>Email</title>
11+
<path d="M1.5 4.5h21a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-21a1 1 0 0 1-1-1v-13a1 1 0 0 1 1-1zm1 2v11h19v-11l-9.5 6.5L2.5 6.5zm1.62-1l8.38 5.73L20.88 5.5H4.12z" />
12+
</svg>
13+
);

app/components/icons/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ export { FaWindows } from 'react-icons/fa';
1515
// export { VscCode } from 'react-icons/vsc';
1616

1717
export { RefreshCw } from 'lucide-react';
18+
19+
export { EmailIcon } from './EmailIcon';

app/components/sections/AnimatedSections.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ export default function AnimatedSections({ children }: { children: React.ReactNo
109109
height: 3px;
110110
background: linear-gradient(90deg, #FF1493, #00FFFF);
111111
z-index: 1000;
112-
will-change: width;
113112
transition: none;
114113
box-shadow: 0 0 10px rgba(255, 20, 147, 0.5);
115114
`
@@ -189,7 +188,7 @@ export default function AnimatedSections({ children }: { children: React.ReactNo
189188
rotation: i % 4 === 0 ? 10 : -10,
190189
ease: "none",
191190
scrollTrigger: {
192-
trigger: "body",
191+
trigger: document.body || containerRef.current,
193192
start: "top top",
194193
end: "bottom bottom",
195194
scrub: 1,
@@ -330,7 +329,6 @@ export default function AnimatedSections({ children }: { children: React.ReactNo
330329

331330
animatedCards.add(card)
332331
card.setAttribute("data-card-animated", "true")
333-
card.style.willChange = "transform"
334332

335333
// Check if it's a project card specifically
336334
const isProjectCard = card.closest("#projects")
@@ -365,9 +363,6 @@ export default function AnimatedSections({ children }: { children: React.ReactNo
365363
end: "bottom 5%",
366364
toggleActions: "play none none reverse",
367365
fastScrollEnd: true,
368-
onEnter: () => {
369-
card.style.willChange = "auto"
370-
},
371366
},
372367
})
373368

@@ -472,7 +467,6 @@ export default function AnimatedSections({ children }: { children: React.ReactNo
472467
height: 100%;
473468
pointer-events: none;
474469
z-index: -1;
475-
will-change: background;
476470
background: linear-gradient(135deg,
477471
rgba(255, 20, 147, 0.02) 0%,
478472
transparent 30%,
@@ -579,12 +573,20 @@ export default function AnimatedSections({ children }: { children: React.ReactNo
579573
return () => {
580574
ctx.revert()
581575
ScrollTrigger.getAll().forEach((trigger) => trigger.kill())
576+
gsap.killTweensOf("*")
582577

583578
const elements = [".scroll-progress", ".morphing-bg", ".reverse-scroll-bg"]
584579
elements.forEach((selector) => {
585580
document.querySelectorAll(selector).forEach((el) => el.remove())
586581
})
587582

583+
// Clean up any remaining will-change styles
584+
document.querySelectorAll('[style*="will-change"]').forEach((el) => {
585+
if (el instanceof HTMLElement) {
586+
el.style.willChange = 'auto'
587+
}
588+
})
589+
588590
setIsInitialized(false)
589591
}
590592
}, 50)

0 commit comments

Comments
 (0)