feat: 完善项目架构和功能
- 修复路由配置,实现根路径自动重定向到/project - 新增Gitea OAuth认证系统和相关组件 - 完善日志系统实现,包含pino日志工具和中间件 - 重构页面结构,分离项目管理和环境管理页面 - 新增CORS、Session等关键中间件 - 优化前端请求封装和类型定义 - 修复TypeScript类型错误和参数传递问题
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
import { Route, Routes } from 'react-router';
|
||||
import { Route, Routes, Navigate } from 'react-router';
|
||||
import Home from '@pages/home';
|
||||
import Login from '@pages/login';
|
||||
import Application from '@pages/application';
|
||||
import Project from '@pages/project';
|
||||
import Env from '@pages/env';
|
||||
|
||||
import '@styles/index.css';
|
||||
const App = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />}>
|
||||
<Route path="application" element={<Application />} index />
|
||||
<Route index element={<Navigate to="project" replace />} />
|
||||
<Route path="project" element={<Project />} />
|
||||
<Route path="env" element={<Env />} />
|
||||
</Route>
|
||||
<Route path="/login" element={<Login />} />
|
||||
</Routes>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { useState } from "react";
|
||||
|
||||
function Application() {
|
||||
const [apps, setApps] = useState<Application[]>([]);
|
||||
return (
|
||||
<div>
|
||||
application
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Application;
|
||||
12
apps/web/src/pages/env/index.tsx
vendored
Normal file
12
apps/web/src/pages/env/index.tsx
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useState } from "react";
|
||||
|
||||
function Env() {
|
||||
const [env, setEnv] = useState([]);
|
||||
return (
|
||||
<div>
|
||||
env page
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Env;
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@arco-design/web-react/icon';
|
||||
import { useState } from 'react';
|
||||
import Logo from '@assets/images/logo.svg?react';
|
||||
import { Outlet } from 'react-router';
|
||||
import { Link, Outlet } from 'react-router';
|
||||
|
||||
function Home() {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
@@ -33,24 +33,16 @@ function Home() {
|
||||
defaultSelectedKeys={['0_1']}
|
||||
>
|
||||
<Menu.Item key="0">
|
||||
<IconApps />
|
||||
Navigation 1
|
||||
<Link to="/project" className="flex flex-row items-center">
|
||||
<IconApps fontSize={18} />
|
||||
项目管理
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="1">
|
||||
<IconRobot />
|
||||
Navigation 2
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2">
|
||||
<IconBulb />
|
||||
Navigation 3
|
||||
</Menu.Item>
|
||||
<Menu.Item key="3">
|
||||
<IconSafe />
|
||||
Navigation 4
|
||||
</Menu.Item>
|
||||
<Menu.Item key="4">
|
||||
<IconFire />
|
||||
Navigation 5
|
||||
<Link to="/env" className="flex flex-row items-center">
|
||||
<IconRobot fontSize={18} />
|
||||
环境管理
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Layout.Sider>
|
||||
|
||||
@@ -1,16 +1,41 @@
|
||||
import { Input, Space } from '@arco-design/web-react';
|
||||
import { IconUser, IconInfoCircle } from '@arco-design/web-react/icon';
|
||||
import { Button } from '@arco-design/web-react';
|
||||
import Gitea from '@assets/images/gitea.svg?react';
|
||||
import { loginService } from './service';
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router';
|
||||
|
||||
function Login() {
|
||||
const [ searchParams ] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const authCode = searchParams.get('code');
|
||||
|
||||
const onLoginClick = async () => {
|
||||
const url = await loginService.getAuthUrl();
|
||||
if (url) {
|
||||
window.location.href = url;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (authCode) {
|
||||
loginService.login(authCode, navigate);
|
||||
}
|
||||
}, [authCode, navigate]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Space direction='vertical'>
|
||||
<Input placeholder="username" prefix={<IconUser />} size="large" />
|
||||
<Input.Password
|
||||
placeholder="password"
|
||||
prefix={<IconInfoCircle />}
|
||||
size="large"
|
||||
/>
|
||||
</Space>
|
||||
<div className="flex justify-center items-center h-[100vh]">
|
||||
<Button
|
||||
type="primary"
|
||||
color="green"
|
||||
shape="round"
|
||||
size="large"
|
||||
onClick={onLoginClick}
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<Gitea className="w-5 h-5" />
|
||||
<span>Gitea 授权登录</span>
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
39
apps/web/src/pages/login/service.ts
Normal file
39
apps/web/src/pages/login/service.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { net } from '@shared';
|
||||
import type { AuthURLResponse } from './types';
|
||||
import type { NavigateFunction } from 'react-router';
|
||||
import { Notification } from '@arco-design/web-react';
|
||||
|
||||
class LoginService {
|
||||
async getAuthUrl() {
|
||||
const { code, data } = await net.request<AuthURLResponse>({
|
||||
method: 'GET',
|
||||
url: '/api/auth/url',
|
||||
params: {
|
||||
redirect: encodeURIComponent(`${location.origin}/login`),
|
||||
},
|
||||
});
|
||||
if (code === 0) {
|
||||
return data.url;
|
||||
}
|
||||
}
|
||||
|
||||
async login(authCode: string, navigate: NavigateFunction) {
|
||||
const { data, code } = await net.request<AuthURLResponse>({
|
||||
method: 'POST',
|
||||
url: '/api/auth/login',
|
||||
data: {
|
||||
code: authCode,
|
||||
},
|
||||
});
|
||||
if (code === 0) {
|
||||
localStorage.setItem('user', JSON.stringify(data));
|
||||
navigate('/');
|
||||
Notification.success({
|
||||
title: '提示',
|
||||
content: '登录成功'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const loginService = new LoginService();
|
||||
5
apps/web/src/pages/login/types.ts
Normal file
5
apps/web/src/pages/login/types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { APIResponse } from '@shared';
|
||||
|
||||
export type AuthURLResponse = APIResponse<{
|
||||
url: string;
|
||||
}>
|
||||
8
apps/web/src/pages/project/index.tsx
Normal file
8
apps/web/src/pages/project/index.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
function Project() {
|
||||
const [projects, setProjects] = useState([]);
|
||||
return <div>project page</div>;
|
||||
}
|
||||
|
||||
export default Project;
|
||||
@@ -1,15 +1,15 @@
|
||||
enum AppStatus {
|
||||
enum BuildStatus {
|
||||
Idle = "Pending",
|
||||
Running = "Running",
|
||||
Stopped = "Stopped",
|
||||
}
|
||||
|
||||
export interface Application {
|
||||
export interface Project {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
git: string;
|
||||
env: Record<string, string>;
|
||||
createdAt: string;
|
||||
status: AppStatus;
|
||||
status: BuildStatus;
|
||||
}
|
||||
Reference in New Issue
Block a user