platform.share ist der eine Teilen-Service der Plattform: ein System für Token-Erzeugung, Ablauf, Passwort-Gate, Widerruf und View-Zählung. Tools bringen nur ihren Renderer für die öffentliche Seite mit; die Link-Mechanik bauen sie nie selbst.
Der Service ist live und wird vom zentralen Dateispeicher genutzt; weitere Tools hängen ihre Freigaben an dieselbe Mechanik. Die Tool-Perspektive (Datei teilen) zeigt die Dateispeicher-Seite; hier steht die Service-API.
Link anlegen
import { createShareService } from "@reosa/sdk";
const share = createShareService(db);
const link = await share.create(ctx, {
resourceType: "file_node", // frei wählbarer, stabiler Typ-String
resourceId: node.id,
expiresInDays: 30, // 1..90, Default 7
passwordHash, // optional: vorberechneter Hash, nie Klartext
allowDownload: true, // Default true; false = nur ansehen
maxViews: 100, // optional: Ansichts-Limit
});
// link: { id, token, expiresAt }
// link.token ist NUR hier verfügbar; öffentlicher Pfad: /share/<token>- Token-Sicherheit: Der Token ist 256 Bit aus dem CSPRNG, URL-safe. Gespeichert wird ausschließlich sein SHA-256-Hash; der Klartext existiert nur in dieser einen Antwort. Zeige ihn der Nutzer:in genau einmal.
- Ablauf:
expiresInDayserlaubt ganze Tage von 1 bis 90 (die Datei-Freigabe-UI bietet daraus feste Stufen an). Alternativ setztexpiresAteinen absoluten Zeitpunkt, für interne Consumer mit Sub-Tages-Ablauf (z.B. 1-Stunden-Links); er muss in der Zukunft und innerhalb von 366 Tagen liegen.noExpiry: true(Link läuft nie ab) existiert nur für Alt-Consumer; neue Flächen setzen es nicht. - Passwort: Das Hashing bleibt beim Aufrufer (App-Utility); der Service bekommt nur den fertigen Hash und speichert nie Klartext.
Widerrufen
await share.revoke(ctx, link.id); // ein Link, sofort wirksam
await share.revokeForResource(ctx, "file_node", node.id); // alle Links einer Ressource
await share.revokeAll(ctx); // alle aktiven Links des TenantsWiderruf wirkt sofort; die öffentliche Seite zeigt danach dieselbe neutrale Meldung wie für nie existierende Links. Beim endgültigen Löschen einer Ressource gehört revokeForResource in deinen Lösch-Pfad.
Übersichten und Badges
await share.listForResource(ctx, "file_node", node.id); // alle Links, neueste zuerst
await share.listByResourceTypes(ctx, ["hs_generation"]); // tool-eigene Teilen-Liste
await share.listActiveResourceIds(ctx, "file_node", nodeIds); // welche IDs haben aktive Links?listActiveResourceIds ist gebatcht und gedacht für Link-Badges in Listen ("dieses Element ist geteilt"), ohne pro Zeile eine Abfrage zu machen.
Öffentliche Auflösung und Zähler
Die öffentliche Seite (der Empfänger hat keine Session) löst über resolveByToken(token) auf und verbucht Ansichten mit recordView(share): view_count und last_viewed_at zählen atomar hoch, das Ansichts-Limit wird race-sicher durchgesetzt, und der erste View schreibt ein Audit-Event (share_viewed, Empfänger bleibt anonym). Gültigkeit (widerrufen, abgelaufen, Limit erreicht) und das Passwort-Gate prüft die aufrufende Seite. Diese beiden Pfade laufen über die Service-Connection der Plattform; Tool-Code ruft sie nicht direkt auf.
Regeln
- Token nie speichern oder loggen. Nur der Hash liegt in der Datenbank; auch dein Tool hält den Klartext nirgends fest.
- Kein eigener Link-Mechanismus. Ein Tool, das teilt, nutzt diesen Service, keine selbstgebauten Token-Tabellen.
- Widerruf in Lösch-Pfade einbauen. Eine gelöschte Ressource darf keinen lebenden Link zurücklassen.
- Neutrale Fehlerseite respektieren: ungültig, abgelaufen und widerrufen sind für Außenstehende nicht unterscheidbar.