From 58e48a449e75ed3434043d19a073182bb691f5f7 Mon Sep 17 00:00:00 2001 From: Jon_K Date: Sat, 3 Jan 2026 02:27:20 -0500 Subject: [PATCH] Add quota changes to frontend usage bar --- frontend/src/components/Sidebar.vue | 40 +++++++++++++++++++++++++++-- http/auth.go | 21 +++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Sidebar.vue b/frontend/src/components/Sidebar.vue index c7961e3e..a56c5c4f 100644 --- a/frontend/src/components/Sidebar.vue +++ b/frontend/src/components/Sidebar.vue @@ -90,9 +90,9 @@ v-if="isFiles && !disableUsedPercentage" style="width: 90%; margin: 2em 2.5em 3em 2.5em" > - +
- {{ usage.used }} of {{ usage.total }} used + {{ usedFormatted }} of {{ totalFormatted }} used

@@ -161,6 +161,37 @@ export default { disableExternal: () => disableExternal, disableUsedPercentage: () => disableUsedPercentage, canLogout: () => !noAuth && (loginPage || logoutPage !== "/login"), + hasQuotaData() { + return this.user?.quotaLimit && this.user.quotaLimit > 0; + }, + quotaLimitBytes() { + // QuotaLimit is already stored in bytes in the backend + return this.user?.quotaLimit || 0; + }, + quotaUsedBytes() { + // QuotaUsed is already in bytes + return this.user?.quotaUsed || 0; + }, + // Unified properties that use quota if available, otherwise disk usage + usagePercentage() { + if (this.hasQuotaData) { + if (this.quotaLimitBytes === 0) return 0; + return Math.min(Math.round((this.quotaUsedBytes / this.quotaLimitBytes) * 100), 100); + } + return this.usage.usedPercentage; + }, + usedFormatted() { + if (this.hasQuotaData) { + return prettyBytes(this.quotaUsedBytes, { binary: true }); + } + return this.usage.used; + }, + totalFormatted() { + if (this.hasQuotaData) { + return prettyBytes(this.quotaLimitBytes, { binary: true }); + } + return this.usage.total; + }, }, methods: { ...mapActions(useLayoutStore, ["closeHovers", "showHover"]), @@ -168,6 +199,11 @@ export default { this.usageAbortController.abort(); }, async fetchUsage() { + // If user has quota, don't fetch disk usage + if (this.hasQuotaData) { + return; + } + const path = this.$route.path.endsWith("/") ? this.$route.path : this.$route.path + "/"; diff --git a/http/auth.go b/http/auth.go index 5a483a8d..95e32da0 100644 --- a/http/auth.go +++ b/http/auth.go @@ -34,6 +34,10 @@ type userInfo struct { DateFormat bool `json:"dateFormat"` Username string `json:"username"` AceEditorTheme string `json:"aceEditorTheme"` + QuotaLimit uint64 `json:"quotaLimit"` + QuotaUnit string `json:"quotaUnit"` + EnforceQuota bool `json:"enforceQuota"` + QuotaUsed uint64 `json:"quotaUsed"` } type authToken struct { @@ -202,6 +206,19 @@ func renewHandler(tokenExpireTime time.Duration) handleFunc { } func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User, tokenExpirationTime time.Duration) (int, error) { + // Calculate current quota usage if quota is enabled + var quotaUsed uint64 + if user.QuotaLimit > 0 { + used, err := users.CalculateUserQuota(user.Fs, user.Scope) + if err != nil { + // Log error but don't fail login - just set usage to 0 + log.Printf("Failed to calculate quota for user %s: %v", user.Username, err) + quotaUsed = 0 + } else { + quotaUsed = used + } + } + claims := &authToken{ User: userInfo{ ID: user.ID, @@ -215,6 +232,10 @@ func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.Use DateFormat: user.DateFormat, Username: user.Username, AceEditorTheme: user.AceEditorTheme, + QuotaLimit: user.QuotaLimit, + QuotaUnit: user.QuotaUnit, + EnforceQuota: user.EnforceQuota, + QuotaUsed: quotaUsed, }, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(time.Now()),