Introduces comprehensive quota management with real-time usage tracking, frontend components, backend API endpoints, and TUS upload integration for storage limit enforcement.
124 lines
3.3 KiB
Go
124 lines
3.3 KiB
Go
package users
|
|
|
|
import (
|
|
"path/filepath"
|
|
|
|
"github.com/spf13/afero"
|
|
|
|
fberrors "github.com/filebrowser/filebrowser/v2/errors"
|
|
"github.com/filebrowser/filebrowser/v2/files"
|
|
"github.com/filebrowser/filebrowser/v2/rules"
|
|
)
|
|
|
|
// ViewMode describes a view mode.
|
|
type ViewMode string
|
|
|
|
const (
|
|
ListViewMode ViewMode = "list"
|
|
MosaicViewMode ViewMode = "mosaic"
|
|
)
|
|
|
|
// User describes a user.
|
|
type User struct {
|
|
ID uint `storm:"id,increment" json:"id"`
|
|
Username string `storm:"unique" json:"username"`
|
|
Password string `json:"password"`
|
|
Scope string `json:"scope"`
|
|
Locale string `json:"locale"`
|
|
LockPassword bool `json:"lockPassword"`
|
|
ViewMode ViewMode `json:"viewMode"`
|
|
SingleClick bool `json:"singleClick"`
|
|
Perm Permissions `json:"perm"`
|
|
Commands []string `json:"commands"`
|
|
Sorting files.Sorting `json:"sorting"`
|
|
Fs afero.Fs `json:"-" yaml:"-"`
|
|
Rules []rules.Rule `json:"rules"`
|
|
HideDotfiles bool `json:"hideDotfiles"`
|
|
DateFormat bool `json:"dateFormat"`
|
|
AceEditorTheme string `json:"aceEditorTheme"`
|
|
QuotaLimit uint64 `json:"quotaLimit"` // Quota limit in bytes (0 = unlimited)
|
|
QuotaUnit string `json:"quotaUnit"` // "KB", "MB", "GB" or "TB" for UI display
|
|
EnforceQuota bool `json:"enforceQuota"` // Hard limit if true, soft limit if false
|
|
QuotaUsed uint64 `json:"quotaUsed"` // Current usage in bytes (calculated, not stored)
|
|
}
|
|
|
|
// GetRules implements rules.Provider.
|
|
func (u *User) GetRules() []rules.Rule {
|
|
return u.Rules
|
|
}
|
|
|
|
var checkableFields = []string{
|
|
"Username",
|
|
"Password",
|
|
"Scope",
|
|
"ViewMode",
|
|
"Commands",
|
|
"Sorting",
|
|
"Rules",
|
|
"QuotaLimit",
|
|
"QuotaUnit",
|
|
"EnforceQuota",
|
|
}
|
|
|
|
// Clean cleans up a user and verifies if all its fields
|
|
// are alright to be saved.
|
|
func (u *User) Clean(baseScope string, fields ...string) error {
|
|
if len(fields) == 0 {
|
|
fields = checkableFields
|
|
}
|
|
|
|
for _, field := range fields {
|
|
switch field {
|
|
case "Username":
|
|
if u.Username == "" {
|
|
return fberrors.ErrEmptyUsername
|
|
}
|
|
case "Password":
|
|
if u.Password == "" {
|
|
return fberrors.ErrEmptyPassword
|
|
}
|
|
case "ViewMode":
|
|
if u.ViewMode == "" {
|
|
u.ViewMode = ListViewMode
|
|
}
|
|
case "Commands":
|
|
if u.Commands == nil {
|
|
u.Commands = []string{}
|
|
}
|
|
case "Sorting":
|
|
if u.Sorting.By == "" {
|
|
u.Sorting.By = "name"
|
|
}
|
|
case "Rules":
|
|
if u.Rules == nil {
|
|
u.Rules = []rules.Rule{}
|
|
}
|
|
case "QuotaUnit":
|
|
if u.QuotaUnit == "" {
|
|
u.QuotaUnit = "GB"
|
|
}
|
|
if u.QuotaUnit != "KB" && u.QuotaUnit != "MB" && u.QuotaUnit != "GB" && u.QuotaUnit != "TB" {
|
|
return fberrors.ErrInvalidQuotaUnit
|
|
}
|
|
case "QuotaLimit":
|
|
// QuotaLimit of 0 means unlimited, which is valid
|
|
// No validation needed
|
|
case "EnforceQuota":
|
|
// Boolean field, no validation needed
|
|
}
|
|
}
|
|
|
|
if u.Fs == nil {
|
|
scope := u.Scope
|
|
scope = filepath.Join(baseScope, filepath.Join("/", scope))
|
|
u.Fs = afero.NewBasePathFs(afero.NewOsFs(), scope)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// FullPath gets the full path for a user's relative path.
|
|
func (u *User) FullPath(path string) string {
|
|
return afero.FullBaseFsPath(u.Fs.(*afero.BasePathFs), path)
|
|
}
|