# 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:
```bash
git clone https://git.biohazardvfx.com/Nicholai/nicholais-website.git
```

2. Install dependencies:
```bash
npm install
```

### Development

Run the development server:

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

Open [http://localhost:3000](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

```bash
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:
```bash
docker build -t nicholais-website .
```

2. Run the container:
```bash
docker run -p 3000:3000 nicholais-website
```

3. Or with environment variables:
```bash
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):
```md
---
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:  
    ```bash
    curl -sS "https://your-domain/api/revalidate?secret=REVALIDATE_SECRET"
    ```
  - Revalidate a specific post (by slug):  
    ```bash
    curl -sS "https://your-domain/api/revalidate?secret=REVALIDATE_SECRET&slug=my-post-slug"
    ```
  - Optionally revalidate additional paths:  
    ```bash
    curl -sS "https://your-domain/api/revalidate?secret=REVALIDATE_SECRET&path=/blog/my-post-slug"
    ```

- POST (advanced, supports multiple slugs/paths):
  ```bash
  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
- Website: [nicholai.work](https://nicholai.work)
- Email: nicholai@biohazardvfx.com
- Instagram: [@nicholai.exe](https://www.instagram.com/nicholai.exe/)
