diff --git a/backend/src/modules/auth/auth.controller.ts b/backend/src/modules/auth/auth.controller.ts index 15222ba..78e475f 100644 --- a/backend/src/modules/auth/auth.controller.ts +++ b/backend/src/modules/auth/auth.controller.ts @@ -1,6 +1,8 @@ import { Controller, Post, Body, Headers, UnauthorizedException } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; import { AuthService } from './auth.service'; +@ApiTags('auth') @Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {} @@ -23,4 +25,10 @@ export class AuthController { if (!tenantId) throw new UnauthorizedException('Tenant ID (x-tenant-id) is required'); return this.authService.verifyOtp(tenantId, phone, code); } + + @Post('refresh') + async refresh(@Body('refresh_token') refreshToken: string) { + if (!refreshToken) throw new UnauthorizedException('refresh_token is required'); + return this.authService.refresh(refreshToken); + } } diff --git a/backend/src/modules/auth/auth.module.ts b/backend/src/modules/auth/auth.module.ts index 02b4c18..c1ce585 100644 --- a/backend/src/modules/auth/auth.module.ts +++ b/backend/src/modules/auth/auth.module.ts @@ -17,7 +17,7 @@ import { JwtStrategy } from './strategies/jwt.strategy'; useFactory: async (configService: ConfigService) => ({ secret: configService.get('JWT_SECRET') || 'change_me_jwt_secret', signOptions: { - expiresIn: configService.get('JWT_EXPIRES') || '15m', + expiresIn: (configService.get('JWT_EXPIRES') || '15m') as any, }, }), }), diff --git a/backend/src/modules/auth/auth.service.ts b/backend/src/modules/auth/auth.service.ts index 40f1185..851e996 100644 --- a/backend/src/modules/auth/auth.service.ts +++ b/backend/src/modules/auth/auth.service.ts @@ -69,7 +69,7 @@ export class AuthService { access_token: this.jwtService.sign(base), refresh_token: this.jwtService.sign( { ...base, type: 'refresh' }, - { expiresIn: this.config.get('jwt.refreshExpires') ?? '30d' }, + { expiresIn: (this.config.get('jwt.refreshExpires') ?? '30d') as any }, ), user, }; diff --git a/backend/src/modules/auth/decorators/current-user.decorator.ts b/backend/src/modules/auth/decorators/current-user.decorator.ts new file mode 100644 index 0000000..d045790 --- /dev/null +++ b/backend/src/modules/auth/decorators/current-user.decorator.ts @@ -0,0 +1,16 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +export interface AuthUser { + userId: string; + phone: string; + role: string; + tenantId: string; +} + +/** + * @CurrentUser() — يحقن المستخدم المصادَق (من JwtStrategy.validate) في المتحكّم. + */ +export const CurrentUser = createParamDecorator( + (_data: unknown, ctx: ExecutionContext): AuthUser => + ctx.switchToHttp().getRequest().user, +); diff --git a/backend/src/modules/users/users.controller.ts b/backend/src/modules/users/users.controller.ts new file mode 100644 index 0000000..6644b25 --- /dev/null +++ b/backend/src/modules/users/users.controller.ts @@ -0,0 +1,27 @@ +import { Body, Controller, Get, Patch, UseGuards } from '@nestjs/common'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { UsersService } from './users.service'; +import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; +import { + CurrentUser, + AuthUser, +} from '../auth/decorators/current-user.decorator'; + +@ApiTags('users') +@ApiBearerAuth() +@UseGuards(JwtAuthGuard) +@Controller('users') +export class UsersController { + constructor(private readonly users: UsersService) {} + + // بيانات المستخدم المصادَق حالياً — يثبت أن سلسلة JWT+الحارس تعمل. + @Get('me') + me(@CurrentUser() user: AuthUser) { + return this.users.findById(user.tenantId, user.userId); + } + + @Patch('me') + updateMe(@CurrentUser() user: AuthUser, @Body('name') name: string) { + return this.users.updateProfile(user.tenantId, user.userId, { name }); + } +} diff --git a/backend/src/modules/users/users.module.ts b/backend/src/modules/users/users.module.ts index f8cc930..dc8898f 100644 --- a/backend/src/modules/users/users.module.ts +++ b/backend/src/modules/users/users.module.ts @@ -2,9 +2,11 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; import { UsersService } from './users.service'; +import { UsersController } from './users.controller'; @Module({ imports: [TypeOrmModule.forFeature([User])], + controllers: [UsersController], providers: [UsersService], exports: [UsersService], }) diff --git a/backend/src/modules/users/users.service.ts b/backend/src/modules/users/users.service.ts index 6c2ab0d..e825af5 100644 --- a/backend/src/modules/users/users.service.ts +++ b/backend/src/modules/users/users.service.ts @@ -22,4 +22,17 @@ export class UsersService { const user = this.userRepository.create({ tenant_id: tenantId, phone, role: role as UserRole }); return this.userRepository.save(user); } + + async listByTenant(tenantId: string): Promise { + return this.userRepository.find({ where: { tenant_id: tenantId } }); + } + + async updateProfile( + tenantId: string, + id: string, + data: Partial>, + ): Promise { + await this.userRepository.update({ tenant_id: tenantId, id }, { name: data.name }); + return this.findById(tenantId, id); + } }