260218:1712 20260218 TASK-BEFE-001n
Build and Deploy / deploy (push) Successful in 4m55s

This commit is contained in:
admin
2026-02-18 17:12:11 +07:00
parent 01ce68acda
commit b84284f8a9
54 changed files with 1307 additions and 339 deletions
+2 -1
View File
@@ -10,6 +10,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthService } from './auth.service.js';
import { AuthController } from './auth.controller.js';
import { SessionController } from './session.controller.js';
import { UserModule } from '../../modules/user/user.module.js';
import { JwtStrategy } from './strategies/jwt.strategy.js';
import { JwtRefreshStrategy } from './strategies/jwt-refresh.strategy.js';
@@ -37,7 +38,7 @@ import { PermissionsGuard } from './guards/permissions.guard';
CaslModule,
],
providers: [AuthService, JwtStrategy, JwtRefreshStrategy, PermissionsGuard],
controllers: [AuthController],
controllers: [AuthController, SessionController],
exports: [AuthService, PermissionsGuard],
})
export class AuthModule {}
@@ -0,0 +1,59 @@
import {
Controller,
Get,
Delete,
Param,
UseGuards,
ParseIntPipe,
UnauthorizedException,
Req,
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiBearerAuth,
ApiResponse,
} from '@nestjs/swagger';
import { AuthService } from './auth.service';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { User } from '../../modules/user/entities/user.entity';
@ApiTags('Authentication')
@Controller('auth/sessions')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
export class SessionController {
constructor(private readonly authService: AuthService) {}
@Get()
@ApiOperation({ summary: 'List all active sessions (Admin/DC Only)' })
@ApiResponse({ status: 200, description: 'List of active sessions' })
async getActiveSessions(@Req() req: any) {
this.checkAdminRole(req.user);
return this.authService.getActiveSessions();
}
@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) {
this.checkAdminRole(req.user);
await this.authService.revokeSession(id);
return { message: 'Session revoked successfully' };
}
private checkAdminRole(user: User) {
// Check if user has ADMIN or DC role via assignments
const hasPermission = user.assignments?.some(
(assignment) =>
assignment.role.roleName === 'ADMIN' ||
assignment.role.roleName === 'DC'
);
if (!hasPermission) {
throw new UnauthorizedException(
'Insufficient permissions: ADMIN or DC role required'
);
}
}
}
@@ -40,6 +40,9 @@ export class Attachment {
@Column({ length: 64, nullable: true })
checksum?: string;
@Column({ name: 'reference_date', type: 'date', nullable: true })
referenceDate?: Date;
@Column({ name: 'uploaded_by_user_id' })
uploadedByUserId!: number;
@@ -81,7 +81,15 @@ export class FileStorageService {
* Phase 2: Commit (ย้ายไฟล์จาก Temp -> Permanent)
* เมธอดนี้จะถูกเรียกโดย Service อื่น (เช่น CorrespondenceService) เมื่อกด Save
*/
async commit(tempIds: string[]): Promise<Attachment[]> {
/**
* Phase 2: Commit (ย้ายไฟล์จาก Temp -> Permanent)
* เมธอดนี้จะถูกเรียกโดย Service อื่น (เช่น CorrespondenceService) เมื่อกด Save
* Updated [Phase 2]: Support issueDate and documentType for organized storage
*/
async commit(
tempIds: string[],
options?: { issueDate?: Date; documentType?: string }
): Promise<Attachment[]> {
if (!tempIds || tempIds.length === 0) {
return [];
}
@@ -100,12 +108,27 @@ export class FileStorageService {
}
const committedAttachments: Attachment[] = [];
const today = new Date();
const year = today.getFullYear().toString();
const month = (today.getMonth() + 1).toString().padStart(2, '0');
// Use issueDate if provided, otherwise default to current date
const refDate = options?.issueDate
? new Date(options.issueDate)
: new Date();
// โฟลเดอร์ถาวรแยกตาม ปี/เดือน
const permanentDir = path.join(this.permanentDir, year, month);
// Validate Date (in case invalid string passed)
const effectiveDate = isNaN(refDate.getTime()) ? new Date() : refDate;
const year = effectiveDate.getFullYear().toString();
const month = (effectiveDate.getMonth() + 1).toString().padStart(2, '0');
// Construct Path: permanent/{DocumentType}/{YYYY}/{MM}/filename
const docTypeFolder = options?.documentType || 'General';
// โฟลเดอร์ถาวรแยกตาม Type/ปี/เดือน
const permanentDir = path.join(
this.permanentDir,
docTypeFolder,
year,
month
);
await fs.ensureDir(permanentDir);
for (const att of attachments) {
@@ -122,6 +145,7 @@ export class FileStorageService {
att.isTemporary = false;
att.tempId = null as any; // เคลียร์ tempId (TypeORM อาจต้องการ null แทน undefined สำหรับ nullable)
att.expiresAt = null as any; // เคลียร์วันหมดอายุ
att.referenceDate = effectiveDate; // Save reference date
committedAttachments.push(await this.attachmentRepository.save(att));
} else {