fix(api): revert import type for runtime-injected NestJS deps
The biome safe-fix sweep in 3184e4c rewrote `import { X }` to
`import type { X }` for several NestJS providers (ConfigService,
Reflector, EmailService, Database, etc.). Type-only imports are
erased at compile time, but Nest's reflect-metadata DI reads
constructor parameter types at runtime, so every affected provider
threw "Nest can't resolve dependencies (?)" and the API crash-
looped under PM2 (377+ restarts).
Convert all `import type {` to `import {` in apps/api/src so the
type tokens survive into the emitted JS for runtime DI.
Note: biome's useImportType rule does not understand the
reflect-metadata pattern. Worth disabling for apps/api/ in a
follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { Body, Controller, Get, Param, Post, Query } from "@nestjs/common";
|
import { Body, Controller, Get, Param, Post, Query } from "@nestjs/common";
|
||||||
import { Roles } from "../common/decorators/roles.decorator";
|
import { Roles } from "../common/decorators/roles.decorator";
|
||||||
import type { AdminService } from "./admin.service";
|
import { AdminService } from "./admin.service";
|
||||||
|
|
||||||
@Controller("admin")
|
@Controller("admin")
|
||||||
@Roles("admin")
|
@Roles("admin")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ConflictException, Inject, Injectable, Logger, NotFoundException } from
|
|||||||
import { generateReferralCode } from "@sase/shared";
|
import { generateReferralCode } from "@sase/shared";
|
||||||
import { hashPassword } from "better-auth/crypto";
|
import { hashPassword } from "better-auth/crypto";
|
||||||
import { and, count, desc, eq, gte, ilike, inArray, or, sql } from "drizzle-orm";
|
import { and, count, desc, eq, gte, ilike, inArray, or, sql } from "drizzle-orm";
|
||||||
import type { AnalyticsService } from "../analytics/analytics.service";
|
import { AnalyticsService } from "../analytics/analytics.service";
|
||||||
import { DATABASE, type Database } from "../database/database.provider";
|
import { DATABASE, type Database } from "../database/database.provider";
|
||||||
import {
|
import {
|
||||||
accounts,
|
accounts,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Body, Controller, Post } from "@nestjs/common";
|
import { Body, Controller, Post } from "@nestjs/common";
|
||||||
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
||||||
import type { AnalyticsService } from "./analytics.service";
|
import { AnalyticsService } from "./analytics.service";
|
||||||
|
|
||||||
@Controller("analytics")
|
@Controller("analytics")
|
||||||
export class AnalyticsController {
|
export class AnalyticsController {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { All, Controller, Req, Res } from "@nestjs/common";
|
import { All, Controller, Req, Res } from "@nestjs/common";
|
||||||
import { toNodeHandler } from "better-auth/node";
|
import { toNodeHandler } from "better-auth/node";
|
||||||
import type { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { Public } from "../common/decorators/public.decorator";
|
import { Public } from "../common/decorators/public.decorator";
|
||||||
import { getAuth } from "./auth";
|
import { getAuth } from "./auth";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Module, type OnModuleInit } from "@nestjs/common";
|
import { Module, type OnModuleInit } from "@nestjs/common";
|
||||||
import type { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import type { EmailService } from "../email/email.service";
|
import { EmailService } from "../email/email.service";
|
||||||
import { createAuth } from "./auth";
|
import { createAuth } from "./auth";
|
||||||
import { AuthController } from "./auth.controller";
|
import { AuthController } from "./auth.controller";
|
||||||
import { AuthService } from "./auth.service";
|
import { AuthService } from "./auth.service";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|||||||
import { drizzle } from "drizzle-orm/postgres-js";
|
import { drizzle } from "drizzle-orm/postgres-js";
|
||||||
import postgres from "postgres";
|
import postgres from "postgres";
|
||||||
import * as schema from "../database/schema/core";
|
import * as schema from "../database/schema/core";
|
||||||
import type { EmailService } from "../email/email.service";
|
import { EmailService } from "../email/email.service";
|
||||||
|
|
||||||
let authInstance: ReturnType<typeof betterAuth> | null = null;
|
let authInstance: ReturnType<typeof betterAuth> | null = null;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Body, Controller, Get, Param, Patch, Post, UseGuards } from "@nestjs/co
|
|||||||
import { Public } from "../common/decorators/public.decorator";
|
import { Public } from "../common/decorators/public.decorator";
|
||||||
import { Roles } from "../common/decorators/roles.decorator";
|
import { Roles } from "../common/decorators/roles.decorator";
|
||||||
import { RolesGuard } from "../common/guards/roles.guard";
|
import { RolesGuard } from "../common/guards/roles.guard";
|
||||||
import type { BrandsService } from "./brands.service";
|
import { BrandsService } from "./brands.service";
|
||||||
|
|
||||||
@Controller("brands")
|
@Controller("brands")
|
||||||
export class BrandsController {
|
export class BrandsController {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Controller, Get, Param, Post, Query } from "@nestjs/common";
|
import { Controller, Get, Param, Post, Query } from "@nestjs/common";
|
||||||
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
||||||
import { Roles } from "../common/decorators/roles.decorator";
|
import { Roles } from "../common/decorators/roles.decorator";
|
||||||
import type { CatalogService } from "./catalog.service";
|
import { CatalogService } from "./catalog.service";
|
||||||
|
|
||||||
@Controller("catalog")
|
@Controller("catalog")
|
||||||
export class CatalogController {
|
export class CatalogController {
|
||||||
|
|||||||
@@ -11,15 +11,15 @@ import {
|
|||||||
userBrands,
|
userBrands,
|
||||||
userSubscriptions,
|
userSubscriptions,
|
||||||
} from "../database/schema/core";
|
} from "../database/schema/core";
|
||||||
import type { PL24Service } from "../integrations/pl24/pl24.service";
|
import { PL24Service } from "../integrations/pl24/pl24.service";
|
||||||
import {
|
import {
|
||||||
PL24_SERVICE_CATALOGS,
|
PL24_SERVICE_CATALOGS,
|
||||||
SERVICE_DISPLAY_NAMES,
|
SERVICE_DISPLAY_NAMES,
|
||||||
SERVICE_TO_BRAND,
|
SERVICE_TO_BRAND,
|
||||||
isP5Modern,
|
isP5Modern,
|
||||||
} from "../integrations/pl24/pl24.types";
|
} from "../integrations/pl24/pl24.types";
|
||||||
import type { RedisService } from "../redis/redis.service";
|
import { RedisService } from "../redis/redis.service";
|
||||||
import type { StorageService } from "../storage/storage.service";
|
import { StorageService } from "../storage/storage.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CatalogService {
|
export class CatalogService {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Controller, Get, Param } from "@nestjs/common";
|
import { Controller, Get, Param } from "@nestjs/common";
|
||||||
import type { CategoriesService } from "./categories.service";
|
import { CategoriesService } from "./categories.service";
|
||||||
|
|
||||||
@Controller("categories")
|
@Controller("categories")
|
||||||
export class CategoriesController {
|
export class CategoriesController {
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import { Inject, Injectable, Logger, NotFoundException } from "@nestjs/common";
|
|||||||
import { eq, inArray, isNull, sql } from "drizzle-orm";
|
import { eq, inArray, isNull, sql } from "drizzle-orm";
|
||||||
import { DATABASE, type Database } from "../database/database.provider";
|
import { DATABASE, type Database } from "../database/database.provider";
|
||||||
import { categories, parts, schemaPics, vehicles } from "../database/schema/core";
|
import { categories, parts, schemaPics, vehicles } from "../database/schema/core";
|
||||||
import type { EmexService } from "../integrations/emex/emex.service";
|
import { EmexService } from "../integrations/emex/emex.service";
|
||||||
import type { PartsCatalogsService } from "../integrations/parts-catalogs/parts-catalogs.service";
|
import { PartsCatalogsService } from "../integrations/parts-catalogs/parts-catalogs.service";
|
||||||
import type { PcatGroup } from "../integrations/parts-catalogs/parts-catalogs.types";
|
import { PcatGroup } from "../integrations/parts-catalogs/parts-catalogs.types";
|
||||||
import type { PL24FordLegacyService } from "../integrations/pl24/pl24-ford-legacy.service";
|
import { PL24FordLegacyService } from "../integrations/pl24/pl24-ford-legacy.service";
|
||||||
import type { PL24Service } from "../integrations/pl24/pl24.service";
|
import { PL24Service } from "../integrations/pl24/pl24.service";
|
||||||
import type { RedisService } from "../redis/redis.service";
|
import { RedisService } from "../redis/redis.service";
|
||||||
import type { StorageService } from "../storage/storage.service";
|
import { StorageService } from "../storage/storage.service";
|
||||||
import type { TranslationsService } from "../translations/translations.service";
|
import { TranslationsService } from "../translations/translations.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CategoriesService {
|
export class CategoriesService {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
HttpStatus,
|
HttpStatus,
|
||||||
Logger,
|
Logger,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import type { Response } from "express";
|
import { Response } from "express";
|
||||||
|
|
||||||
// Drizzle/postgres unique violation error
|
// Drizzle/postgres unique violation error
|
||||||
@Catch()
|
@Catch()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
Logger,
|
Logger,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
||||||
import type { Response } from "express";
|
import { Response } from "express";
|
||||||
|
|
||||||
@Catch()
|
@Catch()
|
||||||
export class HttpExceptionFilter implements ExceptionFilter {
|
export class HttpExceptionFilter implements ExceptionFilter {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { UnauthorizedException } from "@nestjs/common";
|
import { UnauthorizedException } from "@nestjs/common";
|
||||||
import type { Reflector } from "@nestjs/core";
|
import { Reflector } from "@nestjs/core";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { AuthGuard } from "./auth.guard";
|
import { AuthGuard } from "./auth.guard";
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
Injectable,
|
Injectable,
|
||||||
UnauthorizedException,
|
UnauthorizedException,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import type { Reflector } from "@nestjs/core";
|
import { Reflector } from "@nestjs/core";
|
||||||
import { getAuth } from "../../auth/auth";
|
import { getAuth } from "../../auth/auth";
|
||||||
import { IS_PUBLIC_KEY } from "../decorators/public.decorator";
|
import { IS_PUBLIC_KEY } from "../decorators/public.decorator";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ForbiddenException } from "@nestjs/common";
|
import { ForbiddenException } from "@nestjs/common";
|
||||||
import type { Reflector } from "@nestjs/core";
|
import { Reflector } from "@nestjs/core";
|
||||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { RolesGuard } from "./roles.guard";
|
import { RolesGuard } from "./roles.guard";
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
ForbiddenException,
|
ForbiddenException,
|
||||||
Injectable,
|
Injectable,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import type { Reflector } from "@nestjs/core";
|
import { Reflector } from "@nestjs/core";
|
||||||
import { ROLES_KEY } from "../decorators/roles.decorator";
|
import { ROLES_KEY } from "../decorators/roles.decorator";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { NextFunction, Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
|
|
||||||
const ALLOWED_MIME_TYPES = ["image/png", "image/jpeg", "image/jpg", "application/pdf"];
|
const ALLOWED_MIME_TYPES = ["image/png", "image/jpeg", "image/jpg", "application/pdf"];
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Provider } from "@nestjs/common";
|
import { Provider } from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { type PostgresJsDatabase, drizzle } from "drizzle-orm/postgres-js";
|
import { type PostgresJsDatabase, drizzle } from "drizzle-orm/postgres-js";
|
||||||
import postgres from "postgres";
|
import postgres from "postgres";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable, Logger } from "@nestjs/common";
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
import type { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
|
||||||
export interface SendEmailOptions {
|
export interface SendEmailOptions {
|
||||||
to: string;
|
to: string;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Controller, Get, Inject } from "@nestjs/common";
|
|||||||
import { sql } from "drizzle-orm";
|
import { sql } from "drizzle-orm";
|
||||||
import { Public } from "./common/decorators/public.decorator";
|
import { Public } from "./common/decorators/public.decorator";
|
||||||
import { DATABASE, type Database } from "./database/database.provider";
|
import { DATABASE, type Database } from "./database/database.provider";
|
||||||
import type { RedisService } from "./redis/redis.service";
|
import { RedisService } from "./redis/redis.service";
|
||||||
import { isOtelEnabled } from "./telemetry";
|
import { isOtelEnabled } from "./telemetry";
|
||||||
|
|
||||||
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable, Logger, type OnModuleDestroy, type OnModuleInit } from "@nestjs/common";
|
import { Injectable, Logger, type OnModuleDestroy, type OnModuleInit } from "@nestjs/common";
|
||||||
import type { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import type { Browser, BrowserContext, Page } from "playwright";
|
import { Browser, BrowserContext, Page } from "playwright";
|
||||||
|
|
||||||
const SESSION_TTL_MS = 25 * 60 * 1000; // 25 minutes
|
const SESSION_TTL_MS = 25 * 60 * 1000; // 25 minutes
|
||||||
const MAX_CONCURRENT_PAGES = 3;
|
const MAX_CONCURRENT_PAGES = 3;
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ import {
|
|||||||
Logger,
|
Logger,
|
||||||
ServiceUnavailableException,
|
ServiceUnavailableException,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import type { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
|
||||||
import { ProxyAgent } from "undici";
|
import { ProxyAgent } from "undici";
|
||||||
import type { RedisService } from "../../redis/redis.service";
|
import { RedisService } from "../../redis/redis.service";
|
||||||
import type { EmexBrowserService } from "./emex.browser";
|
import { EmexBrowserService } from "./emex.browser";
|
||||||
import { createEmptyDecodedVehicle, mapEmexResponse } from "./emex.mapper";
|
import { createEmptyDecodedVehicle, mapEmexResponse } from "./emex.mapper";
|
||||||
import {
|
import {
|
||||||
CATALOG_MAP,
|
CATALOG_MAP,
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable, Logger, type OnModuleDestroy, type OnModuleInit } from "@nestjs/common";
|
import { Injectable, Logger, type OnModuleDestroy, type OnModuleInit } from "@nestjs/common";
|
||||||
import type { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import type { Browser, BrowserContext } from "playwright";
|
import { Browser, BrowserContext } from "playwright";
|
||||||
import type { JwtSlot, PcatJwtToken, PcatSession } from "./parts-catalogs.types";
|
import { JwtSlot, PcatJwtToken, PcatSession } from "./parts-catalogs.types";
|
||||||
|
|
||||||
const TOKEN_TTL = 600; // seconds — TWS- has no built-in expiry, refresh aggressively
|
const TOKEN_TTL = 600; // seconds — TWS- has no built-in expiry, refresh aggressively
|
||||||
const REFRESH_BUFFER = 90; // Refresh 90s before expiry
|
const REFRESH_BUFFER = 90; // Refresh 90s before expiry
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable, Logger } from "@nestjs/common";
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
import type { RedisService } from "../../redis/redis.service";
|
import { RedisService } from "../../redis/redis.service";
|
||||||
import type { PartsCatalogsAuthService } from "./parts-catalogs-auth.service";
|
import { PartsCatalogsAuthService } from "./parts-catalogs-auth.service";
|
||||||
import type {
|
import {
|
||||||
PcatCar,
|
PcatCar,
|
||||||
PcatGroup,
|
PcatGroup,
|
||||||
PcatPartsResult,
|
PcatPartsResult,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* response normalization.
|
* response normalization.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { PL24DecodedCategory, PL24DecodedVehicle, PL24Part } from "../pl24.types";
|
import { PL24DecodedCategory, PL24DecodedVehicle, PL24Part } from "../pl24.types";
|
||||||
|
|
||||||
export abstract class BasePL24Parser {
|
export abstract class BasePL24Parser {
|
||||||
abstract readonly brandName: string;
|
abstract readonly brandName: string;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* parsing is done directly in PL24Service using the actual API response format.
|
* parsing is done directly in PL24Service using the actual API response format.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { PL24DecodedCategory, PL24DecodedVehicle, PL24Part } from "../pl24.types";
|
import { PL24DecodedCategory, PL24DecodedVehicle, PL24Part } from "../pl24.types";
|
||||||
import { BasePL24Parser } from "./base-parser";
|
import { BasePL24Parser } from "./base-parser";
|
||||||
|
|
||||||
export class GenericPL24Parser extends BasePL24Parser {
|
export class GenericPL24Parser extends BasePL24Parser {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { PL24_WMI_SERVICE_MAP, isP5Modern } from "../pl24.types";
|
import { PL24_WMI_SERVICE_MAP, isP5Modern } from "../pl24.types";
|
||||||
import type { BasePL24Parser } from "./base-parser";
|
import { BasePL24Parser } from "./base-parser";
|
||||||
import { BmwPL24Parser } from "./bmw-parser";
|
import { BmwPL24Parser } from "./bmw-parser";
|
||||||
import { GenericPL24Parser } from "./generic-parser";
|
import { GenericPL24Parser } from "./generic-parser";
|
||||||
import { MercedesPL24Parser } from "./mercedes-parser";
|
import { MercedesPL24Parser } from "./mercedes-parser";
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable, Logger, UnauthorizedException } from "@nestjs/common";
|
import { Injectable, Logger, UnauthorizedException } from "@nestjs/common";
|
||||||
import type { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { PL24_ENDPOINTS } from "./pl24.constants";
|
import { PL24_ENDPOINTS } from "./pl24.constants";
|
||||||
import type {
|
import {
|
||||||
PL24AuthorizeRequest,
|
PL24AuthorizeRequest,
|
||||||
PL24AuthorizeResponse,
|
PL24AuthorizeResponse,
|
||||||
PL24JWTPayload,
|
PL24JWTPayload,
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { Injectable, Logger } from "@nestjs/common";
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
import type { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import type { RedisService } from "../../redis/redis.service";
|
import { RedisService } from "../../redis/redis.service";
|
||||||
import type { StorageService } from "../../storage/storage.service";
|
import { StorageService } from "../../storage/storage.service";
|
||||||
import type { PL24AuthService } from "./pl24-auth.service";
|
import { PL24AuthService } from "./pl24-auth.service";
|
||||||
import { FORD_LEGACY_ENDPOINTS, type FordPL24Support } from "./pl24-ford-legacy.types";
|
import { FORD_LEGACY_ENDPOINTS, type FordPL24Support } from "./pl24-ford-legacy.types";
|
||||||
import { PL24_DEFAULTS } from "./pl24.constants";
|
import { PL24_DEFAULTS } from "./pl24.constants";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ import {
|
|||||||
NotFoundException,
|
NotFoundException,
|
||||||
ServiceUnavailableException,
|
ServiceUnavailableException,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import type { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import type { RedisService } from "../../redis/redis.service";
|
import { RedisService } from "../../redis/redis.service";
|
||||||
import type { StorageService } from "../../storage/storage.service";
|
import { StorageService } from "../../storage/storage.service";
|
||||||
import type { PL24AuthService } from "./pl24-auth.service";
|
import { PL24AuthService } from "./pl24-auth.service";
|
||||||
import type { PL24FordLegacyService } from "./pl24-ford-legacy.service";
|
import { PL24FordLegacyService } from "./pl24-ford-legacy.service";
|
||||||
import { PL24_DEFAULTS } from "./pl24.constants";
|
import { PL24_DEFAULTS } from "./pl24.constants";
|
||||||
import {
|
import {
|
||||||
type PL24DecodedCategory,
|
type PL24DecodedCategory,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ConnectionOptions } from "bullmq";
|
import { ConnectionOptions } from "bullmq";
|
||||||
import { isOtelEnabled } from "../telemetry";
|
import { isOtelEnabled } from "../telemetry";
|
||||||
|
|
||||||
export function getBullConnection(): ConnectionOptions {
|
export function getBullConnection(): ConnectionOptions {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Inject, Module, type OnModuleDestroy, type OnModuleInit } from "@nestjs/common";
|
import { Inject, Module, type OnModuleDestroy, type OnModuleInit } from "@nestjs/common";
|
||||||
import type { Queue } from "bullmq";
|
import { Queue } from "bullmq";
|
||||||
import { CategoriesModule } from "../categories/categories.module";
|
import { CategoriesModule } from "../categories/categories.module";
|
||||||
import { PrefetchWorkerService } from "./prefetch-worker.service";
|
import { PrefetchWorkerService } from "./prefetch-worker.service";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { RedisService } from "../redis/redis.service";
|
import { RedisService } from "../redis/redis.service";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom error that tells BullMQ to retry after a delay.
|
* Custom error that tells BullMQ to retry after a delay.
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import {
|
|||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { type Job, type Queue, Worker } from "bullmq";
|
import { type Job, type Queue, Worker } from "bullmq";
|
||||||
import { and, eq, isNull } from "drizzle-orm";
|
import { and, eq, isNull } from "drizzle-orm";
|
||||||
import type { CategoriesService } from "../categories/categories.service";
|
import { CategoriesService } from "../categories/categories.service";
|
||||||
import { DATABASE, type Database } from "../database/database.provider";
|
import { DATABASE, type Database } from "../database/database.provider";
|
||||||
import { categories, parts, vehicles } from "../database/schema/core";
|
import { categories, parts, vehicles } from "../database/schema/core";
|
||||||
import type { RedisService } from "../redis/redis.service";
|
import { RedisService } from "../redis/redis.service";
|
||||||
import { QUEUE_NAMES, getBullConnection } from "./bull.config";
|
import { QUEUE_NAMES, getBullConnection } from "./bull.config";
|
||||||
import {
|
import {
|
||||||
RateLimitError,
|
RateLimitError,
|
||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
initProgress,
|
initProgress,
|
||||||
updateProgress,
|
updateProgress,
|
||||||
} from "./prefetch-utils";
|
} from "./prefetch-utils";
|
||||||
import type { PrefetchCategoryJobData, PrefetchInitJobData } from "./prefetch.types";
|
import { PrefetchCategoryJobData, PrefetchInitJobData } from "./prefetch.types";
|
||||||
import { CATALOG_PREFETCH_QUEUE } from "./queues/catalog-prefetch.queue";
|
import { CATALOG_PREFETCH_QUEUE } from "./queues/catalog-prefetch.queue";
|
||||||
|
|
||||||
const MAX_DEPTH = 5;
|
const MAX_DEPTH = 5;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Job } from "bullmq";
|
import { Job } from "bullmq";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
||||||
import {
|
import {
|
||||||
emexCatalogs,
|
emexCatalogs,
|
||||||
emexPartGroups,
|
emexPartGroups,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Job } from "bullmq";
|
import { Job } from "bullmq";
|
||||||
import { lt } from "drizzle-orm";
|
import { lt } from "drizzle-orm";
|
||||||
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
||||||
import { queryLogs } from "../../database/schema/core";
|
import { queryLogs } from "../../database/schema/core";
|
||||||
|
|
||||||
type Database = PostgresJsDatabase<Record<string, unknown>>;
|
type Database = PostgresJsDatabase<Record<string, unknown>>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Job } from "bullmq";
|
import { Job } from "bullmq";
|
||||||
import { and, eq, lt } from "drizzle-orm";
|
import { and, eq, lt } from "drizzle-orm";
|
||||||
import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
||||||
import { userBrands, userSubscriptions } from "../../database/schema/core";
|
import { userBrands, userSubscriptions } from "../../database/schema/core";
|
||||||
|
|
||||||
type Database = PostgresJsDatabase<Record<string, unknown>>;
|
type Database = PostgresJsDatabase<Record<string, unknown>>;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Provider } from "@nestjs/common";
|
import { Provider } from "@nestjs/common";
|
||||||
import { Queue } from "bullmq";
|
import { Queue } from "bullmq";
|
||||||
import { QUEUE_NAMES, getBullConnection, getBullTelemetry } from "../bull.config";
|
import { QUEUE_NAMES, getBullConnection, getBullTelemetry } from "../bull.config";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Provider } from "@nestjs/common";
|
import { Provider } from "@nestjs/common";
|
||||||
import { Queue } from "bullmq";
|
import { Queue } from "bullmq";
|
||||||
import { QUEUE_NAMES, getBullConnection, getBullTelemetry } from "../bull.config";
|
import { QUEUE_NAMES, getBullConnection, getBullTelemetry } from "../bull.config";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Provider } from "@nestjs/common";
|
import { Provider } from "@nestjs/common";
|
||||||
import { Queue } from "bullmq";
|
import { Queue } from "bullmq";
|
||||||
import { QUEUE_NAMES, getBullConnection, getBullTelemetry } from "../bull.config";
|
import { QUEUE_NAMES, getBullConnection, getBullTelemetry } from "../bull.config";
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Provider } from "@nestjs/common";
|
import { Provider } from "@nestjs/common";
|
||||||
import { Queue } from "bullmq";
|
import { Queue } from "bullmq";
|
||||||
import { QUEUE_NAMES, getBullConnection, getBullTelemetry } from "../bull.config";
|
import { QUEUE_NAMES, getBullConnection, getBullTelemetry } from "../bull.config";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import "./telemetry/tracing"; // MUST be first — instruments modules before th
|
|||||||
|
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { NestFactory } from "@nestjs/core";
|
import { NestFactory } from "@nestjs/core";
|
||||||
import type { NextFunction, Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import helmet from "helmet";
|
import helmet from "helmet";
|
||||||
import { AppModule } from "./app.module";
|
import { AppModule } from "./app.module";
|
||||||
import { fileUploadValidation } from "./common/middleware/file-upload-validation.middleware";
|
import { fileUploadValidation } from "./common/middleware/file-upload-validation.middleware";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Controller, Get, Param, Query } from "@nestjs/common";
|
import { Controller, Get, Param, Query } from "@nestjs/common";
|
||||||
import type { PartsService } from "./parts.service";
|
import { PartsService } from "./parts.service";
|
||||||
|
|
||||||
@Controller("parts")
|
@Controller("parts")
|
||||||
export class PartsController {
|
export class PartsController {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Inject, Injectable, Logger, NotFoundException } from "@nestjs/common";
|
|||||||
import { eq, like } from "drizzle-orm";
|
import { eq, like } from "drizzle-orm";
|
||||||
import { DATABASE, type Database } from "../database/database.provider";
|
import { DATABASE, type Database } from "../database/database.provider";
|
||||||
import { categories, parts, schemaPics, vehicles } from "../database/schema/core";
|
import { categories, parts, schemaPics, vehicles } from "../database/schema/core";
|
||||||
import type { PL24Service } from "../integrations/pl24/pl24.service";
|
import { PL24Service } from "../integrations/pl24/pl24.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PartsService {
|
export class PartsService {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { FileInterceptor } from "@nestjs/platform-express";
|
|||||||
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
||||||
import { Roles } from "../common/decorators/roles.decorator";
|
import { Roles } from "../common/decorators/roles.decorator";
|
||||||
import { RolesGuard } from "../common/guards/roles.guard";
|
import { RolesGuard } from "../common/guards/roles.guard";
|
||||||
import type { PaymentsService } from "./payments.service";
|
import { PaymentsService } from "./payments.service";
|
||||||
|
|
||||||
@Controller("payments")
|
@Controller("payments")
|
||||||
export class PaymentsController {
|
export class PaymentsController {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { BadRequestException, Inject, Injectable, Logger, NotFoundException } from "@nestjs/common";
|
import { BadRequestException, Inject, Injectable, Logger, NotFoundException } from "@nestjs/common";
|
||||||
import type { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { and, desc, eq } from "drizzle-orm";
|
import { and, desc, eq } from "drizzle-orm";
|
||||||
import { DATABASE, type Database } from "../database/database.provider";
|
import { DATABASE, type Database } from "../database/database.provider";
|
||||||
import { payments, plans, userSubscriptions } from "../database/schema/core";
|
import { payments, plans, userSubscriptions } from "../database/schema/core";
|
||||||
import type { StorageService } from "../storage/storage.service";
|
import { StorageService } from "../storage/storage.service";
|
||||||
import type { SubscriptionsService } from "../subscriptions/subscriptions.service";
|
import { SubscriptionsService } from "../subscriptions/subscriptions.service";
|
||||||
|
|
||||||
const PLAN_KEY_TO_BRAND_COUNT: Record<string, number> = {
|
const PLAN_KEY_TO_BRAND_COUNT: Record<string, number> = {
|
||||||
brand1: 1,
|
brand1: 1,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Body, Controller, Get, Param, Patch, Post, UseGuards } from "@nestjs/co
|
|||||||
import { Public } from "../common/decorators/public.decorator";
|
import { Public } from "../common/decorators/public.decorator";
|
||||||
import { Roles } from "../common/decorators/roles.decorator";
|
import { Roles } from "../common/decorators/roles.decorator";
|
||||||
import { RolesGuard } from "../common/guards/roles.guard";
|
import { RolesGuard } from "../common/guards/roles.guard";
|
||||||
import type { PlansService } from "./plans.service";
|
import { PlansService } from "./plans.service";
|
||||||
|
|
||||||
@Controller("plans")
|
@Controller("plans")
|
||||||
export class PlansController {
|
export class PlansController {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Provider } from "@nestjs/common";
|
import { Provider } from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
import Redis from "ioredis";
|
import Redis from "ioredis";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Body, Controller, Get, Post } from "@nestjs/common";
|
import { Body, Controller, Get, Post } from "@nestjs/common";
|
||||||
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
||||||
import type { ReferralsService } from "./referrals.service";
|
import { ReferralsService } from "./referrals.service";
|
||||||
|
|
||||||
@Controller("referrals")
|
@Controller("referrals")
|
||||||
export class ReferralsController {
|
export class ReferralsController {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { generateReferralCode } from "@sase/shared";
|
|||||||
import { and, eq, sql } from "drizzle-orm";
|
import { and, eq, sql } from "drizzle-orm";
|
||||||
import { DATABASE, type Database } from "../database/database.provider";
|
import { DATABASE, type Database } from "../database/database.provider";
|
||||||
import { referrals, users } from "../database/schema/core";
|
import { referrals, users } from "../database/schema/core";
|
||||||
import type { SubscriptionsService } from "../subscriptions/subscriptions.service";
|
import { SubscriptionsService } from "../subscriptions/subscriptions.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ReferralsService {
|
export class ReferralsService {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
S3Client,
|
S3Client,
|
||||||
} from "@aws-sdk/client-s3";
|
} from "@aws-sdk/client-s3";
|
||||||
import { Injectable, Logger } from "@nestjs/common";
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
import type { ConfigService } from "@nestjs/config";
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StorageService {
|
export class StorageService {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Body, Controller, Get, Patch, Post, Query, UseGuards } from "@nestjs/co
|
|||||||
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
||||||
import { Roles } from "../common/decorators/roles.decorator";
|
import { Roles } from "../common/decorators/roles.decorator";
|
||||||
import { RolesGuard } from "../common/guards/roles.guard";
|
import { RolesGuard } from "../common/guards/roles.guard";
|
||||||
import type { SubscriptionsService } from "./subscriptions.service";
|
import { SubscriptionsService } from "./subscriptions.service";
|
||||||
|
|
||||||
@Controller("subscriptions")
|
@Controller("subscriptions")
|
||||||
export class SubscriptionsController {
|
export class SubscriptionsController {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
||||||
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
||||||
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
||||||
import type { Instrumentation } from "@opentelemetry/instrumentation";
|
import { Instrumentation } from "@opentelemetry/instrumentation";
|
||||||
import { resourceFromAttributes } from "@opentelemetry/resources";
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
||||||
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
|
||||||
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import "dotenv/config"; // Load env vars before checking OTEL_ENABLED
|
import "dotenv/config"; // Load env vars before checking OTEL_ENABLED
|
||||||
import type { NodeSDK } from "@opentelemetry/sdk-node";
|
import { NodeSDK } from "@opentelemetry/sdk-node";
|
||||||
import { isOtelEnabled } from "./index";
|
import { isOtelEnabled } from "./index";
|
||||||
|
|
||||||
let sdk: NodeSDK | null = null;
|
let sdk: NodeSDK | null = null;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Body, Controller, Get, Param, Post, Put, Query, UseGuards } from "@nest
|
|||||||
import { Public } from "../common/decorators/public.decorator";
|
import { Public } from "../common/decorators/public.decorator";
|
||||||
import { Roles } from "../common/decorators/roles.decorator";
|
import { Roles } from "../common/decorators/roles.decorator";
|
||||||
import { RolesGuard } from "../common/guards/roles.guard";
|
import { RolesGuard } from "../common/guards/roles.guard";
|
||||||
import type { TranslationsService } from "./translations.service";
|
import { TranslationsService } from "./translations.service";
|
||||||
|
|
||||||
@Controller("translations")
|
@Controller("translations")
|
||||||
export class TranslationsController {
|
export class TranslationsController {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Inject, Injectable, Logger } from "@nestjs/common";
|
|||||||
import { eq, ilike, inArray, or } from "drizzle-orm";
|
import { eq, ilike, inArray, or } from "drizzle-orm";
|
||||||
import { DATABASE, type Database } from "../database/database.provider";
|
import { DATABASE, type Database } from "../database/database.provider";
|
||||||
import { emexCategoryTranslations } from "../database/schema/core";
|
import { emexCategoryTranslations } from "../database/schema/core";
|
||||||
import type { RedisService } from "../redis/redis.service";
|
import { RedisService } from "../redis/redis.service";
|
||||||
|
|
||||||
/** 30 days in seconds */
|
/** 30 days in seconds */
|
||||||
const CACHE_TTL = 30 * 24 * 60 * 60;
|
const CACHE_TTL = 30 * 24 * 60 * 60;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { CurrentUser } from "../common/decorators/current-user.decorator";
|
|||||||
import { Public } from "../common/decorators/public.decorator";
|
import { Public } from "../common/decorators/public.decorator";
|
||||||
import { Roles } from "../common/decorators/roles.decorator";
|
import { Roles } from "../common/decorators/roles.decorator";
|
||||||
import { RolesGuard } from "../common/guards/roles.guard";
|
import { RolesGuard } from "../common/guards/roles.guard";
|
||||||
import type { UsersService } from "./users.service";
|
import { UsersService } from "./users.service";
|
||||||
|
|
||||||
@Controller("users")
|
@Controller("users")
|
||||||
export class UsersController {
|
export class UsersController {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Post, Query } from "@nestjs/common";
|
import { Body, Controller, Delete, Get, Param, Post, Query } from "@nestjs/common";
|
||||||
import type { CategoriesService } from "../categories/categories.service";
|
import { CategoriesService } from "../categories/categories.service";
|
||||||
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
||||||
import { Public } from "../common/decorators/public.decorator";
|
import { Public } from "../common/decorators/public.decorator";
|
||||||
import { VinValidationPipe } from "../common/pipes/vin-validation.pipe";
|
import { VinValidationPipe } from "../common/pipes/vin-validation.pipe";
|
||||||
import type { EmailService } from "../email/email.service";
|
import { EmailService } from "../email/email.service";
|
||||||
import type { VehiclesService } from "./vehicles.service";
|
import { VehiclesService } from "./vehicles.service";
|
||||||
|
|
||||||
@Controller("vehicles")
|
@Controller("vehicles")
|
||||||
export class VehiclesController {
|
export class VehiclesController {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
NotFoundException,
|
NotFoundException,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { isValidVin } from "@sase/shared";
|
import { isValidVin } from "@sase/shared";
|
||||||
import type { Queue } from "bullmq";
|
import { Queue } from "bullmq";
|
||||||
import { and, desc, eq, or } from "drizzle-orm";
|
import { and, desc, eq, or } from "drizzle-orm";
|
||||||
import { DATABASE, type Database } from "../database/database.provider";
|
import { DATABASE, type Database } from "../database/database.provider";
|
||||||
import {
|
import {
|
||||||
@@ -20,16 +20,16 @@ import {
|
|||||||
userVehicles,
|
userVehicles,
|
||||||
vehicles,
|
vehicles,
|
||||||
} from "../database/schema/core";
|
} from "../database/schema/core";
|
||||||
import type { CorgiService } from "../integrations/corgi/corgi.service";
|
import { CorgiService } from "../integrations/corgi/corgi.service";
|
||||||
import type { EmexService } from "../integrations/emex/emex.service";
|
import { EmexService } from "../integrations/emex/emex.service";
|
||||||
import type { EmexCandidate } from "../integrations/emex/emex.service";
|
import { EmexCandidate } from "../integrations/emex/emex.service";
|
||||||
import type { PartsCatalogsService } from "../integrations/parts-catalogs/parts-catalogs.service";
|
import { PartsCatalogsService } from "../integrations/parts-catalogs/parts-catalogs.service";
|
||||||
import type { PcatCar } from "../integrations/parts-catalogs/parts-catalogs.types";
|
import { PcatCar } from "../integrations/parts-catalogs/parts-catalogs.types";
|
||||||
import type { PL24Service } from "../integrations/pl24/pl24.service";
|
import { PL24Service } from "../integrations/pl24/pl24.service";
|
||||||
import type { VinApiService } from "../integrations/vin-api/vin-api.service";
|
import { VinApiService } from "../integrations/vin-api/vin-api.service";
|
||||||
import type { PrefetchSource } from "../jobs/prefetch.types";
|
import { PrefetchSource } from "../jobs/prefetch.types";
|
||||||
import { CATALOG_PREFETCH_QUEUE } from "../jobs/queues/catalog-prefetch.queue";
|
import { CATALOG_PREFETCH_QUEUE } from "../jobs/queues/catalog-prefetch.queue";
|
||||||
import type { RedisService } from "../redis/redis.service";
|
import { RedisService } from "../redis/redis.service";
|
||||||
|
|
||||||
interface VinResolveResult {
|
interface VinResolveResult {
|
||||||
brandName: string | null;
|
brandName: string | null;
|
||||||
|
|||||||
Reference in New Issue
Block a user