fix(vehicles): make decoded vehicles readable by any authenticated user

Decoded vehicle data is shared across the platform — once any user
decodes a VIN, the vehicle, its categories, and its parts should be
visible to every authenticated user. The previous getById enforced a
user↔vehicle junction via inner join, returning 404 "Araç bulunamadı"
when a user tried to view a vehicle they hadn't decoded themselves.
This surfaced as "Veriler yüklenirken bir hata oluştu" on the category
detail page for any vehicle the current user wasn't linked to.

Drop the ownership filter from getById; the userVehicles junction is
now used only for per-user history listing and delete operations.
Verified with Playwright: GET /api/vehicles/.../categories/... was
returning 404 from the ownership check for non-owner users.
This commit is contained in:
Semih
2026-05-14 20:17:38 +00:00
parent 47f76e47d2
commit 33e3a7a7d6
4 changed files with 44 additions and 13 deletions

View File

@@ -66,8 +66,8 @@ export class VehiclesController {
}
@Get(":vehicleId/prefetch-status")
async prefetchStatus(@Param("vehicleId") vehicleId: string, @CurrentUser("id") userId: string) {
await this.vehiclesService.getById(vehicleId, userId);
async prefetchStatus(@Param("vehicleId") vehicleId: string) {
await this.vehiclesService.getById(vehicleId);
return this.vehiclesService.getPrefetchStatus(vehicleId);
}
@@ -75,16 +75,15 @@ export class VehiclesController {
async getCategoryParts(
@Param("vehicleId") vehicleId: string,
@Param("categoryId") categoryId: string,
@CurrentUser("id") userId: string,
) {
// Verify vehicle belongs to user
await this.vehiclesService.getById(vehicleId, userId);
// Decoded vehicles are shared — any authenticated user can read.
await this.vehiclesService.getById(vehicleId);
return this.categoriesService.getCategoryWithParts(categoryId);
}
@Get(":id")
async getById(@Param("id") id: string, @CurrentUser("id") userId: string) {
return this.vehiclesService.getById(id, userId);
async getById(@Param("id") id: string) {
return this.vehiclesService.getById(id);
}
@Delete(":id")

View File

@@ -485,7 +485,7 @@ describe("VehiclesService", () => {
const db = createMockDb({ _selectRows: [vehicle] });
const { service } = createService(db);
const result = await service.getById("v1", "u1");
const result = await service.getById("v1");
expect(result).toEqual(vehicle);
});
@@ -493,7 +493,7 @@ describe("VehiclesService", () => {
const db = createMockDb({ _selectRows: [] });
const { service } = createService(db);
await expect(service.getById("nonexistent", "u1")).rejects.toThrow(NotFoundException);
await expect(service.getById("nonexistent")).rejects.toThrow(NotFoundException);
});
});

View File

@@ -881,9 +881,11 @@ export class VehiclesService {
}
/**
* Get vehicle by ID — verify user has access via junction table.
* Get vehicle by ID. Decoded vehicle data is shared across users — any
* authenticated user can read any vehicle. The userVehicles junction is
* used only for per-user history and delete operations.
*/
async getById(id: string, userId: string) {
async getById(id: string) {
const [result] = await this.db
.select({
id: vehicles.id,
@@ -902,8 +904,7 @@ export class VehiclesService {
updatedAt: vehicles.updatedAt,
})
.from(vehicles)
.innerJoin(userVehicles, eq(userVehicles.vehicleId, vehicles.id))
.where(and(eq(vehicles.id, id), eq(userVehicles.userId, userId)))
.where(eq(vehicles.id, id))
.limit(1);
if (!result) throw new NotFoundException("Araç bulunamadı");