import { Type } from 'class-transformer'; import { IsIn, IsInt, IsNotEmpty, IsObject, IsOptional, IsString, ValidateNested } from 'class-validator'; const EXTERNAL_CHANNELS = ['EMAIL', 'SMS'] as const; const ALL_CHANNELS = ['EMAIL', 'SMS', 'INTERNAL'] as const; /** Inline template content — an ad-hoc source (e.g. finished HTML from marketing). */ export class InlineTemplateDto { @IsOptional() @IsString() subject?: string; @IsOptional() @IsString() html?: string; @IsOptional() @IsString() text?: string; @IsOptional() @IsString({ each: true }) variables?: string[]; } /** Body of POST /v1/templates/send. Provide EITHER `key` (stored) OR `inline` (ad-hoc). */ export class SendTemplateDto { @IsOptional() @IsString() @IsNotEmpty() key?: string; @IsOptional() @IsInt() version?: number; @IsOptional() @ValidateNested() @Type(() => InlineTemplateDto) inline?: InlineTemplateDto; @IsIn(EXTERNAL_CHANNELS) channel!: (typeof EXTERNAL_CHANNELS)[number]; @IsString() @IsNotEmpty() target!: string; @IsOptional() @IsObject() vars?: Record; @IsOptional() @IsString() locale?: string; @IsString() @IsNotEmpty() idempotencyKey!: string; @IsOptional() @IsString() purpose?: string; } /** Body of POST /v1/templates/preview — render only, no send. INTERNAL allowed (preview only). */ export class PreviewTemplateDto { @IsOptional() @IsString() @IsNotEmpty() key?: string; @IsOptional() @IsInt() version?: number; @IsOptional() @ValidateNested() @Type(() => InlineTemplateDto) inline?: InlineTemplateDto; @IsIn(ALL_CHANNELS) channel!: (typeof ALL_CHANNELS)[number]; @IsOptional() @IsObject() vars?: Record; @IsOptional() @IsString() locale?: string; }