- Added support for Faster Whisper transcription via Jigasi and Skynet. - Updated .env.example to include new environment variables for transcription settings. - Enhanced Jitsi Docker Compose configuration to include Skynet and Jigasi services. - Introduced new API endpoints for managing organizational folders in the drive service. - Updated Nextcloud initialization script to enable external file mounting. - Improved error handling and response structures in the drive API. - Added new properties for organization settings related to transcription and agenda management.
253 lines
7.6 KiB
Go
253 lines
7.6 KiB
Go
package nextcloud
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const groupFolderAllPermissions = 31
|
|
|
|
type GroupFolder struct {
|
|
ID int `json:"id"`
|
|
MountPoint string `json:"mount_point"`
|
|
Groups map[string]int `json:"groups"`
|
|
Quota int64 `json:"quota"`
|
|
Size int64 `json:"size"`
|
|
ACL bool `json:"acl"`
|
|
Manage []string `json:"manage"`
|
|
}
|
|
|
|
// GroupFolderWebDAVPath builds the WebDAV URL for a group folder root or subpath.
|
|
func GroupFolderWebDAVPath(folderID int, logicalPath string) string {
|
|
base := fmt.Sprintf("/remote.php/dav/groupfolders/%d", folderID)
|
|
logical := strings.Trim(logicalPath, "/")
|
|
if logical == "" {
|
|
return base
|
|
}
|
|
parts := strings.Split(logical, "/")
|
|
for i, p := range parts {
|
|
parts[i] = url.PathEscape(p)
|
|
}
|
|
return base + "/" + strings.Join(parts, "/")
|
|
}
|
|
|
|
func (c *Client) ListGroupFolders(ctx context.Context, userID string) ([]GroupFolder, error) {
|
|
resp, err := c.DoAsUser(ctx, "GET", "/index.php/apps/groupfolders/folders?format=json", nil, userID, ocsJSONHeaders())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, &HTTPStatusError{Operation: "list groupfolders", StatusCode: resp.StatusCode}
|
|
}
|
|
return decodeGroupFoldersResponse(resp.Body)
|
|
}
|
|
|
|
func (c *Client) ListGroupFoldersAdmin(ctx context.Context) ([]GroupFolder, error) {
|
|
resp, err := c.doRequest(ctx, "GET", "/index.php/apps/groupfolders/folders?format=json", nil, ocsJSONHeaders())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, &HTTPStatusError{Operation: "list groupfolders admin", StatusCode: resp.StatusCode}
|
|
}
|
|
return decodeGroupFoldersResponse(resp.Body)
|
|
}
|
|
|
|
func decodeGroupFoldersResponse(body io.Reader) ([]GroupFolder, error) {
|
|
var payload struct {
|
|
OCS struct {
|
|
Data json.RawMessage `json:"data"`
|
|
} `json:"ocs"`
|
|
}
|
|
if err := json.NewDecoder(body).Decode(&payload); err != nil {
|
|
return nil, err
|
|
}
|
|
raw := payload.OCS.Data
|
|
if len(raw) == 0 || string(raw) == "[]" || string(raw) == "null" {
|
|
return nil, nil
|
|
}
|
|
|
|
var asMap map[string]GroupFolder
|
|
if err := json.Unmarshal(raw, &asMap); err == nil && len(asMap) > 0 {
|
|
out := make([]GroupFolder, 0, len(asMap))
|
|
for _, item := range asMap {
|
|
out = append(out, item)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
var asList []GroupFolder
|
|
if err := json.Unmarshal(raw, &asList); err != nil {
|
|
return nil, err
|
|
}
|
|
return asList, nil
|
|
}
|
|
|
|
func (c *Client) CreateGroupFolder(ctx context.Context, mountPoint string) (int, error) {
|
|
form := url.Values{}
|
|
form.Set("mountpoint", mountPoint)
|
|
resp, err := c.doRequest(ctx, "POST", "/index.php/apps/groupfolders/folders?format=json", strings.NewReader(form.Encode()), map[string]string{
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return 0, &HTTPStatusError{Operation: "create groupfolder", StatusCode: resp.StatusCode}
|
|
}
|
|
var payload struct {
|
|
OCS struct {
|
|
Data struct {
|
|
ID int `json:"id"`
|
|
} `json:"data"`
|
|
} `json:"ocs"`
|
|
}
|
|
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
|
|
return 0, err
|
|
}
|
|
if payload.OCS.Data.ID <= 0 {
|
|
return 0, fmt.Errorf("groupfolder create returned empty id")
|
|
}
|
|
return payload.OCS.Data.ID, nil
|
|
}
|
|
|
|
func (c *Client) DeleteGroupFolder(ctx context.Context, folderID int) error {
|
|
path := fmt.Sprintf("/index.php/apps/groupfolders/folders/%d?format=json", folderID)
|
|
resp, err := c.doRequest(ctx, "DELETE", path, nil, ocsJSONHeaders())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return &HTTPStatusError{Operation: "delete groupfolder", StatusCode: resp.StatusCode}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) AssignGroupToFolder(ctx context.Context, folderID int, groupID string, permissions int) error {
|
|
if permissions <= 0 {
|
|
permissions = groupFolderAllPermissions
|
|
}
|
|
form := url.Values{}
|
|
form.Set("group", groupID)
|
|
path := fmt.Sprintf("/index.php/apps/groupfolders/folders/%d/groups?format=json", folderID)
|
|
resp, err := c.doRequest(ctx, "POST", path, strings.NewReader(form.Encode()), map[string]string{
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return &HTTPStatusError{Operation: "assign group to folder", StatusCode: resp.StatusCode}
|
|
}
|
|
|
|
formPerm := url.Values{}
|
|
formPerm.Set("permissions", strconv.Itoa(permissions))
|
|
pathPerm := fmt.Sprintf("/index.php/apps/groupfolders/folders/%d/groups/%s?format=json", folderID, url.PathEscape(groupID))
|
|
resp2, err := c.doRequest(ctx, "POST", pathPerm, strings.NewReader(formPerm.Encode()), map[string]string{
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp2.Body.Close()
|
|
if resp2.StatusCode != http.StatusOK {
|
|
return &HTTPStatusError{Operation: "set groupfolder permissions", StatusCode: resp2.StatusCode}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) SetGroupFolderQuota(ctx context.Context, folderID int, quotaBytes int64) error {
|
|
form := url.Values{}
|
|
form.Set("quota", strconv.FormatInt(quotaBytes, 10))
|
|
path := fmt.Sprintf("/index.php/apps/groupfolders/folders/%d/quota?format=json", folderID)
|
|
resp, err := c.doRequest(ctx, "POST", path, strings.NewReader(form.Encode()), map[string]string{
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return &HTTPStatusError{Operation: "set groupfolder quota", StatusCode: resp.StatusCode}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) RenameGroupFolder(ctx context.Context, folderID int, mountPoint string) error {
|
|
form := url.Values{}
|
|
form.Set("mountpoint", mountPoint)
|
|
path := fmt.Sprintf("/index.php/apps/groupfolders/folders/%d/mountpoint?format=json", folderID)
|
|
resp, err := c.doRequest(ctx, "POST", path, strings.NewReader(form.Encode()), map[string]string{
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return &HTTPStatusError{Operation: "rename groupfolder", StatusCode: resp.StatusCode}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) EnsureGroup(ctx context.Context, groupID string) error {
|
|
groupID = strings.TrimSpace(groupID)
|
|
if groupID == "" {
|
|
return fmt.Errorf("group id is empty")
|
|
}
|
|
exists, err := c.groupExists(ctx, groupID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if exists {
|
|
return nil
|
|
}
|
|
form := url.Values{}
|
|
form.Set("groupid", groupID)
|
|
resp, err := c.doRequest(ctx, "POST", "/ocs/v1.php/cloud/groups", strings.NewReader(form.Encode()), map[string]string{
|
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode != http.StatusOK {
|
|
return &HTTPStatusError{Operation: "create group", StatusCode: resp.StatusCode}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) groupExists(ctx context.Context, groupID string) (bool, error) {
|
|
path := "/ocs/v1.php/cloud/groups/" + url.PathEscape(groupID)
|
|
resp, err := c.doRequest(ctx, "GET", path, nil, ocsJSONHeaders())
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer resp.Body.Close()
|
|
switch resp.StatusCode {
|
|
case http.StatusOK:
|
|
return true, nil
|
|
case http.StatusNotFound:
|
|
return false, nil
|
|
default:
|
|
return false, &HTTPStatusError{Operation: "group exists", StatusCode: resp.StatusCode}
|
|
}
|
|
}
|
|
|
|
func OrgGroupID(orgSlug string) string {
|
|
slug := strings.TrimSpace(strings.ToLower(orgSlug))
|
|
slug = strings.ReplaceAll(slug, " ", "-")
|
|
return "org-" + slug
|
|
}
|