Добавлена возможность подключаться из вне
This commit is contained in:
@@ -7,8 +7,8 @@ NODE_ENV=development
|
||||
JWT_SECRET=change-this-in-production-to-random-string
|
||||
|
||||
# AI Providers
|
||||
AI_PROVIDER=iieasy
|
||||
# iieasy | lmstudio
|
||||
AI_PROVIDER=ollama
|
||||
# iieasy | lmstudio | ollama
|
||||
|
||||
# iieasy.ru API
|
||||
IIEASY_API_URL=https://ai.iieasy.ru/v1
|
||||
@@ -19,6 +19,10 @@ IIEASY_MODEL=google/gemma-3n-e4b
|
||||
LMSTUDIO_API_URL=http://localhost:1234/v1
|
||||
LMSTUDIO_MODEL=local-model
|
||||
|
||||
# Ollama
|
||||
OLLAMA_API_URL=http://192.168.88.160:11434
|
||||
OLLAMA_MODEL=gemma3n:e4b
|
||||
|
||||
# Default company settings
|
||||
DEFAULT_EXECUTOR=ООО "ГеоВектор"
|
||||
DEFAULT_VAT_RATE=20
|
||||
|
||||
@@ -7,7 +7,7 @@ NODE_ENV=development
|
||||
|
||||
# AI Providers
|
||||
AI_PROVIDER=iieasy
|
||||
# iieasy | lmstudio
|
||||
# iieasy | lmstudio | ollama
|
||||
|
||||
# iieasy.ru API
|
||||
IIEASY_API_URL=https://ai.iieasy.ru/v1
|
||||
@@ -18,6 +18,10 @@ IIEASY_MODEL=google/gemma-3n-e4b
|
||||
LMSTUDIO_API_URL=http://localhost:1234/v1
|
||||
LMSTUDIO_MODEL=local-model
|
||||
|
||||
# Ollama
|
||||
OLLAMA_API_URL=http://192.168.88.160:11434
|
||||
OLLAMA_MODEL=gemma3n:e4b
|
||||
|
||||
# Default company settings
|
||||
DEFAULT_EXECUTOR=ООО "ГеоВектор"
|
||||
DEFAULT_VAT_RATE=20
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Шрифты для PDF
|
||||
|
||||
Для генерации PDF с кириллицей используются шрифты PT Sans из npm-пакета `@fontsource/pt-sans` (файлы в `node_modules/@fontsource/pt-sans/files/`).
|
||||
|
||||
При необходимости можно положить сюда свои TTF/WOFF (например, PTSans-Regular.ttf и PTSans-Bold.ttf) — тогда в `pdf.service.ts` нужно указать путь к этой папке через `path.join(process.cwd(), 'fonts', '...')`.
|
||||
4
backend/node_modules/.package-lock.json
generated
vendored
4
backend/node_modules/.package-lock.json
generated
vendored
@@ -276,6 +276,7 @@
|
||||
"integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^4.17.33",
|
||||
@@ -344,6 +345,7 @@
|
||||
"integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
@@ -2633,6 +2635,7 @@
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "5.22.0"
|
||||
},
|
||||
@@ -3374,6 +3377,7 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
0
backend/node_modules/.prisma/client/default.d.ts
generated
vendored
Executable file → Normal file
0
backend/node_modules/.prisma/client/default.d.ts
generated
vendored
Executable file → Normal file
0
backend/node_modules/.prisma/client/default.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/.prisma/client/default.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/.prisma/client/edge.d.ts
generated
vendored
Executable file → Normal file
0
backend/node_modules/.prisma/client/edge.d.ts
generated
vendored
Executable file → Normal file
25
backend/node_modules/.prisma/client/edge.js
generated
vendored
Executable file → Normal file
25
backend/node_modules/.prisma/client/edge.js
generated
vendored
Executable file → Normal file
File diff suppressed because one or more lines are too long
12
backend/node_modules/.prisma/client/index-browser.js
generated
vendored
Executable file → Normal file
12
backend/node_modules/.prisma/client/index-browser.js
generated
vendored
Executable file → Normal file
@@ -219,10 +219,13 @@ exports.Prisma.EstimateScalarFieldEnum = {
|
||||
totalLaboratory: 'totalLaboratory',
|
||||
subtotal: 'subtotal',
|
||||
regionalCoef: 'regionalCoef',
|
||||
regionalCoefDocRef: 'regionalCoefDocRef',
|
||||
inflationIndex: 'inflationIndex',
|
||||
inflationDocRef: 'inflationDocRef',
|
||||
companyCoef: 'companyCoef',
|
||||
companyCoefDocRef: 'companyCoefDocRef',
|
||||
executorCoef: 'executorCoef',
|
||||
executorCoefDocRef: 'executorCoefDocRef',
|
||||
withVat: 'withVat',
|
||||
totalWithoutVat: 'totalWithoutVat',
|
||||
vatRate: 'vatRate',
|
||||
@@ -249,6 +252,14 @@ exports.Prisma.EstimateShareScalarFieldEnum = {
|
||||
createdAt: 'createdAt'
|
||||
};
|
||||
|
||||
exports.Prisma.EstimateShareNoteScalarFieldEnum = {
|
||||
id: 'id',
|
||||
shareId: 'shareId',
|
||||
authorId: 'authorId',
|
||||
content: 'content',
|
||||
createdAt: 'createdAt'
|
||||
};
|
||||
|
||||
exports.Prisma.EstimateItemScalarFieldEnum = {
|
||||
id: 'id',
|
||||
estimateId: 'estimateId',
|
||||
@@ -355,6 +366,7 @@ exports.Prisma.ModelName = {
|
||||
Estimate: 'Estimate',
|
||||
EstimateVersion: 'EstimateVersion',
|
||||
EstimateShare: 'EstimateShare',
|
||||
EstimateShareNote: 'EstimateShareNote',
|
||||
EstimateItem: 'EstimateItem',
|
||||
EstimateTotal: 'EstimateTotal',
|
||||
Setting: 'Setting',
|
||||
|
||||
1875
backend/node_modules/.prisma/client/index.d.ts
generated
vendored
Executable file → Normal file
1875
backend/node_modules/.prisma/client/index.d.ts
generated
vendored
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
29
backend/node_modules/.prisma/client/index.js
generated
vendored
Executable file → Normal file
29
backend/node_modules/.prisma/client/index.js
generated
vendored
Executable file → Normal file
File diff suppressed because one or more lines are too long
BIN
backend/node_modules/.prisma/client/libquery_engine-debian-openssl-3.0.x.so.node
generated
vendored
Executable file
BIN
backend/node_modules/.prisma/client/libquery_engine-debian-openssl-3.0.x.so.node
generated
vendored
Executable file
Binary file not shown.
2
backend/node_modules/.prisma/client/package.json
generated
vendored
Executable file → Normal file
2
backend/node_modules/.prisma/client/package.json
generated
vendored
Executable file → Normal file
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "prisma-client-4b58c981519ea022802510544edb111bbd4ef5e4d3b50b2269f96083d9bf4ae3",
|
||||
"name": "prisma-client-aa42ad392173f3ebbcce688c9d47fd7d65145cf816fa2f07c9d3255147711e22",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"browser": "index-browser.js",
|
||||
|
||||
61
backend/node_modules/.prisma/client/schema.prisma
generated
vendored
61
backend/node_modules/.prisma/client/schema.prisma
generated
vendored
@@ -9,7 +9,7 @@ datasource db {
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
// Справочники базовых цен
|
||||
// Справочники базовых цен (СБЦ)
|
||||
model PriceBook {
|
||||
id String @id @default(uuid())
|
||||
code String @unique // SBC-GEODESY-2004, SBC-GEOLOGY-1999
|
||||
@@ -114,8 +114,9 @@ model User {
|
||||
|
||||
estimates Estimate[]
|
||||
chatSessions ChatSession[]
|
||||
ownedShares EstimateShare[] @relation("ShareOwner")
|
||||
receivedShares EstimateShare[] @relation("ShareReceiver")
|
||||
ownedShares EstimateShare[] @relation("ShareOwner")
|
||||
receivedShares EstimateShare[] @relation("ShareReceiver")
|
||||
shareNotes EstimateShareNote[]
|
||||
}
|
||||
|
||||
// Направления изысканий
|
||||
@@ -146,13 +147,17 @@ model Estimate {
|
||||
subtotal Decimal? @db.Decimal(14, 2) // Итого по изысканиям
|
||||
|
||||
// Коэффициенты и пересчет
|
||||
regionalCoef Decimal? @db.Decimal(6, 4) // Районный коэффициент
|
||||
inflationIndex Decimal? @db.Decimal(10, 4) // Индекс перевода в текущие цены
|
||||
inflationDocRef String? // Ссылка на документ с индексом
|
||||
companyCoef Decimal? @db.Decimal(6, 4) // Коэффициент компании (Газпром и т.д.)
|
||||
executorCoef Decimal? @db.Decimal(6, 4) // Коэффициент исполнителя
|
||||
regionalCoef Decimal? @db.Decimal(6, 4) // Районный коэффициент
|
||||
regionalCoefDocRef String? // Описание (С районным коэффициентом и т.д.)
|
||||
inflationIndex Decimal? @db.Decimal(10, 4) // Индекс перевода в текущие цены
|
||||
inflationDocRef String? // Ссылка на документ (Письмо Минстроя и т.д.)
|
||||
companyCoef Decimal? @db.Decimal(6, 4) // Коэффициент компании (Газпром и т.д.)
|
||||
companyCoefDocRef String? // Описание (Коэффициент ОАО «Газпром» №544 и т.д.)
|
||||
executorCoef Decimal? @db.Decimal(6, 4) // Коэффициент исполнителя
|
||||
executorCoefDocRef String? // Описание (Коэффициент ООО «ГеоВектор» и т.д.)
|
||||
|
||||
// Итоги
|
||||
withVat Boolean @default(true) // Смета с НДС (true) или без НДС (false)
|
||||
totalWithoutVat Decimal? @db.Decimal(14, 2) // Итого без НДС
|
||||
vatRate Decimal? @db.Decimal(4, 2) // Ставка НДС (18, 20, 7)
|
||||
vatAmount Decimal? @db.Decimal(14, 2) // Сумма НДС
|
||||
@@ -162,11 +167,26 @@ model Estimate {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
direction SurveyDirection @relation(fields: [directionId], references: [id])
|
||||
owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
direction SurveyDirection @relation(fields: [directionId], references: [id])
|
||||
items EstimateItem[]
|
||||
totals EstimateTotal[]
|
||||
shares EstimateShare[]
|
||||
versions EstimateVersion[]
|
||||
}
|
||||
|
||||
// История версий сметы (снимок при сохранении/пересчёте)
|
||||
model EstimateVersion {
|
||||
id String @id @default(uuid())
|
||||
estimateId String
|
||||
versionNumber Int // Порядковый номер версии по смете
|
||||
snapshot Json // Полный снимок: estimate + items + totals
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
estimate Estimate @relation(fields: [estimateId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([estimateId])
|
||||
@@index([estimateId, createdAt])
|
||||
}
|
||||
|
||||
// Шаринг сметы с другим пользователем
|
||||
@@ -177,14 +197,29 @@ model EstimateShare {
|
||||
sharedWithId String // С кем поделились
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
estimate Estimate @relation(fields: [estimateId], references: [id], onDelete: Cascade)
|
||||
sharedWith User @relation("ShareReceiver", fields: [sharedWithId], references: [id], onDelete: Cascade)
|
||||
owner User @relation("ShareOwner", fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
estimate Estimate @relation(fields: [estimateId], references: [id], onDelete: Cascade)
|
||||
sharedWith User @relation("ShareReceiver", fields: [sharedWithId], references: [id], onDelete: Cascade)
|
||||
owner User @relation("ShareOwner", fields: [ownerId], references: [id], onDelete: Cascade)
|
||||
notes EstimateShareNote[]
|
||||
|
||||
@@unique([estimateId, sharedWithId])
|
||||
@@index([sharedWithId])
|
||||
}
|
||||
|
||||
// Заметки к шарингу сметы (кто что написал)
|
||||
model EstimateShareNote {
|
||||
id String @id @default(uuid())
|
||||
shareId String
|
||||
authorId String
|
||||
content String @db.Text
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
share EstimateShare @relation(fields: [shareId], references: [id], onDelete: Cascade)
|
||||
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([shareId])
|
||||
}
|
||||
|
||||
// Позиции сметы
|
||||
model EstimateItem {
|
||||
id String @id @default(uuid())
|
||||
|
||||
0
backend/node_modules/.prisma/client/wasm.d.ts
generated
vendored
Executable file → Normal file
0
backend/node_modules/.prisma/client/wasm.d.ts
generated
vendored
Executable file → Normal file
12
backend/node_modules/.prisma/client/wasm.js
generated
vendored
Executable file → Normal file
12
backend/node_modules/.prisma/client/wasm.js
generated
vendored
Executable file → Normal file
@@ -219,10 +219,13 @@ exports.Prisma.EstimateScalarFieldEnum = {
|
||||
totalLaboratory: 'totalLaboratory',
|
||||
subtotal: 'subtotal',
|
||||
regionalCoef: 'regionalCoef',
|
||||
regionalCoefDocRef: 'regionalCoefDocRef',
|
||||
inflationIndex: 'inflationIndex',
|
||||
inflationDocRef: 'inflationDocRef',
|
||||
companyCoef: 'companyCoef',
|
||||
companyCoefDocRef: 'companyCoefDocRef',
|
||||
executorCoef: 'executorCoef',
|
||||
executorCoefDocRef: 'executorCoefDocRef',
|
||||
withVat: 'withVat',
|
||||
totalWithoutVat: 'totalWithoutVat',
|
||||
vatRate: 'vatRate',
|
||||
@@ -249,6 +252,14 @@ exports.Prisma.EstimateShareScalarFieldEnum = {
|
||||
createdAt: 'createdAt'
|
||||
};
|
||||
|
||||
exports.Prisma.EstimateShareNoteScalarFieldEnum = {
|
||||
id: 'id',
|
||||
shareId: 'shareId',
|
||||
authorId: 'authorId',
|
||||
content: 'content',
|
||||
createdAt: 'createdAt'
|
||||
};
|
||||
|
||||
exports.Prisma.EstimateItemScalarFieldEnum = {
|
||||
id: 'id',
|
||||
estimateId: 'estimateId',
|
||||
@@ -355,6 +366,7 @@ exports.Prisma.ModelName = {
|
||||
Estimate: 'Estimate',
|
||||
EstimateVersion: 'EstimateVersion',
|
||||
EstimateShare: 'EstimateShare',
|
||||
EstimateShareNote: 'EstimateShareNote',
|
||||
EstimateItem: 'EstimateItem',
|
||||
EstimateTotal: 'EstimateTotal',
|
||||
Setting: 'Setting',
|
||||
|
||||
BIN
backend/node_modules/@prisma/engines/libquery_engine-debian-openssl-3.0.x.so.node
generated
vendored
Executable file
BIN
backend/node_modules/@prisma/engines/libquery_engine-debian-openssl-3.0.x.so.node
generated
vendored
Executable file
Binary file not shown.
BIN
backend/node_modules/@prisma/engines/schema-engine-debian-openssl-3.0.x
generated
vendored
Executable file
BIN
backend/node_modules/@prisma/engines/schema-engine-debian-openssl-3.0.x
generated
vendored
Executable file
Binary file not shown.
0
backend/node_modules/bcrypt/.editorconfig
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/.editorconfig
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/.github/workflows/ci.yaml
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/.github/workflows/ci.yaml
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/.travis.yml
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/.travis.yml
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/CHANGELOG.md
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/CHANGELOG.md
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/ISSUE_TEMPLATE.md
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/ISSUE_TEMPLATE.md
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/LICENSE
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/LICENSE
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/Makefile
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/Makefile
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/README.md
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/README.md
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/SECURITY.md
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/SECURITY.md
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/appveyor.yml
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/appveyor.yml
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/bcrypt.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/bcrypt.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/binding.gyp
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/binding.gyp
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/examples/async_compare.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/examples/async_compare.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/examples/forever_gen_salt.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/examples/forever_gen_salt.js
generated
vendored
Executable file → Normal file
BIN
backend/node_modules/bcrypt/lib/binding/napi-v3/bcrypt_lib.node
generated
vendored
BIN
backend/node_modules/bcrypt/lib/binding/napi-v3/bcrypt_lib.node
generated
vendored
Binary file not shown.
0
backend/node_modules/bcrypt/package.json
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/package.json
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/promises.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/promises.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/src/bcrypt.cc
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/src/bcrypt.cc
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/src/bcrypt_node.cc
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/src/bcrypt_node.cc
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/src/blowfish.cc
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/src/blowfish.cc
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/src/node_blf.h
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/src/node_blf.h
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/test/async.test.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/test/async.test.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/test/implementation.test.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/test/implementation.test.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/test/promise.test.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/test/promise.test.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/test/repetitions.test.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/test/repetitions.test.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/test/sync.test.js
generated
vendored
Executable file → Normal file
0
backend/node_modules/bcrypt/test/sync.test.js
generated
vendored
Executable file → Normal file
BIN
backend/node_modules/prisma/libquery_engine-debian-openssl-3.0.x.so.node
generated
vendored
Executable file
BIN
backend/node_modules/prisma/libquery_engine-debian-openssl-3.0.x.so.node
generated
vendored
Executable file
Binary file not shown.
4
backend/package-lock.json
generated
4
backend/package-lock.json
generated
@@ -309,6 +309,7 @@
|
||||
"integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^4.17.33",
|
||||
@@ -377,6 +378,7 @@
|
||||
"integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
@@ -2681,6 +2683,7 @@
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "5.22.0"
|
||||
},
|
||||
@@ -3422,6 +3425,7 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.1",
|
||||
"express": "^4.18.2",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pdfkit": "^0.14.0",
|
||||
"uuid": "^9.0.1",
|
||||
"xlsx": "^0.18.5",
|
||||
"jsonwebtoken": "^9.0.2"
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
|
||||
@@ -19,8 +19,8 @@ const app = express();
|
||||
const prisma = new PrismaClient();
|
||||
const PORT = process.env.PORT || 5000;
|
||||
|
||||
// Middleware — фронт на :3500, прокси Vite тоже
|
||||
app.use(cors({ origin: ['http://localhost:3500', 'http://127.0.0.1:3500'], credentials: true }));
|
||||
// Middleware — CORS: разрешаем запросы с любого origin (для внутренней сети)
|
||||
app.use(cors({ origin: true, credentials: true }));
|
||||
app.use(cookieParser());
|
||||
app.use(express.json({ limit: '50mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '50mb' }));
|
||||
|
||||
@@ -22,6 +22,8 @@ export class AIService {
|
||||
private iieasyModel: string;
|
||||
private lmstudioUrl: string;
|
||||
private lmstudioModel: string;
|
||||
private ollamaUrl: string;
|
||||
private ollamaModel: string;
|
||||
|
||||
constructor() {
|
||||
this.provider = process.env.AI_PROVIDER || 'iieasy';
|
||||
@@ -30,9 +32,11 @@ export class AIService {
|
||||
this.iieasyModel = process.env.IIEASY_MODEL || 'google/gemma-3n-e4b';
|
||||
this.lmstudioUrl = process.env.LMSTUDIO_API_URL || 'http://localhost:1234/v1';
|
||||
this.lmstudioModel = process.env.LMSTUDIO_MODEL || 'local-model';
|
||||
this.ollamaUrl = (process.env.OLLAMA_API_URL || 'http://localhost:11434').replace(/\/+$/, '');
|
||||
this.ollamaModel = process.env.OLLAMA_MODEL || 'gemma3n:e4b';
|
||||
}
|
||||
|
||||
setProvider(provider: 'iieasy' | 'lmstudio') {
|
||||
setProvider(provider: 'iieasy' | 'lmstudio' | 'ollama') {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@@ -45,11 +49,9 @@ export class AIService {
|
||||
|
||||
allMessages.push(...messages);
|
||||
|
||||
if (this.provider === 'lmstudio') {
|
||||
return this.chatLMStudio(allMessages);
|
||||
} else {
|
||||
return this.chatIIEasy(allMessages);
|
||||
}
|
||||
if (this.provider === 'ollama') return this.chatOllama(allMessages);
|
||||
if (this.provider === 'lmstudio') return this.chatLMStudio(allMessages);
|
||||
return this.chatIIEasy(allMessages);
|
||||
}
|
||||
|
||||
private async chatIIEasy(messages: ChatMessage[]): Promise<AIResponse> {
|
||||
@@ -117,6 +119,44 @@ export class AIService {
|
||||
}
|
||||
}
|
||||
|
||||
private async chatOllama(messages: ChatMessage[]): Promise<AIResponse> {
|
||||
// Ollama API docs: POST /api/chat { model, messages, stream:false, options:{} }
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 120_000);
|
||||
try {
|
||||
const response = await fetch(`${this.ollamaUrl}/api/chat`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
signal: controller.signal,
|
||||
body: JSON.stringify({
|
||||
model: this.ollamaModel,
|
||||
messages,
|
||||
stream: false,
|
||||
options: {
|
||||
temperature: 0.7,
|
||||
num_predict: 4096,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text();
|
||||
throw new Error(`Ollama API error: ${response.status} - ${error}`);
|
||||
}
|
||||
|
||||
const data: any = await response.json();
|
||||
return {
|
||||
content: data?.message?.content || '',
|
||||
};
|
||||
} catch (error: any) {
|
||||
const msg = error?.name === 'AbortError' ? 'timeout' : error?.message;
|
||||
console.error('Ollama API error:', error);
|
||||
throw new Error(`AI service error: ${msg}`);
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
async extractEstimateData(
|
||||
text: string,
|
||||
previousData?: Partial<{
|
||||
|
||||
@@ -88,6 +88,15 @@ function getScriptPath(): string {
|
||||
return candidates[0];
|
||||
}
|
||||
|
||||
/** Python для запуска скрипта: предпочитаем venv в каталоге pdf_generator (Ubuntu 24.04). */
|
||||
function getPythonCommand(): string {
|
||||
const scriptPath = getScriptPath();
|
||||
const pdfGenDir = path.dirname(scriptPath);
|
||||
const venvPython = path.join(pdfGenDir, 'venv', 'bin', 'python3');
|
||||
if (fs.existsSync(venvPython)) return venvPython;
|
||||
return process.platform === 'win32' ? 'python' : 'python3';
|
||||
}
|
||||
|
||||
/**
|
||||
* Сгенерировать PDF сметы через Python (ReportLab, кириллица).
|
||||
* При ошибке возвращает null — тогда используется Node (PDFKit).
|
||||
@@ -113,7 +122,7 @@ export async function generateEstimatePdfWithPython(estimate: any): Promise<Buff
|
||||
return null;
|
||||
}
|
||||
|
||||
const pythonCmd = process.platform === 'win32' ? 'python' : 'python3';
|
||||
const pythonCmd = getPythonCommand();
|
||||
const args = [scriptPath, tempFile];
|
||||
|
||||
return new Promise((resolve) => {
|
||||
|
||||
Reference in New Issue
Block a user