This commit is contained in:
2025-08-20 22:02:42 +08:00
parent b40bb84468
commit 1a80726cd4
22 changed files with 1462 additions and 14993 deletions

View File

@ -1,7 +1,7 @@
# UAdmin 配置文件
server:
host: "127.0.0.1"
port: 3000
port: 8080
database:
url: "mysql://root:123456@127.0.0.1:3306/uadmin"
@ -10,5 +10,5 @@ database:
sqlx_logging: true
jwt:
secret: "your-secret-key-here-please-change-in-production"
secret: "test"
expires_in: 86400 # 24 hours in seconds

View File

@ -1,7 +1,7 @@
# 开发环境配置
PORT=3001
REACT_APP_ENV=development
REACT_APP_API_URL=http://localhost:8080
REACT_APP_API_URL=http://localhost:3001
REACT_APP_API_TIMEOUT=10000
REACT_APP_DEBUG=true
GENERATE_SOURCEMAP=true

30
frontend/index.html Normal file
View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="UAdmin - 用户权限管理系统"
/>
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>UAdmin - 用户权限管理系统</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
</html>

15599
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,12 +4,8 @@
"private": true,
"dependencies": {
"@ant-design/icons": "^6.0.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.7.0",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^13.5.0",
"@ant-design/v5-patch-for-react-19": "^1.0.3",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.126",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@types/react-router-dom": "^5.3.3",
@ -18,24 +14,30 @@
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router-dom": "^7.8.1",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"devDependencies": {
"env-cmd": "^10.1.0"
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.7.0",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^24.3.0",
"@vitejs/plugin-react": "^4.7.0",
"env-cmd": "^10.1.0",
"typescript": "^5.9.2",
"vite": "^5.4.19"
},
"scripts": {
"start": "react-scripts start",
"start:dev": "env-cmd -f .env.development react-scripts start",
"start:prod": "env-cmd -f .env.production react-scripts start",
"start:test": "env-cmd -f .env.test react-scripts start",
"build": "env-cmd -f .env.production react-scripts build",
"build:dev": "env-cmd -f .env.development react-scripts build",
"build:prod": "env-cmd -f .env.production react-scripts build",
"build:test": "env-cmd -f .env.test react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
"start": "vite",
"dev": "env-cmd -f .env.development vite",
"prod": "env-cmd -f .env.production vite",
"start:test": "env-cmd -f .env.test vite",
"build": "env-cmd -f .env.production vite build",
"build:dev": "env-cmd -f .env.development vite build",
"build:prod": "env-cmd -f .env.production vite build",
"build:test": "env-cmd -f .env.test vite build",
"test": "vitest",
"preview": "vite preview"
},
"eslintConfig": {
"extends": [

View File

@ -1,6 +1,6 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { ConfigProvider } from 'antd';
import { ConfigProvider, Spin, App as AntdApp } from 'antd';
import zhCN from 'antd/locale/zh_CN';
import { AuthProvider, useAuth } from './hooks/useAuth';
import Layout from './components/Layout';
@ -13,12 +13,30 @@ import Permissions from './pages/Permissions';
import Menus from './pages/Menus';
import './App.css';
// 全局message配置
import { message } from 'antd';
message.config({
top: 100,
duration: 2,
maxCount: 3,
});
// 受保护的路由组件
const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { user, loading } = useAuth();
if (loading) {
return <div>Loading...</div>;
return (
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '100vh',
background: '#f0f2f5'
}}>
<Spin size="large" />
</div>
);
}
if (!user) {
@ -33,7 +51,17 @@ const PublicRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { user, loading } = useAuth();
if (loading) {
return <div>Loading...</div>;
return (
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
}}>
<Spin size="large" style={{ color: 'white' }} />
</div>
);
}
if (user) {
@ -46,40 +74,42 @@ const PublicRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
function App() {
return (
<ConfigProvider locale={zhCN}>
<AuthProvider>
<Router>
<Routes>
{/* 公共路由 */}
<Route path="/login" element={
<PublicRoute>
<Login />
</PublicRoute>
} />
<Route path="/register" element={
<PublicRoute>
<Register />
</PublicRoute>
} />
{/* 受保护的路由 */}
<Route path="/" element={
<ProtectedRoute>
<Layout />
</ProtectedRoute>
}>
<Route index element={<Navigate to="/dashboard" replace />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="users" element={<Users />} />
<Route path="roles" element={<Roles />} />
<Route path="permissions" element={<Permissions />} />
<Route path="menus" element={<Menus />} />
</Route>
{/* 404 重定向 */}
<Route path="*" element={<Navigate to="/dashboard" replace />} />
</Routes>
</Router>
</AuthProvider>
<AntdApp>
<AuthProvider>
<Router>
<Routes>
{/* 公共路由 */}
<Route path="/login" element={
<PublicRoute>
<Login />
</PublicRoute>
} />
<Route path="/register" element={
<PublicRoute>
<Register />
</PublicRoute>
} />
{/* 受保护的路由 */}
<Route path="/" element={
<ProtectedRoute>
<Layout />
</ProtectedRoute>
}>
<Route index element={<Navigate to="/dashboard" replace />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="users" element={<Users />} />
<Route path="roles" element={<Roles />} />
<Route path="permissions" element={<Permissions />} />
<Route path="menus" element={<Menus />} />
</Route>
{/* 404 重定向 */}
<Route path="*" element={<Navigate to="/dashboard" replace />} />
</Routes>
</Router>
</AuthProvider>
</AntdApp>
</ConfigProvider>
);
}

View File

@ -49,8 +49,9 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
const login = async (data: LoginRequest): Promise<boolean> => {
try {
setLoading(true);
console.log('useAuth: 开始登录请求', data);
const response = await apiService.login(data);
console.log('useAuth: 登录响应', response);
setToken(response.token);
setUser(response.user);
@ -60,19 +61,26 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
localStorage.setItem('user', JSON.stringify(response.user));
message.success('登录成功');
console.log('useAuth: 登录成功');
return true;
} catch (error: any) {
console.error('Login failed:', error);
message.error(error.response?.data?.message || '登录失败');
console.error('useAuth: 登录失败', error);
// 检查是否是apiService抛出的业务错误
if (error.response?.data?.code && error.response.data.code !== 200) {
message.error(error.response.data.message || '登录失败');
} else if (error.response?.status === 401) {
message.error('用户名或密码错误');
} else if (error.message) {
message.error(error.message);
} else {
message.error('登录失败');
}
return false;
} finally {
setLoading(false);
}
};
const register = async (data: RegisterRequest): Promise<boolean> => {
try {
setLoading(true);
await apiService.register(data);
message.success('注册成功,请登录');
return true;
@ -80,8 +88,6 @@ export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) =>
console.error('Register failed:', error);
message.error(error.response?.data?.message || '注册失败');
return false;
} finally {
setLoading(false);
}
};

View File

@ -1,3 +1,4 @@
import '@ant-design/v5-patch-for-react-19';
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';

View File

@ -1,9 +1,10 @@
import React, { useState } from 'react';
import { Form, Input, Button, Card, Typography, message } from 'antd';
import { Form, Input, Button, Card, Typography } from 'antd';
import { UserOutlined, LockOutlined } from '@ant-design/icons';
import { useAuth } from '../hooks/useAuth';
import { LoginRequest } from '../types';
import { useNavigate, Link } from 'react-router-dom';
import message from 'antd/es/message';
const { Title } = Typography;
@ -15,12 +16,18 @@ const Login: React.FC = () => {
const onFinish = async (values: LoginRequest) => {
setLoading(true);
try {
console.log('开始登录:', values);
const success = await login(values);
console.log('登录结果:', success);
if (success) {
console.log('登录成功跳转到dashboard');
navigate('/dashboard');
} else {
console.log('登录失败');
}
} catch (error) {
console.error('Login error:', error);
message.error('登录过程中发生错误');
} finally {
setLoading(false);
}

View File

@ -25,7 +25,7 @@ class ApiService {
constructor() {
this.api = axios.create({
baseURL: process.env.REACT_APP_API_URL || 'http://localhost:3000/api',
baseURL: '/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
@ -53,10 +53,13 @@ class ApiService {
},
(error) => {
if (error.response?.status === 401) {
// Token过期或无效清除本地存储并跳转到登录页
localStorage.removeItem('token');
localStorage.removeItem('user');
window.location.href = '/login';
// 如果不是登录请求才清除token并跳转
if (!error.config?.url?.includes('/auth/login')) {
localStorage.removeItem('token');
localStorage.removeItem('user');
window.location.href = '/login';
}
// 对于登录请求的401错误直接返回错误让组件处理
}
return Promise.reject(error);
}
@ -65,7 +68,13 @@ class ApiService {
// 认证相关API
async login(data: LoginRequest): Promise<LoginResponse> {
const response = await this.api.post<ApiResponse<LoginResponse>>('/auth/login', data);
const response = await this.api.post<ApiResponse<LoginResponse>>("/auth/login", data);
// 检查业务错误码
if (response.data.code !== 200) {
const error = new Error(response.data.message);
(error as any).response = { data: response.data };
throw error;
}
return response.data.data!;
}

View File

@ -0,0 +1,232 @@
import axios, { AxiosInstance, AxiosResponse } from 'axios';
import {
User,
Role,
Permission,
Menu,
CreateUserRequest,
UpdateUserRequest,
CreateRoleRequest,
UpdateRoleRequest,
CreatePermissionRequest,
UpdatePermissionRequest,
CreateMenuRequest,
UpdateMenuRequest,
LoginRequest,
LoginResponse,
RegisterRequest,
ApiResponse,
PaginatedResponse,
QueryParams,
} from '../types';
class ApiService {
private api: AxiosInstance;
constructor() {
this.api = axios.create({
baseURL: '/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// 请求拦截器 - 添加认证token
this.api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器 - 处理错误
this.api.interceptors.response.use(
(response: AxiosResponse) => {
return response;
},
(error) => {
if (error.response?.status === 401) {
// 如果不是登录请求才清除token并跳转
if (!error.config?.url?.includes('/auth/login')) {
localStorage.removeItem('token');
localStorage.removeItem('user');
window.location.href = '/login';
}
// 对于登录请求的401错误直接返回错误让组件处理
}
return Promise.reject(error);
}
);
}
// 认证相关API
async login(data: LoginRequest): Promise<LoginResponse> {
const response = await this.api.post<ApiResponse<LoginResponse>>('/auth/login', data);
return response.data.data!;
}
async register(data: RegisterRequest): Promise<User> {
const response = await this.api.post<ApiResponse<User>>('/auth/register', data);
return response.data.data!;
}
async logout(): Promise<void> {
await this.api.post('/auth/logout');
}
// 用户管理API
async getUsers(params?: QueryParams): Promise<PaginatedResponse<User>> {
const response = await this.api.get('/users', { params });
const backendData = response.data.data;
// 转换后端数据结构到前端期望的结构
return {
data: backendData.items.map((user: any) => ({
...user,
status: user.status === 1 ? 'active' : 'inactive'
})),
total: backendData.total,
page: backendData.page,
per_page: backendData.page_size,
total_pages: Math.ceil(backendData.total / backendData.page_size)
};
}
async getUser(id: number): Promise<User> {
const response = await this.api.get<ApiResponse<User>>(`/users/${id}`);
return response.data.data!;
}
async createUser(data: CreateUserRequest): Promise<User> {
const response = await this.api.post<ApiResponse<User>>('/users', data);
return response.data.data!;
}
async updateUser(id: number, data: UpdateUserRequest): Promise<User> {
// 转换前端的字符串状态为后端期望的数字格式
const backendData = {
...data,
status: data.status ? (data.status === 'active' ? 1 : 0) : undefined
};
const response = await this.api.put<ApiResponse<User>>(`/users/${id}`, backendData);
return response.data.data!;
}
async deleteUser(id: number): Promise<void> {
await this.api.delete(`/users/${id}`);
}
// 角色管理API
async getRoles(params?: QueryParams): Promise<PaginatedResponse<Role>> {
const response = await this.api.get<ApiResponse<PaginatedResponse<Role>>>('/roles', { params });
return response.data.data!;
}
async getAllRoles(): Promise<Role[]> {
const response = await this.api.get<ApiResponse<Role[]>>('/roles/all');
return response.data.data!;
}
async getRole(id: number): Promise<Role> {
const response = await this.api.get<ApiResponse<Role>>(`/roles/${id}`);
return response.data.data!;
}
async createRole(data: CreateRoleRequest): Promise<Role> {
const response = await this.api.post<ApiResponse<Role>>('/roles', data);
return response.data.data!;
}
async updateRole(id: number, data: UpdateRoleRequest): Promise<Role> {
// 转换前端的字符串状态为后端期望的数字格式
const backendData = {
...data,
status: data.status ? (data.status === 'active' ? 1 : 0) : undefined
};
const response = await this.api.put<ApiResponse<Role>>(`/roles/${id}`, backendData);
return response.data.data!;
}
async deleteRole(id: number): Promise<void> {
await this.api.delete(`/roles/${id}`);
}
// 权限管理API
async getPermissions(params?: QueryParams): Promise<PaginatedResponse<Permission>> {
const response = await this.api.get<ApiResponse<PaginatedResponse<Permission>>>('/permissions', { params });
return response.data.data!;
}
async getAllPermissions(): Promise<Permission[]> {
const response = await this.api.get<ApiResponse<Permission[]>>('/permissions/all');
return response.data.data!;
}
async getPermission(id: number): Promise<Permission> {
const response = await this.api.get<ApiResponse<Permission>>(`/permissions/${id}`);
return response.data.data!;
}
async createPermission(data: CreatePermissionRequest): Promise<Permission> {
const response = await this.api.post<ApiResponse<Permission>>('/permissions', data);
return response.data.data!;
}
async updatePermission(id: number, data: UpdatePermissionRequest): Promise<Permission> {
// 转换前端的字符串状态为后端期望的数字格式
const backendData = {
...data,
status: data.status ? (data.status === 'active' ? 1 : 0) : undefined
};
const response = await this.api.put<ApiResponse<Permission>>(`/permissions/${id}`, backendData);
return response.data.data!;
}
async deletePermission(id: number): Promise<void> {
await this.api.delete(`/permissions/${id}`);
}
// 菜单管理API
async getMenus(params?: QueryParams): Promise<PaginatedResponse<Menu>> {
const response = await this.api.get<ApiResponse<PaginatedResponse<Menu>>>('/menus', { params });
return response.data.data!;
}
async getAllMenus(): Promise<Menu[]> {
const response = await this.api.get<ApiResponse<Menu[]>>('/menus/all');
return response.data.data!;
}
async getMenu(id: number): Promise<Menu> {
const response = await this.api.get<ApiResponse<Menu>>(`/menus/${id}`);
return response.data.data!;
}
async createMenu(data: CreateMenuRequest): Promise<Menu> {
const response = await this.api.post<ApiResponse<Menu>>('/menus', data);
return response.data.data!;
}
async updateMenu(id: number, data: UpdateMenuRequest): Promise<Menu> {
// 转换前端的字符串状态为后端期望的数字格式
const backendData = {
...data,
status: data.status ? (data.status === 'active' ? 1 : 0) : undefined
};
const response = await this.api.put<ApiResponse<Menu>>(`/menus/${id}`, backendData);
return response.data.data!;
}
async deleteMenu(id: number): Promise<void> {
await this.api.delete(`/menus/${id}`);
}
}
export const apiService = new ApiService();
export default apiService;

30
frontend/vite.config.ts Normal file
View File

@ -0,0 +1,30 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
port: 3001,
host: '0.0.0.0',
proxy: {
'/api': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
secure: false,
configure: (proxy, options) => {
proxy.on('proxyReq', (proxyReq, req, res) => {
console.log('Proxying request:', req.method, req.url);
console.log('Headers:', req.headers);
});
proxy.on('proxyRes', (proxyRes, req, res) => {
console.log('Proxy response:', proxyRes.statusCode, 'for', req.url);
});
proxy.on('error', (err, req, res) => {
console.log('Proxy error:', err.message);
});
}
}
}
}
})

View File

@ -37,15 +37,21 @@ impl Config {
pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
// 尝试从config.yml加载配置
if let Ok(config) = Self::from_file("config.yml") {
return Ok(config);
match Self::from_file("config.yml") {
Ok(config) => {
tracing::info!("Successfully loaded config from config.yml: port={}", config.server.port);
return Ok(config);
}
Err(e) => {
tracing::info!("Failed to load config.yml: {}, using default config", e);
}
}
// 如果config.yml不存在使用默认配置
Ok(Config {
let default_config = Config {
server: ServerConfig {
host: "127.0.0.1".to_string(),
port: 3000,
port: 3000, // 修改默认端口为3000
},
database: DatabaseConfig {
url: "mysql://root:123456@127.0.0.1:3306/uadmin".to_string(),
@ -57,6 +63,8 @@ impl Config {
secret: "your-secret-key-here-please-change-in-production".to_string(),
expires_in: 86400,
},
})
};
tracing::info!("Using default config: port={}", default_config.server.port);
Ok(default_config)
}
}

View File

@ -1,10 +1,11 @@
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use sea_orm::{
ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, Set,
ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set,
};
use serde::{Deserialize, Serialize};
use chrono::Utc;
@ -12,6 +13,7 @@ use chrono::Utc;
use crate::{
models::{user, ApiResponse},
utils::{jwt::generate_token, password::{hash_password, verify_password}},
routes::AppState,
};
#[derive(Debug, Deserialize)]
@ -46,41 +48,63 @@ pub struct UserInfo {
}
pub async fn login(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Json(payload): Json<LoginRequest>,
) -> Result<Json<ApiResponse<AuthResponse>>, StatusCode> {
) -> impl IntoResponse {
// 查找用户
let user = user::Entity::find()
let user = match user::Entity::find()
.filter(user::Column::Username.eq(&payload.username))
.filter(user::Column::Status.eq(1))
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
{
Ok(user) => user,
Err(_) => {
return Json(ApiResponse::<AuthResponse>::error(
500,
"数据库查询失败".to_string(),
))
}
};
let user = match user {
Some(user) => user,
None => {
return Ok(Json(ApiResponse::error(
return Json(ApiResponse::<AuthResponse>::error(
401,
"用户名或密码错误".to_string(),
)))
))
}
};
// 验证密码
let is_valid = verify_password(&payload.password, &user.password_hash)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let is_valid = match verify_password(&payload.password, &user.password_hash) {
Ok(valid) => valid,
Err(_) => {
return Json(ApiResponse::<AuthResponse>::error(
500,
"密码验证失败".to_string(),
))
}
};
if !is_valid {
return Ok(Json(ApiResponse::error(
return Json(ApiResponse::<AuthResponse>::error(
401,
"用户名或密码错误".to_string(),
)));
));
}
// 生成JWT token
let token = generate_token(user.id, user.username.clone())
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let token = match generate_token(user.id, user.username.clone(), &app_state.config.jwt) {
Ok(token) => token,
Err(_) => {
return Json(ApiResponse::<AuthResponse>::error(
500,
"Token生成失败".to_string(),
))
}
};
let auth_response = AuthResponse {
token,
@ -94,17 +118,17 @@ pub async fn login(
},
};
Ok(Json(ApiResponse::success(auth_response)))
Json(ApiResponse::<AuthResponse>::success(auth_response))
}
pub async fn register(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Json(payload): Json<RegisterRequest>,
) -> Result<Json<ApiResponse<AuthResponse>>, StatusCode> {
// 检查用户名是否已存在
let existing_user = user::Entity::find()
.filter(user::Column::Username.eq(&payload.username))
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -118,7 +142,7 @@ pub async fn register(
// 检查邮箱是否已存在
let existing_email = user::Entity::find()
.filter(user::Column::Email.eq(&payload.email))
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -148,12 +172,12 @@ pub async fn register(
};
let user = new_user
.insert(&db)
.insert(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// 生成JWT token
let token = generate_token(user.id, user.username.clone())
let token = generate_token(user.id, user.username.clone(), &app_state.config.jwt)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let auth_response = AuthResponse {

View File

@ -4,7 +4,7 @@ use axum::{
Json,
};
use sea_orm::{
ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, QueryFilter,
ActiveModelTrait, ColumnTrait, EntityTrait, ModelTrait, QueryFilter,
QueryOrder, Set,
};
use serde::Deserialize;
@ -16,6 +16,7 @@ use crate::{
menu::{self, CreateMenuRequest, UpdateMenuRequest, MenuTreeResponse},
ApiResponse,
},
routes::AppState,
};
#[derive(Debug, Deserialize)]
@ -26,7 +27,7 @@ pub struct MenuQuery {
}
pub async fn get_menus(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Query(params): Query<MenuQuery>,
) -> Result<Json<ApiResponse<Vec<MenuTreeResponse>>>, StatusCode> {
let mut query = menu::Entity::find();
@ -44,7 +45,7 @@ pub async fn get_menus(
let menus = query
.order_by_asc(menu::Column::SortOrder)
.all(&db)
.all(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -55,13 +56,13 @@ pub async fn get_menus(
}
pub async fn create_menu(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Json(payload): Json<CreateMenuRequest>,
) -> Result<Json<ApiResponse<menu::Model>>, StatusCode> {
// 如果有父菜单,检查父菜单是否存在
if let Some(parent_id) = payload.parent_id {
let parent_menu = menu::Entity::find_by_id(parent_id)
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -92,7 +93,7 @@ pub async fn create_menu(
};
let menu = new_menu
.insert(&db)
.insert(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -100,13 +101,13 @@ pub async fn create_menu(
}
pub async fn update_menu(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Path(id): Path<i32>,
Json(payload): Json<UpdateMenuRequest>,
) -> Result<Json<ApiResponse<menu::Model>>, StatusCode> {
// 查找菜单
let menu = menu::Entity::find_by_id(id)
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -131,7 +132,7 @@ pub async fn update_menu(
// 检查父菜单是否存在
let parent_menu = menu::Entity::find_by_id(parent_id)
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -179,7 +180,7 @@ pub async fn update_menu(
menu.updated_at = Set(Utc::now());
let updated_menu = menu
.update(&db)
.update(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -187,12 +188,12 @@ pub async fn update_menu(
}
pub async fn delete_menu(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Path(id): Path<i32>,
) -> Result<Json<ApiResponse<()>>, StatusCode> {
// 查找菜单
let menu = menu::Entity::find_by_id(id)
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -201,7 +202,7 @@ pub async fn delete_menu(
// 检查是否有子菜单
let children = menu::Entity::find()
.filter(menu::Column::ParentId.eq(id))
.all(&db)
.all(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -213,7 +214,7 @@ pub async fn delete_menu(
}
// 删除菜单
menu.delete(&db)
menu.delete(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

View File

@ -4,7 +4,7 @@ use axum::{
Json,
};
use sea_orm::{
ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, PaginatorTrait, QueryFilter,
ActiveModelTrait, ColumnTrait, EntityTrait, ModelTrait, PaginatorTrait, QueryFilter,
QueryOrder, Set,
};
use serde::Deserialize;
@ -15,6 +15,7 @@ use crate::{
permission::{self, CreatePermissionRequest, UpdatePermissionRequest, PermissionResponse},
ApiResponse, PageResponse,
},
routes::AppState,
};
#[derive(Debug, Deserialize)]
@ -28,7 +29,7 @@ pub struct PermissionQuery {
}
pub async fn get_permissions(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Query(params): Query<PermissionQuery>,
) -> Result<Json<ApiResponse<PageResponse<PermissionResponse>>>, StatusCode> {
let page = params.page.unwrap_or(1);
@ -53,14 +54,14 @@ pub async fn get_permissions(
// 获取总数
let total = query
.clone()
.count(&db)
.count(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// 分页查询
let permissions = query
.order_by_desc(permission::Column::CreatedAt)
.paginate(&db, page_size)
.paginate(&app_state.db, page_size)
.fetch_page(page - 1)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -85,13 +86,13 @@ pub async fn get_permissions(
}
pub async fn get_all_permissions(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
) -> Result<Json<ApiResponse<Vec<PermissionResponse>>>, StatusCode> {
let permissions = permission::Entity::find()
.filter(permission::Column::Status.eq(1))
.order_by_asc(permission::Column::Resource)
.order_by_asc(permission::Column::Action)
.all(&db)
.all(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -114,13 +115,13 @@ pub async fn get_all_permissions(
}
pub async fn create_permission(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Json(payload): Json<CreatePermissionRequest>,
) -> Result<Json<ApiResponse<PermissionResponse>>, StatusCode> {
// 检查权限键是否已存在
let existing_permission = permission::Entity::find()
.filter(permission::Column::Key.eq(&payload.key))
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -146,7 +147,7 @@ pub async fn create_permission(
};
let permission = new_permission
.insert(&db)
.insert(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -166,13 +167,13 @@ pub async fn create_permission(
}
pub async fn update_permission(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Path(id): Path<i32>,
Json(payload): Json<UpdatePermissionRequest>,
) -> Result<Json<ApiResponse<PermissionResponse>>, StatusCode> {
// 查找权限
let permission = permission::Entity::find_by_id(id)
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -191,7 +192,7 @@ pub async fn update_permission(
if key != &permission.key {
let existing_permission = permission::Entity::find()
.filter(permission::Column::Key.eq(key))
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -228,7 +229,7 @@ pub async fn update_permission(
permission.updated_at = Set(Utc::now());
let updated_permission = permission
.update(&db)
.update(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -248,12 +249,12 @@ pub async fn update_permission(
}
pub async fn delete_permission(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Path(id): Path<i32>,
) -> Result<Json<ApiResponse<()>>, StatusCode> {
// 查找权限
let permission = permission::Entity::find_by_id(id)
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -261,7 +262,7 @@ pub async fn delete_permission(
Some(permission) => {
// 删除权限(级联删除会自动处理关联表)
permission
.delete(&db)
.delete(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

View File

@ -4,7 +4,7 @@ use axum::{
Json,
};
use sea_orm::{
ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, PaginatorTrait, QueryFilter,
ActiveModelTrait, ColumnTrait, EntityTrait, ModelTrait, PaginatorTrait, QueryFilter,
QueryOrder, Set,
};
use serde::Deserialize;
@ -15,6 +15,7 @@ use crate::{
role::{self, CreateRoleRequest, UpdateRoleRequest, RoleResponse},
role_permission, permission, ApiResponse, PageResponse,
},
routes::AppState,
};
#[derive(Debug, Deserialize)]
@ -26,7 +27,7 @@ pub struct RoleQuery {
}
pub async fn get_roles(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Query(params): Query<RoleQuery>,
) -> Result<Json<ApiResponse<PageResponse<RoleResponse>>>, StatusCode> {
let page = params.page.unwrap_or(1);
@ -45,24 +46,24 @@ pub async fn get_roles(
// 获取总数
let total = query
.clone()
.count(&db)
.count(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// 分页查询
// 获取分页数据
let roles = query
.order_by_desc(role::Column::CreatedAt)
.paginate(&db, page_size)
.paginate(&app_state.db, page_size)
.fetch_page(page - 1)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// 获取角色权限信息
// 转换为响应格式并获取权限信息
let mut role_responses = Vec::new();
for role in roles {
let permissions = role
.find_related(permission::Entity)
.all(&db)
.all(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -78,27 +79,28 @@ pub async fn get_roles(
}
let page_response = PageResponse::new(role_responses, total, page, page_size);
Ok(Json(ApiResponse::success(page_response)))
}
pub async fn create_role(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Json(payload): Json<CreateRoleRequest>,
) -> Result<Json<ApiResponse<RoleResponse>>, StatusCode> {
// 检查角色名是否已存在
let existing_role = role::Entity::find()
.filter(role::Column::Name.eq(&payload.name))
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
if existing_role.is_some() {
return Ok(Json(ApiResponse::error(
400,
"角色名已存在".to_string(),
)));
}
// 创建角色
let now = Utc::now();
let new_role = role::ActiveModel {
@ -109,13 +111,13 @@ pub async fn create_role(
updated_at: Set(now),
..Default::default()
};
let role = new_role
.insert(&db)
.insert(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// 分配权限
// 如果有权限ID创建角色权限关联
if let Some(permission_ids) = payload.permission_ids {
for permission_id in permission_ids {
let role_permission = role_permission::ActiveModel {
@ -125,19 +127,19 @@ pub async fn create_role(
..Default::default()
};
role_permission
.insert(&db)
.insert(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
}
}
// 获取角色权限信息
// 获取角色权限信息
let permissions = role
.find_related(permission::Entity)
.all(&db)
.all(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let role_response = RoleResponse {
id: role.id,
name: role.name,
@ -147,23 +149,22 @@ pub async fn create_role(
created_at: role.created_at,
updated_at: role.updated_at,
};
Ok(Json(ApiResponse::success(role_response)))
}
pub async fn update_role(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Path(id): Path<i32>,
Json(payload): Json<UpdateRoleRequest>,
) -> Result<Json<ApiResponse<RoleResponse>>, StatusCode> {
// 查找角色
let role = role::Entity::find_by_id(id)
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let role = match role {
Some(role) => role,
let mut role: role::ActiveModel = match role {
Some(role) => role.into(),
None => {
return Ok(Json(ApiResponse::error(
404,
@ -171,9 +172,7 @@ pub async fn update_role(
)))
}
};
let mut role: role::ActiveModel = role.into();
// 更新字段
if let Some(name) = payload.name {
role.name = Set(name);
@ -185,22 +184,22 @@ pub async fn update_role(
role.status = Set(status);
}
role.updated_at = Set(Utc::now());
let updated_role = role
.update(&db)
.update(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// 更新权限关联
// 如果有权限ID更新先删除现有关联再创建新关联
if let Some(permission_ids) = payload.permission_ids {
// 删除现有权限关联
role_permission::Entity::delete_many()
.filter(role_permission::Column::RoleId.eq(id))
.exec(&db)
.exec(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// 添加新的权限关联
// 创建新的权限关联
let now = Utc::now();
for permission_id in permission_ids {
let role_permission = role_permission::ActiveModel {
@ -210,19 +209,19 @@ pub async fn update_role(
..Default::default()
};
role_permission
.insert(&db)
.insert(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
}
}
// 获取角色权限信息
// 获取更新后的权限信息
let permissions = updated_role
.find_related(permission::Entity)
.all(&db)
.all(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
let role_response = RoleResponse {
id: updated_role.id,
name: updated_role.name,
@ -232,27 +231,25 @@ pub async fn update_role(
created_at: updated_role.created_at,
updated_at: updated_role.updated_at,
};
Ok(Json(ApiResponse::success(role_response)))
}
pub async fn delete_role(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Path(id): Path<i32>,
) -> Result<Json<ApiResponse<()>>, StatusCode> {
// 查找角色
let role = role::Entity::find_by_id(id)
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
match role {
Some(role) => {
// 删除角色(级联删除会自动处理关联表)
role.delete(&db)
role.delete(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(ApiResponse::success(())))
}
None => Ok(Json(ApiResponse::error(

View File

@ -4,7 +4,7 @@ use axum::{
Json,
};
use sea_orm::{
ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, PaginatorTrait, QueryFilter,
ActiveModelTrait, ColumnTrait, EntityTrait, ModelTrait, PaginatorTrait, QueryFilter,
QueryOrder, Set,
};
use serde::Deserialize;
@ -16,6 +16,7 @@ use crate::{
user_role, role, ApiResponse, PageResponse,
},
utils::password::hash_password,
routes::AppState,
};
#[derive(Debug, Deserialize)]
@ -28,7 +29,7 @@ pub struct UserQuery {
}
pub async fn get_users(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Query(params): Query<UserQuery>,
) -> Result<Json<ApiResponse<PageResponse<UserResponse>>>, StatusCode> {
let page = params.page.unwrap_or(1);
@ -50,14 +51,14 @@ pub async fn get_users(
// 获取总数
let total = query
.clone()
.count(&db)
.count(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
// 分页查询
let users = query
.order_by_desc(user::Column::CreatedAt)
.paginate(&db, page_size)
.paginate(&app_state.db, page_size)
.fetch_page(page - 1)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -67,7 +68,7 @@ pub async fn get_users(
for user in users {
let roles = user
.find_related(role::Entity)
.all(&db)
.all(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -90,13 +91,13 @@ pub async fn get_users(
}
pub async fn create_user(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Json(payload): Json<CreateUserRequest>,
) -> Result<Json<ApiResponse<UserResponse>>, StatusCode> {
// 检查用户名是否已存在
let existing_user = user::Entity::find()
.filter(user::Column::Username.eq(&payload.username))
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -110,7 +111,7 @@ pub async fn create_user(
// 检查邮箱是否已存在
let existing_email = user::Entity::find()
.filter(user::Column::Email.eq(&payload.email))
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -140,7 +141,7 @@ pub async fn create_user(
};
let user = new_user
.insert(&db)
.insert(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -154,7 +155,7 @@ pub async fn create_user(
..Default::default()
};
user_role
.insert(&db)
.insert(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
}
@ -163,7 +164,7 @@ pub async fn create_user(
// 获取用户角色信息
let roles = user
.find_related(role::Entity)
.all(&db)
.all(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -184,13 +185,13 @@ pub async fn create_user(
}
pub async fn update_user(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Path(id): Path<i32>,
Json(payload): Json<UpdateUserRequest>,
) -> Result<Json<ApiResponse<UserResponse>>, StatusCode> {
// 查找用户
let user = user::Entity::find_by_id(id)
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -230,7 +231,7 @@ pub async fn update_user(
user.updated_at = Set(Utc::now());
let updated_user = user
.update(&db)
.update(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -239,7 +240,7 @@ pub async fn update_user(
// 删除现有角色关联
user_role::Entity::delete_many()
.filter(user_role::Column::UserId.eq(id))
.exec(&db)
.exec(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -253,7 +254,7 @@ pub async fn update_user(
..Default::default()
};
user_role
.insert(&db)
.insert(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
}
@ -262,7 +263,7 @@ pub async fn update_user(
// 获取用户角色信息
let roles = updated_user
.find_related(role::Entity)
.all(&db)
.all(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@ -283,19 +284,19 @@ pub async fn update_user(
}
pub async fn delete_user(
State(db): State<DatabaseConnection>,
State(app_state): State<AppState>,
Path(id): Path<i32>,
) -> Result<Json<ApiResponse<()>>, StatusCode> {
// 查找用户
let user = user::Entity::find_by_id(id)
.one(&db)
.one(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
match user {
Some(user) => {
// 删除用户(级联删除会自动处理关联表)
user.delete(&db)
user.delete(&app_state.db)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

View File

@ -9,7 +9,9 @@ mod utils;
use std::env;
use tracing::{info, Level};
use tracing_subscriber;
use sea_orm::Database;
use config::Config;
use routes::AppState;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -28,19 +30,36 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
info!("Starting UAdmin API Server...");
// 加载配置
info!("About to load configuration from config.yml...");
let config = match Config::load() {
Ok(config) => {
info!("✅ Configuration loaded successfully: host={}, port={}", config.server.host, config.server.port);
config
}
Err(e) => {
info!("❌ Failed to load configuration: {}", e);
panic!("Configuration loading failed: {}", e);
}
};
info!("Configuration loading completed, proceeding with database connection...");
// 建立数据库连接
let db = database::establish_connection().await?;
let db = Database::connect(&config.database.url).await?;
info!("Database connected successfully");
// 运行数据库迁移
database::run_migrations(&db).await?;
info!("Database migrations completed");
// 创建路由
let app = routes::create_routes(db);
// 创建应用状态
let app_state = AppState {
db,
config: config.clone(),
};
// 加载配置
let config = Config::load().expect("Failed to load configuration");
// 创建路由
let app = routes::create_routes(app_state);
let addr = format!("{}:{}", config.server.host, config.server.port);
info!("Server running on http://{}", addr);

View File

@ -4,11 +4,13 @@ use axum::{
middleware::Next,
response::Response,
};
use sea_orm::DatabaseConnection;
use crate::utils::jwt::{extract_token_from_header, verify_token};
use crate::{
utils::jwt::{extract_token_from_header, verify_token},
routes::AppState,
};
pub async fn auth_middleware(
State(_db): State<DatabaseConnection>,
State(app_state): State<AppState>,
mut request: Request,
next: Next,
) -> Result<Response, StatusCode> {
@ -46,7 +48,7 @@ pub async fn auth_middleware(
};
// 验证JWT token
match verify_token(token) {
match verify_token(token, &app_state.config.jwt) {
Ok(claims) => {
// 将用户信息添加到请求扩展中
request.extensions_mut().insert(claims);

View File

@ -8,19 +8,31 @@ use tower_http::cors::{Any, CorsLayer};
use crate::{
handlers::{auth, user, role, menu, permission},
middleware::auth::auth_middleware,
config::config::Config,
};
pub fn create_routes(db: DatabaseConnection) -> Router<()> {
#[derive(Clone)]
pub struct AppState {
pub db: DatabaseConnection,
pub config: Config,
}
pub fn create_routes(app_state: AppState) -> Router<()> {
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
Router::new()
// 认证路由(无需认证)
// 无需认证的路由
let public_routes = Router::new()
.route("/api/auth/login", post(auth::login))
.route("/api/auth/register", post(auth::register))
.route("/", get(|| async { "UAdmin API Server is running!" }))
.route("/health", get(|| async { "OK" }))
.with_state(app_state.clone());
// 需要认证的路由
let protected_routes = Router::new()
// 用户管理路由
.route("/api/users", get(user::get_users))
.route("/api/users", post(user::create_user))
@ -46,13 +58,13 @@ pub fn create_routes(db: DatabaseConnection) -> Router<()> {
.route("/api/permissions/:id", put(permission::update_permission))
.route("/api/permissions/:id", delete(permission::delete_permission))
// 健康检查
.route("/", get(|| async { "UAdmin API Server is running!" }))
.route("/health", get(|| async { "OK" }))
// 添加CORS中间件
// 添加认证中间件
.layer(axum::middleware::from_fn_with_state(app_state.clone(), auth_middleware))
.with_state(app_state.clone());
// 合并路由并添加CORS
Router::new()
.merge(public_routes)
.merge(protected_routes)
.layer(cors)
// 添加认证中间件(除了登录、注册和健康检查路由)
.layer(axum::middleware::from_fn_with_state(db.clone(), auth_middleware))
.with_state(db)
}

View File

@ -1,7 +1,7 @@
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use std::env;
use chrono::{Duration, Utc};
use crate::config::config::JwtConfig;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Claims {
@ -25,17 +25,15 @@ impl Claims {
}
}
pub fn generate_token(user_id: i32, username: String) -> Result<String, jsonwebtoken::errors::Error> {
pub fn generate_token(user_id: i32, username: String, jwt_config: &JwtConfig) -> Result<String, jsonwebtoken::errors::Error> {
let claims = Claims::new(user_id, username);
let secret = env::var("JWT_SECRET").expect("JWT_SECRET must be set");
let key = EncodingKey::from_secret(secret.as_ref());
let key = EncodingKey::from_secret(jwt_config.secret.as_ref());
encode(&Header::default(), &claims, &key)
}
pub fn verify_token(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
let secret = env::var("JWT_SECRET").expect("JWT_SECRET must be set");
let key = DecodingKey::from_secret(secret.as_ref());
pub fn verify_token(token: &str, jwt_config: &JwtConfig) -> Result<Claims, jsonwebtoken::errors::Error> {
let key = DecodingKey::from_secret(jwt_config.secret.as_ref());
let validation = Validation::default();
decode::<Claims>(token, &key, &validation).map(|data| data.claims)