1
1
Fork
You've already forked menuverse
0
No description
  • TypeScript 51.2%
  • Vue 47.4%
  • Shell 0.4%
  • JavaScript 0.4%
  • CSS 0.3%
  • Other 0.3%
2026年06月23日 14:04:01 +02:00
e2e Init 2026年06月01日 16:07:55 +02:00
marketplace #1 Ernährungspräferenz im Marketplace: vegan, vegetarisch 2026年06月23日 13:08:10 +02:00
shopmenu improve fix for runnning import in container 2026年06月23日 14:04:01 +02:00
.dockerignore Init 2026年06月01日 16:07:55 +02:00
.env.example add functionality for regular import of menue data from api 2026年06月23日 12:39:29 +02:00
.gitignore Init 2026年06月01日 16:07:55 +02:00
.npmrc Init 2026年06月01日 16:07:55 +02:00
dev-up.sh Init 2026年06月01日 16:07:55 +02:00
docker-compose.coolify-demo.yml improve fix for runnning import in container 2026年06月23日 14:04:01 +02:00
docker-compose.marketplace.yml Init 2026年06月01日 16:07:55 +02:00
docker-compose.override.yml Init 2026年06月01日 16:07:55 +02:00
docker-compose.shopmenu.yml Init 2026年06月01日 16:07:55 +02:00
docker-entrypoint.sh Init 2026年06月01日 16:07:55 +02:00
Dockerfile Init 2026年06月01日 16:07:55 +02:00
Dockerfile.e2e Init 2026年06月01日 16:07:55 +02:00
LICENSE.md update license 2026年06月01日 16:10:14 +02:00
package.json Init 2026年06月01日 16:07:55 +02:00
pnpm-lock.yaml Init 2026年06月01日 16:07:55 +02:00
pnpm-workspace.yaml Init 2026年06月01日 16:07:55 +02:00
README.md Init 2026年06月01日 16:07:55 +02:00

Menuverse

In English: Menuverse is a federated menu system for canteens and institutions, built on FEP-0837: Federated Marketplace, schema.org and Fedify/ActivityPub. It consists of two Nuxt 4 apps: ShopMenu publishes a canteen's daily dishes as federated Proposal activities (each canteen is its own ActivityPub actor), and Marketplace follows one or more ShopMenu instances, caches their proposals and lets users browse them filtered by day, allergen and full text. The rest of this README is in German. This project is still in heavy development and should probaly not use in unsupervised production.

Föderiertes Speiseplan-System für Kantinen und Einrichtungen auf Basis von FEP-0837: Federated Marketplace und schema.org. Zwei Nuxt 4 Apps, beide mit Fedify für ActivityPub und Prisma/Postgres:

  • ShopMenu — publiziert Gerichte als Proposal-Activities. Jede Kantine ist ein eigener ActivityPub Person-Actor (/users/<canteen>). Ein Server kann mehrere Kantinen hosten.
  • Marketplace — folgt einer oder mehreren ShopMenu-Instanzen, cached deren Proposals, zeigt sie gefiltert nach Tag, Allergen und Volltext.

Datenmodell: langlebige MenuItem-Zeilen (Rezept mit Allergenen, Nährwerten — als schema.org-JSON-LD ausgespielt) und kurzlebige Dish-Zeilen (eine Portion an einem Tag mit Preis). Jeder Dish wird ein FEP-0837-Proposal.

graph LR
 MP["🛒 Marketplace<br/><small>folgt &amp; cached alle Proposals</small>"]
 FED["🐘 Mastodon / Misskey / etc...<br/><small>beliebiger Fediverse-Account</small>"]
 subgraph SA["🖥️ ShopMenu-Server A · a.example.com"]
 A1["👤 @nord@a.example.com<br/><small>Kantine Nord</small>"]
 A2["👤 @sued@a.example.com<br/><small>Cafeteria Süd</small>"]
 end
 subgraph SB["🖥️ ShopMenu-Server B · b.example.com"]
 B1["👤 @zentral@b.example.com<br/><small>Mensa Zentral</small>"]
 end
 MP <--> A1
 MP <--> A2
 MP <--> B1
 FED <--> A2
 FED <--> B1
 classDef market fill:#bfe3c0,stroke:#5fa463,color:#1f4d29;
 classDef actor fill:#e9f6ea,stroke:#a9d4ad,color:#2f6b39;
 classDef fed fill:#f2f7ec,stroke:#cbe0b4,color:#4e7136;
 class MP market;
 class FED fed;
 class A1,A2,B1 actor;
 style SA fill:#fafdf6,stroke:#cfe6cf,color:#3a5d3f;
 style SB fill:#fafdf6,stroke:#cfe6cf,color:#3a5d3f;
 linkStyle default stroke:#9ccaa0,stroke-width:1.5px;

Pfeil = Zwei Akteure die sich folgen.

Wichtig: Das Projekt ist immer nocht in der Entwicklung und es wird breaking changes geben. Es sollte nicht vermutlich nicht in einem überwachten Produktivsystem eingesetzt werden.

Was technisch geht

  • Föderation via Fedify: HTTP-Signaturen, WebFinger, Followers/Inbox/Outbox, Proposal-Dereferencing
  • Multi-Tenancy: ein ShopMenu-Server kann beliebig viele Kantinen-Actors hosten
  • Allergen-Filter (EU-LMIV-Codes), Nährwerte, Wochenpläne, Tagesansicht
  • Live-Daten: Seed-Set studentenwerk-sh zieht alle 18 Mensen/Cafeterien des Studierendenwerks Schleswig-Holstein aus inoffizieller API aus dem ProjektDraculente/mensa-api

Coolify Deploy

Drei Compose-Dateien, je nach Use-Case:

Datei Zweck
docker-compose.shopmenu.yml Eine einzelne ShopMenu-Instanz
docker-compose.marketplace.yml Ein einzelner Marketplace
docker-compose.coolify-demo.yml Demo-Stack: 2 ShopMenus + 1 Marketplace in einem Coolify-Resource — zum Vorzeigen der Federation zwischen unabhängigen Instanzen

Lokal entwickeln

pnpm install
cp .env.example .env # Dev-Defaults — Session-Secrets sind drin
./dev-up.sh # picks free ports, starts everything via compose
docker compose up -d postgres db-init # nur die DB starten
pnpm dev:shopmenu # http://localhost:3001
pnpm dev:marketplace # http://localhost:3002

Beispiel-Payload

FEP-0837-Proposal unter /users/shopmenu/proposals/2026-05-21-schnitzel-kartoffelsalat:

{
 "@context": ["https://www.w3.org/ns/activitystreams",
 {"vf": "https://w3id.org/valueflows/ont/vf#"}],
 "type": "Proposal",
 "attributedTo": "https://shopmenu.example.com/users/shopmenu",
 "name": "Schnitzel mit Kartoffelsalat",
 "startTime": "2026年05月21日T11:30:00+02:00",
 "endTime": "2026年05月21日T14:30:00+02:00",
 "purpose": "offer",
 "publishes": {
 "type": "Intent", "action": "transfer",
 "resourceConformsTo": "https://shopmenu.example.com/users/shopmenu/menu-items/schnitzel-kartoffelsalat",
 "resourceQuantity": {"type": "Measure", "unit": "portion", "numericalValue": 1},
 "availableQuantity": {"type": "Measure", "unit": "portion", "numericalValue": 120}
 },
 "reciprocal": {
 "type": "Intent", "action": "transfer",
 "resourceQuantity": {"type": "Measure", "unit": "EUR", "numericalValue": 4.20}
 }
}

Ein Consumer liest das Proposal, folgt publishes.resourceConformsTo und bekommt das MenuItem mit Allergenen/Nährwerten als schema.org-JSON-LD.

Spec & Quellen

Nächste Schritte

Federation

  • Richtige nutzung von resourceConformsTo in dieser Form
  • Konfiguration und Anzeige von Bildern bei Profilen
  • Richtige nutzung von DIET_LABELS
  • Richtige konforme nutzung von ID

Technisch

  • Update von Proposal und berücksichtigung in Marketplace
  • Löschen von Proposal und berücksichtigung in Marketplace
  • Verbessern und ausweiten der e2e playwright tests
  • Bessere Suchfunktionen für das hinterlegen von Adresse und OSM ID
  • Korrekte auslieferung der Öffnungszeiten bei hinterlegten OSM Daten
  • Testen und verbessern der Importe von Webseiten