package files import ( "context" "errors" "fmt" "io" "github.com/google/uuid" ) // ErrQuotaExceeded wird gemeldet, sobald ein Upload das Speicherkontingent des // Nutzers überschreitet. var ErrQuotaExceeded = errors.New("storage quota exceeded") // quotaReader reicht Daten durch und bricht ab, sobald mehr als `remaining` // Bytes gelesen wurden. Nötig, weil Uploads gestreamt werden: die tatsächliche // Größe steht erst am Ende fest, und bis dahin wären die Bytes längst auf der // Platte. So bricht der Schreibvorgang bereits beim Überschreiten ab statt // erst hinterher zu prüfen. type quotaReader struct { r io.Reader remaining int64 } func newQuotaReader(r io.Reader, remaining int64) *quotaReader { if remaining < 0 { remaining = 0 } return "aReader{r: r, remaining: remaining} } func (q *quotaReader) Read(p []byte) (int, error) { n, err := q.r.Read(p) q.remaining -= int64(n) if q.remaining < 0 { return n, ErrQuotaExceeded } return n, err } // RemainingQuota liefert, wie viele Bytes der Nutzer noch schreiben darf, und // ob sein Kontingent unbegrenzt ist. // // `allowance` sind Bytes, die durch diesen Vorgang frei werden – beim // Überschreiben einer Datei zählt die alte Fassung nicht mehr zum Verbrauch, // sonst schlüge das Ersetzen einer Datei durch eine gleich große fehl. // // Ein Kontingent <= 0 gilt als unbegrenzt. func (s *Store) RemainingQuota(ctx context.Context, userID uuid.UUID, quotaBytes, allowance int64) (int64, bool, error) { if quotaBytes <= 0 { return 0, true, nil } used, err := s.UsedBytes(ctx, userID) if err != nil { return 0, false, fmt.Errorf("read used bytes: %w", err) } remaining := quotaBytes - used + allowance if remaining < 0 { remaining = 0 } return remaining, false, nil }