# Implementation Plan: Link Preview (Open Graph Meta-Tags) **Branch**: `012-link-preview` | **Date**: 2026-03-09 | **Spec**: `specs/012-link-preview/spec.md` **Input**: Feature specification from `/specs/012-link-preview/spec.md` ## Summary Inject Open Graph and Twitter Card meta-tags into the server-rendered HTML so that shared event links display rich preview cards in messengers and on social media. The Spring Boot backend replaces its current static SPA fallback with a controller that dynamically injects event-specific or generic meta-tags into the cached `index.html` template before serving it. ## Technical Context **Language/Version**: Java 25 (backend), TypeScript 5.9 (frontend — minimal changes) **Primary Dependencies**: Spring Boot 3.5.x (existing), Vue 3 (existing) — no new dependencies **Storage**: PostgreSQL via JPA (existing event data, read-only access) **Testing**: JUnit 5 + Spring MockMvc (backend), Playwright (E2E) **Target Platform**: Self-hosted Docker container (Linux) **Project Type**: Web application (SPA + REST API) **Performance Goals**: N/A — meta-tag injection adds negligible overhead (<1ms string replacement) **Constraints**: Meta-tags MUST be in initial HTML response (no client-side JS injection). No external services or CDNs. **Scale/Scope**: Affects all HTML page responses. No new database tables or API endpoints. ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* | Principle | Status | Notes | |---|---|---| | I. Privacy by Design | ✅ PASS | No tracking, analytics, or external services. Meta-tags contain only public event info (title, date, location). No PII exposed. `og:image` is a self-hosted static asset. | | II. Test-Driven Methodology | ✅ PLAN | Unit tests for meta-tag generation, integration tests for controller, E2E tests for full HTML verification. TDD enforced. | | III. API-First Development | ✅ N/A | No new API endpoints. This feature modifies HTML serving, not the REST API. Existing OpenAPI spec unchanged. | | IV. Simplicity & Quality | ✅ PASS | Simple string replacement in cached HTML template. No SSR framework, no prerendering service, no user-agent sniffing. Minimal moving parts. | | V. Dependency Discipline | ✅ PASS | Zero new dependencies. Uses only Spring Boot's existing capabilities. | | VI. Accessibility | ✅ N/A | Meta-tags are invisible to users. No UI changes. | **Gate result: PASS** — No violations. No complexity tracking needed. ## Project Structure ### Documentation (this feature) ```text specs/012-link-preview/ ├── plan.md # This file ├── research.md # Phase 0 output — technical decisions ├── data-model.md # Phase 1 output — meta-tag value objects ├── quickstart.md # Phase 1 output — implementation guide ├── contracts/ │ └── html-meta-tags.md # Phase 1 output — meta-tag HTML contract └── tasks.md # Phase 2 output (created by /speckit.tasks) ``` ### Source Code (repository root) ```text backend/ ├── src/main/java/de/fete/ │ ├── adapter/in/web/ │ │ └── SpaController.java # NEW — serves index.html with injected meta-tags │ ├── application/ │ │ └── OpenGraphService.java # NEW — composes meta-tag values from event data │ └── config/ │ └── WebConfig.java # MODIFIED — remove PathResourceResolver SPA fallback ├── src/main/resources/ │ └── application.properties # MODIFIED — add forward-headers-strategy └── src/test/java/de/fete/ ├── adapter/in/web/ │ └── SpaControllerTest.java # NEW — integration tests └── application/ └── OpenGraphServiceTest.java # NEW — unit tests frontend/ ├── index.html # MODIFIED — add placeholder ├── public/ │ └── og-image.png # NEW — brand image for og:image (1200x630) └── e2e/ └── link-preview.spec.ts # NEW — E2E tests ``` **Structure Decision**: Web application structure (existing). Backend changes in adapter/web and application layers. Frontend changes minimal (HTML placeholder + static asset). ## Complexity Tracking > No violations — section intentionally empty.