docs: add dual-domain implementation plan
This commit is contained in:
113
docs/superpowers/plans/2026-05-16-dual-domain.md
Normal file
113
docs/superpowers/plans/2026-05-16-dual-domain.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Dual-Domain Support Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Serve the portfolio on both `gabrielkaszewski.dev` and `gabrielkaszewski.pl` with identical content, with `.dev` as the canonical domain for SEO.
|
||||
|
||||
**Architecture:** Two independent changes — Traefik routing config to accept both hostnames, and Next.js metadata to inject a canonical `<link>` tag. No app logic changes.
|
||||
|
||||
**Tech Stack:** Next.js 15 (App Router metadata API), Traefik (Docker labels), Let's Encrypt TLS.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add `.pl` domain to Traefik router
|
||||
|
||||
**Files:**
|
||||
- Modify: `compose.yml`
|
||||
|
||||
- [ ] **Step 1: Update the Traefik router rule**
|
||||
|
||||
In `compose.yml`, change the router rule label from:
|
||||
|
||||
```yaml
|
||||
- "traefik.http.routers.gabrielkaszewski.rule=Host(`gabrielkaszewski.dev`)"
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```yaml
|
||||
- "traefik.http.routers.gabrielkaszewski.rule=Host(`gabrielkaszewski.dev`) || Host(`gabrielkaszewski.pl`)"
|
||||
```
|
||||
|
||||
The full `labels` block should look like:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.gabrielkaszewski.rule=Host(`gabrielkaszewski.dev`) || Host(`gabrielkaszewski.pl`)"
|
||||
- "traefik.http.routers.gabrielkaszewski.entrypoints=websecure"
|
||||
- "traefik.http.routers.gabrielkaszewski.tls.certresolver=letsencrypt"
|
||||
```
|
||||
|
||||
Note: Traefik's Let's Encrypt cert resolver automatically provisions a cert for every hostname matched by the router rule, so no additional TLS labels are needed.
|
||||
|
||||
- [ ] **Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add compose.yml
|
||||
git commit -m "feat: add gabrielkaszewski.pl to traefik router"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Add canonical URL to Next.js metadata
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/layout.tsx`
|
||||
|
||||
- [ ] **Step 1: Add `alternates.canonical` to the metadata export**
|
||||
|
||||
In `app/layout.tsx`, add `alternates` to the existing `metadata` object:
|
||||
|
||||
```ts
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: "Gabriel Kaszewski | Software Engineer",
|
||||
template: "%s | Gabriel Kaszewski",
|
||||
},
|
||||
description:
|
||||
"The portfolio of Gabriel Kaszewski, a software engineer specializing in Rust, Python, and modern web technologies.",
|
||||
keywords: [
|
||||
"Gabriel Kaszewski",
|
||||
"Software Engineer",
|
||||
"Rust Developer",
|
||||
"Python Developer",
|
||||
"Next.js",
|
||||
"Portfolio",
|
||||
],
|
||||
alternates: {
|
||||
canonical: "https://gabrielkaszewski.dev",
|
||||
},
|
||||
openGraph: {
|
||||
// ... rest unchanged
|
||||
},
|
||||
// ... rest unchanged
|
||||
};
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify the canonical tag is present in the built HTML**
|
||||
|
||||
Run the dev server:
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
Then in another terminal, check the rendered HTML for the canonical tag:
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:3000 | grep -i canonical
|
||||
```
|
||||
|
||||
Expected output:
|
||||
|
||||
```
|
||||
<link rel="canonical" href="https://gabrielkaszewski.dev"/>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add app/layout.tsx
|
||||
git commit -m "feat: add canonical URL to metadata"
|
||||
```
|
||||
Reference in New Issue
Block a user