From ef59e29af5e9279b3038b80a252c0ea12bd26e1b Mon Sep 17 00:00:00 2001 From: Vsion <442153598@qq.com> Date: Fri, 19 Jan 2024 14:15:25 +0800 Subject: [PATCH 1/5] feat: login / logout with oidc --- next.config.mjs | 11 +++++ package.json | 6 ++- pnpm-lock.yaml | 34 ++++++++++++--- src/app/actions/user.ts | 14 +++--- src/app/chat/layout.tsx | 10 +---- src/app/oidc/auth/page.tsx | 31 +++++++++++++ src/app/oidc/callback/page.tsx | 41 ++++++++++++++++++ src/app/oidc/layout.tsx | 38 ++++++++++++++++ src/app/oidc/logout/page.tsx | 19 ++++++++ src/app/oidc/page.tsx | 7 +++ src/app/oidc/token/route.ts | 43 +++++++++++++++++++ src/app/page.tsx | 12 ------ .../setting/SettingClient/BtnList/index.tsx | 5 +++ src/app/setting/layout.tsx | 10 +---- src/config/oidc.mjs | 12 ++++++ src/layout/AppLayout/SideBar/Chats/index.tsx | 2 + .../AppLayout/SideBar/UserInfoBottom.tsx | 12 ++++-- src/layout/AppLayout/SideBar/index.tsx | 10 ++--- src/layout/AppLayout/index.tsx | 6 ++- src/layout/AppLayoutWithUser/index.tsx | 9 ++++ src/layout/GlobalLayout/index.tsx | 22 +++++++++- src/store/index.ts | 21 +++++---- src/types/user.ts | 4 ++ src/utils/index.ts | 8 ++++ 24 files changed, 322 insertions(+), 65 deletions(-) create mode 100644 src/app/oidc/auth/page.tsx create mode 100644 src/app/oidc/callback/page.tsx create mode 100644 src/app/oidc/layout.tsx create mode 100644 src/app/oidc/logout/page.tsx create mode 100644 src/app/oidc/page.tsx create mode 100644 src/app/oidc/token/route.ts create mode 100644 src/config/oidc.mjs create mode 100644 src/layout/AppLayoutWithUser/index.tsx create mode 100644 src/types/user.ts diff --git a/next.config.mjs b/next.config.mjs index 1eb924c..48381a2 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,6 @@ +import config from './src/config/oidc.mjs'; +const oidcUrl = config.server.url; const isProd = process.env.NODE_ENV === 'production'; const nextConfig = { @@ -24,6 +26,15 @@ const nextConfig = { }, reactStrictMode: true, transpilePackages: ['antd', '@ant-design', 'antd-style', '@lobehub/ui', 'antd-mobile'], + // async rewrites() { + // return [ + // { + // source: '/oidc/token', + // destination: `${oidcUrl}/oidc/token`, + // secure: false, + // }, + // ] + // }, webpack: (config) => { config.experiments = { asyncWebAssembly: true, diff --git a/package.json b/package.json index f63d38d..eadb3da 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version": "0.1.0", "scripts": { "build": "next build", - "dev": "next dev", + "dev": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 next dev", + "dev-https": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0 next dev --experimental-https --experimental-https-key ./cert/key.pem --experimental-https-cert ./cert/cert.pem", "lint": "npm run lint:es && npm run lint:style", "lint-fix": "npm run lint-fix:es && npm run lint-fix:style", "lint-fix:es": "eslint --ext .jsx,.js,.tsx,.ts src --fix", @@ -50,6 +51,7 @@ "classnames": "^2.5.1", "lucide-react": "^0.304.0", "next": "14.0.4", + "query-string": "^8.1.0", "react": "^18", "react-dom": "^18", "react-layout-kit": "^1.7.4", @@ -60,11 +62,13 @@ "devDependencies": { "@types/mdx": "^2.0.10", "@types/node": "^20", + "@types/query-string": "^6.3.0", "@types/react": "^18", "@types/react-dom": "^18", "@types/ua-parser-js": "^0.7.39", "@yuntijs/lint": "^1.4.0", "commitlint": "^18.4.3", + "cross-env": "^7.0.3", "eslint": "^8.56.0", "eslint-config-next": "14.0.4", "husky": "^8.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7d10c04..ffa8a18 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ dependencies: next: specifier: 14.0.4 version: 14.0.4(@babel/core@7.23.7)(react-dom@18.2.0)(react@18.2.0) + query-string: + specifier: ^8.1.0 + version: 8.1.0 react: specifier: ^18 version: 18.2.0 @@ -67,6 +70,9 @@ devDependencies: '@types/node': specifier: ^20 version: 20.10.6 + '@types/query-string': + specifier: ^6.3.0 + version: 6.3.0 '@types/react': specifier: ^18 version: 18.2.46 @@ -82,6 +88,9 @@ devDependencies: commitlint: specifier: ^18.4.3 version: 18.4.4(@types/node@20.10.6)(typescript@5.3.3) + cross-env: + specifier: ^7.0.3 + version: 7.0.3 eslint: specifier: ^8.56.0 version: 8.56.0 @@ -2342,7 +2351,7 @@ packages: immer: 10.0.3 leva: 0.9.35(@types/react-dom@18.2.18)(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0) lodash-es: 4.17.21 - lucide-react: 0.309.0(react@18.2.0) + lucide-react: 0.312.0(react@18.2.0) polished: 4.2.2 prism-react-renderer: 2.3.1(react@18.2.0) query-string: 8.1.0 @@ -3882,6 +3891,13 @@ packages: /@types/prop-types@15.7.11: resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + /@types/query-string@6.3.0: + resolution: {integrity: sha512-yuIv/WRffRzL7cBW+sla4HwBZrEXRNf1MKQ5SklPEadth+BKbDxiVG8A3iISN5B3yC4EeSCzMZP8llHTcUhOzQ==} + deprecated: This is a stub types definition. query-string provides its own type definitions, so you do not need this installed. + dependencies: + query-string: 8.1.0 + dev: true + /@types/react-dom@18.2.18: resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==} dependencies: @@ -5314,6 +5330,14 @@ packages: path-type: 4.0.0 typescript: 5.3.3 + /cross-env@7.0.3: + resolution: {integrity: sha1-hlJkspZ33AFbqEGJGJZd0jL8VM8=} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + /cross-spawn@7.0.3: resolution: {integrity: sha1-9zqFudXUHQRVUcF34ogtSshXKKY=} engines: {node: '>= 8'} @@ -5442,7 +5466,6 @@ packages: /decode-uri-component@0.4.1: resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} engines: {node: '>=14.16'} - dev: false /deep-extend@0.6.0: resolution: {integrity: sha1-xPp8lUBKF6nD6Mp+FTcxK3NjMKw=} @@ -6339,7 +6362,6 @@ packages: /filter-obj@5.1.0: resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==} engines: {node: '>=14.16'} - dev: false /find-root@1.1.0: resolution: {integrity: sha1-q8/Iunb3CMQql7PWhbfpRQv7nOQ=} @@ -7752,8 +7774,8 @@ packages: react: 18.2.0 dev: false - /lucide-react@0.309.0(react@18.2.0): - resolution: {integrity: sha512-zNVPczuwFrCfksZH3zbd1UDE6/WYhYAdbe2k7CImVyPAkXLgIwbs6eXQ4loigqDnUFjyFYCI5jZ1y10Kqal0dg==} + /lucide-react@0.312.0(react@18.2.0): + resolution: {integrity: sha512-3UZsqyswRXjW4t+nw+InICewSimjPKHuSxiFYqTshv9xkK3tPPntXk/lvXc9pKlXIxm3v9WKyoxcrB6YHhP+dg==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 dependencies: @@ -9434,7 +9456,6 @@ packages: decode-uri-component: 0.4.1 filter-obj: 5.1.0 split-on-first: 3.0.0 - dev: false /queue-microtask@1.2.3: resolution: {integrity: sha1-SSkii7xyTfrEPg77BYyve2z7YkM=} @@ -11768,7 +11789,6 @@ packages: /split-on-first@3.0.0: resolution: {integrity: sha1-8ElZyeqBAbmwu/NaYbnr6nhKI+c=} engines: {node: '>=12'} - dev: false /split-string@3.1.0: resolution: {integrity: sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=} diff --git a/src/app/actions/user.ts b/src/app/actions/user.ts index c546968..1026987 100644 --- a/src/app/actions/user.ts +++ b/src/app/actions/user.ts @@ -1,16 +1,16 @@ 'use server'; -type User = { - name: string; - full_name: string; -}; +import { User } from '@/types/user'; export async function getUserData() { - // Fetch data from external API + // todo + // 1. use api + // 2. error handle + const res = await fetch(`https://api.github.com/repos/kubeagi/agent-portal`, { - // cache: 'no-store', + // cache: 'no-store', // 每次都请求动态数据 next: { - revalidate: 5, + revalidate: 5, // 缓存 }, }); const data: any = await res.json(); diff --git a/src/app/chat/layout.tsx b/src/app/chat/layout.tsx index a6f425d..611a98f 100644 --- a/src/app/chat/layout.tsx +++ b/src/app/chat/layout.tsx @@ -1,9 +1 @@ -import { PropsWithChildren, memo } from 'react'; - -import AppLayout from '@/layout/AppLayout'; - -const AppLayoutDesktop = memo(({ children }) => { - return {children}; -}); - -export default AppLayoutDesktop; +export { default } from '@/layout/AppLayoutWithUser'; diff --git a/src/app/oidc/auth/page.tsx b/src/app/oidc/auth/page.tsx new file mode 100644 index 0000000..107c4fd --- /dev/null +++ b/src/app/oidc/auth/page.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { useRouter } from 'next/navigation'; +import queryString from 'query-string'; +import React from 'react'; + +import oidc from '@/config/oidc.mjs'; + +const { client, server, AUTH_DATA } = oidc; +const { url } = server; +const { client_id, redirect_uri } = client; + +export default function Auth() { + const router = useRouter(); + React.useEffect(() => { + const authData = localStorage.getItem(AUTH_DATA); + if (authData) { + // todo validate auth + router.push('/chat'); + return; + } + const query = queryString.stringify({ + client_id, + redirect_uri: `${window.location.origin}${redirect_uri}`, + response_type: 'code', + scope: 'openid profile email groups offline_access', + }); + window.location.href = `${url}/oidc/auth?${query}`; + }, []); + return <> ; +} diff --git a/src/app/oidc/callback/page.tsx b/src/app/oidc/callback/page.tsx new file mode 100644 index 0000000..a364867 --- /dev/null +++ b/src/app/oidc/callback/page.tsx @@ -0,0 +1,41 @@ +'use client'; + +import { useRouter, useSearchParams } from 'next/navigation'; +import React from 'react'; +import { useDispatch } from 'react-redux'; + +import oidc from '@/config/oidc.mjs'; + +const { client, AUTH_DATA } = oidc; +const { redirect_uri } = client; + +export default function Auth() { + const searchParams = useSearchParams(); + const dispatch = useDispatch(); + const router = useRouter(); + const code = searchParams.get('code'); + const fetchAuth = async () => { + fetch(`/oidc/token?code=${code}&redirect_uri=${location.origin}${redirect_uri}`, { + method: 'POST', + }) + .then(res => res.json()) + .then(res => { + if (res.data?.errors) { + console.warn(res.data?.errors); + return; + } + if (res.data) { + localStorage.setItem(AUTH_DATA, JSON.stringify(res.data)); + dispatch({ + type: 'SAVE_AUTH_DATA', + authData: res.data, + }); + router.push('/chat'); + } + }); + }; + React.useEffect(() => { + fetchAuth(); + }, []); + return <> ; +} diff --git a/src/app/oidc/layout.tsx b/src/app/oidc/layout.tsx new file mode 100644 index 0000000..8eb5402 --- /dev/null +++ b/src/app/oidc/layout.tsx @@ -0,0 +1,38 @@ +'use client'; + +import { Flex, Spin } from 'antd'; +import { usePathname } from 'next/navigation'; +import React from 'react'; + +export default function Layout({ children }: { children: React.ReactNode }) { + const pathname = usePathname(); + const tip = React.useMemo(() => { + switch (pathname) { + case '/oidc/logout': { + return '登出中...'; + } + default: { + return '加载中...'; + } + } + }, [pathname]); + return ( + + {tip}} + > + {children} + + + ); +} diff --git a/src/app/oidc/logout/page.tsx b/src/app/oidc/logout/page.tsx new file mode 100644 index 0000000..8e1e73f --- /dev/null +++ b/src/app/oidc/logout/page.tsx @@ -0,0 +1,19 @@ +'use client'; + +import React from 'react'; + +import oidc from '@/config/oidc.mjs'; + +const { client, AUTH_DATA, server } = oidc; +const { redirect_uri } = client; +const { url } = server; + +export default function Auth() { + React.useEffect(() => { + localStorage.removeItem(AUTH_DATA); + window.location.href = `${url}/oidc/logout/remove-auth-data?redirect=${encodeURIComponent( + `${url}/oidc/auth?redirect_uri=${location.origin}${redirect_uri}&response_type=code&scope=openid+profile+email+groups+offline_access` + )}`; + }, []); + return <> ; +} diff --git a/src/app/oidc/page.tsx b/src/app/oidc/page.tsx new file mode 100644 index 0000000..a6ee9d9 --- /dev/null +++ b/src/app/oidc/page.tsx @@ -0,0 +1,7 @@ +'use client'; + +import React from 'react'; + +export default function oidc() { + return <>oidc; +} diff --git a/src/app/oidc/token/route.ts b/src/app/oidc/token/route.ts new file mode 100644 index 0000000..666fa00 --- /dev/null +++ b/src/app/oidc/token/route.ts @@ -0,0 +1,43 @@ +import { type NextRequest, NextResponse } from 'next/server'; + +import oidc from '@/config/oidc.mjs'; +import { btoa } from '@/utils'; + +const { client, server } = oidc; +const { url } = server; +const { client_id, client_secret } = client; + +function fetchWithTimeout(url: string, options: RequestInit, timeout = 3000) { + const fetchPromise = fetch(url, options); + const timeoutPromise = new Promise((resolve, reject) => { + setTimeout(() => { + reject(new Error('Request timed out')); + }, timeout); + }); + return Promise.race([fetchPromise, timeoutPromise]); +} + +export async function POST(request: NextRequest) { + const searchParams = request.nextUrl.searchParams; + const code = searchParams.get('code'); + const redirect_uri = searchParams.get('redirect_uri'); + const body = { + grant_type: 'authorization_code', + code, + redirect_uri, + }; + const res: any = await fetchWithTimeout( + `${url}/oidc/token`, + { + method: 'POST', + headers: { + 'Authorization': `Basic ${btoa(`${client_id}:${client_secret}`)}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }, + 10_000 + ); + const data = await res.json(); + return NextResponse.json({ data }); +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 38251fa..d771045 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,15 +1,3 @@ -// import DesktopPage from './(desktop)'; -// import { isMobileDevice } from '@/utils'; -// import { getUserData } from './actions/user'; -// export default async function Page() { -// const user = await getUserData(); -// const mobile = isMobileDevice(); -// const Page = mobile ? DesktopPage : DesktopPage; -// // return user.username; -// return ( -// -// ) -// } import { redirect } from 'next/navigation'; export default function RootPage() { diff --git a/src/app/setting/SettingClient/BtnList/index.tsx b/src/app/setting/SettingClient/BtnList/index.tsx index 719cf57..c51494f 100644 --- a/src/app/setting/SettingClient/BtnList/index.tsx +++ b/src/app/setting/SettingClient/BtnList/index.tsx @@ -9,6 +9,7 @@ import { Share2, User2Icon, } from 'lucide-react'; +import { useRouter } from 'next/navigation'; import React from 'react'; import BtnsBlock, { Btn } from '@/components/BtnsBlock'; @@ -18,6 +19,7 @@ import { useStyles } from './styles'; // interface SettingBtnListProps {} const SettingBtnList = React.memo(() => { + const router = useRouter(); const { styles, theme } = useStyles(); const btnsUser: Btn[] = React.useMemo( () => [ @@ -78,6 +80,9 @@ const SettingBtnList = React.memo(() => { { icon: LogOut, title: '退出登录', + onClick: () => { + router.push('/oidc/logout'); + }, }, ], [] diff --git a/src/app/setting/layout.tsx b/src/app/setting/layout.tsx index a6f425d..611a98f 100644 --- a/src/app/setting/layout.tsx +++ b/src/app/setting/layout.tsx @@ -1,9 +1 @@ -import { PropsWithChildren, memo } from 'react'; - -import AppLayout from '@/layout/AppLayout'; - -const AppLayoutDesktop = memo(({ children }) => { - return {children}; -}); - -export default AppLayoutDesktop; +export { default } from '@/layout/AppLayoutWithUser'; diff --git a/src/config/oidc.mjs b/src/config/oidc.mjs new file mode 100644 index 0000000..4c6fca3 --- /dev/null +++ b/src/config/oidc.mjs @@ -0,0 +1,12 @@ + +export default { + AUTH_DATA: 'authData', + server: { + url: 'https://portal.172.22.96.136.nip.io', + }, + client: { + client_id: 'bff-client', + client_secret: '61324af0-1234-4f61-b110-ef57013267d6', + redirect_uri: '/oidc/callback', + }, +}; diff --git a/src/layout/AppLayout/SideBar/Chats/index.tsx b/src/layout/AppLayout/SideBar/Chats/index.tsx index 9453928..ea24f8e 100644 --- a/src/layout/AppLayout/SideBar/Chats/index.tsx +++ b/src/layout/AppLayout/SideBar/Chats/index.tsx @@ -1,3 +1,5 @@ +'use server'; + import classnames from 'classnames'; import React from 'react'; diff --git a/src/layout/AppLayout/SideBar/UserInfoBottom.tsx b/src/layout/AppLayout/SideBar/UserInfoBottom.tsx index 5052853..3cf9121 100644 --- a/src/layout/AppLayout/SideBar/UserInfoBottom.tsx +++ b/src/layout/AppLayout/SideBar/UserInfoBottom.tsx @@ -9,6 +9,8 @@ import React from 'react'; import { Flexbox } from 'react-layout-kit'; import { useDispatch, useSelector } from 'react-redux'; +import { User } from '@/types/user'; + const { Text } = Typography; export const useStyles = createStyles(() => { @@ -55,10 +57,12 @@ export const useStyles = createStyles(() => { }; }); -function UserInfoBottom() { - const user = { - name: 'testtesttesttesttesttesttesttesttesttesttesttest', - }; // await getUserData() +interface Props { + user: User; +} + +function UserInfoBottom(props: Props) { + const { user = {} } = props; const dispatch = useDispatch(); const { styles } = useStyles(); const theme = useSelector((store: any) => store.theme); diff --git a/src/layout/AppLayout/SideBar/index.tsx b/src/layout/AppLayout/SideBar/index.tsx index a480d46..99b27fb 100644 --- a/src/layout/AppLayout/SideBar/index.tsx +++ b/src/layout/AppLayout/SideBar/index.tsx @@ -1,18 +1,18 @@ import React from 'react'; +import { User } from '@/types/user'; + import Chats from './Chats'; import SideBarHeader from './SideBarHeader'; import UserInfoBottom from './UserInfoBottom'; import styles from './index.module.css'; -const ChatList = () => { +export default function SideBar({ user }: { user: User }) { return (
- +
); -}; - -export default ChatList; +} diff --git a/src/layout/AppLayout/index.tsx b/src/layout/AppLayout/index.tsx index fdf05c2..b818ad4 100644 --- a/src/layout/AppLayout/index.tsx +++ b/src/layout/AppLayout/index.tsx @@ -1,8 +1,10 @@ import { Flex } from 'antd'; +import { User } from '@/types/user'; + import SideBar from './SideBar'; -export default function AppLayout({ children }: { children: React.ReactNode }) { +export default function AppLayout({ children, user }: { children: React.ReactNode; user: User }) { return ( - + {children} ); diff --git a/src/layout/AppLayoutWithUser/index.tsx b/src/layout/AppLayoutWithUser/index.tsx new file mode 100644 index 0000000..84859b5 --- /dev/null +++ b/src/layout/AppLayoutWithUser/index.tsx @@ -0,0 +1,9 @@ +'use server'; + +import { getUserData } from '@/app/actions/user'; +import AppLayout from '@/layout/AppLayout'; + +export default async function Layout({ children }: { children: React.ReactNode }) { + const user = await getUserData(); + return {children}; +} diff --git a/src/layout/GlobalLayout/index.tsx b/src/layout/GlobalLayout/index.tsx index 41ce96b..9436cc0 100644 --- a/src/layout/GlobalLayout/index.tsx +++ b/src/layout/GlobalLayout/index.tsx @@ -3,17 +3,37 @@ import { App } from 'antd'; import { ThemeProvider } from 'antd-style'; import 'antd/dist/reset.css'; +import { usePathname, useRouter } from 'next/navigation'; import React from 'react'; import { Provider } from 'react-redux'; +import oidc from '@/config/oidc.mjs'; import { useStore } from '@/store'; import { GlobalStyle } from '@/styles'; import themeConfig from '@/theme/themeConfig'; +const { AUTH_DATA } = oidc; + // import './globals.css'; export default function GlobalLayout({ children }: { children: React.ReactNode }) { - const store = useStore(); + const [initialState, setInitState] = React.useState(); + const pathname = usePathname(); + const router = useRouter(); + React.useEffect(() => { + if (pathname === '/oidc/callback') { + return; + } + const auth = localStorage.getItem(AUTH_DATA); + if (!auth) { + router.push('/oidc/auth'); + } + setInitState({ + theme: localStorage.getItem('theme') || 'light', + activeChat: 'name', + }); + }, []); + const store = useStore(initialState); return ( diff --git a/src/store/index.ts b/src/store/index.ts index 9b2a9c4..e50451e 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -3,14 +3,13 @@ import { configureStore } from '@reduxjs/toolkit'; import { useMemo } from 'react'; -let store: any; +import oidc from '@/config/oidc.mjs'; -const initialState = { - theme: typeof window === 'undefined' ? 'light' : localStorage.getItem('theme') || 'light', // todo remove: use server - activeChat: 'name', -}; +const { AUTH_DATA } = oidc; + +let store: any; -const reducer = (state = initialState, action: any) => { +const reducer = (state = {}, action: any) => { switch (action.type) { case 'TRIGGER_SHEME': { localStorage.setItem('theme', action.theme); // todo remove @@ -19,6 +18,12 @@ const reducer = (state = initialState, action: any) => { theme: action.theme, }; } + case 'SAVE_AUTH_DATA': { + return { + ...state, + [AUTH_DATA]: action[AUTH_DATA], + }; + } case 'CLICK_CHAT': { return { ...state, @@ -31,7 +36,7 @@ const reducer = (state = initialState, action: any) => { } }; -function initStore(preloadedState = initialState) { +function initStore(preloadedState = {}) { return configureStore({ reducer, preloadedState, @@ -62,7 +67,7 @@ export const initializeStore = (preloadedState: any) => { }; export function useStore(_initialState?: any) { - const init = _initialState || initialState; + const init = _initialState || {}; const _store = useMemo(() => initializeStore(init), [init]); return _store; } diff --git a/src/types/user.ts b/src/types/user.ts new file mode 100644 index 0000000..4c9a212 --- /dev/null +++ b/src/types/user.ts @@ -0,0 +1,4 @@ +export type User = { + name: string; + full_name?: string; +}; diff --git a/src/utils/index.ts b/src/utils/index.ts index 61c3900..eb8b19a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -16,3 +16,11 @@ export const isMobileDevice = () => { const device = new UAParser(ua || '').getDevice(); return device.type === 'mobile'; }; + +export const atob = (encodedData: string) => { + return Buffer.from(encodedData, 'base64').toString(); +}; + +export const btoa = (stringToEncode: string) => { + return Buffer.from(stringToEncode).toString('base64'); +}; From f171ff5a896f7e0f17d5e43df2ad6cf498ae148f Mon Sep 17 00:00:00 2001 From: Vsion <442153598@qq.com> Date: Fri, 19 Jan 2024 14:27:28 +0800 Subject: [PATCH 2/5] fix: console warning --- src/layout/AppLayout/SideBar/Chats/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layout/AppLayout/SideBar/Chats/index.tsx b/src/layout/AppLayout/SideBar/Chats/index.tsx index ea24f8e..1b5b9b9 100644 --- a/src/layout/AppLayout/SideBar/Chats/index.tsx +++ b/src/layout/AppLayout/SideBar/Chats/index.tsx @@ -14,7 +14,7 @@ const Chats: any = async () => {
{list.map((item: any, idx: number) => ( - + ))}
From 2c8bef4b6f99587a18ae4727e0986b0fd1091790 Mon Sep 17 00:00:00 2001 From: Vsion <442153598@qq.com> Date: Fri, 19 Jan 2024 15:37:28 +0800 Subject: [PATCH 3/5] fix: use env with oidc configs --- .gitignore | 3 + README.md | 26 ++- package.json | 2 + pnpm-lock.yaml | 374 ++++++++++++++++++++++++++++++++- src/app/oidc/auth/Auth.tsx | 29 +++ src/app/oidc/auth/page.tsx | 28 +-- src/app/oidc/logout/Logout.tsx | 16 ++ src/app/oidc/logout/page.tsx | 16 +- src/config/oidc.mjs | 8 +- 9 files changed, 454 insertions(+), 48 deletions(-) create mode 100644 src/app/oidc/auth/Auth.tsx create mode 100644 src/app/oidc/logout/Logout.tsx diff --git a/.gitignore b/.gitignore index fd3dbb5..77f69e5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ # production /build +# dev +.env.development + # misc .DS_Store *.pem diff --git a/README.md b/README.md index 80e6aa3..c8be3e1 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ Console to let user manage their AI agents for their preference. -* Add existing agent -* Build your own AI agent -* Chat with agents -* Keep chat history -* .. + +- Add existing agent +- Build your own AI agent +- Chat with agents +- Keep chat history +- .. ## Environment - - node >= v18.17.0 + +- node >= v18.17.0 ## Getting Started @@ -22,5 +24,15 @@ pnpm dev bun dev ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Open with your browser to see the result. + +## Development OIDC 配置 +根目录新建 `.env.development` + +``` +OIDC_SERVER_URL=[OIDC_SERVER_URL] +CLIENT_ID=[CLIENT_ID] +CLIENT_SECRET=[CLIENT_SECRET] +CLIENT_REDIRECT_URI=[CLIENT_REDIRECT_URI] +``` diff --git a/package.json b/package.json index eadb3da..f45a554 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,8 @@ "husky": "^8.0.3", "lint-staged": "^15.2.0", "prettier": "^3.1.1", + "remark": "^15.0.1", + "remark-cli": "^12.0.0", "stylelint": "^15", "typescript": "^5" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ffa8a18..372aec6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,6 +106,12 @@ devDependencies: prettier: specifier: ^3.1.1 version: 3.1.1 + remark: + specifier: ^15.0.1 + version: 15.0.1 + remark-cli: + specifier: ^12.0.0 + version: 12.0.0 stylelint: specifier: ^15 version: 15.11.0(typescript@5.3.3) @@ -2278,6 +2284,18 @@ packages: resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} dev: true + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: true + /@jridgewell/gen-mapping@0.3.3: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} @@ -2517,6 +2535,35 @@ packages: fastq: 1.16.0 dev: true + /@npmcli/config@8.1.0: + resolution: {integrity: sha512-61LNEybTFaa9Z/f8y6X9s2Blc75aijZK67LxqC5xicBcfkw8M/88nYrRXGXxAUKm6GRlxTZ216dp1UK2+TbaYw==} + engines: {node: ^16.14.0 || >=18.0.0} + dependencies: + '@npmcli/map-workspaces': 3.0.4 + ci-info: 4.0.0 + ini: 4.1.1 + nopt: 7.2.0 + proc-log: 3.0.0 + read-package-json-fast: 3.0.2 + semver: 7.5.4 + walk-up-path: 3.0.1 + dev: true + + /@npmcli/map-workspaces@3.0.4: + resolution: {integrity: sha512-Z0TbvXkRbacjFFLpVpV0e2mheCh+WzQpcqL+4xp49uNJOxOnIAPZyXtUxZ5Qn3QBTGKA11Exjd9a5411rBrhDg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + '@npmcli/name-from-folder': 2.0.0 + glob: 10.3.10 + minimatch: 9.0.3 + read-package-json-fast: 3.0.2 + dev: true + + /@npmcli/name-from-folder@2.0.0: + resolution: {integrity: sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /@octokit/auth-token@2.5.0: resolution: {integrity: sha1-J8N+omwgXyhENAJHf/0mExHyHjY=} dependencies: @@ -2824,6 +2871,13 @@ packages: '@octokit/openapi-types': 18.1.1 dev: true + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true + optional: true + /@pkgr/core@0.1.0: resolution: {integrity: sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -3799,6 +3853,12 @@ packages: engines: {node: '>=10.13.0'} dev: false + /@types/concat-stream@2.0.3: + resolution: {integrity: sha512-3qe4oQAPNwVNwK4C9c8u+VJqv9kez+2MR4qJpoPFfXtgxxif1QbFusvXzK0/Wra2VX07smostI2VMmJNSpZjuQ==} + dependencies: + '@types/node': 20.10.6 + dev: true + /@types/debug@4.1.12: resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} dependencies: @@ -3819,6 +3879,10 @@ packages: dependencies: '@types/unist': 2.0.10 + /@types/is-empty@1.2.3: + resolution: {integrity: sha512-4J1l5d79hoIvsrKh5VUKVRA1aIdsOb10Hu5j3J2VfP/msDnfTdGPmNp2E1Wg+vs97Bktzo+MZePFFXSGoykYJw==} + dev: true + /@types/js-cookie@2.2.7: resolution: {integrity: sha1-ImqeMWgINaYYjoh/OYjmDATT9qM=} dev: false @@ -3923,6 +3987,14 @@ packages: resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} dev: true + /@types/supports-color@8.1.3: + resolution: {integrity: sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg==} + dev: true + + /@types/text-table@0.2.5: + resolution: {integrity: sha512-hcZhlNvMkQG/k1vcZ6yHOl6WAYftQ2MLfTHcYRZ2xYZFD8tGVnE3qFV0lj1smQeDSR7/yY0PyuUalauf33bJeA==} + dev: true + /@types/trusted-types@2.0.7: resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} dev: false @@ -4402,6 +4474,11 @@ packages: through: 2.3.8 dev: true + /abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha1-ftW7VZCLOy8bxVxq8WU7rafweTc=} peerDependencies: @@ -4932,6 +5009,10 @@ packages: resolution: {integrity: sha1-FfS5vO8BIETfMRQsFDM8r24CYNA=} dev: false + /buffer-from@1.1.2: + resolution: {integrity: sha1-KxRqb9cugLT1XSVfNe1Zo6mkG9U=} + dev: true + /builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -5051,6 +5132,11 @@ packages: engines: {node: '>=8'} dev: true + /ci-info@4.0.0: + resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} + engines: {node: '>=8'} + dev: true + /classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} dev: false @@ -5153,7 +5239,6 @@ packages: /comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - dev: false /commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} @@ -5201,6 +5286,16 @@ packages: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} dev: true + /concat-stream@2.0.0: + resolution: {integrity: sha1-QUz1r3kKSMYKub5FJ9VtXkETPLE=} + engines: {'0': node >= 6.0} + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 3.6.2 + typedarray: 0.0.6 + dev: true + /config-chain@1.1.13: resolution: {integrity: sha1-+tB5Wqamza/57Rto6d/5Q3LCMvQ=} dependencies: @@ -5611,6 +5706,10 @@ packages: resolution: {integrity: sha512-tzBmM2mFSnAq5BuxPSyin6qXb3yMe1wufJN7L7ZPcEWS5S+jI2dhKQEoqHVEcSMMXo/j5lcWpX5jzA6wLSmX6w==} dev: true + /eastasianwidth@0.2.0: + resolution: {integrity: sha1-aWzi7Aqg5uqTo5f/zySqeEDIJ8s=} + dev: true + /electron-to-chromium@1.4.622: resolution: {integrity: sha512-GZ47DEy0Gm2Z8RVG092CkFvX7SdotG57c4YZOe8W8qD4rOmk3plgeNmiLVRHP/Liqj1wRiY3uUUod9vb9hnxZA==} @@ -6434,6 +6533,14 @@ packages: engines: {node: '>=0.10.0'} dev: false + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: true + /format@0.2.2: resolution: {integrity: sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=} engines: {node: '>=0.4.x'} @@ -6612,6 +6719,18 @@ packages: resolution: {integrity: sha1-x1KXCHyFG5pXi9IX3VmpL1n+VG4=} dev: false + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 7.0.4 + path-scurry: 1.10.1 + dev: true + /glob@7.1.7: resolution: {integrity: sha1-Oxk+kjPwHULQs/eClLvutBj5SpA=} dependencies: @@ -7015,6 +7134,10 @@ packages: engines: {node: '>=8'} dev: true + /import-meta-resolve@3.1.1: + resolution: {integrity: sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw==} + dev: true + /import-meta-resolve@4.0.0: resolution: {integrity: sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==} dev: true @@ -7053,6 +7176,11 @@ packages: resolution: {integrity: sha1-op2kJbSIBvNHZ6Tvzjlyaa8oQyw=} dev: true + /ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /inline-style-parser@0.1.1: resolution: {integrity: sha1-7Io7QpJ06cCh8cT/qUU6f+9yzqE=} dev: false @@ -7145,6 +7273,10 @@ packages: has-tostringtag: 1.0.0 dev: true + /is-empty@1.2.0: + resolution: {integrity: sha512-F2FnH/otLNJv0J6wc73A5Xo7oHLNnqplYqZhUu01tD54DIPvxIRSTSLkrUB/M0nHO4vo1O9PDfN4KoTxCzLh/w==} + dev: true + /is-extendable@0.1.1: resolution: {integrity: sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=} engines: {node: '>=0.10.0'} @@ -7370,6 +7502,15 @@ packages: set-function-name: 2.0.1 dev: true + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + /java-properties@1.0.2: resolution: {integrity: sha1-zNH6c5B0OKW1w4mCJp0Odx/nghE=} engines: {node: '>= 0.6.0'} @@ -7423,6 +7564,11 @@ packages: /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha1-fEeAWpQxmSjgV3dAXcEuH3pO4C0=} + /json-parse-even-better-errors@3.0.1: + resolution: {integrity: sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /json-schema-traverse@0.4.1: resolution: {integrity: sha1-afaofZUTq4u4/mO9sJecRI5oRmA=} dev: true @@ -7557,6 +7703,11 @@ packages: /lines-and-columns@1.2.4: resolution: {integrity: sha1-7KKE910pZQeTCdwK2SVauy68FjI=} + /lines-and-columns@2.0.4: + resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /lint-staged@15.2.0: resolution: {integrity: sha512-TFZzUEV00f+2YLaVPWBWGAMq7So6yQx+GG8YRMDeOEIf95Zn5RyiLMsEiX4KTNl9vq/w+NqRJkLA1kPIo15ufQ==} engines: {node: '>=18.12.0'} @@ -7620,6 +7771,13 @@ packages: strip-bom: 3.0.0 dev: true + /load-plugin@6.0.1: + resolution: {integrity: sha512-YZyxJaWfN4F1xfPCyKFNIOL26vlFukmJY7wegxsriav4y2/0ZiICota6uFvyy52GjUj+tsPSjGLX+2m7kiU0+g==} + dependencies: + '@npmcli/config': 8.1.0 + import-meta-resolve: 4.0.0 + dev: true + /locate-path@2.0.0: resolution: {integrity: sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=} engines: {node: '>=4'} @@ -7792,6 +7950,11 @@ packages: engines: {node: '>=8'} dev: true + /markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + dev: true + /markdown-table@3.0.3: resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} @@ -8594,6 +8757,11 @@ packages: /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true + /mixin-deep@1.3.2: resolution: {integrity: sha1-ESC0PcNZp4Xc5ltVuC4lfM9HlWY=} engines: {node: '>=0.10.0'} @@ -8726,6 +8894,14 @@ packages: /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + /nopt@7.2.0: + resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + dependencies: + abbrev: 2.0.0 + dev: true + /normalize-package-data@2.5.0: resolution: {integrity: sha1-5m2xg4sgDB38IzIl0SyzZSDiNKg=} dependencies: @@ -8765,6 +8941,11 @@ packages: engines: {node: '>=14.16'} dev: true + /npm-normalize-package-bin@3.0.1: + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /npm-run-path@4.0.1: resolution: {integrity: sha1-t+zR5e1T2o43pV4cImnguX7XSOo=} engines: {node: '>=8'} @@ -9116,6 +9297,17 @@ packages: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + /parse-json@7.1.1: + resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==} + engines: {node: '>=16'} + dependencies: + '@babel/code-frame': 7.23.5 + error-ex: 1.3.2 + json-parse-even-better-errors: 3.0.1 + lines-and-columns: 2.0.4 + type-fest: 3.13.1 + dev: true + /parse-json@8.1.0: resolution: {integrity: sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==} engines: {node: '>=18'} @@ -9168,6 +9360,14 @@ packages: /path-parse@1.0.7: resolution: {integrity: sha1-+8EUtgykKzDZ2vWFjkvWi77bZzU=} + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.1.0 + minipass: 7.0.4 + dev: true + /path-type@4.0.0: resolution: {integrity: sha1-hO0BwKe6OAr+CdkKjBgNzZ0DBDs=} engines: {node: '>=8'} @@ -9414,6 +9614,11 @@ packages: react: 18.2.0 dev: false + /proc-log@3.0.0: + resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /process-nextick-args@2.0.1: resolution: {integrity: sha1-eCDZsWEgzFXKmud5JoCufbptf+I=} @@ -10195,6 +10400,14 @@ packages: loose-envify: 1.4.0 dev: false + /read-package-json-fast@3.0.2: + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + json-parse-even-better-errors: 3.0.1 + npm-normalize-package-bin: 3.0.1 + dev: true + /read-pkg-up@11.0.0: resolution: {integrity: sha512-LOVbvF1Q0SZdjClSefZ0Nz5z8u+tIE7mV5NibzmE9VYmDe9CaBbAVtz1veOSZbofrdsilxuDAYnFenukZVp8/Q==} engines: {node: '>=18'} @@ -10417,6 +10630,18 @@ packages: unist-util-visit: 4.1.2 dev: false + /remark-cli@12.0.0: + resolution: {integrity: sha512-IGxCo2VsXC/GS2YdlF7+S8DsUiyULyiauik01NFoiMIrOlbDhXjrKLD8hYazwQdD67nw2k7cwOBIxcK/cbNd9Q==} + hasBin: true + dependencies: + import-meta-resolve: 3.1.1 + markdown-extensions: 2.0.0 + remark: 15.0.1 + unified-args: 11.0.1 + transitivePeerDependencies: + - supports-color + dev: true + /remark-frontmatter@5.0.0: resolution: {integrity: sha512-XTFYvNASMe5iPN0719nPrdItC9aU0ssC4v14mH1BCi1u0n1gAocqcujWUrByftZTbLhRtiKRyjYTSIOcr69UVQ==} dependencies: @@ -11169,6 +11394,17 @@ packages: - supports-color dev: false + /remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-from-markdown: 2.0.0 + micromark-util-types: 2.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: true + /remark-preset-lint-consistent@5.1.2: resolution: {integrity: sha512-RQrWBFmyIkKfXtp9P1Fui7UbGSfXth9nuvRJUVnO0vfevBJe02iyMZWPokXSwkDOI/cM539wj0i3vrQupz+v5A==} dependencies: @@ -11296,6 +11532,14 @@ packages: unist-util-visit: 4.1.2 dev: true + /remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + dependencies: + '@types/mdast': 4.0.3 + mdast-util-to-markdown: 2.1.0 + unified: 11.0.4 + dev: true + /remark-textr@5.0.1: resolution: {integrity: sha512-Kd5q7I1LsZ04meMZJJwBze/wxiLhAItRTGevDeyx3K/WMd4juO6WbGLro0/99Ui6I+lYx/l90v0Ax4dVPj3/0Q==} dependencies: @@ -11312,6 +11556,17 @@ packages: mdast-util-toc: 7.0.0 dev: true + /remark@15.0.1: + resolution: {integrity: sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A==} + dependencies: + '@types/mdast': 4.0.3 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: true + /require-directory@2.1.1: resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} engines: {node: '>=0.10.0'} @@ -11856,6 +12111,24 @@ packages: strip-ansi: 6.0.1 dev: true + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + dev: true + + /string-width@6.1.0: + resolution: {integrity: sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==} + engines: {node: '>=16'} + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 10.3.0 + strip-ansi: 7.1.0 + dev: true + /string-width@7.0.0: resolution: {integrity: sha512-GPQHj7row82Hjo9hKZieKcHIhaAIKOJvFSIZXuCU9OASVZrMNUaZuz++SPVrBjnLsnk4k+z9f2EIypgxf2vNFw==} engines: {node: '>=18'} @@ -12128,6 +12401,11 @@ packages: has-flag: 4.0.0 dev: true + /supports-color@9.4.0: + resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==} + engines: {node: '>=12'} + dev: true + /supports-hyperlinks@3.0.0: resolution: {integrity: sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==} engines: {node: '>=14.18'} @@ -12447,6 +12725,10 @@ packages: is-typed-array: 1.1.12 dev: true + /typedarray@0.0.6: + resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=} + dev: true + /typescript@5.1.6: resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} @@ -12516,6 +12798,50 @@ packages: engines: {node: '>=18'} dev: true + /unified-args@11.0.1: + resolution: {integrity: sha512-WEQghE91+0s3xPVs0YW6a5zUduNLjmANswX7YbBfksHNDGMjHxaWCql4SR7c9q0yov/XiIEdk6r/LqfPjaYGcw==} + dependencies: + '@types/text-table': 0.2.5 + chalk: 5.3.0 + chokidar: 3.5.3 + comma-separated-tokens: 2.0.3 + json5: 2.2.3 + minimist: 1.2.8 + strip-ansi: 7.1.0 + text-table: 0.2.0 + unified-engine: 11.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /unified-engine@11.2.0: + resolution: {integrity: sha512-H9wEDpBSM0cpEUuuYAOIiPzLCVN0pjASZZ6FFNzgzYS/HHzl9tArk/ereOMGtcF8m8vgjzw+HrU3YN7oenT7Ww==} + dependencies: + '@types/concat-stream': 2.0.3 + '@types/debug': 4.1.12 + '@types/is-empty': 1.2.3 + '@types/node': 20.10.6 + '@types/unist': 3.0.2 + '@ungap/structured-clone': 1.2.0 + concat-stream: 2.0.0 + debug: 4.3.4 + glob: 10.3.10 + ignore: 5.3.0 + is-empty: 1.2.0 + is-plain-obj: 4.1.0 + load-plugin: 6.0.1 + parse-json: 7.1.1 + trough: 2.1.0 + unist-util-inspect: 8.0.0 + vfile: 6.0.1 + vfile-message: 4.0.2 + vfile-reporter: 8.1.0 + vfile-statistics: 3.0.0 + yaml: 2.3.4 + transitivePeerDependencies: + - supports-color + dev: true + /unified-lint-rule@1.0.6: resolution: {integrity: sha512-YPK15YBFwnsVorDFG/u0cVVQN5G2a3V8zv5/N6KN3TCG+ajKtaALcy7u14DCSrJI+gZeyYquFL9cioJXOGXSvg==} dependencies: @@ -12582,6 +12908,12 @@ packages: /unist-util-generated@2.0.1: resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==} + /unist-util-inspect@8.0.0: + resolution: {integrity: sha512-/3Wn/wU6/H6UEo4FoYUeo8KUePN8ERiZpQYFWYoihOsr1DoDuv80PeB0hobVZyYSvALa2e556bG1A1/AbwU4yg==} + dependencies: + '@types/unist': 3.0.2 + dev: true + /unist-util-is@3.0.0: resolution: {integrity: sha1-2ehDgcJGjoJinkpb6dfQWi3TJM0=} dev: true @@ -12791,6 +13123,33 @@ packages: unist-util-stringify-position: 4.0.0 dev: true + /vfile-reporter@8.1.0: + resolution: {integrity: sha512-NfHyHdkCcy0BsXiLA3nId29TY7W7hgpc8nd8Soe3imATx5N4/+mkLYdMR+Y6Zvu6BXMMi0FZsD4FLCm1dN85Pg==} + dependencies: + '@types/supports-color': 8.1.3 + string-width: 6.1.0 + supports-color: 9.4.0 + unist-util-stringify-position: 4.0.0 + vfile: 6.0.1 + vfile-message: 4.0.2 + vfile-sort: 4.0.0 + vfile-statistics: 3.0.0 + dev: true + + /vfile-sort@4.0.0: + resolution: {integrity: sha512-lffPI1JrbHDTToJwcq0rl6rBmkjQmMuXkAxsZPRS9DXbaJQvc642eCg6EGxcX2i1L+esbuhq+2l9tBll5v8AeQ==} + dependencies: + vfile: 6.0.1 + vfile-message: 4.0.2 + dev: true + + /vfile-statistics@3.0.0: + resolution: {integrity: sha512-/qlwqwWBWFOmpXujL/20P+Iuydil0rZZNglR+VNm6J0gpLHwuVM5s7g2TfVoswbXjZ4HuIhLMySEyIw5i7/D8w==} + dependencies: + vfile: 6.0.1 + vfile-message: 4.0.2 + dev: true + /vfile@5.3.7: resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} dependencies: @@ -12807,6 +13166,10 @@ packages: vfile-message: 4.0.2 dev: true + /walk-up-path@3.0.1: + resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==} + dev: true + /watchpack@2.4.0: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} engines: {node: '>=10.13.0'} @@ -12916,6 +13279,15 @@ packages: strip-ansi: 6.0.1 dev: true + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + dev: true + /wrap-ansi@9.0.0: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} engines: {node: '>=18'} diff --git a/src/app/oidc/auth/Auth.tsx b/src/app/oidc/auth/Auth.tsx new file mode 100644 index 0000000..5fe1ad8 --- /dev/null +++ b/src/app/oidc/auth/Auth.tsx @@ -0,0 +1,29 @@ +'use client'; + +import { useRouter } from 'next/navigation'; +import queryString from 'query-string'; +import React from 'react'; + +export default function Auth({ oidc }: any) { + const { client, server, AUTH_DATA } = oidc; + const { url } = server; + const { client_id, redirect_uri } = client; + + const router = useRouter(); + React.useEffect(() => { + const authData = localStorage.getItem(AUTH_DATA); + if (authData) { + // todo validate auth + router.push('/chat'); + return; + } + const query = queryString.stringify({ + client_id, + redirect_uri: `${window.location.origin}${redirect_uri}`, + response_type: 'code', + scope: 'openid profile email groups offline_access', + }); + window.location.href = `${url}/oidc/auth?${query}`; + }, []); + return <> ; +} diff --git a/src/app/oidc/auth/page.tsx b/src/app/oidc/auth/page.tsx index 107c4fd..16f3b8c 100644 --- a/src/app/oidc/auth/page.tsx +++ b/src/app/oidc/auth/page.tsx @@ -1,31 +1,11 @@ -'use client'; +'use server'; -import { useRouter } from 'next/navigation'; -import queryString from 'query-string'; import React from 'react'; import oidc from '@/config/oidc.mjs'; -const { client, server, AUTH_DATA } = oidc; -const { url } = server; -const { client_id, redirect_uri } = client; +import Auth from './Auth'; -export default function Auth() { - const router = useRouter(); - React.useEffect(() => { - const authData = localStorage.getItem(AUTH_DATA); - if (authData) { - // todo validate auth - router.push('/chat'); - return; - } - const query = queryString.stringify({ - client_id, - redirect_uri: `${window.location.origin}${redirect_uri}`, - response_type: 'code', - scope: 'openid profile email groups offline_access', - }); - window.location.href = `${url}/oidc/auth?${query}`; - }, []); - return <> ; +export default async function AuthServer() { + return ; } diff --git a/src/app/oidc/logout/Logout.tsx b/src/app/oidc/logout/Logout.tsx new file mode 100644 index 0000000..ed778c7 --- /dev/null +++ b/src/app/oidc/logout/Logout.tsx @@ -0,0 +1,16 @@ +'use client'; + +import React from 'react'; + +export default function Logout({ oidc }: any) { + const { client, AUTH_DATA, server } = oidc; + const { redirect_uri } = client; + const { url } = server; + React.useEffect(() => { + localStorage.removeItem(AUTH_DATA); + window.location.href = `${url}/oidc/logout/remove-auth-data?redirect=${encodeURIComponent( + `${url}/oidc/auth?redirect_uri=${location.origin}${redirect_uri}&response_type=code&scope=openid+profile+email+groups+offline_access` + )}`; + }, []); + return <> ; +} diff --git a/src/app/oidc/logout/page.tsx b/src/app/oidc/logout/page.tsx index 8e1e73f..4c16385 100644 --- a/src/app/oidc/logout/page.tsx +++ b/src/app/oidc/logout/page.tsx @@ -1,19 +1,11 @@ -'use client'; +'use server'; import React from 'react'; import oidc from '@/config/oidc.mjs'; -const { client, AUTH_DATA, server } = oidc; -const { redirect_uri } = client; -const { url } = server; +import Logout from './Logout'; -export default function Auth() { - React.useEffect(() => { - localStorage.removeItem(AUTH_DATA); - window.location.href = `${url}/oidc/logout/remove-auth-data?redirect=${encodeURIComponent( - `${url}/oidc/auth?redirect_uri=${location.origin}${redirect_uri}&response_type=code&scope=openid+profile+email+groups+offline_access` - )}`; - }, []); - return <> ; +export default async function LogoutServer() { + return ; } diff --git a/src/config/oidc.mjs b/src/config/oidc.mjs index 4c6fca3..123e853 100644 --- a/src/config/oidc.mjs +++ b/src/config/oidc.mjs @@ -2,11 +2,11 @@ export default { AUTH_DATA: 'authData', server: { - url: 'https://portal.172.22.96.136.nip.io', + url: process.env.OIDC_SERVER_URL, }, client: { - client_id: 'bff-client', - client_secret: '61324af0-1234-4f61-b110-ef57013267d6', - redirect_uri: '/oidc/callback', + client_id: process.env.CLIENT_ID, + client_secret: process.env.CLIENT_SECRET, + redirect_uri: process.env.CLIENT_REDIRECT_URI, }, }; From 142ebd5b7c635781738cd6a8f4f5866b779857fc Mon Sep 17 00:00:00 2001 From: Vsion <442153598@qq.com> Date: Fri, 19 Jan 2024 16:59:45 +0800 Subject: [PATCH 4/5] fix: callback page miss redirect_uri --- src/app/oidc/callback/Callback.tsx | 40 ++++++++++++++++++++++++++++++ src/app/oidc/callback/page.tsx | 38 +++------------------------- 2 files changed, 44 insertions(+), 34 deletions(-) create mode 100644 src/app/oidc/callback/Callback.tsx diff --git a/src/app/oidc/callback/Callback.tsx b/src/app/oidc/callback/Callback.tsx new file mode 100644 index 0000000..ab50da9 --- /dev/null +++ b/src/app/oidc/callback/Callback.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { useRouter, useSearchParams } from 'next/navigation'; +import React from 'react'; +import { useDispatch } from 'react-redux'; + +import oidc from '@/config/oidc.mjs'; + +const { AUTH_DATA } = oidc; + +export default function Callback({ redirect_uri }: { redirect_uri: string }) { + const searchParams = useSearchParams(); + const dispatch = useDispatch(); + const router = useRouter(); + const code = searchParams.get('code'); + const fetchAuth = async () => { + fetch(`/oidc/token?code=${code}&redirect_uri=${location.origin}${redirect_uri}`, { + method: 'POST', + }) + .then(res => res.json()) + .then(res => { + if (res.data?.errors) { + console.warn(res.data?.errors); + return; + } + if (res.data) { + localStorage.setItem(AUTH_DATA, JSON.stringify(res.data)); + dispatch({ + type: 'SAVE_AUTH_DATA', + authData: res.data, + }); + router.push('/chat'); + } + }); + }; + React.useEffect(() => { + fetchAuth(); + }, []); + return <> ; +} diff --git a/src/app/oidc/callback/page.tsx b/src/app/oidc/callback/page.tsx index a364867..217c333 100644 --- a/src/app/oidc/callback/page.tsx +++ b/src/app/oidc/callback/page.tsx @@ -1,41 +1,11 @@ -'use client'; +'use server'; -import { useRouter, useSearchParams } from 'next/navigation'; import React from 'react'; -import { useDispatch } from 'react-redux'; import oidc from '@/config/oidc.mjs'; -const { client, AUTH_DATA } = oidc; -const { redirect_uri } = client; +import Callback from './Callback'; -export default function Auth() { - const searchParams = useSearchParams(); - const dispatch = useDispatch(); - const router = useRouter(); - const code = searchParams.get('code'); - const fetchAuth = async () => { - fetch(`/oidc/token?code=${code}&redirect_uri=${location.origin}${redirect_uri}`, { - method: 'POST', - }) - .then(res => res.json()) - .then(res => { - if (res.data?.errors) { - console.warn(res.data?.errors); - return; - } - if (res.data) { - localStorage.setItem(AUTH_DATA, JSON.stringify(res.data)); - dispatch({ - type: 'SAVE_AUTH_DATA', - authData: res.data, - }); - router.push('/chat'); - } - }); - }; - React.useEffect(() => { - fetchAuth(); - }, []); - return <> ; +export default async function CallbackServer() { + return ; } From 5789b636afb173c16922e85cbc32b369b8321ea7 Mon Sep 17 00:00:00 2001 From: Vsion <442153598@qq.com> Date: Fri, 19 Jan 2024 23:57:43 +0800 Subject: [PATCH 5/5] feat: oidc in server side --- src/app/oidc/auth/Auth.tsx | 29 ------------- src/app/oidc/auth/page.tsx | 22 ++++++++-- src/app/oidc/callback/Callback.tsx | 43 +++++++------------ src/app/oidc/callback/page.tsx | 12 ++++-- src/app/oidc/logout/Logout.tsx | 16 ------- src/app/oidc/logout/page.tsx | 14 +++--- src/app/oidc/token/route.ts | 2 + .../setting/SettingClient/BtnList/index.tsx | 2 + src/config/oidc.mjs | 1 + src/layout/GlobalLayout/index.tsx | 2 +- src/utils/constants.ts | 1 + src/utils/index.ts | 1 - 12 files changed, 57 insertions(+), 88 deletions(-) delete mode 100644 src/app/oidc/auth/Auth.tsx delete mode 100644 src/app/oidc/logout/Logout.tsx create mode 100644 src/utils/constants.ts diff --git a/src/app/oidc/auth/Auth.tsx b/src/app/oidc/auth/Auth.tsx deleted file mode 100644 index 5fe1ad8..0000000 --- a/src/app/oidc/auth/Auth.tsx +++ /dev/null @@ -1,29 +0,0 @@ -'use client'; - -import { useRouter } from 'next/navigation'; -import queryString from 'query-string'; -import React from 'react'; - -export default function Auth({ oidc }: any) { - const { client, server, AUTH_DATA } = oidc; - const { url } = server; - const { client_id, redirect_uri } = client; - - const router = useRouter(); - React.useEffect(() => { - const authData = localStorage.getItem(AUTH_DATA); - if (authData) { - // todo validate auth - router.push('/chat'); - return; - } - const query = queryString.stringify({ - client_id, - redirect_uri: `${window.location.origin}${redirect_uri}`, - response_type: 'code', - scope: 'openid profile email groups offline_access', - }); - window.location.href = `${url}/oidc/auth?${query}`; - }, []); - return <> ; -} diff --git a/src/app/oidc/auth/page.tsx b/src/app/oidc/auth/page.tsx index 16f3b8c..6873e41 100644 --- a/src/app/oidc/auth/page.tsx +++ b/src/app/oidc/auth/page.tsx @@ -1,11 +1,25 @@ 'use server'; -import React from 'react'; - import oidc from '@/config/oidc.mjs'; +import queryString from 'query-string'; +import { redirect } from 'next/navigation'; +// import { getUserData } from '@/app/actions/user'; -import Auth from './Auth'; +const { client, server } = oidc; +const { url } = server; +const { client_id, redirect_uri, origin } = client; export default async function AuthServer() { - return ; + const query = queryString.stringify({ + client_id, + redirect_uri: `${origin}${redirect_uri}`, + response_type: 'code', + scope: 'openid profile email groups offline_access', + }); + // todo validate + // const user = await getUserData(); + // if (!user) { + // redirect(`${url}/oidc/auth?${query}`); + // } + redirect(`${url}/oidc/auth?${query}`); } diff --git a/src/app/oidc/callback/Callback.tsx b/src/app/oidc/callback/Callback.tsx index ab50da9..752bbcb 100644 --- a/src/app/oidc/callback/Callback.tsx +++ b/src/app/oidc/callback/Callback.tsx @@ -1,40 +1,29 @@ 'use client'; -import { useRouter, useSearchParams } from 'next/navigation'; +import { AUTH_DATA } from '@/utils/constants'; +import { useRouter } from 'next/navigation'; import React from 'react'; import { useDispatch } from 'react-redux'; -import oidc from '@/config/oidc.mjs'; - -const { AUTH_DATA } = oidc; - -export default function Callback({ redirect_uri }: { redirect_uri: string }) { - const searchParams = useSearchParams(); +export default function Callback({ data: res }: { data: any }) { const dispatch = useDispatch(); const router = useRouter(); - const code = searchParams.get('code'); - const fetchAuth = async () => { - fetch(`/oidc/token?code=${code}&redirect_uri=${location.origin}${redirect_uri}`, { - method: 'POST', - }) - .then(res => res.json()) - .then(res => { - if (res.data?.errors) { - console.warn(res.data?.errors); - return; - } - if (res.data) { - localStorage.setItem(AUTH_DATA, JSON.stringify(res.data)); - dispatch({ - type: 'SAVE_AUTH_DATA', - authData: res.data, - }); - router.push('/chat'); - } + const saveAuth = async () => { + if (res?.data?.errors) { + console.warn(res?.data?.errors); + return; + } + if (res?.data) { + localStorage.setItem(AUTH_DATA, JSON.stringify(res.data)); + dispatch({ + type: 'SAVE_AUTH_DATA', + authData: res.data, }); + router.push('/chat'); + } }; React.useEffect(() => { - fetchAuth(); + saveAuth(); }, []); return <> ; } diff --git a/src/app/oidc/callback/page.tsx b/src/app/oidc/callback/page.tsx index 217c333..7b905c6 100644 --- a/src/app/oidc/callback/page.tsx +++ b/src/app/oidc/callback/page.tsx @@ -1,11 +1,15 @@ 'use server'; import React from 'react'; - import oidc from '@/config/oidc.mjs'; - import Callback from './Callback'; -export default async function CallbackServer() { - return ; +export default async function CallbackServer({ searchParams }: any) { + const { code } = searchParams; + const { origin, redirect_uri } = oidc.client + const res = await fetch(`${origin}/oidc/token?code=${code}&redirect_uri=${origin}${redirect_uri}`, { + method: 'POST', + }); + const data = await res.json(); + return ; } diff --git a/src/app/oidc/logout/Logout.tsx b/src/app/oidc/logout/Logout.tsx deleted file mode 100644 index ed778c7..0000000 --- a/src/app/oidc/logout/Logout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -'use client'; - -import React from 'react'; - -export default function Logout({ oidc }: any) { - const { client, AUTH_DATA, server } = oidc; - const { redirect_uri } = client; - const { url } = server; - React.useEffect(() => { - localStorage.removeItem(AUTH_DATA); - window.location.href = `${url}/oidc/logout/remove-auth-data?redirect=${encodeURIComponent( - `${url}/oidc/auth?redirect_uri=${location.origin}${redirect_uri}&response_type=code&scope=openid+profile+email+groups+offline_access` - )}`; - }, []); - return <> ; -} diff --git a/src/app/oidc/logout/page.tsx b/src/app/oidc/logout/page.tsx index 4c16385..b920fbc 100644 --- a/src/app/oidc/logout/page.tsx +++ b/src/app/oidc/logout/page.tsx @@ -1,11 +1,13 @@ 'use server'; -import React from 'react'; - -import oidc from '@/config/oidc.mjs'; - -import Logout from './Logout'; +import oidc from '@/config/oidc.mjs' +import { redirect } from 'next/navigation'; export default async function LogoutServer() { - return ; + const { client, server } = oidc; + const { redirect_uri, origin } = client; + const { url } = server; + redirect(`${url}/oidc/logout/remove-auth-data?redirect=${encodeURIComponent( + `${url}/oidc/auth?redirect_uri=${origin}${redirect_uri}&response_type=code&scope=openid+profile+email+groups+offline_access` + )}`); } diff --git a/src/app/oidc/token/route.ts b/src/app/oidc/token/route.ts index 666fa00..d049896 100644 --- a/src/app/oidc/token/route.ts +++ b/src/app/oidc/token/route.ts @@ -1,3 +1,5 @@ +'use server'; + import { type NextRequest, NextResponse } from 'next/server'; import oidc from '@/config/oidc.mjs'; diff --git a/src/app/setting/SettingClient/BtnList/index.tsx b/src/app/setting/SettingClient/BtnList/index.tsx index c51494f..a23a67d 100644 --- a/src/app/setting/SettingClient/BtnList/index.tsx +++ b/src/app/setting/SettingClient/BtnList/index.tsx @@ -15,6 +15,7 @@ import React from 'react'; import BtnsBlock, { Btn } from '@/components/BtnsBlock'; import { useStyles } from './styles'; +import { AUTH_DATA } from '@/utils/constants'; // interface SettingBtnListProps {} @@ -81,6 +82,7 @@ const SettingBtnList = React.memo(() => { icon: LogOut, title: '退出登录', onClick: () => { + localStorage.removeItem(AUTH_DATA); router.push('/oidc/logout'); }, }, diff --git a/src/config/oidc.mjs b/src/config/oidc.mjs index 123e853..e5ce4cc 100644 --- a/src/config/oidc.mjs +++ b/src/config/oidc.mjs @@ -8,5 +8,6 @@ export default { client_id: process.env.CLIENT_ID, client_secret: process.env.CLIENT_SECRET, redirect_uri: process.env.CLIENT_REDIRECT_URI, + origin: process.env.CLIENT_ORIGIN, }, }; diff --git a/src/layout/GlobalLayout/index.tsx b/src/layout/GlobalLayout/index.tsx index 9436cc0..13b83ff 100644 --- a/src/layout/GlobalLayout/index.tsx +++ b/src/layout/GlobalLayout/index.tsx @@ -32,7 +32,7 @@ export default function GlobalLayout({ children }: { children: React.ReactNode } theme: localStorage.getItem('theme') || 'light', activeChat: 'name', }); - }, []); + }, [ pathname ]); const store = useStore(initialState); return ( diff --git a/src/utils/constants.ts b/src/utils/constants.ts new file mode 100644 index 0000000..93b70f5 --- /dev/null +++ b/src/utils/constants.ts @@ -0,0 +1 @@ +export const AUTH_DATA = 'authData'; diff --git a/src/utils/index.ts b/src/utils/index.ts index eb8b19a..989498b 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,5 @@ import { headers } from 'next/headers'; import { UAParser } from 'ua-parser-js'; - /** * check mobile device in server */