From f87e5b9d8ef3ef4ab825ebc84fd361764b7234d3 Mon Sep 17 00:00:00 2001 From: ahasani194 Date: Tue, 16 Jun 2026 15:47:29 +0330 Subject: [PATCH] feat: refactor Prisma client initialization and update related services --- prisma/seed.ts | 5 +- scripts/generate-permissions.ts | 111 ++++---- scripts/seedStockKeepingUnits.ts | 243 +++++++++--------- src/lib/prisma.ts | 6 +- src/main.ts | 2 + .../dto/create-stock-keeping-unit.dto.ts | 9 +- src/modules/enums/enums.service.ts | 4 +- src/prisma/prisma.service.ts | 12 +- 8 files changed, 192 insertions(+), 200 deletions(-) diff --git a/prisma/seed.ts b/prisma/seed.ts index 0ba6795..3948df6 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,7 +1,10 @@ import { PasswordUtil } from '@/common/utils/password.util' import { GoodPricingModel } from '@/generated/prisma/enums' import { GoodCreateManyInput } from '@/generated/prisma/models' -import { prisma } from '../src/lib/prisma' +import { PrismaClient } from '../src/generated/prisma/client' +import { prismaAdapter } from '../src/lib/prisma' + +const prisma = new PrismaClient({ adapter: prismaAdapter }) async function main() { const password = await PasswordUtil.hash('123456') diff --git a/scripts/generate-permissions.ts b/scripts/generate-permissions.ts index 76b6b06..ef72134 100644 --- a/scripts/generate-permissions.ts +++ b/scripts/generate-permissions.ts @@ -1,66 +1,65 @@ -#!/usr/bin/env ts-node -import * as fs from 'fs' -import * as path from 'path' -import { prisma } from '../src/lib/prisma' +// #!/usr/bin/env ts-node +// import * as fs from 'fs' +// import * as path from 'path' -function findModules(dir: string): string[] { - const results: string[] = [] - const entries = fs.readdirSync(dir, { withFileTypes: true }) - for (const e of entries) { - const full = path.join(dir, e.name) - if (e.isDirectory()) { - results.push(...findModules(full)) - } else if (e.isFile() && e.name.endsWith('.module.ts')) { - results.push(full) - } - } - return results -} +// function findModules(dir: string): string[] { +// const results: string[] = [] +// const entries = fs.readdirSync(dir, { withFileTypes: true }) +// for (const e of entries) { +// const full = path.join(dir, e.name) +// if (e.isDirectory()) { +// results.push(...findModules(full)) +// } else if (e.isFile() && e.name.endsWith('.module.ts')) { +// results.push(full) +// } +// } +// return results +// } -function moduleNameFromFile(filePath: string) { - const name = path.basename(filePath).replace('.module.ts', '') - return name.replace(/-module$|\.module$/i, '') -} +// function moduleNameFromFile(filePath: string) { +// const name = path.basename(filePath).replace('.module.ts', '') +// return name.replace(/-module$|\.module$/i, '') +// } -function makePermissionsFor(moduleName: string) { - const base = moduleName.replace(/\W+/g, '_').toLowerCase() - return [`${base}:create`, `${base}:read`, `${base}:update`, `${base}:delete`] -} +// function makePermissionsFor(moduleName: string) { +// const base = moduleName.replace(/\W+/g, '_').toLowerCase() +// return [`${base}:create`, `${base}:read`, `${base}:update`, `${base}:delete`] +// } -async function main() { - // const srcDir = path.resolve(__dirname, '..', 'src') - // const moduleFiles = findModules(srcDir) - // const modules = moduleFiles.map(moduleNameFromFile) +// async function main() { +// // const srcDir = path.resolve(__dirname, '..', 'src') +// // const moduleFiles = findModules(srcDir) +// // const modules = moduleFiles.map(moduleNameFromFile) - // const permsMap: Record = {} - // for (const m of modules) { - // for (const p of makePermissionsFor(m)) permsMap[p] = false - // } +// // const permsMap: Record = {} +// // for (const m of modules) { +// // for (const p of makePermissionsFor(m)) permsMap[p] = false +// // } - // // Upsert roles: ensure admin has full permissions, others get entries added - // const roles = await prisma.role.findMany() - // for (const r of roles) { - // const current: Record = (r.permissions as any) || {} - // const merged = { ...permsMap, ...current } +// // // Upsert roles: ensure admin has full permissions, others get entries added +// // const roles = await prisma.role.findMany() +// // for (const r of roles) { +// // const current: Record = (r.permissions as any) || {} +// // const merged = { ...permsMap, ...current } - // // If role name is admin (case-insensitive), set all permissions to true - // if (r.name && r.name.toLowerCase() === 'admin') { - // for (const key of Object.keys(merged)) merged[key] = true - // } else { - // // keep existing truthy values, otherwise false - // for (const key of Object.keys(merged)) merged[key] = merged[key] || false - // } +// // // If role name is admin (case-insensitive), set all permissions to true +// // if (r.name && r.name.toLowerCase() === 'admin') { +// // for (const key of Object.keys(merged)) merged[key] = true +// // } else { +// // // keep existing truthy values, otherwise false +// // for (const key of Object.keys(merged)) merged[key] = merged[key] || false +// // } - // await prisma.role.update({ where: { id: r.id }, data: { permissions: merged } }) - // console.log( - // `Updated role ${r.name} (id=${r.id}) with ${Object.keys(merged).length} permissions`, - // ) - // } +// // await prisma.role.update({ where: { id: r.id }, data: { permissions: merged } }) +// // console.log( +// // `Updated role ${r.name} (id=${r.id}) with ${Object.keys(merged).length} permissions`, +// // ) +// // } - await prisma.$disconnect() -} +// await prisma.$disconnect() +// } -main().catch(e => { - console.error(e) - process.exit(1) -}) +// main().catch(e => { +// console.error(e) +// process.exit(1) +// }) diff --git a/scripts/seedStockKeepingUnits.ts b/scripts/seedStockKeepingUnits.ts index 62c49d2..e242dc4 100644 --- a/scripts/seedStockKeepingUnits.ts +++ b/scripts/seedStockKeepingUnits.ts @@ -1,144 +1,143 @@ -import fs from 'node:fs' -import path from 'node:path' -import { prisma } from '../src/lib/prisma' +// import fs from 'node:fs' +// import path from 'node:path' -type CsvRow = { - ID?: string - DescriptionOfID?: string - Vat?: string - Type?: string -} +// type CsvRow = { +// ID?: string +// DescriptionOfID?: string +// Vat?: string +// Type?: string +// } -function parseCsvLine(line: string): string[] { - const result: string[] = [] - let current = '' - let inQuotes = false +// function parseCsvLine(line: string): string[] { +// const result: string[] = [] +// let current = '' +// let inQuotes = false - for (let index = 0; index < line.length; index++) { - const char = line[index] +// for (let index = 0; index < line.length; index++) { +// const char = line[index] - if (char === '"') { - if (inQuotes && line[index + 1] === '"') { - current += '"' - index++ - } else { - inQuotes = !inQuotes - } - continue - } +// if (char === '"') { +// if (inQuotes && line[index + 1] === '"') { +// current += '"' +// index++ +// } else { +// inQuotes = !inQuotes +// } +// continue +// } - if (char === ',' && !inQuotes) { - result.push(current) - current = '' - continue - } +// if (char === ',' && !inQuotes) { +// result.push(current) +// current = '' +// continue +// } - current += char - } +// current += char +// } - result.push(current) - return result.map(value => value.trim()) -} +// result.push(current) +// return result.map(value => value.trim()) +// } -function parseCsv(content: string): CsvRow[] { - const lines = content - .split(/\r?\n/) - .map(line => line.trim()) - .filter(Boolean) +// function parseCsv(content: string): CsvRow[] { +// const lines = content +// .split(/\r?\n/) +// .map(line => line.trim()) +// .filter(Boolean) - if (!lines.length) return [] +// if (!lines.length) return [] - const headers = parseCsvLine(lines[0]) - return lines.slice(1).map(line => { - const cols = parseCsvLine(line) - const row: Record = {} - for (let index = 0; index < headers.length; index++) { - row[headers[index]] = cols[index] || '' - } - return row - }) -} +// const headers = parseCsvLine(lines[0]) +// return lines.slice(1).map(line => { +// const cols = parseCsvLine(line) +// const row: Record = {} +// for (let index = 0; index < headers.length; index++) { +// row[headers[index]] = cols[index] || '' +// } +// return row +// }) +// } -function toBooleanFlags(typeValue: string) { - const normalized = typeValue || '' - const isPublic = normalized.includes('شناسه عمومی') - const isImported = normalized.includes('وارداتی') - const isDomestic = !isImported +// function toBooleanFlags(typeValue: string) { +// const normalized = typeValue || '' +// const isPublic = normalized.includes('شناسه عمومی') +// const isImported = normalized.includes('وارداتی') +// const isDomestic = !isImported - return { isPublic, isDomestic } -} +// return { isPublic, isDomestic } +// } -function parseVat(vatValue: string) { - const parsed = Number(vatValue || '0') - if (!Number.isFinite(parsed)) return 0 - return parsed -} +// function parseVat(vatValue: string) { +// const parsed = Number(vatValue || '0') +// if (!Number.isFinite(parsed)) return 0 +// return parsed +// } -async function main() { - const defaultPath = - '/Users/ahasani/Desktop/product_good_2026-05-01T11-35-57_part_1_28812448-0dd9-411c-99e1-334c28d781fe.csv' - const csvPath = process.argv[2] || defaultPath - const absolutePath = path.resolve(csvPath) +// async function main() { +// const defaultPath = +// '/Users/ahasani/Desktop/product_good_2026-05-01T11-35-57_part_1_28812448-0dd9-411c-99e1-334c28d781fe.csv' +// const csvPath = process.argv[2] || defaultPath +// const absolutePath = path.resolve(csvPath) - if (!fs.existsSync(absolutePath)) { - throw new Error(`CSV file not found: ${absolutePath}`) - } +// if (!fs.existsSync(absolutePath)) { +// throw new Error(`CSV file not found: ${absolutePath}`) +// } - const raw = fs.readFileSync(absolutePath, 'utf8').replace(/^\uFEFF/, '') - const rows = parseCsv(raw) +// const raw = fs.readFileSync(absolutePath, 'utf8').replace(/^\uFEFF/, '') +// const rows = parseCsv(raw) - let upserted = 0 - let skipped = 0 +// let upserted = 0 +// let skipped = 0 - const guild = await prisma.guild.findFirst({}) +// const guild = await prisma.guild.findFirst({}) - for (const row of rows) { - const code = (row.ID || '').trim() - const name = (row.DescriptionOfID || '').trim() - const vat = parseVat((row.Vat || '').trim()) - const type = (row.Type || '').trim() - const { isPublic, isDomestic } = toBooleanFlags(type) +// for (const row of rows) { +// const code = (row.ID || '').trim() +// const name = (row.DescriptionOfID || '').trim() +// const vat = parseVat((row.Vat || '').trim()) +// const type = (row.Type || '').trim() +// const { isPublic, isDomestic } = toBooleanFlags(type) - if (!code || !name) { - skipped++ - continue - } +// if (!code || !name) { +// skipped++ +// continue +// } - await prisma.stockKeepingUnits.upsert({ - where: { code }, - create: { - code, - name, - VAT: vat, - guild: { - connect: { - id: guild?.id, - }, - }, - is_public: isPublic, - is_domestic: isDomestic, - }, - update: { - name, - VAT: vat, - guild: { - connect: { - id: guild?.id, - }, - }, - is_public: isPublic, - is_domestic: isDomestic, - }, - }) - upserted++ - } -} +// await prisma.stockKeepingUnits.upsert({ +// where: { code }, +// create: { +// code, +// name, +// VAT: vat, +// guild: { +// connect: { +// id: guild?.id, +// }, +// }, +// is_public: isPublic, +// is_domestic: isDomestic, +// }, +// update: { +// name, +// VAT: vat, +// guild: { +// connect: { +// id: guild?.id, +// }, +// }, +// is_public: isPublic, +// is_domestic: isDomestic, +// }, +// }) +// upserted++ +// } +// } -main() - .catch(error => { - console.error(error) - process.exit(1) - }) - .finally(async () => { - await prisma.$disconnect() - }) +// main() +// .catch(error => { +// console.error(error) +// process.exit(1) +// }) +// .finally(async () => { +// await prisma.$disconnect() +// }) diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 15a33bf..62dce8f 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -2,8 +2,6 @@ import { PrismaMariaDb } from '@prisma/adapter-mariadb' import 'dotenv/config' import { env } from 'prisma/config' -import { PrismaClient } from '../generated/prisma/client' - const adapter = new PrismaMariaDb({ host: env('DATABASE_HOST'), user: env('DATABASE_USER'), @@ -18,6 +16,6 @@ const adapter = new PrismaMariaDb({ connectTimeout: 10000, }) -const prisma = new PrismaClient({ adapter }) +// const prisma = new PrismaClient({ adapter }) -export { prisma } +export { adapter as prismaAdapter } diff --git a/src/main.ts b/src/main.ts index 0b312fc..0a9818b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -17,6 +17,8 @@ const cookieParser = require('cookie-parser') async function bootstrap() { const app = await NestFactory.create(AppModule) + app.enableShutdownHooks() + app.use(cookieParser()) const config = new DocumentBuilder() diff --git a/src/modules/admin/guilds/stock-keeping-units/dto/create-stock-keeping-unit.dto.ts b/src/modules/admin/guilds/stock-keeping-units/dto/create-stock-keeping-unit.dto.ts index 7bb27dc..47b50e2 100644 --- a/src/modules/admin/guilds/stock-keeping-units/dto/create-stock-keeping-unit.dto.ts +++ b/src/modules/admin/guilds/stock-keeping-units/dto/create-stock-keeping-unit.dto.ts @@ -1,7 +1,6 @@ -import { SKUGuildType } from '@/common/enums/enums' import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' import { Type } from 'class-transformer' -import { IsBoolean, IsEnum, IsNumber, IsString, Min } from 'class-validator' +import { IsBoolean, IsNumber, IsString, Min } from 'class-validator' export class CreateStockKeepingUnitDto { @ApiProperty() @@ -18,9 +17,9 @@ export class CreateStockKeepingUnitDto { @Min(0) VAT: number - @ApiPropertyOptional({ enum: SKUGuildType, default: SKUGuildType.GOLD }) - @IsEnum(SKUGuildType) - type: SKUGuildType = SKUGuildType.GOLD + // @ApiPropertyOptional({ enum: SKUGuildType, default: SKUGuildType.GOLD }) + // @IsEnum(SKUGuildType) + // type: SKUGuildType = SKUGuildType.GOLD @ApiPropertyOptional({ default: true }) @IsBoolean() diff --git a/src/modules/enums/enums.service.ts b/src/modules/enums/enums.service.ts index 38fad40..17aa612 100644 --- a/src/modules/enums/enums.service.ts +++ b/src/modules/enums/enums.service.ts @@ -25,7 +25,7 @@ import { TspProviderType, } from '@/generated/prisma/enums' import { Injectable } from '@nestjs/common' -import { GoldKarat, SKUGuildType, TspProviderCustomerType } from 'common/enums/enums' +import { GoldKarat, TspProviderCustomerType } from 'common/enums/enums' import { ResponseMapper } from 'common/response/response-mapper' @Injectable() @@ -80,7 +80,7 @@ export class EnumsService { TspProviderCustomerType, 'TspProviderCustomerType', ), - SKUGuildType: this.prepareData(SKUGuildType, 'SKUGuildType'), + // SKUGuildType: this.prepareData(SKUGuildType, 'SKUGuildType'), InvoiceTemplateType: this.prepareData(InvoiceTemplateType, 'InvoiceTemplateType'), } } diff --git a/src/prisma/prisma.service.ts b/src/prisma/prisma.service.ts index d7a7902..459d5e7 100644 --- a/src/prisma/prisma.service.ts +++ b/src/prisma/prisma.service.ts @@ -1,21 +1,13 @@ +import { prismaAdapter } from '@/lib/prisma' import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common' -import { PrismaMariaDb } from '@prisma/adapter-mariadb' import 'dotenv/config' -import { env } from 'prisma/config' import { PrismaClient } from '../generated/prisma/client' import { getPrismaOptions } from './prisma-config.service' @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { constructor() { - const adapter = new PrismaMariaDb({ - host: env('DATABASE_HOST'), - user: env('DATABASE_USER'), - password: env('DATABASE_PASSWORD'), - database: env('DATABASE_NAME'), - port: env('DATABASE_PORT') ? Number(env('DATABASE_PORT')) : 3306, - connectionLimit: 10, - }) + const adapter = prismaAdapter const prismaOptions = getPrismaOptions() super({ ...prismaOptions, adapter })