This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user