Erste Vereins-App, die fremde Drittseiten crawlt und aggregiert (Event-Übersicht Konstanz/Kreuzlingen, DE+CH zusammen). Phase-0- Build dependency-arm (manaspur-Pattern): keine @mana/* via Verdaccio, Föderations-Boilerplate inline. - Hono+Bun-API mit health/manifest/search/takedown/dsgvo - Drizzle-Schema (8 Tabellen) gemäß AGGREGATOR_POLICY §6: countries, regions, venues, event_sources, events, crawl_jobs (mit robots_check_passed-Compliance-Beweis), takedown_requests, blocked_domains - Crawl-Policy-Skelett (assertCrawlAllowed) mit Block-List-Check + Pro-Host-Rate-Limit; robots.txt-Lookup pending α-1 - mana-research- und mana-geocoding-Client-Stubs - Search-Endpoint public (kein Auth — Browse-only MVP) - Postgres lokal :5441, db:push grün, /healthz + /readyz + /.well-known/mana-app.json smoke-getestet Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
176 lines
7.7 KiB
Markdown
176 lines
7.7 KiB
Markdown
# CLAUDE.md — Seepuls
|
||
|
||
Guidance für Claude Code in diesem Repo.
|
||
|
||
> **Wenn du gerade neu bist:** lies zuerst [`STATUS.md`](STATUS.md) — dort
|
||
> steht der aktuelle Phasen-Stand. Dieses CLAUDE.md ist nur die
|
||
> Konventions- und Architektur-Referenz.
|
||
|
||
## Was dieses Repo ist
|
||
|
||
**Seepuls** — Event-Aggregator für die Grenzregion Konstanz/Kreuzlingen
|
||
(Bodensee, DE+CH zusammen). Föderierte App des **mana e.V.**-Ökosystems.
|
||
Erste Vereins-App, die **fremde Drittseiten crawlt, aggregiert und
|
||
re-publiziert** — gilt die [`AGGREGATOR_POLICY.md`](../mana/docs/AGGREGATOR_POLICY.md)
|
||
in `mana/docs/`.
|
||
|
||
```
|
||
HTTPS ┌──────────────────┐
|
||
mana e.V. ◄─────────────── │ seepuls/ │ Postgres `seepuls`
|
||
Plattform │ (this repo) │ seepuls.mana.how
|
||
• mana-research (crawl) │ Astro-Web + │
|
||
• mana-geocoding (DE/CH-Geo) │ Hono-API │
|
||
• mana-media (Bilder) └──────────────────┘
|
||
• mana-auth (Phase β) ▲
|
||
│ HTTPS
|
||
│
|
||
┌─────────┴────────┐
|
||
│ Drittseiten │
|
||
│ (Kulturhaus etc.)│
|
||
│ via Firecrawl │
|
||
└──────────────────┘
|
||
```
|
||
|
||
## Status
|
||
|
||
**Phase α-0 — Repo-Skelett (2026-05-15).** Manifest + API-Boilerplate
|
||
stehen, DB-Schema und Crawler-Pipeline noch nicht implementiert.
|
||
Roadmap siehe [`STATUS.md`](STATUS.md).
|
||
|
||
## Architektonische Invarianten
|
||
|
||
Beschlossen. Nicht ohne explizite Diskussion antasten.
|
||
|
||
1. **Aggregator-Policy ist nicht-verhandelbar.** robots.txt, ≥1s
|
||
Crawl-Delay, User-Agent mit Kontakt-Mail, sichtbare Attribution,
|
||
Take-Down-Endpoint binnen 72h vorläufige Deaktivierung. Siehe
|
||
[`AGGREGATOR_POLICY.md`](../mana/docs/AGGREGATOR_POLICY.md).
|
||
2. **Cross-Border by design.** Default-Ansicht zeigt DE+CH gemeinsam.
|
||
`countries`-Tabelle mit ISO-Codes (DE/CH), `regions`-Tabelle als
|
||
curated Klammer (`bodensee`, `hegau`, `thurgau-west`).
|
||
3. **mana-research als Crawl-Provider.** Eigene Crawler vermeiden;
|
||
Firecrawl/Jina respektieren robots.txt by default. Cost-Tracking
|
||
geht über mana-credits.
|
||
4. **Hot-Link für Bilder (Default).** Bilder von Drittseiten werden
|
||
nicht lokal gehostet — `<img src="originalquelle">`. Lokales
|
||
Caching nur als begründete Ausnahme.
|
||
5. **MVP ohne User-Auth.** Browse + Take-Down sind public; Claim-Flow
|
||
und Lieblings-Venue-Abo kommen mit mana-auth ab Phase β-3.
|
||
6. **Web ist Astro mit Hono-API**, nicht SvelteKit. Begründung in
|
||
Plan: Discovery-/SEO-Last, hohe statische Seitenzahl, Mobile-First.
|
||
7. **Eigene Postgres-DB `seepuls`** im geteilten Mana-Cluster
|
||
(lokal :5441), Schema-Isolation via `pgSchema('seepuls')`.
|
||
|
||
## Stack
|
||
|
||
| Layer | Wahl |
|
||
|---|---|
|
||
| Web | Astro (Node-Adapter, SSR + Islands für Filter+Karte) |
|
||
| API | Hono + Bun |
|
||
| Datenbank | PostgreSQL + Drizzle ORM |
|
||
| Auth | `@mana/shared-hono/auth` (JWKS aus mana-auth, ab β-3) |
|
||
| Föderation | `@mana/shared-app-tpl` für Manifest/Share/Tools/DSGVO |
|
||
| Crawl | `mana-research` (Firecrawl/Jina/Readability) |
|
||
| Geo | `mana-geocoding` (Photon Europe, DE+CH) |
|
||
| Storage | `mana-media` (nur App-eigene Bilder, nicht gecrawlte) |
|
||
| Job-Scheduling | `node-cron` in-process (kein Plattform-Scheduler) |
|
||
|
||
Ports: API `3095`, Web `3096`, Postgres `5441` (siehe
|
||
[`../mana/docs/PORTS.md`](../mana/docs/PORTS.md)).
|
||
|
||
## Repo-Struktur
|
||
|
||
```
|
||
seepuls/
|
||
├── apps/
|
||
│ ├── api/ Hono+Bun-Backend (Phase α-0+)
|
||
│ │ └── src/
|
||
│ │ ├── index.ts App-Boot + shared-app-tpl-Mounts
|
||
│ │ ├── handlers.ts Share-/Tool-Handler (leer im MVP)
|
||
│ │ ├── store.ts Datenzugriff (Drizzle, später)
|
||
│ │ └── db/schema/ Drizzle-Schema (folgt α-0)
|
||
│ └── web/ Astro-Frontend (Phase α-4, später)
|
||
├── packages/ leer für jetzt
|
||
├── docs/ repo-eigene Doks
|
||
├── app-manifest.json Föderations-Manifest (validiert beim Boot)
|
||
├── docker-compose.yml lokales Postgres :5441
|
||
├── STATUS.md aktueller Phasen-Stand
|
||
└── .github/workflows/ ci.yml
|
||
```
|
||
|
||
## Datenmodell (Plan, gemäß AGGREGATOR_POLICY §6)
|
||
|
||
```
|
||
countries DE, CH (hartcodiert)
|
||
regions bodensee, hegau, thurgau-west, … (curated)
|
||
venues Org/Lokal-Eintrag, gecrawlt oder geclaimt
|
||
event_sources N pro venue; Listing-URL + Crawl-Intervall
|
||
events Einzel-Event, deduplikiert via external_id_hash
|
||
crawl_jobs Audit-Trail: was wurde wann gecrawlt, mit
|
||
robots_check_passed-Flag (Compliance-relevant)
|
||
takedown_requests DSGVO-/Urheber-Anfragen mit Frist-Tracking
|
||
blocked_domains Source-Block-Liste (Eskalations-Stufe 2)
|
||
```
|
||
|
||
## Föderations-Vertrag
|
||
|
||
Pflicht-Endpoints (alle aus `@mana/shared-app-tpl`):
|
||
|
||
| Method | Path | Quelle |
|
||
|---|---|---|
|
||
| `GET` | `/healthz`, `/readyz`, `/healthz/details` | `healthRoutes()` |
|
||
| `GET` | `/.well-known/mana-app.json` | `wellKnownManifestHandler()` |
|
||
| `POST` | `/api/v1/share/receive` | `shareReceiveRouter()` (Handler leer im MVP) |
|
||
| `POST` | `/api/v1/tools/:name` | `toolsRouter()` (Handler leer im MVP) |
|
||
| `GET` | `/api/v1/search?q=` | app-eigen, public (keine Auth) |
|
||
| `GET` | `/api/v1/dsgvo/export` | `dsgvoExportHandler()` (takedown-Mails) |
|
||
|
||
Manifest-Drift-Check beim Boot — Service startet nicht, wenn das
|
||
Manifest nicht zum aktuellen Schema-Stand passt.
|
||
|
||
## Konventionen
|
||
|
||
- **pnpm 9.15.x**, Node 20+, Turborepo 2.x, Bun für API-Runtime
|
||
- **Tabs** für Indent, single quotes, 100-col Prettier
|
||
- **Drizzle 0.38 / drizzle-kit 0.30** (Plattform-Einklang)
|
||
- **JWT-Validation lokal** über JWKS-Cache (5 min), kein Live-Call
|
||
- **Service-to-Service** via `X-Service-Key` (`MANA_SERVICE_KEY`)
|
||
- **Tests**: Vitest
|
||
|
||
## Crawler-Disziplin (Pflicht)
|
||
|
||
Jeder Crawl muss durch `src/crawl/policy.ts` (folgt α-1) gehen, die
|
||
folgendes prüft und protokolliert:
|
||
|
||
- `robots.txt`-Lookup pro Host (gecached 24h)
|
||
- User-Agent: `SEEPULS_CRAWL_USER_AGENT` aus env, mit `+https://seepuls.mana.how; kontakt@mana.how`
|
||
- ≥ 1.1s zwischen Requests pro Host (`SEEPULS_CRAWL_INTERVAL_MS`)
|
||
- ETag/If-Modified-Since vom letzten Crawl mitgeben
|
||
- Backoff bei 429/503 (exponential, min 6h)
|
||
- Audit-Log in `crawl_jobs` mit `robots_check_passed: bool` — Compliance-Beweis
|
||
|
||
Bei Verstößen: `mana-compliance`-Subagent kann ein Veto auf einen Live-
|
||
Crawl-Cut werfen. Pre-Live-Check ist Pflicht.
|
||
|
||
## Lokal entwickeln
|
||
|
||
```bash
|
||
pnpm install # @mana/* aus pkg.mana.how
|
||
pnpm docker:up || docker compose up -d # lokales Postgres :5441
|
||
pnpm --filter ./apps/api dev # API auf :3095
|
||
# (apps/web folgt in α-4)
|
||
```
|
||
|
||
Voraussetzungen:
|
||
- mana-Plattform-Stack lokal laufend (mindestens `mana-auth` :3001,
|
||
`mana-research` :3068, `mana-geocoding` :3075)
|
||
- Verdaccio-Token in `~/.npmrc`
|
||
(`npm login --registry=https://pkg.mana.how`)
|
||
|
||
## Wichtige Cross-Repo-Doks
|
||
|
||
- [`../mana/docs/AGGREGATOR_POLICY.md`](../mana/docs/AGGREGATOR_POLICY.md) — Crawl-Regeln, nicht-verhandelbar
|
||
- [`../mana/docs/FEDERATION.md`](../mana/docs/FEDERATION.md) — Architektur-Grundlage
|
||
- [`../mana/docs/PORTS.md`](../mana/docs/PORTS.md) — Port-Allokation
|
||
- [`../mana/services/mana-research/CLAUDE.md`](../mana/services/mana-research/CLAUDE.md) — Crawl-Provider
|
||
- [`../mana/services/mana-geocoding/CLAUDE.md`](../mana/services/mana-geocoding/CLAUDE.md) — DE+CH-Geo
|