2025-10-08 18:10:07 -06:00
2025-10-08 18:10:07 -06:00
2025-10-08 18:10:07 -06:00
2025-10-08 18:10:07 -06:00
2025-10-08 18:10:07 -06:00
2025-10-08 18:10:07 -06:00
2025-09-10 03:05:09 -06:00
2025-10-08 18:10:07 -06:00
2025-10-08 18:10:07 -06:00
2025-10-08 18:10:07 -06:00
2025-10-07 23:54:09 -06:00

Nicholai's website

This is my personal website built with Next.js 15, Tailwind CSS, and TypeScript.

Tech Stack

Next.js 15, Tailwind CSS, Cabin font family, Turbopack, Typescript.

Prerequisites

  • Node.js 18+ installed (node modules: densest thing in the known universe.)
  • npm, yarn, pnpm, whatever honestly.

Installation

  1. Clone the repository:
git clone https://git.biohazardvfx.com/Nicholai/nicholais-website.git
  1. Install dependencies:
npm install

Development

Run the development server:

npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev

Open http://localhost:3000 with your browser to see the result.

Pushing to Git and Docker

  1. Commit changed files to main branch on gitea git add . && git commit -m "added higher res profile image" && git push origin main

  2. Build docker image docker build -t git.biohazardvfx.com/nicholai/nicholais-website:latest .

  3. Push docker package to gitea docker push git.biohazardvfx.com/nicholai/nicholais-website:latest

Building for Production

npm run build

Deployment

Docker Deployment

This project includes a Dockerfile for containerized deployment. To build and run the application using Docker:

  1. Build the image:
docker build -t nicholais-website .
  1. Run the container:
docker run -p 3000:3000 nicholais-website
  1. Or with environment variables:
docker run -p 3000:3000 -e NODE_ENV=production nicholais-website

Environment Variables

The application supports the following environment variables:

Variable Description Default
NODE_ENV Node.js environment development
PORT Port to run the server on 3000

Blog Authoring (MDX)

You can write posts in MDX and have them show up on /blog.

  • Local posts: add .mdx files to app/blog/posts/ (these are always included).
  • Optional GitHub-backed posts: if you configure the BLOG_* environment variables, MDX files from your GitHub repo are fetched and merged with local posts. GitHub posts override local ones on duplicate slugs.

Setup:

  1. Copy .env.local.example to .env.local and fill in values.
  2. For GitHub-backed content, set:
    • BLOG_REPO=owner/repo
    • BLOG_PATH=path/to/mdx/folder (relative to repo root)
    • BLOG_BRANCH=main (or your branch)
    • GITHUB_TOKEN= (only required for private repos or higher rate limits)

Frontmatter template (put at the top of each .mdx file):

---
title: "Post Title"
publishedAt: "2025-01-15" # YYYY-MM-DD
summary: "One-liner summary"
tags: ["tag1", "tag2"]
image: "https://your.cdn/path-or-absolute-url.jpg"
---

Images:

  • Prefer absolute URLs (CDN or repo raw URLs) to avoid build-time asset issues.
  • The MDX renderer handles links and images for you (see components/mdx.tsx).

How updates appear on the site:

  • Incremental Static Regeneration (ISR): GitHub responses are cached with next: { revalidate: BLOG_REVALIDATE_SECONDS }. New/edited posts appear automatically after the configured window (default 300s).
  • On-demand revalidation: trigger an immediate refresh via the /api/revalidate endpoint (see below) or configure a GitHub webhook to call it on each push.

Blog Environment Variables

Variable Description Default
BLOG_REPO GitHub owner/repo containing your MDX files. Leave empty to use only local posts.
BLOG_PATH Path in the repo where .mdx files live (relative to repo root). app/blog/posts
BLOG_BRANCH Branch name to read from. main
GITHUB_TOKEN GitHub token. Required for private repos or higher rate limits.
BLOG_REVALIDATE_SECONDS ISR interval (seconds) for GitHub fetch cache. 300
BLOG_CACHE_TAG Cache tag used for on-demand invalidation (revalidateTag). blog-content
REVALIDATE_SECRET Shared secret for the revalidate API and GitHub webhook signature.

On-demand Revalidation

An API route at /api/revalidate invalidates the blog listing and post pages, and also busts the cache tag used for GitHub content.

  • GET (simple manual trigger):

    • Revalidate listing:
      curl -sS "https://your-domain/api/revalidate?secret=REVALIDATE_SECRET"
      
    • Revalidate a specific post (by slug):
      curl -sS "https://your-domain/api/revalidate?secret=REVALIDATE_SECRET&slug=my-post-slug"
      
    • Optionally revalidate additional paths:
      curl -sS "https://your-domain/api/revalidate?secret=REVALIDATE_SECRET&path=/blog/my-post-slug"
      
  • POST (advanced, supports multiple slugs/paths):

    curl -sS -X POST "https://your-domain/api/revalidate" \
      -H "x-revalidate-secret: REVALIDATE_SECRET" \
      -H "content-type: application/json" \
      -d '{
        "slugs": ["my-post-slug"],
        "paths": ["/blog", "/blog/my-post-slug"]
      }'
    
  • GitHub Webhook (recommended):

    1. In your repo settings, add a Webhook:
      • Payload URL: https://your-domain/api/revalidate
      • Content type: application/json
      • Secret: set to the same value as REVALIDATE_SECRET
      • Event: “Just the push event”
    2. On push, the webhook sends changed file paths. The API will:
      • Revalidate the BLOG_CACHE_TAG to refresh GitHub fetches.
      • Revalidate /blog and any changed post slugs under BLOG_PATH.

Security notes:

  • Do not expose REVALIDATE_SECRET publicly. For manual GET usage, keep the URL private.
  • For CI/CD, use the POST form with the x-revalidate-secret header.

License

This project is open source, take it. I don't give a fuck. I am not your dad.

Author

Nicholai - VFX Supervisor & Developer

Description
why do i need a description. THE NAME SAYS WHAT IT IS. FUCK YOU.
Readme 4.2 MiB
Languages
TypeScript 72.5%
CSS 14.3%
Dockerfile 9.1%
JavaScript 4.1%