feat: add github login

This commit is contained in:
xion 2024-10-01 20:30:55 +08:00
parent 7d4e6bd299
commit 6528ee78c6
11 changed files with 213 additions and 25 deletions

View File

@ -9,6 +9,7 @@ import { App as PromptApp } from './pages/prompt';
import { App as AiAgentApp } from './pages/ai-agent';
import { App as UserApp } from './pages/user';
import { App as ChatApp } from './pages/chat-manager';
import { App as GitHubApp } from './pages/github';
import '@abearxiong/container/dist/container.css';
@ -31,6 +32,7 @@ export const App = () => {
<Route path='/agent/*' element={<AiAgentApp />} />
<Route path='/user/*' element={<UserApp />} />
<Route path='/chat/*' element={<ChatApp />} />
<Route path='/github/*' element={<GitHubApp />} />
<Route path='/404' element={<div>404</div>} />
<Route path='*' element={<div>404</div>} />
</Routes>

View File

@ -0,0 +1,78 @@
import { useShallow } from 'zustand/react/shallow';
import { useMenuStore } from './store';
import clsx from 'clsx';
import { Button, message } from 'antd';
import { CloseOutlined, CodeOutlined, DashboardOutlined, HomeOutlined, MessageOutlined, ReadOutlined, RocketOutlined, SmileOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router';
const meun = [
{
title: 'Home',
icon: <HomeOutlined />,
link: '/map',
},
{
title: 'Panel',
icon: <DashboardOutlined />,
link: '/panel/edit/list',
},
{
title: 'Prompt',
icon: <MessageOutlined />,
link: '/prompt',
},
{
title: 'Container',
icon: <CodeOutlined />,
link: '/container/edit/list',
},
{
title: 'Agent',
icon: <RocketOutlined />,
link: '/agent/edit/list',
},
{
title: 'Chat Prompt',
icon: <ReadOutlined />,
link: '/chat/chat-prompt/list',
},
{
title: 'About',
icon: <SmileOutlined />,
},
];
export const LayoutMenu = () => {
const { open, setOpen } = useMenuStore(useShallow((state) => ({ open: state.open, setOpen: state.setOpen })));
const navigate = useNavigate();
return (
<div className={clsx('w-full h-full absolute z-20', !open && 'hidden')}>
<div className='bg-white w-full absolute h-full opacity-60 z-0'></div>
<div className='w-[300px] h-full absolute top-0 left-0 bg-white'>
<div className='flex justify-between p-6 font-bold items-center'>
Envision
<div>
<Button icon={<CloseOutlined />} onClick={() => setOpen(false)}></Button>
</div>
</div>
<div className='mt-3 font-medium'>
{meun.map((item, index) => {
return (
<div
key={index}
className='flex items-center p-4 gap-3 cursor-pointer hover:bg-slate-200 rounded-md'
onClick={() => {
if (item.link) navigate(`${item.link}`);
else {
message.info('About Envision');
}
setOpen(false);
}}>
<div className='w-6 h-6'>{item.icon}</div>
<div>{item.title}</div>
</div>
);
})}
</div>
</div>
</div>
);
};

View File

@ -1,14 +1,33 @@
import { AiMoudle } from '@/pages/ai-chat';
import { MenuOutlined } from '@ant-design/icons';
import { Button } from 'antd';
import { Outlet } from 'react-router-dom';
import { LayoutMenu } from './Menu';
import { useMenuStore } from './store';
import { useShallow } from 'zustand/react/shallow';
type LayoutMainProps = {
title?: React.ReactNode;
children?: React.ReactNode;
};
export const LayoutMain = (props: LayoutMainProps) => {
const menuStore = useMenuStore(
useShallow((state) => {
return { open: state.open, setOpen: state.setOpen };
}),
);
return (
<div className='flex w-full h-full flex-col'>
<div className='layout-menu'>{props.title}</div>
<div className='flex w-full h-full flex-col relative'>
<LayoutMenu />
<div className='layout-menu items-center'>
<Button
className='mr-4'
onClick={() => {
menuStore.setOpen(true);
}}
icon={<MenuOutlined />}></Button>
<div className='flex flex-grow justify-between'>{props.title}</div>
</div>
<div
className='flex'
style={{

View File

@ -0,0 +1,10 @@
import { create } from 'zustand';
export type MenuStore = {
open: boolean;
setOpen: (open: boolean) => void;
};
export const useMenuStore = create<MenuStore>((set) => ({
open: false,
setOpen: (open) => set({ open }),
}));

View File

@ -1,14 +1,5 @@
import { Outlet } from 'react-router';
import { LayoutMain } from '@/modules/layout';
export const Main = () => {
return (
<div className='flex w-full h-full flex-col bg-gray-200'>
<div className='layout-menu'>Agent</div>
<div className='flex-grow w-full'>
<div className='w-full h-full overflow-hidden'>
<Outlet />
</div>
</div>
</div>
);
return <LayoutMain title='Agent' />;
};

View File

@ -46,7 +46,7 @@ export const useContainerStore = create<ContainerStore>((set, get) => {
});
if (res.code === 200) {
message.success('Success');
set({ showEdit: false, formData: [] });
set({ showEdit: false, formData: res.data });
getList();
} else {
message.error(res.message || 'Request failed');

View File

@ -0,0 +1,28 @@
import { useEffect, useState } from 'react';
import { getCodeFromUrl, useGithubStore } from './store';
import { useShallow } from 'zustand/react/shallow';
export const Callback = () => {
const [code, setCode] = useState('');
const githubStore = useGithubStore(
useShallow((state) => {
return {
getToken: state.getToken,
githubToken: state.githubToken,
};
}),
);
useEffect(() => {
setCode(getCodeFromUrl());
githubStore.getToken();
}, []);
return (
<div className='w-full h-full'>
<div className='p-4'>
<h1>Callback</h1>
<p>code: {code}</p>
<p>access_token: {githubStore.githubToken}</p>
</div>
</div>
);
};

View File

@ -0,0 +1,21 @@
import { nanoid } from 'nanoid';
import { useEffect, useState } from 'react';
const getGithubAuth = () => {
const client_id = 'Ov23littcejmbA5iKrhK';
const redirect_uri = 'https://envision.xiongxiao.me/github/callback';
const scope = 'user';
const state = 'abc123' && nanoid(6);
return `https://github.com/login/oauth/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}&scope=${scope}&state=${state}`;
};
export const Login = () => {
const [url, setUrl] = useState('');
useEffect(() => {
setUrl(getGithubAuth());
}, []);
return (
<div>
<a href={url}>Github登录</a>
</div>
);
};

View File

@ -0,0 +1,16 @@
import { LayoutMain } from '@/modules/layout';
import { Navigate, Route, Routes } from 'react-router-dom';
import { Login } from './Login';
import { Callback } from './Callback';
export const App = () => {
return (
<Routes>
<Route element={<LayoutMain title='Github登录' />}>
<Route path='/' element={<Navigate to='/github/login' />}></Route>
<Route path='login' element={<Login />} />
<Route path='/callback' element={<Callback />}></Route>
</Route>
</Routes>
);
};

View File

@ -0,0 +1,32 @@
import { query } from '@/modules';
import { message } from 'antd';
import { create } from 'zustand';
export const getCodeFromUrl = () => {
const searchParams = new URLSearchParams(window.location.search);
return searchParams.get('code') || '';
};
type GithubStore = {
githubToken?: string;
getToken: () => Promise<any>;
};
export const useGithubStore = create<GithubStore>((set) => ({
githubToken: '',
getToken: async () => {
const code = getCodeFromUrl();
if (!code) {
message.error('code不存在');
}
const loaded = message.loading('获取token中', 0);
const res = await query.post({
path: 'github',
key: 'token',
code,
});
loaded();
if (res.code === 200) {
const { githubToken } = res.data;
set({ githubToken: githubToken });
localStorage.setItem('githubToken', githubToken);
}
},
}));

View File

@ -1,13 +1,4 @@
import { AiMoudle } from '@/pages/ai-chat';
import { Outlet } from 'react-router';
import { LayoutMain } from '@/modules/layout';
export const Main = () => {
return (
<div className='w-full h-full flex'>
<div className='flex-grow h-full'>
<Outlet />
</div>
<AiMoudle />
</div>
);
return <LayoutMain title='Prompt'></LayoutMain>;
};