This commit is contained in:
2026-09-13 15:35:58 +03:30
parent 6439a91218
commit 602da7f860
13 changed files with 186 additions and 18 deletions
@@ -0,0 +1 @@
ALTER TABLE "Catalog" ADD COLUMN IF NOT EXISTS "imageUrl" TEXT;
+1
View File
@@ -175,6 +175,7 @@ model Catalog {
name String name String
code String code String
description String? description String?
imageUrl String?
displayOrder Int @default(0) displayOrder Int @default(0)
isActive Boolean @default(true) isActive Boolean @default(true)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
@@ -0,0 +1 @@
ALTER TABLE "Catalog" ADD COLUMN IF NOT EXISTS "imageUrl" TEXT;
+1
View File
@@ -175,6 +175,7 @@ model Catalog {
name String name String
code String code String
description String? description String?
imageUrl String?
displayOrder Int @default(0) displayOrder Int @default(0)
isActive Boolean @default(true) isActive Boolean @default(true)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
+4 -3
View File
File diff suppressed because one or more lines are too long
+100 -5
View File
File diff suppressed because one or more lines are too long
+8 -8
View File
@@ -22,14 +22,6 @@ function mb(file) {
return `${(statSync(file).size / (1024 * 1024)).toFixed(1)}MB` return `${(statSync(file).size / (1024 * 1024)).toFixed(1)}MB`
} }
console.log('==> nest build')
rmSync(path.join(root, 'dist'), { recursive: true, force: true })
rmSync(path.join(root, 'tsconfig.build.tsbuildinfo'), { force: true })
execSync('npx nest build', { stdio: 'inherit' })
if (!existsSync(path.join(root, 'dist', 'main.js'))) {
throw new Error('dist/main.js ساخته نشد')
}
console.log('==> prisma generate (debian engine)') console.log('==> prisma generate (debian engine)')
execSync('npx prisma generate', { execSync('npx prisma generate', {
stdio: 'inherit', stdio: 'inherit',
@@ -44,6 +36,14 @@ if (!existsSync(engineSrc)) {
throw new Error(`${engineName} ساخته نشد. binaryTargets را در schema.prisma چک کنید.`) throw new Error(`${engineName} ساخته نشد. binaryTargets را در schema.prisma چک کنید.`)
} }
console.log('==> nest build')
rmSync(path.join(root, 'dist'), { recursive: true, force: true })
rmSync(path.join(root, 'tsconfig.build.tsbuildinfo'), { force: true })
execSync('npx nest build', { stdio: 'inherit' })
if (!existsSync(path.join(root, 'dist', 'main.js'))) {
throw new Error('dist/main.js ساخته نشد')
}
rmSync(runtimeDir, { recursive: true, force: true }) rmSync(runtimeDir, { recursive: true, force: true })
mkdirSync(runtimeDir, { recursive: true }) mkdirSync(runtimeDir, { recursive: true })
+24
View File
@@ -1,4 +1,5 @@
import { import {
BadRequestException,
ConflictException, ConflictException,
Injectable, Injectable,
NotFoundException, NotFoundException,
@@ -51,6 +52,7 @@ export class CatalogsService {
name: dto.name.trim(), name: dto.name.trim(),
code: dto.code.trim(), code: dto.code.trim(),
description: dto.description?.trim() || null, description: dto.description?.trim() || null,
imageUrl: this.normalizeImage(dto.imageUrl),
displayOrder: dto.displayOrder ?? 0, displayOrder: dto.displayOrder ?? 0,
isActive: dto.isActive ?? true, isActive: dto.isActive ?? true,
}, },
@@ -83,6 +85,10 @@ export class CatalogsService {
dto.description === undefined dto.description === undefined
? undefined ? undefined
: dto.description?.trim() || null, : dto.description?.trim() || null,
imageUrl:
dto.imageUrl === undefined
? undefined
: this.normalizeImage(dto.imageUrl),
displayOrder: dto.displayOrder, displayOrder: dto.displayOrder,
isActive: dto.isActive, isActive: dto.isActive,
}, },
@@ -122,12 +128,29 @@ export class CatalogsService {
return catalog; return catalog;
} }
private normalizeImage(value?: string | null) {
if (value == null || value === '') return null;
const image = value.trim();
if (
!image.startsWith('data:image/') &&
!image.startsWith('https://') &&
!image.startsWith('http://')
) {
throw new BadRequestException('فرمت تصویر کالیته نامعتبر است');
}
if (image.startsWith('data:image/') && image.length > 1_600_000) {
throw new BadRequestException('حجم تصویر کالیته خیلی زیاد است');
}
return image;
}
private map(catalog: { private map(catalog: {
id: string; id: string;
companyId: string; companyId: string;
name: string; name: string;
code: string; code: string;
description: string | null; description: string | null;
imageUrl: string | null;
displayOrder: number; displayOrder: number;
isActive: boolean; isActive: boolean;
createdAt: Date; createdAt: Date;
@@ -142,6 +165,7 @@ export class CatalogsService {
name: catalog.name, name: catalog.name,
code: catalog.code, code: catalog.code,
description: catalog.description ?? undefined, description: catalog.description ?? undefined,
imageUrl: catalog.imageUrl ?? undefined,
displayOrder: catalog.displayOrder, displayOrder: catalog.displayOrder,
isActive: catalog.isActive, isActive: catalog.isActive,
productCount: catalog._count.products, productCount: catalog._count.products,
+8
View File
@@ -31,6 +31,10 @@ export class CreateCatalogDto {
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
isActive?: boolean; isActive?: boolean;
@IsOptional()
@IsString()
imageUrl?: string | null;
} }
export class UpdateCatalogDto { export class UpdateCatalogDto {
@@ -56,6 +60,10 @@ export class UpdateCatalogDto {
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
isActive?: boolean; isActive?: boolean;
@IsOptional()
@IsString()
imageUrl?: string | null;
} }
export class SetCatalogActiveDto { export class SetCatalogActiveDto {
@@ -0,0 +1,14 @@
import { Controller, Get } from '@nestjs/common';
import { Public } from '../common/decorators/auth.decorators';
import { SettingsService } from './settings.service';
@Controller('settings')
export class PublicSettingsController {
constructor(private readonly settingsService: SettingsService) {}
@Public()
@Get('public')
getPublic() {
return this.settingsService.getPublicContact();
}
}
+2 -1
View File
@@ -1,9 +1,10 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { SettingsController } from './settings.controller'; import { SettingsController } from './settings.controller';
import { PublicSettingsController } from './public-settings.controller';
import { SettingsService } from './settings.service'; import { SettingsService } from './settings.service';
@Module({ @Module({
controllers: [SettingsController], controllers: [SettingsController, PublicSettingsController],
providers: [SettingsService], providers: [SettingsService],
exports: [SettingsService], exports: [SettingsService],
}) })
+8
View File
@@ -12,6 +12,14 @@ export class SettingsService {
return this.map(settings); return this.map(settings);
} }
async getPublicContact() {
const settings = await this.ensureDefaults();
return {
supportPhone: settings.supportPhone,
supportEmail: settings.supportEmail,
};
}
async update(dto: UpdateSettingsDto) { async update(dto: UpdateSettingsDto) {
await this.ensureDefaults(); await this.ensureDefaults();
const settings = await this.prisma.systemSettings.update({ const settings = await this.prisma.systemSettings.update({
+14 -1
View File
@@ -22,7 +22,20 @@ export class SmsService {
const raw = ( const raw = (
this.config.get<string>('SMS_ENABLED', 'true') ?? 'true' this.config.get<string>('SMS_ENABLED', 'true') ?? 'true'
).toLowerCase(); ).toLowerCase();
return raw === '1' || raw === 'true' || raw === 'yes'; const flagOn = raw === '1' || raw === 'true' || raw === 'yes';
if (!flagOn) return false;
return this.hasUsableSecret();
}
private hasUsableSecret(): boolean {
const secretKey = this.config.get<string>('SMS_SECRET_KEY')?.trim() ?? '';
if (!secretKey) return false;
const placeholders = new Set([
'your-sms-secret-key',
'کلید-پیامک',
'change-me',
]);
return !placeholders.has(secretKey);
} }
async send(to: string, message: string): Promise<SmsSendResult> { async send(to: string, message: string): Promise<SmsSendResult> {