- 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.
257 lines
7.2 KiB
Go
257 lines
7.2 KiB
Go
package drive
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/ultisuite/ulti-backend/internal/drivestore"
|
|
"github.com/ultisuite/ulti-backend/internal/nextcloud"
|
|
)
|
|
|
|
type OrgFolderView struct {
|
|
ID string `json:"id"`
|
|
OrgSlug string `json:"org_slug"`
|
|
MountPoint string `json:"mount_point"`
|
|
NCFolderID int `json:"nc_folder_id"`
|
|
QuotaBytes *int64 `json:"quota_bytes,omitempty"`
|
|
AutoProvisioned bool `json:"auto_provisioned"`
|
|
Permissions int `json:"permissions,omitempty"`
|
|
Size int64 `json:"size,omitempty"`
|
|
}
|
|
|
|
func (s *Service) ListOrgFoldersForUser(ctx context.Context, ncUserID string) ([]OrgFolderView, error) {
|
|
store := s.ensureStore()
|
|
if store == nil {
|
|
return nil, fmt.Errorf("store not configured")
|
|
}
|
|
rows, err := store.ListOrgFolders(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ncFolders, err := s.nc.ListGroupFolders(ctx, ncUserID)
|
|
if err != nil {
|
|
return nil, mapDriveError(err)
|
|
}
|
|
accessible := make(map[int]nextcloud.GroupFolder, len(ncFolders))
|
|
for _, f := range ncFolders {
|
|
accessible[f.ID] = f
|
|
}
|
|
out := make([]OrgFolderView, 0, len(rows))
|
|
for _, row := range rows {
|
|
ncFolder, ok := accessible[row.NCFolderID]
|
|
if !ok {
|
|
continue
|
|
}
|
|
perms := 0
|
|
for _, p := range ncFolder.Groups {
|
|
if p > perms {
|
|
perms = p
|
|
}
|
|
}
|
|
out = append(out, OrgFolderView{
|
|
ID: row.ID,
|
|
OrgSlug: row.OrgSlug,
|
|
MountPoint: row.MountPoint,
|
|
NCFolderID: row.NCFolderID,
|
|
QuotaBytes: row.QuotaBytes,
|
|
AutoProvisioned: row.AutoProvisioned,
|
|
Permissions: perms,
|
|
Size: ncFolder.Size,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *Service) ListOrgFoldersAdmin(ctx context.Context) ([]OrgFolderView, error) {
|
|
store := s.ensureStore()
|
|
if store == nil {
|
|
return nil, fmt.Errorf("store not configured")
|
|
}
|
|
rows, err := store.ListOrgFolders(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]OrgFolderView, 0, len(rows))
|
|
for _, row := range rows {
|
|
out = append(out, OrgFolderView{
|
|
ID: row.ID,
|
|
OrgSlug: row.OrgSlug,
|
|
MountPoint: row.MountPoint,
|
|
NCFolderID: row.NCFolderID,
|
|
QuotaBytes: row.QuotaBytes,
|
|
AutoProvisioned: row.AutoProvisioned,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
type CreateOrgFolderParams struct {
|
|
OrgSlug string
|
|
MountPoint string
|
|
QuotaBytes *int64
|
|
AutoProvisioned bool
|
|
CreatedBy string
|
|
}
|
|
|
|
func (s *Service) CreateOrgFolder(ctx context.Context, p CreateOrgFolderParams) (OrgFolderView, error) {
|
|
store := s.ensureStore()
|
|
if store == nil {
|
|
return OrgFolderView{}, fmt.Errorf("store not configured")
|
|
}
|
|
orgSlug := strings.TrimSpace(strings.ToLower(p.OrgSlug))
|
|
mountPoint := strings.TrimSpace(p.MountPoint)
|
|
if orgSlug == "" || mountPoint == "" {
|
|
return OrgFolderView{}, ErrInvalid
|
|
}
|
|
if _, err := store.GetOrgFolderBySlug(ctx, orgSlug); err == nil {
|
|
return OrgFolderView{}, ErrConflict
|
|
} else if !errors.Is(err, drivestore.ErrOrgFolderNotFound) {
|
|
return OrgFolderView{}, err
|
|
}
|
|
|
|
groupID := nextcloud.OrgGroupID(orgSlug)
|
|
if err := s.nc.EnsureGroup(ctx, groupID); err != nil {
|
|
return OrgFolderView{}, mapDriveError(err)
|
|
}
|
|
ncFolderID, err := s.nc.CreateGroupFolder(ctx, mountPoint)
|
|
if err != nil {
|
|
return OrgFolderView{}, mapDriveError(err)
|
|
}
|
|
if err := s.nc.AssignGroupToFolder(ctx, ncFolderID, groupID, 31); err != nil {
|
|
_ = s.nc.DeleteGroupFolder(ctx, ncFolderID)
|
|
return OrgFolderView{}, mapDriveError(err)
|
|
}
|
|
if p.QuotaBytes != nil {
|
|
if err := s.nc.SetGroupFolderQuota(ctx, ncFolderID, *p.QuotaBytes); err != nil {
|
|
return OrgFolderView{}, mapDriveError(err)
|
|
}
|
|
}
|
|
row, err := store.CreateOrgFolder(ctx, drivestore.CreateOrgFolderParams{
|
|
OrgSlug: orgSlug,
|
|
NCFolderID: ncFolderID,
|
|
MountPoint: mountPoint,
|
|
QuotaBytes: p.QuotaBytes,
|
|
AutoProvisioned: p.AutoProvisioned,
|
|
CreatedBy: p.CreatedBy,
|
|
})
|
|
if err != nil {
|
|
_ = s.nc.DeleteGroupFolder(ctx, ncFolderID)
|
|
return OrgFolderView{}, err
|
|
}
|
|
return OrgFolderView{
|
|
ID: row.ID,
|
|
OrgSlug: row.OrgSlug,
|
|
MountPoint: row.MountPoint,
|
|
NCFolderID: row.NCFolderID,
|
|
QuotaBytes: row.QuotaBytes,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Service) ProvisionOrgFolder(ctx context.Context, orgSlug, createdBy string) (OrgFolderView, error) {
|
|
orgSlug = strings.TrimSpace(strings.ToLower(orgSlug))
|
|
if orgSlug == "" {
|
|
return OrgFolderView{}, ErrInvalid
|
|
}
|
|
store := s.ensureStore()
|
|
if store == nil {
|
|
return OrgFolderView{}, fmt.Errorf("store not configured")
|
|
}
|
|
if existing, err := store.GetOrgFolderBySlug(ctx, orgSlug); err == nil {
|
|
return OrgFolderView{
|
|
ID: existing.ID,
|
|
OrgSlug: existing.OrgSlug,
|
|
MountPoint: existing.MountPoint,
|
|
NCFolderID: existing.NCFolderID,
|
|
QuotaBytes: existing.QuotaBytes,
|
|
AutoProvisioned: existing.AutoProvisioned,
|
|
}, nil
|
|
} else if !errors.Is(err, drivestore.ErrOrgFolderNotFound) {
|
|
return OrgFolderView{}, err
|
|
}
|
|
mountPoint := orgSlug
|
|
if mountPoint == "" {
|
|
mountPoint = "org"
|
|
}
|
|
return s.createOrgFolderInternal(ctx, orgSlug, mountPoint, nil, true, createdBy)
|
|
}
|
|
|
|
func (s *Service) createOrgFolderInternal(ctx context.Context, orgSlug, mountPoint string, quota *int64, auto bool, createdBy string) (OrgFolderView, error) {
|
|
view, err := s.CreateOrgFolder(ctx, CreateOrgFolderParams{
|
|
OrgSlug: orgSlug,
|
|
MountPoint: mountPoint,
|
|
QuotaBytes: quota,
|
|
AutoProvisioned: auto,
|
|
CreatedBy: createdBy,
|
|
})
|
|
if err != nil {
|
|
return OrgFolderView{}, err
|
|
}
|
|
view.AutoProvisioned = auto
|
|
return view, nil
|
|
}
|
|
|
|
func (s *Service) UpdateOrgFolder(ctx context.Context, id, mountPoint string, quotaBytes *int64) (OrgFolderView, error) {
|
|
store := s.ensureStore()
|
|
if store == nil {
|
|
return OrgFolderView{}, fmt.Errorf("store not configured")
|
|
}
|
|
row, err := store.GetOrgFolder(ctx, id)
|
|
if err != nil {
|
|
return OrgFolderView{}, err
|
|
}
|
|
if mountPoint != "" && mountPoint != row.MountPoint {
|
|
if err := s.nc.RenameGroupFolder(ctx, row.NCFolderID, mountPoint); err != nil {
|
|
return OrgFolderView{}, mapDriveError(err)
|
|
}
|
|
}
|
|
if quotaBytes != nil {
|
|
if err := s.nc.SetGroupFolderQuota(ctx, row.NCFolderID, *quotaBytes); err != nil {
|
|
return OrgFolderView{}, mapDriveError(err)
|
|
}
|
|
}
|
|
updated, err := store.UpdateOrgFolder(ctx, id, mountPoint, quotaBytes)
|
|
if err != nil {
|
|
return OrgFolderView{}, err
|
|
}
|
|
return OrgFolderView{
|
|
ID: updated.ID,
|
|
OrgSlug: updated.OrgSlug,
|
|
MountPoint: updated.MountPoint,
|
|
NCFolderID: updated.NCFolderID,
|
|
QuotaBytes: updated.QuotaBytes,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Service) DeleteOrgFolder(ctx context.Context, id string) error {
|
|
store := s.ensureStore()
|
|
if store == nil {
|
|
return fmt.Errorf("store not configured")
|
|
}
|
|
row, err := store.GetOrgFolder(ctx, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := s.nc.DeleteGroupFolder(ctx, row.NCFolderID); err != nil {
|
|
return mapDriveError(err)
|
|
}
|
|
return store.DeleteOrgFolder(ctx, id)
|
|
}
|
|
|
|
func (s *Service) SyncOrgFolders(ctx context.Context, orgSlugs []string, createdBy string) ([]OrgFolderView, error) {
|
|
out := make([]OrgFolderView, 0, len(orgSlugs))
|
|
for _, slug := range orgSlugs {
|
|
slug = strings.TrimSpace(strings.ToLower(slug))
|
|
if slug == "" {
|
|
continue
|
|
}
|
|
view, err := s.ProvisionOrgFolder(ctx, slug, createdBy)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, view)
|
|
}
|
|
return out, nil
|
|
}
|