From b095d5d28aa94ffb5ce87008dc67e3db95fbff72 Mon Sep 17 00:00:00 2001 From: wenxuan70 <55948263+wenxuan70@users.noreply.github.com> Date: Wed, 10 Aug 2022 09:41:24 +0800 Subject: [PATCH] feat: integrate alist with casdoor (#50) * feat: integrate alist with casdoor * fix: casdoor as an option for login Co-authored-by: wenxuan70 --- src/i18n/locales/jp.ts | 3 +- src/i18n/locales/zh.ts | 3 +- src/pages/list/context.tsx | 2 +- src/pages/manage/index.tsx | 739 +++++++++++++++++++------------------ src/pages/manage/login.tsx | 126 ++++--- src/utils/admin.ts | 17 +- 6 files changed, 467 insertions(+), 423 deletions(-) diff --git a/src/i18n/locales/jp.ts b/src/i18n/locales/jp.ts index 93fd383..398de2c 100644 --- a/src/i18n/locales/jp.ts +++ b/src/i18n/locales/jp.ts @@ -12,6 +12,7 @@ const zh = { "Accounts": "アカウント", "Meta": "メタ情報", "login": "ログイン", + "casdoor login": "casdoor ログイン", "password": "パスワード", "success": "成功", "exit": "終了する", @@ -155,4 +156,4 @@ export default zh export const config = { code: 'jp', text: '日本語', -} \ No newline at end of file +} diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts index 8b3ec98..fb1884c 100644 --- a/src/i18n/locales/zh.ts +++ b/src/i18n/locales/zh.ts @@ -12,6 +12,7 @@ const zh = { "Accounts": "账号", "Meta": "元信息", "login": "登录", + "casdoor login": "casdoor登录", "password": "密码", "success": "成功", "exit": "退出", @@ -159,4 +160,4 @@ export default zh export const config = { code: 'zh', text: '简体中文', -} \ No newline at end of file +} diff --git a/src/pages/list/context.tsx b/src/pages/list/context.tsx index 770429e..1628380 100644 --- a/src/pages/list/context.tsx +++ b/src/pages/list/context.tsx @@ -260,7 +260,7 @@ const IContextProvider = (props: any) => { if (!localStorage.getItem("alist_admin-token")) { return; } - admin.get("login").then((resp) => { + admin.get("verify").then((resp) => { if (resp.data.code === 200) { setLoggedIn(true); } diff --git a/src/pages/manage/index.tsx b/src/pages/manage/index.tsx index 4ea0797..9663725 100644 --- a/src/pages/manage/index.tsx +++ b/src/pages/manage/index.tsx @@ -1,40 +1,40 @@ import { - Box, - Center, - Drawer, - DrawerContent, - DrawerOverlay, - Flex, - Icon, - IconButton, - Spinner, - Text, - useColorModeValue, - useDisclosure, - Link as Clink, - useToast, - Tooltip, - Collapse, + Box, + Center, + Drawer, + DrawerContent, + DrawerOverlay, + Flex, + Icon, + IconButton, + Spinner, + Text, + useColorModeValue, + useDisclosure, + Link as Clink, + useToast, + Tooltip, + Collapse, } from "@chakra-ui/react"; -import { BsGearFill } from "react-icons/bs"; -import { FiMenu } from "react-icons/fi"; -import { MdCached, MdKeyboardArrowRight, MdStorage } from "react-icons/md"; -import { SiMetabase } from "react-icons/si"; -import { DiGithubAlt } from "react-icons/di"; -import { BiExit } from "react-icons/bi"; -import React, { lazy, Suspense, useEffect } from "react"; +import {BsGearFill} from "react-icons/bs"; +import {FiMenu} from "react-icons/fi"; +import {MdCached, MdKeyboardArrowRight, MdStorage} from "react-icons/md"; +import {SiMetabase} from "react-icons/si"; +import {DiGithubAlt} from "react-icons/di"; +import {BiExit} from "react-icons/bi"; +import React, {lazy, Suspense, useEffect} from "react"; import { - Link, - Route, - useRouteMatch, - useHistory, - useLocation, + Link, + Route, + useRouteMatch, + useHistory, + useLocation, } from "react-router-dom"; -import { useTranslation } from "react-i18next"; -import admin, { changeToken } from "../../utils/admin"; +import {useTranslation} from "react-i18next"; +import admin, {changeToken} from "../../utils/admin"; import Overlay from "../../components/overlay"; import useTitle from "../../hooks/useTitle"; -import { IconType } from "react-icons"; +import {IconType} from "react-icons"; import {FaDatabase} from "react-icons/fa"; const Login = lazy(() => import("./login")); @@ -43,351 +43,374 @@ const Accounts = lazy(() => import("./accounts")); const Metas = lazy(() => import("./metas")); interface NavItem { - name: string; - to: string; - icon?: IconType; - component?: React.LazyExoticComponent<() => JSX.Element>; - children?: NavItem[]; + name: string; + to: string; + icon?: IconType; + component?: React.LazyExoticComponent<() => JSX.Element>; + children?: NavItem[]; } const NavItems: NavItem[] = [ - { - name: "Settings", - to: "settings", - icon: BsGearFill, - // component: Settings, - children: [ - { - name: "Frontend", - to: "settings/0", - component: Settings, - }, - { - name: "Backend", - to: "settings/1", - component: Settings, - }, - ], - }, - { - name: "Accounts", - to: "accounts", - icon: MdStorage, - component: Accounts, - }, - { - name: "Meta", - to: "meta", - icon: SiMetabase, - component: Metas, - }, - { - name: "Backup & Restore", - to: "backup-restore", - icon: FaDatabase, - component: lazy(() => import("./backup-restore")), - } + { + name: "Settings", + to: "settings", + icon: BsGearFill, + // component: Settings, + children: [ + { + name: "Frontend", + to: "settings/0", + component: Settings, + }, + { + name: "Backend", + to: "settings/1", + component: Settings, + }, + ], + }, + { + name: "Accounts", + to: "accounts", + icon: MdStorage, + component: Accounts, + }, + { + name: "Meta", + to: "meta", + icon: SiMetabase, + component: Metas, + }, + { + name: "Backup & Restore", + to: "backup-restore", + icon: FaDatabase, + component: lazy(() => import("./backup-restore")), + } ]; const getAllNavItems = (items: NavItem[], acc: NavItem[] = []) => { - items.forEach((item) => { - acc.push(item); - if (item.children) { - getAllNavItems(item.children, acc); - } - }); - return acc; + items.forEach((item) => { + acc.push(item); + if (item.children) { + getAllNavItems(item.children, acc); + } + }); + return acc; }; export default function Swibc() { - const disclosureSet = {} as any; - for (const item of NavItems) { - if (item.children) { - disclosureSet[item.name] = useDisclosure(); - } - } - const sidebar = useDisclosure(); - const { t } = useTranslation(); - const match = useRouteMatch(); - const history = useHistory(); - const location = useLocation(); - const toast = useToast(); - useTitle(t("Alist Manage")); - useEffect(() => { - admin.post("login").then((resp) => { - const res = resp.data; - let url = match.url; - if (!url.endsWith("/")) { - url = url + "/"; - } - if (res.code === 401) { - history.push(`${url}login`); - } else { - if (match.url === location.pathname) { - history.push(`${url}settings/0`); + const disclosureSet = {} as any; + for (const item of NavItems) { + if (item.children) { + disclosureSet[item.name] = useDisclosure(); } - } - }); - }, [location.pathname]); - - const NavItem = (props: any) => { - const { icon, children, ...rest } = props; - const MyLink: any = props.to ? Link : Box; - return ( - { - if (props.to) { - document.title = `${children} - ${t("Alist Manage")}`; - sidebar.onClose(); - } - }} - > - - {icon && ( - - )} - {children} - - - ); - }; - - const SidebarContent = (props: any) => ( - - - - {/* */} - - Alist {t("Manage")} - - - - - {NavItems.map((item) => { - if (!item.children) { - return ( - - {t(item.name)} - - ); - } else { - return ( - - - {t(item.name)} - { + const query = new URLSearchParams(location.search); + const code = query.get("code"), state = query.get("state"); + if (code && code.length > 0 && state && state.length > 0) { + admin.post("oauth", { + code: code, + state: state + }).then((resp) => { + const res = resp.data; + let url = match.url; + if (!url.endsWith("/")) { + url = url + "/"; + } + if (res.code === 401) { + history.push(`${url}login`); + } else { + changeToken(res.data, false); + if (match.url === location.pathname) { + history.push(`${url}settings/0`); } - /> - - - {item.children.map((child) => ( - - {t(child.name)} - - ))} - - - ); - } - })} - - - ); - return ( - - - - - - - - - - - - } - size="sm" - /> - + } + }); + } else { + admin.get("verify").then((resp) => { + const res = resp.data; + let url = match.url; + if (!url.endsWith("/")) { + url = url + "/"; + } + if (res.code === 401) { + history.push(`${url}login`); + } else { + if (match.url === location.pathname) { + history.push(`${url}settings/0`); + } + } + }); + } + }, [location.pathname]); - - - - - - } + const NavItem = (props: any) => { + const {icon, children, ...rest} = props; + const MyLink: any = props.to ? Link : Box; + return ( + { - admin.get("clear_cache").then((resp) => { - const res = resp.data; - if (res.code === 200) { - toast({ - title: t(res.message), - status: "success", - duration: 3000, - isClosable: true, - }); - } else { - toast({ - title: t(res.message), - status: "error", - duration: 3000, - isClosable: true, - }); + if (props.to) { + document.title = `${children} - ${t("Alist Manage")}`; + sidebar.onClose(); } - }); }} - /> - - - { - changeToken(""); - history.push(`${match.url}/login`); - }} - colorScheme="blank" - aria-label={t("logout")} - icon={} - > - - - + + {icon && ( + + )} + {children} + + + ); + }; - - ( + - - - - } + bg={useColorModeValue("white", "gray.800")} + borderColor={useColorModeValue("inherit", "gray.700")} + borderRightWidth="1px" + w="48" + shadow="md" + {...props} + > + + + {/* */} + + Alist {t("Manage")} + + + + + {NavItems.map((item) => { + if (!item.children) { + return ( + + {t(item.name)} + + ); + } else { + return ( + + + {t(item.name)} + + + + {item.children.map((child) => ( + + {t(child.name)} + + ))} + + + ); + } + })} + + + ); + return ( + + + + - {getAllNavItems(NavItems).map((item) => { - return ( - item.component && ( - - - - ) - ); - })} - - - - - + + + + + + + + } + size="sm" + /> + + + + + + + + } + onClick={() => { + admin.get("clear_cache").then((resp) => { + const res = resp.data; + if (res.code === 200) { + toast({ + title: t(res.message), + status: "success", + duration: 3000, + isClosable: true, + }); + } else { + toast({ + title: t(res.message), + status: "error", + duration: 3000, + isClosable: true, + }); + } + }); + }} + /> + + + { + changeToken("", false); + history.push(`${match.url}/login`); + }} + colorScheme="blank" + aria-label={t("logout")} + icon={} + > + + + + + + + + + + } + > + {getAllNavItems(NavItems).map((item) => { + return ( + item.component && ( + + + + ) + ); + })} + + + + + + + - - - ); + ); } diff --git a/src/pages/manage/login.tsx b/src/pages/manage/login.tsx index 778d055..b79ca79 100644 --- a/src/pages/manage/login.tsx +++ b/src/pages/manage/login.tsx @@ -1,64 +1,80 @@ import { - Center, - Input, - Button, - VStack, - FormControl, - FormLabel, - useToast, + Center, + Input, + Button, + VStack, + FormControl, + FormLabel, + useToast, } from "@chakra-ui/react"; -import React, { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { useHistory } from "react-router-dom"; -import admin, { changeToken } from "../../utils/admin"; +import React, {useState} from "react"; +import {useTranslation} from "react-i18next"; +import {useHistory} from "react-router-dom"; +import admin, {changeToken} from "../../utils/admin"; const Login = () => { - const { t } = useTranslation(); - const history = useHistory(); - const toast = useToast(); - const [password, setPassword] = useState(""); - const login = () => { - changeToken(password); - admin.post("login").then((resp) => { - const res = resp.data; - if (res.code === 200) { - toast({ - title: t(res.message), - status: "success", - duration: 3000, - isClosable: true, + const {t} = useTranslation(); + const history = useHistory(); + const toast = useToast(); + const [password, setPassword] = useState(""); + const login = () => { + changeToken(password, true); + admin.get("verify").then((resp) => { + const res = resp.data; + if (res.code === 200) { + toast({ + title: t(res.message), + status: "success", + duration: 3000, + isClosable: true, + }); + history.push("settings/0"); + } else { + toast({ + title: t(res.message), + status: "error", + duration: 3000, + isClosable: true, + }); + } }); - history.push("settings/0"); - } else { - toast({ - title: t(res.message), - status: "error", - duration: 3000, - isClosable: true, + }; + const oauthLogin = () => { + admin.get("get_redirect_url").then((resp) => { + const res = resp.data; + if (res.code === 200) { + window.location.href = res.data; + } else { + toast({ + title: t(res.message), + status: "error", + duration: 3000, + isClosable: true, + }); + } }); - } - }); - }; - return ( -
- - - {t("password")} - setPassword(e.target.value)} - onKeyUp={(e) => { - if (e.key === "Enter") { - login(); - } - }} - > - - - -
- ); + }; + return ( +
+ + + {t("password")} + setPassword(e.target.value)} + onKeyUp={(e) => { + if (e.key === "Enter") { + login(); + } + }} + > + + + + +
+ ); }; export default Login; diff --git a/src/utils/admin.ts b/src/utils/admin.ts index 393cf75..a7d4d9b 100644 --- a/src/utils/admin.ts +++ b/src/utils/admin.ts @@ -59,14 +59,17 @@ instance.interceptors.response.use( instance.defaults.headers.common["Authorization"] = localStorage.getItem("alist_admin-token") || ""; -export const changeToken = (password: string) => { - let token = ""; - if (password) { - token = Md5.hashStr(`https://github.com/Xhofe/alist-${password}`); +export const changeToken = (token: string, isPassword: boolean) => { + if (isPassword && token) { + token = Md5.hashStr(`https://github.com/Xhofe/alist-${token}`); + } + if (token) { + instance.defaults.headers.common["Authorization"] = token; + publicR.defaults.headers.common["Authorization"] = token; + localStorage.setItem("alist_admin-token", token); + } else { + localStorage.removeItem("alist_admin-token"); } - instance.defaults.headers.common["Authorization"] = token; - publicR.defaults.headers.common["Authorization"] = token; - localStorage.setItem("alist_admin-token", token); }; export default instance;