feat: Complete PL24 architecture overhaul with email & auth improvements
## PL24 Integration Rewrite - Add dual architecture support: P5 Modern (JSON API) + Legacy (HTML scraping) - New services: factory, session, fetch, html-parser, legacy-scraper - Add NHTSA WMI data for accurate brand detection - Brand-specific parsers for VW, Mercedes, BMW, Audi, Porsche, Ford, etc. - Add 254 schema images for parts diagrams ## Authentication & Security - Add PasswordResetToken database model with proper expiration - Implement dual persistence (Redis + DB) for password reset tokens - Add email enumeration protection ## Email Module - New EmailService for password reset emails - Configurable SMTP settings ## Database Schema - Add Vehicle fields: equipment, interiorCode, productionDate, salesType - Add Part.hotspotId for schema hotspot linking - Add SchemaPic.hotspots JSON field - Add VehicleCategory.subGroupsJson for subgroup data ## Frontend - Improve auth pages (login, register, forgot-password) - Enhance vehicle detail and categories pages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@@ -44,6 +44,11 @@ pnpm test # Tests
|
|||||||
- Parçaları on-demand çek (kullanıcı kategoriye tıklayınca)
|
- Parçaları on-demand çek (kullanıcı kategoriye tıklayınca)
|
||||||
- Credentials: `.env` dosyasında
|
- Credentials: `.env` dosyasında
|
||||||
|
|
||||||
|
## Test Kullanıcı
|
||||||
|
|
||||||
|
- **Email**: admin@sase.tr
|
||||||
|
- **Password**: SaseAdmin2024
|
||||||
|
|
||||||
## Kod Stili
|
## Kod Stili
|
||||||
|
|
||||||
- TypeScript strict mode
|
- TypeScript strict mode
|
||||||
|
|||||||
@@ -21,7 +21,12 @@
|
|||||||
"db:migrate:dev": "prisma migrate dev",
|
"db:migrate:dev": "prisma migrate dev",
|
||||||
"db:seed": "ts-node prisma/seed.ts",
|
"db:seed": "ts-node prisma/seed.ts",
|
||||||
"db:studio": "prisma studio",
|
"db:studio": "prisma studio",
|
||||||
"clean": "rm -rf dist"
|
"clean": "rm -rf dist",
|
||||||
|
"pl24:analyze": "ts-node scripts/pl24_analysis/catalog_explorer.ts",
|
||||||
|
"pl24:test-legacy": "ts-node scripts/pl24_analysis/test_legacy_parsers.ts",
|
||||||
|
"pl24:capture-html": "ts-node scripts/pl24_analysis/legacy_vin_tester.ts",
|
||||||
|
"pl24:direct-test": "ts-node scripts/pl24_analysis/direct_parser_test.ts",
|
||||||
|
"pl24:offline-test": "ts-node scripts/pl24_analysis/offline_parser_test.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^10.4.15",
|
"@nestjs/common": "^10.4.15",
|
||||||
@@ -30,6 +35,7 @@
|
|||||||
"@nestjs/jwt": "^10.2.0",
|
"@nestjs/jwt": "^10.2.0",
|
||||||
"@nestjs/passport": "^10.0.3",
|
"@nestjs/passport": "^10.0.3",
|
||||||
"@nestjs/platform-express": "^10.4.15",
|
"@nestjs/platform-express": "^10.4.15",
|
||||||
|
"@nestjs/swagger": "^8.0.0",
|
||||||
"@nestjs/throttler": "^6.3.0",
|
"@nestjs/throttler": "^6.3.0",
|
||||||
"@prisma/client": "^6.1.0",
|
"@prisma/client": "^6.1.0",
|
||||||
"@sase/shared": "workspace:*",
|
"@sase/shared": "workspace:*",
|
||||||
@@ -38,12 +44,17 @@
|
|||||||
"bullmq": "^5.30.1",
|
"bullmq": "^5.30.1",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
|
"css-select": "^5.1.0",
|
||||||
|
"domhandler": "^5.0.3",
|
||||||
|
"domutils": "^3.1.0",
|
||||||
|
"htmlparser2": "^9.1.0",
|
||||||
"ioredis": "^5.4.2",
|
"ioredis": "^5.4.2",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"puppeteer": "^24.0.0",
|
"puppeteer": "^24.0.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
|
"undici": "^7.2.0",
|
||||||
"uuid": "^11.0.3"
|
"uuid": "^11.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -8,27 +8,44 @@ datasource db {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
email String @unique
|
email String @unique
|
||||||
name String?
|
name String?
|
||||||
avatar String?
|
avatar String?
|
||||||
provider AuthProvider @default(EMAIL)
|
provider AuthProvider @default(EMAIL)
|
||||||
providerId String?
|
providerId String?
|
||||||
passwordHash String?
|
passwordHash String?
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
role Role @default(USER)
|
role Role @default(USER)
|
||||||
queryLogs QueryLog[]
|
passwordResetTokens PasswordResetToken[]
|
||||||
selectedBrands UserBrand[]
|
queryLogs QueryLog[]
|
||||||
subscription UserSubscription?
|
selectedBrands UserBrand[]
|
||||||
vehicles Vehicle[] @relation("QueriedBy")
|
subscription UserSubscription?
|
||||||
|
vehicles Vehicle[] @relation("QueriedBy")
|
||||||
|
|
||||||
@@index([email])
|
@@index([email])
|
||||||
@@index([role])
|
@@index([role])
|
||||||
@@map("users")
|
@@map("users")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model PasswordResetToken {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
tokenHash String @unique
|
||||||
|
userId String
|
||||||
|
expiresAt DateTime
|
||||||
|
isUsed Boolean @default(false)
|
||||||
|
usedAt DateTime?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([tokenHash])
|
||||||
|
@@index([userId])
|
||||||
|
@@index([expiresAt])
|
||||||
|
@@map("password_reset_tokens")
|
||||||
|
}
|
||||||
|
|
||||||
model Brand {
|
model Brand {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
code String @unique
|
code String @unique
|
||||||
@@ -101,28 +118,32 @@ model UserBrand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Vehicle {
|
model Vehicle {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
vin String @unique @db.VarChar(17)
|
vin String @unique @db.VarChar(17)
|
||||||
brandId String
|
brandId String
|
||||||
model String
|
model String
|
||||||
year Int
|
year Int
|
||||||
series String?
|
series String?
|
||||||
bodyType String?
|
bodyType String?
|
||||||
engineCode String?
|
engineCode String?
|
||||||
engineType String?
|
engineType String?
|
||||||
engineVolume String?
|
engineVolume String?
|
||||||
transmission String?
|
transmission String?
|
||||||
driveType String?
|
driveType String?
|
||||||
colorCode String?
|
colorCode String?
|
||||||
rawResponse Json
|
rawResponse Json
|
||||||
queriedById String?
|
queriedById String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
parts Part[]
|
equipment Json?
|
||||||
queryLogs QueryLog[]
|
interiorCode String?
|
||||||
categories VehicleCategory[]
|
productionDate String?
|
||||||
brand Brand @relation(fields: [brandId], references: [id])
|
salesType String?
|
||||||
queriedBy User? @relation("QueriedBy", fields: [queriedById], references: [id])
|
parts Part[]
|
||||||
|
queryLogs QueryLog[]
|
||||||
|
categories VehicleCategory[]
|
||||||
|
brand Brand @relation(fields: [brandId], references: [id])
|
||||||
|
queriedBy User? @relation("QueriedBy", fields: [queriedById], references: [id])
|
||||||
|
|
||||||
@@index([vin])
|
@@index([vin])
|
||||||
@@index([brandId])
|
@@index([brandId])
|
||||||
@@ -158,12 +179,13 @@ model VehicleCategory {
|
|||||||
vehicleId String
|
vehicleId String
|
||||||
categoryId String
|
categoryId String
|
||||||
partCount Int @default(0)
|
partCount Int @default(0)
|
||||||
schemaImageUrl String? @db.Text // Original URL from PL24
|
|
||||||
schemaPicId String? // Reference to deduplicated schema image
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
schemaImageUrl String? @db.Text
|
||||||
|
schemaPicId String?
|
||||||
|
subGroupsJson Json?
|
||||||
category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
|
category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
|
||||||
vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
|
|
||||||
schemaPic SchemaPic? @relation(fields: [schemaPicId], references: [id])
|
schemaPic SchemaPic? @relation(fields: [schemaPicId], references: [id])
|
||||||
|
vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@unique([vehicleId, categoryId])
|
@@unique([vehicleId, categoryId])
|
||||||
@@index([vehicleId])
|
@@index([vehicleId])
|
||||||
@@ -173,44 +195,56 @@ model VehicleCategory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model SchemaPic {
|
model SchemaPic {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
imageId String @unique // PL24 image ID (e.g., "194500200") for deduplication
|
imageId String @unique
|
||||||
localPath String // Local path: /images/schemas/194500200.png
|
localPath String
|
||||||
originalUrl String? @db.Text // Original PL24 URL for reference
|
originalUrl String? @db.Text
|
||||||
fileSize Int? // File size in bytes
|
fileSize Int?
|
||||||
width Int? // Image width
|
width Int?
|
||||||
height Int? // Image height
|
height Int?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
vehicleCategories VehicleCategory[]
|
hotspots Json?
|
||||||
|
vehicleCategories VehicleCategory[]
|
||||||
|
|
||||||
@@index([imageId])
|
@@index([imageId])
|
||||||
@@map("schema_pics")
|
@@map("schema_pics")
|
||||||
}
|
}
|
||||||
|
|
||||||
model Part {
|
model Part {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
vehicleId String
|
vehicleId String
|
||||||
categoryId String
|
categoryId String
|
||||||
oemCode String
|
oemCode String
|
||||||
oemCodes Json?
|
oemCodes Json?
|
||||||
nameEn String
|
nameEn String
|
||||||
nameTr String
|
nameTr String
|
||||||
description String? @db.Text
|
description String? @db.Text
|
||||||
positionCode String?
|
positionCode String?
|
||||||
positionX Float?
|
positionX Float?
|
||||||
positionY Float?
|
positionY Float?
|
||||||
brandPrices Json
|
brandPrices Json
|
||||||
imageUrl String?
|
imageUrl String?
|
||||||
notes String? @db.Text
|
notes String? @db.Text
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
|
hotspotId String?
|
||||||
vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
|
modelCodes String? @db.Text
|
||||||
|
quantity Int?
|
||||||
|
remark String?
|
||||||
|
subGroupId String?
|
||||||
|
formattedPartNo String?
|
||||||
|
isPreselected Boolean @default(false)
|
||||||
|
restrictions Json?
|
||||||
|
supersededBy String?
|
||||||
|
supersedes String?
|
||||||
|
category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
|
||||||
|
vehicle Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@index([oemCode])
|
@@index([oemCode])
|
||||||
@@index([vehicleId])
|
@@index([vehicleId])
|
||||||
@@index([categoryId])
|
@@index([categoryId])
|
||||||
@@index([vehicleId, categoryId])
|
@@index([vehicleId, categoryId])
|
||||||
|
@@index([subGroupId])
|
||||||
@@map("parts")
|
@@map("parts")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,6 +517,251 @@ model EmexCategoryTranslation {
|
|||||||
@@map("emex_category_translations")
|
@@map("emex_category_translations")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model PL24Catalog {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
serviceName String @unique
|
||||||
|
name String
|
||||||
|
brand String
|
||||||
|
catalogBase String
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
partGroups PL24PartGroup[]
|
||||||
|
parts PL24Part[]
|
||||||
|
vehicles PL24Vehicle[]
|
||||||
|
|
||||||
|
@@index([brand])
|
||||||
|
@@map("pl24_catalogs")
|
||||||
|
}
|
||||||
|
|
||||||
|
model PL24Vehicle {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
catalogId String
|
||||||
|
vehicleId String
|
||||||
|
name String
|
||||||
|
model String
|
||||||
|
series String?
|
||||||
|
bodyType String?
|
||||||
|
engineCode String?
|
||||||
|
engineType String?
|
||||||
|
engineVolume String?
|
||||||
|
transmission String?
|
||||||
|
driveType String?
|
||||||
|
fuelType String?
|
||||||
|
yearFrom Int?
|
||||||
|
yearTo Int?
|
||||||
|
catalogPath String @db.Text
|
||||||
|
mainGroupsPath String? @db.Text
|
||||||
|
ssd String? @db.VarChar(500)
|
||||||
|
rawSegments Json?
|
||||||
|
uniqueKey String @unique @db.VarChar(255)
|
||||||
|
dataHash String? @db.Char(32)
|
||||||
|
scrapeComplete Boolean @default(false)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
groupLinks PL24VehicleGroup[]
|
||||||
|
partLinks PL24VehiclePart[]
|
||||||
|
vinLinks PL24VehicleVin[]
|
||||||
|
catalog PL24Catalog @relation(fields: [catalogId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([catalogId])
|
||||||
|
@@index([vehicleId])
|
||||||
|
@@index([model])
|
||||||
|
@@index([engineCode])
|
||||||
|
@@map("pl24_vehicles")
|
||||||
|
}
|
||||||
|
|
||||||
|
model PL24VehicleVin {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
vin String @unique @db.VarChar(17)
|
||||||
|
vehicleId String
|
||||||
|
productionDate String?
|
||||||
|
colorCode String?
|
||||||
|
interiorCode String?
|
||||||
|
transmissionCode String?
|
||||||
|
equipment String? @db.Text
|
||||||
|
salesType String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
vehicle PL24Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([vehicleId])
|
||||||
|
@@map("pl24_vehicle_vins")
|
||||||
|
}
|
||||||
|
|
||||||
|
model PL24PartGroup {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
catalogId String
|
||||||
|
parentId String?
|
||||||
|
groupCode String
|
||||||
|
name String
|
||||||
|
nameTr String?
|
||||||
|
description String? @db.Text
|
||||||
|
level Int @default(1)
|
||||||
|
sortOrder Int @default(0)
|
||||||
|
hasParts Boolean @default(false)
|
||||||
|
hasChildren Boolean @default(false)
|
||||||
|
lft Int?
|
||||||
|
rgt Int?
|
||||||
|
iconUrl String? @db.Text
|
||||||
|
uniqueKey String @unique @db.VarChar(255)
|
||||||
|
dataHash String? @db.Char(32)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
catalog PL24Catalog @relation(fields: [catalogId], references: [id], onDelete: Cascade)
|
||||||
|
parent PL24PartGroup? @relation("PL24GroupTree", fields: [parentId], references: [id])
|
||||||
|
children PL24PartGroup[] @relation("PL24GroupTree")
|
||||||
|
images PL24PartImage[]
|
||||||
|
parts PL24Part[]
|
||||||
|
vehicleLinks PL24VehicleGroup[]
|
||||||
|
vehicleParts PL24VehiclePart[]
|
||||||
|
|
||||||
|
@@index([catalogId])
|
||||||
|
@@index([parentId])
|
||||||
|
@@index([groupCode])
|
||||||
|
@@map("pl24_part_groups")
|
||||||
|
}
|
||||||
|
|
||||||
|
model PL24VehicleGroup {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
vehicleId String
|
||||||
|
groupId String
|
||||||
|
linkPath String? @db.Text
|
||||||
|
linkWid String?
|
||||||
|
partCount Int @default(0)
|
||||||
|
isScraped Boolean @default(false)
|
||||||
|
schemaImageUrl String? @db.Text
|
||||||
|
schemaPicId String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
group PL24PartGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
||||||
|
schemaPic PL24SchemaPic? @relation(fields: [schemaPicId], references: [id])
|
||||||
|
vehicle PL24Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([vehicleId, groupId])
|
||||||
|
@@index([vehicleId])
|
||||||
|
@@index([groupId])
|
||||||
|
@@index([schemaPicId])
|
||||||
|
@@map("pl24_vehicle_groups")
|
||||||
|
}
|
||||||
|
|
||||||
|
model PL24Part {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
catalogId String
|
||||||
|
groupId String?
|
||||||
|
partNumber String @db.VarChar(50)
|
||||||
|
partNumberClean String? @db.VarChar(50)
|
||||||
|
formattedPartNo String?
|
||||||
|
name String
|
||||||
|
nameTr String?
|
||||||
|
description String? @db.Text
|
||||||
|
brand String?
|
||||||
|
quantity Float @default(1)
|
||||||
|
unit String @default("PC")
|
||||||
|
positionCode String?
|
||||||
|
positionX Float?
|
||||||
|
positionY Float?
|
||||||
|
remark String? @db.Text
|
||||||
|
modelCodes String? @db.Text
|
||||||
|
notes String? @db.Text
|
||||||
|
uniqueKey String @unique @db.VarChar(255)
|
||||||
|
dataHash String? @db.Char(32)
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
isDiscontinued Boolean @default(false)
|
||||||
|
supersededById String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
altNumbers PL24PartNumber[]
|
||||||
|
catalog PL24Catalog @relation(fields: [catalogId], references: [id], onDelete: Cascade)
|
||||||
|
group PL24PartGroup? @relation(fields: [groupId], references: [id])
|
||||||
|
vehicleLinks PL24VehiclePart[]
|
||||||
|
|
||||||
|
@@index([catalogId])
|
||||||
|
@@index([groupId])
|
||||||
|
@@index([partNumber])
|
||||||
|
@@index([partNumberClean])
|
||||||
|
@@map("pl24_parts")
|
||||||
|
}
|
||||||
|
|
||||||
|
model PL24PartNumber {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
partId String
|
||||||
|
number String @db.VarChar(50)
|
||||||
|
numberClean String? @db.VarChar(50)
|
||||||
|
numberType String @default("ALTERNATE")
|
||||||
|
brand String?
|
||||||
|
priority Int @default(0)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
part PL24Part @relation(fields: [partId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([partId, number])
|
||||||
|
@@index([partId])
|
||||||
|
@@index([number])
|
||||||
|
@@index([numberClean])
|
||||||
|
@@map("pl24_part_numbers")
|
||||||
|
}
|
||||||
|
|
||||||
|
model PL24VehiclePart {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
vehicleId String
|
||||||
|
partId String
|
||||||
|
groupId String?
|
||||||
|
quantity Float @default(1)
|
||||||
|
position String?
|
||||||
|
notes String? @db.Text
|
||||||
|
dateFrom DateTime?
|
||||||
|
dateTo DateTime?
|
||||||
|
conditions Json?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
group PL24PartGroup? @relation(fields: [groupId], references: [id])
|
||||||
|
part PL24Part @relation(fields: [partId], references: [id], onDelete: Cascade)
|
||||||
|
vehicle PL24Vehicle @relation(fields: [vehicleId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([vehicleId, partId, groupId])
|
||||||
|
@@index([vehicleId])
|
||||||
|
@@index([partId])
|
||||||
|
@@index([groupId])
|
||||||
|
@@map("pl24_vehicle_parts")
|
||||||
|
}
|
||||||
|
|
||||||
|
model PL24SchemaPic {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
imageId String @unique
|
||||||
|
localPath String
|
||||||
|
originalUrl String? @db.Text
|
||||||
|
width Int?
|
||||||
|
height Int?
|
||||||
|
fileSize Int?
|
||||||
|
mimeType String? @db.VarChar(50)
|
||||||
|
hotspots Json?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
downloadedAt DateTime?
|
||||||
|
vehicleGroups PL24VehicleGroup[]
|
||||||
|
|
||||||
|
@@index([imageId])
|
||||||
|
@@map("pl24_schema_pics")
|
||||||
|
}
|
||||||
|
|
||||||
|
model PL24PartImage {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
groupId String?
|
||||||
|
imageType String @default("DIAGRAM")
|
||||||
|
originalUrl String @db.Text
|
||||||
|
localPath String?
|
||||||
|
imageId String? @unique
|
||||||
|
width Int?
|
||||||
|
height Int?
|
||||||
|
fileSize Int?
|
||||||
|
isPrimary Boolean @default(false)
|
||||||
|
sortOrder Int @default(0)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
downloadedAt DateTime?
|
||||||
|
group PL24PartGroup? @relation(fields: [groupId], references: [id])
|
||||||
|
|
||||||
|
@@index([groupId])
|
||||||
|
@@index([imageId])
|
||||||
|
@@map("pl24_part_images")
|
||||||
|
}
|
||||||
|
|
||||||
enum Role {
|
enum Role {
|
||||||
USER
|
USER
|
||||||
MODERATOR
|
MODERATOR
|
||||||
|
|||||||
BIN
apps/api/public/images/schemas/00000148.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
apps/api/public/images/schemas/00007221.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
apps/api/public/images/schemas/00009623.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
apps/api/public/images/schemas/00011022.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
apps/api/public/images/schemas/00014526.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
apps/api/public/images/schemas/00014527.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
apps/api/public/images/schemas/00019897.png
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
apps/api/public/images/schemas/00023413.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
apps/api/public/images/schemas/00028226.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
apps/api/public/images/schemas/00028257.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
apps/api/public/images/schemas/00028297.png
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
apps/api/public/images/schemas/00028311.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
apps/api/public/images/schemas/00028331.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
apps/api/public/images/schemas/00028410.png
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
apps/api/public/images/schemas/00028446.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
apps/api/public/images/schemas/00028637.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
apps/api/public/images/schemas/00039131.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
apps/api/public/images/schemas/00039136.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
apps/api/public/images/schemas/00039161.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
apps/api/public/images/schemas/00039170.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
apps/api/public/images/schemas/00039187.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
apps/api/public/images/schemas/00056891.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
apps/api/public/images/schemas/00058004.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
apps/api/public/images/schemas/00058052.png
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
apps/api/public/images/schemas/00060007.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
apps/api/public/images/schemas/00060826.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
apps/api/public/images/schemas/00061950.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
apps/api/public/images/schemas/00062179.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
apps/api/public/images/schemas/00062261.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
apps/api/public/images/schemas/00062315.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
apps/api/public/images/schemas/00062483.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
apps/api/public/images/schemas/00064790.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
apps/api/public/images/schemas/00068776.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
apps/api/public/images/schemas/00072861.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
apps/api/public/images/schemas/00075213.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
apps/api/public/images/schemas/00078102.png
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
apps/api/public/images/schemas/00086529.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
apps/api/public/images/schemas/00086577.png
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
apps/api/public/images/schemas/00086638.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
apps/api/public/images/schemas/00086900.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
apps/api/public/images/schemas/00087169.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
apps/api/public/images/schemas/00089580.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
apps/api/public/images/schemas/00091192.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
apps/api/public/images/schemas/00093534.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
apps/api/public/images/schemas/00094130.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
apps/api/public/images/schemas/00094553.png
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
apps/api/public/images/schemas/00096002.png
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
apps/api/public/images/schemas/121409150.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
apps/api/public/images/schemas/158100100.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
apps/api/public/images/schemas/158105050.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
apps/api/public/images/schemas/158422400.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
apps/api/public/images/schemas/166103000.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
apps/api/public/images/schemas/166615150.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
apps/api/public/images/schemas/167321550.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
apps/api/public/images/schemas/194103350.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
apps/api/public/images/schemas/194109020.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
apps/api/public/images/schemas/194109520.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
apps/api/public/images/schemas/194300500.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
apps/api/public/images/schemas/194419220.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
apps/api/public/images/schemas/194419350.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
apps/api/public/images/schemas/194511100.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
apps/api/public/images/schemas/194601100.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
apps/api/public/images/schemas/194611220.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
apps/api/public/images/schemas/194611300.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
apps/api/public/images/schemas/194611700.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
apps/api/public/images/schemas/194612100.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
apps/api/public/images/schemas/194614300.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
apps/api/public/images/schemas/194615600.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
apps/api/public/images/schemas/201321500.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
apps/api/public/images/schemas/201325500.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
apps/api/public/images/schemas/203300800.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
apps/api/public/images/schemas/203323700.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
apps/api/public/images/schemas/203419200.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
apps/api/public/images/schemas/203422450.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
apps/api/public/images/schemas/203501100.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
apps/api/public/images/schemas/208321750.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
apps/api/public/images/schemas/220323500.png
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
apps/api/public/images/schemas/231103410.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
apps/api/public/images/schemas/231419350.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
apps/api/public/images/schemas/231422550.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
apps/api/public/images/schemas/240407100.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
apps/api/public/images/schemas/240411100.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
apps/api/public/images/schemas/240419200.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
apps/api/public/images/schemas/266409500.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
apps/api/public/images/schemas/266411000.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
apps/api/public/images/schemas/266601510.png
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
apps/api/public/images/schemas/405407280.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
apps/api/public/images/schemas/405422400.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
apps/api/public/images/schemas/405611100.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
apps/api/public/images/schemas/420109000.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
apps/api/public/images/schemas/429260010.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
apps/api/public/images/schemas/460100980.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
apps/api/public/images/schemas/515906100.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
apps/api/public/images/schemas/587300230.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
apps/api/public/images/schemas/596300150.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
apps/api/public/images/schemas/597103028.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
apps/api/public/images/schemas/649109800.png
Normal file
|
After Width: | Height: | Size: 64 KiB |