Compare commits

1 Commits

Author SHA1 Message Date
D8D Developer
0cc35e6351 feat(auth): 添加默认管理员账户功能
- 新增ensureAdminExists方法用于确保管理员账户存在
- 在登录流程中自动创建默认管理员账户
- 使用logger替代console.error进行错误记录

 feat(logger): 扩展日志功能

- 新增info级别日志记录功能
- 统一使用logger进行日志输出
2025-07-07 06:47:21 +00:00
24 changed files with 1826 additions and 117 deletions

View File

@@ -29,34 +29,28 @@ k8s:deployment:create # K8S-部署-创建
### 前端示例 ### 前端示例
```typescript ```typescript
// 在需要使用日志的文件中直接引入debug // src/client/utils/logger.ts
import debug from 'debug'; import debug from 'debug';
// 按需定义命名空间 export const logger = {
const errorLogger = debug('frontend:error'); error: debug('frontend:error'),
const apiLogger = debug('frontend:api'); api: debug('frontend:api'),
const authLogger = debug('frontend:auth'); auth: debug('frontend:auth'),
const uiLogger = debug('frontend:ui'); ui: debug('frontend:ui')
};
// 使用示例
errorLogger('用户登录失败: %s', error.message);
apiLogger('API请求: %s', url);
``` ```
### 后端示例 ### 后端示例
```typescript ```typescript
// 在需要使用日志的文件中直接引入debug // src/server/utils/logger.ts
import debug from 'debug'; import debug from 'debug';
// 按需定义命名空间 export const logger = {
const errorLogger = debug('backend:error'); error: debug('backend:error'),
const apiLogger = debug('backend:api'); api: debug('backend:api'),
const dbLogger = debug('backend:db'); db: debug('backend:db'),
const middlewareLogger = debug('backend:middleware'); middleware: debug('backend:middleware')
};
// 使用示例
errorLogger('数据库连接失败: %s', error.message);
dbLogger('查询执行: %s', sql);
``` ```
## 5. 最佳实践 ## 5. 最佳实践

View File

@@ -4,7 +4,7 @@
```typescript ```typescript
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import { z } from '@hono/zod-openapi'; import { z } from 'zod';
@Entity('table_name') // 使用小写下划线命名表名 @Entity('table_name') // 使用小写下划线命名表名
export class EntityName { export class EntityName {

View File

@@ -8,7 +8,6 @@
"start": "export NODE_ENV='production' && node dist-server/index.js" "start": "export NODE_ENV='production' && node dist-server/index.js"
}, },
"dependencies": { "dependencies": {
"@ant-design/icons": "^6.0.0",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@heroicons/react": "^2.2.0", "@heroicons/react": "^2.2.0",
"@hono/node-server": "^1.14.3", "@hono/node-server": "^1.14.3",

1767
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,17 @@
import React from 'react'; import React from 'react';
import { useRouteError, useNavigate } from 'react-router'; import { useRouteError, useNavigate } from 'react-router';
import { Alert, Button } from 'antd'; import { Alert, Button } from 'antd';
import { useTheme } from '../hooks/ThemeProvider';
export const ErrorPage = () => { export const ErrorPage = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { isDark } = useTheme();
const error = useRouteError() as any; const error = useRouteError() as any;
const errorMessage = error?.statusText || error?.message || '未知错误'; const errorMessage = error?.statusText || error?.message || '未知错误';
return ( return (
<div className="flex flex-col items-center justify-center flex-grow p-4" <div className="flex flex-col items-center justify-center flex-grow p-4"
style={{ color: isDark ? '#fff' : 'inherit' }}
> >
<div className="max-w-3xl w-full"> <div className="max-w-3xl w-full">
<h1 className="text-2xl font-bold mb-4"></h1> <h1 className="text-2xl font-bold mb-4"></h1>

View File

@@ -1,12 +1,16 @@
import React from 'react'; import React from 'react';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { Button } from 'antd'; import { Button } from 'antd';
import { useTheme } from '../hooks/ThemeProvider';
export const NotFoundPage = () => { export const NotFoundPage = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const { isDark } = useTheme();
return ( return (
<div className="flex flex-col items-center justify-center flex-grow p-4"> <div className="flex flex-col items-center justify-center flex-grow p-4"
style={{ color: isDark ? '#fff' : 'inherit' }}
>
<div className="max-w-3xl w-full"> <div className="max-w-3xl w-full">
<h1 className="text-2xl font-bold mb-4">404 - </h1> <h1 className="text-2xl font-bold mb-4">404 - </h1>
<p className="mb-6 text-gray-600 dark:text-gray-300"> <p className="mb-6 text-gray-600 dark:text-gray-300">

View File

@@ -108,13 +108,12 @@ export const MainLayout = () => {
return ( return (
<Layout style={{ minHeight: '100vh' }}> <Layout style={{ minHeight: '100vh' }}>
<Sider <Sider
trigger={null} trigger={null}
collapsible collapsible
collapsed={collapsed} collapsed={collapsed}
width={240} width={240}
className="custom-sider" className="custom-sider"
theme='light'
style={{ style={{
overflow: 'auto', overflow: 'auto',
height: '100vh', height: '100vh',
@@ -123,8 +122,6 @@ export const MainLayout = () => {
top: 0, top: 0,
bottom: 0, bottom: 0,
zIndex: 100, zIndex: 100,
transition: 'all 0.2s ease',
boxShadow: '2px 0 8px 0 rgba(29, 35, 41, 0.05)',
}} }}
> >
<div className="p-4"> <div className="p-4">
@@ -159,9 +156,12 @@ export const MainLayout = () => {
</Sider> </Sider>
<Layout style={{ marginLeft: collapsed ? 80 : 240, transition: 'margin-left 0.2s' }}> <Layout style={{ marginLeft: collapsed ? 80 : 240, transition: 'margin-left 0.2s' }}>
<div className="sticky top-0 z-50 bg-white shadow-sm transition-all duration-200 h-16 flex items-center justify-between pl-2" <Header className="p-0 flex justify-between items-center"
style={{ style={{
boxShadow: '0 1px 4px rgba(0,21,41,0.08)' position: 'sticky',
top: 0,
zIndex: 99,
boxShadow: '0 1px 4px rgba(0,21,41,0.08)',
}} }}
> >
<Button <Button
@@ -191,10 +191,10 @@ export const MainLayout = () => {
</Space> </Space>
</Dropdown> </Dropdown>
</Space> </Space>
</div> </Header>
<Content className="m-6" style={{ overflow: 'initial', transition: 'all 0.2s ease' }}> <Content className="m-6" style={{ overflow: 'initial' }}>
<div className="site-layout-content p-6 rounded-lg bg-white shadow-sm"> <div className="site-layout-content p-6 rounded-lg">
<Outlet /> <Outlet />
</div> </div>

View File

@@ -4,7 +4,7 @@ import {
Input, Input,
Button, Button,
Card, Card,
App, message,
} from 'antd'; } from 'antd';
import { import {
UserOutlined, UserOutlined,
@@ -17,7 +17,6 @@ import {
// 登录页面 // 登录页面
export const LoginPage = () => { export const LoginPage = () => {
const { message } = App.useApp();
const { login } = useAuth(); const { login } = useAuth();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -47,7 +46,7 @@ export const LoginPage = () => {
// 登录成功后跳转到管理后台首页 // 登录成功后跳转到管理后台首页
navigate('/admin/dashboard'); navigate('/admin/dashboard');
} catch (error: any) { } catch (error: any) {
message.error(error instanceof Error ? error.message : '登录失败'); message.error(error.response?.data?.error || '登录失败');
} finally { } finally {
setLoading(false); setLoading(false);
} }

View File

@@ -1,3 +1,4 @@
import { Link } from 'react-router-dom'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import { getGlobalConfig } from '../utils/utils' import { getGlobalConfig } from '../utils/utils'
@@ -21,20 +22,20 @@ const Home = () => {
{/* 管理入口按钮 */} {/* 管理入口按钮 */}
<div className="space-y-4"> <div className="space-y-4">
<a <Link
href="/admin" to="/admin"
className="w-full flex justify-center py-3 px-4 border border-transparent rounded-md shadow-sm text-lg font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" className="w-full flex justify-center py-3 px-4 border border-transparent rounded-md shadow-sm text-lg font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
> >
</a> </Link>
{/* 移动端入口按钮 */} {/* 移动端入口按钮 */}
<a <Link
href="/mobile" to="/mobile"
className="w-full flex justify-center py-3 px-4 border border-blue-600 rounded-md shadow-sm text-lg font-medium text-blue-600 bg-white hover:bg-blue-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" className="w-full flex justify-center py-3 px-4 border border-blue-600 rounded-md shadow-sm text-lg font-medium text-blue-600 bg-white hover:bg-blue-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
> >
</a> </Link>
</div> </div>
</div> </div>

View File

@@ -1,7 +1,7 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi' import { createRoute, OpenAPIHono } from '@hono/zod-openapi'
import { AuthService } from '../../../modules/auth/auth.service' import { AuthService } from '../../../modules/auth/auth.service'
import { UserService } from '../../../modules/users/user.service' import { UserService } from '../../../modules/users/user.service'
import { z } from '@hono/zod-openapi' import { z } from 'zod'
import { ErrorSchema } from '../../../utils/errorHandler' import { ErrorSchema } from '../../../utils/errorHandler'
import { AppDataSource } from '../../../data-source' import { AppDataSource } from '../../../data-source'
import { AuthContext } from '../../../types/context' import { AuthContext } from '../../../types/context'

View File

@@ -1,5 +1,5 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi'; import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
import { z } from '@hono/zod-openapi' import { z } from 'zod'
import { AuthContext } from '@/server/types/context'; import { AuthContext } from '@/server/types/context';
import { authMiddleware } from '@/server/middleware/auth.middleware'; import { authMiddleware } from '@/server/middleware/auth.middleware';
import { AppDataSource } from '@/server/data-source'; import { AppDataSource } from '@/server/data-source';

View File

@@ -1,7 +1,7 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi' import { createRoute, OpenAPIHono } from '@hono/zod-openapi'
import { AuthService } from '../../../modules/auth/auth.service' import { AuthService } from '../../../modules/auth/auth.service'
import { UserService } from '../../../modules/users/user.service' import { UserService } from '../../../modules/users/user.service'
import { z } from '@hono/zod-openapi' import { z } from 'zod'
import { AppDataSource } from '../../../data-source' import { AppDataSource } from '../../../data-source'
import { ErrorSchema } from '../../../utils/errorHandler' import { ErrorSchema } from '../../../utils/errorHandler'
import { AuthContext } from '../../../types/context' import { AuthContext } from '../../../types/context'

View File

@@ -1,6 +1,6 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi'; import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
import { UserService } from '@/server/modules/users/user.service'; import { UserService } from '@/server/modules/users/user.service';
import { z } from '@hono/zod-openapi'; import { z } from 'zod';
import { authMiddleware } from '@/server/middleware/auth.middleware'; import { authMiddleware } from '@/server/middleware/auth.middleware';
import { ErrorSchema } from '@/server/utils/errorHandler'; import { ErrorSchema } from '@/server/utils/errorHandler';
import { AppDataSource } from '@/server/data-source'; import { AppDataSource } from '@/server/data-source';

View File

@@ -1,6 +1,6 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi'; import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
import { UserService } from '@/server/modules/users/user.service'; import { UserService } from '@/server/modules/users/user.service';
import { z } from '@hono/zod-openapi'; import { z } from 'zod';
import { authMiddleware } from '@/server/middleware/auth.middleware'; import { authMiddleware } from '@/server/middleware/auth.middleware';
import { ErrorSchema } from '@/server/utils/errorHandler'; import { ErrorSchema } from '@/server/utils/errorHandler';
import { AppDataSource } from '@/server/data-source'; import { AppDataSource } from '@/server/data-source';

View File

@@ -1,6 +1,6 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi'; import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
import { UserService } from '@/server/modules/users/user.service'; import { UserService } from '@/server/modules/users/user.service';
import { z } from '@hono/zod-openapi'; import { z } from 'zod';
import { authMiddleware } from '@/server/middleware/auth.middleware'; import { authMiddleware } from '@/server/middleware/auth.middleware';
import { ErrorSchema } from '@/server/utils/errorHandler'; import { ErrorSchema } from '@/server/utils/errorHandler';
import { AppDataSource } from '@/server/data-source'; import { AppDataSource } from '@/server/data-source';

View File

@@ -1,6 +1,6 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi'; import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
import { UserService } from '../../modules/users/user.service'; import { UserService } from '../../modules/users/user.service';
import { z } from '@hono/zod-openapi'; import { z } from 'zod';
import { authMiddleware } from '../../middleware/auth.middleware'; import { authMiddleware } from '../../middleware/auth.middleware';
import { ErrorSchema } from '../../utils/errorHandler'; import { ErrorSchema } from '../../utils/errorHandler';
import { AppDataSource } from '../../data-source'; import { AppDataSource } from '../../data-source';

View File

@@ -1,6 +1,6 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi'; import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
import { UserService } from '../../modules/users/user.service'; import { UserService } from '../../modules/users/user.service';
import { z } from '@hono/zod-openapi'; import { z } from 'zod';
import { authMiddleware } from '../../middleware/auth.middleware'; import { authMiddleware } from '../../middleware/auth.middleware';
import { ErrorSchema } from '../../utils/errorHandler'; import { ErrorSchema } from '../../utils/errorHandler';
import { AppDataSource } from '../../data-source'; import { AppDataSource } from '../../data-source';

View File

@@ -17,6 +17,6 @@ export const AppDataSource = new DataSource({
User, Role User, Role
], ],
migrations: [], migrations: [],
synchronize: process.env.DB_SYNCHRONIZE !== "false", synchronize: process.env.DB_SYNCHRONIZE === "true",
logging: process.env.DB_LOGGING === "true", logging: process.env.DB_LOGGING === "true",
}); });

View File

@@ -1,13 +1,8 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { UserService } from '../users/user.service'; import { UserService } from '../users/user.service';
import { UserEntity as User } from '../users/user.entity'; import { UserEntity as User } from '../users/user.entity';
import { logger } from '@/server/utils/logger';
import { DisabledStatus } from '@/share/types'; import { DisabledStatus } from '@/share/types';
import debug from 'debug';
const logger = {
info: debug('backend:auth:info'),
error: debug('backend:auth:error')
}
const JWT_SECRET = 'your-secret-key'; // 生产环境应使用环境变量 const JWT_SECRET = 'your-secret-key'; // 生产环境应使用环境变量
const JWT_EXPIRES_IN = '7d'; // 7天有效期 const JWT_EXPIRES_IN = '7d'; // 7天有效期

View File

@@ -1,5 +1,5 @@
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import { z } from '@hono/zod-openapi'; import { z } from 'zod';
export type Permission = string; export type Permission = string;

View File

@@ -1,5 +1,5 @@
import { Context } from 'hono' import { Context } from 'hono'
import { z } from '@hono/zod-openapi' import { z } from 'zod'
import { HTTPException } from 'hono/http-exception' import { HTTPException } from 'hono/http-exception'
export const ErrorSchema = z.object({ export const ErrorSchema = z.object({

View File

@@ -5,4 +5,5 @@ export const logger = {
api: debug('backend:api'), api: debug('backend:api'),
db: debug('backend:db'), db: debug('backend:db'),
middleware: debug('backend:middleware'), middleware: debug('backend:middleware'),
info: debug('backend:info'),
}; };

View File

@@ -1,55 +1 @@
@import 'tailwindcss'; @import 'tailwindcss';
/* 全局滚动条样式 */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* 响应式断点 */
@media (max-width: 768px) {
.custom-sider {
width: 100% !important;
max-width: 100% !important;
}
.site-layout-content {
padding: 1rem !important;
}
}
/* 全局过渡效果 */
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
/* 内容区域阴影优化 */
.shadow-sm {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
/* 按钮悬停效果 */
.ant-btn:hover {
transform: translateY(-1px);
}
/* 卡片样式优化 */
.ant-card {
border-radius: 8px;
}

View File

@@ -1,8 +1,12 @@
import reactStack from 'hono-vite-react-stack-node' import reactStack from 'hono-vite-react-stack-node'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import i18nextLoader from 'vite-plugin-i18next-loader'
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
i18nextLoader({
paths: ['src/client/i18n/locales']
}),
reactStack({ reactStack({
minify: false, minify: false,
port: 8080 port: 8080