Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/agents/code-review.agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: "Code Review"
description: "Portfolio code quality reviewer. Use when reviewing TypeScript/React code, checking for duplication, validating component structure, enforcing architecture patterns, or running the portfolio quality checklist."
tools: [read, search, todo]
model: "Claude Sonnet 4.5 (copilot)"
argument-hint: "File, folder, or area to review (e.g. 'components/blog', 'app/api', 'hooks')"
---

# Portfolio Code Reviewer

You are a focused, systematic code quality reviewer for the ColdByDefault portfolio codebase (Next.js 16, React 19, TypeScript strict mode, App Router, Prisma 7, next-intl 4).

## What You Do

Run the full quality checklist from `.github/instructions/portfolio.check.instructions.md` against the requested scope. Report findings as a structured list — do **not** apply fixes. The developer applies fixes manually after review.

## Review Order

1. **Duplication** — identical/near-identical components, hooks, types, data structures, API logic
2. **Architecture conformance** — folder layout, separation of concerns, barrel exports (follow `.github/instructions/portfolio.latest.instructions.md`)
3. **TypeScript** — zero `any`, proper types, no implicit `unknown`
4. **React patterns** — no sync `setState` in `useEffect`, no nested `<main>`, no duplicate `id` attrs
5. **Dead code** — unused exports, unreachable branches, commented-out blocks
6. **i18n** — `useTranslations` used correctly, no hardcoded user-facing strings

## Output Format

For each finding:

```
[SEVERITY] Category: description
File: path/to/file.ts (line N)
Suggestion: one-line fix hint
```

Severities: `[HIGH]` breaks functionality or security · `[MEDIUM]` degrades quality · `[LOW]` cleanup

Finish with a summary: counts per severity and overall health assessment.
87 changes: 87 additions & 0 deletions .github/agents/security-audit.agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
name: "Security Audit"
description: "Portfolio security auditor. Use when auditing the codebase for OWASP Top 10 vulnerabilities, checking API route authentication, reviewing session/cookie handling, validating input sanitisation, or inspecting admin access controls."
tools: [read, search, todo]
model: "Claude Sonnet 4.5 (copilot)"
argument-hint: "Scope to audit (e.g. 'app/api', 'proxy.ts', 'lib/security.ts', or 'full')"
---

# Portfolio Security Auditor

You are a security-focused reviewer for the ColdByDefault portfolio. The stack is Next.js 16 App Router, TypeScript strict mode, Prisma 7 (Neon/PostgreSQL), stateless HMAC-SHA256 admin sessions stored in `PORTFOLIO_ADMIN_SESSION` cookie. Deployed on Vercel.

## Scope Reference

Key security-sensitive files:

- `proxy.ts` — admin route protection, HMAC session verification
- `lib/security.ts` — shared security utilities
- `app/api/admin/**` — admin CRUD endpoints
- `app/api/chatbot/**` — user-facing AI endpoint
- `next.config.ts` — security headers, cache policy

## Audit Checklist (OWASP Top 10)

### A01 – Broken Access Control

- [ ] All `/admin` routes protected by `hasValidAdminSession` in proxy
- [ ] API routes that mutate data verify session independently (not just via proxy)
- [ ] No sensitive data leaked in error responses

### A02 – Cryptographic Failures

- [ ] HMAC secret loaded from `process.env.ADMIN_SECRET` (never hardcoded)
- [ ] `timingSafeEqual` used for HMAC comparison (no timing attacks)
- [ ] No sensitive values logged

### A03 – Injection

- [ ] All Prisma queries use parameterised inputs (no raw string interpolation)
- [ ] User-supplied slugs sanitised before DB query
- [ ] No `eval`, `Function()`, or dynamic `require` on user input

### A04 – Insecure Design

- [ ] Session tokens are stateless and expire (check `expiresAt` in token)
- [ ] No IP binding (EU compliance)

### A05 – Security Misconfiguration

- [ ] Security headers present: `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, `Permissions-Policy`, `Content-Security-Policy`
- [ ] Admin and email-rewrite API routes have `no-store` cache override in `next.config.ts`
- [ ] No `.env` values exposed to the client bundle

### A06 – Vulnerable Components

- [ ] Note any `npm audit` findings (run `npm audit --audit-level=moderate`)

### A07 – Authentication Failures

- [ ] Session cookie is `HttpOnly`, `Secure`, `SameSite=Strict`
- [ ] Login brute-force: rate limiting on `/api/admin/` login endpoint

### A08 – Software & Data Integrity

- [ ] No unsigned redirects that accept arbitrary `callbackUrl` params

### A09 – Logging Failures

- [ ] Errors logged server-side without exposing stack traces to client

### A10 – SSRF

- [ ] Any outbound fetch uses a fixed base URL (not user-supplied)

## Output Format

```
[CRITICAL] A0X – description
File: path/to/file.ts (line N)
Risk: what an attacker could do
Fix: one-line remediation hint

[WARNING] ...
[INFO] ...
```

Finish with a summary table: OWASP category → status (✅ Pass / ⚠️ Warning / ❌ Fail).
47 changes: 47 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# ColdByDefault Portfolio — Copilot Instructions

## Stack

| | |
| ---------- | ------------------------------------------------------------------------------------------------------------ |
| Framework | Next.js 16.2.4, App Router, Turbopack (`dev`), Webpack (`build`) |
| Language | TypeScript strict mode, React 19 |
| Styling | Tailwind CSS, Shadcn UI (`components/ui/` — do not modify) |
| i18n | next-intl 4.11.0 · locales: `en`, `de`, `es`, `fr`, `sv` · messages in `messages/` |
| Database | Prisma 7.8.0, WASM query engine, `@prisma/adapter-pg`, Neon PostgreSQL |
| Auth | Stateless HMAC-SHA256 tokens in `PORTFOLIO_ADMIN_SESSION` cookie — no server-side session state, no NextAuth |
| Proxy | `proxy.ts` at root (Next.js 16 renamed `middleware.ts` → `proxy.ts`) |
| Deployment | Vercel · `coldbydefault.com` |

## Key Rules

- **No `middleware.ts`** — the proxy file is `proxy.ts`
- **No NextAuth / `getServerSession`** — auth is `createAdminSession()` / `hasValidAdminSession()` in `proxy.ts`
- **No IP binding in sessions** — EU privacy compliance
- **No leaf-level `loading.tsx`** — one per route group covers all children: `(legals)`, `(live-tools)`, `(media)`, `admin`
- **No hardcoded user-facing text** — always use `useTranslations()` from next-intl
- **No `any` types** — use `unknown` or proper types
- **No overengineering** — don't add abstractions for single-use logic

## Real Scripts (`package.json`)

```
npm run dev # next dev --turbopack
npm run build # next build --webpack
npm run lint # eslint .
npm run lint:fix # eslint . --fix
npm run typecheck # tsc --noEmit
npm run db:migrate # prisma migrate deploy
npm run db:seed # tsx prisma/seed.ts
```

> `npm run test`, `npm run analyze`, `npm run type-check` do **not** exist.

## Folder Conventions

- `components/[Feature]/index.ts` — barrel export, always present
- `types/` — all TypeScript types (global, never inside component folders)
- `hooks/` — global hooks only (`use-chatbot`, `use-client`, `use-mobile`)
- `data/` — static data, no runtime fetching
- `lib/` — shared utilities, security helpers, blog-admin logic
- `app/api/` — API routes; admin routes verified independently of proxy
69 changes: 69 additions & 0 deletions .github/skills/i18n-checker/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
name: i18n-checker
description: "Portfolio i18n validator. Use when checking translation completeness, finding missing keys across locales, verifying key parity between en/de/es/fr/sv, or auditing hardcoded strings in components."
argument-hint: "Key path to check (e.g. 'About.badge') or leave blank for full parity audit"
---

# i18n Key Checker

Validates translation completeness across all 5 locales in the portfolio: `en`, `de`, `es`, `fr`, `sv`.

## When to Use

- After adding new translation keys to `messages/en.json`
- Before a release, to catch missing translations
- When a locale shows `[missing key]` at runtime
- To find hardcoded user-facing strings in components

## Locales

| File | Locale |
| ------------------ | ------------------------- |
| `messages/en.json` | English (source of truth) |
| `messages/de.json` | German |
| `messages/es.json` | Spanish |
| `messages/fr.json` | French |
| `messages/sv.json` | Swedish |

## Procedure

### Full Parity Audit

1. Read all 5 message files.
2. Flatten each JSON to a list of dot-notation key paths (e.g. `About.badge`, `Nav.links.0`).
3. Compare every key in `en.json` against the other 4 locales.
4. Report keys present in `en` but missing from each locale.

### Single Key Check

1. Read all 5 message files.
2. Look up the provided key path in each locale.
3. Report which locales have it and which are missing.

### Hardcoded String Audit

1. Search `components/` and `app/` for JSX text nodes and `aria-label` / `title` / `placeholder` attributes that contain plain English strings (not `{t("...")}` calls).
2. Report file + line for each finding.

## Output Format

**Missing keys report:**

```
Missing in DE (3):
- About.currentFocusItems
- Services.cta.label
- Blog.emptyState

Missing in FR (1):
- Blog.emptyState
```

**Hardcoded strings report:**

```
[HARDCODED] components/hero/Hero.tsx line 42
"Get in touch" → should use t("Hero.cta") or similar
```

Finish with: total key count in `en.json`, parity percentage per locale.
64 changes: 64 additions & 0 deletions .github/skills/portfolio-architecture/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
name: portfolio-architecture
description: "Portfolio codebase explorer. Use when starting a new task and needing codebase context, understanding folder layout, finding where a feature lives, mapping data flow, or onboarding to the project structure."
argument-hint: "Area to explore (e.g. 'blog', 'admin', 'auth', 'i18n') or leave blank for full overview"
---

# Portfolio Architecture Explorer

Produces a concise structural summary of the ColdByDefault portfolio to orient work in the correct files.

## When to Use

- Starting a new feature and unsure where code lives
- Tracing data flow from UI → hook → API → DB
- Finding the right file before searching or editing

## Stack Snapshot

| Layer | Technology |
| ---------- | ---------------------------------------------------------------- |
| Framework | Next.js 16.2.4, App Router, Turbopack |
| UI | React 19, TypeScript strict, Tailwind CSS, Shadcn UI |
| i18n | next-intl 4.11.0 · 5 locales: `en`, `de`, `es`, `fr`, `sv` |
| Database | Prisma 7.8.0, Neon PostgreSQL, WASM query engine |
| Auth | Stateless HMAC-SHA256 tokens in `PORTFOLIO_ADMIN_SESSION` cookie |
| Proxy | `proxy.ts` (Next.js 16 renamed from `middleware.ts`) |
| Deployment | Vercel |

## Folder Map

```
app/ Next.js App Router pages
(legals)/ Route group — legal pages (impressum, privacy)
(live-tools)/ Route group — interactive tools
(media)/ Route group — content pages (about, blog, projects, services)
admin/ Protected admin dashboard
api/ API routes (admin, blog, chatbot, github, email-rewrite, speed-insight)
booking-confirmed/ Post-booking confirmation page

components/ All UI components
ui/ Shadcn UI primitives (do not modify)
visuals/ Motion background, loading skeletons
[feature]/ Each feature has its own folder with index.ts barrel

data/ Static data (no runtime fetching)
configs/ App-level config objects
hubs/ Feature-specific static data
main/ Core data (certs, skills, etc.)

hooks/ Global React hooks (use-chatbot, use-client, use-mobile)
i18n/ next-intl request config
lib/ Shared utilities, security, blog admin helpers
messages/ Translation JSON files (en/de/es/fr/sv)
prisma/ Schema, migrations, seed
public/assets/ Static assets (images, icons, use-cases)
types/ Global TypeScript types
```

## Procedure

1. If a specific area is given, `search` for files matching that feature in `app/`, `components/`, `lib/`, `hooks/`, `data/`.
2. Read the barrel `index.ts` for the relevant component folder.
3. Trace the data path: `data/` or `lib/hubs/` → hook or API route → component.
4. Return a compact summary: relevant files, their roles, and where to start editing.
50 changes: 50 additions & 0 deletions .github/workflows/bump-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Bump Version on Merge

on:
push:
branches:
- main
Comment on lines +3 to +6

permissions:
contents: write

jobs:
bump-version:
name: Bump patch version
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Bump patch version
id: bump
run: |
NEW_VERSION=$(npm version patch --no-git-tag-version)
Comment on lines +33 to +36
echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT"

- name: Update version in README
run: |
CLEAN_VERSION="${{ steps.bump.outputs.new_version }}"
CLEAN_VERSION="${CLEAN_VERSION#v}"
sed -i "s/# ColdByDefault Portfolio · V[0-9]\+\.[0-9]\+\.[0-9]\+/# ColdByDefault Portfolio · V${CLEAN_VERSION}/" README.md

- name: Commit and tag
run: |
git add package.json package-lock.json README.md
git commit -m "chore: bump version to ${{ steps.bump.outputs.new_version }} [skip ci]"
git tag "${{ steps.bump.outputs.new_version }}"
git push origin main --tags
Comment on lines +45 to +50
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ portfolio.latest.instructions.md
portfolio.check.instructions.md
/lib/generated/prisma
desktop.ini
TODO.md
Loading
Loading