This commit is contained in:
@@ -34,7 +34,6 @@
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/elasticsearch": "^11.1.0",
|
||||
"@nestjs/jwt": "^11.0.1",
|
||||
"@nestjs/mapped-types": "^2.1.0",
|
||||
"@nestjs/passport": "^11.0.5",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/platform-socket.io": "^11.1.9",
|
||||
|
||||
@@ -27,12 +27,7 @@ import {
|
||||
ApiResponse,
|
||||
ApiBody,
|
||||
} from '@nestjs/swagger';
|
||||
import { Request } from 'express';
|
||||
|
||||
// สร้าง Interface สำหรับ Request ที่มี User
|
||||
interface RequestWithUser extends Request {
|
||||
user: any;
|
||||
}
|
||||
import type { RequestWithUser, RequestWithRefreshUser } from '../interfaces/request-with-user.interface';
|
||||
|
||||
@ApiTags('Authentication')
|
||||
@Controller('auth')
|
||||
@@ -95,7 +90,7 @@ export class AuthController {
|
||||
},
|
||||
},
|
||||
})
|
||||
async refresh(@Req() req: RequestWithUser) {
|
||||
async refresh(@Req() req: RequestWithRefreshUser) {
|
||||
return this.authService.refreshToken(req.user.sub, req.user.refreshToken);
|
||||
}
|
||||
|
||||
@@ -121,7 +116,7 @@ export class AuthController {
|
||||
}
|
||||
// ส่ง refresh token ไปด้วยถ้ามี (ใน header หรือ body)
|
||||
// สำหรับตอนนี้ส่งแค่ access token ไป blacklist
|
||||
return this.authService.logout(req.user.sub, token);
|
||||
return this.authService.logout(req.user.user_id, token);
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { AuthService } from './auth.service';
|
||||
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
|
||||
import { User } from '../../modules/user/entities/user.entity';
|
||||
import type { RequestWithUser } from '../interfaces/request-with-user.interface';
|
||||
|
||||
@ApiTags('Authentication')
|
||||
@Controller('auth/sessions')
|
||||
@@ -28,7 +29,7 @@ export class SessionController {
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'List all active sessions (Admin/DC Only)' })
|
||||
@ApiResponse({ status: 200, description: 'List of active sessions' })
|
||||
async getActiveSessions(@Req() req: any) {
|
||||
async getActiveSessions(@Req() req: RequestWithUser) {
|
||||
this.checkAdminRole(req.user);
|
||||
return this.authService.getActiveSessions();
|
||||
}
|
||||
@@ -36,7 +37,10 @@ export class SessionController {
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: 'Revoke a session by ID (Admin/DC Only)' })
|
||||
@ApiResponse({ status: 200, description: 'Session revoked' })
|
||||
async revokeSession(@Param('id', ParseIntPipe) id: number, @Req() req: any) {
|
||||
async revokeSession(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() req: RequestWithUser
|
||||
) {
|
||||
this.checkAdminRole(req.user);
|
||||
await this.authService.revokeSession(id);
|
||||
return { message: 'Session revoked successfully' };
|
||||
|
||||
@@ -7,11 +7,12 @@ import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Request } from 'express';
|
||||
import type { JwtPayload } from './jwt.strategy';
|
||||
|
||||
@Injectable()
|
||||
export class JwtRefreshStrategy extends PassportStrategy(
|
||||
Strategy,
|
||||
'jwt-refresh',
|
||||
'jwt-refresh'
|
||||
) {
|
||||
constructor(configService: ConfigService) {
|
||||
super({
|
||||
@@ -23,7 +24,7 @@ export class JwtRefreshStrategy extends PassportStrategy(
|
||||
});
|
||||
}
|
||||
|
||||
async validate(req: Request, payload: any) {
|
||||
async validate(req: Request, payload: JwtPayload) {
|
||||
const refreshToken = ExtractJwt.fromAuthHeaderAsBearerToken()(req);
|
||||
return {
|
||||
...payload,
|
||||
|
||||
@@ -20,14 +20,7 @@ import type { Response } from 'express';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { FileStorageService } from './file-storage.service';
|
||||
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
|
||||
|
||||
// Interface เพื่อระบุ Type ของ Request ที่ผ่าน JwtAuthGuard มาแล้ว
|
||||
interface RequestWithUser {
|
||||
user: {
|
||||
userId: number;
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
import type { RequestWithUser } from '../interfaces/request-with-user.interface';
|
||||
|
||||
@Controller('files')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@@ -53,7 +46,7 @@ export class FileStorageController {
|
||||
@Request() req: RequestWithUser
|
||||
) {
|
||||
// ส่ง userId จาก Token ไปด้วย
|
||||
return this.fileStorageService.upload(file, req.user.userId);
|
||||
return this.fileStorageService.upload(file, req.user.user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +83,7 @@ export class FileStorageController {
|
||||
@Request() req: RequestWithUser
|
||||
) {
|
||||
// ส่ง userId ไปด้วยเพื่อตรวจสอบความเป็นเจ้าของ
|
||||
await this.fileStorageService.delete(id, req.user.userId);
|
||||
await this.fileStorageService.delete(id, req.user.user_id);
|
||||
return { message: 'File deleted successfully', id };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// File: src/common/interfaces/request-with-user.interface.ts
|
||||
// NestJS 11: Shared typed Request interfaces (replaces scattered `req: any` patterns)
|
||||
|
||||
import { Request } from 'express';
|
||||
import { User } from '../../modules/user/entities/user.entity';
|
||||
|
||||
/**
|
||||
* Request object after JwtAuthGuard has validated the token.
|
||||
* Passport attaches the User entity returned by JwtStrategy.validate() to `req.user`.
|
||||
*/
|
||||
export interface RequestWithUser extends Request {
|
||||
user: User;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload shape returned by JwtRefreshStrategy.validate().
|
||||
* Contains JWT claims + the raw refresh token for rotation.
|
||||
*/
|
||||
export interface JwtRefreshPayload {
|
||||
sub: number;
|
||||
username: string;
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request object after JwtRefreshGuard has validated the refresh token.
|
||||
*/
|
||||
export interface RequestWithRefreshUser extends Request {
|
||||
user: JwtRefreshPayload;
|
||||
}
|
||||
+1
-1
@@ -73,7 +73,7 @@ async function bootstrap() {
|
||||
const swaggerConfig = new DocumentBuilder()
|
||||
.setTitle('LCBP3 DMS API')
|
||||
.setDescription('Document Management System API Documentation')
|
||||
.setVersion('1.4.3')
|
||||
.setVersion('1.8.1')
|
||||
.addBearerAuth() // เพิ่มปุ่มใส่ Token (รูปกุญแจ)
|
||||
.build();
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import { RbacGuard } from '../../common/guards/rbac.guard';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
import { Audit } from '../../common/decorators/audit.decorator';
|
||||
import { ParseUuidPipe } from '../../common/pipes/parse-uuid.pipe';
|
||||
import type { RequestWithUser } from '../../common/interfaces/request-with-user.interface';
|
||||
|
||||
@ApiTags('Correspondences')
|
||||
@Controller('correspondences')
|
||||
@@ -48,13 +49,7 @@ export class CorrespondenceController {
|
||||
@RequirePermission('workflow.action_review')
|
||||
processAction(
|
||||
@Body() actionDto: WorkflowActionDto,
|
||||
@Request()
|
||||
req: Request & {
|
||||
user: {
|
||||
user_id: number;
|
||||
assignments?: Array<{ role: { roleName: string } }>;
|
||||
};
|
||||
}
|
||||
@Request() req: RequestWithUser
|
||||
) {
|
||||
// Extract roles from user assignments for DSL requirements check
|
||||
const userRoles =
|
||||
@@ -87,12 +82,9 @@ export class CorrespondenceController {
|
||||
@Audit('correspondence.create', 'correspondence')
|
||||
create(
|
||||
@Body() createDto: CreateCorrespondenceDto,
|
||||
@Request() req: Request & { user: unknown }
|
||||
@Request() req: RequestWithUser
|
||||
) {
|
||||
return this.correspondenceService.create(
|
||||
createDto,
|
||||
req.user as Parameters<typeof this.correspondenceService.create>[1]
|
||||
);
|
||||
return this.correspondenceService.create(createDto, req.user);
|
||||
}
|
||||
|
||||
@Post('preview-number')
|
||||
@@ -104,11 +96,11 @@ export class CorrespondenceController {
|
||||
@RequirePermission('correspondence.create')
|
||||
previewNumber(
|
||||
@Body() createDto: CreateCorrespondenceDto,
|
||||
@Request() req: Request & { user: unknown }
|
||||
@Request() req: RequestWithUser
|
||||
) {
|
||||
return this.correspondenceService.previewDocumentNumber(
|
||||
createDto,
|
||||
req.user as Parameters<typeof this.correspondenceService.create>[1]
|
||||
req.user
|
||||
);
|
||||
}
|
||||
|
||||
@@ -131,13 +123,7 @@ export class CorrespondenceController {
|
||||
async submit(
|
||||
@Param('uuid', ParseUuidPipe) uuid: string,
|
||||
@Body() submitDto: SubmitCorrespondenceDto,
|
||||
@Request()
|
||||
req: Request & {
|
||||
user: {
|
||||
user_id: number;
|
||||
assignments?: Array<{ role: { roleName: string } }>;
|
||||
};
|
||||
}
|
||||
@Request() req: RequestWithUser
|
||||
) {
|
||||
const corr = await this.correspondenceService.findOneByUuid(uuid);
|
||||
// Extract roles from user assignments
|
||||
@@ -172,14 +158,10 @@ export class CorrespondenceController {
|
||||
async update(
|
||||
@Param('uuid', ParseUuidPipe) uuid: string,
|
||||
@Body() updateDto: UpdateCorrespondenceDto,
|
||||
@Request() req: Request & { user: unknown }
|
||||
@Request() req: RequestWithUser
|
||||
) {
|
||||
const corr = await this.correspondenceService.findOneByUuid(uuid);
|
||||
return this.correspondenceService.update(
|
||||
corr.id,
|
||||
updateDto,
|
||||
req.user as Parameters<typeof this.correspondenceService.create>[1]
|
||||
);
|
||||
return this.correspondenceService.update(corr.id, updateDto, req.user);
|
||||
}
|
||||
|
||||
@Get(':uuid/references')
|
||||
|
||||
@@ -31,6 +31,7 @@ import { WorkflowTransitionDto } from './dto/workflow-transition.dto';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { RbacGuard } from '../../common/guards/rbac.guard';
|
||||
import type { RequestWithUser } from '../../common/interfaces/request-with-user.interface';
|
||||
|
||||
@ApiTags('Workflow Engine')
|
||||
@ApiBearerAuth() // ระบุว่าต้องใช้ Token ใน Swagger
|
||||
@@ -95,10 +96,10 @@ export class WorkflowEngineController {
|
||||
async processTransition(
|
||||
@Param('id') instanceId: string,
|
||||
@Body() dto: WorkflowTransitionDto,
|
||||
@Request() req: any
|
||||
@Request() req: RequestWithUser
|
||||
) {
|
||||
// ดึง User ID จาก Token (req.user มาจาก JwtStrategy)
|
||||
const userId = req.user?.userId;
|
||||
const userId = req.user?.user_id;
|
||||
|
||||
return this.workflowService.processTransition(
|
||||
instanceId,
|
||||
|
||||
Reference in New Issue
Block a user