251123:2300 Update T1

This commit is contained in:
2025-11-24 08:15:15 +07:00
parent 23006898d9
commit 9360d78ea6
81 changed files with 4232 additions and 347 deletions

View File

@@ -0,0 +1,26 @@
import {
IsString,
IsNotEmpty,
IsInt,
IsOptional,
IsBoolean,
IsObject,
} from 'class-validator';
export class CreateJsonSchemaDto {
@IsString()
@IsNotEmpty()
schemaCode!: string; // รหัส Schema (ต้องไม่ซ้ำ เช่น 'RFA_DWG_V1')
@IsInt()
@IsOptional()
version?: number; // เวอร์ชัน (Default: 1)
@IsObject()
@IsNotEmpty()
schemaDefinition!: Record<string, any>; // โครงสร้าง JSON Schema (Standard Format)
@IsBoolean()
@IsOptional()
isActive?: boolean; // สถานะการใช้งาน
}

View File

@@ -0,0 +1,27 @@
import { IsString, IsOptional, IsBoolean, IsInt } from 'class-validator';
import { Type, Transform } from 'class-transformer';
export class SearchJsonSchemaDto {
@IsString()
@IsOptional()
search?: string; // ค้นหาจาก schemaCode
@IsBoolean()
@IsOptional()
@Transform(({ value }) => {
if (value === 'true') return true;
if (value === 'false') return false;
return value;
})
isActive?: boolean;
@IsOptional()
@IsInt()
@Type(() => Number)
page: number = 1;
@IsOptional()
@IsInt()
@Type(() => Number)
limit: number = 20;
}

View File

@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/swagger';
import { CreateJsonSchemaDto } from './create-json-schema.dto';
export class UpdateJsonSchemaDto extends PartialType(CreateJsonSchemaDto) {}

View File

@@ -1,25 +1,36 @@
import { Controller, Post, Body, Param, UseGuards } from '@nestjs/common';
import { JsonSchemaService } from './json-schema.service.js';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard.js';
import { RbacGuard } from '../../common/guards/rbac.guard.js';
import { RequirePermission } from '../../common/decorators/require-permission.decorator.js';
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
@Controller('json-schemas')
import { JsonSchemaService } from './json-schema.service';
// ✅ FIX: Import DTO
import { CreateJsonSchemaDto } from './dto/create-json-schema.dto';
// ✅ FIX: แก้ไข Path ของ Guards
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
import { RbacGuard } from '../../common/guards/rbac.guard';
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
@ApiTags('JSON Schemas') // ✅ Add Swagger Tag
@ApiBearerAuth()
@UseGuards(JwtAuthGuard, RbacGuard)
@Controller('json-schemas')
export class JsonSchemaController {
constructor(private readonly schemaService: JsonSchemaService) {}
@Post(':code')
@RequirePermission('system.manage_all') // เฉพาะ Superadmin หรือผู้มีสิทธิ์จัดการ System
create(@Param('code') code: string, @Body() definition: any) {
return this.schemaService.createOrUpdate(code, definition);
@Post()
@ApiOperation({ summary: 'Create or Update JSON Schema' })
@RequirePermission('system.manage_all') // Admin Only
create(@Body() createDto: CreateJsonSchemaDto) {
return this.schemaService.createOrUpdate(
createDto.schemaCode,
createDto.schemaDefinition,
);
}
// Endpoint สำหรับ Test Validate (Optional)
@Post(':code/validate')
@ApiOperation({ summary: 'Test validation against a schema' })
@RequirePermission('document.view')
async validate(@Param('code') code: string, @Body() data: any) {
const isValid = await this.schemaService.validate(code, data);
return { valid: isValid };
return { valid: isValid, message: 'Validation passed' };
}
}

View File

@@ -1,17 +1,14 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { JsonSchemaService } from './json-schema.service.js';
import { JsonSchemaController } from './json-schema.controller.js';
import { JsonSchema } from './entities/json-schema.entity.js';
import { UserModule } from '../user/user.module.js'; // <--- 1. Import UserModule
import { JsonSchemaService } from './json-schema.service';
import { JsonSchemaController } from './json-schema.controller';
import { JsonSchema } from './entities/json-schema.entity';
import { UserModule } from '../user/user.module';
@Module({
imports: [
TypeOrmModule.forFeature([JsonSchema]),
UserModule, // <--- 2. ใส่ UserModule ใน imports
],
imports: [TypeOrmModule.forFeature([JsonSchema]), UserModule],
controllers: [JsonSchemaController],
providers: [JsonSchemaService],
exports: [JsonSchemaService], // Export ให้ Module อื่นเรียกใช้ .validate()
exports: [JsonSchemaService],
})
export class JsonSchemaModule {}

View File

@@ -3,31 +3,32 @@ import {
OnModuleInit,
BadRequestException,
NotFoundException,
Logger,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
import { JsonSchema } from './entities/json-schema.entity.js';
import { JsonSchema } from './entities/json-schema.entity'; // ลบ .js
@Injectable()
export class JsonSchemaService implements OnModuleInit {
private ajv: Ajv;
// Cache ตัว Validator ที่ Compile แล้ว เพื่อประสิทธิภาพ
private validators = new Map<string, any>();
private readonly logger = new Logger(JsonSchemaService.name);
constructor(
@InjectRepository(JsonSchema)
private schemaRepo: Repository<JsonSchema>,
) {
// ตั้งค่า AJV
this.ajv = new Ajv({ allErrors: true, strict: false }); // strict: false เพื่อยืดหยุ่นกับ custom keywords
addFormats(this.ajv); // รองรับ format เช่น email, date-time
this.ajv = new Ajv({ allErrors: true, strict: false });
addFormats(this.ajv);
}
onModuleInit() {
// (Optional) โหลด Schema ทั้งหมดมา Cache ตอนเริ่ม App ก็ได้
// แต่ตอนนี้ใช้วิธี Lazy Load (โหลดเมื่อใช้) ไปก่อน
async onModuleInit() {
// Pre-load schemas (Optional for performance)
// const schemas = await this.schemaRepo.find({ where: { isActive: true } });
// schemas.forEach(s => this.createValidator(s.schemaCode, s.schemaDefinition));
}
/**
@@ -36,7 +37,6 @@ export class JsonSchemaService implements OnModuleInit {
async validate(schemaCode: string, data: any): Promise<boolean> {
let validate = this.validators.get(schemaCode);
// ถ้ายังไม่มีใน Cache หรือต้องการตัวล่าสุด ให้ดึงจาก DB
if (!validate) {
const schema = await this.schemaRepo.findOne({
where: { schemaCode, isActive: true },
@@ -59,19 +59,21 @@ export class JsonSchemaService implements OnModuleInit {
const valid = validate(data);
if (!valid) {
// รวบรวม Error ทั้งหมดส่งกลับไป
const errors = validate.errors
?.map((e: any) => `${e.instancePath} ${e.message}`)
.join(', ');
// โยน Error กลับไปเพื่อให้ Controller/Service ปลายทางจัดการ
throw new BadRequestException(`JSON Validation Failed: ${errors}`);
}
return true;
}
// ฟังก์ชันสำหรับสร้าง/อัปเดต Schema (สำหรับ Admin)
async createOrUpdate(schemaCode: string, definition: any) {
// ตรวจสอบก่อนว่า Definition เป็น JSON Schema ที่ถูกต้องไหม
/**
* สร้างหรืออัปเดต Schema
*/
async createOrUpdate(schemaCode: string, definition: Record<string, any>) {
// 1. ตรวจสอบว่า Definition เป็น JSON Schema ที่ถูกต้องไหม
try {
this.ajv.compile(definition);
} catch (error: any) {
@@ -80,6 +82,7 @@ export class JsonSchemaService implements OnModuleInit {
);
}
// 2. บันทึกลง DB
let schema = await this.schemaRepo.findOne({ where: { schemaCode } });
if (schema) {
@@ -93,9 +96,12 @@ export class JsonSchemaService implements OnModuleInit {
});
}
// Clear Cache เก่า
this.validators.delete(schemaCode);
const savedSchema = await this.schemaRepo.save(schema);
return this.schemaRepo.save(schema);
// 3. Clear Cache เพื่อให้ครั้งหน้าโหลดตัวใหม่
this.validators.delete(schemaCode);
this.logger.log(`Schema '${schemaCode}' updated (v${savedSchema.version})`);
return savedSchema;
}
}