edit image business

This commit is contained in:
2026-06-13 12:39:35 +03:30
parent 4e33d3f8e1
commit 1d685b3338
3 changed files with 145 additions and 11 deletions
@@ -92,6 +92,19 @@ export class BusinessImagesService {
return saved;
}
async setCover(businessId: string, imageId: string): Promise<BusinessImageEntity> {
const image = await this.imageRepo.findOne({
where: { id: imageId, businessId },
});
if (!image) {
throw new NotFoundException('Image not found');
}
await this.imageRepo.update({ businessId }, { isCover: false });
image.isCover = true;
return this.imageRepo.save(image);
}
async remove(businessId: string, imageId: string): Promise<void> {
const image = await this.imageRepo.findOne({
where: { id: imageId, businessId },
@@ -22,7 +22,6 @@ import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { Roles } from '../auth/decorators/roles.decorator';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { BusinessImagesService } from './business-images.service';
import { BusinessesService } from './businesses.service';
import { AssignBusinessOwnerDto } from './dto/assign-business-owner.dto';
import { CreateBusinessDto } from './dto/create-business.dto';
@@ -31,10 +30,7 @@ import { UpdateBusinessDto } from './dto/update-business.dto';
@Controller('businesses')
export class BusinessesController {
constructor(
private readonly service: BusinessesService,
private readonly images: BusinessImagesService,
) {}
constructor(private readonly service: BusinessesService) {}
@Get()
findAll(
@@ -57,6 +53,43 @@ export class BusinessesController {
return this.service.findMine(user.id);
}
@Post('mine/images')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(UserRole.BUSINESS_OWNER)
@UseInterceptors(
FilesInterceptor('files', 10, {
storage: memoryStorage(),
limits: { fileSize: 5 * 1024 * 1024 },
}),
)
uploadMyImages(
@UploadedFiles() files: Express.Multer.File[],
@CurrentUser() user: AuthUser,
): Promise<BusinessImageEntity[]> {
return this.service.uploadMyImages(files, user);
}
@Delete('mine/images/:imageId')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(UserRole.BUSINESS_OWNER)
async removeMyImage(
@Param('imageId') imageId: string,
@CurrentUser() user: AuthUser,
): Promise<{ success: true }> {
await this.service.removeMyImage(imageId, user);
return { success: true };
}
@Patch('mine/images/:imageId/cover')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(UserRole.BUSINESS_OWNER)
setMyCoverImage(
@Param('imageId') imageId: string,
@CurrentUser() user: AuthUser,
): Promise<BusinessImageEntity> {
return this.service.setMyCoverImage(imageId, user);
}
@Get(':id/availability')
getAvailability(@Param('id') id: string) {
return this.service.getAvailability(id);
@@ -87,7 +120,7 @@ export class BusinessesController {
@Post(':id/images')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(UserRole.ADMIN)
@Roles(UserRole.ADMIN, UserRole.BUSINESS_OWNER)
@UseInterceptors(
FilesInterceptor('files', 10, {
storage: memoryStorage(),
@@ -97,18 +130,20 @@ export class BusinessesController {
uploadImages(
@Param('id') id: string,
@UploadedFiles() files: Express.Multer.File[],
@CurrentUser() user: AuthUser,
): Promise<BusinessImageEntity[]> {
return this.images.upload(id, files);
return this.service.uploadImages(id, files, user);
}
@Delete(':id/images/:imageId')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(UserRole.ADMIN)
@Roles(UserRole.ADMIN, UserRole.BUSINESS_OWNER)
async removeImage(
@Param('id') id: string,
@Param('imageId') imageId: string,
@CurrentUser() user: AuthUser,
): Promise<{ success: true }> {
await this.images.remove(id, imageId);
await this.service.removeImage(id, imageId, user);
return { success: true };
}
+88 -2
View File
@@ -70,10 +70,12 @@ export class BusinessesService {
async findMine(ownerId: string): Promise<BusinessEntity[]> {
const businesses = await this.repo.find({
where: { ownerId },
relations: { category: true },
relations: { category: true, images: true },
order: { createdAt: 'DESC' },
});
await this.attachCoverImages(businesses);
for (const business of businesses) {
business.images = this.sortImages(business.images ?? []);
}
return businesses;
}
@@ -85,6 +87,7 @@ export class BusinessesService {
if (!business) {
throw new NotFoundException('Business not found');
}
business.images = this.sortImages(business.images ?? []);
return business;
}
@@ -282,6 +285,89 @@ export class BusinessesService {
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
}
async uploadImages(
id: string,
files: Express.Multer.File[],
actor: AuthUser,
): Promise<BusinessImageEntity[]> {
const business = await this.findOne(id);
this.assertCanManageBusiness(business, actor);
return this.images.upload(id, files);
}
async uploadMyImages(
files: Express.Multer.File[],
actor: AuthUser,
): Promise<BusinessImageEntity[]> {
const business = await this.requireOwnedBusiness(actor);
return this.images.upload(business.id, files);
}
async removeImage(
id: string,
imageId: string,
actor: AuthUser,
): Promise<void> {
const business = await this.findOne(id);
this.assertCanManageBusiness(business, actor);
await this.images.remove(id, imageId);
}
async removeMyImage(imageId: string, actor: AuthUser): Promise<void> {
const business = await this.requireOwnedBusiness(actor);
await this.images.remove(business.id, imageId);
}
async setMyCoverImage(
imageId: string,
actor: AuthUser,
): Promise<BusinessImageEntity> {
const business = await this.requireOwnedBusiness(actor);
return this.images.setCover(business.id, imageId);
}
private async requireOwnedBusiness(actor: AuthUser): Promise<BusinessEntity> {
if (actor.role === UserRole.ADMIN) {
throw new ForbiddenException('Use admin image endpoints for this account');
}
if (actor.role !== UserRole.BUSINESS_OWNER) {
throw new ForbiddenException('Insufficient role');
}
const business = await this.repo.findOne({
where: { ownerId: actor.id },
order: { createdAt: 'DESC' },
});
if (!business) {
throw new NotFoundException('No business linked to your account');
}
return business;
}
private sortImages(images: BusinessImageEntity[]): BusinessImageEntity[] {
return [...images].sort((a, b) => {
if (a.isCover !== b.isCover) {
return a.isCover ? -1 : 1;
}
return a.sortOrder - b.sortOrder;
});
}
private assertCanManageBusiness(
business: BusinessEntity,
actor?: AuthUser,
): void {
if (!actor || actor.role === UserRole.ADMIN) {
return;
}
if (
actor.role !== UserRole.BUSINESS_OWNER ||
business.ownerId !== actor.id
) {
throw new ForbiddenException('You can only manage your own business');
}
}
private assertCanManageAvailability(
business: BusinessEntity,
actor?: AuthUser,