Skip to content

Latest commit

 

History

History
158 lines (117 loc) · 6.24 KB

File metadata and controls

158 lines (117 loc) · 6.24 KB

A TypeScript-first Next.js 15 App Router project for showcasing developer utilities. The homepage renders a searchable grid of tools sourced from a single configuration file so new tools can be added with minimal effort.

Features

  • ⚡️ Next.js 15 App Router with React Server Components
  • 🎨 Tailwind CSS, shadcn/ui primitives, and lucide-react icons
  • 🔍 Fuse.js fuzzy search across tool titles and tags
  • 🧱 Modular tool registry via lib/tools.config.ts
  • 🔗 Short URL Expander follows redirects and surfaces the final destination for shortened links
  • #️⃣ QR Code Generator renders live previews and lets you download crisp PNGs
  • 🧮 Base64 Tool converts strings to and from Base64 with instant copy-friendly results
  • 🛡️ JWT Generator crafts HS256 tokens and decodes existing tokens with workflow-aware helpers
  • 🧰 Example tools: JSON Prettifier, UUID Generator, JSON Viewer, Short URL Expander, QR Code Generator, JWT Generator, and Base64 Tool
  • 🗺️ SEO-ready with autogenerated robots.txt and dynamic sitemap.xml
  • 🖼️ Rich thumbnails with matching lucide-react icons
Screenshot 2025-10-08 at 8 13 02 PM

Getting started

pnpm install
pnpm dev

Available scripts

  • pnpm dev – start the development server
  • pnpm build – create an optimized production build
  • pnpm start – run the production server
  • pnpm lint – lint with ESLint and Next.js rules
  • pnpm format – format all supported files with Prettier
  • pnpm test – run regression tests (Vitest)
  • pnpm test:watch – run tests in watch mode

Security

This project includes nonce-based CSP and response hardening headers via Next.js middleware (middleware.ts).

  • Per-request nonce generation and forwarding to app routes via x-nonce request header.
  • Content-Security-Policy enforced with strict script-src 'self' 'nonce-...' in production.
  • Additional hardening headers:
    • X-Frame-Options: DENY
    • X-Content-Type-Options: nosniff
    • Referrer-Policy: strict-origin-when-cross-origin
    • Permissions-Policy: camera=(), microphone=(), geolocation=()
  • Middleware matcher applies to app/API routes while excluding static/framework assets.

Development note (CSP)

For local development only, CSP includes limited allowances required by Next.js dev runtime:

  • script-src includes 'unsafe-eval'
  • connect-src allows ws:/wss: for HMR

Production remains strict and does not include 'unsafe-eval'.

Testing

Regression tests are documented in testcase.md, with important cases for each tool:

  • JSON Tools
  • Compare Tools
  • Base64 Tool
  • JWT Generator
  • QR Code Generator
  • URL Expander
  • UUID Generator

Recommended post-change verification:

pnpm test
pnpm build

Project layout

app/
  layout.tsx               # Global metadata + layout
  page.tsx                 # Homepage grid & search
  tools/[slug]/page.tsx    # Dynamic tool route using the registry
components/
  ToolCard.tsx             # Card representation for each tool tile
  ToolGallery.tsx          # Client component for Fuse.js search
  ToolShell.tsx            # Shell used on individual tool pages
  tools/                   # Lazy-loaded tool implementations
  ui/                      # shadcn/ui primitives (button, input, etc.)
lib/
  tools.config.ts          # Single source of truth for available tools
  search.ts                # Fuse.js wrapper with memoised index
styles/
  globals.css              # Tailwind base + design tokens
public/
  thumbs/                  # Tool thumbnails

Adding a new tool

  1. Register metadata in lib/tools.config.ts:
    • Add an object with slug, title, description, tags, thumbnail, icon, and component.
    • icon should be a string key that maps to one of the icons in components/ToolCard.tsx's iconLibrary. Add new entries there if you need additional icons.
    • Point component to a lazy import, e.g. () => import("@/components/tools/MyTool").
  2. Create the component under components/tools/ and export a default React component.
    • Use shadcn/ui primitives when possible for visual consistency.
    • For client-side interactivity, start the file with "use client";.
  3. Add a thumbnail image to public/thumbs/ and reference it in the config.
  4. Optionally add tests or stories that live close to the new component.

Once saved, the tool automatically appears on the homepage, participates in search, and can be accessed at /tools/{slug}.

Deployment

The project is Vercel-ready with defaults provided by Next.js. Configure your deployment to use the pnpm build and pnpm start commands or deploy directly from GitHub with Vercel.

Set NEXT_PUBLIC_SITE_URL in your hosting environment so metadata, robots.txt, and sitemap.xml use the correct canonical domain.

Docker on Raspberry Pi

The repository ships with a multi-stage Dockerfile optimised for ARM64 (Raspberry Pi 4/5).

Build and run locally (replace the URL with your public hostname):

docker buildx build \
  --platform linux/arm64/v8 \
  --tag dev-tools:latest \
  --build-arg NEXT_PUBLIC_SITE_URL="https://dev-tools.local" \
  .

docker run -d \
  --name dev-tools \
  -p 3000:3000 \
  -e NEXT_PUBLIC_SITE_URL="https://dev-tools.local" \
  dev-tools:latest

Jenkins pipeline (Docker deploy)

A declarative Jenkinsfile is included for CI builds. The pipeline:

  1. Creates/uses a Docker Buildx builder (required for ARM64 cross-builds).
  2. Builds the container image for linux/arm64/v8, injecting NEXT_PUBLIC_SITE_URL.
  3. Optionally pushes the image to a registry when the PUSH_IMAGE parameter is enabled.

Before running the job:

  • Ensure the Jenkins agent has Docker Buildx/QEMU support for arm64.
  • Configure a credential with ID registry-creds for your container registry (username/password or token).
  • Set the IMAGE_NAME parameter to your registry path (e.g., ghcr.io/your-org/dev-tools).
  • Provide the public SITE_URL so generated metadata and the sitemap use the canonical domain.

Once pushed, you can pull the image on the Raspberry Pi host and run it via docker run or docker compose.