feat: Ford legacy support, EMEX browser pooling, collapsible sidebar

- Add PL24 Ford legacy service for fordt_parts architecture
- Refactor EMEX to use persistent browser pool instead of per-call instances
- Make vehicle decode resilient: fallback to PL24 when Corgi doesn't recognize VIN
- Add collapsible sidebar with persistent user preference
- Improve brand access guard and categories service
- Add debug/test scripts for VIN e2e testing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sase Dev
2026-02-14 22:25:06 +00:00
parent 7b024df4d5
commit 4a06ba5fdb
53 changed files with 17053 additions and 682 deletions

View File

@@ -1,7 +1,7 @@
import { CanActivate, ExecutionContext, ForbiddenException, Inject, Injectable } from "@nestjs/common";
import { eq, and } from "drizzle-orm";
import { DATABASE, Database } from "../../database/database.provider";
import { userSubscriptions, userBrands } from "../../database/schema/core";
import { userSubscriptions, userBrands, plans } from "../../database/schema/core";
@Injectable()
export class BrandAccessGuard implements CanActivate {
@@ -22,8 +22,12 @@ export class BrandAccessGuard implements CanActivate {
// Check if user has an active subscription with access to this brand
const activeSub = await this.db
.select()
.select({
id: userSubscriptions.id,
brandCount: plans.brandCount,
})
.from(userSubscriptions)
.innerJoin(plans, eq(userSubscriptions.planId, plans.id))
.where(and(eq(userSubscriptions.userId, user.id), eq(userSubscriptions.status, "active")))
.limit(1);
@@ -31,10 +35,12 @@ export class BrandAccessGuard implements CanActivate {
throw new ForbiddenException("No active subscription");
}
// Check if the subscription includes this brand (brandCount=0 means all brands)
const subscription = activeSub[0];
// Check userBrands junction
// brandCount === 0 means unlimited (Full Paket) — all brands accessible
if (subscription.brandCount === 0) return true;
// Check userBrands junction for limited plans
const brandAccess = await this.db
.select()
.from(userBrands)