- 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.
244 lines
7.3 KiB
Go
244 lines
7.3 KiB
Go
package drive
|
|
|
|
import (
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
|
|
"github.com/ultisuite/ulti-backend/internal/nextcloud"
|
|
)
|
|
|
|
const maxJSONRequestBody = 32 << 10
|
|
|
|
type moveRequest struct {
|
|
Source string `json:"source"`
|
|
Destination string `json:"destination"`
|
|
SourceRoot string `json:"source_root,omitempty"`
|
|
SourceRootID string `json:"source_root_id,omitempty"`
|
|
DestinationRoot string `json:"destination_root,omitempty"`
|
|
DestinationRootID string `json:"destination_root_id,omitempty"`
|
|
}
|
|
|
|
type copyRequest struct {
|
|
Source string `json:"source"`
|
|
Destination string `json:"destination"`
|
|
SourceRoot string `json:"source_root,omitempty"`
|
|
SourceRootID string `json:"source_root_id,omitempty"`
|
|
DestinationRoot string `json:"destination_root,omitempty"`
|
|
DestinationRootID string `json:"destination_root_id,omitempty"`
|
|
}
|
|
|
|
type renameRequest struct {
|
|
Path string `json:"path"`
|
|
NewName string `json:"new_name"`
|
|
Root string `json:"root,omitempty"`
|
|
RootID string `json:"root_id,omitempty"`
|
|
}
|
|
|
|
type favoriteRequest struct {
|
|
Path string `json:"path"`
|
|
Favorite bool `json:"favorite"`
|
|
Root string `json:"root,omitempty"`
|
|
RootID string `json:"root_id,omitempty"`
|
|
}
|
|
|
|
type createMountRequest struct {
|
|
Scope string `json:"scope"`
|
|
OrgSlug string `json:"org_slug,omitempty"`
|
|
DisplayName string `json:"display_name"`
|
|
BackendType string `json:"backend_type"`
|
|
WebDAV *nextcloud.WebDAVMountConfig `json:"webdav,omitempty"`
|
|
OAuthBackend string `json:"oauth_backend,omitempty"`
|
|
OAuthAuth string `json:"oauth_auth,omitempty"`
|
|
}
|
|
|
|
func validateMoveRequest(req *moveRequest) *apivalidate.ValidationError {
|
|
var details []apivalidate.FieldDetail
|
|
if strings.TrimSpace(req.Source) == "" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "source", Message: "required"})
|
|
}
|
|
if strings.TrimSpace(req.Destination) == "" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "destination", Message: "required"})
|
|
}
|
|
if len(details) == 0 {
|
|
return nil
|
|
}
|
|
return apivalidate.NewValidationError(details...)
|
|
}
|
|
|
|
func validateCopyRequest(req *copyRequest) *apivalidate.ValidationError {
|
|
var details []apivalidate.FieldDetail
|
|
if strings.TrimSpace(req.Source) == "" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "source", Message: "required"})
|
|
}
|
|
if strings.TrimSpace(req.Destination) == "" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "destination", Message: "required"})
|
|
}
|
|
if len(details) == 0 {
|
|
return nil
|
|
}
|
|
return apivalidate.NewValidationError(details...)
|
|
}
|
|
|
|
func validateRenameRequest(req *renameRequest) *apivalidate.ValidationError {
|
|
var details []apivalidate.FieldDetail
|
|
if strings.TrimSpace(req.Path) == "" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "path", Message: "required"})
|
|
}
|
|
newName := strings.TrimSpace(req.NewName)
|
|
if newName == "" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "new_name", Message: "required"})
|
|
} else if strings.Contains(newName, "/") {
|
|
details = append(details, apivalidate.FieldDetail{Field: "new_name", Message: "invalid"})
|
|
}
|
|
if len(details) == 0 {
|
|
return nil
|
|
}
|
|
return apivalidate.NewValidationError(details...)
|
|
}
|
|
|
|
type createShareRequest struct {
|
|
Path string `json:"path"`
|
|
ShareType int `json:"share_type"`
|
|
Permissions int `json:"permissions"`
|
|
Role string `json:"role"`
|
|
Mode string `json:"mode"`
|
|
ShareWith string `json:"share_with"`
|
|
Note string `json:"note"`
|
|
SendMail *bool `json:"send_mail"`
|
|
Root string `json:"root,omitempty"`
|
|
RootID string `json:"root_id,omitempty"`
|
|
}
|
|
|
|
func sharePermissionsForRole(role string) (int, bool) {
|
|
switch strings.TrimSpace(strings.ToLower(role)) {
|
|
case "owner":
|
|
return 31, true
|
|
case "editor":
|
|
return 15, true
|
|
case "viewer":
|
|
return 1, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
func validateCreateShareRequest(req *createShareRequest) *apivalidate.ValidationError {
|
|
var details []apivalidate.FieldDetail
|
|
if strings.TrimSpace(req.Path) == "" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "path", Message: "required"})
|
|
}
|
|
if role := strings.TrimSpace(req.Role); role != "" {
|
|
if _, ok := sharePermissionsForRole(role); !ok {
|
|
details = append(details, apivalidate.FieldDetail{Field: "role", Message: "invalid"})
|
|
}
|
|
}
|
|
mode := strings.TrimSpace(strings.ToLower(req.Mode))
|
|
switch mode {
|
|
case "", "public", "internal":
|
|
case "contact":
|
|
if strings.TrimSpace(req.ShareWith) == "" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "share_with", Message: "required"})
|
|
}
|
|
default:
|
|
details = append(details, apivalidate.FieldDetail{Field: "mode", Message: "invalid"})
|
|
}
|
|
if len(details) == 0 {
|
|
return nil
|
|
}
|
|
return apivalidate.NewValidationError(details...)
|
|
}
|
|
|
|
type restoreTrashRequest struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
func validateRestoreTrashRequest(req *restoreTrashRequest) *apivalidate.ValidationError {
|
|
if strings.TrimSpace(req.Name) == "" {
|
|
return apivalidate.NewValidationError(apivalidate.FieldDetail{
|
|
Field: "name", Message: "required",
|
|
})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type deleteTrashRequest struct {
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
func validateDeleteTrashRequest(req *deleteTrashRequest) *apivalidate.ValidationError {
|
|
if strings.TrimSpace(req.Name) == "" {
|
|
return apivalidate.NewValidationError(apivalidate.FieldDetail{
|
|
Field: "name", Message: "required",
|
|
})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateFavoriteRequest(req *favoriteRequest) *apivalidate.ValidationError {
|
|
if strings.TrimSpace(req.Path) == "" {
|
|
return apivalidate.NewValidationError(apivalidate.FieldDetail{
|
|
Field: "path", Message: "required",
|
|
})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type updateShareRequest struct {
|
|
Permissions int `json:"permissions"`
|
|
Role string `json:"role"`
|
|
ExpireDate string `json:"expire_date"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
type newFileRequest struct {
|
|
ParentPath string `json:"parent_path"`
|
|
Name string `json:"name"`
|
|
Kind string `json:"kind"`
|
|
}
|
|
|
|
func validateNewFileRequest(req *newFileRequest) *apivalidate.ValidationError {
|
|
var details []apivalidate.FieldDetail
|
|
if strings.TrimSpace(req.Name) == "" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "name", Message: "required"})
|
|
}
|
|
k := strings.TrimSpace(strings.ToLower(req.Kind))
|
|
if k != "document" && k != "spreadsheet" && k != "presentation" && k != "drawing" {
|
|
details = append(details, apivalidate.FieldDetail{Field: "kind", Message: "invalid"})
|
|
}
|
|
if len(details) > 0 {
|
|
return apivalidate.NewValidationError(details...)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validatePath(path string) *apivalidate.ValidationError {
|
|
if strings.TrimSpace(path) == "" {
|
|
return apivalidate.NewValidationError(apivalidate.FieldDetail{
|
|
Field: "path", Message: "required",
|
|
})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const mountOAuthCallbackPath = "/drive/mounts/oauth/callback"
|
|
|
|
func validateMountOAuthRedirectURI(raw string) error {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return ErrInvalid
|
|
}
|
|
parsed, err := url.Parse(raw)
|
|
if err != nil || parsed.Scheme == "" || parsed.Host == "" {
|
|
return ErrInvalid
|
|
}
|
|
if parsed.Scheme != "http" && parsed.Scheme != "https" {
|
|
return ErrInvalid
|
|
}
|
|
path := strings.TrimRight(parsed.Path, "/")
|
|
if path != mountOAuthCallbackPath {
|
|
return ErrInvalid
|
|
}
|
|
return nil
|
|
}
|