.
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "Catalog" ADD COLUMN IF NOT EXISTS "imageUrl" TEXT;
|
||||||
@@ -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;
|
||||||
@@ -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
File diff suppressed because one or more lines are too long
+100
-5
File diff suppressed because one or more lines are too long
@@ -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 })
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
@@ -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> {
|
||||||
|
|||||||
Reference in New Issue
Block a user