ultisuite-backend/internal/nextcloud/public_share_binding.go
R3D347HR4Y d4ccf7eb6e
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
hocuspocus lol 2
2026-06-09 14:30:34 +02:00

123 lines
3.3 KiB
Go

package nextcloud
import (
"context"
"fmt"
"path"
"strings"
)
// PublicShareBinding maps a public link token to owner storage paths.
type PublicShareBinding struct {
Token string
OwnerID string
SharePath string // OCS path, e.g. /Documents/hello.docx or /Documents
ItemType string // file | folder
}
func (b *PublicShareBinding) IsSingleFile() bool {
return strings.EqualFold(strings.TrimSpace(b.ItemType), "file")
}
// ClientSourcePath resolves the logical client path for a shared item.
// Single-file shares expose WebDAV at "/" but editors use /filename.
func (b *PublicShareBinding) ClientSourcePath(clientPath, displayName string) string {
clientPath = NormalizeClientPath(clientPath)
if clientPath != "/" {
return clientPath
}
if b != nil && b.IsSingleFile() && b.SharePath != "" {
return NormalizeClientPath("/" + path.Base(strings.TrimPrefix(b.SharePath, "/")))
}
if name := strings.TrimSpace(displayName); name != "" {
return NormalizeClientPath("/" + name)
}
return "/"
}
// OwnerPathForClient maps a client-facing path to the owner's Nextcloud path.
func (b *PublicShareBinding) OwnerPathForClient(clientPath, displayName string) string {
if b == nil {
return NormalizeClientPath(clientPath)
}
src := b.ClientSourcePath(clientPath, displayName)
rel := strings.TrimPrefix(src, "/")
sharePath := NormalizeClientPath(b.SharePath)
if b.IsSingleFile() {
dir := path.Dir(strings.TrimPrefix(sharePath, "/"))
if dir == "." || dir == "" {
return NormalizeClientPath("/" + rel)
}
return NormalizeClientPath(path.Join("/", dir, rel))
}
if sharePath == "/" || sharePath == "" {
return NormalizeClientPath("/" + rel)
}
return NormalizeClientPath(path.Join(sharePath, rel))
}
func normalizeOCSSharePath(p string) string {
p = strings.TrimSpace(p)
if p == "" {
return "/"
}
if !strings.HasPrefix(p, "/") {
p = "/" + p
}
return NormalizeClientPath(p)
}
// ResolvePublicShareBinding resolves owner + OCS share metadata for a public token.
func (c *Client) ResolvePublicShareBinding(ctx context.Context, token, password string) (*PublicShareBinding, error) {
token = strings.TrimSpace(token)
if token == "" {
return nil, ErrInvalidPublicShare
}
ownerID, err := c.getPublicShareOwnerID(ctx, token, password)
if err != nil {
return nil, err
}
ownerID = strings.TrimSpace(ownerID)
if ownerID == "" {
return nil, fmt.Errorf("public share owner not found")
}
shares, err := c.ListShares(ctx, ownerID, "")
if err == nil {
for _, sh := range shares {
if strings.TrimSpace(sh.Token) != token {
continue
}
itemType := strings.TrimSpace(sh.ItemType)
if itemType == "" {
itemType = "folder"
}
return &PublicShareBinding{
Token: token,
OwnerID: ownerID,
SharePath: normalizeOCSSharePath(sh.Path),
ItemType: itemType,
}, nil
}
}
view, err := c.GetPublicShare(ctx, token, "/", password)
if err != nil {
return nil, fmt.Errorf("public share token not found: %w", err)
}
itemType := strings.TrimSpace(view.ItemType)
if itemType == "" {
itemType = "folder"
}
sharePath := "/"
if itemType == "file" && strings.TrimSpace(view.Name) != "" {
sharePath = normalizeOCSSharePath("/" + view.Name)
}
return &PublicShareBinding{
Token: token,
OwnerID: ownerID,
SharePath: sharePath,
ItemType: itemType,
}, nil
}