Add NestJS fabric trade API with Prisma and Docker

Provide backend for admin and user apps with Postgres, migrations, seed data, and docker-compose for LiteSpeed reverse-proxy deployment.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-05 20:26:39 +03:30
co-authored by Cursor
commit 9f0422c1db
86 changed files with 18369 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
import { IsBoolean, IsInt, IsOptional, IsString, Min } from 'class-validator';
import { Type } from 'class-transformer';
export class UpdateSettingsDto {
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
maxMeteragePerInvoice?: number;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
cashInvoiceValidityHours?: number;
@IsOptional()
@IsBoolean()
defaultSettlementCheck?: boolean;
@IsOptional()
@IsBoolean()
defaultSettlementCash?: boolean;
@IsOptional()
@IsBoolean()
smsOrderRegistered?: boolean;
@IsOptional()
@IsBoolean()
smsPaymentReminder?: boolean;
@IsOptional()
@IsBoolean()
smsCheckReview?: boolean;
@IsOptional()
@IsBoolean()
smsShippingUpdate?: boolean;
@IsOptional()
@IsString()
supportPhone?: string | null;
@IsOptional()
@IsString()
supportEmail?: string | null;
}
+20
View File
@@ -0,0 +1,20 @@
import { Body, Controller, Get, Patch } from '@nestjs/common';
import { SettingsService } from './settings.service';
import { AuthType } from '../common/decorators/auth.decorators';
import { UpdateSettingsDto } from './dto/settings.dto';
@Controller('admin/settings')
@AuthType('admin')
export class SettingsController {
constructor(private readonly settingsService: SettingsService) {}
@Get()
get() {
return this.settingsService.get();
}
@Patch()
update(@Body() dto: UpdateSettingsDto) {
return this.settingsService.update(dto);
}
}
+10
View File
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { SettingsController } from './settings.controller';
import { SettingsService } from './settings.service';
@Module({
controllers: [SettingsController],
providers: [SettingsService],
exports: [SettingsService],
})
export class SettingsModule {}
+77
View File
@@ -0,0 +1,77 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { UpdateSettingsDto } from './dto/settings.dto';
@Injectable()
export class SettingsService {
constructor(private readonly prisma: PrismaService) {}
async get() {
const settings = await this.ensureDefaults();
return this.map(settings);
}
async update(dto: UpdateSettingsDto) {
await this.ensureDefaults();
const settings = await this.prisma.systemSettings.update({
where: { id: 'default' },
data: {
maxMeteragePerInvoice: dto.maxMeteragePerInvoice,
cashInvoiceValidityHours: dto.cashInvoiceValidityHours,
defaultSettlementCheck: dto.defaultSettlementCheck,
defaultSettlementCash: dto.defaultSettlementCash,
smsOrderRegistered: dto.smsOrderRegistered,
smsPaymentReminder: dto.smsPaymentReminder,
smsCheckReview: dto.smsCheckReview,
smsShippingUpdate: dto.smsShippingUpdate,
supportPhone:
dto.supportPhone === undefined
? undefined
: dto.supportPhone?.trim() || null,
supportEmail:
dto.supportEmail === undefined
? undefined
: dto.supportEmail?.trim() || null,
},
});
return this.map(settings);
}
private async ensureDefaults() {
return this.prisma.systemSettings.upsert({
where: { id: 'default' },
create: { id: 'default' },
update: {},
});
}
private map(settings: {
id: string;
maxMeteragePerInvoice: number;
cashInvoiceValidityHours: number;
defaultSettlementCheck: boolean;
defaultSettlementCash: boolean;
smsOrderRegistered: boolean;
smsPaymentReminder: boolean;
smsCheckReview: boolean;
smsShippingUpdate: boolean;
supportPhone: string | null;
supportEmail: string | null;
updatedAt: Date;
}) {
return {
id: settings.id,
maxMeteragePerInvoice: settings.maxMeteragePerInvoice,
cashInvoiceValidityHours: settings.cashInvoiceValidityHours,
defaultSettlementCheck: settings.defaultSettlementCheck,
defaultSettlementCash: settings.defaultSettlementCash,
smsOrderRegistered: settings.smsOrderRegistered,
smsPaymentReminder: settings.smsPaymentReminder,
smsCheckReview: settings.smsCheckReview,
smsShippingUpdate: settings.smsShippingUpdate,
supportPhone: settings.supportPhone,
supportEmail: settings.supportEmail,
updatedAt: settings.updatedAt.toISOString(),
};
}
}