universo-platform-3d

Форк
0
870 строк · 26.4 Кб
1
import {
2
  BadRequestException,
3
  Body,
4
  Controller,
5
  Delete,
6
  Get,
7
  Optional,
8
  Param,
9
  ParseIntPipe,
10
  Patch,
11
  Post,
12
  Query,
13
  Res,
14
  UploadedFile,
15
  UseGuards,
16
  UseInterceptors,
17
  UsePipes,
18
  ValidationPipe
19
} from '@nestjs/common'
20
import { FileInterceptor } from '@nestjs/platform-express'
21
import {
22
  ApiBody,
23
  ApiConsumes,
24
  ApiCreatedResponse,
25
  ApiOkResponse,
26
  ApiParam,
27
  ApiProperty,
28
  ApiQuery
29
} from '@nestjs/swagger'
30
import { plainToInstance } from 'class-transformer'
31
import { validateOrReject } from 'class-validator'
32
import { Types } from 'mongoose'
33
import { FirebaseTokenAuthGuard } from '../auth/auth.guard'
34
import { UserToken } from '../auth/get-user.decorator'
35
import { PublicFirebaseAuthNotRequired } from '../auth/public.decorator'
36
import { ASSET_TYPE } from '../option-sets/asset-type'
37
import {
38
  FileUploadApiResponse,
39
  FileUploadPublicApiResponse
40
} from '../util/file-upload/file-upload'
41
import { AssetId, UserId } from '../util/mongo-object-id-helpers'
42
import {
43
  PaginatedResponse,
44
  PaginationInterface
45
} from './../util/pagination/pagination.interface'
46
import { AssetApiResponse, AssetUsageApiResponse } from './asset.models'
47
import { Asset } from './asset.schema'
48
import { AssetService } from './asset.service'
49
import {
50
  CreateAssetDto,
51
  CreateMapDto,
52
  CreateMaterialDto,
53
  CreateTextureDto
54
} from './dto/create-asset.dto'
55
import {
56
  PaginatedSearchAssetDtoV2,
57
  getPopulateFieldsFromPaginatedSearchAssetDto
58
} from './dto/paginated-search-asset.dto'
59
import { SearchAssetDto } from './dto/search-asset.dto'
60
import {
61
  AddAssetPurchaseOptionDto,
62
  UpdateAssetDto
63
} from './dto/update-asset.dto'
64
import { PopulateField } from '../util/pagination/pagination.service'
65
import { TAG_TYPES } from '../tag/models/tag-types.enum'
66
import { AddTagToAssetDto } from './dto/add-tag-to-asset.dto'
67
import { UpdateAssetTagsDto } from './dto/update-asset-tags.dto'
68
import { IncludeSoftDeletedAssetDto } from './dto/include-soft-deleted-asset.dto'
69
import { DomainOrAuthUserGuard } from '../space/guards/DomainOrAuthUserGuard.guard'
70
import { GetAssetsPriceDto } from './dto/assets-price.dto'
71
import { Response } from 'express'
72

73
/**
74
 * @description Swagger generation doesn't support generics, so for each paginated response, it has to extended the PaginatedResponse class and implement the PaginationInterface
75
 * @date 2023-07-10 16:33
76
 */
77
export class AssetFullDataPaginatedResponse
78
  extends PaginatedResponse
79
  implements PaginationInterface
80
{
81
  @ApiProperty({ type: [Asset] })
82
  data: Asset[]
83
}
84
@UsePipes(new ValidationPipe({ whitelist: false }))
85
@Controller('asset')
86
export class AssetController {
87
  constructor(private readonly assetService: AssetService) {}
88

89
  /*****************************
90
   PUBLICLY ACCESSIBLE ENDPOINTS
91
   ****************************/
92

93
  /** Search for an asset */
94
  @PublicFirebaseAuthNotRequired()
95
  @Get('search')
96
  @ApiOkResponse({ type: [AssetApiResponse] })
97
  public async search(@Query() searchAssetDto: PaginatedSearchAssetDtoV2) {
98
    return await this.assetService.searchAssetsPublic(searchAssetDto, true)
99
  }
100

101
  /**
102
   * @description Same as above but no populate
103
   * @date 2023-07-12 23:49
104
   */
105
  @PublicFirebaseAuthNotRequired()
106
  @Get('search-v2')
107
  @ApiOkResponse({ type: [AssetApiResponse] })
108
  public async searchV2(@Query() searchAssetDto: PaginatedSearchAssetDtoV2) {
109
    return await this.assetService.searchAssetsPublic(searchAssetDto, false)
110
  }
111

112
  /***********************
113
   AUTH REQUIRED ENDPOINTS
114
   **********************/
115

116
  /**
117
   * Get Mirror Library assets
118
   * TODO this needs to be paginated
119
   */
120
  @Get('library')
121
  @FirebaseTokenAuthGuard()
122
  @ApiOkResponse({ type: [AssetApiResponse] })
123
  @ApiQuery({ required: false })
124
  public async getMirrorPublicLibraryAssets(
125
    @Query() searchAssetDto?: PaginatedSearchAssetDtoV2
126
  ) {
127
    return await this.assetService.findMirrorPublicLibraryAssets(
128
      searchAssetDto,
129
      undefined,
130
      true
131
    )
132
  }
133

134
  /**
135
   * @description Same as getMirrorPublicLibraryAssets, but doesn't populate the fields
136
   * @date 2023-07-12 23:33
137
   */
138
  @Get('library-v2')
139
  @ApiOkResponse({ type: [AssetApiResponse] })
140
  @FirebaseTokenAuthGuard()
141
  @ApiQuery({ required: false })
142
  public async getMirrorPublicLibraryAssetsV2(
143
    @Query() searchAssetDto?: PaginatedSearchAssetDtoV2
144
  ) {
145
    return await this.assetService.findMirrorPublicLibraryAssets(
146
      searchAssetDto,
147
      undefined,
148
      false
149
    )
150
  }
151

152
  @Post()
153
  @FirebaseTokenAuthGuard()
154
  @ApiCreatedResponse({ type: AssetApiResponse })
155
  @ApiBody({
156
    type: CreateAssetDto
157
  })
158
  public async create(
159
    @UserToken('user_id') userId: UserId,
160
    @Body()
161
    createAssetDto: CreateAssetDto &
162
      CreateMaterialDto &
163
      CreateTextureDto &
164
      CreateMapDto // See https://www.loom.com/share/7e09d2777ef94368bcd5fd8c8341b5ef for walkthrough of DTOs with discriminators
165
  ) {
166
    // check whether to use the subclass as determined by assetType
167
    switch (createAssetDto.assetType) {
168
      case ASSET_TYPE.MATERIAL:
169
        const dtoMaterial = plainToInstance(CreateMaterialDto, createAssetDto)
170
        try {
171
          await validateOrReject(dtoMaterial)
172
        } catch (error) {
173
          console.error(error.join(','))
174
          throw new BadRequestException(error.join(','))
175
        }
176
        return await this.assetService.createMaterial({
177
          ownerId: userId,
178
          ...dtoMaterial
179
        })
180
      case ASSET_TYPE.TEXTURE:
181
        const dtoTexture = plainToInstance(CreateTextureDto, createAssetDto)
182
        try {
183
          await validateOrReject(dtoTexture)
184
        } catch (error) {
185
          console.error(error.join(','))
186
          throw new BadRequestException(error.join(','))
187
        }
188
        return await this.assetService.createTexture({
189
          ownerId: userId,
190
          ...dtoTexture
191
        })
192
      case ASSET_TYPE.MAP:
193
        const dtoMap = plainToInstance(CreateMapDto, createAssetDto)
194
        try {
195
          await validateOrReject(dtoMap)
196
        } catch (error) {
197
          console.error(error.join(','))
198
          throw new BadRequestException(error.join(','))
199
        }
200
        return await this.assetService.createMap({
201
          ownerId: userId,
202
          ...dtoMap
203
        })
204
      default:
205
        const dtoAsset = plainToInstance(CreateAssetDto, createAssetDto)
206
        try {
207
          await validateOrReject(dtoAsset)
208
        } catch (error) {
209
          console.error(error.join(','))
210
          throw new BadRequestException(error.join(','))
211
        }
212
        return await this.assetService.createAsset({
213
          ownerId: userId,
214
          ...dtoAsset
215
        })
216
    }
217
  }
218

219
  @Get('recents')
220
  @FirebaseTokenAuthGuard()
221
  @ApiOkResponse({ type: [AssetApiResponse] })
222
  public async getUserRecentInstancedAssets(
223
    @UserToken('user_id') userId: UserId,
224
    @Query() searchAssetDto?: PaginatedSearchAssetDtoV2
225
  ) {
226
    return await this.assetService.getRecentInstancedAssets(
227
      userId,
228
      searchAssetDto
229
    )
230
  }
231

232
  @Post('new')
233
  @FirebaseTokenAuthGuard()
234
  @ApiConsumes('multipart/form-data')
235
  @ApiBody({ schema: { type: 'file' } })
236
  @ApiCreatedResponse({ type: AssetApiResponse })
237
  @UseInterceptors(
238
    FileInterceptor('file', {
239
      limits: { fieldSize: 20 * 1024 * 1024, fileSize: 20 * 1024 * 1024 } // 20MB limit. Arbitrary. I wanted to reference a class property of this.fileSizeBytes, but not sure if that's accessible in a decorator
240
    })
241
  )
242
  public async createWithUpload(
243
    @UserToken('user_id') userId: UserId,
244
    @Body()
245
    createAssetDto:
246
      | CreateAssetDto
247
      | CreateMaterialDto
248
      | CreateTextureDto
249
      | CreateMapDto, // See https://www.loom.com/share/7e09d2777ef94368bcd5fd8c8341b5ef for walkthrough of DTOs with discriminators
250
    @UploadedFile() file: Express.Multer.File
251
  ) {
252
    // check whether to use the subclass as determined by assetType
253
    switch (createAssetDto.assetType) {
254
      case ASSET_TYPE.MATERIAL:
255
        const dtoMaterial = plainToInstance(CreateMaterialDto, createAssetDto)
256
        try {
257
          await validateOrReject(dtoMaterial)
258
        } catch (error) {
259
          console.error(error.join(','))
260
          throw new BadRequestException(error.join(','))
261
        }
262
        return await this.assetService.createMaterialWithUpload(
263
          { ownerId: userId, ...dtoMaterial },
264
          file
265
        )
266
      case ASSET_TYPE.TEXTURE:
267
        const dtoTexture = plainToInstance(CreateTextureDto, createAssetDto)
268
        try {
269
          await validateOrReject(dtoTexture)
270
        } catch (error) {
271
          console.error(error.join(','))
272
          throw new BadRequestException(error.join(','))
273
        }
274
        return await this.assetService.createTextureWithUpload(
275
          { ownerId: userId, ...dtoTexture },
276
          file
277
        )
278
      case ASSET_TYPE.MAP:
279
        const dtoMap = plainToInstance(CreateMapDto, createAssetDto)
280
        try {
281
          await validateOrReject(dtoMap)
282
        } catch (error) {
283
          console.error(error.join(','))
284
          throw new BadRequestException(error.join(','))
285
        }
286
        return await this.assetService.createMapWithUpload(
287
          { ownerId: userId, ...dtoMap },
288
          file
289
        )
290
      // Default to base Asset class
291
      default:
292
        const dtoAsset = plainToInstance(CreateAssetDto, createAssetDto)
293
        try {
294
          await validateOrReject(dtoAsset)
295
        } catch (error) {
296
          console.error(error.join(','))
297
          throw new BadRequestException(error.join(','))
298
        }
299
        return await this.assetService.createAssetWithUpload(
300
          { ownerId: userId, ...dtoAsset },
301
          file
302
        )
303
    }
304
  }
305

306
  /**
307
   * Get player's created assets
308
   * TODO this needs to be paginated
309
   */
310
  @Get('me')
311
  @FirebaseTokenAuthGuard()
312
  @ApiOkResponse({ type: [AssetApiResponse] })
313
  @ApiQuery({ required: false })
314
  public async getAssetsForMe(
315
    @UserToken('user_id') userId: UserId,
316
    @Query() searchAssetDto?: PaginatedSearchAssetDtoV2
317
  ) {
318
    return await this.assetService.findAllAssetsForUserIncludingPrivate(
319
      userId,
320
      searchAssetDto,
321
      undefined,
322
      true
323
    )
324
  }
325
  /**
326
   * Same as above but no populate
327
   * TODO this needs to be paginated
328
   */
329
  @Get('me-v2')
330
  @FirebaseTokenAuthGuard()
331
  @ApiOkResponse({ type: [AssetApiResponse] })
332
  @ApiQuery({ required: false })
333
  public async getAssetsForMeV2(
334
    @UserToken('user_id') userId: UserId,
335
    @Query() searchAssetDto?: PaginatedSearchAssetDtoV2
336
  ) {
337
    return await this.assetService.findAllAssetsForUserIncludingPrivate(
338
      userId,
339
      searchAssetDto,
340
      undefined,
341
      false
342
    )
343
  }
344

345
  /**
346
   * Get all player's accessible assets. Accessible assets are assets that are public, or assets that are private but owned by the user.
347
   */
348
  @Get('my-library')
349
  @FirebaseTokenAuthGuard()
350
  @UsePipes(new ValidationPipe({ transform: true }))
351
  @ApiOkResponse({ type: AssetFullDataPaginatedResponse })
352
  @ApiQuery({ required: false })
353
  public async getAllAccessibleAssetsOfUser(
354
    @UserToken('user_id') userId: UserId,
355
    @Query() searchAssetDto: PaginatedSearchAssetDtoV2
356
  ): Promise<AssetFullDataPaginatedResponse> {
357
    return await this.assetService.findAllAccessibleAssetsOfUser(
358
      userId,
359
      searchAssetDto,
360
      true
361
    )
362
  }
363

364
  /**
365
   * @description Same as above but no populate
366
   * @date 2023-07-12 23:45
367
   */
368
  @Get('my-library-v2')
369
  @FirebaseTokenAuthGuard()
370
  @UsePipes(new ValidationPipe({ transform: true }))
371
  @ApiOkResponse({ type: AssetFullDataPaginatedResponse })
372
  @ApiQuery({ required: false })
373
  public async getAllAccessibleAssetsOfUserV2(
374
    @UserToken('user_id') userId: UserId,
375
    @Query() searchAssetDto: PaginatedSearchAssetDtoV2
376
  ): Promise<AssetFullDataPaginatedResponse> {
377
    return await this.assetService.findAllAccessibleAssetsOfUser(
378
      userId,
379
      searchAssetDto,
380
      false
381
    )
382
  }
383

384
  @Get('recent')
385
  @FirebaseTokenAuthGuard()
386
  @UsePipes(new ValidationPipe({ transform: true }))
387
  @ApiOkResponse({ type: [AssetApiResponse] })
388
  public async getRecentAssetsForUser(
389
    @UserToken('user_id') userId: UserId,
390
    @Query('limit', ParseIntPipe) @Optional() limit?: number,
391
    @Query()
392
    includeSoftDeletedAssetDto?: IncludeSoftDeletedAssetDto
393
  ) {
394
    const assets = await this.assetService.findRecentAssetsOfUserWithRolesCheck(
395
      userId,
396
      includeSoftDeletedAssetDto.includeSoftDeleted,
397
      limit,
398
      true
399
    )
400
    return assets
401
  }
402

403
  @Get('recent-v2')
404
  @FirebaseTokenAuthGuard()
405
  @UsePipes(new ValidationPipe({ transform: true }))
406
  @ApiOkResponse({ type: [AssetApiResponse] })
407
  public async getRecentAssetsForUserV2(
408
    @UserToken('user_id') userId: UserId,
409
    @Query('limit', ParseIntPipe) @Optional() limit?: number,
410
    @Query()
411
    includeSoftDeletedAssetDto?: IncludeSoftDeletedAssetDto
412
  ) {
413
    const assets = await this.assetService.findRecentAssetsOfUserWithRolesCheck(
414
      userId,
415
      includeSoftDeletedAssetDto.includeSoftDeleted,
416
      limit,
417
      false
418
    )
419
    return assets
420
  }
421

422
  @Get('my-assets')
423
  @FirebaseTokenAuthGuard()
424
  @ApiOkResponse({ type: AssetFullDataPaginatedResponse })
425
  @ApiQuery({ required: false })
426
  public async getPaginatedMyAssets(
427
    @UserToken('user_id') userId: UserId,
428
    @Query() searchAssetDto?: PaginatedSearchAssetDtoV2
429
  ): Promise<AssetFullDataPaginatedResponse> {
430
    return await this.assetService.findPaginatedMyAssetsWithRolesCheck(
431
      userId,
432
      searchAssetDto,
433
      true
434
    )
435
  }
436

437
  /**
438
   * @description Same as above but no populate
439
   * @date 2023-07-12 23:43
440
   */
441
  @Get('my-assets-v2')
442
  @FirebaseTokenAuthGuard()
443
  @ApiOkResponse({ type: AssetFullDataPaginatedResponse })
444
  @ApiQuery({ required: false })
445
  public async getPaginatedMyAssetsV2(
446
    @UserToken('user_id') userId: UserId,
447
    @Query() searchAssetDto?: PaginatedSearchAssetDtoV2
448
  ): Promise<AssetFullDataPaginatedResponse> {
449
    return await this.assetService.findPaginatedMyAssetsWithRolesCheck(
450
      userId,
451
      searchAssetDto,
452
      false
453
    )
454
  }
455

456
  @Get('mirror-assets')
457
  @FirebaseTokenAuthGuard()
458
  @ApiOkResponse({ type: AssetFullDataPaginatedResponse })
459
  @ApiQuery({ required: false })
460
  public async getPaginatedMirrorAssets(
461
    @UserToken('user_id') userId: UserId,
462
    @Query() searchAssetDto?: PaginatedSearchAssetDtoV2
463
  ): Promise<AssetFullDataPaginatedResponse> {
464
    return await this.assetService.findPaginatedMirrorAssetsWithRolesCheck(
465
      userId,
466
      searchAssetDto,
467
      this.assetService.standardPopulateFields
468
    )
469
  }
470

471
  /**
472
   * @description Same as above but no populate
473
   * @date 2023-07-12 23:41
474
   */
475
  @Get('mirror-assets-v2')
476
  @UseGuards(DomainOrAuthUserGuard)
477
  @ApiOkResponse({ type: AssetFullDataPaginatedResponse })
478
  @ApiQuery({ required: false })
479
  public async getPaginatedMirrorAssetsV2(
480
    @UserToken('user_id') userId: UserId,
481
    @Query() searchAssetDto?: PaginatedSearchAssetDtoV2
482
  ): Promise<AssetFullDataPaginatedResponse> {
483
    // parse through populate fields
484
    const populateFields: PopulateField[] =
485
      getPopulateFieldsFromPaginatedSearchAssetDto(searchAssetDto)
486

487
    return await this.assetService.findPaginatedMirrorAssetsWithRolesCheck(
488
      userId,
489
      searchAssetDto,
490
      populateFields
491
    )
492
  }
493

494
  @Get('user/:targetUserId')
495
  @FirebaseTokenAuthGuard()
496
  @ApiOkResponse({ type: [AssetApiResponse] })
497
  @ApiParam({ name: 'targetUserId', type: 'string', required: true })
498
  public async findAllForUser(
499
    @UserToken('user_id') requestingUserId: UserId,
500
    @Param('targetUserId') targetUserId: UserId,
501
    @Query() searchAssetDto?: SearchAssetDto
502
  ) {
503
    // Validate that it's a Mongo ObjectId
504
    if (!Types.ObjectId.isValid(targetUserId)) {
505
      throw new BadRequestException('ID is not a valid Mongo ObjectID')
506
    }
507
    return await this.assetService.findAllPublicAssetsForUserWithRolesCheck(
508
      requestingUserId,
509
      targetUserId
510
    )
511
  }
512

513
  @Get('usage/:assetId')
514
  @FirebaseTokenAuthGuard()
515
  @ApiOkResponse({ type: AssetUsageApiResponse })
516
  @ApiParam({ name: 'assetId', type: 'string', required: true })
517
  public async findOneAssetUsage(
518
    @UserToken('user_id') userId: UserId,
519
    @Param('assetId') assetId: AssetId
520
  ) {
521
    // Validate that it's a Mongo ObjectId
522
    if (!Types.ObjectId.isValid(assetId)) {
523
      throw new BadRequestException('ID is not a valid Mongo ObjectID')
524
    }
525
    return await this.assetService.findAssetUsageWithRolesCheck(userId, assetId)
526
  }
527

528
  @Get('tag')
529
  @ApiOkResponse({ type: AssetFullDataPaginatedResponse })
530
  @UseGuards(DomainOrAuthUserGuard)
531
  public async getAssetsByTag(
532
    @Query() searchDto: PaginatedSearchAssetDtoV2,
533
    @UserToken('user_id') userId: UserId
534
  ) {
535
    return await this.assetService.getAssetsByTag(searchDto, userId)
536
  }
537

538
  @Post('tag')
539
  public async addTagToAssetsWithRoleChecks(
540
    @UserToken('user_id') userId: UserId,
541
    @Body() addTagToAssetDto: AddTagToAssetDto
542
  ) {
543
    return await this.assetService.addTagToAssetsWithRoleChecks(
544
      userId,
545
      addTagToAssetDto
546
    )
547
  }
548

549
  @Patch('tag')
550
  @FirebaseTokenAuthGuard()
551
  public async updateAssetTagsByTypeWithRoleChecks(
552
    @UserToken('user_id') userId: UserId,
553
    @Body() updateAssetTagsDto: UpdateAssetTagsDto
554
  ) {
555
    return await this.assetService.updateAssetTagsByTypeWithRoleChecks(
556
      userId,
557
      updateAssetTagsDto.assetId,
558
      updateAssetTagsDto.tagType,
559
      updateAssetTagsDto.tags
560
    )
561
  }
562

563
  @Delete('tag/:assetId/:tagType/:tagName')
564
  @FirebaseTokenAuthGuard()
565
  @ApiParam({ name: 'assetId', type: 'string', required: true })
566
  @ApiParam({ name: 'tagType', enum: TAG_TYPES, required: true })
567
  @ApiParam({ name: 'tagName', type: 'string', required: true })
568
  public async deleteTagFromAssetWithRoleChecks(
569
    @UserToken('user_id') userId: UserId,
570
    @Param('assetId') assetId: AssetId,
571
    @Param('tagType') tagType: TAG_TYPES,
572
    @Param('tagName') tagName: string
573
  ) {
574
    return await this.assetService.deleteTagFromAssetWithRoleChecks(
575
      userId,
576
      assetId,
577
      tagName,
578
      tagType
579
    )
580
  }
581

582
  @Get(':id')
583
  @UseGuards(DomainOrAuthUserGuard)
584
  @ApiOkResponse({ type: AssetApiResponse })
585
  @ApiParam({ name: 'id', type: 'string', required: true })
586
  public async findOne(
587
    @UserToken('user_id') userId: UserId,
588
    @Param('id') assetId: AssetId
589
  ) {
590
    // Validate that it's a Mongo ObjectId
591
    if (!Types.ObjectId.isValid(assetId)) {
592
      throw new BadRequestException('ID is not a valid Mongo ObjectID')
593
    }
594
    return await this.assetService.findOneWithRolesCheck(userId, assetId)
595
  }
596

597
  @Patch(':id')
598
  @FirebaseTokenAuthGuard()
599
  @ApiOkResponse({ type: AssetApiResponse })
600
  @ApiParam({ name: 'id', type: 'string', required: true })
601
  public async update(
602
    @UserToken('user_id') userId: UserId,
603
    @Param('id') assetId: AssetId,
604
    @Body() updateAssetDto: UpdateAssetDto
605
  ) {
606
    // Validate that it's a Mongo ObjectId
607
    if (!Types.ObjectId.isValid(assetId)) {
608
      throw new BadRequestException('ID is not a valid Mongo ObjectID')
609
    }
610
    return await this.assetService.updateOneWithRolesCheck(
611
      userId,
612
      assetId,
613
      updateAssetDto
614
    )
615
  }
616

617
  @Delete(':id')
618
  @FirebaseTokenAuthGuard()
619
  @ApiParam({ name: 'id', type: 'string', required: true })
620
  @ApiOkResponse({ type: AssetApiResponse })
621
  public async remove(
622
    @UserToken('user_id') userId: UserId,
623
    @Param('id') assetId: AssetId
624
  ) {
625
    return await this.assetService.removeOneWithRolesCheck(userId, assetId)
626
  }
627

628
  /**
629
   * @description This endpoint is used to undo soft delete of an asset.
630
   * (Remove isSoftDeleted and softDeletedAt fields from the asset document)
631
   *
632
   * @date 2023-11-23
633
   */
634
  @Post('undo-soft-delete/:assetId')
635
  @FirebaseTokenAuthGuard()
636
  @ApiParam({ name: 'assetId', type: 'string', required: true })
637
  @ApiOkResponse({ type: String })
638
  public async undoAssetSoftDelete(
639
    @UserToken('user_id') userId: UserId,
640
    @Param('assetId') assetId: AssetId
641
  ) {
642
    return await this.assetService.undoAssetSoftDelete(userId, assetId)
643
  }
644

645
  ///////////////////////////////////////
646
  /// File Uploading Below //////////////
647
  ///////////////////////////////////////
648

649
  @Post('/:assetId/upload')
650
  @FirebaseTokenAuthGuard()
651
  @ApiParam({ name: 'assetId', type: 'string', required: true })
652
  @ApiConsumes('multipart/form-data')
653
  @ApiBody({ schema: { type: 'file' } })
654
  @ApiCreatedResponse({ type: FileUploadApiResponse })
655
  @UseInterceptors(
656
    FileInterceptor('file', {
657
      limits: { fieldSize: 20 * 1024 * 1024, fileSize: 20 * 1024 * 1024 } // 20MB limit. Arbitrary. I wanted to reference a class property of this.fileSizeBytes, but not sure if that's accessible in a decorator
658
    })
659
  )
660
  public async upload(
661
    @Param('assetId') assetId: AssetId,
662
    @UserToken('user_id') userId: UserId,
663
    @UploadedFile() file: Express.Multer.File
664
  ) {
665
    // Validate that it's a Mongo ObjectId
666
    if (!Types.ObjectId.isValid(assetId)) {
667
      throw new BadRequestException('ID is not a valid Mongo ObjectID')
668
    }
669

670
    const { relativePath: currentFile } =
671
      await this.assetService.uploadAssetFileWithRolesCheck({
672
        assetId,
673
        userId,
674
        file
675
      })
676

677
    return await this.assetService.updateOneWithRolesCheck(userId, assetId, {
678
      currentFile
679
    })
680
  }
681

682
  @Post('/:id/upload/public')
683
  @FirebaseTokenAuthGuard()
684
  @ApiParam({ name: 'id', type: 'string', required: true })
685
  @ApiConsumes('multipart/form-data')
686
  @ApiBody({ schema: { type: 'file' } })
687
  @ApiCreatedResponse({ type: FileUploadPublicApiResponse })
688
  @UseInterceptors(
689
    FileInterceptor('file', {
690
      limits: { fieldSize: 20 * 1024 * 1024, fileSize: 20 * 1024 * 1024 } // 20MB limit. Arbitrary. I wanted to reference a class property of this.fileSizeBytes, but not sure if that's accessible in a decorator
691
    })
692
  )
693
  public async uploadPublic(
694
    @Param('id') assetId: AssetId,
695
    @UserToken('user_id') userId: UserId,
696
    @UploadedFile() file: Express.Multer.File
697
  ) {
698
    // Validate that it's a Mongo ObjectId
699
    if (!Types.ObjectId.isValid(assetId)) {
700
      throw new BadRequestException('ID is not a valid Mongo ObjectID')
701
    }
702
    const { publicUrl: currentFile } =
703
      await this.assetService.uploadAssetFilePublicWithRolesCheck({
704
        assetId,
705
        userId,
706
        file
707
      })
708

709
    return await this.assetService.updateOneWithRolesCheck(userId, assetId, {
710
      currentFile
711
    })
712
  }
713

714
  @Post('/:id/upload/thumbnail')
715
  @FirebaseTokenAuthGuard()
716
  @ApiParam({ name: 'id', type: 'string', required: true })
717
  @ApiConsumes('multipart/form-data')
718
  @ApiBody({ schema: { type: 'file' } })
719
  @ApiCreatedResponse({ type: FileUploadPublicApiResponse })
720
  @UseInterceptors(
721
    FileInterceptor('file', {
722
      limits: { fieldSize: 20 * 1024 * 1024, fileSize: 20 * 1024 * 1024 } // 20MB limit. Arbitrary. I wanted to reference a class property of this.fileSizeBytes, but not sure if that's accessible in a decorator
723
    })
724
  )
725
  public async uploadThumbnail(
726
    @Param('id') assetId: AssetId,
727
    @UserToken('user_id') userId: UserId,
728
    @UploadedFile() file: Express.Multer.File
729
  ) {
730
    // Validate that it's a Mongo ObjectId
731
    if (!Types.ObjectId.isValid(assetId)) {
732
      throw new BadRequestException('ID is not a valid Mongo ObjectID')
733
    }
734
    const { publicUrl: thumbnail } =
735
      await this.assetService.uploadAssetThumbnailWithRolesCheck({
736
        assetId,
737
        userId,
738
        file
739
      })
740

741
    return await this.assetService.updateOneWithRolesCheck(userId, assetId, {
742
      thumbnail
743
    })
744
  }
745

746
  @Get('by/start-item')
747
  @FirebaseTokenAuthGuard()
748
  public async getAsset(
749
    @UserToken('user_id') userId: UserId,
750
    @Query() queryParams: PaginatedSearchAssetDtoV2
751
  ) {
752
    return await this.assetService.getPaginatedQueryResponseByStartItemWithRolesCheck(
753
      userId,
754
      queryParams
755
    )
756
  }
757

758
  @Post('/:assetId/purchase-option')
759
  @FirebaseTokenAuthGuard()
760
  @ApiBody({
761
    type: AddAssetPurchaseOptionDto
762
  })
763
  @ApiParam({ name: 'assetId', type: 'string', required: true })
764
  public async addAssetPurchaseOption(
765
    @Param('assetId') assetId: AssetId,
766
    @UserToken('user_id') userId: UserId,
767
    @Body() data: AddAssetPurchaseOptionDto
768
  ) {
769
    return await this.assetService.addAssetPurchaseOption(userId, assetId, data)
770
  }
771

772
  @Delete('/:assetId/purchase-option/:purchaseOptionId')
773
  @FirebaseTokenAuthGuard()
774
  @ApiBody({
775
    type: AddAssetPurchaseOptionDto
776
  })
777
  @ApiParam({ name: 'assetId', type: 'string', required: true })
778
  @ApiParam({ name: 'purchaseOptionId', type: 'string', required: true })
779
  public async deleteAssetPurchaseOption(
780
    @Param('assetId') assetId: AssetId,
781
    @Param('purchaseOptionId') purchaseOptionId: string,
782
    @UserToken('user_id') userId: UserId
783
  ) {
784
    return await this.assetService.deleteAssetPurchaseOption(
785
      userId,
786
      assetId,
787
      purchaseOptionId
788
    )
789
  }
790

791
  @Get('check-if-asset-copied/:assetId')
792
  @FirebaseTokenAuthGuard()
793
  @ApiParam({ name: 'assetId', type: 'string', required: true })
794
  public async checkIfAssetCopied(
795
    @UserToken('user_id') userId: UserId,
796
    @Param('assetId') assetId: AssetId
797
  ) {
798
    return await this.assetService.checkIfAssetCopiedByUser(assetId, userId)
799
  }
800

801
  @Post('copy-free-asset/:assetId')
802
  @ApiParam({ name: 'assetId', type: 'string', required: true })
803
  @FirebaseTokenAuthGuard()
804
  public async copyFreeAsset(
805
    @UserToken('user_id') userId: UserId,
806
    @Param('assetId') assetId: AssetId
807
  ) {
808
    return await this.assetService.copyFreeAssetToNewUserWithRolesCheck(
809
      userId,
810
      assetId
811
    )
812
  }
813

814
  @Get('download/:assetId')
815
  @ApiParam({ name: 'assetId', type: 'string', required: true })
816
  @FirebaseTokenAuthGuard()
817
  public async downloadAsset(
818
    @UserToken('user_id') userId: UserId,
819
    @Param('assetId') assetId: AssetId,
820
    @Res() res: Response
821
  ) {
822
    return await this.assetService.downloadAssetFileWithRoleChecks(
823
      userId,
824
      assetId,
825
      res
826
    )
827
  }
828

829
  @Get('/space/:spaceId')
830
  @FirebaseTokenAuthGuard()
831
  public async getAllAssetsBySpaceIdWithRolesCheck(
832
    @UserToken('user_id') userId: UserId,
833
    @Param('spaceId') spaceId: string
834
  ) {
835
    return await this.assetService.getAllAssetsBySpaceIdWithRolesCheck(
836
      spaceId,
837
      userId
838
    )
839
  }
840

841
  @Patch('pack/add-asset/:packId/:assetId')
842
  @FirebaseTokenAuthGuard()
843
  @ApiParam({ name: 'assetId', type: 'string', required: true })
844
  public async addAssetToPackWithRolesCheck(
845
    @UserToken('user_id') userId: UserId,
846
    @Param('packId') packId: string,
847
    @Param('assetId') assetId: string
848
  ) {
849
    return await this.assetService.addAssetToPackWithRolesCheck(
850
      packId,
851
      assetId,
852
      userId
853
    )
854
  }
855

856
  @Delete('pack/remove-asset/:packId/:assetId')
857
  @FirebaseTokenAuthGuard()
858
  @ApiParam({ name: 'assetId', type: 'string', required: true })
859
  public async deleteAssetFromPackWithRolesCheck(
860
    @UserToken('user_id') userId: UserId,
861
    @Param('packId') packId: string,
862
    @Param('assetId') assetId: string
863
  ) {
864
    return await this.assetService.deleteAssetFromPackWithRolesCheck(
865
      packId,
866
      assetId,
867
      userId
868
    )
869
  }
870
}
871

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.