mirror of
https://github.com/ApfelTeeSaft/R2-Explorer.git
synced 2026-08-26 19:33:39 +00:00
Improve error messages (#77)
This commit is contained in:
@@ -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,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" });
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -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;
|
||||
};
|
||||
|
||||
Generated
+8
-8
@@ -80,13 +80,13 @@ importers:
|
||||
dependencies:
|
||||
'@hono/cloudflare-access':
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0([email protected]4)
|
||||
version: 0.2.0([email protected]5)
|
||||
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'}
|
||||
|
||||
[email protected]4:
|
||||
resolution: {integrity: sha512-j4VkyUp2xazGJ8eCCLN1Vm/bxdvm/j5ZuU9AIjLu9vapn2M44p9L3Ktr9Vnb2RN2QtcR/wVjZVMlT5k7GJQgPw==}
|
||||
[email protected]5:
|
||||
resolution: {integrity: sha512-OiQwvAOAaI2JrABBH69z5rsctHDzFzIKJge0nYXgtzGJ0KftwLWcBXm1upJC23/omNRtnqM0gjRMbtXshPdqhQ==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
|
||||
[email protected]:
|
||||
@@ -3421,9 +3421,9 @@ snapshots:
|
||||
|
||||
'@fastify/[email protected]': {}
|
||||
|
||||
'@hono/[email protected]([email protected]4)':
|
||||
'@hono/[email protected]([email protected]5)':
|
||||
dependencies:
|
||||
hono: 4.6.14
|
||||
hono: 4.6.15
|
||||
|
||||
'@isaacs/[email protected]':
|
||||
dependencies:
|
||||
@@ -4748,7 +4748,7 @@ snapshots:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
[email protected]4: {}
|
||||
[email protected]5: {}
|
||||
|
||||
[email protected]:
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user