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

View File

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

View File

@@ -8,7 +8,6 @@
"start": "export NODE_ENV='production' && node dist-server/index.js"
},
"dependencies": {
"@ant-design/icons": "^6.0.0",
"@emotion/react": "^11.14.0",
"@heroicons/react": "^2.2.0",
"@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 { useRouteError, useNavigate } from 'react-router';
import { Alert, Button } from 'antd';
import { useTheme } from '../hooks/ThemeProvider';
export const ErrorPage = () => {
const navigate = useNavigate();
const { isDark } = useTheme();
const error = useRouteError() as any;
const errorMessage = error?.statusText || error?.message || '未知错误';
return (
<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">
<h1 className="text-2xl font-bold mb-4"></h1>

View File

@@ -1,12 +1,16 @@
import React from 'react';
import { useNavigate } from 'react-router';
import { Button } from 'antd';
import { useTheme } from '../hooks/ThemeProvider';
export const NotFoundPage = () => {
const navigate = useNavigate();
const { isDark } = useTheme();
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">
<h1 className="text-2xl font-bold mb-4">404 - </h1>
<p className="mb-6 text-gray-600 dark:text-gray-300">

View File

@@ -114,7 +114,6 @@ export const MainLayout = () => {
collapsed={collapsed}
width={240}
className="custom-sider"
theme='light'
style={{
overflow: 'auto',
height: '100vh',
@@ -123,8 +122,6 @@ export const MainLayout = () => {
top: 0,
bottom: 0,
zIndex: 100,
transition: 'all 0.2s ease',
boxShadow: '2px 0 8px 0 rgba(29, 35, 41, 0.05)',
}}
>
<div className="p-4">
@@ -159,9 +156,12 @@ export const MainLayout = () => {
</Sider>
<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={{
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
@@ -191,10 +191,10 @@ export const MainLayout = () => {
</Space>
</Dropdown>
</Space>
</div>
</Header>
<Content className="m-6" style={{ overflow: 'initial', transition: 'all 0.2s ease' }}>
<div className="site-layout-content p-6 rounded-lg bg-white shadow-sm">
<Content className="m-6" style={{ overflow: 'initial' }}>
<div className="site-layout-content p-6 rounded-lg">
<Outlet />
</div>

View File

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

View File

@@ -1,3 +1,4 @@
import { Link } from 'react-router-dom'
import { createRoot } from 'react-dom/client'
import { getGlobalConfig } from '../utils/utils'
@@ -21,20 +22,20 @@ const Home = () => {
{/* 管理入口按钮 */}
<div className="space-y-4">
<a
href="/admin"
<Link
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"
>
</a>
</Link>
{/* 移动端入口按钮 */}
<a
href="/mobile"
<Link
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"
>
</a>
</Link>
</div>
</div>

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
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 { ErrorSchema } from '@/server/utils/errorHandler';
import { AppDataSource } from '@/server/data-source';

View File

@@ -1,6 +1,6 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
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 { ErrorSchema } from '@/server/utils/errorHandler';
import { AppDataSource } from '@/server/data-source';

View File

@@ -1,6 +1,6 @@
import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
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 { ErrorSchema } from '@/server/utils/errorHandler';
import { AppDataSource } from '@/server/data-source';

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,8 @@
import jwt from 'jsonwebtoken';
import { UserService } from '../users/user.service';
import { UserEntity as User } from '../users/user.entity';
import { logger } from '@/server/utils/logger';
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_EXPIRES_IN = '7d'; // 7天有效期

View File

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

View File

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

View File

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

View File

@@ -1,55 +1 @@
@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 { defineConfig } from 'vite'
import i18nextLoader from 'vite-plugin-i18next-loader'
export default defineConfig({
plugins: [
i18nextLoader({
paths: ['src/client/i18n/locales']
}),
reactStack({
minify: false,
port: 8080