xpeditis2.0/apps/backend/src/application/dto/blog-post.dto.ts
2026-06-11 19:18:56 +02:00

214 lines
4.7 KiB
TypeScript

import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import {
IsString,
IsNotEmpty,
IsOptional,
IsArray,
IsBoolean,
IsEnum,
IsDateString,
MaxLength,
MinLength,
Matches,
} from 'class-validator';
import { BlogPostStatus, type BlogPostCategory } from '@domain/entities/blog-post.entity';
const CATEGORIES: BlogPostCategory[] = ['industry', 'technology', 'guides', 'news'];
export class CreateBlogPostDto {
@ApiProperty()
@IsString()
@IsNotEmpty()
@MaxLength(255)
title: string;
@ApiProperty({ description: 'URL-friendly slug, e.g. "my-article"' })
@IsString()
@IsNotEmpty()
@MaxLength(255)
@Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {
message: 'Slug must be lowercase alphanumeric with hyphens',
})
slug: string;
@ApiProperty()
@IsString()
@IsNotEmpty()
@MinLength(10)
excerpt: string;
@ApiProperty()
@IsString()
@IsNotEmpty()
content: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(500)
coverImageUrl?: string;
@ApiProperty({ enum: CATEGORIES })
@IsEnum(CATEGORIES)
category: BlogPostCategory;
@ApiPropertyOptional({ type: [String] })
@IsOptional()
@IsArray()
@IsString({ each: true })
tags?: string[];
@ApiProperty()
@IsString()
@IsNotEmpty()
@MaxLength(255)
authorName: string;
@ApiPropertyOptional({ description: 'ISO date string for scheduled publication' })
@IsOptional()
@IsDateString()
scheduledAt?: string;
@ApiPropertyOptional({ description: 'SEO meta title (50-60 chars recommended)' })
@IsOptional()
@IsString()
@MaxLength(255)
metaTitle?: string;
@ApiPropertyOptional({ description: 'SEO meta description (150-160 chars recommended)' })
@IsOptional()
@IsString()
@MaxLength(500)
metaDescription?: string;
@ApiPropertyOptional({ description: 'Primary SEO keyword' })
@IsOptional()
@IsString()
@MaxLength(255)
primaryKeyword?: string;
@ApiPropertyOptional({ type: [String], description: 'Secondary SEO keywords' })
@IsOptional()
@IsArray()
@IsString({ each: true })
secondaryKeywords?: string[];
}
export class UpdateBlogPostDto {
@ApiPropertyOptional()
@IsOptional()
@IsString()
@IsNotEmpty()
@MaxLength(255)
title?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(255)
@Matches(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, {
message: 'Slug must be lowercase alphanumeric with hyphens',
})
slug?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
excerpt?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
content?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(500)
coverImageUrl?: string;
@ApiPropertyOptional({ enum: CATEGORIES })
@IsOptional()
@IsEnum(CATEGORIES)
category?: BlogPostCategory;
@ApiPropertyOptional({ type: [String] })
@IsOptional()
@IsArray()
@IsString({ each: true })
tags?: string[];
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(255)
authorName?: string;
@ApiPropertyOptional({ enum: BlogPostStatus })
@IsOptional()
@IsEnum(BlogPostStatus)
status?: BlogPostStatus;
@ApiPropertyOptional()
@IsOptional()
@IsBoolean()
isFeatured?: boolean;
@ApiPropertyOptional({ description: 'ISO date string for scheduled publication' })
@IsOptional()
@IsDateString()
scheduledAt?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(255)
metaTitle?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(500)
metaDescription?: string;
@ApiPropertyOptional()
@IsOptional()
@IsString()
@MaxLength(255)
primaryKeyword?: string;
@ApiPropertyOptional({ type: [String] })
@IsOptional()
@IsArray()
@IsString({ each: true })
secondaryKeywords?: string[];
}
export class BlogPostResponseDto {
@ApiProperty() id: string;
@ApiProperty() title: string;
@ApiProperty() slug: string;
@ApiProperty() excerpt: string;
@ApiProperty() content: string;
@ApiPropertyOptional() coverImageUrl?: string;
@ApiProperty() category: string;
@ApiProperty({ type: [String] }) tags: string[];
@ApiProperty() authorName: string;
@ApiProperty({ enum: BlogPostStatus }) status: BlogPostStatus;
@ApiProperty() isFeatured: boolean;
@ApiPropertyOptional() publishedAt?: Date;
@ApiPropertyOptional() metaTitle?: string;
@ApiPropertyOptional() metaDescription?: string;
@ApiPropertyOptional() primaryKeyword?: string;
@ApiProperty({ type: [String] }) secondaryKeywords: string[];
@ApiProperty() createdAt: Date;
@ApiProperty() updatedAt: Date;
}
export class BlogPostListResponseDto {
@ApiProperty({ type: [BlogPostResponseDto] }) posts: BlogPostResponseDto[];
@ApiProperty() total: number;
@ApiProperty() limit: number;
@ApiProperty() offset: number;
}