mirror of
https://github.com/gromlab-ru/slm-design.git
synced 2026-08-22 07:30:16 +03:00
218 lines
6.2 KiB
TypeScript
218 lines
6.2 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Get,
|
|
Headers,
|
|
Param,
|
|
Patch,
|
|
Post,
|
|
Query,
|
|
Req,
|
|
Res,
|
|
UseGuards,
|
|
} from "@nestjs/common";
|
|
import {
|
|
ApiCookieAuth,
|
|
ApiCreatedResponse,
|
|
ApiNotModifiedResponse,
|
|
ApiOkResponse,
|
|
ApiOperation,
|
|
ApiSecurity,
|
|
ApiTags,
|
|
} from "@nestjs/swagger";
|
|
import type { Response } from "express";
|
|
import {
|
|
ApiDemoScenarioHeader,
|
|
ApiOrganizationHeader,
|
|
ApiStandardErrors,
|
|
} from "../../../common/api.decorators";
|
|
import {
|
|
ComplexCookieGuard,
|
|
ComplexCsrfGuard,
|
|
type ComplexDemoRequest,
|
|
ComplexOrganizationGuard,
|
|
ComplexRoles,
|
|
ComplexRolesGuard,
|
|
} from "../complex.auth";
|
|
import { ComplexStore } from "../complex.store";
|
|
import {
|
|
AdjustInventoryDto,
|
|
BrandsResponseDto,
|
|
ComplexCategoriesResponseDto,
|
|
ComplexProductResponseDto,
|
|
ComplexProductsResponseDto,
|
|
CreateComplexProductDto,
|
|
CursorProductQueryDto,
|
|
InventoryItemResponseDto,
|
|
InventoryResponseDto,
|
|
UpdateComplexProductDto,
|
|
WarehousesResponseDto,
|
|
} from "../dto/catalog.dto";
|
|
import { ComplexRole } from "../dto/identity.dto";
|
|
|
|
@ApiTags("Products")
|
|
@ApiCookieAuth("cookieSession")
|
|
@ApiOrganizationHeader()
|
|
@ApiDemoScenarioHeader()
|
|
@UseGuards(ComplexCookieGuard, ComplexOrganizationGuard)
|
|
@Controller("products")
|
|
export class ComplexProductsController {
|
|
constructor(private readonly store: ComplexStore) {}
|
|
|
|
@Get()
|
|
@ApiOperation({ summary: "List tenant products using cursor pagination" })
|
|
@ApiOkResponse({ type: ComplexProductsResponseDto })
|
|
@ApiStandardErrors({ auth: true })
|
|
list(
|
|
@Req() request: ComplexDemoRequest,
|
|
@Query() query: CursorProductQueryDto,
|
|
): ComplexProductsResponseDto {
|
|
return this.store.listProducts(request.organizationId!, query);
|
|
}
|
|
|
|
@Get(":id")
|
|
@ApiOperation({
|
|
summary: "Get a rich product with variants and ETag support",
|
|
})
|
|
@ApiOkResponse({ type: ComplexProductResponseDto })
|
|
@ApiNotModifiedResponse()
|
|
@ApiStandardErrors({ auth: true, notFound: true })
|
|
get(
|
|
@Req() request: ComplexDemoRequest,
|
|
@Param("id") id: string,
|
|
@Headers("if-none-match") ifNoneMatch: string | undefined,
|
|
@Res({ passthrough: true }) response: Response,
|
|
): ComplexProductResponseDto | undefined {
|
|
const product = this.store.getProduct(request.organizationId!, id);
|
|
const etag = `W/\"${product.id}-v${product.version}\"`;
|
|
response.setHeader("ETag", etag);
|
|
response.setHeader("Cache-Control", "private, max-age=0, must-revalidate");
|
|
if (etag === ifNoneMatch) {
|
|
response.status(304);
|
|
return undefined;
|
|
}
|
|
return { data: product };
|
|
}
|
|
|
|
@Post()
|
|
@UseGuards(ComplexRolesGuard, ComplexCsrfGuard)
|
|
@ComplexRoles(ComplexRole.Admin, ComplexRole.Manager)
|
|
@ApiSecurity("csrf")
|
|
@ApiOperation({ summary: "Create a tenant product with variants" })
|
|
@ApiCreatedResponse({ type: ComplexProductResponseDto })
|
|
@ApiStandardErrors({ auth: true })
|
|
create(
|
|
@Req() request: ComplexDemoRequest,
|
|
@Body() dto: CreateComplexProductDto,
|
|
): ComplexProductResponseDto {
|
|
return {
|
|
data: this.store.createProduct(
|
|
request.organizationId!,
|
|
request.user!.id,
|
|
dto,
|
|
),
|
|
};
|
|
}
|
|
|
|
@Patch(":id")
|
|
@UseGuards(ComplexRolesGuard, ComplexCsrfGuard)
|
|
@ComplexRoles(ComplexRole.Admin, ComplexRole.Manager)
|
|
@ApiSecurity("csrf")
|
|
@ApiOperation({ summary: "Update a product using optimistic locking" })
|
|
@ApiOkResponse({ type: ComplexProductResponseDto })
|
|
@ApiStandardErrors({ auth: true, notFound: true, conflict: true })
|
|
update(
|
|
@Req() request: ComplexDemoRequest,
|
|
@Param("id") id: string,
|
|
@Body() dto: UpdateComplexProductDto,
|
|
): ComplexProductResponseDto {
|
|
return {
|
|
data: this.store.updateProduct(
|
|
request.organizationId!,
|
|
request.user!.id,
|
|
id,
|
|
dto,
|
|
),
|
|
};
|
|
}
|
|
}
|
|
|
|
@ApiTags("Catalog")
|
|
@ApiCookieAuth("cookieSession")
|
|
@ApiOrganizationHeader()
|
|
@ApiDemoScenarioHeader()
|
|
@UseGuards(ComplexCookieGuard, ComplexOrganizationGuard)
|
|
@Controller()
|
|
export class ComplexCatalogController {
|
|
constructor(private readonly store: ComplexStore) {}
|
|
|
|
@Get("categories")
|
|
@ApiOperation({ summary: "List a recursive category tree" })
|
|
@ApiOkResponse({ type: ComplexCategoriesResponseDto })
|
|
@ApiStandardErrors({ auth: true })
|
|
categories(@Req() request: ComplexDemoRequest): ComplexCategoriesResponseDto {
|
|
return { data: this.store.listCategories(request.organizationId!) };
|
|
}
|
|
|
|
@Get("brands")
|
|
@ApiOperation({ summary: "List product brands" })
|
|
@ApiOkResponse({ type: BrandsResponseDto })
|
|
@ApiStandardErrors({ auth: true })
|
|
brands(@Req() request: ComplexDemoRequest): BrandsResponseDto {
|
|
return { data: this.store.listBrands(request.organizationId!) };
|
|
}
|
|
}
|
|
|
|
@ApiTags("Inventory")
|
|
@ApiCookieAuth("cookieSession")
|
|
@ApiOrganizationHeader()
|
|
@ApiDemoScenarioHeader()
|
|
@UseGuards(ComplexCookieGuard, ComplexOrganizationGuard)
|
|
@Controller()
|
|
export class ComplexInventoryController {
|
|
constructor(private readonly store: ComplexStore) {}
|
|
|
|
@Get("warehouses")
|
|
@ApiOperation({ summary: "List tenant warehouses" })
|
|
@ApiOkResponse({ type: WarehousesResponseDto })
|
|
@ApiStandardErrors({ auth: true })
|
|
warehouses(@Req() request: ComplexDemoRequest): WarehousesResponseDto {
|
|
return { data: this.store.listWarehouses(request.organizationId!) };
|
|
}
|
|
|
|
@Get("inventory")
|
|
@ApiOperation({ summary: "List inventory with optional product filter" })
|
|
@ApiOkResponse({ type: InventoryResponseDto })
|
|
@ApiStandardErrors({ auth: true })
|
|
inventory(
|
|
@Req() request: ComplexDemoRequest,
|
|
@Query("productId") productId?: string,
|
|
): InventoryResponseDto {
|
|
return {
|
|
data: this.store.listInventory(request.organizationId!, productId),
|
|
};
|
|
}
|
|
|
|
@Post("inventory/:id/adjust")
|
|
@UseGuards(ComplexRolesGuard, ComplexCsrfGuard)
|
|
@ComplexRoles(ComplexRole.Admin, ComplexRole.Manager)
|
|
@ApiSecurity("csrf")
|
|
@ApiOperation({ summary: "Adjust stock using optimistic locking" })
|
|
@ApiOkResponse({ type: InventoryItemResponseDto })
|
|
@ApiStandardErrors({ auth: true, notFound: true, conflict: true })
|
|
adjust(
|
|
@Req() request: ComplexDemoRequest,
|
|
@Param("id") id: string,
|
|
@Body() dto: AdjustInventoryDto,
|
|
): InventoryItemResponseDto {
|
|
return {
|
|
data: this.store.adjustInventory(
|
|
request.organizationId!,
|
|
request.user!.id,
|
|
id,
|
|
dto,
|
|
),
|
|
};
|
|
}
|
|
}
|