- Updated .env.example to include new configuration options for the UltiAI branding and API endpoints. - Enhanced Nginx configuration to support new API routes for the MCP and WebSocket connections. - Introduced sub-filters for branding adjustments in Nginx responses. - Added new JavaScript patch for API endpoint adjustments. - Implemented tests for new API functionalities and improved error handling in the AI gateway.
374 lines
10 KiB
Go
374 lines
10 KiB
Go
package drivestore
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
var ErrOrgFolderNotFound = errors.New("org folder not found")
|
|
var ErrMountNotFound = errors.New("mount not found")
|
|
|
|
type OrgFolder struct {
|
|
ID string `json:"id"`
|
|
OrgSlug string `json:"org_slug"`
|
|
NCFolderID int `json:"nc_folder_id"`
|
|
MountPoint string `json:"mount_point"`
|
|
QuotaBytes *int64 `json:"quota_bytes,omitempty"`
|
|
AutoProvisioned bool `json:"auto_provisioned"`
|
|
CreatedBy string `json:"created_by"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
type Mount struct {
|
|
ID string `json:"id"`
|
|
Scope string `json:"scope"`
|
|
OwnerUserID *string `json:"owner_user_id,omitempty"`
|
|
OrgSlug *string `json:"org_slug,omitempty"`
|
|
NCMountID *int `json:"nc_mount_id,omitempty"`
|
|
DisplayName string `json:"display_name"`
|
|
BackendType string `json:"backend_type"`
|
|
MountPoint string `json:"mount_point"`
|
|
Status string `json:"status"`
|
|
LastError string `json:"last_error,omitempty"`
|
|
ConfigEnc []byte `json:"-"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
}
|
|
|
|
type Store struct {
|
|
db *pgxpool.Pool
|
|
}
|
|
|
|
func NewStore(db *pgxpool.Pool) *Store {
|
|
return &Store{db: db}
|
|
}
|
|
|
|
func (s *Store) ListOrgFolders(ctx context.Context) ([]OrgFolder, error) {
|
|
if s.db == nil {
|
|
return nil, fmt.Errorf("database not configured")
|
|
}
|
|
rows, err := s.db.Query(ctx, `
|
|
SELECT id, org_slug, nc_folder_id, mount_point, quota_bytes,
|
|
auto_provisioned, created_by, created_at, updated_at
|
|
FROM drive_org_folders
|
|
ORDER BY mount_point ASC
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []OrgFolder
|
|
for rows.Next() {
|
|
var item OrgFolder
|
|
if err := rows.Scan(
|
|
&item.ID, &item.OrgSlug, &item.NCFolderID, &item.MountPoint,
|
|
&item.QuotaBytes, &item.AutoProvisioned, &item.CreatedBy,
|
|
&item.CreatedAt, &item.UpdatedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, item)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func (s *Store) GetOrgFolder(ctx context.Context, id string) (OrgFolder, error) {
|
|
if s.db == nil {
|
|
return OrgFolder{}, fmt.Errorf("database not configured")
|
|
}
|
|
var item OrgFolder
|
|
err := s.db.QueryRow(ctx, `
|
|
SELECT id, org_slug, nc_folder_id, mount_point, quota_bytes,
|
|
auto_provisioned, created_by, created_at, updated_at
|
|
FROM drive_org_folders WHERE id = $1
|
|
`, id).Scan(
|
|
&item.ID, &item.OrgSlug, &item.NCFolderID, &item.MountPoint,
|
|
&item.QuotaBytes, &item.AutoProvisioned, &item.CreatedBy,
|
|
&item.CreatedAt, &item.UpdatedAt,
|
|
)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return OrgFolder{}, ErrOrgFolderNotFound
|
|
}
|
|
if err != nil {
|
|
return OrgFolder{}, err
|
|
}
|
|
return item, nil
|
|
}
|
|
|
|
func (s *Store) GetOrgFolderBySlug(ctx context.Context, orgSlug string) (OrgFolder, error) {
|
|
if s.db == nil {
|
|
return OrgFolder{}, fmt.Errorf("database not configured")
|
|
}
|
|
var item OrgFolder
|
|
err := s.db.QueryRow(ctx, `
|
|
SELECT id, org_slug, nc_folder_id, mount_point, quota_bytes,
|
|
auto_provisioned, created_by, created_at, updated_at
|
|
FROM drive_org_folders WHERE org_slug = $1
|
|
`, orgSlug).Scan(
|
|
&item.ID, &item.OrgSlug, &item.NCFolderID, &item.MountPoint,
|
|
&item.QuotaBytes, &item.AutoProvisioned, &item.CreatedBy,
|
|
&item.CreatedAt, &item.UpdatedAt,
|
|
)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return OrgFolder{}, ErrOrgFolderNotFound
|
|
}
|
|
if err != nil {
|
|
return OrgFolder{}, err
|
|
}
|
|
return item, nil
|
|
}
|
|
|
|
type CreateOrgFolderParams struct {
|
|
OrgSlug string
|
|
NCFolderID int
|
|
MountPoint string
|
|
QuotaBytes *int64
|
|
AutoProvisioned bool
|
|
CreatedBy string
|
|
}
|
|
|
|
func (s *Store) CreateOrgFolder(ctx context.Context, p CreateOrgFolderParams) (OrgFolder, error) {
|
|
if s.db == nil {
|
|
return OrgFolder{}, fmt.Errorf("database not configured")
|
|
}
|
|
id := uuid.NewString()
|
|
var item OrgFolder
|
|
err := s.db.QueryRow(ctx, `
|
|
INSERT INTO drive_org_folders (
|
|
id, org_slug, nc_folder_id, mount_point, quota_bytes,
|
|
auto_provisioned, created_by
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
RETURNING id, org_slug, nc_folder_id, mount_point, quota_bytes,
|
|
auto_provisioned, created_by, created_at, updated_at
|
|
`, id, p.OrgSlug, p.NCFolderID, p.MountPoint, p.QuotaBytes, p.AutoProvisioned, p.CreatedBy).Scan(
|
|
&item.ID, &item.OrgSlug, &item.NCFolderID, &item.MountPoint,
|
|
&item.QuotaBytes, &item.AutoProvisioned, &item.CreatedBy,
|
|
&item.CreatedAt, &item.UpdatedAt,
|
|
)
|
|
if err != nil {
|
|
return OrgFolder{}, err
|
|
}
|
|
return item, nil
|
|
}
|
|
|
|
func (s *Store) UpdateOrgFolder(ctx context.Context, id, mountPoint string, quotaBytes *int64) (OrgFolder, error) {
|
|
if s.db == nil {
|
|
return OrgFolder{}, fmt.Errorf("database not configured")
|
|
}
|
|
var item OrgFolder
|
|
err := s.db.QueryRow(ctx, `
|
|
UPDATE drive_org_folders
|
|
SET mount_point = $2, quota_bytes = $3, updated_at = NOW()
|
|
WHERE id = $1
|
|
RETURNING id, org_slug, nc_folder_id, mount_point, quota_bytes,
|
|
auto_provisioned, created_by, created_at, updated_at
|
|
`, id, mountPoint, quotaBytes).Scan(
|
|
&item.ID, &item.OrgSlug, &item.NCFolderID, &item.MountPoint,
|
|
&item.QuotaBytes, &item.AutoProvisioned, &item.CreatedBy,
|
|
&item.CreatedAt, &item.UpdatedAt,
|
|
)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return OrgFolder{}, ErrOrgFolderNotFound
|
|
}
|
|
if err != nil {
|
|
return OrgFolder{}, err
|
|
}
|
|
return item, nil
|
|
}
|
|
|
|
func (s *Store) DeleteOrgFolder(ctx context.Context, id string) error {
|
|
if s.db == nil {
|
|
return fmt.Errorf("database not configured")
|
|
}
|
|
tag, err := s.db.Exec(ctx, `DELETE FROM drive_org_folders WHERE id = $1`, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return ErrOrgFolderNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Store) ListOrgMounts(ctx context.Context) ([]Mount, error) {
|
|
if s.db == nil {
|
|
return nil, fmt.Errorf("database not configured")
|
|
}
|
|
rows, err := s.db.Query(ctx, `
|
|
SELECT id, scope, owner_user_id, org_slug, nc_mount_id, display_name,
|
|
backend_type, mount_point, status, last_error, created_at, updated_at
|
|
FROM drive_mounts
|
|
WHERE scope = 'org'
|
|
ORDER BY display_name ASC
|
|
`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
return scanMounts(rows)
|
|
}
|
|
|
|
func (s *Store) ListMountsForUser(ctx context.Context, ownerUserID string, orgSlugs []string) ([]Mount, error) {
|
|
if s.db == nil {
|
|
return nil, fmt.Errorf("database not configured")
|
|
}
|
|
rows, err := s.db.Query(ctx, `
|
|
SELECT id, scope, owner_user_id, org_slug, nc_mount_id, display_name,
|
|
backend_type, mount_point, status, last_error, created_at, updated_at
|
|
FROM drive_mounts
|
|
WHERE (scope = 'user' AND owner_user_id = $1::uuid)
|
|
OR (
|
|
scope = 'org'
|
|
AND (cardinality($2::text[]) = 0 OR org_slug = ANY($2))
|
|
)
|
|
ORDER BY display_name ASC
|
|
`, ownerUserID, orgSlugs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
return scanMounts(rows)
|
|
}
|
|
|
|
func (s *Store) GetMount(ctx context.Context, id string) (Mount, error) {
|
|
if s.db == nil {
|
|
return Mount{}, fmt.Errorf("database not configured")
|
|
}
|
|
row := s.db.QueryRow(ctx, `
|
|
SELECT id, scope, owner_user_id, org_slug, nc_mount_id, display_name,
|
|
backend_type, mount_point, status, last_error, created_at, updated_at
|
|
FROM drive_mounts WHERE id = $1
|
|
`, id)
|
|
item, err := scanMount(row)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return Mount{}, ErrMountNotFound
|
|
}
|
|
return item, err
|
|
}
|
|
|
|
type CreateMountParams struct {
|
|
Scope string
|
|
OwnerUserID *string
|
|
OrgSlug *string
|
|
NCMountID *int
|
|
DisplayName string
|
|
BackendType string
|
|
MountPoint string
|
|
Status string
|
|
ConfigEnc []byte
|
|
}
|
|
|
|
func (s *Store) CreateMount(ctx context.Context, p CreateMountParams) (Mount, error) {
|
|
if s.db == nil {
|
|
return Mount{}, fmt.Errorf("database not configured")
|
|
}
|
|
id := uuid.NewString()
|
|
status := p.Status
|
|
if status == "" {
|
|
status = "active"
|
|
}
|
|
row := s.db.QueryRow(ctx, `
|
|
INSERT INTO drive_mounts (
|
|
id, scope, owner_user_id, org_slug, nc_mount_id, display_name,
|
|
backend_type, mount_point, status, config_encrypted
|
|
) VALUES ($1, $2, $3::uuid, $4, $5, $6, $7, $8, $9, $10)
|
|
RETURNING id, scope, owner_user_id, org_slug, nc_mount_id, display_name,
|
|
backend_type, mount_point, status, last_error, created_at, updated_at
|
|
`, id, p.Scope, p.OwnerUserID, p.OrgSlug, p.NCMountID, p.DisplayName,
|
|
p.BackendType, p.MountPoint, status, p.ConfigEnc)
|
|
return scanMount(row)
|
|
}
|
|
|
|
func (s *Store) UpdateMountConfig(ctx context.Context, id string, configEnc []byte) error {
|
|
if s.db == nil {
|
|
return fmt.Errorf("database not configured")
|
|
}
|
|
tag, err := s.db.Exec(ctx, `
|
|
UPDATE drive_mounts SET config_encrypted = $2, updated_at = NOW() WHERE id = $1
|
|
`, id, configEnc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return ErrMountNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Store) GetMountConfig(ctx context.Context, id string) ([]byte, error) {
|
|
if s.db == nil {
|
|
return nil, fmt.Errorf("database not configured")
|
|
}
|
|
var config []byte
|
|
err := s.db.QueryRow(ctx, `SELECT config_encrypted FROM drive_mounts WHERE id = $1`, id).Scan(&config)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, ErrMountNotFound
|
|
}
|
|
return config, err
|
|
}
|
|
|
|
func (s *Store) UpdateMountStatus(ctx context.Context, id, status, lastError string, ncMountID *int) error {
|
|
if s.db == nil {
|
|
return fmt.Errorf("database not configured")
|
|
}
|
|
tag, err := s.db.Exec(ctx, `
|
|
UPDATE drive_mounts
|
|
SET status = $2, last_error = $3, nc_mount_id = COALESCE($4, nc_mount_id), updated_at = NOW()
|
|
WHERE id = $1
|
|
`, id, status, lastError, ncMountID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return ErrMountNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Store) DeleteMount(ctx context.Context, id string) error {
|
|
if s.db == nil {
|
|
return fmt.Errorf("database not configured")
|
|
}
|
|
tag, err := s.db.Exec(ctx, `DELETE FROM drive_mounts WHERE id = $1`, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return ErrMountNotFound
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type scannable interface {
|
|
Scan(dest ...any) error
|
|
}
|
|
|
|
func scanMount(row scannable) (Mount, error) {
|
|
var item Mount
|
|
err := row.Scan(
|
|
&item.ID, &item.Scope, &item.OwnerUserID, &item.OrgSlug, &item.NCMountID,
|
|
&item.DisplayName, &item.BackendType, &item.MountPoint, &item.Status,
|
|
&item.LastError, &item.CreatedAt, &item.UpdatedAt,
|
|
)
|
|
return item, err
|
|
}
|
|
|
|
func scanMounts(rows pgx.Rows) ([]Mount, error) {
|
|
var out []Mount
|
|
for rows.Next() {
|
|
item, err := scanMount(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, item)
|
|
}
|
|
return out, rows.Err()
|
|
}
|