From 2df5c8092840bd3f01c27f5ed97de543b65eaa0c Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Sat, 21 Feb 2026 16:13:10 -0500 Subject: [PATCH] feat: Add file manager view using VueFinder Installs VueFinder and wires it to the backend /api/files endpoint with JWT Bearer auth. Adds /files route, File Manager nav item (files.view permission-gated, FolderOpen icon), and imports VueFinder CSS globally. Driver token is computed reactively so it tracks token refreshes automatically. Uses midnight theme to match the dark admin panel aesthetic. Co-Authored-By: Claude Opus 4.6 --- frontend/package-lock.json | 587 +++++++++++++++++- frontend/package.json | 3 +- .../src/components/layout/DashboardLayout.vue | 2 + frontend/src/main.ts | 1 + frontend/src/router/index.ts | 5 + frontend/src/views/admin/FileManagerView.vue | 49 ++ 6 files changed, 645 insertions(+), 2 deletions(-) create mode 100644 frontend/src/views/admin/FileManagerView.vue diff --git a/frontend/package-lock.json b/frontend/package-lock.json index efaab4b..ec9d0f5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,7 +13,8 @@ "pinia": "^3.0.4", "pinia-plugin-persistedstate": "^4.7.1", "vue": "^3.5.25", - "vue-router": "^5.0.2" + "vue-router": "^5.0.2", + "vuefinder": "^4.1.1" }, "devDependencies": { "@tailwindcss/vite": "^4.1.18", @@ -530,6 +531,31 @@ "node": ">=18" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz", + "integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz", + "integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.4", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -575,6 +601,71 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@nanostores/i18n": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@nanostores/i18n/-/i18n-1.2.2.tgz", + "integrity": "sha512-5LLxl95+ZI46MrM/Kn7YjORKsD7+Xy2tgjZ7/oDT/BGPEiaBM9lK89/afeK+BqaQL0Xd9Xaa5MPuuVSyWAo+/w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "nanostores": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^1.0.0" + } + }, + "node_modules/@nanostores/persistent": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@nanostores/persistent/-/persistent-1.3.3.tgz", + "integrity": "sha512-+b4I8xrmjhKE3hQ9V7/b4Xa+MBMkM2P4Ulv33zFEF/+2Hucsb24vTjYiWR8R97y8YdRptmRKlL5Qwy0q1Jj5nQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "nanostores": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^1.0.0" + } + }, + "node_modules/@nanostores/vue": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@nanostores/vue/-/vue-1.0.1.tgz", + "integrity": "sha512-0VwubMTMvEdWQhVN4BAvDZ+vHQH3O1G9BaOfgrjfF4erqBsWScoK/zyaBeRfFjptNOb25947EFPHBZwEf9JcMg==", + "funding": [ + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/euaaaio" + } + ], + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "@nanostores/logger": "^0.4.0 || ^1.0.0", + "@vue/devtools-api": ">=7.6.2", + "nanostores": "^0.11.3 || ^1.0.0", + "vue": ">=3.3.1" + }, + "peerDependenciesMeta": { + "@nanostores/logger": { + "optional": true + }, + "@vue/devtools-api": { + "optional": true + } + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.2", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", @@ -1204,6 +1295,95 @@ "vite": "^5.2.0 || ^6 || ^7" } }, + "node_modules/@tanstack/match-sorter-utils": { + "version": "8.19.4", + "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz", + "integrity": "sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==", + "license": "MIT", + "dependencies": { + "remove-accents": "0.5.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz", + "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/vue-query": { + "version": "5.92.9", + "resolved": "https://registry.npmjs.org/@tanstack/vue-query/-/vue-query-5.92.9.tgz", + "integrity": "sha512-jjAZcqKveyX0C4w/6zUqbnqk/XzuxNWaFsWjGTJWULVFizUNeLGME2gf9vVSDclIyiBhR13oZJPPs6fJgfpIJQ==", + "license": "MIT", + "dependencies": { + "@tanstack/match-sorter-utils": "^8.19.4", + "@tanstack/query-core": "5.90.20", + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@vue/composition-api": "^1.1.2", + "vue": "^2.6.0 || ^3.3.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@tanstack/vue-query/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@tanstack/vue-query/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@transloadit/prettier-bytes": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.3.5.tgz", + "integrity": "sha512-xF4A3d/ZyX2LJWeQZREZQw+qFX4TGQ8bGVP97OLRt6sPO6T0TNHBFTuRHOJh7RNmYOBmQ9MHxpolD9bXihpuVA==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1221,6 +1401,166 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "license": "MIT" + }, + "node_modules/@uppy/companion-client": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-5.1.1.tgz", + "integrity": "sha512-DzrOWTbIZHvtgAFXBMYHk2wD27NjpBSVhY2tEiEIUhPd2CxbFRZjHM/N3HOt3VwZEAP471QWFLlJRWPcIY3A2Q==", + "license": "MIT", + "dependencies": { + "@uppy/utils": "^7.1.1", + "namespace-emitter": "^2.0.1", + "p-retry": "^6.1.0" + }, + "peerDependencies": { + "@uppy/core": "^5.1.1" + } + }, + "node_modules/@uppy/components": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@uppy/components/-/components-1.2.0.tgz", + "integrity": "sha512-rtIr+77Rw/q5Vw++xazF1dCg2d4A4zT9CV+ZyN8Rsx8xiIr2CxCR4TaHHBy+WeC0b7Mk6yNuJ0wUa34tFJ6pKg==", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1", + "dequal": "^2.0.3", + "preact": "^10.26.10", + "pretty-bytes": "^6.1.1" + }, + "peerDependencies": { + "@uppy/core": "^5.2.0", + "@uppy/image-editor": "^4.2.0", + "@uppy/screen-capture": "^5.1.0", + "@uppy/webcam": "^5.1.0" + }, + "peerDependenciesMeta": { + "@uppy/image-editor": { + "optional": true + }, + "@uppy/screen-capture": { + "optional": true + }, + "@uppy/webcam": { + "optional": true + } + } + }, + "node_modules/@uppy/core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@uppy/core/-/core-5.2.0.tgz", + "integrity": "sha512-uvfNyz4cnaplt7LYJmEZHuqOuav0tKp4a9WKJIaH6iIj7XiqYvS2J5SEByexAlUFlzefOAyjzj4Ja2dd/8aMrw==", + "license": "MIT", + "dependencies": { + "@transloadit/prettier-bytes": "^0.3.4", + "@uppy/store-default": "^5.0.0", + "@uppy/utils": "^7.1.4", + "lodash": "^4.17.21", + "mime-match": "^1.0.2", + "namespace-emitter": "^2.0.1", + "nanoid": "^5.0.9", + "preact": "^10.5.13" + } + }, + "node_modules/@uppy/core/node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@uppy/locales": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@uppy/locales/-/locales-5.1.1.tgz", + "integrity": "sha512-zSbDU27JzfdssRJoa5/xmGOsrEtS+2Z9j41weaoCa/NoK4wqZzkFNQ0Z44etbTg3PDVFakZVDu/Z+c+vsJCfdQ==", + "license": "MIT", + "dependencies": { + "@uppy/utils": "^7.1.5" + } + }, + "node_modules/@uppy/store-default": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-5.0.0.tgz", + "integrity": "sha512-hQtCSQ1yGiaval/wVYUWquYGDJ+bpQ7e4FhUUAsRQz1x1K+o7NBtjfp63O9I4Ks1WRoKunpkarZ+as09l02cPw==", + "license": "MIT" + }, + "node_modules/@uppy/utils": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-7.1.5.tgz", + "integrity": "sha512-Vz4WGTjef6WebECGur4clWjpkET4o3bdvPMj1m2sD5cL+dTt69m+FIE5h5JD3HBMLEPTXPVkrXGMIFcbOYC12Q==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21", + "preact": "^10.5.13" + } + }, + "node_modules/@uppy/vue": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@uppy/vue/-/vue-3.2.0.tgz", + "integrity": "sha512-BZiyUZxpadf3uUp8YzgwRZTIde/m9Ne4ILFygJg7ilFq/Qfb1pBVspG9FJoG23RbOiRuxd4JixwFh0gaFdfL+w==", + "license": "MIT", + "dependencies": { + "@uppy/components": "^1.2.0", + "preact": "^10.26.10", + "shallow-equal": "^3.0.0" + }, + "peerDependencies": { + "@uppy/core": "^5.2.0", + "@uppy/dashboard": "^5.1.1", + "@uppy/screen-capture": "^5.1.0", + "@uppy/status-bar": "^5.1.0", + "@uppy/webcam": "^5.1.0", + "vue": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@uppy/dashboard": { + "optional": true + }, + "@uppy/screen-capture": { + "optional": true + }, + "@uppy/status-bar": { + "optional": true + }, + "@uppy/webcam": { + "optional": true + } + } + }, + "node_modules/@uppy/xhr-upload": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-5.1.1.tgz", + "integrity": "sha512-Vp0HWVA8o+niC2uISxPt0pZ+95bHHkk9HzNaUTrff/vq+20Ln68BS2auJhc9ecJzI6SKAlGZ342dcTQ/onw0nA==", + "license": "MIT", + "dependencies": { + "@uppy/companion-client": "^5.1.1", + "@uppy/utils": "^7.1.5" + }, + "peerDependencies": { + "@uppy/core": "^5.2.0" + } + }, + "node_modules/@viselect/vanilla": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@viselect/vanilla/-/vanilla-3.9.0.tgz", + "integrity": "sha512-E9eBgoi/crJ0SlZMAc+Yst7nU324LZ5LLvcXjzWEcrfllscdpTml2OLOKHC7O8Bbz19OybSLv6VexxnjlJrLxQ==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-vue": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.4.tgz", @@ -1537,6 +1877,21 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/confbox": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", @@ -1564,12 +1919,27 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", + "license": "MIT" + }, "node_modules/defu": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", "license": "MIT" }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1580,6 +1950,12 @@ "node": ">=8" } }, + "node_modules/easy-bem": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/easy-bem/-/easy-bem-1.1.1.tgz", + "integrity": "sha512-GJRqdiy2h+EXy6a8E6R+ubmqUM08BK0FWNq41k24fup6045biQ8NXxoXimiwegMQvFFV3t1emADdGNL1TlS61A==", + "license": "MIT" + }, "node_modules/echarts": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz", @@ -1715,6 +2091,18 @@ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "license": "MIT" }, + "node_modules/is-network-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz", + "integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-what": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", @@ -2039,6 +2427,12 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "license": "MIT" + }, "node_modules/lucide-vue-next": { "version": "0.564.0", "resolved": "https://registry.npmjs.org/lucide-vue-next/-/lucide-vue-next-0.564.0.tgz", @@ -2072,6 +2466,15 @@ "url": "https://github.com/sponsors/sxzz" } }, + "node_modules/mime-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz", + "integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==", + "license": "ISC", + "dependencies": { + "wildcard": "^1.1.0" + } + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -2113,6 +2516,12 @@ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", "license": "MIT" }, + "node_modules/namespace-emitter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz", + "integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -2131,6 +2540,44 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/nanostores": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nanostores/-/nanostores-1.1.0.tgz", + "integrity": "sha512-yJBmDJr18xy47dbNVlHcgdPrulSn1nhSE6Ns9vTG+Nx9VPT6iV1MD6aQFp/t52zpf82FhLLTXAXr30NuCnxvwA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + } + }, + "node_modules/overlayscrollbars": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-2.14.0.tgz", + "integrity": "sha512-RjV0pqc79kYhQLC3vTcLRb5GLpI1n6qh0Oua3g+bGH4EgNOJHVBGP7u0zZtxoAa0dkHlAqTTSYRb9MMmxNLjig==", + "license": "MIT" + }, + "node_modules/p-retry": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", + "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -2253,6 +2700,28 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/preact": { + "version": "10.28.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.4.tgz", + "integrity": "sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "license": "MIT", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/quansync": { "version": "0.2.11", "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", @@ -2282,6 +2751,21 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==", + "license": "MIT" + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", @@ -2339,6 +2823,12 @@ "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", "license": "MIT" }, + "node_modules/shallow-equal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz", + "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==", + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2469,6 +2959,16 @@ "url": "https://github.com/sponsors/sxzz" } }, + "node_modules/vanilla-lazyload": { + "version": "19.1.3", + "resolved": "https://registry.npmjs.org/vanilla-lazyload/-/vanilla-lazyload-19.1.3.tgz", + "integrity": "sha512-bBMERPu2AFJc35krS+8BOhq++c6dRfL6q368lJPnkS5U92fRQagTR3FsNta69/GukfZzDwDEjD5M3U7VuSiCDw==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://ko-fi.com/verlok" + } + }, "node_modules/vite": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", @@ -2572,6 +3072,24 @@ } } }, + "node_modules/vue-advanced-cropper": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/vue-advanced-cropper/-/vue-advanced-cropper-2.8.9.tgz", + "integrity": "sha512-1jc5gO674kVGpJKekoaol6ZlwaF5VYDLSBwBOUpViW0IOrrRsyLw6XNszjEqgbavvqinlKNS6Kqlom3B5M72Tw==", + "license": "MIT", + "dependencies": { + "classnames": "^2.2.6", + "debounce": "^1.2.0", + "easy-bem": "^1.0.2" + }, + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, "node_modules/vue-router": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.2.tgz", @@ -2656,6 +3174,28 @@ "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", "license": "MIT" }, + "node_modules/vue-sonner": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/vue-sonner/-/vue-sonner-2.0.9.tgz", + "integrity": "sha512-i6BokNlNDL93fpzNxN/LZSn6D6MzlO+i3qXt6iVZne3x1k7R46d5HlFB4P8tYydhgqOrRbIZEsnRd3kG7qGXyw==", + "license": "MIT", + "peerDependencies": { + "@nuxt/kit": "^4.0.3", + "@nuxt/schema": "^4.0.3", + "nuxt": "^4.0.3" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "@nuxt/schema": { + "optional": true + }, + "nuxt": { + "optional": true + } + } + }, "node_modules/vue-tsc": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.2.4.tgz", @@ -2673,12 +3213,57 @@ "typescript": ">=5.0.0" } }, + "node_modules/vuefinder": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/vuefinder/-/vuefinder-4.1.1.tgz", + "integrity": "sha512-sRrDj7+jrSN4CCQCkBDhc4Z3ZEP2CHa3HibPsByy/yEj2BVH6G7Fn9fSJ3IjGEivYTdURc6DbusidU6Rg6/SqQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4", + "@nanostores/i18n": "^1.2.2", + "@nanostores/persistent": "^1.1.0", + "@nanostores/vue": "^1.0.1", + "@tanstack/vue-query": "^5.90.2", + "@uppy/core": "^5.0.2", + "@uppy/locales": "^5.0.1", + "@uppy/vue": "^3.1.0", + "@uppy/xhr-upload": "^5.0.1", + "@viselect/vanilla": "^3.9.0", + "mitt": "^3.0.1", + "nanostores": "^1.0.1", + "overlayscrollbars": "^2.12.0", + "vanilla-lazyload": "^19.1.3", + "vue-advanced-cropper": "^2.8.9", + "vue-sonner": "^2.0.9" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/n1crack" + }, + "peerDependencies": { + "vue": "^3.5.22" + }, + "peerDependenciesMeta": { + "vue": { + "optional": false + } + } + }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", "license": "MIT" }, + "node_modules/wildcard": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz", + "integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==", + "license": "MIT" + }, "node_modules/yaml": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 01d586e..2155338 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,7 +14,8 @@ "pinia": "^3.0.4", "pinia-plugin-persistedstate": "^4.7.1", "vue": "^3.5.25", - "vue-router": "^5.0.2" + "vue-router": "^5.0.2", + "vuefinder": "^4.1.1" }, "devDependencies": { "@tailwindcss/vite": "^4.1.18", diff --git a/frontend/src/components/layout/DashboardLayout.vue b/frontend/src/components/layout/DashboardLayout.vue index 24c29b0..af8f2a3 100644 --- a/frontend/src/components/layout/DashboardLayout.vue +++ b/frontend/src/components/layout/DashboardLayout.vue @@ -26,6 +26,7 @@ import { Clock, AlertTriangle, FileText, + FolderOpen, Menu, X, } from 'lucide-vue-next' @@ -42,6 +43,7 @@ const navItems = [ { name: 'Console', path: '/console', icon: Terminal, permission: 'console.view' }, { name: 'Players', path: '/players', icon: Users, permission: 'players.view' }, { name: 'Plugins', path: '/plugins', icon: Puzzle, permission: 'plugins.view' }, + { name: 'File Manager', path: '/files', icon: FolderOpen, permission: 'files.view' }, { name: 'Auto-Wiper', path: '/wipes', icon: RefreshCw, permission: 'wipes.view' }, { name: 'Maps', path: '/maps', icon: Map, permission: 'maps.view' }, { name: 'Chat Log', path: '/chat', icon: MessageSquare, permission: 'chat.view' }, diff --git a/frontend/src/main.ts b/frontend/src/main.ts index ec6fe3e..eb0945b 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -5,6 +5,7 @@ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' import App from './App.vue' import router from './router' import './style.css' +import 'vuefinder/dist/vuefinder.css' const app = createApp(App) diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 595b1f8..f94554a 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -105,6 +105,11 @@ const panelRoutes: RouteRecordRaw[] = [ name: 'plugins', component: () => import('@/views/admin/PluginsView.vue'), }, + { + path: 'files', + name: 'files', + component: () => import('@/views/admin/FileManagerView.vue'), + }, { path: 'wipes', name: 'wipes', diff --git a/frontend/src/views/admin/FileManagerView.vue b/frontend/src/views/admin/FileManagerView.vue new file mode 100644 index 0000000..70b48f3 --- /dev/null +++ b/frontend/src/views/admin/FileManagerView.vue @@ -0,0 +1,49 @@ + + +