226 lines
5.9 KiB
TypeScript
226 lines
5.9 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Delete,
|
|
Get,
|
|
HttpCode,
|
|
Param,
|
|
ParseIntPipe,
|
|
Patch,
|
|
Post,
|
|
Put,
|
|
Query,
|
|
Request,
|
|
UseGuards,
|
|
} from '@nestjs/common';
|
|
import { Throttle } from '@nestjs/throttler';
|
|
import { Public } from '../auth/decorators/public.decorator';
|
|
import { CreateProductDto } from './dto/create-product.dto';
|
|
import { UpdateProductDto } from './dto/update-product.dto';
|
|
import { ProductsService } from './products.service';
|
|
import { MergeProductsDto } from './dto/merge-products.dto';
|
|
import { UpdateCanonicalNameDto } from './dto/update-canonical-name.dto';
|
|
import { SetTagsDto } from './dto/set-tags.dto';
|
|
import { UpsertNutritionDto } from './dto/upsert-nutrition.dto';
|
|
import { BulkUpdateProductsDto } from './dto/bulk-update-products.dto';
|
|
import { AiCategorizeBulkDto } from './dto/ai-categorize-bulk.dto';
|
|
import { SetProductStatusDto } from './dto/set-product-status.dto';
|
|
import { Roles } from '../auth/decorators/roles.decorator';
|
|
import { AiService } from '../ai/ai.service';
|
|
import { CategoriesService } from '../categories/categories.service';
|
|
import { PremiumOrAdminGuard } from '../auth/premium-or-admin.guard';
|
|
|
|
@Controller('products')
|
|
export class ProductsController {
|
|
constructor(
|
|
private readonly productsService: ProductsService,
|
|
private readonly aiService: AiService,
|
|
private readonly categoriesService: CategoriesService,
|
|
) {}
|
|
|
|
@Public()
|
|
@Get()
|
|
findAll(
|
|
@Query('tag') tag?: string,
|
|
@Query('subcategory') subcategory?: string,
|
|
) {
|
|
return this.productsService.findAll({ tag, subcategory });
|
|
}
|
|
|
|
@Public()
|
|
@Get('tags')
|
|
findAllTags() {
|
|
return this.productsService.findAllTags();
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Get('duplicates')
|
|
findDuplicates() {
|
|
return this.productsService.findDuplicateCandidates();
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Get('merge-preview')
|
|
previewMerge(
|
|
@Query('sourceProductId', ParseIntPipe) sourceProductId: number,
|
|
@Query('targetProductId', ParseIntPipe) targetProductId: number,
|
|
) {
|
|
return this.productsService.previewMerge(sourceProductId, targetProductId);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Post('backfill-canonical')
|
|
backfillCanonical() {
|
|
return this.productsService.backfillCanonicalNames();
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Get('pending')
|
|
findPending() {
|
|
return this.productsService.findPending();
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Post('ai-categorize-bulk')
|
|
@Throttle({ default: { ttl: 60_000, limit: 5 } })
|
|
@HttpCode(200)
|
|
aiCategorizeBulk(@Body() body: AiCategorizeBulkDto) {
|
|
return this.productsService.aiCategorizeBulk(body.productIds);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Get('deleted')
|
|
findDeleted() {
|
|
return this.productsService.findDeleted();
|
|
}
|
|
|
|
// Inloggad användares egna privata produkter (måste vara före :id)
|
|
@Get('mine')
|
|
findMine(@Request() req: { user: { id: number } }) {
|
|
return this.productsService.findByOwner(req.user.id);
|
|
}
|
|
|
|
// Tillgänglig för alla inloggade användare
|
|
@Get(':id')
|
|
findOne(@Param('id', ParseIntPipe) id: number) {
|
|
return this.productsService.findOne(id);
|
|
}
|
|
|
|
// Skapa en privat produkt för den inloggade användaren
|
|
@Post('private')
|
|
createPrivate(
|
|
@Body() body: CreateProductDto,
|
|
@Request() req: { user: { id: number } },
|
|
) {
|
|
return this.productsService.createPrivate(body, req.user.id);
|
|
}
|
|
|
|
@UseGuards(PremiumOrAdminGuard)
|
|
@Get(':id/suggest-category')
|
|
@Throttle({ default: { ttl: 60_000, limit: 20 } })
|
|
async suggestCategory(
|
|
@Param('id', ParseIntPipe) id: number,
|
|
) {
|
|
const product = await this.productsService.findOne(id);
|
|
const categories = await this.categoriesService.findFlattened();
|
|
return this.aiService.suggestCategory(product.canonicalName ?? product.name, categories);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Post()
|
|
create(@Body() body: CreateProductDto) {
|
|
return this.productsService.create(body);
|
|
}
|
|
|
|
// Tillgänglig för alla inloggade användare — req.user.id injiceras av JWT-guard
|
|
@Post('pending')
|
|
createPending(
|
|
@Body() body: CreateProductDto,
|
|
@Request() req: { user: { id: number } },
|
|
) {
|
|
return this.productsService.createPending(body, req.user.id);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Post('merge')
|
|
merge(@Body() body: MergeProductsDto) {
|
|
return this.productsService.merge(body.sourceProductId, body.targetProductId);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Patch(':id/canonical-name')
|
|
updateCanonicalName(
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Body() body: UpdateCanonicalNameDto,
|
|
) {
|
|
return this.productsService.updateCanonicalName(id, body.canonicalName);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Put(':id/tags')
|
|
setTags(
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Body() body: SetTagsDto,
|
|
) {
|
|
return this.productsService.setTags(id, body.tags);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Put(':id/nutrition')
|
|
upsertNutrition(
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Body() body: UpsertNutritionDto,
|
|
) {
|
|
return this.productsService.upsertNutrition(id, body);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Patch(':id')
|
|
update(
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Body() body: UpdateProductDto,
|
|
) {
|
|
return this.productsService.update(id, body);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Delete(':id/permanent')
|
|
permanentDelete(@Param('id', ParseIntPipe) id: number) {
|
|
return this.productsService.permanentDelete(id);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Delete(':id')
|
|
remove(@Param('id', ParseIntPipe) id: number) {
|
|
return this.productsService.remove(id);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Patch(':id/status')
|
|
setStatus(
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Body() body: SetProductStatusDto,
|
|
) {
|
|
return this.productsService.setStatus(id, body.status);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Post(':id/restore')
|
|
restore(@Param('id', ParseIntPipe) id: number) {
|
|
return this.productsService.restore(id);
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Post('reset-all')
|
|
@HttpCode(200)
|
|
resetAll() {
|
|
return this.productsService.resetAll();
|
|
}
|
|
|
|
@Roles('admin')
|
|
@Post('bulk-update')
|
|
@HttpCode(200)
|
|
bulkUpdate(@Body() body: BulkUpdateProductsDto) {
|
|
return this.productsService.bulkUpdate(body.ids, { categoryId: body.categoryId });
|
|
}
|
|
} |