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

103 lines
3.4 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 files
import (
"context"
"errors"
"fmt"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
// Auflösung von Pfaden zu Ordnern – gebraucht von WebDAV, das über Pfade
// adressiert statt über IDs.
// ── WebDAV helpers ────────────────────────────────────────────────────────────
// FolderByPath resolves a path like ["photos", "2024"] to a Folder.
// Returns a zero-ID folder for an empty parts slice (root).
func (s *Store) FolderByPath(ctx context.Context, principalID uuid.UUID, parts []string) (*Folder, error) {
return s.FolderByPathFrom(ctx, principalID, nil, parts)
}
// FolderByPathFrom löst Pfadteile ab einem bestimmten Ordner auf. base == nil
// heißt: ab der Wurzel des Prinzipals.
//
// Der Einstiegspunkt ist nötig, weil WebDAV geteilte Ordner unter einem
// virtuellen Pfad einhängt: Dort beginnt der Weg nicht bei der Wurzel des
// Eigentümers, sondern genau bei dem Ordner, der freigegeben wurde – alles
// darüber geht den Empfänger nichts an.
func (s *Store) FolderByPathFrom(ctx context.Context, principalID uuid.UUID, base *uuid.UUID, parts []string) (*Folder, error) {
if len(parts) == 0 {
if base == nil {
return &Folder{OwnerID: &principalID, Name: "/"}, nil
}
return s.folderByID(ctx, *base)
}
parentID := base
var current *Folder
for _, part := range parts {
var f Folder
var err error
if parentID == nil {
err = s.db.QueryRow(ctx,
`SELECT id, owner_id, group_id, parent_id, name, created_at FROM folders
WHERE (owner_id = $1 OR group_id = $1) AND parent_id IS NULL AND name=$2`,
principalID, part,
).Scan(&f.ID, &f.OwnerID, &f.GroupID, &f.ParentID, &f.Name, &f.CreatedAt)
} else {
err = s.db.QueryRow(ctx,
`SELECT id, owner_id, group_id, parent_id, name, created_at FROM folders
WHERE (owner_id = $1 OR group_id = $1) AND parent_id=$2 AND name=$3`,
principalID, parentID, part,
).Scan(&f.ID, &f.OwnerID, &f.GroupID, &f.ParentID, &f.Name, &f.CreatedAt)
}
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}
if err != nil {
return nil, fmt.Errorf("folder by path: %w", err)
}
parentID = &f.ID
current = &f
}
return current, nil
}
func (s *Store) folderByID(ctx context.Context, id uuid.UUID) (*Folder, error) {
var f Folder
err := s.db.QueryRow(ctx,
`SELECT id, owner_id, group_id, parent_id, name, created_at FROM folders WHERE id = $1`, id,
).Scan(&f.ID, &f.OwnerID, &f.GroupID, &f.ParentID, &f.Name, &f.CreatedAt)
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}
if err != nil {
return nil, fmt.Errorf("folder by id: %w", err)
}
return &f, nil
}
// FileInFolder finds a non-deleted file by name within a folder (nil = root).
func (s *Store) FileInFolder(ctx context.Context, ownerID uuid.UUID, folderID *uuid.UUID, name string) (*File, error) {
var f File
var err error
if folderID == nil {
err = scanFile(s.db.QueryRow(ctx,
`SELECT `+fileCols+` FROM files
WHERE (owner_id = $1 OR group_id = $1) AND folder_id IS NULL AND name=$2 AND is_deleted=false`,
ownerID, name,
), &f)
} else {
err = scanFile(s.db.QueryRow(ctx,
`SELECT `+fileCols+` FROM files
WHERE (owner_id = $1 OR group_id = $1) AND folder_id=$2 AND name=$3 AND is_deleted=false`,
ownerID, folderID, name,
), &f)
}
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}
return &f, err
}