Changelog
Alle wesentlichen Änderungen an Maschinchenring werden hier dokumentiert.
Preise im Chat wieder löschbar
- Das MCP-Tool
update_item akzeptiert für
pricePerWeek, pricePerWeekend und
deposit jetzt auch null, um einen Preis
explizit zu entfernen. Vorher war das Feld nur
optional — weggelassen hieß „unverändert lassen",
und es gab keinen Weg, einen einmal gesetzten Preis wieder zu
entfernen. pricePerDay bleibt weiterhin Pflicht und kann nicht
gelöscht werden, weil jedes Gerät einen Tagespreis braucht.
Chat-Links zu Geräten funktionieren wieder
- Das MCP-Tool
search_items liefert jetzt
itemUrl und thumbnailUrl wie
list_items und get_item. Der Chat-Assistent
(Haiku) hatte ohne itemUrl die Detailseiten-URL aus dem
Gerätenamen zusammengeraten (z. B. /geraete/rasenwalze/),
was am Astro-Slug-Handler vorbeiging und auf die Startseite umleitete
— für den Nutzer sah das wie ein defekter Link aus
(#143). - Regressionstest in
server/src/mcp/tools.test.ts
stellt sicher, dass search_items in Zukunft
itemUrl mit der Item-ID liefert.
Chat-Input-Feld bleibt über der Tastatur auf iOS (nativer Fix)
- Der erste Anlauf am 2026-04-17 (Scroll-Korrektur im
visualViewport-Resize-Handler plus onFocus-
Hack) hat das Problem auf iPhone XR nicht behoben — iOS hat das
Layout-Viewport weiterhin hochgescrollt, das Input-Feld landete
unter der Tastatur. - Nativer Fix:
body.fullscreen bekommt
position: fixed; inset: 0. Damit gibt es kein
scrollbares Layout-Viewport mehr, in das iOS die fokussierte
Textarea schieben könnte. Der visualViewport-Resize-
Handler muss nur noch die Body-Höhe auf die verbleibende
sichtbare Fläche setzen — die Flex-Chain (main →
astro-island → Chat) schrumpft sauber mit. - Dadurch konnten der
window.scrollTo-Workaround im
Resize-Handler, der zusätzliche scroll-Listener und
der onFocus-Hack am Textarea komplett entfallen —
die Lösung bleibt bei der Plattform.
Burger-Menü für mobile Navigation
- Auf schmalen Viewports (≤ 640px) kollabiert die Navigation in
website/src/layouts/BaseLayout.astro zu einem
Burger-Icon. Ein Klick öffnet die Links als Dropdown unterhalb
der Kopfleiste; aria-expanded und
aria-controls halten das Ganze tastatur- und
screenreader-freundlich. - Im eingeloggten Zustand (Chat, Buchungen, Konto, Abmelden) gab
es vorher horizontales Scrollen auf iPhone-Breite. Die Links
sind jetzt gestapelt und verschwinden hinter dem Icon, bis der
Nutzer sie explizit öffnet.
Chat-Input-Feld bleibt über der Tastatur auf iOS
- Auf iPhone XR (und anderen iOS-Geräten) rutschte das
Text-Input-Element unter die aufklappende Software-Tastatur, sobald
es fokussiert wurde. Ursache: iOS Safari scrollt das Layout-
Viewport hoch, ohne die Body-Höhe zu verkleinern, und die
bestehende
visualViewport-Logik hat diesen Scroll
nicht zurückgesetzt. - Zusätzlich hing die Flex-Chain an einer
<astro-island>-
Komponente, die per Default display: inline ist und
die Höhenbegrenzung nicht an den Chat-Container weiterreichte. - Fix in
website/src/layouts/BaseLayout.astro und
website/src/components/Chat.tsx:
<astro-island> wird im Fullscreen-Modus zum Flex-
Item, und der Resize-Handler sowie ein onFocus am
Textarea scrollen das Layout-Viewport zurück auf Null, sobald die
Tastatur öffnet.
Vermieter-Sperren auch im Website-Chat aufrufbar
- Die Tools
block_period, unblock_period
und list_my_blocks standen bisher nur am MCP-Endpoint
/mcp zur Verfügung, nicht aber am Website-Chat-
Endpoint /api/chat. Dadurch konnte die KI im Chat
auf der Website keine Sperren setzen oder auflisten. - Die drei Tools sind jetzt auch in
server/src/http/chat.ts
registriert (authentifizierten Nutzern vorbehalten) und der
System-Prompt erwähnt sie explizit.
Markdown in Gerätebeschreibungen
- Die Beschreibung eines Geräts (oder eines Sets) wird jetzt als
Markdown gerendert. Aufzählungen (
- / *),
Fettdruck, Links und Überschriften landen in der passenden HTML-
Auszeichnung auf /geraete/:slug und
/sets/:slug statt als Fließtext zusammenzufallen. - Vor dem Rendering läuft jeder Inhalt durch
sanitize-html mit strikter Allowlist: nur
Fließtext-Tags, keine Bilder, keine Iframes, keine Event-Handler,
keine javascript:-URLs, keine <script>-
Blöcke. Damit kann die KI (oder ein Vermieter) bösartigen
Markdown-Content nicht in ein XSS umwandeln. - Externe Links (
http:///https://)
bekommen automatisch rel="noopener nofollow ugc" und
target="_blank"; interne/relative Links bleiben
unverändert und öffnen im selben Tab. - Regressionstests in
e2e/device-pages.spec.ts
(„renders Markdown bullets and bold in description" +
„sanitizes dangerous HTML in description") prüfen beide Seiten der
Medaille.
Astro-Middleware verifiziert JWT in-process
- Die Astro-Middleware in
website/src/middleware.ts
macht bei jedem Request keinen Loopback-HTTP-Call nach
/auth/me mehr. Stattdessen importiert sie
makeTokenVerifier aus
server/src/auth/jwt.ts und prüft den JWT direkt
gegen process.env.JWT_PUBLIC_KEY. - Öffentliche Seiten (Startseite, Gerätedetails, Impressum,
Datenschutz, Docs) zahlen damit keinen Context-Switch mehr für
Auth — der JWT-Check läuft komplett in-process.
- Profil-Daten (Telefon, Adresse) lädt nur noch
/konto selbst per /auth/me, weil das
die einzige Seite ist, die sie anzeigt. Regressionstests in
e2e/stale-auth-cookie.spec.ts bleiben grün.
Vermieter können Zeiträume sperren
- Neue MCP-Tools
block_period, unblock_period
und list_my_blocks: Vermieter können Zeiträume auf
ihren Geräten als „belegt" markieren — z.B. wenn sie das Gerät
selbst brauchen oder es bereits über eine andere Plattform
vermietet ist. - Sperren blockieren Buchungsversuche und erscheinen im öffentlichen
Kalender als unverfügbar (ohne Grund oder Namen des Vermieters
preiszugeben).
- Neue Tabelle
blocked_periods (Migration 013) — saubere
Trennung zu Buchungen: kein Mieter, keine Approval-Logik, keine
Dummy-Namen. - Regressionstests:
server/src/db/queries/blocked_periods.test.ts
und der neue Vermieter-Sperren-Block in
server/src/mcp/e2e.test.ts.
Konto-Zombie-Login und kaputtes Buchung-Stornieren repariert
- Astro-Middleware prüft jetzt bei jedem Request den
auth_token-Cookie gegen /auth/me.
Abgelaufene oder nach Server-Reboot ungültige Tokens werden
gelöscht — vorher zeigte die Navigation fälschlich
"Konto/Abmelden" an, und die Kontoseite hatte leere Profildaten
(keine Adresse). - Buchung stornieren, bestätigen und ablehnen laufen jetzt über
Full-Page-Confirmations
(
/buchungen/:id/cancel, /accept,
/decline) statt eines JS-Popups. Vorher las der
Inline-Handler den JWT über document.cookie — der
Cookie ist aber httpOnly, also sah er nichts, der
Bearer-Header war leer und der Server antwortete mit
"Unauthorized". Der E-Mail-Approval-Flow
(/buchungen/:id?token=xxx) bleibt davon
unberührt. - Regressionstests:
e2e/cancel-booking.spec.ts,
e2e/owner-decision.spec.ts und
e2e/stale-auth-cookie.spec.ts.
Anonyme Reichweitenmessung mit Plausible
- Tracking-Script von
analytics.levinkeller.de (selbst
gehostetes Plausible) im BaseLayout eingebunden — läuft
damit auf allen gerenderten Seiten - Cookielos, kein Personenbezug, keine Cross-Site-Tracking-IDs
Doku aus Nutzer-Sicht neu geschrieben
/docs komplett umgeschrieben: Community-Idee, Rollen
(Mieter / Vermieter), Warum-Freischaltung, FAQ (Haftung, Versicherung,
Absagen, No-Shows) statt Tech-Stack- und Infrastruktur-Details - Neuer Abschnitt "Was darf hier angeboten werden?":
kein Marktplatz, keine Dienstleistungen, keine alkoholischen /
pornografischen / illegalen Inhalte — Meldung fragwürdiger
Angebote per E-Mail mit Link zum Gerät
- Gewerbetreibende ausdrücklich erlaubt (keine Haftung, egal ob
privat oder gewerblich)
- Kaution im Mieter- und Vermieter-Flow erwähnt — Übergabe direkt
zwischen den Parteien, keine Zahlungsabwicklung über die Plattform
- Betreiber-Hinweis (Levin Keller, Impressum) und Kontakt
(
post@levinkeller.de) ergänzt /docs/mcp-tools entschlackt: als API-Referenz aus Sicht
des Clients positioniert, Implementierungs-Details (Suche, Bilder-Pipeline)
entfernt, Admin-Tools aus der öffentlichen Referenz genommen,
"Owner" → "Vermieter" vereinheitlicht
Vermieter-Status sichtbar im Chat
get_profile gibt jetzt lender, admin,
lenderStatus (approved / pending /
none) und profileComplete zurück — der Chat-Assistent
kann den Vermieter-Status damit direkt auslesen statt zu raten request_lender_status ist jetzt auch im Web-Chat (Haiku) als Tool
verfügbar; vorher fehlte es in den Chat-Tool-Definitionen - Tool-Beschreibungen für den Vermieter-Flow geschärft
server/src/mcp/tools.ts in Domänen-Module aufgeteilt
(tools/items.ts, tools/bookings.ts,
tools/profile.ts, tools/admin.ts,
tools/deps.ts)
Vermieter-Flow & User-Cleanup
- Neues MCP-Tool
request_lender_status: User beantragt Vermieter-Freischaltung
(Profil-Check, 1h-Cooldown, E-Mail an alle Admins) - Admin-UI unter
/admin/lender-requests/:userId (Cookie-Auth, GET + POST approve/reject) - DB-Rename:
can_upload → lender, is_admin → admin - Spalte
lender_requested_at als Spam-Schutz - Tote Spalten
github_id / google_id entfernt, User-IDs sind jetzt opaque UUIDs - GitHub-OAuth übergibt
randomUUID() statt GitHub-Login als ID; Profile werden weiterhin per E-Mail gematcht
Öffentliche Dokumentation
- Neue Dokumentationsseiten unter
/docs hinzugefügt - Vollständige MCP-Tools-Referenz unter
/docs/mcp-tools - Changelog unter
/docs/changelog - Footer-Link zur Dokumentation ergänzt
Chat-Rework
- Chat-Interface überarbeitet und stabilisiert
- Konversationsverwaltung verbessert
Semantische Suche
search_items-Tool mit Hybrid-Suche (semantisch + lexikal) - Jina Embeddings für Geräte-Beschreibungen
- 70% semantisches Gewicht, 30% lexikal
Bild-Upload und imgproxy
- Bild-Upload-Endpoint
POST /images/upload (S3/MinIO) - imgproxy-Integration für optimierte, HMAC-signierte Bild-URLs
attach_image_to_item-Tool zum Verknüpfen von Bildern - Uploader-Berechtigung (
can_upload) für Vermieter
Gerätesets
- Sets: Bündel aus mehreren Geräten zu einem Tagespreis
list_sets-Tool - Sets-Übersicht auf der Webseite
Chat-Assistent
- Chat-Interface mit Claude (SSE-Streaming)
- Konversationspersistenz in SQLite
- Konversationstitel via
set_conversation_title-Tool
Admin-Tools und Vermieter-Verwaltung
admin_approve_uploader, admin_revoke_uploader, admin_list_users - Vermieter-Whitelist (
can_upload-Flag) - Admin-Flag (
is_admin) für Nutzerverwaltung
OAuth PKCE für MCP-Clients
- OIDC-Provider mit Authorization Code + PKCE
- Dynamic Client Registration (
POST /oauth/register) - Kompatibel mit Claude Desktop und anderen MCP-Clients
Buchungsfluss
- Buchungsanfragen via MCP-Tool
create_booking - Owner-Benachrichtigung per E-Mail (Approve/Reject-Links)
- Stornierung durch Mieter und Owner
- Status:
pending → approved / rejected / cancelled
Authentifizierung
- GitHub OAuth (arctic)
- Magic Link per E-Mail
- JWT RS256 (jose) als Cookie
MCP-Server und Webseite
- Initiale Implementierung des MCP-Servers (Streamable HTTP)
- Astro SSR Website mit Geräteübersicht und Detailseiten
- Verfügbarkeitskalender
- SQLite-Datenbankschema (Migrationen)
- Deployment auf k3s (ARM, Traefik, cert-manager)