ultisuite-backend/internal/nextcloud/share_ocs.go
R3D347HR4Y 556d5f416d Enhance API and configuration for contact discovery and public sharing
- Introduced new endpoints for contact discovery, including scanning, listing, and managing discovered contacts.
- Implemented retry logic for handling missing DAV credentials during contact operations.
- Added public share functionality for drive API, allowing users to manage public shares, including upload, delete, and rename operations.
- Updated Nextcloud configuration to support public share links and improved error handling for public share permissions.
- Enhanced logging and validation across contact and drive APIs for better error tracking and user feedback.
- Added tests for new contact matching and ranking functionalities to ensure accuracy and reliability.
2026-06-06 20:27:02 +02:00

133 lines
4.0 KiB
Go

package nextcloud
import (
"encoding/json"
"fmt"
"io"
"strings"
"time"
)
type ocsShareRecord struct {
ID flexShareID `json:"id"`
Path string `json:"path"`
ShareType int `json:"share_type"`
Permissions int `json:"permissions"`
URL string `json:"url"`
Expiration string `json:"expiration"`
ShareWith *string `json:"share_with"`
ShareWithDisplayName string `json:"share_with_displayname"`
Label string `json:"label"`
Token string `json:"token"`
UIDOwner string `json:"uid_owner"`
DisplayNameOwner string `json:"displayname_owner"`
UIDFileOwner string `json:"uid_file_owner"`
DisplayNameFileOwner string `json:"displayname_file_owner"`
Stime int64 `json:"stime"`
Note string `json:"note"`
ItemType string `json:"item_type"`
Password *string `json:"password"`
}
// flexShareID accepts Nextcloud share ids as JSON string or number.
type flexShareID string
func (f *flexShareID) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err == nil {
*f = flexShareID(strings.TrimSpace(s))
return nil
}
var n json.Number
if err := json.Unmarshal(b, &n); err == nil {
*f = flexShareID(n.String())
return nil
}
return fmt.Errorf("invalid share id")
}
func (f flexShareID) String() string {
return string(f)
}
func decodeOCSShareRecords(raw json.RawMessage) ([]ocsShareRecord, error) {
raw = json.RawMessage(strings.TrimSpace(string(raw)))
if len(raw) == 0 || string(raw) == "null" || string(raw) == "[]" {
return nil, nil
}
if raw[0] == '[' {
var list []ocsShareRecord
if err := json.Unmarshal(raw, &list); err != nil {
return nil, err
}
return list, nil
}
var one ocsShareRecord
if err := json.Unmarshal(raw, &one); err != nil {
return nil, err
}
return []ocsShareRecord{one}, nil
}
func decodeOCSShareResponse(body io.Reader, fallbackPath string) (*ShareInfo, error) {
var ocsResp struct {
OCS struct {
Data json.RawMessage `json:"data"`
} `json:"ocs"`
}
if err := json.NewDecoder(body).Decode(&ocsResp); err != nil {
return nil, err
}
items, err := decodeOCSShareRecords(ocsResp.OCS.Data)
if err != nil {
return nil, err
}
if len(items) == 0 {
return nil, fmt.Errorf("empty share response")
}
info := mapOCSShareRecord(items[0], fallbackPath)
return &info, nil
}
func mapOCSShareRecord(item ocsShareRecord, fallbackPath string) ShareInfo {
path := strings.TrimSpace(item.Path)
if path == "" {
path = fallbackPath
}
shareWith := ""
if item.ShareWith != nil {
shareWith = strings.TrimSpace(*item.ShareWith)
}
expiration := strings.TrimSpace(item.Expiration)
info := ShareInfo{
ID: item.ID.String(),
Path: path,
ShareType: item.ShareType,
Permissions: item.Permissions,
URL: item.URL,
ExpiresAt: expiration,
ShareWith: shareWith,
ShareWithDisplayName: strings.TrimSpace(item.ShareWithDisplayName),
Label: strings.TrimSpace(item.Label),
Token: strings.TrimSpace(item.Token),
OwnerID: strings.TrimSpace(item.UIDOwner),
OwnerDisplayName: strings.TrimSpace(item.DisplayNameOwner),
FileOwnerID: strings.TrimSpace(item.UIDFileOwner),
FileOwnerDisplayName: strings.TrimSpace(item.DisplayNameFileOwner),
Note: strings.TrimSpace(item.Note),
ItemType: strings.TrimSpace(item.ItemType),
HasPassword: item.Password != nil && strings.TrimSpace(*item.Password) != "",
}
if ts := item.Stime; ts > 0 {
info.CreatedAt = time.Unix(ts, 0).UTC().Format(time.RFC3339)
}
if item.ShareType == 3 {
if strings.EqualFold(info.Label, "internal") {
info.AccessMode = "internal"
} else {
info.AccessMode = "public"
}
}
return info
}