diff --git a/cmd/users.go b/cmd/users.go
index df535c97..f14e3708 100644
--- a/cmd/users.go
+++ b/cmd/users.go
@@ -75,6 +75,7 @@ func addUserFlags(flags *pflag.FlagSet) {
flags.StringSlice("commands", nil, "a list of the commands a user can execute")
flags.String("scope", ".", "scope for users")
flags.String("trashDir", "", "trash directory path for users")
+ flags.String("quotaFile", "", "path to file with quota data")
flags.String("locale", "en", "locale for users")
flags.String("viewMode", string(users.ListViewMode), "view mode for users")
flags.Bool("singleClick", false, "use single clicks only")
@@ -96,6 +97,8 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all
defaults.Scope = mustGetString(flags, flag.Name)
case "trashDir":
defaults.TrashDir = mustGetString(flags, flag.Name)
+ case "quotaFile":
+ defaults.QuotaFile = mustGetString(flags, flag.Name)
case "locale":
defaults.Locale = mustGetString(flags, flag.Name)
case "viewMode":
diff --git a/frontend/src/api/users.js b/frontend/src/api/users.js
index 7975d66a..1f12c503 100644
--- a/frontend/src/api/users.js
+++ b/frontend/src/api/users.js
@@ -49,3 +49,9 @@ export async function remove(id) {
throw new Error(res.status);
}
}
+
+export async function getQuota() {
+ return await fetchJSON(`/api/quota`, {
+ method: "GET",
+ });
+}
diff --git a/frontend/src/components/Quota.vue b/frontend/src/components/Quota.vue
new file mode 100644
index 00000000..647d1f07
--- /dev/null
+++ b/frontend/src/components/Quota.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/Sidebar.vue b/frontend/src/components/Sidebar.vue
index 5e607a9d..40c1eda6 100644
--- a/frontend/src/components/Sidebar.vue
+++ b/frontend/src/components/Sidebar.vue
@@ -45,6 +45,8 @@
+
+
import { mapState, mapGetters } from "vuex";
import * as auth from "@/utils/auth";
+import Quota from "@/components/Quota";
import {
version,
signup,
disableExternal,
trashDir,
+ quotaExists,
noAuth,
authMethod,
authLogoutURL,
@@ -129,6 +133,7 @@ import {
export default {
name: "sidebar",
+ components: { Quota },
computed: {
...mapState(["user"]),
...mapGetters(["isLogged"]),
@@ -139,6 +144,7 @@ export default {
version: () => version,
disableExternal: () => disableExternal,
trashDir: () => trashDir,
+ quotaExists: () => quotaExists,
noAuth: () => noAuth,
authMethod: () => authMethod,
authLogoutURL: () => authLogoutURL,
diff --git a/frontend/src/css/base.css b/frontend/src/css/base.css
index 970d81f7..a3b4bbc0 100644
--- a/frontend/src/css/base.css
+++ b/frontend/src/css/base.css
@@ -143,4 +143,29 @@ main {
.break-word {
word-break: break-all;
+}
+
+#quota {
+ padding: 1em;
+}
+
+#quota > div + div {
+ margin-top: .5em;
+}
+
+#quota label {
+ color: #546E7A;
+}
+
+#quota .bar {
+ width: 100%;
+ height: 10px;
+ background-color: rgba(0,0,0, 0.1);
+}
+
+#quota .bar .progress {
+ width: 0;
+ height: 100%;
+ transition: .2s ease width;
+ background-color: #40c4ff;
}
\ No newline at end of file
diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json
index 30d210e7..53c1524f 100644
--- a/frontend/src/i18n/en.json
+++ b/frontend/src/i18n/en.json
@@ -267,7 +267,11 @@
"preview": "Preview",
"settings": "Settings",
"signup": "Signup",
- "siteSettings": "Site Settings"
+ "siteSettings": "Site Settings",
+ "quota": {
+ "space": "Space",
+ "inodes": "Inodes"
+ }
},
"success": {
"linkCopied": "Link copied!"
diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js
index fb8c0cb6..6c9e5dc8 100644
--- a/frontend/src/store/index.js
+++ b/frontend/src/store/index.js
@@ -3,6 +3,7 @@ import Vuex from "vuex";
import mutations from "./mutations";
import getters from "./getters";
import upload from "./modules/upload";
+import quota from "./modules/quota";
Vue.use(Vuex);
@@ -31,5 +32,5 @@ export default new Vuex.Store({
state,
getters,
mutations,
- modules: { upload },
+ modules: { upload, quota },
});
diff --git a/frontend/src/store/modules/quota.js b/frontend/src/store/modules/quota.js
new file mode 100644
index 00000000..28e66fc1
--- /dev/null
+++ b/frontend/src/store/modules/quota.js
@@ -0,0 +1,32 @@
+import { users as api } from "@/api";
+
+const state = {
+ inodes: null,
+ space: null,
+};
+
+const mutations = {
+ setQuota(state, { inodes, space }) {
+ state.inodes = inodes;
+ state.space = space;
+ },
+};
+
+const actions = {
+ fetch: async (context) => {
+ try {
+ let data = await api.getQuota();
+ if (
+ data !== null &&
+ data.inodes != undefined &&
+ data.space != undefined
+ ) {
+ context.commit("setQuota", data);
+ }
+ } catch (e) {
+ console.log(e);
+ }
+ },
+};
+
+export default { state, mutations, actions, namespaced: true };
diff --git a/frontend/src/utils/buttons.js b/frontend/src/utils/buttons.js
index 7b897662..45706209 100644
--- a/frontend/src/utils/buttons.js
+++ b/frontend/src/utils/buttons.js
@@ -48,7 +48,7 @@ function success(button) {
el.style.opacity = 0;
setTimeout(() => {
- el.classList.remove("spin");
+ el.classList.remove("pulse-spin");
el.innerHTML = "done";
el.style.opacity = 1;
diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js
index 666c3adf..d30f8b90 100644
--- a/frontend/src/utils/constants.js
+++ b/frontend/src/utils/constants.js
@@ -8,6 +8,7 @@ const signup = window.FileBrowser.Signup;
const version = window.FileBrowser.Version;
const logoURL = `${staticURL}/img/logo.svg`;
const trashDir = window.FileBrowser.TrashDir;
+const quotaExists = window.FileBrowser.QuotaExists;
const noAuth = window.FileBrowser.NoAuth;
const authMethod = window.FileBrowser.AuthMethod;
const authLogoutURL = window.FileBrowser.AuthLogoutURL;
@@ -27,6 +28,7 @@ export {
signup,
version,
trashDir,
+ quotaExists,
noAuth,
authMethod,
authLogoutURL,
diff --git a/http/http.go b/http/http.go
index bc798561..1a6332c2 100644
--- a/http/http.go
+++ b/http/http.go
@@ -67,6 +67,8 @@ func NewHandler(
api.Handle("/settings", monkey(settingsGetHandler, "")).Methods("GET")
api.Handle("/settings", monkey(settingsPutHandler, "")).Methods("PUT")
+ api.Handle("/quota", monkey(quotaGetHandler, "")).Methods("GET")
+
api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET")
api.PathPrefix("/preview/{size}/{path:.*}").
Handler(monkey(previewHandler(imgSvc, fileCache, server.EnableThumbnails, server.ResizePreview), "/api/preview")).Methods("GET")
diff --git a/http/quota.go b/http/quota.go
new file mode 100644
index 00000000..2155cda7
--- /dev/null
+++ b/http/quota.go
@@ -0,0 +1,42 @@
+package http
+
+import (
+ "encoding/json"
+ "net/http"
+ "os"
+)
+
+type quotaData struct {
+ InodeLimit uint64 `json:"inodeLimit"`
+ InodeQuota uint64 `json:"inodeQuota"`
+ InodeUsage uint64 `json:"inodeUsage"`
+ SpaceLimit uint64 `json:"spaceLimit"`
+ SpaceQuota uint64 `json:"spaceQuota"`
+ SpaceUsage uint64 `json:"spaceUsage"`
+}
+
+var quotaGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
+ content, err := os.ReadFile(d.user.QuotaFile)
+ if err != nil {
+ return errToStatus(err), err
+ }
+
+ data := quotaData{}
+ err = json.Unmarshal(content, &data)
+ if err != nil {
+ return errToStatus(err), err
+ }
+
+ res := map[string]map[string]uint64{
+ "inodes": {
+ "quota": data.InodeQuota,
+ "usage": data.InodeUsage,
+ },
+ "space": {
+ "quota": data.SpaceQuota,
+ "usage": data.SpaceUsage,
+ },
+ }
+
+ return renderJSON(w, r, res)
+})
diff --git a/http/static.go b/http/static.go
index d3c3765f..88313ae5 100644
--- a/http/static.go
+++ b/http/static.go
@@ -34,6 +34,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
"StaticURL": path.Join(d.server.BaseURL, "/static"),
"Signup": d.settings.Signup,
"TrashDir": d.settings.Defaults.TrashDir,
+ "QuotaExists": d.settings.Defaults.QuotaFile != "",
"NoAuth": d.settings.AuthMethod == auth.MethodNoAuth,
"AuthMethod": d.settings.AuthMethod,
"AuthLogoutURL": d.settings.AuthLogoutURL,
diff --git a/settings/defaults.go b/settings/defaults.go
index 8f0109e3..ba1b3c94 100644
--- a/settings/defaults.go
+++ b/settings/defaults.go
@@ -10,6 +10,7 @@ import (
type UserDefaults struct {
Scope string `json:"scope"`
TrashDir string `json:"trashDir"`
+ QuotaFile string `json:"quotaFile"`
Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
@@ -23,6 +24,7 @@ type UserDefaults struct {
func (d *UserDefaults) Apply(u *users.User) {
u.Scope = d.Scope
u.TrashDir = d.TrashDir
+ u.QuotaFile = d.QuotaFile
u.Locale = d.Locale
u.ViewMode = d.ViewMode
u.SingleClick = d.SingleClick
diff --git a/users/users.go b/users/users.go
index 9279f0cf..ff543928 100644
--- a/users/users.go
+++ b/users/users.go
@@ -26,6 +26,7 @@ type User struct {
Password string `json:"password"`
Scope string `json:"scope"`
TrashDir string `json:"trashDir"`
+ QuotaFile string `json:"quotaFile"`
Locale string `json:"locale"`
LockPassword bool `json:"lockPassword"`
ViewMode ViewMode `json:"viewMode"`