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>
This commit is contained in:
Semih
2026-01-29 08:51:11 +01:00
parent 384aea0a01
commit 757905f41c
277 changed files with 13012 additions and 322 deletions

View File

@@ -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

View File

@@ -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": {

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Some files were not shown because too many files have changed in this diff Show More