This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import { Column, BeforeInsert } from 'typeorm';
|
||||
import { v7 as uuidv7 } from 'uuid';
|
||||
|
||||
/**
|
||||
* Abstract base entity providing a UUID public identifier column.
|
||||
* Uses MariaDB native UUID type (stored as BINARY(16) internally,
|
||||
* auto-converts to string format — no transformer needed).
|
||||
*
|
||||
* App generates UUIDv7 via @BeforeInsert(); DB DEFAULT UUID() is fallback.
|
||||
*
|
||||
* @see ADR-019 Hybrid Identifier Strategy
|
||||
*/
|
||||
export abstract class UuidBaseEntity {
|
||||
@Column({
|
||||
type: 'uuid',
|
||||
unique: true,
|
||||
nullable: false,
|
||||
comment: 'UUID Public Identifier (ADR-019)',
|
||||
})
|
||||
uuid!: string;
|
||||
|
||||
@BeforeInsert()
|
||||
generateUuid(): void {
|
||||
if (!this.uuid) {
|
||||
this.uuid = uuidv7();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,13 @@ import {
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { User } from '../../../modules/user/entities/user.entity';
|
||||
import { UuidBaseEntity } from '../../entities/uuid-base.entity';
|
||||
import { Exclude } from 'class-transformer';
|
||||
|
||||
@Entity('attachments')
|
||||
export class Attachment {
|
||||
export class Attachment extends UuidBaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
@Exclude()
|
||||
id!: number;
|
||||
|
||||
@Column({ name: 'original_filename', length: 255 })
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { instanceToPlain } from 'class-transformer';
|
||||
|
||||
/** Metadata สำหรับ Paginated Response */
|
||||
export interface ResponseMeta {
|
||||
@@ -53,24 +54,31 @@ export class TransformInterceptor<T>
|
||||
): Observable<ApiResponse<T>> {
|
||||
return next.handle().pipe(
|
||||
map((data: T) => {
|
||||
const response = context.switchToHttp().getResponse<{ statusCode: number }>();
|
||||
const response = context
|
||||
.switchToHttp()
|
||||
.getResponse<{ statusCode: number }>();
|
||||
|
||||
// ADR-019: Serialize entities via class-transformer
|
||||
// This applies @Exclude() decorators to strip internal INT ids from responses
|
||||
const serialized = instanceToPlain(data) as T;
|
||||
|
||||
// Handle Pagination Response (Standardize)
|
||||
// ถ้า data มี structure { data: [], meta: {} } ให้ unzip ออกมา
|
||||
if (isPaginatedPayload(data)) {
|
||||
if (isPaginatedPayload(serialized)) {
|
||||
return {
|
||||
statusCode: response.statusCode,
|
||||
message: data.message ?? 'Success',
|
||||
data: data.data as unknown as T,
|
||||
meta: data.meta,
|
||||
message: serialized.message ?? 'Success',
|
||||
data: serialized.data as unknown as T,
|
||||
meta: serialized.meta,
|
||||
};
|
||||
}
|
||||
|
||||
const dataAsRecord = data as Record<string, unknown>;
|
||||
const dataAsRecord = serialized as Record<string, unknown>;
|
||||
return {
|
||||
statusCode: response.statusCode,
|
||||
message: (dataAsRecord?.['message'] as string | undefined) ?? 'Success',
|
||||
data: (dataAsRecord?.['result'] as T | undefined) ?? data,
|
||||
message:
|
||||
(dataAsRecord?.['message'] as string | undefined) ?? 'Success',
|
||||
data: (dataAsRecord?.['result'] as T | undefined) ?? serialized,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
|
||||
import { validate as uuidValidate } from 'uuid';
|
||||
|
||||
/**
|
||||
* Validates that a route parameter is a valid UUID string.
|
||||
* Accepts any UUID version (v1 from DB DEFAULT, v7 from app generation).
|
||||
*
|
||||
* Usage: @Param('uuid', ParseUuidPipe) uuid: string
|
||||
*
|
||||
* @see ADR-019 Hybrid Identifier Strategy
|
||||
*/
|
||||
@Injectable()
|
||||
export class ParseUuidPipe implements PipeTransform<string> {
|
||||
transform(value: string): string {
|
||||
if (!uuidValidate(value)) {
|
||||
throw new BadRequestException(`Invalid UUID format: ${value}`);
|
||||
}
|
||||
return value.toLowerCase();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user