File size: 4,159 Bytes
d9494a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import {
  Controller,
  Get,
  Logger,
  Param,
  Res,
  UseFilters,
  UseGuards,
} from '@nestjs/common';

import { pipeline } from 'stream/promises';

import { Response } from 'express';
import { FileFolder } from 'twenty-shared/types';

import {
  FileStorageException,
  FileStorageExceptionCode,
} from 'src/engine/core-modules/file-storage/interfaces/file-storage-exception';
import { PRESIGNED_URL_NO_STORE_CACHE_CONTROL } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
import { setFileResponseHeaders } from 'src/engine/core-modules/file/utils/set-file-response-headers.utils';

import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { FlatEntityMapsRestApiExceptionFilter } from 'src/engine/metadata-modules/flat-entity/filters/flat-entity-maps-rest-api-exception.filter';
import { FrontComponentRestApiExceptionFilter } from 'src/engine/metadata-modules/front-component/filters/front-component-rest-api-exception.filter';
import {
  FrontComponentException,
  FrontComponentExceptionCode,
} from 'src/engine/metadata-modules/front-component/front-component.exception';
import { FrontComponentService } from 'src/engine/metadata-modules/front-component/front-component.service';
import { PermissionsRestApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-rest-api-exception.filter';
import { WorkspaceMigrationRunnerRestApiExceptionFilter } from 'src/engine/workspace-manager/workspace-migration/filters/workspace-migration-runner-rest-api-exception.filter';

@Controller('rest/front-components')
@UseGuards(WorkspaceAuthGuard)
@UseFilters(
  PermissionsRestApiExceptionFilter,
  FrontComponentRestApiExceptionFilter,
  FlatEntityMapsRestApiExceptionFilter,
  WorkspaceMigrationRunnerRestApiExceptionFilter,
)
export class FrontComponentController {
  private readonly logger = new Logger(FrontComponentController.name);

  constructor(private readonly frontComponentService: FrontComponentService) {}

  @Get([':frontComponentId', ':frontComponentId/:cacheKey'])
  @UseGuards(NoPermissionGuard)
  async getBuiltJs(
    @Res() res: Response,
    @Param('frontComponentId') frontComponentId: string,
    @AuthWorkspace() workspace: WorkspaceEntity,
  ) {
    const fileResponse = await this.frontComponentService
      .getBuiltComponentPresignedUrlOrStream({
        frontComponentId,
        workspaceId: workspace.id,
      })
      .catch((error) => {
        if (error instanceof FrontComponentException) {
          throw error;
        }

        if (
          error instanceof FileStorageException &&
          error.code === FileStorageExceptionCode.FILE_NOT_FOUND
        ) {
          throw new FrontComponentException(
            'Front component built file not found',
            FrontComponentExceptionCode.FRONT_COMPONENT_NOT_FOUND,
          );
        }

        this.logger.error(
          'getBuiltComponentPresignedUrlOrStream failed unexpectedly',
          { error },
        );

        throw new FrontComponentException(
          'Error retrieving front component built file',
          FrontComponentExceptionCode.FRONT_COMPONENT_NOT_READY,
        );
      });

    if (fileResponse.type === 'redirect') {
      res.setHeader('Cache-Control', PRESIGNED_URL_NO_STORE_CACHE_CONTROL);

      return res.json({ url: fileResponse.presignedUrl });
    }

    setFileResponseHeaders(
      res,
      fileResponse.mimeType,
      FileFolder.BuiltFrontComponent,
    );

    try {
      await pipeline(fileResponse.stream, res);
    } catch (error) {
      this.logger.error('Front component stream failed mid-transfer', {
        error,
      });

      if (!res.headersSent) {
        throw new FrontComponentException(
          'Error streaming front component built file',
          FrontComponentExceptionCode.FRONT_COMPONENT_NOT_READY,
        );
      }

      res.destroy();
    }
  }
}