Add getters factory for attachements (#4567)

* Add getter factory for attachements

* Override guard in test

* Add secret in env variables

* Return custom message on expiration

* Rename to signPayload

---------

Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
Thomas Trompette 2024-03-19 16:39:53 +01:00 committed by GitHub
parent 9f6c578a46
commit e579554d47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 306 additions and 55 deletions

View File

@ -1,7 +1,9 @@
import { QueryResultGettersFactory } from './query-result-getters.factory';
import { RecordPositionFactory } from './record-position.factory';
import { QueryRunnerArgsFactory } from './query-runner-args.factory';
export const workspaceQueryRunnerFactories = [
QueryRunnerArgsFactory,
RecordPositionFactory,
QueryResultGettersFactory,
];

View File

@ -0,0 +1,75 @@
import { Injectable } from '@nestjs/common';
import { addMilliseconds } from 'date-fns';
import ms from 'ms';
import { ObjectMetadataInterface } from 'src/engine-metadata/field-metadata/interfaces/object-metadata.interface';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { TokenService } from 'src/engine/modules/auth/services/token.service';
@Injectable()
export class QueryResultGettersFactory {
constructor(
private readonly tokenService: TokenService,
private readonly environmentService: EnvironmentService,
) {}
async create<Result>(
result: Result,
objectMetadataItem: ObjectMetadataInterface,
): Promise<Result> {
// TODO: look for file type once implemented
switch (objectMetadataItem.nameSingular) {
case 'attachment':
return this.applyAttachmentGetters(result);
default:
return result;
}
}
private async applyAttachmentGetters<Result>(
attachments: any,
): Promise<Result> {
if (!attachments || !attachments.edges) {
return attachments;
}
const fileTokenExpiresIn = this.environmentService.get(
'FILE_TOKEN_EXPIRES_IN',
);
const secret = this.environmentService.get('FILE_TOKEN_SECRET');
const mappedEdges = await Promise.all(
attachments.edges.map(async (attachment: any) => {
if (!attachment.node.id || !attachment?.node?.fullPath) {
return attachment;
}
const expirationDate = addMilliseconds(
new Date(),
ms(fileTokenExpiresIn),
);
const signedPayload = await this.tokenService.encodePayload(
{
expiration_date: expirationDate,
attachment_id: attachment.node.id,
},
{
secret,
},
);
attachment.node.fullPath = `${attachment.node.fullPath}?token=${signedPayload}`;
return attachment;
}),
);
return {
...attachments,
edges: mappedEdges,
} as Result;
}
}

View File

@ -5,11 +5,13 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works
import { WorkspacePreQueryHookModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-pre-query-hook/workspace-pre-query-hook.module';
import { workspaceQueryRunnerFactories } from 'src/engine/api/graphql/workspace-query-runner/factories';
import { RecordPositionListener } from 'src/engine/api/graphql/workspace-query-runner/listeners/record-position.listener';
import { AuthModule } from 'src/engine/modules/auth/auth.module';
import { WorkspaceQueryRunnerService } from './workspace-query-runner.service';
@Module({
imports: [
AuthModule,
WorkspaceQueryBuilderModule,
WorkspaceDataSourceModule,
WorkspacePreQueryHookModule,

View File

@ -45,6 +45,7 @@ import { WorkspacePreQueryHookService } from 'src/engine/api/graphql/workspace-q
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { NotFoundError } from 'src/engine/filters/utils/graphql-errors.util';
import { QueryRunnerArgsFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory';
import { QueryResultGettersFactory } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters.factory';
import { WorkspaceQueryRunnerOptions } from './interfaces/query-runner-option.interface';
import {
@ -61,6 +62,7 @@ export class WorkspaceQueryRunnerService {
private readonly workspaceQueryBuilderFactory: WorkspaceQueryBuilderFactory,
private readonly workspaceDataSourceService: WorkspaceDataSourceService,
private readonly queryRunnerArgsFactory: QueryRunnerArgsFactory,
private readonly queryResultGettersFactory: QueryResultGettersFactory,
@Inject(MessageQueue.webhookQueue)
private readonly messageQueueService: MessageQueueService,
private readonly eventEmitter: EventEmitter2,
@ -133,7 +135,7 @@ export class WorkspaceQueryRunnerService {
);
const result = await this.execute(query, workspaceId);
const parsedResult = this.parseResult<IConnection<Record>>(
const parsedResult = await this.parseResult<IConnection<Record>>(
result,
objectMetadataItem,
'',
@ -174,7 +176,7 @@ export class WorkspaceQueryRunnerService {
workspaceId,
);
const parsedResult = this.parseResult<Record<string, unknown>>(
const parsedResult = await this.parseResult<Record<string, unknown>>(
existingRecordResult,
objectMetadataItem,
'',
@ -227,10 +229,12 @@ export class WorkspaceQueryRunnerService {
const result = await this.execute(query, workspaceId);
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
result,
objectMetadataItem,
'insertInto',
const parsedResults = (
await this.parseResult<PGGraphQLMutation<Record>>(
result,
objectMetadataItem,
'insertInto',
)
)?.records;
await this.triggerWebhooks<Record>(
@ -280,10 +284,12 @@ export class WorkspaceQueryRunnerService {
const result = await this.execute(query, workspaceId);
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
result,
objectMetadataItem,
'update',
const parsedResults = (
await this.parseResult<PGGraphQLMutation<Record>>(
result,
objectMetadataItem,
'update',
)
)?.records;
await this.triggerWebhooks<Record>(
@ -316,10 +322,12 @@ export class WorkspaceQueryRunnerService {
const result = await this.execute(query, workspaceId);
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
result,
objectMetadataItem,
'update',
const parsedResults = (
await this.parseResult<PGGraphQLMutation<Record>>(
result,
objectMetadataItem,
'update',
)
)?.records;
await this.triggerWebhooks<Record>(
@ -349,10 +357,12 @@ export class WorkspaceQueryRunnerService {
const result = await this.execute(query, workspaceId);
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
result,
objectMetadataItem,
'deleteFrom',
const parsedResults = (
await this.parseResult<PGGraphQLMutation<Record>>(
result,
objectMetadataItem,
'deleteFrom',
)
)?.records;
await this.triggerWebhooks<Record>(
@ -382,10 +392,12 @@ export class WorkspaceQueryRunnerService {
);
const result = await this.execute(query, workspaceId);
const parsedResults = this.parseResult<PGGraphQLMutation<Record>>(
result,
objectMetadataItem,
'deleteFrom',
const parsedResults = (
await this.parseResult<PGGraphQLMutation<Record>>(
result,
objectMetadataItem,
'deleteFrom',
)
)?.records;
await this.triggerWebhooks<Record>(
@ -445,11 +457,11 @@ export class WorkspaceQueryRunnerService {
return results;
}
private parseResult<Result>(
private async parseResult<Result>(
graphqlResult: PGGraphQLResult | undefined,
objectMetadataItem: ObjectMetadataInterface,
command: string,
): Result {
): Promise<Result> {
const entityKey = `${command}${computeObjectTargetTable(
objectMetadataItem,
)}Collection`;
@ -481,7 +493,12 @@ export class WorkspaceQueryRunnerService {
throw error;
}
return parseResult(result);
const resultWithGetters = await this.queryResultGettersFactory.create(
result,
objectMetadataItem,
);
return parseResult(resultWithGetters);
}
async executeAndParse<Result>(

View File

@ -129,6 +129,13 @@ export class EnvironmentVariables {
@IsOptional()
LOGIN_TOKEN_EXPIRES_IN: string = '15m';
@IsString()
FILE_TOKEN_SECRET: string;
@IsDuration()
@IsOptional()
FILE_TOKEN_EXPIRES_IN: string;
// Auth
@IsUrl({ require_tld: false })
@IsOptional()

View File

@ -0,0 +1,79 @@
import { EmailDriver } from 'src/engine/integrations/email/interfaces/email.interface';
import { SupportDriver } from 'src/engine/integrations/environment/interfaces/support.interface';
import { ExceptionHandlerDriver } from 'src/engine/integrations/exception-handler/interfaces';
import { StorageDriverType } from 'src/engine/integrations/file-storage/interfaces';
import { LoggerDriverType } from 'src/engine/integrations/logger/interfaces';
import { MessageQueueDriverType } from 'src/engine/integrations/message-queue/interfaces';
import { EnvironmentVariables } from 'src/engine/integrations/environment/environment-variables';
const EnvironmentDefault = new EnvironmentVariables();
EnvironmentDefault.DEBUG_MODE = false;
EnvironmentDefault.SIGN_IN_PREFILLED = false;
EnvironmentDefault.IS_BILLING_ENABLED = false;
EnvironmentDefault.BILLING_PLAN_REQUIRED_LINK = '';
EnvironmentDefault.BILLING_STRIPE_BASE_PLAN_PRODUCT_ID = '';
EnvironmentDefault.BILLING_FREE_TRIAL_DURATION_IN_DAYS = 7;
EnvironmentDefault.BILLING_STRIPE_API_KEY = '';
EnvironmentDefault.BILLING_STRIPE_WEBHOOK_SECRET = '';
EnvironmentDefault.TELEMETRY_ENABLED = true;
EnvironmentDefault.TELEMETRY_ANONYMIZATION_ENABLED = true;
EnvironmentDefault.PORT = 3000;
EnvironmentDefault.REDIS_HOST = '127.0.0.1';
EnvironmentDefault.REDIS_PORT = 6379;
EnvironmentDefault.PG_DATABASE_URL = '';
EnvironmentDefault.FRONT_BASE_URL = '';
EnvironmentDefault.SERVER_URL = '';
EnvironmentDefault.ACCESS_TOKEN_SECRET = 'random_string';
EnvironmentDefault.ACCESS_TOKEN_EXPIRES_IN = '30m';
EnvironmentDefault.REFRESH_TOKEN_SECRET = 'random_string';
EnvironmentDefault.REFRESH_TOKEN_EXPIRES_IN = '30m';
EnvironmentDefault.REFRESH_TOKEN_COOL_DOWN = '1m';
EnvironmentDefault.LOGIN_TOKEN_SECRET = 'random_string';
EnvironmentDefault.LOGIN_TOKEN_EXPIRES_IN = '30m';
EnvironmentDefault.FILE_TOKEN_SECRET = 'random_string';
EnvironmentDefault.FILE_TOKEN_EXPIRES_IN = '1d';
EnvironmentDefault.API_TOKEN_EXPIRES_IN = '100y';
EnvironmentDefault.SHORT_TERM_TOKEN_EXPIRES_IN = '5m';
EnvironmentDefault.FRONT_AUTH_CALLBACK_URL = '';
EnvironmentDefault.MESSAGING_PROVIDER_GMAIL_ENABLED = false;
EnvironmentDefault.MESSAGING_PROVIDER_GMAIL_CALLBACK_URL = '';
EnvironmentDefault.AUTH_GOOGLE_ENABLED = false;
EnvironmentDefault.AUTH_GOOGLE_CLIENT_ID = '';
EnvironmentDefault.AUTH_GOOGLE_CLIENT_SECRET = '';
EnvironmentDefault.AUTH_GOOGLE_CALLBACK_URL = '';
EnvironmentDefault.STORAGE_TYPE = StorageDriverType.Local;
EnvironmentDefault.STORAGE_S3_REGION = 'aws-east-1';
EnvironmentDefault.STORAGE_S3_NAME = '';
EnvironmentDefault.STORAGE_S3_ENDPOINT = '';
EnvironmentDefault.STORAGE_LOCAL_PATH = '.local-storage';
EnvironmentDefault.MESSAGE_QUEUE_TYPE = MessageQueueDriverType.Sync;
EnvironmentDefault.EMAIL_FROM_ADDRESS = 'noreply@yourdomain.com';
EnvironmentDefault.EMAIL_SYSTEM_ADDRESS = 'system@yourdomain.com';
EnvironmentDefault.EMAIL_FROM_NAME = 'John from Twenty';
EnvironmentDefault.EMAIL_DRIVER = EmailDriver.Logger;
EnvironmentDefault.EMAIL_SMTP_HOST = '';
EnvironmentDefault.EMAIL_SMTP_PORT = 587;
EnvironmentDefault.EMAIL_SMTP_USER = '';
EnvironmentDefault.EMAIL_SMTP_PASSWORD = '';
EnvironmentDefault.SUPPORT_DRIVER = SupportDriver.None;
EnvironmentDefault.SUPPORT_FRONT_CHAT_ID = '';
EnvironmentDefault.SUPPORT_FRONT_HMAC_KEY = '';
EnvironmentDefault.LOGGER_DRIVER = LoggerDriverType.Console;
EnvironmentDefault.EXCEPTION_HANDLER_DRIVER = ExceptionHandlerDriver.Console;
EnvironmentDefault.LOG_LEVELS = ['log', 'error', 'warn'];
EnvironmentDefault.SENTRY_DSN = '';
EnvironmentDefault.DEMO_WORKSPACE_IDS = [];
EnvironmentDefault.OPENROUTER_API_KEY = '';
EnvironmentDefault.PASSWORD_RESET_TOKEN_EXPIRES_IN = '5m';
EnvironmentDefault.WORKSPACE_INACTIVE_DAYS_BEFORE_NOTIFICATION = 30;
EnvironmentDefault.WORKSPACE_INACTIVE_DAYS_BEFORE_DELETION = 60;
EnvironmentDefault.IS_SIGN_UP_DISABLED = false;
EnvironmentDefault.API_RATE_LIMITING_TTL = 100;
EnvironmentDefault.API_RATE_LIMITING_LIMIT = 500;
EnvironmentDefault.MUTATION_MAXIMUM_RECORD_AFFECTED = 100;
EnvironmentDefault.CACHE_STORAGE_TYPE = 'memory';
EnvironmentDefault.CACHE_STORAGE_TTL = 3600 * 24 * 7;
export { EnvironmentDefault };

View File

@ -5,7 +5,6 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { HttpModule } from '@nestjs/axios';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { FileModule } from 'src/engine/modules/file/file.module';
import { Workspace } from 'src/engine/modules/workspace/workspace.entity';
import { User } from 'src/engine/modules/user/user.entity';
import { RefreshToken } from 'src/engine/modules/refresh-token/refresh-token.entity';
@ -22,6 +21,7 @@ import { UserWorkspaceModule } from 'src/engine/modules/user-workspace/user-work
import { SignUpService } from 'src/engine/modules/auth/services/sign-up.service';
import { GoogleGmailAuthController } from 'src/engine/modules/auth/controllers/google-gmail-auth.controller';
import { FeatureFlagEntity } from 'src/engine/modules/feature-flag/feature-flag.entity';
import { FileUploadModule } from 'src/engine/modules/file/file-upload/file-upload.module';
import { AuthResolver } from './auth.resolver';
@ -42,7 +42,7 @@ const jwtModule = JwtModule.registerAsync({
@Module({
imports: [
jwtModule,
FileModule,
FileUploadModule,
DataSourceModule,
UserModule,
WorkspaceManagerModule,

View File

@ -6,7 +6,7 @@ import { Workspace } from 'src/engine/modules/workspace/workspace.entity';
import { User } from 'src/engine/modules/user/user.entity';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { SignUpService } from 'src/engine/modules/auth/services/sign-up.service';
import { FileUploadService } from 'src/engine/modules/file/services/file-upload.service';
import { FileUploadService } from 'src/engine/modules/file/file-upload/services/file-upload.service';
import { UserWorkspaceService } from 'src/engine/modules/user-workspace/user-workspace.service';
describe('SignUpService', () => {

View File

@ -19,7 +19,7 @@ import {
} from 'src/engine/modules/auth/auth.util';
import { User } from 'src/engine/modules/user/user.entity';
import { Workspace } from 'src/engine/modules/workspace/workspace.entity';
import { FileUploadService } from 'src/engine/modules/file/services/file-upload.service';
import { FileUploadService } from 'src/engine/modules/file/file-upload/services/file-upload.service';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { getImageBufferFromUrl } from 'src/utils/image';
import { UserWorkspaceService } from 'src/engine/modules/user-workspace/user-workspace.service';

View File

@ -7,7 +7,6 @@ import { RefreshToken } from 'src/engine/modules/refresh-token/refresh-token.ent
import { User } from 'src/engine/modules/user/user.entity';
import { JwtAuthStrategy } from 'src/engine/modules/auth/strategies/jwt.auth.strategy';
import { EmailService } from 'src/engine/integrations/email/email.service';
import { UserWorkspaceService } from 'src/engine/modules/user-workspace/user-workspace.service';
import { Workspace } from 'src/engine/modules/workspace/workspace.entity';
import { TokenService } from './token.service';
@ -35,10 +34,6 @@ describe('TokenService', () => {
provide: EmailService,
useValue: {},
},
{
provide: UserWorkspaceService,
useValue: {},
},
{
provide: getRepositoryToken(User, 'core'),
useValue: {},

View File

@ -40,7 +40,6 @@ import { EmailService } from 'src/engine/integrations/email/email.service';
import { InvalidatePassword } from 'src/engine/modules/auth/dto/invalidate-password.entity';
import { EmailPasswordResetLink } from 'src/engine/modules/auth/dto/email-password-reset-link.entity';
import { JwtData } from 'src/engine/modules/auth/types/jwt-data.type';
import { UserWorkspaceService } from 'src/engine/modules/user-workspace/user-workspace.service';
import { Workspace } from 'src/engine/modules/workspace/workspace.entity';
@Injectable()
@ -56,7 +55,6 @@ export class TokenService {
@InjectRepository(Workspace, 'core')
private readonly workspaceRepository: Repository<Workspace>,
private readonly emailService: EmailService,
private readonly userWorkspaceService: UserWorkspaceService,
) {}
async generateAccessToken(
@ -528,4 +526,12 @@ export class TokenService {
return { success: true };
}
async encodePayload(payload: any, options?: any): Promise<string> {
return this.jwtService.sign(payload, options);
}
async decodePayload(payload: any, options?: any): Promise<string> {
return this.jwtService.decode(payload, options);
}
}

View File

@ -1,11 +1,14 @@
import { Test, TestingModule } from '@nestjs/testing';
import { CanActivate } from '@nestjs/common';
import { FileService } from 'src/engine/modules/file/services/file.service';
import { FilePathGuard } from 'src/engine/modules/file/guards/file-path-guard';
import { FileController } from './file.controller';
describe('FileController', () => {
let controller: FileController;
const mock_FilePathGuard: CanActivate = { canActivate: jest.fn(() => true) };
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
@ -16,7 +19,10 @@ describe('FileController', () => {
useValue: {},
},
],
}).compile();
})
.overrideGuard(FilePathGuard)
.useValue(mock_FilePathGuard)
.compile();
controller = module.get<FileController>(FileController);
});

View File

@ -1,7 +1,8 @@
import { Controller, Get, Param, Res } from '@nestjs/common';
import { Controller, Get, Param, Res, UseGuards } from '@nestjs/common';
import { Response } from 'express';
import { FilePathGuard } from 'src/engine/modules/file/guards/file-path-guard';
import {
checkFilePath,
checkFilename,
@ -10,6 +11,7 @@ import { FileService } from 'src/engine/modules/file/services/file.service';
// TODO: Add cookie authentication
@Controller('files')
@UseGuards(FilePathGuard)
export class FileController {
constructor(private readonly fileService: FileService) {}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { FileUploadResolver } from 'src/engine/modules/file/file-upload/resolvers/file-upload.resolver';
import { FileUploadService } from 'src/engine/modules/file/file-upload/services/file-upload.service';
@Module({
providers: [FileUploadService, FileUploadResolver, EnvironmentService],
exports: [FileUploadService, FileUploadResolver],
})
export class FileUploadModule {}

View File

@ -1,6 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing';
import { FileUploadService } from 'src/engine/modules/file/services/file-upload.service';
import { FileUploadService } from 'src/engine/modules/file/file-upload/services/file-upload.service';
import { FileUploadResolver } from './file-upload.resolver';

View File

@ -5,7 +5,7 @@ import { GraphQLUpload, FileUpload } from 'graphql-upload';
import { FileFolder } from 'src/engine/modules/file/interfaces/file-folder.interface';
import { FileUploadService } from 'src/engine/modules/file/services/file-upload.service';
import { FileUploadService } from 'src/engine/modules/file/file-upload/services/file-upload.service';
import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard';
import { streamToBuffer } from 'src/utils/stream-to-buffer';
import { DemoEnvGuard } from 'src/engine/guards/demo.env.guard';

View File

@ -1,20 +1,17 @@
import { Module } from '@nestjs/common';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { FilePathGuard } from 'src/engine/modules/file/guards/file-path-guard';
import { AuthModule } from 'src/engine/modules/auth/auth.module';
import { FileUploadModule } from 'src/engine/modules/file/file-upload/file-upload.module';
import { FileService } from './services/file.service';
import { FileUploadService } from './services/file-upload.service';
import { FileUploadResolver } from './resolvers/file-upload.resolver';
import { FileController } from './controllers/file.controller';
@Module({
providers: [
FileService,
FileUploadService,
FileUploadResolver,
EnvironmentService,
],
exports: [FileService, FileUploadService],
imports: [FileUploadModule, AuthModule],
providers: [FileService, EnvironmentService, FilePathGuard],
exports: [FileService],
controllers: [FileController],
})
export class FileModule {}

View File

@ -0,0 +1,52 @@
import {
Injectable,
CanActivate,
ExecutionContext,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { TokenService } from 'src/engine/modules/auth/services/token.service';
@Injectable()
export class FilePathGuard implements CanActivate {
constructor(
private readonly tokenService: TokenService,
private readonly environmentService: EnvironmentService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const query = context.switchToHttp().getRequest().query;
if (query && query['token']) {
return !(await this.isExpired(query['token']));
}
return true;
}
private async isExpired(signedExpirationDate: string): Promise<boolean> {
const decodedPayload = await this.tokenService.decodePayload(
signedExpirationDate,
{
secret: this.environmentService.get('FILE_TOKEN_SECRET'),
},
);
const expirationDate = decodedPayload?.['expiration_date'];
if (!expirationDate) {
return true;
}
if (new Date(expirationDate) < new Date()) {
throw new HttpException(
'This url has expired. Please reload twenty page and open file again.',
HttpStatus.FORBIDDEN,
);
}
return false;
}
}

View File

@ -4,7 +4,6 @@ import { Module } from '@nestjs/common';
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { FileModule } from 'src/engine/modules/file/file.module';
import { User } from 'src/engine/modules/user/user.entity';
import { UserResolver } from 'src/engine/modules/user/user.resolver';
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
@ -12,6 +11,7 @@ import { DataSourceModule } from 'src/engine-metadata/data-source/data-source.mo
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { UserWorkspaceModule } from 'src/engine/modules/user-workspace/user-workspace.module';
import { UserWorkspace } from 'src/engine/modules/user-workspace/user-workspace.entity';
import { FileUploadModule } from 'src/engine/modules/file/file-upload/file-upload.module';
import { userAutoResolverOpts } from './user.auto-resolver-opts';
@ -27,7 +27,7 @@ import { UserService } from './services/user.service';
resolvers: userAutoResolverOpts,
}),
DataSourceModule,
FileModule,
FileUploadModule,
UserWorkspaceModule,
],
exports: [UserService],

View File

@ -20,7 +20,7 @@ import { FileFolder } from 'src/engine/modules/file/interfaces/file-folder.inter
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
import { streamToBuffer } from 'src/utils/stream-to-buffer';
import { FileUploadService } from 'src/engine/modules/file/services/file-upload.service';
import { FileUploadService } from 'src/engine/modules/file/file-upload/services/file-upload.service';
import { assert } from 'src/utils/assert';
import { DemoEnvGuard } from 'src/engine/guards/demo.env.guard';
import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard';

View File

@ -3,7 +3,6 @@ import { Module } from '@nestjs/common';
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { FileModule } from 'src/engine/modules/file/file.module';
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
import { WorkspaceResolver } from 'src/engine/modules/workspace/workspace.resolver';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
@ -12,6 +11,7 @@ import { UserWorkspace } from 'src/engine/modules/user-workspace/user-workspace.
import { User } from 'src/engine/modules/user/user.entity';
import { UserWorkspaceModule } from 'src/engine/modules/user-workspace/user-workspace.module';
import { BillingModule } from 'src/engine/modules/billing/billing.module';
import { FileUploadModule } from 'src/engine/modules/file/file-upload/file-upload.module';
import { Workspace } from './workspace.entity';
import { workspaceAutoResolverOpts } from './workspace.auto-resolver-opts';
@ -24,7 +24,7 @@ import { WorkspaceService } from './services/workspace.service';
NestjsQueryGraphQLModule.forFeature({
imports: [
BillingModule,
FileModule,
FileUploadModule,
NestjsQueryTypeOrmModule.forFeature(
[User, Workspace, UserWorkspace, FeatureFlagEntity],
'core',

View File

@ -13,7 +13,7 @@ import { FileUpload, GraphQLUpload } from 'graphql-upload';
import { FileFolder } from 'src/engine/modules/file/interfaces/file-folder.interface';
import { streamToBuffer } from 'src/utils/stream-to-buffer';
import { FileUploadService } from 'src/engine/modules/file/services/file-upload.service';
import { FileUploadService } from 'src/engine/modules/file/file-upload/services/file-upload.service';
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { assert } from 'src/utils/assert';
import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard';