FullStack-Blog-Nestjs-Nextjs-Postgres
This commit is contained in:
78
backend/src/blog-posts/dto/create-post.dto.ts
Normal file
78
backend/src/blog-posts/dto/create-post.dto.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Transform } from 'class-transformer';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsOptional,
|
||||
IsString,
|
||||
MaxLength,
|
||||
MinLength,
|
||||
} from 'class-validator';
|
||||
import { ContentFormat, PostStatus } from '../entities/blog-post.entity';
|
||||
|
||||
/** Parse "true"/"false" strings into a real boolean */
|
||||
function transformBoolean({ value }: { value: unknown }): boolean | undefined {
|
||||
if (value === 'true' || value === true) return true;
|
||||
if (value === 'false' || value === false) return false;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Parse a comma-separated string OR an array into a trimmed string[] */
|
||||
function transformCSV({ value }: { value: unknown }): string[] {
|
||||
if (Array.isArray(value)) return (value as unknown[]).map((v) => String(v).trim()).filter(Boolean);
|
||||
if (typeof value === 'string') return value.split(',').map((s) => s.trim()).filter(Boolean);
|
||||
return [];
|
||||
}
|
||||
|
||||
export class CreatePostDto {
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
@MaxLength(255)
|
||||
title: string;
|
||||
|
||||
/** Optional custom slug; if omitted the service generates one from the title */
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@MaxLength(300)
|
||||
slug?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
excerpt?: string;
|
||||
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
content: string;
|
||||
|
||||
@IsEnum(ContentFormat)
|
||||
@IsOptional()
|
||||
contentFormat?: ContentFormat;
|
||||
|
||||
@IsEnum(PostStatus)
|
||||
@IsOptional()
|
||||
status?: PostStatus;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
featuredImageUrl?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
featuredImageAlt?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
@Transform(transformBoolean)
|
||||
isFeatured?: boolean;
|
||||
|
||||
/** Accepts a comma-separated string "tag1,tag2" or a plain string[] */
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
@Transform(transformCSV)
|
||||
tags?: string[];
|
||||
|
||||
/** Accepts a comma-separated string "cat1,cat2" or a plain string[] */
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
@Transform(transformCSV)
|
||||
categories?: string[];
|
||||
}
|
||||
37
backend/src/blog-posts/dto/list-posts-query.dto.ts
Normal file
37
backend/src/blog-posts/dto/list-posts-query.dto.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { IsEnum, IsNumberString, IsOptional, IsString } from 'class-validator';
|
||||
import { PostStatus } from '../entities/blog-post.entity';
|
||||
|
||||
export class ListPostsQueryDto {
|
||||
@IsNumberString()
|
||||
@IsOptional()
|
||||
page?: string;
|
||||
|
||||
@IsNumberString()
|
||||
@IsOptional()
|
||||
pageSize?: string;
|
||||
|
||||
/** Full-text search on title + excerpt */
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
q?: string;
|
||||
|
||||
/** Comma-separated tags e.g. "nestjs,htmx" */
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
tags?: string;
|
||||
|
||||
/** Single category filter */
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
category?: string;
|
||||
|
||||
/** Sort order: newest | oldest | most_viewed | featured */
|
||||
@IsEnum(['newest', 'oldest', 'most_viewed', 'featured'])
|
||||
@IsOptional()
|
||||
sort?: string;
|
||||
|
||||
/** Admin/manager status filter */
|
||||
@IsEnum(PostStatus)
|
||||
@IsOptional()
|
||||
status?: PostStatus;
|
||||
}
|
||||
4
backend/src/blog-posts/dto/update-post.dto.ts
Normal file
4
backend/src/blog-posts/dto/update-post.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/mapped-types';
|
||||
import { CreatePostDto } from './create-post.dto';
|
||||
|
||||
export class UpdatePostDto extends PartialType(CreatePostDto) {}
|
||||
Reference in New Issue
Block a user