Replace PathResourceResolver SPA fallback with SpaController that injects OG/Twitter meta-tags into cached index.html template. Event pages get event-specific tags (title, date, location), all other pages get generic fete branding. Includes og-image.png brand asset and forward-headers-strategy for proxy support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
84 lines
2.7 KiB
Markdown
84 lines
2.7 KiB
Markdown
# Data Model: Link Preview (Open Graph Meta-Tags)
|
|
|
|
**Feature**: 012-link-preview | **Date**: 2026-03-09
|
|
|
|
## Overview
|
|
|
|
This feature does NOT introduce new database entities. It reads existing event data and projects it into HTML meta-tags. The "model" here is the meta-tag value object used during HTML generation.
|
|
|
|
## Meta-Tag Value Objects
|
|
|
|
### OpenGraphMeta
|
|
|
|
Represents the set of Open Graph meta-tags to inject into the HTML response.
|
|
|
|
| Field | Type | Source | Rules |
|
|
|---|---|---|---|
|
|
| `title` | String | Event title or "fete" | Max 70 chars, truncated with "..." |
|
|
| `description` | String | Composed from event fields or generic text | Max 200 chars |
|
|
| `url` | String | Canonical URL from request | Absolute URL |
|
|
| `type` | String | Always "website" | Constant |
|
|
| `siteName` | String | Always "fete" | Constant |
|
|
| `image` | String | Static brand image URL | Absolute URL to `/og-image.png` |
|
|
|
|
### TwitterCardMeta
|
|
|
|
| Field | Type | Source | Rules |
|
|
|---|---|---|---|
|
|
| `card` | String | Always "summary" | Constant |
|
|
| `title` | String | Same as OG title | Max 70 chars |
|
|
| `description` | String | Same as OG description | Max 200 chars |
|
|
|
|
## Data Flow
|
|
|
|
```
|
|
Request for /events/{token}
|
|
│
|
|
▼
|
|
LinkPreviewController
|
|
│
|
|
├── Resolve event token → Event domain object (existing EventRepository)
|
|
│
|
|
├── Build OpenGraphMeta from Event fields:
|
|
│ title ← event.title (truncated)
|
|
│ description ← formatDescription(event.dateTime, event.timezone, event.location, event.description)
|
|
│ url ← request base URL + /events/{token}
|
|
│ image ← request base URL + /og-image.png
|
|
│
|
|
├── Build TwitterCardMeta (mirrors OG values)
|
|
│
|
|
├── Inject meta-tags into cached index.html template
|
|
│
|
|
└── Return modified HTML
|
|
|
|
Request for / or /create (non-event pages)
|
|
│
|
|
▼
|
|
LinkPreviewController
|
|
│
|
|
├── Build generic OpenGraphMeta:
|
|
│ title ← "fete"
|
|
│ description ← "Privacy-focused event planning. Create and share events without accounts."
|
|
│ url ← request URL
|
|
│ image ← request base URL + /og-image.png
|
|
│
|
|
├── Build generic TwitterCardMeta
|
|
│
|
|
├── Inject meta-tags into cached index.html template
|
|
│
|
|
└── Return modified HTML
|
|
```
|
|
|
|
## Existing Entities Used (Read-Only)
|
|
|
|
### Event (from `de.fete.domain.model.Event`)
|
|
|
|
| Field | Used For |
|
|
|---|---|
|
|
| `title` | `og:title`, `twitter:title` |
|
|
| `description` | Part of `og:description`, `twitter:description` |
|
|
| `dateTime` | Part of `og:description` (formatted) |
|
|
| `timezone` | Date formatting context |
|
|
| `location` | Part of `og:description` |
|
|
| `eventToken` | URL construction |
|