# 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/)