This commit is contained in:
longdayi 2024-09-10 11:23:02 +08:00
parent 5630af88ba
commit 6e95554e0c
10 changed files with 65 additions and 63 deletions

View File

@ -10,9 +10,14 @@ import { AuthModule } from './auth/auth.module';
import { ScheduleModule } from '@nestjs/schedule'; import { ScheduleModule } from '@nestjs/schedule';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { TasksModule } from './tasks/tasks.module'; import { TasksModule } from './tasks/tasks.module';
import { JwtModule } from '@nestjs/jwt';
import { env } from './env';
@Module({ @Module({
imports: [ScheduleModule.forRoot(), TrpcModule, RedisModule, QueueModule, TransformModule, AuthModule, TasksModule], imports: [ScheduleModule.forRoot(), JwtModule.register({
global: true,
secret: env.JWT_SECRET
}), TrpcModule, RedisModule, QueueModule, TransformModule, AuthModule, TasksModule],
providers: [RedisService, SocketGateway, ConfigService], providers: [RedisService, SocketGateway, ConfigService],
}) })
export class AppModule { } export class AppModule { }

View File

@ -12,8 +12,6 @@ export class AuthController {
@Get("user-profile") @Get("user-profile")
async getUserProfile(@Req() request: Request) { async getUserProfile(@Req() request: Request) {
const user: JwtPayload = (request as any).user const user: JwtPayload = (request as any).user
console.log(user)
// console.log(request)
return this.authService.getUserProfile(user) return this.authService.getUserProfile(user)
} }
@Post('login') @Post('login')

View File

@ -15,7 +15,7 @@ export class AuthGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> { async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();
const token = this.extractTokenFromHeader(request); const token = this.extractTokenFromHeader(request);
console.log(token)
if (!token) { if (!token) {
throw new UnauthorizedException(); throw new UnauthorizedException();
} }
@ -26,7 +26,7 @@ export class AuthGuard implements CanActivate {
secret: env.JWT_SECRET secret: env.JWT_SECRET
} }
); );
// 💡 We're assigning the payload to the request object here // 💡 We're assigning the payload to the request object here
// so that we can access it in our route handlers // so that we can access it in our route handlers
request['user'] = payload; request['user'] = payload;
@ -36,7 +36,7 @@ export class AuthGuard implements CanActivate {
return true; return true;
} }
private extractTokenFromHeader(request: Request): string | undefined { extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? []; const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined; return type === 'Bearer' ? token : undefined;
} }

View File

@ -9,11 +9,6 @@ import { DepartmentService } from '@server/models/department/department.service'
@Module({ @Module({
providers: [AuthService, StaffService, RoleMapService, DepartmentService], providers: [AuthService, StaffService, RoleMapService, DepartmentService],
imports: [JwtModule.register({
global: true,
secret: env.JWT_SECRET,
signOptions: { expiresIn: '60s' },
}),],
controllers: [AuthController], controllers: [AuthController],
exports: [AuthService] exports: [AuthService]
}) })

View File

@ -1,18 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { TrpcService } from './trpc.service';
describe('TrpcService', () => {
let service: TrpcService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [TrpcService],
}).compile();
service = module.get<TrpcService>(TrpcService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { initTRPC, TRPCError } from '@trpc/server'; import { initTRPC, TRPCError } from '@trpc/server';
import superjson from 'superjson-cjs'; import superjson from 'superjson-cjs';
import * as trpcExpress from '@trpc/server/adapters/express'; import * as trpcExpress from '@trpc/server/adapters/express';
@ -9,7 +9,7 @@ import { JwtService } from '@nestjs/jwt';
type Context = Awaited<ReturnType<TrpcService['createContext']>>; type Context = Awaited<ReturnType<TrpcService['createContext']>>;
@Injectable() @Injectable()
export class TrpcService { export class TrpcService {
constructor(private readonly jwtService: JwtService) { }
async createContext({ async createContext({
req, req,
res, res,
@ -17,10 +17,11 @@ export class TrpcService {
const token = req.headers.authorization?.split(' ')[1]; const token = req.headers.authorization?.split(' ')[1];
let tokenData: JwtPayload | undefined = undefined; let tokenData: JwtPayload | undefined = undefined;
let staff: Staff | undefined = undefined; let staff: Staff | undefined = undefined;
console.log(token)
if (token) { if (token) {
try { try {
// Verify JWT token and extract tokenData const jwtService = new JwtService()
tokenData = await this.jwtService.verifyAsync(token, { secret: env.JWT_SECRET }) as JwtPayload; tokenData = await jwtService.verifyAsync(token, { secret: env.JWT_SECRET }) as JwtPayload;
if (tokenData) { if (tokenData) {
// Fetch staff details from the database using tokenData.id // Fetch staff details from the database using tokenData.id
staff = await db.staff.findUnique({ where: { id: tokenData.sub } }); staff = await db.staff.findUnique({ where: { id: tokenData.sub } });
@ -35,7 +36,7 @@ export class TrpcService {
} }
return { return {
staff, staff
}; };
}; };
trpc = initTRPC.context<Context>().create({ trpc = initTRPC.context<Context>().create({

View File

@ -6,13 +6,16 @@ import QueryProvider from './providers/query-provider'
import { router } from './routes'; import { router } from './routes';
import { AuthProvider } from './providers/auth-provider'; import { AuthProvider } from './providers/auth-provider';
import ThemeProvider from './providers/theme-provider'; import ThemeProvider from './providers/theme-provider';
import { App as AntdApp } from 'antd';
function App() { function App() {
return ( return (
<AuthProvider> <AuthProvider>
<QueryProvider> <QueryProvider>
<ThemeProvider> <ThemeProvider>
<RouterProvider router={router}></RouterProvider> <AntdApp>
<RouterProvider router={router}></RouterProvider>
</AntdApp>
</ThemeProvider> </ThemeProvider>
</QueryProvider> </QueryProvider>
</AuthProvider> </AuthProvider>

View File

@ -8,29 +8,28 @@ import SineWave from '../components/presentation/animation/sine-wave';
const LoginPage: React.FC = () => { const LoginPage: React.FC = () => {
const [showLogin, setShowLogin] = useState(true); const [showLogin, setShowLogin] = useState(true);
const [registerLoading, setRegisterLoading] = useState(false); const [registerLoading, setRegisterLoading] = useState(false);
const { login, isAuthenticated } = useAuth(); const { login, isAuthenticated, signup } = useAuth();
const loginFormRef = useRef<any>(null); const loginFormRef = useRef<any>(null);
const registerFormRef = useRef<any>(null); const registerFormRef = useRef<any>(null);
const location = useLocation(); const location = useLocation();
const navigate = useNavigate() const navigate = useNavigate();
const onFinishLogin = async (values: any) => { const onFinishLogin = async (values: any) => {
try { try {
const { username, password } = values; const { username, password } = values;
await login(username, password); await login(username, password);
message.success('登录成功!'); message.success('登录成功!');
} catch (err) { } catch (err) {
message.warning('用户名或密码错误!'); message.error('用户名或密码错误!');
console.error(err); console.error(err);
} }
}; };
const onFinishRegister = async (values: any) => { const onFinishRegister = async (values: any) => {
setRegisterLoading(true); setRegisterLoading(true);
const { username, password, phoneNumber } = values;
try { try {
// await wp.RegisterUser().create({ await signup(username, password, phoneNumber);
// ...values,
// custom_data: { org_unit: values.org_unit },
// });
message.success('注册成功!'); message.success('注册成功!');
setShowLogin(true); setShowLogin(true);
loginFormRef.current.submit(); loginFormRef.current.submit();
@ -51,13 +50,10 @@ const LoginPage: React.FC = () => {
return ( return (
<div className="flex justify-center items-center h-screen w-full bg-layout"> <div className="flex justify-center items-center h-screen w-full bg-layout">
<div className="flex items-center transition-all overflow-hidden bg-container rounded-xl" style={{ width: 800, height: 600, padding: 0 }}> <div className="flex items-center transition-all overflow-hidden bg-container rounded-xl " style={{ width: 800, height: 600 }}>
<div className="flex-1 py-10 px-10"> <div className="flex-1 py-10 px-10">
{showLogin ? ( {showLogin ? (
<> <>
<Link to="/" className="text-gray-400 text-sm hover:text-primary hover:cursor-pointer">
</Link>
<div className="text-center text-2xl text-primary select-none"> <div className="text-center text-2xl text-primary select-none">
<span className="px-2"></span> <span className="px-2"></span>
</div> </div>
@ -77,33 +73,28 @@ const LoginPage: React.FC = () => {
</Form> </Form>
</> </>
) : ( ) : (
<> <div >
<div
onClick={() => setShowLogin(true)}
className="text-sm text-gray-400 hover:cursor-pointer hover:text-primary"
>
</div>
<div className="text-center text-2xl text-primary"></div> <div className="text-center text-2xl text-primary"></div>
<Form requiredMark="optional" ref={registerFormRef} onFinish={onFinishRegister} layout="vertical" size="large"> <Form requiredMark="optional" ref={registerFormRef} onFinish={onFinishRegister} layout="vertical" size="large">
<Form.Item name="username" label="用户名" rules={[ <Form.Item name="username" label="用户名" rules={[
{ required: true, message: '请输入用户名' }, { required: true, message: '请输入用户名' },
{ min: 3, max: 15, message: '用户名长度在 3 到 15 个字符' } { min: 3, max: 15, message: '用户名长度在 3 到 15 个字符' }
]}> ]}>
<Input placeholder="输入用户名" /> <Input />
</Form.Item> </Form.Item>
<Form.Item name="phoneNumber" label="手机号" rules={[
<Form.Item name="deptId" label="单位" rules={[{ required: true, message: '请选择单位' }]}> { required: false },
<DepartmentSelect /> { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号' }
]}>
<Input />
</Form.Item> </Form.Item>
<Form.Item name="password" label="密码" rules={[ <Form.Item name="password" label="密码" rules={[
{ required: true, message: '请输入密码' }, { required: true, message: '请输入密码' },
{ min: 6, message: '密码长度不能小于 6 位' } { min: 6, message: '密码长度不能小于 6 位' }
]}> ]}>
<Input.Password placeholder="输入密码" /> <Input.Password />
</Form.Item> </Form.Item>
<Form.Item name="repeatPass" label="确认密码" dependencies={['password']} hasFeedback rules={[ <Form.Item name="repeatPass" label="确认密码" dependencies={['password']} hasFeedback rules={[
{ required: true, message: '请再次输入密码' }, { required: true, message: '请再次输入密码' },
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
@ -115,7 +106,7 @@ const LoginPage: React.FC = () => {
} }
}) })
]}> ]}>
<Input.Password placeholder="确认密码" /> <Input.Password />
</Form.Item> </Form.Item>
<div className="flex items-center justify-center"> <div className="flex items-center justify-center">
@ -124,7 +115,7 @@ const LoginPage: React.FC = () => {
</Button> </Button>
</div> </div>
</Form> </Form>
</> </div>
)} )}
</div> </div>
<div className={`transition-all h-full flex-1 text-white p-10 flex items-center justify-center bg-primary`}> <div className={`transition-all h-full flex-1 text-white p-10 flex items-center justify-center bg-primary`}>

View File

@ -1,3 +1,6 @@
import { useAuth } from "@web/src/providers/auth-provider"
export default function MainPage() { export default function MainPage() {
return <>hello,world</> const { user } = useAuth()
return <>hello,{user?.username}</>
} }

View File

@ -10,6 +10,7 @@ interface AuthContextProps {
user: UserProfile | null; user: UserProfile | null;
login: (username: string, password: string) => Promise<void>; login: (username: string, password: string) => Promise<void>;
logout: () => Promise<void>; logout: () => Promise<void>;
signup: (username: string, password: string, phoneNumber?: string) => Promise<void>;
refreshAccessToken: () => Promise<void>; refreshAccessToken: () => Promise<void>;
initializeAuth: () => void; initializeAuth: () => void;
startTokenRefreshInterval: () => void; startTokenRefreshInterval: () => void;
@ -102,6 +103,28 @@ export function AuthProvider({ children }: AuthProviderProps) {
} }
}; };
const signup = async (username: string, password: string, phoneNumber?: string): Promise<void> => {
try {
setIsLoading(true);
const response = await apiClient.post(`/auth/signup`, { username, password, phoneNumber });
// const { access_token, refresh_token, access_token_expires_at, refresh_token_expires_at } = response.data;
// localStorage.setItem('access_token', access_token);
// localStorage.setItem('refresh_token', refresh_token);
// localStorage.setItem('access_token_expires_at', access_token_expires_at);
// localStorage.setItem('refresh_token_expires_at', refresh_token_expires_at);
// setAccessToken(access_token);
// setRefreshToken(refresh_token);
// setIsAuthenticated(true);
// startTokenRefreshInterval();
// fetchUserProfile();
} catch (err) {
console.error("Signup failed", err);
throw new Error("Signup failed");
} finally {
setIsLoading(false);
}
};
const logout = async (): Promise<void> => { const logout = async (): Promise<void> => {
try { try {
setIsLoading(true); setIsLoading(true);
@ -154,6 +177,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
user, user,
login, login,
logout, logout,
signup,
refreshAccessToken, refreshAccessToken,
initializeAuth, initializeAuth,
startTokenRefreshInterval, startTokenRefreshInterval,