From e9b56d0a60bcd503a6cacf8c8156494b44429b1e Mon Sep 17 00:00:00 2001 From: Julien Constant Date: Tue, 31 Oct 2023 19:45:34 +0100 Subject: [PATCH 1/7] feat(files): add `files/:id/data` endpoint --- src/exported | 2 +- src/modules/files/files.controller.ts | 31 ++++++ src/modules/files/files.module.ts | 2 + src/modules/files/files.service.ts | 13 ++- tests/e2e/files.e2e-spec.ts | 134 ++++++++++++++++++++++++++ 5 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 src/modules/files/files.controller.ts create mode 100644 tests/e2e/files.e2e-spec.ts diff --git a/src/exported b/src/exported index e2ed2db..5d5f482 160000 --- a/src/exported +++ b/src/exported @@ -1 +1 @@ -Subproject commit e2ed2db07067b350fb8847d72dbcde53c9d89136 +Subproject commit 5d5f482951956602535fde7e5498fc6502b18731 diff --git a/src/modules/files/files.controller.ts b/src/modules/files/files.controller.ts new file mode 100644 index 0000000..b723bf5 --- /dev/null +++ b/src/modules/files/files.controller.ts @@ -0,0 +1,31 @@ +import type { RequestWithUser } from '#types/api'; + +import { Controller, Get, Param, Req, UnauthorizedException, UseGuards } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { z } from 'zod'; + +import { TranslateService } from '@modules/translate/translate.service'; +import { User } from '@modules/users/entities/user.entity'; +import { validate } from '@utils/validate'; + +import { FilesService } from './files.service'; + +@ApiTags('Files') +@Controller('files') +@UseGuards(AuthGuard('jwt')) +@ApiBearerAuth() +export class FilesController { + constructor(private readonly filesService: FilesService, private readonly t: TranslateService) {} + + @Get(':id/data') + async getFile(@Req() req: RequestWithUser, @Param('id') id: number) { + validate(z.coerce.number().int().min(1), id, this.t.Errors.Id.Invalid('File', id)); + + const file = await this.filesService.findOne(id); + await file.visibility?.init(); + + if (await this.filesService.canReadFile(file, req.user as User)) return file; + throw new UnauthorizedException(this.t.Errors.File.Unauthorized(file.visibility?.name)); + } +} diff --git a/src/modules/files/files.module.ts b/src/modules/files/files.module.ts index 07bd5d4..28e2166 100644 --- a/src/modules/files/files.module.ts +++ b/src/modules/files/files.module.ts @@ -5,12 +5,14 @@ import { EmailsService } from '@modules/emails/emails.service'; import { UsersDataService } from '@modules/users/services/users-data.service'; import { FileVisibilityGroup } from './entities/file-visibility.entity'; +import { FilesController } from './files.controller'; import { FilesService } from './files.service'; import { ImagesService } from './images.service'; @Module({ imports: [MikroOrmModule.forFeature([FileVisibilityGroup])], providers: [EmailsService, FilesService, ImagesService, UsersDataService], + controllers: [FilesController], exports: [FilesService], }) export class FilesModule {} diff --git a/src/modules/files/files.service.ts b/src/modules/files/files.service.ts index 62044d6..6d46b31 100644 --- a/src/modules/files/files.service.ts +++ b/src/modules/files/files.service.ts @@ -3,7 +3,7 @@ import { accessSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs'; import { join } from 'path'; import { Readable } from 'stream'; -import { MikroORM, CreateRequestContext } from '@mikro-orm/core'; +import { MikroORM, CreateRequestContext, Loaded } from '@mikro-orm/core'; import { Injectable, StreamableFile } from '@nestjs/common'; import { fromBuffer, MimeType } from 'file-type'; @@ -12,7 +12,7 @@ import { User } from '@modules/users/entities/user.entity'; import { UsersDataService } from '@modules/users/services/users-data.service'; import { FileVisibilityGroup } from './entities/file-visibility.entity'; -import { File } from './entities/file.entity'; +import { File, FileKind } from './entities/file.entity'; export type WriteFileOptions = { directory: string; @@ -39,6 +39,13 @@ export class FilesService { return new StreamableFile(this.toReadable(file)); } + async findOne(id: number): Promise { + const file = (await this.orm.em.findOne(File, { id })) as unknown as Loaded; + if (!file) throw new i18nNotFoundException('validations.file.invalid.not_found', { id }); + + return file; + } + /** * Determine if the given user can read the given file. * @param {File} file - The file to check the visibility of. @@ -135,7 +142,7 @@ export class FilesService { * @param {File} file The file to delete * @param {boolean} silent If true, the function will not throw an error if the file doesn't exist */ - deleteFromDisk(file: File, silent: boolean = true) { + deleteFromDisk(file: File, silent: boolean = true) { try { accessSync(file.path); } catch { diff --git a/tests/e2e/files.e2e-spec.ts b/tests/e2e/files.e2e-spec.ts new file mode 100644 index 0000000..a74d444 --- /dev/null +++ b/tests/e2e/files.e2e-spec.ts @@ -0,0 +1,134 @@ +import request from 'supertest'; + +import { TokenDTO } from '@modules/auth/dto/token.dto'; +import { FileVisibilityGroup } from '@modules/files/entities/file-visibility.entity'; +import { File } from '@modules/files/entities/file.entity'; +import { UserPicture } from '@modules/users/entities/user-picture.entity'; + +import { orm, server, t } from '..'; + +describe('Files (e2e)', () => { + let em: typeof orm.em; + let file: File; + + let tokenVerified: string; + + let tokenSubscriber: string; + let userIdSubscriber: number; + + beforeAll(async () => { + type res = Omit & { body: TokenDTO }; + + em = orm.em.fork(); + + const responseA: res = await request(server).post('/auth/login').send({ + email: 'promos@email.com', + password: 'root', + }); + + tokenVerified = responseA.body.token; + + const responseB: res = await request(server).post('/auth/login').send({ + email: 'subscriber@email.com', + password: 'root', + }); + + tokenSubscriber = responseB.body.token; + userIdSubscriber = responseB.body.user_id; + + const visibility = await em.findOne(FileVisibilityGroup, { name: 'SUBSCRIBER' }); + file = em.create(UserPicture, { + filename: 'test.png', + mimetype: 'image/png', + path: 'test.png', + size: 123, + visibility, + description: 'foo bar', + picture_user: userIdSubscriber, + }); + + await em.persistAndFlush(file); + }); + + afterAll(async () => { + await em.removeAndFlush(file); + }); + + describe('(GET) /files/:id/data', () => { + describe('400 : Bad Request', () => { + it('when id is not valid', async () => { + const res = await request(server) + .get(`/files/invalid/data`) + .set('Authorization', `Bearer ${tokenSubscriber}`) + .expect(400); + + expect(res.body).toEqual({ + error: 'Bad Request', + statusCode: 400, + message: t.Errors.Id.Invalid('File', 'invalid'), + }); + }); + }); + + describe('401 : Unauthorized', () => { + it('when user is not logged in', async () => { + const res = await request(server).get(`/files/${file.id}/data`).expect(401); + + expect(res.body).toEqual({ + message: 'Unauthorized', + statusCode: 401, + }); + }); + + it('when user is not in the visibility group', async () => { + const res = await request(server) + .get(`/files/${file.id}/data`) + .set('Authorization', `Bearer ${tokenVerified}`) + .expect(401); + + expect(res.body).toEqual({ + error: 'Unauthorized', + message: t.Errors.File.Unauthorized(file.visibility?.name), + statusCode: 401, + }); + }); + }); + + describe('404 : Not Found', () => { + it('when file is not found', async () => { + const res = await request(server) + .get(`/files/9999/data`) + .set('Authorization', `Bearer ${tokenSubscriber}`) + .expect(404); + + expect(res.body).toEqual({ + error: 'Not Found', + statusCode: 404, + message: t.Errors.Id.NotFound('File', 9999), + }); + }); + }); + + describe('200 : Ok', () => { + it('when file is found', async () => { + const res = await request(server) + .get(`/files/${file.id}/data`) + .set('Authorization', `Bearer ${tokenSubscriber}`) + .expect(200); + + expect(res.body).toEqual({ + id: file.id, + filename: 'test.png', + mimetype: 'image/png', + path: 'test.png', + size: 123, + visibility: file.visibility.id, + description: 'foo bar', + picture_user: userIdSubscriber, + created: expect.any(String), + updated: expect.any(String), + }); + }); + }); + }); +}); From 2d38980ccc141f0a4aa9bf76a88b2b0f4328ad17 Mon Sep 17 00:00:00 2001 From: Julien Constant Date: Wed, 8 Nov 2023 15:45:29 +0100 Subject: [PATCH 2/7] fix(core): use `toObject` to hide owner field --- src/modules/files/files.controller.ts | 2 +- tests/e2e/files.e2e-spec.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/files/files.controller.ts b/src/modules/files/files.controller.ts index b723bf5..483fe5b 100644 --- a/src/modules/files/files.controller.ts +++ b/src/modules/files/files.controller.ts @@ -25,7 +25,7 @@ export class FilesController { const file = await this.filesService.findOne(id); await file.visibility?.init(); - if (await this.filesService.canReadFile(file, req.user as User)) return file; + if (await this.filesService.canReadFile(file, req.user as User)) return file.toObject(); throw new UnauthorizedException(this.t.Errors.File.Unauthorized(file.visibility?.name)); } } diff --git a/tests/e2e/files.e2e-spec.ts b/tests/e2e/files.e2e-spec.ts index a74d444..68eb73b 100644 --- a/tests/e2e/files.e2e-spec.ts +++ b/tests/e2e/files.e2e-spec.ts @@ -120,7 +120,6 @@ describe('Files (e2e)', () => { id: file.id, filename: 'test.png', mimetype: 'image/png', - path: 'test.png', size: 123, visibility: file.visibility.id, description: 'foo bar', From f2005ee10ee18518337e2b52098511afb7e469d2 Mon Sep 17 00:00:00 2001 From: Julien Constant Date: Thu, 9 Nov 2023 18:33:37 +0100 Subject: [PATCH 3/7] fix(rebase): fixes after rebase & add some Swagger doc --- src/exported | 2 +- src/i18n/en-US/validations.json | 5 ++- src/modules/files/dto/output.dto.ts | 3 ++ src/modules/files/files.controller.ts | 35 ++++++++++--------- src/modules/files/files.service.ts | 16 +++++++-- src/modules/promotions/dto/output.dto.ts | 8 +---- .../entities/promotion-picture.entity.ts | 4 +-- .../promotions/promotions.controller.ts | 8 +++-- src/modules/promotions/promotions.service.ts | 7 ++-- .../controllers/users-files.controller.ts | 10 +++--- src/modules/users/dto/output.dto.ts | 19 +--------- .../users/entities/user-banner.entity.ts | 4 +-- .../users/entities/user-picture.entity.ts | 4 +-- .../users/services/users-files.service.ts | 10 +++--- tests/e2e/files.e2e-spec.ts | 28 +++++++-------- tests/e2e/promotions.e2e-spec.ts | 10 ++++-- tests/e2e/users/users-files.e2e-spec.ts | 30 ++++++++++++---- tests/units/services/files.test.ts | 8 +++-- 18 files changed, 119 insertions(+), 92 deletions(-) diff --git a/src/exported b/src/exported index 5d5f482..cc12a9c 160000 --- a/src/exported +++ b/src/exported @@ -1 +1 @@ -Subproject commit 5d5f482951956602535fde7e5498fc6502b18731 +Subproject commit cc12a9ce00080049e04eea3e99054cf69252144f diff --git a/src/i18n/en-US/validations.json b/src/i18n/en-US/validations.json index b155c97..bf40953 100644 --- a/src/i18n/en-US/validations.json +++ b/src/i18n/en-US/validations.json @@ -37,7 +37,10 @@ "file": { "invalid": { "not_provided": "File not provided", - "not_found": "File '{filename}' cannot be found on disk", + "not_found": { + "on_disk": "File '{filename}' cannot be found on disk", + "by_id": "File with ID '{id}' not found" + }, "no_mime_type": "File has no mime type", "unauthorized_mime_type": "File has an unauthorized mime type, valid mime types are ['{mime_types}']", "infected": "File is infected with virus" diff --git a/src/modules/files/dto/output.dto.ts b/src/modules/files/dto/output.dto.ts index e8d2157..21e2f96 100644 --- a/src/modules/files/dto/output.dto.ts +++ b/src/modules/files/dto/output.dto.ts @@ -31,6 +31,9 @@ export class OutputFileDTO implements OutputFileDto { @ApiProperty() description?: string; + + @ApiProperty() + owner: { kind: 'user' | 'promotion'; id: number }; } export class OutputFileVisibilityGroupDTO extends OutputBaseDTO implements OutputFileVisibilityGroupDto { diff --git a/src/modules/files/files.controller.ts b/src/modules/files/files.controller.ts index 483fe5b..79c1113 100644 --- a/src/modules/files/files.controller.ts +++ b/src/modules/files/files.controller.ts @@ -1,14 +1,12 @@ -import type { RequestWithUser } from '#types/api'; - -import { Controller, Get, Param, Req, UnauthorizedException, UseGuards } from '@nestjs/common'; +import { Controller, Get, Param, Req, UseGuards } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; -import { z } from 'zod'; +import { ApiBearerAuth, ApiOkResponse, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger'; -import { TranslateService } from '@modules/translate/translate.service'; -import { User } from '@modules/users/entities/user.entity'; -import { validate } from '@utils/validate'; +import { ApiNotOkResponses } from '@modules/base/decorators'; +import { InputIdParamDTO } from '@modules/base/dto/input.dto'; +import { Request } from '@modules/users/entities/user.entity'; +import { OutputFileDTO } from './dto/output.dto'; import { FilesService } from './files.service'; @ApiTags('Files') @@ -16,16 +14,19 @@ import { FilesService } from './files.service'; @UseGuards(AuthGuard('jwt')) @ApiBearerAuth() export class FilesController { - constructor(private readonly filesService: FilesService, private readonly t: TranslateService) {} + constructor(private readonly filesService: FilesService) {} @Get(':id/data') - async getFile(@Req() req: RequestWithUser, @Param('id') id: number) { - validate(z.coerce.number().int().min(1), id, this.t.Errors.Id.Invalid('File', id)); - - const file = await this.filesService.findOne(id); - await file.visibility?.init(); - - if (await this.filesService.canReadFile(file, req.user as User)) return file.toObject(); - throw new UnauthorizedException(this.t.Errors.File.Unauthorized(file.visibility?.name)); + @ApiOperation({ summary: 'Get file data' }) + @ApiParam({ name: 'id', description: 'The file ID' }) + @ApiOkResponse({ type: OutputFileDTO }) + @ApiNotOkResponses({ + 400: 'Invalid ID', + 401: 'Not in file visibility group', + 404: 'File not found', + }) + async getFile(@Req() req: Request, @Param() params: InputIdParamDTO): Promise { + const file = await this.filesService.findOne(params.id); + return this.filesService.getAsData(file, req.user.id); } } diff --git a/src/modules/files/files.service.ts b/src/modules/files/files.service.ts index 6d46b31..962c289 100644 --- a/src/modules/files/files.service.ts +++ b/src/modules/files/files.service.ts @@ -11,6 +11,7 @@ import { i18nBadRequestException, i18nNotFoundException, i18nUnauthorizedExcepti import { User } from '@modules/users/entities/user.entity'; import { UsersDataService } from '@modules/users/services/users-data.service'; +import { OutputFileDTO } from './dto/output.dto'; import { FileVisibilityGroup } from './entities/file-visibility.entity'; import { File, FileKind } from './entities/file.entity'; @@ -39,9 +40,18 @@ export class FilesService { return new StreamableFile(this.toReadable(file)); } + async getAsData(file: File, user_id: number): Promise { + if (!(await this.canReadFile(file, user_id))) + throw new i18nUnauthorizedException('validations.user.invalid.not_in_file_visibility_group', { + group_name: file.visibility?.name, + }); + + return file.toObject() as unknown as OutputFileDTO; + } + async findOne(id: number): Promise { const file = (await this.orm.em.findOne(File, { id })) as unknown as Loaded; - if (!file) throw new i18nNotFoundException('validations.file.invalid.not_found', { id }); + if (!file) throw new i18nNotFoundException('validations.file.invalid.not_found.by_id', { id }); return file; } @@ -147,7 +157,7 @@ export class FilesService { accessSync(file.path); } catch { if (silent) return; - throw new i18nNotFoundException('validations.file.invalid.not_found', { + throw new i18nNotFoundException('validations.file.invalid.not_found.on_disk', { filename: file.filename, }); } @@ -164,7 +174,7 @@ export class FilesService { try { accessSync(file.path); } catch { - throw new i18nNotFoundException('validations.file.invalid.not_found', { + throw new i18nNotFoundException('validations.file.invalid.not_found.on_disk', { filename: file.filename, }); } diff --git a/src/modules/promotions/dto/output.dto.ts b/src/modules/promotions/dto/output.dto.ts index 2147d5b..b388aed 100644 --- a/src/modules/promotions/dto/output.dto.ts +++ b/src/modules/promotions/dto/output.dto.ts @@ -1,9 +1,8 @@ -import type { OutputPromotionPictureDto, OutputPromotionDto } from '#types/api'; +import type { OutputPromotionDto } from '#types/api'; import { ApiProperty } from '@nestjs/swagger'; import { OutputBaseDTO } from '@modules/base/dto/output.dto'; -import { OutputFileDTO } from '@modules/files/dto/output.dto'; export class OutputPromotionDTO extends OutputBaseDTO implements OutputPromotionDto { @ApiProperty() @@ -15,8 +14,3 @@ export class OutputPromotionDTO extends OutputBaseDTO implements OutputPromotion @ApiProperty({ required: false }) picture?: number; } - -export class OutputPromotionPictureDTO extends OutputFileDTO implements OutputPromotionPictureDto { - @ApiProperty() - picture_promotion_id: number; -} diff --git a/src/modules/promotions/entities/promotion-picture.entity.ts b/src/modules/promotions/entities/promotion-picture.entity.ts index 7c968ae..873638c 100644 --- a/src/modules/promotions/entities/promotion-picture.entity.ts +++ b/src/modules/promotions/entities/promotion-picture.entity.ts @@ -9,8 +9,8 @@ export class PromotionPicture extends File { @OneToOne(() => Promotion, (promotion) => promotion.picture, { nullable: true, owner: true, - serializedName: 'picture_promotion_id', - serializer: (p: Promotion) => p.id, + serializedName: 'owner', + serializer: (p: Promotion) => ({ kind: 'promotion', id: p?.id }), }) picture_promotion: Promotion; diff --git a/src/modules/promotions/promotions.controller.ts b/src/modules/promotions/promotions.controller.ts index 57305f7..d868300 100644 --- a/src/modules/promotions/promotions.controller.ts +++ b/src/modules/promotions/promotions.controller.ts @@ -9,6 +9,7 @@ import { OutputMessageDTO } from '@modules/base/dto/output.dto'; import { i18nBadRequestException } from '@modules/base/http-errors'; import { ApiDownloadFile } from '@modules/files/decorators/download.decorator'; import { ApiUploadFile } from '@modules/files/decorators/upload.decorator'; +import { OutputFileDTO } from '@modules/files/dto/output.dto'; import { FilesService } from '@modules/files/files.service'; import { Request } from '@modules/users/entities/user.entity'; @@ -57,9 +58,12 @@ export class PromotionsController { @ApiOperation({ summary: 'Update the promotion logo' }) @ApiUploadFile() @ApiParam({ name: 'number', description: 'The promotion number (eg: 21)' }) - @ApiOkResponse({ type: OutputPromotionDTO }) + @ApiOkResponse({ type: OutputFileDTO }) @ApiNotOkResponses({ 400: 'Invalid file', 404: 'Promotion not found' }) - async editLogo(@UploadedFile() file: Express.Multer.File, @Param() params: InputPromotionNumberParamDTO) { + async editLogo( + @UploadedFile() file: Express.Multer.File, + @Param() params: InputPromotionNumberParamDTO, + ): Promise { if (!file) throw new i18nBadRequestException('validations.file.invalid.not_provided'); return this.promotionsService.updateLogo(params.number, file); diff --git a/src/modules/promotions/promotions.service.ts b/src/modules/promotions/promotions.service.ts index d5f696b..36317ea 100644 --- a/src/modules/promotions/promotions.service.ts +++ b/src/modules/promotions/promotions.service.ts @@ -7,9 +7,10 @@ import { Cron } from '@nestjs/schedule'; import { env } from '@env'; import { OutputMessageDTO } from '@modules/base/dto/output.dto'; import { i18nNotFoundException } from '@modules/base/http-errors'; +import { OutputFileDTO } from '@modules/files/dto/output.dto'; import { ImagesService } from '@modules/files/images.service'; -import { OutputPromotionPictureDTO, OutputPromotionDTO } from './dto/output.dto'; +import { OutputPromotionDTO } from './dto/output.dto'; import { PromotionPicture } from './entities/promotion-picture.entity'; import { Promotion } from './entities/promotion.entity'; import { OutputBaseUserDTO } from '../users/dto/output.dto'; @@ -90,7 +91,7 @@ export class PromotionsService { } @CreateRequestContext() - async updateLogo(number: number, file: Express.Multer.File): Promise { + async updateLogo(number: number, file: Express.Multer.File): Promise { const promotion = await this.orm.em.findOne(Promotion, { number }, { populate: ['picture'] }); if (!promotion) throw new i18nNotFoundException('validations.promotion.invalid.not_found', { number }); @@ -120,7 +121,7 @@ export class PromotionsService { }); await this.orm.em.persistAndFlush(promotion); - return promotion.picture.toObject() as unknown as OutputPromotionPictureDTO; + return promotion.picture.toObject() as unknown as OutputFileDTO; } @CreateRequestContext() diff --git a/src/modules/users/controllers/users-files.controller.ts b/src/modules/users/controllers/users-files.controller.ts index 70d9372..8d90b80 100644 --- a/src/modules/users/controllers/users-files.controller.ts +++ b/src/modules/users/controllers/users-files.controller.ts @@ -12,9 +12,9 @@ import { OutputMessageDTO } from '@modules/base/dto/output.dto'; import { i18nBadRequestException } from '@modules/base/http-errors'; import { ApiDownloadFile } from '@modules/files/decorators/download.decorator'; import { ApiUploadFile } from '@modules/files/decorators/upload.decorator'; +import { OutputFileDTO } from '@modules/files/dto/output.dto'; import { FilesService } from '@modules/files/files.service'; -import { OutputUserBannerDTO, OutputUserPictureDTO } from '../dto/output.dto'; import { Request } from '../entities/user.entity'; import { UsersFilesService } from '../services/users-files.service'; @@ -30,14 +30,14 @@ export class UsersFilesController { @GuardSelfOrPermissions('id', ['CAN_EDIT_USER']) @ApiOperation({ summary: 'Update user profile picture' }) @ApiParam({ name: 'id', description: 'The user ID' }) - @ApiOkResponse({ description: 'The updated user picture', type: OutputUserPictureDTO }) + @ApiOkResponse({ description: 'The updated user picture', type: OutputFileDTO }) @ApiNotOkResponses({ 400: 'Invalid user ID or missing uploaded file', 404: 'User not found' }) @ApiUploadFile() async editPicture( @Req() req: Request, @Param() params: InputIdParamDTO, @UploadedFile() file: Express.Multer.File, - ): Promise { + ): Promise { if (!file) throw new i18nBadRequestException('validations.file.invalid.not_provided'); return this.usersFilesService.updatePicture(req.user, params.id, file); } @@ -68,13 +68,13 @@ export class UsersFilesController { @GuardSelfOrPermissions('id', ['CAN_EDIT_USER']) @ApiOperation({ summary: 'Update user profile banner' }) @ApiParam({ name: 'id', description: 'The user ID' }) - @ApiOkResponse({ description: 'The updated user banner', type: OutputUserBannerDTO }) + @ApiOkResponse({ description: 'The updated user banner', type: OutputFileDTO }) @ApiNotOkResponses({ 400: 'Invalid user ID or missing uploaded file', 404: 'User not found' }) @ApiUploadFile() async editBanner( @Param() params: InputIdParamDTO, @UploadedFile() file: Express.Multer.File, - ): Promise { + ): Promise { if (!file) throw new i18nBadRequestException('validations.file.invalid.not_provided'); return this.usersFilesService.updateBanner(params.id, file); } diff --git a/src/modules/users/dto/output.dto.ts b/src/modules/users/dto/output.dto.ts index 6805d12..d04b573 100644 --- a/src/modules/users/dto/output.dto.ts +++ b/src/modules/users/dto/output.dto.ts @@ -1,11 +1,5 @@ import type { email } from '#types'; -import type { - OutputUserBannerDto, - OutputUserPictureDto, - OutputUserRoleDto, - OutputUserVisibilityDto, - OutputBaseUserDto, -} from '#types/api'; +import type { OutputUserRoleDto, OutputUserVisibilityDto, OutputBaseUserDto } from '#types/api'; import { ApiProperty } from '@nestjs/swagger'; @@ -13,7 +7,6 @@ import { OutputUserDto, PERMISSION_NAMES, GENDERS } from '#types/api'; import { USER_GENDER } from '@exported/api/constants/genders'; import { PERMISSIONS_NAMES } from '@exported/api/constants/perms'; import { OutputBaseDTO } from '@modules/base/dto/output.dto'; -import { OutputFileDTO } from '@modules/files/dto/output.dto'; export class OutputBaseUserDTO extends OutputBaseDTO implements OutputBaseUserDto { @ApiProperty() @@ -130,13 +123,3 @@ export class OutputUserVisibilityDTO implements OutputUserVisibilityDto { @ApiProperty({ type: Boolean, default: false }) parents_phone: boolean; } - -export class OutputUserPictureDTO extends OutputFileDTO implements OutputUserPictureDto { - @ApiProperty({ minimum: 1 }) - picture_user_id: number; -} - -export class OutputUserBannerDTO extends OutputFileDTO implements OutputUserBannerDto { - @ApiProperty({ minimum: 1 }) - banner_user_id: number; -} diff --git a/src/modules/users/entities/user-banner.entity.ts b/src/modules/users/entities/user-banner.entity.ts index f6331c7..cf3ec63 100644 --- a/src/modules/users/entities/user-banner.entity.ts +++ b/src/modules/users/entities/user-banner.entity.ts @@ -9,8 +9,8 @@ export class UserBanner extends File { @OneToOne(() => User, (user) => user.banner, { nullable: true, owner: true, - serializedName: 'banner_user_id', - serializer: (u: User) => u.id, + serializedName: 'owner', + serializer: (u: User) => ({ kind: 'user', id: u?.id }), }) banner_user: User; diff --git a/src/modules/users/entities/user-picture.entity.ts b/src/modules/users/entities/user-picture.entity.ts index d479ebd..ed89d88 100644 --- a/src/modules/users/entities/user-picture.entity.ts +++ b/src/modules/users/entities/user-picture.entity.ts @@ -9,8 +9,8 @@ export class UserPicture extends File { @OneToOne(() => User, (user) => user.picture, { nullable: true, owner: true, - serializedName: 'picture_user_id', - serializer: (u: User) => u.id, + serializedName: 'owner', + serializer: (u: User) => ({ kind: 'user', id: u?.id }), }) picture_user: User; diff --git a/src/modules/users/services/users-files.service.ts b/src/modules/users/services/users-files.service.ts index 8744462..290b605 100644 --- a/src/modules/users/services/users-files.service.ts +++ b/src/modules/users/services/users-files.service.ts @@ -6,10 +6,10 @@ import { Injectable } from '@nestjs/common'; import { env } from '@env'; import { OutputMessageDTO } from '@modules/base/dto/output.dto'; import { i18nNotFoundException, i18nUnauthorizedException } from '@modules/base/http-errors'; +import { OutputFileDTO } from '@modules/files/dto/output.dto'; import { ImagesService } from '@modules/files/images.service'; import { UsersDataService } from './users-data.service'; -import { OutputUserBannerDTO, OutputUserPictureDTO } from '../dto/output.dto'; import { UserBanner } from '../entities/user-banner.entity'; import { UserPicture } from '../entities/user-picture.entity'; import { User } from '../entities/user.entity'; @@ -30,7 +30,7 @@ export class UsersFilesService { * @returns {Promise} The updated user */ @CreateRequestContext() - async updatePicture(req_user: User, owner_id: number, file: Express.Multer.File): Promise { + async updatePicture(req_user: User, owner_id: number, file: Express.Multer.File): Promise { const user = await this.orm.em.findOne(User, { id: owner_id }, { populate: ['picture'] }); if (!user) throw new i18nNotFoundException('validations.user.not_found.id', { id: owner_id }); @@ -78,7 +78,7 @@ export class UsersFilesService { }); await this.orm.em.persistAndFlush(user); - return user.picture.toObject() as unknown as OutputUserPictureDTO; + return user.picture.toObject() as unknown as OutputFileDTO; } @CreateRequestContext() @@ -103,7 +103,7 @@ export class UsersFilesService { } @CreateRequestContext() - async updateBanner(id: number, file: Express.Multer.File): Promise { + async updateBanner(id: number, file: Express.Multer.File): Promise { const user = await this.orm.em.findOne(User, { id }, { populate: ['banner'] }); if (!user) throw new i18nNotFoundException('validations.user.not_found.id', { id }); @@ -136,7 +136,7 @@ export class UsersFilesService { }); await this.orm.em.persistAndFlush(user); - return user.banner.toObject() as unknown as OutputUserBannerDTO; + return user.banner.toObject() as unknown as OutputFileDTO; } @CreateRequestContext() diff --git a/tests/e2e/files.e2e-spec.ts b/tests/e2e/files.e2e-spec.ts index 68eb73b..c31e20c 100644 --- a/tests/e2e/files.e2e-spec.ts +++ b/tests/e2e/files.e2e-spec.ts @@ -1,11 +1,12 @@ import request from 'supertest'; -import { TokenDTO } from '@modules/auth/dto/token.dto'; +import { OutputTokenDTO } from '@modules/auth/dto/output.dto'; +import { i18nBadRequestException, i18nNotFoundException, i18nUnauthorizedException } from '@modules/base/http-errors'; import { FileVisibilityGroup } from '@modules/files/entities/file-visibility.entity'; import { File } from '@modules/files/entities/file.entity'; import { UserPicture } from '@modules/users/entities/user-picture.entity'; -import { orm, server, t } from '..'; +import { orm, server } from '..'; describe('Files (e2e)', () => { let em: typeof orm.em; @@ -17,7 +18,7 @@ describe('Files (e2e)', () => { let userIdSubscriber: number; beforeAll(async () => { - type res = Omit & { body: TokenDTO }; + type res = Omit & { body: OutputTokenDTO }; em = orm.em.fork(); @@ -63,9 +64,7 @@ describe('Files (e2e)', () => { .expect(400); expect(res.body).toEqual({ - error: 'Bad Request', - statusCode: 400, - message: t.Errors.Id.Invalid('File', 'invalid'), + ...new i18nBadRequestException('validations.id.invalid.format', { property: 'id', value: 'invalid' }), }); }); }); @@ -87,9 +86,9 @@ describe('Files (e2e)', () => { .expect(401); expect(res.body).toEqual({ - error: 'Unauthorized', - message: t.Errors.File.Unauthorized(file.visibility?.name), - statusCode: 401, + ...new i18nUnauthorizedException('validations.user.invalid.not_in_file_visibility_group', { + group_name: file.visibility.name, + }), }); }); }); @@ -102,9 +101,7 @@ describe('Files (e2e)', () => { .expect(404); expect(res.body).toEqual({ - error: 'Not Found', - statusCode: 404, - message: t.Errors.Id.NotFound('File', 9999), + ...new i18nNotFoundException('validations.file.invalid.not_found.by_id', { id: 9999 }), }); }); }); @@ -121,9 +118,12 @@ describe('Files (e2e)', () => { filename: 'test.png', mimetype: 'image/png', size: 123, - visibility: file.visibility.id, + visibility_id: file.visibility.id, description: 'foo bar', - picture_user: userIdSubscriber, + owner: { + kind: 'user', + id: userIdSubscriber, + }, created: expect.any(String), updated: expect.any(String), }); diff --git a/tests/e2e/promotions.e2e-spec.ts b/tests/e2e/promotions.e2e-spec.ts index a26673d..49edbe8 100644 --- a/tests/e2e/promotions.e2e-spec.ts +++ b/tests/e2e/promotions.e2e-spec.ts @@ -509,7 +509,10 @@ describe('Promotions (e2e)', () => { created: expect.any(String), updated: expect.any(String), filename: expect.any(String), - picture_promotion_id: 21, + owner: { + id: 21, + kind: 'promotion', + }, mimetype: 'image/webp', size: 117280, }); @@ -535,7 +538,10 @@ describe('Promotions (e2e)', () => { created: expect.any(String), updated: expect.any(String), filename: expect.any(String), - picture_promotion_id: 21, + owner: { + id: 21, + kind: 'promotion', + }, description: null, mimetype: 'image/webp', size: 117280, diff --git a/tests/e2e/users/users-files.e2e-spec.ts b/tests/e2e/users/users-files.e2e-spec.ts index 0566bec..c73deb6 100644 --- a/tests/e2e/users/users-files.e2e-spec.ts +++ b/tests/e2e/users/users-files.e2e-spec.ts @@ -327,7 +327,10 @@ describe('Users Files (e2e)', () => { mimetype: 'image/webp', size: 3028, updated: expect.any(String), - picture_user_id: 4, + owner: { + id: 4, + kind: 'user', + }, visibility_id: 1, }); @@ -350,7 +353,10 @@ describe('Users Files (e2e)', () => { mimetype: 'image/webp', size: 3028, updated: expect.any(String), - picture_user_id: 4, + owner: { + id: 4, + kind: 'user', + }, visibility_id: 1, }); @@ -381,7 +387,10 @@ describe('Users Files (e2e)', () => { mimetype: 'image/webp', size: 3028, updated: expect.any(String), - picture_user_id: 1, + owner: { + id: 1, + kind: 'user', + }, visibility_id: 1, }); @@ -692,7 +701,10 @@ describe('Users Files (e2e)', () => { mimetype: 'image/webp', size: 9498, updated: expect.any(String), - banner_user_id: 4, + owner: { + id: 4, + kind: 'user', + }, visibility_id: 1, }); @@ -723,7 +735,10 @@ describe('Users Files (e2e)', () => { mimetype: 'image/webp', size: 9498, updated: expect.any(String), - banner_user_id: 4, + owner: { + id: 4, + kind: 'user', + }, visibility_id: 1, }); @@ -746,7 +761,10 @@ describe('Users Files (e2e)', () => { mimetype: 'image/webp', size: 9498, updated: expect.any(String), - banner_user_id: 1, + owner: { + id: 1, + kind: 'user', + }, visibility_id: 1, }); diff --git a/tests/units/services/files.test.ts b/tests/units/services/files.test.ts index aa0e5bf..d1f1cc4 100644 --- a/tests/units/services/files.test.ts +++ b/tests/units/services/files.test.ts @@ -42,7 +42,9 @@ describe('FilesService (unit)', () => { it('should throw when the file cannot be accessed', () => { expect(() => { filesService.toReadable(fake_file); - }).toThrow(new i18nNotFoundException('validations.file.invalid.not_found', { filename: fake_file.filename })); + }).toThrow( + new i18nNotFoundException('validations.file.invalid.not_found.on_disk', { filename: fake_file.filename }), + ); }); }); @@ -54,7 +56,9 @@ describe('FilesService (unit)', () => { it('should throw when asked if the file does not exist', () => { expect(() => { filesService.deleteFromDisk(fake_file, false); - }).toThrow(new i18nNotFoundException('validations.file.invalid.not_found', { filename: fake_file.filename })); + }).toThrow( + new i18nNotFoundException('validations.file.invalid.not_found.on_disk', { filename: fake_file.filename }), + ); }); }); From 60c7679072e6351958469fb2b6cd10aef1b5e207 Mon Sep 17 00:00:00 2001 From: Julien Constant Date: Sat, 11 Nov 2023 16:00:52 +0100 Subject: [PATCH 4/7] fix(dto): add missing properties to OutputUserVisibilityDTO --- src/exported | 2 +- src/modules/users/dto/output.dto.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/exported b/src/exported index cc12a9c..38a92ce 160000 --- a/src/exported +++ b/src/exported @@ -1 +1 @@ -Subproject commit cc12a9ce00080049e04eea3e99054cf69252144f +Subproject commit 38a92ceb6528c52ac6eab1846e26346cf1ab0c70 diff --git a/src/modules/users/dto/output.dto.ts b/src/modules/users/dto/output.dto.ts index d04b573..8582e23 100644 --- a/src/modules/users/dto/output.dto.ts +++ b/src/modules/users/dto/output.dto.ts @@ -95,7 +95,7 @@ export class OutputUserDTO extends OutputBaseDTO implements OutputUserDto { verified?: Date; } -export class OutputUserVisibilityDTO implements OutputUserVisibilityDto { +export class OutputUserVisibilityDTO extends OutputBaseDTO implements OutputUserVisibilityDto { @ApiProperty({ minimum: 1 }) user_id: number; From 8f418abf46b70193c1e0636812924fe4632bdfde Mon Sep 17 00:00:00 2001 From: Julien Constant Date: Sat, 11 Nov 2023 16:01:24 +0100 Subject: [PATCH 5/7] fix(files): fix precision issue with aspect ratio check --- src/modules/files/images.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/files/images.service.ts b/src/modules/files/images.service.ts index c7974f4..6cfb8c3 100644 --- a/src/modules/files/images.service.ts +++ b/src/modules/files/images.service.ts @@ -17,7 +17,7 @@ export class ImagesService extends FilesService { async validateAspectRatio(buffer: Buffer, aspect_ratio: aspect_ratio): Promise { const { width, height } = await sharp(buffer).metadata(); const [aspectWidth, aspectHeight] = aspect_ratio.split(':').map((s) => parseInt(s, 10)); - return Math.abs(width / height - aspectWidth / aspectHeight) < Number.EPSILON; + return Math.trunc((width / height) * 100) / 100 === Math.trunc((aspectWidth / aspectHeight) * 100) / 100; } async convertToWebp(buffer: Buffer): Promise { From 77897955d6cab0cf46498f9aebb91aaf17f40715 Mon Sep 17 00:00:00 2001 From: Julien Constant Date: Sat, 11 Nov 2023 16:01:49 +0100 Subject: [PATCH 6/7] feat(banner): change aspect ratio of user from 16:9 to 3:1 --- .../users/services/users-files.service.ts | 2 +- tests/e2e/users/users-files.e2e-spec.ts | 6 +++--- tests/files/user_banner.jpeg | Bin 167470 -> 35186 bytes 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/users/services/users-files.service.ts b/src/modules/users/services/users-files.service.ts index 290b605..8177472 100644 --- a/src/modules/users/services/users-files.service.ts +++ b/src/modules/users/services/users-files.service.ts @@ -110,7 +110,7 @@ export class UsersFilesService { const fileInfos = await this.imagesService.writeOnDisk(file.buffer, { directory: join(env.USERS_BASE_PATH, 'banners'), filename: user.full_name.replaceAll(' ', '_'), - aspect_ratio: '16:9', + aspect_ratio: '3:1', }); // Remove old file if present diff --git a/tests/e2e/users/users-files.e2e-spec.ts b/tests/e2e/users/users-files.e2e-spec.ts index c73deb6..a2f0414 100644 --- a/tests/e2e/users/users-files.e2e-spec.ts +++ b/tests/e2e/users/users-files.e2e-spec.ts @@ -699,7 +699,7 @@ describe('Users Files (e2e)', () => { filename: expect.stringContaining('logs_moderator') as unknown, id: expect.any(Number), mimetype: 'image/webp', - size: 9498, + size: expect.any(Number), updated: expect.any(String), owner: { id: 4, @@ -733,7 +733,7 @@ describe('Users Files (e2e)', () => { filename: expect.stringContaining('logs_moderator') as unknown, id: expect.any(Number), mimetype: 'image/webp', - size: 9498, + size: expect.any(Number), updated: expect.any(String), owner: { id: 4, @@ -759,7 +759,7 @@ describe('Users Files (e2e)', () => { filename: expect.stringContaining('root_root') as unknown, id: expect.any(Number), mimetype: 'image/webp', - size: 9498, + size: expect.any(Number), updated: expect.any(String), owner: { id: 1, diff --git a/tests/files/user_banner.jpeg b/tests/files/user_banner.jpeg index dabbdfbb1aee7241d228928112db1abec74b838a..fc03c65132ca4531ad62ade527b798754fe1b813 100644 GIT binary patch literal 35186 zcmbTecUY54*FG9`D+*{-5D=x=5D@~>1PqbTMT%7EBGNmA7Nl%7Mk#`#pdcNo(xgd8 zP!ItLNbiV1=q+@T{N@SxzTY|LyMBKhuYGOz)_G>u%v!V7z3#P!=bGwDN9oz=VKCTH z6=iuX7>w>P47UI7;r-xGHkB^^3w}F|QMilIcDBNJnY&rSWFI)+x8zoFG`F_YvNV6- z0|q-_W2q*U|1eJ}#CBElYQ24>t>-53BQk zKL%&_e_se12&9qHaI*zUF?W!6w(xMYbi$~}BhP`q5qx0#KuSnfTv+s)C|U%4O-TH@ zu<&)E>mt`>C1fQOMXpQAT@(L1$N%=Ngouc|to$`u`RiyoVPOR^$?Njs*M%g};^MO6 zl42r~f4^06a>tlESy=vE*A}e1_pRjr^;;=9H%oJjvzv~yv%_Bt(6n*JIJ?_8yKu{C zi*sLAGq+bK8@L=7SYpFq zFkFI)ysVCQ-|Qem_yue8k_<<&X*6loFERJ;3dkJkw0 zWp}ro*g*UJqPs4$IK2WH$dy39kSebt$|UU{tQcAuzWOsW=w`WeQB16@F(F&eJvscR zw(`E+aiqfrdZO;B5u9ZzSKi#}CtqU*Zi)8GS6%&bnPgUskAO(0Ief4%La5BPAiV4Y z&MLXyd2g9On>o(t7W*m3b%I(seR51Ky6jKS7vYruOp+hLWQQSZiXltKILr9rgv$6X zr;J}rQ|39iyU6j!dH9JC2f;}2w}uaG2dLab&Ly38*zxg2sT39*^X{mc;(w5$F$`VW zx71!JYYo(uq^xLA)8w_f3pNu<4>kP?=1*B^dD`IoclgZ9h-mW(l7qUcM*YwgB}GG1 z%ZoN|6!;G($579*v?tUCT}{#RX5{-j|IL0yIM{_B+Sev|UY<${5-L-%;o-0?8u;Nx z^3&WqtH8ulB!Yvfg!?0Gx>40{W(M7UyRJcfa)~{@P5+_wIn`{V)bPE7sZ*GBeb+sg z7~^zYQQ|owMS^1b_rPR3?2MJ+0to#%$M`o@ zb3h99^tPoOLpwM5q`x!wywVIA*-GrKunTFB*{Hv@aIhh6xK~%oi&Igeh#})$cRQMX z%3eQYPY^=P$y-0ZPhgAjPC+~&PW1G)n9x8{%7w;!YR6wKB}m|qq6^MJ!Ytb(*?PYO z5R!A8hQXH2*XxL>dEq|~=^s6=tE^vl#X-zYciw$ZC8jFr=5R6GDxp0|yYRi10}>Mv z4hei2o2R7>4*t&HQ~MIG^9zf4m@dPwKl$bu?Yqj>bw#rZ%c^*=w^6I^z$wY)?adGoNQGv@WGA`G}C#P7jE^HxqaF$FB17zdCyd|TI6S% z__5+^UmkPX;T!OgEM){a2@mNIr&pE=BGw@Z;p)La;mLZVRSdh*JLqeP)Nw{@i|#`f z_PX3+Xj_#dhm})*%^9!_ZXNscpinC!0d1I-qubvQuJv~#XP#lxt2ep6%4{gYLtnrh zXGg3v|qh`=C|X?Q^asI3=IW%@T=lcl4bG?K4>b@Bdt(VfMuZl_s|+a z4auxnQk9kv{*%qhR3r_@ti^jdMZGQ4*K&So_d7PLptz%5Q4%XE0!-T@vhX6-OwTVx ztVY?28sw2mHt@BiWQKL^OnA_hlo@j#fxq@Cq{HkdTIO$~b*e*dvcCae99=1S0LcNy zt4bzXTVbgBZ{97TUij^VqU|~>PloM@$(R|+8Lg2y)kEhjZl6<07(-&T1D>>HJL`M% z2t2j#W&LZSY-~ZoR&T#xZygakwLQ|J*pffVQ9cqGiLJ&vO&182t@@QP*s8ZS1F<~s zDx1*r?QUmFiy&9GI?fc7$7y)=^SJ+XV$7CUh!Yv6g%(K*T@y}e>}kj?_Y{+Q+EL_l zb@!GetY_4F=f!D>^QPjlKslAmZYXeceq4$r{wMhoEmO(EB~Cy8YDUOG9BU(!$d(W1 zT+_@lEZnNZJF&k90-aZJyQ{qN*Qgk!RMbN8d{=oh@hsW*kRN_5D`$ymGD&y?9a=M) zDyx1mT=PtvVD_QmF&Hc%_=1?&iMSgAsratJOh-wDCILiPNZ}#(Lv+`H{NZPCI!(>r zk6c_^nlZhr9Q=2g!>3jW6>%*WUtwpHok@g8Mpf6+kr?|ccyRs5g|9w-NFyz>Y02V5 zr3yb!d|VOQ^@KNXUl6F&kZ@NNNAMQ)D8aEdF82$?7{Q0?_$%PUo>~Um-XpnkxAr_| zjXJoqF%r%1pKBYNELxLTdfA>=H^nz_hBXKJcy{uD? zXQ}uxEwoO^-nag`c%|of0F@Xc{6S&S`_6JH+#v>Rgl5gOvxqyYkJxC0_g@Y2b6FnxU3h zoT@sDzPBe#pe!?q(EQqoekSp%bHQUXdUK5R%B8Y!H$G$9r-A;gYXsZ#G2P9l4PI2^ zyDC31ad1t46zT7$%pF(slKfYeN35=3mT%ADe$K>CPkg+|gA_fViZAo*k;{%&g@>-PDzTlCD6zA&47VMiix zqS@w5f@G{7xKk{yWu#hgz2B4Osd0}SxI4M2yb2`E+%oo$X*z|(#LDMFeRRxz9Rbhi z`BTIi;^5t%%=Q9l;J>`Q9l)?3`PwAj$-35G86woDAx>{Am|=CFtHMNi?vc^ok+Mu6 z?Ee&6F~qvbzxvHtHLk1oc=feIFlQg)aI&9DTLIXok1Vc3K;R$y-YAwm153y)^0g7q z{Q!*Rja^$3e(ij%O@MY`dicbv=+hw1S|Q7h{RJQ-R>_RhR)WSFw!`0_C2$(L1nDKu z3-$X22}yCkinf*iPY|lfJ2@yfxO}RiIP)92VE$S^`Gt^_O_`Q*c3!ynTSwY46Btga zAf?Pq)zqF%Cw@bB784U6KM|73d&1gIlu8lbGm+V&OGr#FpCO4LtH8$b5QLhKmd+EW zkY^y#>iAl=TTiIlexbhI(_PXps$g-#HaznyA7 z*bvZ_um@B2oeVm`x&4MA3?6Eb)k*r%)V8%uBJjwvaVV;YapYLW>yz3x;+6J73%3je zBeUwgfsp}^Y0>&i4u8}flm1*FCHtv8La!fJ_|xG4w7bBcz!ZV1AqZyb)`u*tcYyQO zUfyrc(}KfcZr8Z0)d}Em9eXwuIHQuzSH~Fk`;(iu)95i&&%8u$TJ|Cw~?nZOGaK$!yq1xywocHLJ%JT@QeTo|o4O2jtTN z)xDkzowp0)UL{!rQ?%bolmnCBBC)%C5tDiJCi-(d9Ze&T`7+*pY12=A%h@+TO#HY% z^yhKKe8)Mo-9$%+{7&orkMy*0HTRDA+PG9zP-SNymqZ^;D;z2GwRzmDIC(g^r}W8v zqE%jGWiNg$4wKU9*3_h1KEk|b?6VgYxI(+^)FBj6orxDLHdM1Zvve*5eHAk7 zp8@_I?b}b7+i!XQg>8X?TBLjc{pCpnt7H}v)N4-q#KXzNllfro@yOikkljXmMD1GN z&40cf#-^K3Fxg$9LA=&&-*qRS=bS-;+#yN*dp1O3?PTi)e$M@ff+oBu^V=hR(^Py? z3^+RxH*{!@T;_*MN*DILU8Q9*eu63J1VAxQS)s*)D$#7;hU{6jL~=g>M3(#MqzS(8 zj@RCe%F$3$`==W&=3Wb;@HKom9ZTDu~Y+bmd`YC#dH5-9Q$165@JZZr1C-2dd zu1nta8d!%kAjRlX;>5=qfEB$vb*;`ESWXAyVGCWdJcBoyWKL{vh*=9f6waGAeYbmp zv*Ut!7wo6_kTJm7=-%h5zLOs$sGjILaXTUhv)X4VqmfD>*qr5U!EsI@%SGE+q=`$v7UysUFpj5efb*=t)y-! zxGBoRSJ&;5anJ0JA(`bC1Hnh1=a&FN6MlGvucPv0!BObMo>^OaCjSK)0m*n4yPr}8 zp*+|JClrNKyvUwBEmQ9d?%+gkOeM_kL0RcOk2fa`t!_Pl3n-+awzl({#h)(mD6$J^a@;ms#DVxVy~ZUp==VfyMI#si_GyJuwSx8s1UtF<>EiQm;T6;d0y<=;^&`E z86(G2DZd%ehD_uW1_E^PYx3T|E^0ugX^FdocQgw zOv7r7y@F})J48gjY%Na|UdSgM%wvVi)n0ZPYiLyD>AVK6)^opyf?l-!)_r=+$LrzV z^?hPz@U}1#GstHs3tt6h70Ma*9ddHxeV z2Z!uP!ta;9$#&3l{rIZ~Vo2LUh79)Y4X}@8v$@?HOk{9FFL|Bx5XrSneL^DG5#+Kw z69M{gyw`9z`R47gi(haZ0Y$;4YdhVjU2Df`mgB8f3=NgxtC?ACi6WuwNaknz%$c8U z%bsFx2e`N82wzH3UiejJA5lJViVD zN>s5|jSv0H6_a6uWhrMJWbU9^I*LK&Z z63##3WY5XqPsf*y^MkyvxbtIqvv{69IJ4XvY@HqQ7>@Qmoa`EOhsPw5c2R1?Hp?xb zgqlMXds%1^3~ohU*^LtfwIkmSxBh85GL@JeX=LyMZ~@{stj-WQp4>_B;)(+xS<0&} z;N=THlGR`+ixj3we^-#F1i;U1OPiswHa?Plb3oq!+0m1}jOlpu6bYo=-9;U2hbS*} zy(3Lc#p*4%DgcQz$0?-&KZreR&P?Q`G-4J)|E?517;Ksx~0(KAMS@`$Y*(beRMg)>ZDqnZBAFy+kC$QY(B zh#Gm$Nzcexds!%t+3#xECvPpa2AS)$Q8jU@cBktYyQ1_-4zrKxkR8Xu-~s{!xqtta zXFGT(J}Hd7w;~U1=svsYBCvgtW;%g_%63+mi^9~$fC&&gqi)M{*Q`o(A)wje0Aw4C zFGJDMs+sS4=yv_#@l1ZCsFVx{+>8f-1R>1GGbUlinmit=45M2id)aXLU_jd^U<=K+P(0m;lJ2oB@(usP9SFog1(J%H!vkol8to)0wZ>1iAa${UjuU(@^O;*0~6 z^VMDs3>ih{Fo2n%lZb856R{BZ0WITDJmUe4b%s24&XQ3 z>zNJcujDj@>q8c&O=+yf+^PEj+26?gu;c@`r-ktHTW*T`^_l9dSHtE|B!1lsw z7BxecY>*$GocoApVQ_lKNk4);oD7qCqa)H*Do{&5^@uvOn-(%=0vHqOpne;^nlyvQ zj$2|c2dW=e+~;5fXsLifT~gcM6FXq_7j>A!i)uNsV0ZV7z@(mn8L7$Z5@>qctkJ&U zPEVd}k$xb;O*4QZyuEdE-lqd30r0NDniuC=Cz!NP+_{kbJ(YP7#(VKi#=AVc?_k3& z;zg=!X)`H`r&+H&PvP7qwv-taWiM_e>hJg+Og&SR6PE{;yZ>5KyDj=v5Kn4&WaJ|Y z}Knr3<3fs`-#)P&HyVhRk^H)5j!U)Hjm=?CcOdZ zke&6rH2-?9{DO+!uZS-m`M2*NML+x7?hW}wA@5UM}V7{`0FWEy#M{n@eN;_L4N5u&NARV zh=aT!Ob1qR7l*$n99Vuck{#nHHX{fDlfW;xVP}v!64n=1ti988*%qN+s*v-?~9zh#F(kZ>;mIQ#T zRTuR-pu=)FOi&%vz}`cIsMd%lcVJ|vtnk!JN+}x*833r}$N|QR=J~LGC^L29yLT?g z52vx0Va5wbK*Yay?;l)8^XcCA(Q&UYNY0PaLI-hla61A2bGr9ENRQovflHZ6gdLiE z2aR8Rg2MpNp}?Zm#K%H&6Fp;8g5h+*fQf|K!pQ3 zRO86yl=G&R-|`XEs4si8L&}Vd}FHM{TCMW%{ z>B?GR+K7NXrjNu2wZ<$~O z$q%p61-|)FBU4K;?-g5sOJLrs^B@g@nXdC`sZY0gD67fhUMrx$#7_YsXjR84buI~? zqZ1rS)hnRoOP|+Fq47>T#(caD}h`s95FqX=tGTA{{W_)&Fr5XNgl zz1EH-!}3CTTL2-x;rsx1s{KA@G_6ekelb|%&{HDkG{^=$^G2*8wgX0(l92(kdoIuR z3Z_URMfmf=`*3BdP^2d61D6+z$W6T@ze*TUOICm%42+%aV^Z9APhTVXe{#*G3TbRR zOV#szkPY56J3cHIm0a=+)Q=nv)cjm00(p+Nu4KF>Tr=kp!!fs)0l2+K8zT7<*~(6@ z5|+SK<0mHSN`LmhjAVxW?tRG{vj1kY#>ZC`ca*JZ273UJ$TsR*ZS4Kfrr9t%Rp0bj zS5yhG+2iXJX|Mx%WSwVnxS@K!rh70=j3^pg#6Fe}uL5-13nTz8ush{*;PM=&=#<}4 zE@}>WuL-gnz@|Pi{Rp6TzO-K=wZH#e?W%wAFRDgPu-JvW!D$b z3MjWA!F0Uf)e*ewx^%h8XQLVMFKBn>}XRSg-mKFmGNOMw!pqdo~m3(55 z9r=?+EmA-<-(ppDwu6Cj-cmzXQVKv;?zi()|47%T5{9BH5Na9edu4aYPy;Y(`S2k- zF=OAh0MtYe1F%BkbEIqSWf-Yz#jQMiqSiwBAalFn9_JodoMLroq3V;50uwy^os5-Udf_tKTHLRAt4mFItxli zHZ*lL;j!z(Vx=D-y^w};#GsY7Y-v2_q9HiZY(%`yuY9<_F5JYSC>q!fX@(OTbZBhT zSWQj7n@8=!YLAk02S*y)sdgI0Of(3RVW~WSvJIS$At=Uzi5b7Y5QV;(eI5(Ns;8m% zLlkECXe=+lig8{+2TLmC!3FxaFW^$h{+a}tx~z<)gz#uULw&8rYkEC?6YbC>UbwhRwoI;k+-<{9z~$5LJlD-!jKWBf0h^D-3qecRLf^9r5QjE3D=) z*zfTZ6_Ht!J!cy3Lp8ISnT4cwxqYxf-%ju)ET>B_#Y!geQyD*SChc(+w&EK$}VEUKi?z;J?mI`05ov1XlB>>vn@=yT-|^7{sAKHjfYCiq^dn zQ2vdzY|wgD)LckCbhD}X88Oi#cb1uf;u{z(2YlSu^(|&xFEsSCCmgd7#i8&?$fUWQV9y&0m zq7Z;Om`S9L2v*$s9~?$>?F;17zgGOZ%u8wx8JbUg-qQfz(#8Sor0$QHy|G*Ca|>uP*3680_kK4Lxzcq10=abpTg@1 ztktEvW<&2IQ$TQO;X5?L*NZDNg2JHhP9}h(Fw%=B(bt+m=}_)AkAT3zhVVWbgKe5` zeR-F4DGW*AESqP}23{l#I5Oh9C=@W%1iFi_U%x!1uN>n9mFW@%jX#-ghmBcG^w>OY z+((d;ege36xNrJdh*?7#npMre0TB3RYvnNjdSI1TuYx!mqaV7|LpOXi1(b&!y52fE z1dn06cHYDrG`-2(9&=|U2S{%3sAkQ?&TqEP#aHo_v{FZk4-{m)u0o;2WUMBvF1@`A zGvVfC765e#9k>aDZ#oFiFw!+hKGX%~y5RsO1LdgNqS?uqqx5{H-8j{*O;$6?QfY9% zT|&Pb>ASI$)$i|#MovG~hOpcX?+-kC`Cb#_-W4F+*{%&(0S^H~jf^{4LjAD7A=+7; z9^K0ig2e63$8ih@6uE9P9=m0{UcI$sfggV8Jx_eufBh|U%?nURO@k_G>G~!|yR@%B zuKH#Gevde2cB39;~9v#QrE61?Q{E`5UgC#6H7fdnr zDeaSa+5t)_abwtFYQtppX0~CuwtJft@9$x>{-Bl!3y+9C zVT=;Z{jmOx`ihlAcw^(YsW@m3O!~l6NJi4&H>jNk?a68+Q1a>QoS>HSwvHKZTNVe( zb7M53K@y938|z1D=klMZc3Ahe1_iY0n&%6NlrD&eg5A@EuIcQ}`n&^Ek-7gcX!fv3 zhm-F+R+7Hu?vNwW{I4scI^=lgDT~|warR+u*7Il03m2XtFFuMAfz^aVDt^j;M60`M7<_J9>0AM#46Yc$cOM06)7uVU&S_$x~^@i#&6e} znN(Cp|?!NUh&H)o+a%z6D! zn?+_5kGz;cu1y9Y?@j(RYuK>avf$c;1z$TFSG3yRU9|*Ht8&LisfdDVryA%3mqE_2(=y&9I)Q; z&byobe8}4CC-?thIJAY3gDP`)r05Nqj<_`Ze%Nv7>=y0Jd18+;Or_+Z?IgeL#}!Ib zmY**?q%3I*AG~UVvVTZ7?gRqOo3Oe z%bIAH@{*Fl#x)Qu{)@AyBT_w}jLbl5Tl%`HSyJ(510nTgDG~7Vu)r)x<=!U08K&$o z*DR9tEQ=NH8X?H2cyiNcsjp!#fI8PJ3#A4;1Pnfl1M7!LT;gROe1Ek2 zQ~XIyWu-nS^|mC>69Gzi%Q>c&cKRQ$%gU_)Z)+9$B3>1aR7 zx2UMdZVS|djjhAUgHbxa+Hl#YOf=ypBs0wch#Q0jo`FC^-cg9#C<=KClEtH4pAaCE zEhjZktyEJp5c6)~$x0KHO*!ZC$(+Z&lreeLaMWf=>+FPGN?}_i`KaNFeo~lz*X;P4 zB<8q^X5pxn50%;R-Lh<_ z1Tw5@S6{o`$J(5qO$5XUERds0O9W(Pvo{OwWaUtshc8H;Ho=Pyp)=XXD3P(H=l2@$ zNcF&DwCh(_#$4$uP!V-&HK8NVHEN?af^#3*)9D|%?!NXLk|@`o7(1W~%l9=ftQP`R zK3bs~v_{p9$*DpK0?c#uRSUSNd08ACR+aoXl5%#Lo@2bF22Z7U!zv}0x*jZDB?B3V z+2+V|)9bF3r<_u*3;px{zgVl6*QM`f>@;aST)&wY)Db&i9=PF^BPwF3(MY{n)1+3% zCvbg^v+o}8ThB&|VnDp+PBrPuVwjpRSbP4tUOz-QfPp@v;Epnqek#&Lugz?m@Z5{) z)v!g}im^z3r^F@cvtTr|@eZ}!=LzQ^cf>y&Uo&#5#pkBaxswAL8n zYWBMV@F2`@>)cPUnd9r~4wYq;Pt^x;qGiW2PTTWzHtYyU`=v~Lq)XFc0ZkV-{zQw*I8z750v4YS-{VRBR~TH&r5ck3pYHy8 zMUiI3?#qVa#u|K}$*L>19i3NIQQ2b$g^=TqfO`(SEXyNMHS7H9Jwc=Z3phoqoiK}j z-fkr_5W)L9!<17d5r|J6@#UJGk$D^QZUfw~ohcM~y7rZf^hofrgZM%k>r=ZqJll1b( z#>!sC3cy?Vl3qp}@O;@~lfCI!)P|xA=u?SQ@7HuzTuTdOUW2YP;M$L#%r{9US}2P1 zrsKuWh)GD9#R1q=V+zO|SRP$5J)jVpZIexuEqqNtzU$P+X9zlDbCBM@HhSWztH&~l zP~6y-7?uv71lX zEI=s+Br2((Po+0qa;-ue^zlI1BFy(0*jJ6IT@kx_+j2=lxK%CMuHI0&=;O6FVrj=Z zO{8z-AJA5=Ufhs~zp+|3=idd?7xv=gSsgAa5g)J+K^^|q6wEP1K~ybg$5&G?N2Se* zxo@Yz2mM!G%9RZG�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 z�$EVa9Eg7WbQ|VV)iI4JF24=6fU`)}tF9Q^n}SJqE=;qt5b8v&(&-cAgid*Gt52 z!v9hvC-wdC?ua~Z!hO;QZ`)9gLQv` zR4F$J#?fcBM`a2NcS5-lEgnpI^hQM44n~CNC&=7r6b3!F3P`vu z1ueAbpXKIH(-lP&uVWkF=T-MV!}LRH4OwraO}mjHtoxY8vV_i0PlvXttrhS1v)s87 zTRzUW#Zz}e=6V}_qa9q%tE@t+Q!X~%tdHj*J@p)|vdt{iRedu?=cgzh!|o*vs1H+MLgub)xA2mvi?m#>P9HM%sHbcc(KZKH_q6@7Q;uPacuiy)15 zY$A?cDlYo12VeLFkug8j&tZK{(YDS3`T2jylxPJ<@qYVLIOWydwpP|^_v4 zOx*TCA7@pk38d2XqdRuFhmt_Pu-lArK1XK}ZI>&TwwYjbR@kl4U3&jH7T?PTCl}iB zfc(GoMW{AJ5q1=u_o}!1DatO&25~y$8uvt8RT*e-R$hy+Wb5t_T~;)Nv#ca2;5y6^ zVI{JPUgH!yQK-4B^Eu}x+#r<`zhtWSNx&YOp1ZW(pPWeBDxXq{+iFb#F_A(CSh(9tUk3bQqm17S>zwI9= zicH@$B#mBrrPN)XiWJ#kcmTFbOMM6MFvKz+J$j@{70f)`JDy(CY6vyeO`3Pun^d7x zo?-wtjw+ZhDg2Q{A6>Flm-H=|@64O_LEaH`loEU&XPK4mj{1D0JgL*JaB5Cl7i=Q5 zY=yQH!@43t@kNv-A~G)^npS1i7veAL;FOc3p20&zzjJPX%&Z^x=fOW$!NgwE_x>JN zoc!!qZ0FjOi?3j9{~AA9*;$iiW5TE}btfTXY4-7X-?DaDrYbvHh`&{-n{b;B*vI zL_X|ggqtg+YCntqx%&n@g(7SfigDIfZ?WJN)V=KQiMqnjNT-MJPpM{^_g4;_NR}+^ z;n+u6e4n&UGDMmLoX#`OpLY5k_TSj>Kv-UYn5zx^0=BC@!Yq0o3Lz!&{KgE)&XN{4 z`UX416au$yV-b~Bj@yBRB3K%C1VZKn-KJupR7<3Oi=%j=ACN@lj1}vb<&XG{;6;hu z+Wv5qP;)Tkf>_aK&$(``0ZGVaLpAtgv&??;A|}+8*%7*7--&_94JhE@;KhAq)~Y1j zS<0;0xJKWOd;AoGfsxm5htDBzsRf61O(Ws`N5UB>satl)(yC&A)47pjlAmg%8M#d# zP>)4r4{|2^hakZ6tuL#^XsIJ-sYMw%OAb5XCf5MHz|2$?oNr<`-nTdqX=MyucNY+d zR$;tgR$n2(n6wDn2U6eu?Fjya8VSE~P_pwbY4kes9AfZRrN03tbUQJ=!@VEIYF05T z`eUJ@GEvu_jG&4Z?KQuA;5KwPu^xc`0+;4=nFqt~>Nh)ze_px%6pRGw>rUz0fv<*_rbUznZUa!^g|A)zw7v3#pi=eQ7M&ZAsrW;mxgo6INdnfJH#@6e#N0Fv3usTmt(;c^lma77 z64TD!2|AMf0Wy$h(Y^Xc`^C-r+4d>0IbztYWl&4XCVYNM_1IrI3`i8^&}ghM3z&N6 zt%I1NRzx^cX{l$^RSo!tN%SpMpu zBQIa4hzDf0|KTXUq=XeI2?8|iq8w&@0Ow08P6EF?J^>5xV*21-o>sumzuP^wD2s-& z^`Df&BHZY9_x=yqlkg#d3kB4VssgKq5s;+MtTeC6Lj*)pcNz$v_ zsffoyXHinvDU<2tuU+^uiW-KBhm0RjzobaGzd7EGE79SU_cI* zE7@7L12k4v=sqs8B8sFRb$ll?JzblbQ`hqxJ*yq6pEHbp$xze=Rt@9It%9DF-!qIJIiiJ?1smp8mXIN$qS0XwmXi=RW9< zlltUoDHZ{Zm!CU6BZ$JbsF!YbB$V8ndQsP-JNBOt z6>a*kN!75&0NU-&{y{i&(7)miar6#^`pEBX{fOWP9;*0EKDd|@`@-{IHNA*FfSl>X zhiO5M;!&1MPi^v42f4A0<6y+7Pgm$j%CqK(rpx85rY)o?IZHP%Is@k~^=N|xCxaWS zo|iIpW9_O2U?NpbC5j@tGC|sB+Wy+qcL9lIp*ab%*pw&{*9xmc=g#(#0@xC%Zz&_! z20Fmy=?VtrBi{Y?nL)KT$WQ3ofkyV_uMMzwWocI&#b0e@Sl#wax`xIf$l^V&Fats~ z)Mx&KeBT*v)sZF-*j${odH-(;$MGn>(C0LAIF9RdNU&xUOVDEu6V?HTT?(bIzdJ;G zYGVCNrWn4ip^Nign z-S9Np&$`0TtvFErgpG2i7kujDI894!JiV1~z4RtwEk;(bfP6+`B+6moZ%UyghUKk8 znKvt_$d7(ku|;QqEnZhw!~HFDZ?5D+TP(h#Lj^z>>b1g&Ktpz^M5@tK#$3wGAtao? zbNnU4YcG(~Ojv_9mx1C~XCfEL8G0&`7k33?E zp0XXg-nN`lhj`nnn;;E?bZ6%_mh}&0s6ex$h_nCNPDwC%-V!6bM?Ve_Y-4d>#iMS# z@=7NF3zXbbxKjR^dRjCa5kP=c3>1t5`+v=tzFAED$T=NHgm^rWJ~5Dn+yb?%4@x1G z*J`_Zj4ZC0=?$E^W%{|4>akcxs?1pHAwB+a?kq*zV_(FpV!r~bY$``}jPQC(WuGnk zdfUd7X~9|pSiiz00)kuoKh}QxUB~KpQtwbFrw=Z#2MJsCm#h8~oL!X9$*MTDi-7_K0)Hz3}4vEz@CJnd5OQ9`Z5pK`(Au0^bz< zV-danb$7`6C`VVC0)YEy8jxW%J?BL-Gh6&6p@`M3t%{g2DoXcd`R;Fy zBv^)4FY6dvVwRs8)wk^-RD~QtkvTAY4B7Kgnfvb2DQi?yj~cB@Cpqwsh@g+gR+l>B-{05?b_8savK_OQRr7iDKuPd4j9x%7X!~ zd2R!Bl>=~$9W2E40Z~6ZCEasJbgWdX_W4$wq>3<=E~r?JUbhAwKTI3gjcF3`DXnYT(t#TV^bHms}DKnp4 zlw@z2J{4>^Lv$$~LJ$P3%BZEyn$HS71<-%cO1EPhD_3Bo4C=DW{9iK+$L?EMY3|C) zGWP^E=BF+JXo{-ycCA^5s0%&1EqN$Y2?)o-v}DU=cS>x|frz7a8!s*3d~wzUd*mTP z5oUJ0;mXB4$fHjjG4>yU+LhOwwI@AVEK&uE(U*6MRoh+$aw5cqVPZsH95gNHJ3UNY z12w4W2LOd1=CSVAB~$EDt2K>EbD?gePlF31QVf*w+!)$C?LF3P?1 z7EOsA+#lin{gW+dMtJF5QP|Nu`?W+7RfVgC0RoAT-OTJ}z0s?K_UC#oVLW%pyisX-wcezQzRVk2tz5lXDu-iQa| zGll=lQb4Oi@V}oo_A{myNB~&_*@NC4;w+=spT*704n{oq=ol*to;1l0eWoozircle z3{RUqr1J{S#Vg6sV58t?pSs8yXAa*oWe<$jY&*;a=jwh8&dunb*uV^AUO4zE>BBUx z6Y#s(g74nELjQv32Rj{O%kVC<2*d)?TWfsT7)*vjK#&?L(G<916ZhwrE%a1D66lH$ z+6~gbWb9r#UsFtt+Asw+vSuLlF+u6#f6E>_21)~Qv!_(W+7z+-0P^y5R88~Knud;! zrVISa>$~|2kKG^~qa30T^1VEsEJ2xn|ANlEht)c#r}m3uY=YX+*$)Awq}OJ3P^8iK z_~jK2!88Enz!`(O`JqhvqUtkXg3nC4fW_ z`!a{Yk$p=@*UAAB=@qrtu z@Zt;t13yGYB7c>%WgF}JhET2{<{t|^45#^boe_H|06p9IX0+Q zF>AW;=JTS^(qcpwf`2H&P41kc9Dj0)83>$eo!y@xYd$`b3&}%x4kv`vJhmSo2P;th z>MdrH#i0!~+*xo3fb6F~G1fQ}ROlG6)}6d789Q3AhiKgc(9-8@x`3Ov{*k|-LU+W~ z4S}TnY4q%?jdv8)wM8<=+5B09tS_AOm>P)o;Du9Wn%C~`vv*mmGtyh%ncU*JYifsv z$}Q%YzYQ7}1N8h_!RHbg$ug0&31k$owHSTmA-7}TkgID@;S-WYBAHj_yCObXuX)e^ z)Nqg^AQzP#t&k=PVnOIy8I_aqPi+qG+I7Dbf zT?=eMC3Qg~;ef`E1K*$jo-%(G3ipt5F)a%6q9qq!%SA|LUMP_$$ZDy7N{xb~cC8N| z#k#*HluC`Ul6D7jzPk7JTg~wii|x3dKj?RBPq<}1{!!}a;idPifeh4ND&BEJao?l< z0l#A(>o3mA2-2Z2C&gvCmvEWW;YitH$6{H+GGu615qm;K{hbd^vQ+m%MH1Em2ytK_ zkuH{q>u-!{iVsUCNu!znc5A5Jxd3%5x8_~Oul3AaDE<<%dW%^YdsD{*Dp^Pjo#OkYWQI0bl5lYW!37*tWYT zA)EDFT%Mvu_nPRP4R~{lSuL~v;9y2j-g$c?@(3O3Sur(H2zDZO9D$`%iXm>a~I%*P> zAq`jat)oO;b>oOf9F+%`H74mid0iN62Nc^S|CHqC?1{wsF1L~>wU;j(mEm2sf(wD7 z1qZCI8aYeWvTW`VViYY?W3-eUfY7zS^Wp>eDQ zsZVe#?tyHOJNqA8fXb1DCGH3YBw;XfwK6yStlo%5#9A7Hs*gjs>3BCVVtTY++r}Oq zmNvIt$ET#0rSBc|{okEbek0p|un!O$!$C9$U%jo%jutCT$k_Iw%yPIWy3urd1JuN2 zTZy+}sIxe9UY)=rCm*VVIY|BErI|5#Bqn7F#C<%l7Ad$vP@r0>lyzmaBYF)o1lJ>f zo@fna!H4xAoJKoXRJ#Cd$!+&QW+4L3dISf2_1wZ>$DYg`q8TN&j91XhG=+czyd=`h zG%%+=-ZQ`NU+}k8hIQr92NtEL>Mu#BL@MqCp&RcpbJLY1FyR3wko;sLtSF?Zo_FJ{ zgQmFFGA=ZPLR2HIaAJ#co9P7IPhq}5^0_JwpKxJ6(Sd;)oxMA8Ru__{T$ zXdSc?7>cu8H^CF7n`tCmMo%G3Io}-*lY77|JqGQieO>U6?BWMY`><0>{gk6JbnpB( z_Wuplf;GECeU1CS(Dp1BgI`sAxE6W-e@Bw&MwnLyBL!&1(JH`dAN~);-dHHkx+t0f z{XIWeS6B@5L$L|ZxWG3a3)d=rZK;Rd<0*|BN3I&ydFA+XDBnO5i!>ayk*4C;00@*0 zO|0))UbzyVn+3VsOAKqDNQNmoufSr$EsoE#RY*o^M0@=9yh5HK`UyNk*hlN96yN5G zawW+<9fZSOiaVjp2-IpmJlLP{KK-_YK1mPtzYq)FdlrNTsoD?iq$@f(fBWd&gTb=D z>wqpYlkxB}x?;y-^S_@$7clAn1GH0DMO_~{DiaQ^=Zc;r=fQHHMo*o>6}DSRq3OGW zkcaV_gqUJ-4;jmsqIvC)1oppU(UK-DuSL`qzVS_`ssQ%@rT`ipMu>~si66e$VdvB~ z)+I4l$oE){0g1JmXepgJiZ`q}5M=RyUQUdwy+H2ZZ~(~qW&pq-nk(QmNLOTO5!)S= z+3DB#4cSzWo?nC(YhDjkUuY;gb_bdSuFG5fGwMZ|e~fJU3?buHAyBCcgu(Yv&bWWe zel13#hEK17+L^rLO-~6x5pPJQ%Fz6O$sh@t&|0J~A!H)(F=RfVjI(9UJ*dS)eL}M5 z(QBkdsC$Nq-J|n*)p!kup&T5~XiE!}9F_U#E6>++hn4@dS$lNGW%9bFLwX-Ycaoi> zBi^~Zp`ZNWRYENbZS*GGnDRIuU6{bg{unosT_XfPJoAR{U9 zpeK)~E=t@OM>t8aF=+%T`X-T7U?Pg5deY=3pw9RIcriT#hRNNL=%==N9GQsTHf1kO%4iN{z>% literal 167470 zcmeFa2S60dwl3Uc34#b1K!OB`k~6}9WD$`lh=7D4Gea117$Ybu2qIBLi2@=5k_A*$ zqU4-G$vKCCnZF0O>6{(z-j{RVeea*%o1WEGtE*O5)mK%$zUmUwg&C$>RQ9s71^`V> zfCm78eEjX<;ca8Gzsk-Vghp;D5`5SpEsYH<{oDRuMdi@d$p?CI0zY@K}F5 z3m(tU&w`KlEe$+j{O@Vg7Jf|*Py>Il&$IiB>B5Xq7^*8Onw{6tQc*vv4B&xr6l!Pf zU~v2c0N?~exaz2$W-&Cn$U@QpmJ2CB3$xuu)4rml(}HcfS9MHV+u2`m3N);e}` z0a{P0oo8YB5&mD2s4bmc5ulmpL2VH$sGB7yW6Q(Q6XA>vUjSuV3kR%>kF6hCSI|IE zK7y5PzRG;NI$vdZtZd~3vjTOnHalB6Sz+b(pnS{29SX_>XF)m8!w%{R%59*`?&$7h z2g)0uOzQ+ScLM-I3T!w6YH16~f}l*{s;8p}%BR3aM`81mZ1IzffO>&;0)Qgi+1u65 z#umZCWy#GVBqb%uq6YPJgdz|p&zoC1n7dlBD8ikb&0*dE@b#IocL4{nWy=B>hG~@umrN>|l z-uHStOKJdMk#~W4{A;@hWiWpC{L2YH6uiWDv$J8r1}p05u~@phdSGP`6W9|GKn73& zw7^k-319=bfRlhQAOXk#@_;gM2G9cZ03*N@umo%YC%_f(1pI)jKp=1jxDPx8Vu1u8 z8Au0S0J%UR@EUjvd<5!%CZHYY1%`l8U>aBe)&LY99v%taemokyqj=1C9C*BV!g!K+ za(K#kXYq9KjPNew+2FzO-0}SIuH)Uo3&)Gady1EimxEV~_ZF`PuNki!Zy0Y9ZwYS; zp8%f%pBA4H{{+4Oz9hZ^z6QP?z6rh!z6-t&{tf&P{Am0{{O9-u_;2y+@Z0f+@Tc)t z!H$`n;1I!af|CT21d0S&1Q!Xc30w&R2!aVB2@(lj5R?#95wsEv5zG*55)u>A5V8=S zB$OdkBh)9fBy=GRAiPT$OZbeifbczGE8#HV0wJ1cKhZHFULt8Cbs|F|8zN7lK%z*Z zRHA&M4@B)mqeN@O#Keb)If*5R)rgIV?TLMf?-IuoXA{36ZYKUryh=hsa+HLZM3zL0 z#GC{{5=at5@|>iCq=jUZWMd!sKIVPG`&9N_+y~otZC~WRjD6+%TK0|aLy;aJ5ZPT zc{KS;@@n#7@=c0^6g(7)6qhL6DMBdHDBe(XQ!MY_zyHL3`TZC7Bld^vPv2j;e_;Ox zB@HD%r8=b*C6Y3ZvVgLMa*m3Wii1jl%9P57Dv~OXs*!5u0O^4f2NVyO9|$-QccAz{ z=Ydsf8fqbGEovv~yVM!fHPjOaNe-SksC>}s;EjVx2P+SLrXiqVqfw->q`6L$O!I+e zl$My5lUAM9p7suH7HtFV0^LD6Q91)UFSQa|K)DD+V7p^igahZzqm z9EKjgefY)Umc#2u7>>vvfgTAyl6|E8$ktJoqsm7ej)omAJlaoBNY6vBP47wnnEnI( z3{2(U~!Zv669y=@64VlRZ-eQ#sQl zGYzvGvmJ8;a|QDh3muCBiz7=k%X^lEnAu*TsRSQqVU87=MheIP9M%p&OWaF zTo5i7u0*ajZengpZU^po?j|059x)zUo;aRHUVL70UOV1+-ex``J}EvJUm{=UNs5yS zCp}JPo*d>s#DA9m8h;`GoB+GPMS(DZDgm^hn4qIzvS6>!L7_83*Mv%hmV|kPErsKR z+eG$@sE8m%ibNJgc}1b3k43x0Xv8$dg2dj5?TAZ=yNbUMpOj#ixGeEVqEnJq^1S3- z$!aMgsnb$Osn=4Q(&Ewx>0Ie~nUgXOG8r-xr#MeRPbHuFEXyiuF8fq=NRCDBvfLB7 zA;@uvIV2G>BF`poC7&ukuE4Eeukc)9?zG@(*VFl@*A=A|{T1IR5h$rD1uHcuA5_*? zj#VB|VO6nF$y8ZT6;t(9eWOOCrlA(5)~U{@ZmFKGzHmn3jQ^SU8WbA38nGH9XL-)L zo-NVD*F2;7K(p^0$2r)!LM=c`UF(5X|9Q^yF6T?NiM7vb$7zr02HXd7T5Dhg_sx9=a^JYPhDl;klW(6(MNB$yB{Nm-|ik zVGlWvIFAia1J67!YA={qy*H2dZSQd(6`vGeLSIYYcYeqHuKEr8EBHSRzzeVlc!y*| z-aw9CQN8l)D%n+stDmk3UJJjra{a>fk{gUSuH5*1Q}t$MAZ4IyU}um_P{J+3TXwe^ zZj0QGxs49C3a-5)cqj7C_Fc=nwfBVXMc>1OScf!(iibYFPkbMCzatD1_AHz_+$a3= z1I-5o5iAkG5i1YP9@a#PMkYj&MY%@}MW2l>ieZnrAF~~68`~atIxhRsu}8Nbt;bu% zw>*}A{33xd;ZDNV6T2r}PgS25B%Vl&Od?M5NE%BvNUlneOi53rPrZ}6lLkv0dZzR2 zeY!+?dd9Jg&`kVH_sq%Xm!3CeDPQ zy;Zw2t z#`(?6Tbs9^-W?ZPE2|W$Dyn6xU)M<26xT}D7S@T^71WE> z=YJCWl;0rUP|zsRSkxrdRMLE^xx59^^0rl}^<$fQTV4CP_T~=#j;_v2okLw#T@&4o z-HSaQJ*eI*eMEi1{RjFZ2aXLS4ss7>4~Y+z4J!@TkLZr{ezyEP{l)Fe_UMhV{bP~i z%;V`3!V|A2RVJIJjHgDYou^SVfwR=JkLI}MUd_wT*Dn|@j4rw^VwUbMA6-sc5nXw= zdVY0a&1ns_9=vgMT z11NW|0IMtD%m5G53)}!G_U+q8x{rdCl!BU^jGUT|ih_cQ?g$MH9SzM9YKqF5)Z&9_!fBpz$3&Xz$e5fA|c*K zL_i?|Mp6?H9zQ5Vq@Zn1!|HNXn3y)~Y0hajb~>H679ygq*P0bcIKp)oh8Hc}#119q zUO%DqK+j6~T@ph4aEty3bSdvfWvlz=Wfjhd03RQ;mT=c55@I5;T@#KA9V7%TaABn(622Nni?!gbPV<5Y zyT!2UwQxmUI!iavMGj)D0VF5H-nD>6BptR=))z-WN4}fz#};t@Fags8P!R0qNew`N z26grbzFKwmaGh{aN)A`&;ARh3Kl6J5x|pWFrQO_cRt^8$6?^q_@gZ(ZodpEZim_^x z2V0BFtAd0Ed+l$=a1<5OwrZ~BLA_?T3;dOg7g?{Md22Wr0NocsRr8U;{FtaYc z)rD!vbp4C9)VvEvgUb<_$Lpni<5GNNE`3h5bqqN!^qq2LPthp_jM32z&&#t`4cEG1 zs*{kUW+-x4ThU~-Jjck7z=7zVBkDTicJFie21LIiC97h>!w5~Se6iX=*f=a+ztGt7 zQs*MN40ZR!Ut9F2@1^y-$ua!H$N|&4Mq|$C`SDH4Xe@@_RZA8&Ajbtd?mZv)vPt zQX6%WBU)Vc+qn#pLf9`o_stOqkR1~5S(^vun8pgmP(j;_cjZerqcDK}V3?i11qO(8 zMblz{*#hA+_Ewf=cgKX+X2jd-rso(Qco?VNS)-?1INR8C&SK1iBV-Y#mlc2(Sx1Ro@bzkfB z7ixOlNn^OyNANjsVt|Nj8+2SnGsngBYCB52%4_U?%~>C+GntuBd~{0VJJmF~bvl=R z6F%lIUS6@1j}B8z8!?WD@zV+O)$nD#!1t(L@5KN{nf`;T`PndL?OH~KGy*o}qeE7g z^H&X<&APhmtCIS}b2b@y+mZ$`fPKsvdG3#G_W|~Bf{S?U;U4)}4*m^p@hcaznBp8b z3au+j-j|Sa|71vJU)*9J1+}G7PGsSxzP?jo z9{Qwy;{8iKhEtR9{q7-j9Ze+ThW=UV8ecBJcxSSY2WE1kT>?qz;oI-VO1tMRV^-QT zOZ25Fu)eGHSyiGlKWA(fa~jju-=S<2cc828S)nHjI2 zd|Y;ppk&M~>yy6$gUZxLk;n8N7iL$LXI3FuIUJJnu&Uy?*q!8i^)j)R%bCdW9APKl zrFixVOiP2B((UMY%jkJh_NUfPy~uMOC%tB1=T}VJ9~OU%H*rpOdMSz&Z_E>!@ZFhL zs4B}Z=y7sL2)%!V-(jD_+pK{U37z$Vk3J7!d0lHh9_>!~7~tJ;_F#rXW+AnXRh3Rp zqh?lTU+4MWv}t=0JDu#9ntj2kBOtPu-`^c6VPY$y^_0P6EmTJ{F0fGpz8vrfwpwO} zOh?~SW{-$EG^XwR`ek|TRS(Hl+ajch`zOTK!>0LGAILBaYBp4+#H!9-{9(?8->nbY z-~x65MLQB@1M6k%;Tnev-Hr`N;B$`9y-0}^$%EBWh}|gw-dWvycp-EpXx>Mr9T9%o z@$LhdhU-zW;(Jd7uVU1T=$d%L#T3nZ%MB$N`^UEEW4*KZI;ag<A(sxSv!@&mcB4GUKy(rs;xBfJB>%a`@R|by`cQ)UTorQb3^m@wXs&&I z%HV`UZdwQAfypM_)~wC8yOB8Z-Nj7y@S|hKYl$NalM*dZx?GL5+>+ihenCL;w7MoC$L=V7VE6pfX*W)KH{bzy#>Rgg z(aD`%K|igQJHpu=>!HJXi2+4NS6wK~-RYZ*{RMqRpqCHybixqOZ*f=|8wTp{>S{Wl zcC>)Q9kD4>?WU`1=Y>^2ZHK^yV|Bmg2X#GX?gZ6SHpF@&vFf|_$zT=ma>3IN`wsqO zpO!P$Q3N_6frD60MP~%;Te6@H7OvkfbZp!-zh5Z2!W4g8zz{z!&RRISeUCY3gYfu% zq3qUXJpvZ@#IMc|a`@a91UBggN#J zc5C^(&3U(Pm4=nEy5R3_g`S04e@*6Jveg4!`M;N~uBGGG@bj*g@Kw~(hXvEM_4cf&!4`S%d9-H?+%LUz-{ zy1qeYIRJo@`QJGRCa}@J69ESOcj9)-0-H4GT>P!1z+dqA`b1yxBDfnufSsK0>SJxk zYGCV}a5rS%Zpd$F!j}gV1OI+Yj|nS%^-=EX(qk_P^ug0_dF)<;u4`g>P&xoo9^#<$ zQVRUD0^C7or4!%{%68zdIbZ`|JA&P|hP~^j}~?0`3m8a{CwbSt77x0JZ@E*e2@;huL6d?163CKVP3lz@5Hce>Fn~BqF|E?>-{2 zo1>j2)Xl*0EEXVmzvM%-8wUnsdmfVA3w5QR(k8KSg}Xcd5=0CKnGQSHZ@8mS*2Si- zwHpSevESSs0at~>pswZ!EcXIh@9q4pXC?cpj19rYs5{xPfIt7~=O=J?b^NJI26My~ z{;wgLZZq$Q5D1Um*CX^A5( zaik@Vw8W8?IMNbFTH;7c9BKKlyd;ja+~rMiq$Q5D#F3Ub(h^5n;z-N?GoieRyT zf_{5h*7?#3Ru(%eS=I|;nu40nicniSH6K@~uFp9=OCJYIDJxcp9Lp&$X)h;dCn&<4 z#mmVN<|geW%etGmG$><(1z1^jRS*ucth)jLppH-{C=B5S>Iz}Y?3*Q; zn%~ub*A^$I@7it%We@N%{;ZbWutb1>4%7|&=)n@I>;dM;_AN6v1o(Nv-?<%B-@Vyc zTG1714w9~VAmRG0mVa>b|MEc;HoM>MfPyS7RF)O{h15ww?EiW~LegSV(jpQk1;wNV z1;46lg0093q;vmW`M1xheo=;7*;#x4rRw*pk=|uNG~rgD9U`nOKf=KeqRzX*t?eAa zh=2S1hLy$2Qra5s>SPX(uDB?)CIMQ}m1ps>ofdzIjp?%0O#?OrSJUO`DoK_MXt zQ9+46zxOMR{J0$g&EFOG~k+ zW6R>_V%$~0ruK`1z+a>#A}#u-N&Se&rX+yH@K*x`{@@Y7S^(=s&CU&MrQW|~t_yYf zVa6_Yq|Gg{D=cJLEwSwxYQ@U(HQ4TV>hrJbPSOhMYWGuz|8q9qZD85U4F7U``RjSk|9l#C`LIEVW_8~W)D z=i3Acd~L#t-{05dzP(rBesLv$D*;>y;7R~j0=N?R|4|A2@c2Su;Jdgd=+FI+Yy|{5 zxp7+ofot@4w+O;*1%%rQ2xMgb;hsMK@HQ~RpAXztK)9`da9aW4wgSR!1%%rQ2)7jw zZY!Ywk*$D!Z2lt;&PBd%{Xh1mN~R1Xz>siAmi-`v|Ft4iX71ISVI5UCEBVZ5HDk))l|D7@nx4=ccS8p=!ycZ*YM=_g%}#lGSy@gB#GKyvo+k z+>+MI?mSY`YU*dePoQjU?d&}~y}W&H1_s@_9USp6GAcSIHaR6V?OA%ptNen(qT-VG zA3j!9*VMMPcXW1j_k0;08=sh*T3KCN-`Ly&A0NA25g`E)At50#F_9B!GBvne5s?rv zz)GW_O(JYg>$30kRkkoXou?LuL`ZYk-!?0{E*u`dCJOFabi`6=F){aq*ik)kWh+iH zx9bm75LkQlq3#9}H*W44yv$7>k^Bm~Z_&fA8y9h@Ua*$@zIV}&jf>O_ZM|+qKP#&4 zn%Iz1H?s4-9g|*M(>=M#BYozgy-#p#MoDeY)Yh;23!O0i{5UzJY`)(&+jre`dSIus zWWBsy#+k8xB=CRN+vy$Ea$@p$dm89IEyhRUJ4~;ZTJ`)gGg&6Nf4ss&J^!mT91Eu`7&g+&+~Uxgd1aAVb8k5xFn3Wq8js`eUHIKB#pDjcfz z8dW&H3Wq8js`eUHIKB#pDjcfz8dW&H3Wq8js`eUHIKB#pDjcfz8dbROt8l2op=z&D zh2yJmsKTLYuTh2Lt8l2op=z&Dh2yJmsKTLYuTh2Lt8l2op=z&Dh2yJmsKTLYuTh2L zt8l2op=z&Dh2yJmsKTLYuTh2Lt8l2op=z&Dh2yJmsKTLYuTh2Lt8l2op=z&Dh2yJm zsKTLYuTh2Lt8l2op=z&Dh2yJmsKTLYuTh2Lt8l2op=z&Dh2yJmsKTLYuTh2Lt8l2o zp=z&Dh2yJmsKTLYuTh2Lt8l2op=z&Dh2yJmsKTLYuTh2Lt8l2op=z&Dh2yJmsKTLY zuTh2Lt8l2op=z&Dh2yJmsKTLYuTh2Lt8l2op=z&Dh2yJmsKTLYuTh2Lt8l2op=z&D zh2yJmsKTLYuTk|s!BqaN?;Q&sNLrS_$(A1~HX zez5Wo-coxewb;%_cufHI#5nYAEIk5JP$ z&-L`@PX-mQ%@P*4FlG1!8boq&=)4COt7~C=nmak{G#_>{gm<2VB!rBRB6Hp0FRwQ# z&pv)UJ+RY>`-htU1OHHSn5KrVu{PaF##5{Ax&^oF(w~O=+|2Nw-9f*)O|WtreDi&wu(xiaw>ov~;#Fh3%nY=J%fy?R~FqMb)@$67*`? zArfdEAw3Dph^79Xib~dmwaV?C!x^y`V|i}dIdG3BXIy=ysi{kGU+?sgnbONz0&Wa| zFZ)ti(8uq9t90nR2|U|(-Sq#G7K!;!Go(*>pOswCK@B2@d@3`<3sk+}CN(l2Lf%_; zh>=ooL7!J!@p4)PMutBYw0f5DER5}*a(56@e=s-aedPn0OtF!+LwD=A6GIAB(54um z&NvHA=Tzi*!fXSEn%^EfRauN8O~2H9nLEQ^dDiJFkzuEXN>a17p+1j;+DWcpW=1|% z)cz6~u{Pi2Q(9@&$V^Dx6OZAd8lN6jN6Ra#n+dCyn=3L%K`In0n5eS=mc% zvKMka_fxnV`mYypjR}77w1zVn(n~)-C)J)lnf^s3I3wHW!P?|mw*)5!uc&Qn7k%b^ zr$|>J$G021n~CR(qJvuHZL3Dus(q+p(2BFu*0P%dXID>D_QhcUKZ`Y$wk{Mik$y}A z)c&PmG*?dabH~HY6hexgVb5Q->YmPG&lOfYC>oNX1Rr`Is0#4h{rsfItEHOlw1&_} zpJvN3u=I~r)D6tndr&O|`={r)usT(A`0@uwh6nQzx2C1rv82~1pC)vL@^- z=LR1d4=~E$nV?M%`whl_EE%IMmi*OStI*zX%`K9m$#!=`fMRcnOpWMZtbik@+= zCSvP2-RcUn-K6Y?dhzrZ)@e_cA(t`0g~qH$XnqX9MtN;oQm8lRi^|2!GKoY!w@-z3 zDb-HpFF&-CSA_7;tKMOYSd63Ma8f#7OY-6tzG@C*K^>ugCyCL@5TpUw9&-JHao>_h z?>+MA4;A$gf!_8e$AH&evNkuaJGnR1^!Sp)b%_oPi+c>bpM9<81GlC<;`Q`yA$ugR zt2%qQR^E*e$&b-(T$^Nwb4%*H_F;FJbsU^&ueiNq^gR3U zT`lwy@>BEeDyX1u+>!jQyCIy;=9!6zGq2rEm@XVOqPUZ)bTQaEoSQD9>xvr7@~k}1 zyCk;cFT%0Mm@<36AeYiN+?MzwOJ1bC;3~=X)l(JGoexP#ebPf$;WbsCHzvNyr%H2w z|M3g)Q(Wc4Z(8z&1~25I?!9Okx^OOX^%9bwWDQ~NRAOozYu{FWFQLmTbiuEry>YB+ zseZmOkfyOHw{6iw;+%$IMAuwLPn$k-SBBty4Kv(et@fbsJBz-J@k?Ju#7? zucSW&NCLiq=1!)6+P18kAO^5V09!YSLUuuk=bsN+<$=scJpx)1K6_Smr}p%^+NDlX zK8rMhzO!VHG_vYKP_c)rEgr0YAFeLJ`C!K1@%GB%kRR{p5+lHGf=)e~aQijdCXr;j zFM!l(ZgYYn%4RjSv9f8M^K5g+oW1H>3p-m^nO2LIH}(<+trmTlVrn-!ss@haV$qMwqV@coZ&^%pCVEclsMs`h~tuUtyWc-J{ ziGj>^Hkz@bZa&=0&w8XvR!iGGI&qLOQ>03u`-{+mKJ}Gu0bz1)5qFnpD@|Jr0Ht%x zDJYUZA>)+R-Un3XxDI|{PT1gW_89kGiF}{^vCC{Jp>A7r+A~A0qRVjW%tf;OVXtg$ zZS97V5)+epz3lDJoH=9rUbOAitEPA9$6OL`D9Phr0RSq?1Xs?hs$_A#)W_E%$s{SVi$v^Oj-iBk40YrJZ!ujRHDn%B5# zxjc=4&l!Be0FN~sF~E2b9R@HhyDQ51WG9}$y&`?5%z=9wg%)a_tsmF-i|ILX@vH|k z+JfUA!l2DVSQ<7MVwP z;z>Pt!)rSlTxEi~!Ljzi+Ug6lml)tQQ`!7~?=?6t;SQwMW;Cj%*3o)g21c1U?HFFE zt5q%|EM2eHeaWwRGd$06_Q?G&nU-N>r|1qU6S3F6snS!f7B1+Sdtuwwlva-c?z=#S zHdlUq%kRTRml`b4n*)Z8UiwW>PKwS*KJ{8$pQ+JhzZ9UF4zHv$rW;iS+5R`T^#fC*RlvhN?CpvPx`;qg91IcF& z&j5%E>@BqpyoXTgvZSVcljXk@+I|_DROz5XTQi0pf3ue|2X7Ax?(?W;kiD zp4)V0q2%C2spC)tTu1mNNw2FSZDRN{_$z|euuq~YIYMSH!`gM%2baV*qPm+eSox%; zArMUC@m4;_A-abQ6;||xgfW=~kz?^0_*40bRik!n+V9d!{FG235~cnR!u+di{}&S!*kQ?yhJ~ zsv_MJLv=A_sbmU-s{ZjcMfZaKi|c}IeMrhDDNmO- zkj16~61}Wbt?93(%3e6F%2k$bT^&BJJAwfce5jXDG@tc4he5fdqoT|6Df6EBZZQAJ zHE(ydK0@xqmo9bYaY4s+KxRc{W_~+6= z86w^_DjO)b!O6F;9n$MgIag5@8(e2N*YGZs;oRr}pHc5Adi5_yjk=X>m?G+!IKh4@ z+OLFYV4%+TMB2L$@-L8v0Dr1iCY>GL=Z0z#a=eJoOYuopx*N+2PjpvFW+pfeF&tM8 zdFr>FW5S)m(^GQP&hFBt=>cn=oai3a_sub9f%YruOxMR-iH{Ppx^g8qG3zev#N$`g z6QHX@N43x_1C*o-UwHXPOd$^4?CO*CC>inoHRbYMztnAknQLcUy-{uMrAvDR+*B(arTiuF%{m8 zkMgg&whh>ho_&R&rFG^cKc=0S5t@B0+~+6D=g~w712+cUj5~P}V?y<=>J-f`;+)j{MYc0%-RW4*q%5ht3I|@y zB0pEOzV|(RdlfRqJhO%1?GeWSX(Xs`wPI{csx75vJCP)Q=24c%ZCz&7=l0y}xpdc#nizB& zsr&NZd>6vmgnYD#2;N$g;mR*C+-3tELebu$4(o3_PCt^k;7-|*)GK32P3t70 z5>^w{cT@{s;X;w8_<5chV>8{SXWI`30BRkQVQkuu4k*Kq1Rs}&B-C$j5ow(&6h3M# z#<}SiRF@I4S-Cys)0-rG6qdg+8v1UX8Tz@$hp%8RGcz3ayeDQ~mLcPtsoeQDM7$8%YzT2_o9AOll^2)aA_TW_2#ZJ%c zPi7HFb_ZVQY{S@q(TpZ6Uk1V)=2Vz-In9PIV$t!_Ywny+DUOfr(g$n+Zw`;r12gMU z5+tja!Y1DYybZbGY}9K^)gn)oO@lh;!)w~Il8GH}CbXu|Q?mmRpHz!V9^@8gn?PF( zN5j+XV8^4~%k6vK#UYJF%Ri)2?B-YON!=Hshv2+D=0UVQbZ?q^&%gam4R~ zPZ#fS=yFH3xFYpSIjfP0FHh6$eduBHJ(5WpViY$fP+2>9LbsahXlOFd^zms&zag06 zXlpsX`gqNrt(18-Xj64lb!Ot)vL&ssQNmig4&t>M@P@~43?7k3={Bj zf1EQ(FY%bOh#k6isx3d_pP(Q`4PAZ0 zLfO(u;}3UGVEewCpq1CUh7jmkXU;SuGn+k(7L^Sc)ZW~Qn4C#U3ixHd@(-GN;rthG zaC-l_5Cbe8U4x*fpRPIOVu0ex8ChC5a*hkfH)xA{8+-@Lwc7w4;kP8xk?U8hP|Ur^qGqrU zG+Urdnod|hp|$b}P17_wi;kXFOBich=q~5=yO-U^pmoSj+Oy{VcY3|oPHBu? zhvsn3XRBP+X1)4+QSE2vU%4Mp5)&1ccnO=fTPdWE@Q_oj-V9y!W(thcA8$9FbQ_3< z2z@~|E>As|eYj%xH(zUYxb5s6GVCUvrH?;<{`@ih#WQCNt)8W4q(8f_OeA}X8h@Ux znC#Y*a5dnd{|woXd!js@Ed1(rq1^G7ZP|_Xg0&EX5Te=rlfwf$Ii{K9^`!|MYKd`< zs`^Jb(ihswrD?U{W9Qdv5cG+i_%g3v(A2es;gW+$kM`r}H&KM!9L=$_tO z+<6?&Jm|2c&=EJ-7=*5K179v~Ntk1RdBsO-)B_lxuNfTyz848}fg&3^8UtK$*9fTD zlKPvD+N?HY_<9vh3-f^-na^UOD9!1?P1+g1xcXZ>!`(G_Po|$clu(-6v+| zPCCmy9-;2pZ3g+-MfHTbI>F8tV zhWi|)Wu<+T`{W_<$hV=rEyyvmsx8O^3_veqlX@bA?O|W9bz+*WUTw3|D~4(O=||o9 zny30ysjGG0(y0@vUmewy7m}Oy1d0p1{Hm*ZgdqtAc}3!#+!FVtP?S_l)8m!K%0~CH zGOMh(Z4$GeWIRiJk(6^2Nn4yQTj_d;kFjpk`6m~B=()b=W!8`79_*p#p?9)xr*o&w zdBSq2tv030Jlva5o9qGl`F4rDU1A~VIDW!BRn?ePCP#tf3VFEkLH*{SNv1~79Blde z?DR)7ZpC%u!q$V_`uWjQg~RHJ>t z*jTTIR-5%&Bl}%C`C=AdF?O!>H;lAb44bSTr-yz?Sgzc7+oo$Ui%zH-MW3GQvE(~> zp>PUW%IKo5OB=||7Ug3#6l?!ZGF;-rXM4L*Qz9KE|A&smKj?n=E zPQyIBn%J9@3a4&pSLNDy-szS*c)%}cQxjR!TrVRQq4@eRL3EaSgIDN=K(}eEK%J>DJ-tD!HbzPwx{La!~4%KE}pX3o1!7 z1WydS$I@f%V`EaDJ$n|ZKK|I7$tW%Mg0O~YOSOiG$ie!0b@o1#O{mtwo9Pmt;9&KU zjO54m_R;J{m!G9Q9DaQCd_Q}zVKwx!Hz7l4z;IYc(a?&xWA+0iTgixFN|(uUAA^z- z9g!SC&bo(muv+`|TRcK5s}`h|CFw$S4BRZ2oMm4}Nl3K)=%cs z7cbXiejkyxY0KHl%Z>#)P&~yY$(_K8+ISPKj}_ydi*Cg{E-26Z^xOodLU+4hq6GI2 z#YfXIQ^}vJn6{q=Q@t`J7cI6YQ#)XIv_`JMX+6#^BnEkQdkF&Hd@~U{4lM~P5olxP zW9~*<_!RX8DAE)OA&O7wd&o%KtkbU2SG-@<)@qgGeaipR#gS(znW;9xwopZPgvImJ zq5J-E_($}tn1c85*gC7~xh?l@3cC-wy^WaxhfF;$SoUlnjg_$Rq2o*@s9E2A<@&LX z-Lbm%J^#dFu{zPd5vtP>h%kbW_Eh-N>Zr5}f86~RWMhCEs~Dg@ZgW2dxGe&Xv-5ld z9~h|Cg#DAY8HvF0c{T|}wc+z>evGIVXi31`arQ%ek8c8L=fj6ZCRGKl?Hk9| zSaTdEo9!39?_LVi)2$^qz#gpZa8RHzPiD<3FYNw(`4K@MzTS4zB{AoHoGf45lLt}K z2}0Q1&}T8g#KtTHU4kOsfexZTYX;)BcPP*yjXNs|^So$S8M+~@9*L@hJb-M;$!KAK z7v(#vQONmD4B!J_UY$?M!vOxAs9KQj%tq#=FEgW;8oxB6Lql3Iz;t$n|Bpm{C%}3d zR7WpWj7<;DO!-w|fY*7OA{Zd)^D7LnH9bFQhXY{ov)kkwh1WN- z7m)KOO)m88boR9RS6$b7@ZnJDM#rZb(U#T^u5H6Qnh^LoRSLn*ck=n4<%y6s3Q>RQ z<^J=T+AniS%zx^09hnMJInuT9Pg&c7#?mos>t>fTCbcfF1)K}<7L>8S{Y52;Ike#6 zi@G}U!^t13SR)UG+XMnFHwz4O2GmmolkRv^Ehj{&Wy2G5Lv=?(iI%|^L%Bk$jmE{G zbU9AMf0|Q8b@OoH1{agn?p~ORLp#@uQmt8k`R^8s-f% zb02m~_k8rWd2D3&*&|Yw?_A~sr0trp>4>VO?m39vBy0=c_+u~^Z{-Z5nZz;xPO{lH zb+NNT&=rV+V;#(S0UF(sB69nye~?;>F{k9{M(ygL%}SOg*m+Hn|7TK`u`tvu|Pmy0xfE z^z|ND)k4A9-nbh zp4C*&rfm}g~(!T4KR|YS))x}4z7LCFmtJ#IEYUqVU%%QGoEvji!jX=JDY0a!o z4BGU99WF`vc@_tuLF~?_A3cgM8<`{>;a*Y00I`Cz)hy z4Zch<4o}T)MrN=EpHWZdth`m%rpA(e1>Gkmz-SSz*AdfD5VuTtZ9uoH50Z$i_rn12 zY^AvJ0nWR9N-~|HD?yu8QFTo5PQJY+#(6S@S)OUSYlrwxeQX$vrmrW4-JZ$aXRjwK z>rVTghEAV-noj@N^i;6d=P)%ZlCDICdLe8<%*KB_JQs zvoj&#{TQG$c6zo(_ZHnWxvpYdG5?dm5;-f)%RI8RYd+TjTp-F!SMWDrXc&b5yG+8!&y%yj(<*!C%|g^M zq>WN(`}lRL)~9n1LTj+Co~pN_b&%D!(bms#GT_Zz*1X^m&3jj$RaeR9eIPPLig43L z@6W$ycgw7}+MtGrU1C^c+JQ9(-{E%4z@eB8uAQ{%0Dq#{A>Wut+L5X?Dv?b@?8f!- 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 From 5f5322423ed7002f04d842d17c3fe53d131f6ee6 Mon Sep 17 00:00:00 2001 From: Julien Constant Date: Sat, 11 Nov 2023 16:12:49 +0100 Subject: [PATCH 7/7] fix(aspect_ratio): better fix using GCD --- src/modules/files/images.service.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modules/files/images.service.ts b/src/modules/files/images.service.ts index 6cfb8c3..995e0ed 100644 --- a/src/modules/files/images.service.ts +++ b/src/modules/files/images.service.ts @@ -16,8 +16,15 @@ type WriteImageOptions = WriteFileOptions & { export class ImagesService extends FilesService { async validateAspectRatio(buffer: Buffer, aspect_ratio: aspect_ratio): Promise { const { width, height } = await sharp(buffer).metadata(); - const [aspectWidth, aspectHeight] = aspect_ratio.split(':').map((s) => parseInt(s, 10)); - return Math.trunc((width / height) * 100) / 100 === Math.trunc((aspectWidth / aspectHeight) * 100) / 100; + + const gcd = (...arr: number[]): number => { + const _gcd = (x: number, y: number) => (!y ? x : gcd(y, x % y)); + return [...arr].reduce((a, b) => _gcd(a, b)); + }; + + const gcdResult = gcd(width, height); + + return `${width / gcdResult}:${height / gcdResult}` === aspect_ratio; } async convertToWebp(buffer: Buffer): Promise {