From a74fde5af683cac6641671dc768c07223b968ece Mon Sep 17 00:00:00 2001 From: Gabriel Massadas <5445926+G4brym@users.noreply.github.com> Date: Sat, 4 Jan 2025 12:59:00 +0000 Subject: [PATCH] Improve error messages (#77) --- packages/dashboard/src/boot/auth.js | 13 ++++++-- packages/dashboard/src/layouts/MainLayout.vue | 12 ------- packages/dashboard/src/stores/auth-store.js | 32 +++++-------------- packages/dashboard/src/stores/main-store.js | 32 ++++++++++++++----- packages/worker/package.json | 2 +- packages/worker/src/index.ts | 24 +++++--------- .../worker/src/modules/buckets/listBuckets.ts | 29 ----------------- packages/worker/src/modules/server/getInfo.ts | 14 ++++++++ packages/worker/src/types.d.ts | 2 +- pnpm-lock.yaml | 16 +++++----- 10 files changed, 74 insertions(+), 102 deletions(-) delete mode 100644 packages/worker/src/modules/buckets/listBuckets.ts diff --git a/packages/dashboard/src/boot/auth.js b/packages/dashboard/src/boot/auth.js index 7816136..c3c48bb 100644 --- a/packages/dashboard/src/boot/auth.js +++ b/packages/dashboard/src/boot/auth.js @@ -2,14 +2,21 @@ import { boot } from "quasar/wrappers"; import { useAuthStore } from "stores/auth-store"; import { useMainStore } from "stores/main-store"; -export default boot(async ({ router, store }) => { +export default boot(async ({ app, router, store }) => { // Check if theres any auth token stored, if there is, try to fetch or redirect const authStore = useAuthStore(store); - const authResp = await authStore.CheckLoginInStorage(router); + const authResp = await authStore.CheckLoginInStorage( + router, + app.config.globalProperties.$q, + ); if (authResp === false) { // No auth token stored, try to fetch without auth or redirect const mainStore = useMainStore(store); - await mainStore.loadServerConfigs(router, true); + await mainStore.loadServerConfigs( + router, + app.config.globalProperties.$q, + true, + ); } }); diff --git a/packages/dashboard/src/layouts/MainLayout.vue b/packages/dashboard/src/layouts/MainLayout.vue index 8a5cf26..ee99f9b 100644 --- a/packages/dashboard/src/layouts/MainLayout.vue +++ b/packages/dashboard/src/layouts/MainLayout.vue @@ -28,23 +28,11 @@ import LeftSidebar from "components/main/LeftSidebar.vue"; import RightSidebar from "components/main/RightSidebar.vue"; import TopBar from "components/main/Topbar.vue"; -import { useMainStore } from "stores/main-store"; import { ref } from "vue"; export default { name: "MainLayout", components: { TopBar, RightSidebar, LeftSidebar }, - created() { - const mainStore = useMainStore(); - mainStore.loadUserDisks().then((buckets) => { - if (this.$route.path === "/") { - this.$router.push({ - name: "files-home", - params: { bucket: buckets[0].name }, - }); - } - }); - }, setup() { const leftDrawerOpen = ref(false); const rightDrawerOpen = ref(false); diff --git a/packages/dashboard/src/stores/auth-store.js b/packages/dashboard/src/stores/auth-store.js index fe17a2c..1bbd934 100644 --- a/packages/dashboard/src/stores/auth-store.js +++ b/packages/dashboard/src/stores/auth-store.js @@ -5,10 +5,7 @@ import { useMainStore } from "stores/main-store"; const SESSION_KEY = "r2_explorer_session_token"; export const useAuthStore = defineStore("auth", { - state: () => ({ - user: "", - loginMethod: "", - }), + state: () => ({}), getters: { isAuthenticated: (state) => !!state.user, StateUser: (state) => state.user, @@ -20,7 +17,7 @@ export const useAuthStore = defineStore("auth", { api.defaults.headers.common["Authorization"] = `Basic ${token}`; try { - await mainStore.loadServerConfigs(router); + await mainStore.loadServerConfigs(router, this.q); } catch (e) { console.log(e); delete api.defaults.headers.common["Authorization"]; @@ -29,19 +26,15 @@ export const useAuthStore = defineStore("auth", { api.defaults.headers.common.Authorization = `Basic ${token}`; - this.loginMethod = "basic"; - this.user = form.email; - if (form.remind === true) { localStorage.setItem(SESSION_KEY, token); } else { sessionStorage.setItem(SESSION_KEY, token); } - - router.replace(router.currentRoute.value.query?.next || "/"); }, - async CheckLoginInStorage(router) { + async CheckLoginInStorage(router, q) { let token = sessionStorage.getItem(SESSION_KEY); + let authed = false; if (!token) { token = localStorage.getItem(SESSION_KEY); } @@ -53,27 +46,18 @@ export const useAuthStore = defineStore("auth", { const mainStore = useMainStore(); api.defaults.headers.common["Authorization"] = `Basic ${token}`; - try { - await mainStore.loadServerConfigs(router); - } catch (e) { - // Auth token expired + authed = await mainStore.loadServerConfigs(router, q, true); + if (!authed) { delete api.defaults.headers.common["Authorization"]; - await router.replace({ - name: "login", - query: { next: router.currentRoute.fullPath }, - }); - return; + return false; } - this.user = atob(token).split(":")[0]; - this.loginMethod = "basic"; + return false; }, async LogOut(router) { localStorage.removeItem(SESSION_KEY); sessionStorage.removeItem(SESSION_KEY); - this.user = ""; - this.loginMethod = ""; await router.replace({ name: "login" }); }, }, diff --git a/packages/dashboard/src/stores/main-store.js b/packages/dashboard/src/stores/main-store.js index d0c96f3..ddbf04f 100644 --- a/packages/dashboard/src/stores/main-store.js +++ b/packages/dashboard/src/stores/main-store.js @@ -22,13 +22,7 @@ export const useMainStore = defineStore("main", { }, }, actions: { - async loadUserDisks() { - const response = await api.get("/buckets"); - - this.buckets = response.data.buckets; - return response.data.buckets; - }, - async loadServerConfigs(router, handleError = false) { + async loadServerConfigs(router, q, handleError = false) { // This is the initial requests to server, that also checks if user needs auth try { @@ -41,6 +35,19 @@ export const useMainStore = defineStore("main", { this.auth = response.data.auth; this.version = response.data.version; this.showHiddenFiles = response.data.config.showHiddenFiles; + this.buckets = response.data.buckets; + + const url = new URL(window.location.href); + if (url.searchParams.get("next")) { + await router.replace(url.searchParams.get("next")); + } else if (url.pathname === "/" || url.pathname === "/auth/login") { + await router.push({ + name: "files-home", + params: { bucket: this.buckets[0].name }, + }); + } + + return true; } catch (error) { console.log(error); if (error.response.status === 302) { @@ -52,17 +59,26 @@ export const useMainStore = defineStore("main", { } if (handleError) { - if (error.response?.status === 401) { + const respText = await error.response.data; + if (respText === "Authentication error: Basic Auth required") { await router.push({ name: "login", query: { next: router.currentRoute.value.fullPath }, }); return; } + + q.notify({ + type: "negative", + message: respText, + timeout: 10000, // we will timeout it in 10s + }); } else { throw error; } } + + return false; }, }, }); diff --git a/packages/worker/package.json b/packages/worker/package.json index 4a3abbb..51ae655 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -61,7 +61,7 @@ "dependencies": { "@hono/cloudflare-access": "^0.2.0", "chanfana": "^2.5.1", - "hono": "^4.6.14", + "hono": "^4.6.15", "postal-mime": "^2.3.2", "zod": "^3.24.1" } diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 983d3f7..9f93cfa 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -14,7 +14,6 @@ import { CreateFolder } from "./modules/buckets/createFolder"; import { DeleteObject } from "./modules/buckets/deleteObject"; import { GetObject } from "./modules/buckets/getObject"; import { HeadObject } from "./modules/buckets/headObject"; -import { ListBuckets } from "./modules/buckets/listBuckets"; import { ListObjects } from "./modules/buckets/listObjects"; import { MoveObject } from "./modules/buckets/moveObject"; import { CompleteUpload } from "./modules/buckets/multipart/completeUpload"; @@ -30,7 +29,7 @@ import type { AppContext, AppEnv, AppVariables, - BasicAuth, + BasicAuthType, R2ExplorerConfig, } from "./types"; @@ -68,23 +67,16 @@ export function R2Explorer(config?: R2ExplorerConfig) { }); if (config.cors === true) { - app.use( - "*", - cors({ - origin: "*", - allowMethods: ["*"], - credentials: true, - }), - ); + app.use("/api/*", cors()); } if (config.readonly === true) { - app.use("*", readOnlyMiddleware); + app.use("/api/*", readOnlyMiddleware); } if (config.cfAccessTeamName) { - app.use("*", cloudflareAccess(config.cfAccessTeamName)); - app.use("*", async (c, next) => { + app.use("/api/*", cloudflareAccess(config.cfAccessTeamName)); + app.use("/api/*", async (c, next) => { c.set("authentication_type", "cloudflare-access"); c.set("authentication_username", c.get("accessPayload").email); await next(); @@ -97,14 +89,15 @@ export function R2Explorer(config?: R2ExplorerConfig) { scheme: "basic", }); app.use( - "*", + "/api/*", basicAuth({ + invalidUserMessage: "Authentication error: Basic Auth required", verifyUser: (username, password, c: AppContext) => { const users = ( Array.isArray(c.get("config").basicAuth) ? c.get("config").basicAuth : [c.get("config").basicAuth] - ) as BasicAuth[]; + ) as BasicAuthType[]; for (const user of users) { if (user.username === username && user.password === password) { @@ -122,7 +115,6 @@ export function R2Explorer(config?: R2ExplorerConfig) { openapi.get("/api/server/config", GetInfo); - openapi.get("/api/buckets", ListBuckets); openapi.get("/api/buckets/:bucket", ListObjects); openapi.post("/api/buckets/:bucket/move", MoveObject); openapi.post("/api/buckets/:bucket/folder", CreateFolder); diff --git a/packages/worker/src/modules/buckets/listBuckets.ts b/packages/worker/src/modules/buckets/listBuckets.ts deleted file mode 100644 index 8e77cee..0000000 --- a/packages/worker/src/modules/buckets/listBuckets.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { OpenAPIRoute } from "chanfana"; -import type { AppContext } from "../../types"; - -export class ListBuckets extends OpenAPIRoute { - schema = { - operationId: "get-bucket-list", - tags: ["Buckets"], - summary: "List buckets", - }; - - async handle(c: AppContext) { - const buckets = []; - - for (const [key, value] of Object.entries(c.env)) { - if ( - value.get && - value.put && - value.get.toString().includes("function") && - value.put.toString().includes("function") - ) { - buckets.push({ name: key }); - } - } - - return { - buckets: buckets, - }; - } -} diff --git a/packages/worker/src/modules/server/getInfo.ts b/packages/worker/src/modules/server/getInfo.ts index 66da474..21b5a7f 100644 --- a/packages/worker/src/modules/server/getInfo.ts +++ b/packages/worker/src/modules/server/getInfo.ts @@ -12,6 +12,19 @@ export class GetInfo extends OpenAPIRoute { async handle(c: AppContext) { const { basicAuth, ...config } = c.get("config"); + const buckets = []; + + for (const [key, value] of Object.entries(c.env)) { + if ( + value.get && + value.put && + value.get.toString().includes("function") && + value.put.toString().includes("function") + ) { + buckets.push({ name: key }); + } + } + return { version: settings.version, config: config, @@ -21,6 +34,7 @@ export class GetInfo extends OpenAPIRoute { username: c.get("authentication_username"), } : undefined, + buckets: buckets, }; } } diff --git a/packages/worker/src/types.d.ts b/packages/worker/src/types.d.ts index 67b9cf3..27aeff1 100644 --- a/packages/worker/src/types.d.ts +++ b/packages/worker/src/types.d.ts @@ -1,7 +1,7 @@ import type { CloudflareAccessVariables } from "@hono/cloudflare-access"; import type { Context } from "hono"; -export type BasicAuth = { +export type BasicAuthType = { username: string; password: string; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6a717c8..c96a8cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,13 +80,13 @@ importers: dependencies: '@hono/cloudflare-access': specifier: ^0.2.0 - version: 0.2.0(hono@4.6.14) + version: 0.2.0(hono@4.6.15) chanfana: specifier: ^2.5.1 version: 2.5.1 hono: - specifier: ^4.6.14 - version: 4.6.14 + specifier: ^4.6.15 + version: 4.6.15 postal-mime: specifier: ^2.3.2 version: 2.3.2 @@ -1848,8 +1848,8 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hono@4.6.14: - resolution: {integrity: sha512-j4VkyUp2xazGJ8eCCLN1Vm/bxdvm/j5ZuU9AIjLu9vapn2M44p9L3Ktr9Vnb2RN2QtcR/wVjZVMlT5k7GJQgPw==} + hono@4.6.15: + resolution: {integrity: sha512-OiQwvAOAaI2JrABBH69z5rsctHDzFzIKJge0nYXgtzGJ0KftwLWcBXm1upJC23/omNRtnqM0gjRMbtXshPdqhQ==} engines: {node: '>=16.9.0'} html-minifier-terser@7.2.0: @@ -3421,9 +3421,9 @@ snapshots: '@fastify/busboy@2.1.1': {} - '@hono/cloudflare-access@0.2.0(hono@4.6.14)': + '@hono/cloudflare-access@0.2.0(hono@4.6.15)': dependencies: - hono: 4.6.14 + hono: 4.6.15 '@isaacs/cliui@8.0.2': dependencies: @@ -4748,7 +4748,7 @@ snapshots: dependencies: function-bind: 1.1.2 - hono@4.6.14: {} + hono@4.6.15: {} html-minifier-terser@7.2.0: dependencies: