Files
HoneyandClaude Opus 5 31a027c93a feat(aether): initial commit – self-hosted Cloud, Phasen 1–7
Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA.
Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb,
Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming,
Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office
(Collabora/OnlyOffice).

Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort)
sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 09:25:36 +02:00

71 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package storage
import (
"context"
"io"
"os"
"time"
)
// TempFiler kann Uploads unbekannter Größe auf der Platte zwischenlagern,
// statt sie im Arbeitsspeicher zu halten. Bewusst eine eigene, optionale
// Schnittstelle: *os.File gehört nicht in den allgemeinen Storage-Vertrag, und
// eine Implementierung ohne lokales Dateisystem kann sie schlicht weglassen.
type TempFiler interface {
CreateTemp(pattern string) (*os.File, error)
}
// LocalPather gibt den echten Pfad eines Blobs heraus – nur lesend gedacht.
//
// Gebraucht von der Texterkennung: ocrmypdf und tesseract sind externe
// Programme und wollen einen Pfad, keinen Datenstrom. Ohne diese Schnittstelle
// müsste jede Datei erst in eine vollständige Kopie geschrieben werden, bevor
// sie erkannt werden kann – bei einem 300-MB-Scan also 300 MB zusätzlich auf
// einer Platte, die ohnehin knapp sein kann.
//
// Wie TempFiler bewusst optional: Eine Ablage ohne lokales Dateisystem (S3)
// lässt sie weg, und der Aufrufer fällt dann auf die Kopie zurück.
type LocalPather interface {
LocalPath(key string) (string, error)
}
// FileInfo holds metadata about a stored blob.
type FileInfo struct {
Size int64
Checksum string // SHA-256 hex
ModTime time.Time
}
// Storage is the blob storage abstraction.
// All implementations must guarantee atomic writes and never corrupt existing data.
type Storage interface {
// Put streams r to the given key atomically (temp → fsync → rename).
// Returns the SHA-256 checksum and byte count written.
Put(ctx context.Context, key string, r io.Reader) (checksum string, size int64, err error)
// Get returns a reader for the blob at key. Caller must close it.
Get(ctx context.Context, key string) (io.ReadCloser, error)
// Delete removes a blob. In Phase 1 this is a hard delete used only by
// cleanup/GC paths; user-facing deletes are soft-deletes in the DB.
Delete(ctx context.Context, key string) error
// Stat returns metadata for a blob.
Stat(ctx context.Context, key string) (FileInfo, error)
// Move atomically moves a blob from oldKey to newKey within the same storage root.
Move(ctx context.Context, oldKey, newKey string) error
// Walk ruft fn für jeden Blob unterhalb von prefix auf. Existiert der
// Präfix nicht, ist das kein Fehler – nur nichts zu tun. Gedacht für die
// Bereinigung verwaister Blobs, die den Bestand auf der Platte gegen die
// Datenbank abgleichen muss.
Walk(ctx context.Context, prefix string, fn func(key string, info FileInfo) error) error
// DeletePrefix entfernt alles unterhalb eines Präfixes, etwa den kompletten
// Bereich eines Nutzers ("users/alice/"). Gedacht für das endgültige Löschen
// eines Kontos: danach existiert keine Datenbankzeile mehr, die noch auf die
// einzelnen Blobs zeigen könnte.
DeletePrefix(ctx context.Context, prefix string) error
}