Fast follows on 0.34 (#9034)

Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
Charles Bochet 2024-12-12 16:46:48 +01:00 committed by GitHub
parent 05cd0d1803
commit 77c2961912
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 141 additions and 76 deletions

View File

@ -7,6 +7,9 @@ PG_DATABASE_HOST=db:5432
REDIS_URL=redis://redis:6379
SERVER_URL=http://localhost:3000
FRONT_DOMAIN=localhost
FRONT_PORT=3000
FRONT_PROTOCOL=http
# Use openssl rand -base64 32 for each secret
# APP_SECRET=replace_me_with_a_random_string

View File

@ -24,7 +24,6 @@ services:
PORT: 3000
PG_DATABASE_URL: postgres://${PGUSER_SUPERUSER:-postgres}:${PGPASSWORD_SUPERUSER:-postgres}@${PG_DATABASE_HOST:-db:5432}/default
SERVER_URL: ${SERVER_URL}
FRONT_BASE_URL: ${FRONT_BASE_URL:-$SERVER_URL}
REDIS_URL: ${REDIS_URL:-redis://redis:6379}
ENABLE_DB_MIGRATIONS: "true"
@ -54,9 +53,7 @@ services:
environment:
PG_DATABASE_URL: postgres://${PGUSER_SUPERUSER:-postgres}:${PGPASSWORD_SUPERUSER:-postgres}@${PG_DATABASE_HOST:-db:5432}/default
SERVER_URL: ${SERVER_URL}
FRONT_BASE_URL: ${FRONT_BASE_URL:-$SERVER_URL}
REDIS_URL: ${REDIS_URL:-redis://redis:6379}
ENABLE_DB_MIGRATIONS: "false" # it already runs on the server
STORAGE_TYPE: ${STORAGE_TYPE}

View File

@ -37,8 +37,6 @@ spec:
value: 3000
- name: SERVER_URL
value: "https://crm.example.com:443"
- name: FRONT_BASE_URL
value: "https://crm.example.com:443"
- name: "PG_DATABASE_URL"
value: "postgres://postgres:postgres@twentycrm-db.twentycrm.svc.cluster.local/default"
- name: "REDIS_URL"

View File

@ -28,8 +28,6 @@ spec:
env:
- name: SERVER_URL
value: "https://crm.example.com:443"
- name: FRONT_BASE_URL
value: "https://crm.example.com:443"
- name: PG_DATABASE_URL
value: "postgres://postgres:postgres@twentycrm-db.twentycrm.svc.cluster.local/default"
- name: ENABLE_DB_MIGRATIONS

View File

@ -51,11 +51,6 @@ resource "kubernetes_deployment" "twentycrm_server" {
value = var.twentycrm_app_hostname
}
env {
name = "FRONT_BASE_URL"
value = var.twentycrm_app_hostname
}
env {
name = "PG_DATABASE_URL"
value = "postgres://twenty:${var.twentycrm_pgdb_admin_password}@${kubernetes_service.twentycrm_db.metadata.0.name}.${kubernetes_namespace.twentycrm.metadata.0.name}.svc.cluster.local/default"

View File

@ -43,11 +43,6 @@ resource "kubernetes_deployment" "twentycrm_worker" {
value = var.twentycrm_app_hostname
}
env {
name = "FRONT_BASE_URL"
value = var.twentycrm_app_hostname
}
env {
name = "PG_DATABASE_URL"
value = "postgres://twenty:${var.twentycrm_pgdb_admin_password}@${kubernetes_service.twentycrm_db.metadata.0.name}.${kubernetes_namespace.twentycrm.metadata.0.name}.svc.cluster.local/default"

View File

@ -2,13 +2,15 @@
PG_DATABASE_URL=postgres://postgres:postgres@localhost:5432/default
REDIS_URL=redis://localhost:6379
FRONT_BASE_URL=http://localhost:3001
APP_SECRET=replace_me_with_a_random_string
SIGN_IN_PREFILLED=true
ACCESS_TOKEN_SECRET=replace_me_with_a_random_string_access
FRONT_PROTOCOL=http
FRONT_DOMAIN=localhost
FRONT_PORT=3001
# ———————— Optional ————————
# PORT=3000
# DEBUG_MODE=true

View File

@ -3,7 +3,6 @@ REDIS_URL=redis://localhost:6379
DEBUG_MODE=true
DEBUG_PORT=9000
FRONT_BASE_URL=http://localhost:3001
APP_SECRET=replace_me_with_a_random_string
SIGN_IN_PREFILLED=true
EXCEPTION_HANDLER_DRIVER=console

View File

@ -6,7 +6,6 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
import { AppTokenService } from 'src/engine/core-modules/app-token/services/app-token.service';
import { AuthExceptionHandlerService } from 'src/engine/core-modules/auth/auth-exception-handler.service';
import { GoogleAPIsAuthController } from 'src/engine/core-modules/auth/controllers/google-apis-auth.controller';
import { GoogleAuthController } from 'src/engine/core-modules/auth/controllers/google-auth.controller';
import { MicrosoftAPIsAuthController } from 'src/engine/core-modules/auth/controllers/microsoft-apis-auth.controller';
@ -103,7 +102,6 @@ import { JwtAuthStrategy } from './strategies/jwt.auth.strategy';
ResetPasswordService,
SwitchWorkspaceService,
TransientTokenService,
AuthExceptionHandlerService,
ApiKeyService,
OAuthService,
],

View File

@ -2,16 +2,16 @@ import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
import { Response } from 'express';
import { AuthExceptionHandlerService } from 'src/engine/core-modules/auth/auth-exception-handler.service';
import {
AuthException,
AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception';
import { HttpExceptionHandlerService } from 'src/engine/core-modules/exception-handler/http-exception-handler.service';
@Catch(AuthException)
export class AuthRestApiExceptionFilter implements ExceptionFilter {
constructor(
private readonly authExceptionHandlerService: AuthExceptionHandlerService,
private readonly httpExceptionHandlerService: HttpExceptionHandlerService,
) {}
catch(exception: AuthException, host: ArgumentsHost) {
@ -21,7 +21,7 @@ export class AuthRestApiExceptionFilter implements ExceptionFilter {
switch (exception.code) {
case AuthExceptionCode.USER_NOT_FOUND:
case AuthExceptionCode.CLIENT_NOT_FOUND:
return this.authExceptionHandlerService.handleError(
return this.httpExceptionHandlerService.handleError(
exception,
response,
404,
@ -29,13 +29,13 @@ export class AuthRestApiExceptionFilter implements ExceptionFilter {
case AuthExceptionCode.INVALID_INPUT:
case AuthExceptionCode.INVALID_DATA:
case AuthExceptionCode.MISSING_ENVIRONMENT_VARIABLE:
return this.authExceptionHandlerService.handleError(
return this.httpExceptionHandlerService.handleError(
exception,
response,
400,
);
case AuthExceptionCode.FORBIDDEN_EXCEPTION:
return this.authExceptionHandlerService.handleError(
return this.httpExceptionHandlerService.handleError(
exception,
response,
401,
@ -43,14 +43,14 @@ export class AuthRestApiExceptionFilter implements ExceptionFilter {
case AuthExceptionCode.GOOGLE_API_AUTH_DISABLED:
case AuthExceptionCode.MICROSOFT_API_AUTH_DISABLED:
case AuthExceptionCode.SIGNUP_DISABLED:
return this.authExceptionHandlerService.handleError(
return this.httpExceptionHandlerService.handleError(
exception,
response,
403,
);
case AuthExceptionCode.INTERNAL_SERVER_ERROR:
default:
return this.authExceptionHandlerService.handleError(
return this.httpExceptionHandlerService.handleError(
exception,
response,
500,

View File

@ -26,6 +26,7 @@ import {
} from 'src/engine/core-modules/auth/auth.util';
import { AuthorizeApp } from 'src/engine/core-modules/auth/dto/authorize-app.entity';
import { AuthorizeAppInput } from 'src/engine/core-modules/auth/dto/authorize-app.input';
import { AvailableWorkspaceOutput } from 'src/engine/core-modules/auth/dto/available-workspaces.output';
import { ChallengeInput } from 'src/engine/core-modules/auth/dto/challenge.input';
import { UpdatePassword } from 'src/engine/core-modules/auth/dto/update-password.entity';
import {
@ -37,17 +38,16 @@ import { WorkspaceInviteHashValid } from 'src/engine/core-modules/auth/dto/works
import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-up.service';
import { AccessTokenService } from 'src/engine/core-modules/auth/token/services/access-token.service';
import { RefreshTokenService } from 'src/engine/core-modules/auth/token/services/refresh-token.service';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { EmailService } from 'src/engine/core-modules/email/email.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
import { AvailableWorkspaceOutput } from 'src/engine/core-modules/auth/dto/available-workspaces.output';
import { UserService } from 'src/engine/core-modules/user/services/user.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { userValidator } from 'src/engine/core-modules/user/user.validate';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
@Injectable()
// eslint-disable-next-line @nx/workspace-inject-workspace-repository

View File

@ -21,10 +21,10 @@ import { EmailPasswordResetLink } from 'src/engine/core-modules/auth/dto/email-p
import { InvalidatePassword } from 'src/engine/core-modules/auth/dto/invalidate-password.entity';
import { PasswordResetToken } from 'src/engine/core-modules/auth/dto/token.entity';
import { ValidatePasswordResetToken } from 'src/engine/core-modules/auth/dto/validate-password-reset-token.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { EmailService } from 'src/engine/core-modules/email/email.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
@Injectable()
export class ResetPasswordService {

View File

@ -6,6 +6,7 @@ import {
RawBodyRequest,
Req,
Res,
UseFilters,
} from '@nestjs/common';
import { Response } from 'express';
@ -15,11 +16,13 @@ import {
BillingExceptionCode,
} from 'src/engine/core-modules/billing/billing.exception';
import { WebhookEvent } from 'src/engine/core-modules/billing/enums/billing-webhook-events.enum';
import { BillingRestApiExceptionFilter } from 'src/engine/core-modules/billing/filters/billing-api-exception.filter';
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
import { BillingWebhookEntitlementService } from 'src/engine/core-modules/billing/services/billing-webhook-entitlement.service';
import { BillingWebhookSubscriptionService } from 'src/engine/core-modules/billing/services/billing-webhook-subscription.service';
import { StripeService } from 'src/engine/core-modules/billing/stripe/stripe.service';
@Controller('billing')
@UseFilters(BillingRestApiExceptionFilter)
export class BillingController {
protected readonly logger = new Logger(BillingController.name);

View File

@ -10,6 +10,7 @@ import { BillingPrice } from 'src/engine/core-modules/billing/entities/billing-p
import { BillingProduct } from 'src/engine/core-modules/billing/entities/billing-product.entity';
import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity';
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
import { BillingRestApiExceptionFilter } from 'src/engine/core-modules/billing/filters/billing-api-exception.filter';
import { BillingWorkspaceMemberListener } from 'src/engine/core-modules/billing/listeners/billing-workspace-member.listener';
import { BillingPortalWorkspaceService } from 'src/engine/core-modules/billing/services/billing-portal.workspace-service';
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
@ -53,6 +54,7 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
BillingResolver,
BillingWorkspaceMemberListener,
BillingService,
BillingRestApiExceptionFilter,
],
exports: [
BillingSubscriptionService,

View File

@ -0,0 +1,37 @@
import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
import { Response } from 'express';
import Stripe from 'stripe';
import {
BillingException,
BillingExceptionCode,
} from 'src/engine/core-modules/billing/billing.exception';
import { HttpExceptionHandlerService } from 'src/engine/core-modules/exception-handler/http-exception-handler.service';
@Catch(BillingException, Stripe.errors.StripeError)
export class BillingRestApiExceptionFilter implements ExceptionFilter {
constructor(
private readonly httpExceptionHandlerService: HttpExceptionHandlerService,
) {}
catch(exception: BillingException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
switch (exception.code) {
case BillingExceptionCode.BILLING_CUSTOMER_NOT_FOUND:
return this.httpExceptionHandlerService.handleError(
exception,
response,
404,
);
default:
return this.httpExceptionHandlerService.handleError(
exception,
response,
500,
);
}
}
}

View File

@ -31,10 +31,13 @@ export class BillingPortalWorkspaceService {
priceId: string,
successUrlPath?: string,
): Promise<string> {
const frontBaseUrl = this.domainManagerService.getBaseUrl().toString();
const successUrl = successUrlPath
? frontBaseUrl + successUrlPath
: frontBaseUrl;
const frontBaseUrl = this.domainManagerService.getBaseUrl();
const cancelUrl = frontBaseUrl.toString();
if (successUrlPath) {
frontBaseUrl.pathname = successUrlPath;
}
const successUrl = frontBaseUrl.toString();
const quantity = await this.userWorkspaceRepository.countBy({
workspaceId: workspace.id,
@ -51,7 +54,7 @@ export class BillingPortalWorkspaceService {
priceId,
quantity,
successUrl,
frontBaseUrl,
cancelUrl,
stripeCustomerId,
);
@ -81,10 +84,12 @@ export class BillingPortalWorkspaceService {
throw new Error('Error: missing stripeCustomerId');
}
const frontBaseUrl = this.domainManagerService.getBaseUrl().toString();
const returnUrl = returnUrlPath
? frontBaseUrl + returnUrlPath
: frontBaseUrl;
const frontBaseUrl = this.domainManagerService.getBaseUrl();
if (returnUrlPath) {
frontBaseUrl.pathname = returnUrlPath;
}
const returnUrl = frontBaseUrl.toString();
const session = await this.stripeService.createBillingPortalSession(
stripeCustomerId,

View File

@ -1,8 +1,11 @@
import { Module } from '@nestjs/common';
import { DomainManagerModule } from 'src/engine/core-modules/domain-manager/domain-manager.module';
import { ClientConfigResolver } from './client-config.resolver';
@Module({
imports: [DomainManagerModule],
providers: [ClientConfigResolver],
})
export class ClientConfigModule {}

View File

@ -1,5 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { ClientConfigResolver } from './client-config.resolver';
@ -15,6 +16,10 @@ describe('ClientConfigResolver', () => {
provide: EnvironmentService,
useValue: {},
},
{
provide: DomainManagerService,
useValue: {},
},
],
}).compile();

View File

@ -1,12 +1,16 @@
import { Query, Resolver } from '@nestjs/graphql';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { ClientConfig } from './client-config.entity';
@Resolver()
export class ClientConfigResolver {
constructor(private environmentService: EnvironmentService) {}
constructor(
private environmentService: EnvironmentService,
private domainManagerService: DomainManagerService,
) {}
@Query(() => ClientConfig)
async clientConfig(): Promise<ClientConfig> {
@ -24,7 +28,7 @@ export class ClientConfigResolver {
'IS_MULTIWORKSPACE_ENABLED',
),
defaultSubdomain: this.environmentService.get('DEFAULT_SUBDOMAIN'),
frontDomain: this.environmentService.get('FRONT_DOMAIN'),
frontDomain: this.domainManagerService.getFrontUrl().hostname,
debugMode: this.environmentService.get('DEBUG_MODE'),
support: {
supportDriver: this.environmentService.get('SUPPORT_DRIVER'),

View File

@ -1,12 +1,11 @@
import { Module } from '@nestjs/common';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
@Module({
imports: [NestjsQueryTypeOrmModule.forFeature([Workspace], 'core')],
imports: [TypeOrmModule.forFeature([Workspace], 'core')],
providers: [DomainManagerService],
exports: [DomainManagerService],
})

View File

@ -21,10 +21,28 @@ export class DomainManagerService {
private readonly environmentService: EnvironmentService,
) {}
getBaseUrl() {
const baseUrl = new URL(
`${this.environmentService.get('FRONT_PROTOCOL')}://${this.environmentService.get('FRONT_DOMAIN')}`,
);
getFrontUrl() {
let baseUrl: URL;
if (!this.environmentService.get('FRONT_DOMAIN')) {
baseUrl = new URL(this.environmentService.get('SERVER_URL'));
} else {
baseUrl = new URL(
`${this.environmentService.get('FRONT_PROTOCOL')}://${this.environmentService.get('FRONT_DOMAIN')}`,
);
const port = this.environmentService.get('FRONT_PORT');
if (port) {
baseUrl.port = port.toString();
}
}
return baseUrl;
}
getBaseUrl(): URL {
const baseUrl = this.getFrontUrl();
if (
this.environmentService.get('IS_MULTIWORKSPACE_ENABLED') &&
@ -33,10 +51,6 @@ export class DomainManagerService {
baseUrl.hostname = `${this.environmentService.get('DEFAULT_SUBDOMAIN')}.${baseUrl.hostname}`;
}
if (this.environmentService.get('FRONT_PORT')) {
baseUrl.port = this.environmentService.get('FRONT_PORT').toString();
}
return baseUrl;
}
@ -87,10 +101,9 @@ export class DomainManagerService {
getWorkspaceSubdomainByOrigin = (origin: string) => {
const { hostname: originHostname } = new URL(origin);
const subdomain = originHostname.replace(
`.${this.environmentService.get('FRONT_DOMAIN')}`,
'',
);
const frontDomain = this.getFrontUrl().hostname;
const subdomain = originHostname.replace(`.${frontDomain}`, '');
if (this.isDefaultSubdomain(subdomain)) {
return;

View File

@ -129,7 +129,7 @@ export class EnvironmentVariables {
// Frontend URL
@IsString()
@IsOptional()
FRONT_DOMAIN = 'localhost';
FRONT_DOMAIN?: string;
@IsString()
@ValidateIf((env) => env.IS_MULTIWORKSPACE_ENABLED)
@ -137,12 +137,12 @@ export class EnvironmentVariables {
@IsString()
@IsOptional()
FRONT_PROTOCOL: 'http' | 'https' = 'http';
FRONT_PROTOCOL?: 'http' | 'https' = 'http';
@CastToPositiveNumber()
@IsNumber()
@IsOptional()
FRONT_PORT = 3001;
FRONT_PORT?: number;
@IsUrl({ require_tld: false, require_protocol: true })
@IsOptional()

View File

@ -9,12 +9,13 @@ import {
OPTIONS_TYPE,
} from 'src/engine/core-modules/exception-handler/exception-handler.module-definition';
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
import { HttpExceptionHandlerService } from 'src/engine/core-modules/exception-handler/http-exception-handler.service';
import { ExceptionHandlerDriver } from 'src/engine/core-modules/exception-handler/interfaces';
@Global()
@Module({
providers: [ExceptionHandlerService],
exports: [ExceptionHandlerService],
providers: [ExceptionHandlerService, HttpExceptionHandlerService],
exports: [ExceptionHandlerService, HttpExceptionHandlerService],
})
export class ExceptionHandlerModule extends ConfigurableModuleClass {
static forRoot(options: typeof OPTIONS_TYPE): DynamicModule {

View File

@ -6,11 +6,11 @@ import { Response } from 'express';
import { ExceptionHandlerUser } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-user.interface';
import { ExceptionHandlerWorkspace } from 'src/engine/core-modules/exception-handler/interfaces/exception-handler-workspace.interface';
import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handler/exception-handler.service';
import { CustomException } from 'src/utils/custom-exception';
export const handleException = (
exception: AuthException,
exception: CustomException,
exceptionHandlerService: ExceptionHandlerService,
user?: ExceptionHandlerUser,
workspace?: ExceptionHandlerWorkspace,
@ -24,7 +24,7 @@ interface RequestAndParams {
}
@Injectable({ scope: Scope.REQUEST })
export class AuthExceptionHandlerService {
export class HttpExceptionHandlerService {
constructor(
private readonly exceptionHandlerService: ExceptionHandlerService,
@Inject(REQUEST)
@ -32,7 +32,7 @@ export class AuthExceptionHandlerService {
) {}
handleError = (
exception: AuthException,
exception: CustomException,
response: Response<any, Record<string, any>>,
errorCode?: number,
user?: ExceptionHandlerUser,

View File

@ -4,15 +4,15 @@ import { NestExpressApplication } from '@nestjs/platform-express';
import fs from 'fs';
import session from 'express-session';
import bytes from 'bytes';
import { useContainer } from 'class-validator';
import session from 'express-session';
import { graphqlUploadExpress } from 'graphql-upload';
import { LoggerService } from 'src/engine/core-modules/logger/logger.service';
import { ApplyCorsToExceptions } from 'src/utils/apply-cors-to-exceptions';
import { getSessionStorageOptions } from 'src/engine/core-modules/session-storage/session-storage.module-factory';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { LoggerService } from 'src/engine/core-modules/logger/logger.service';
import { getSessionStorageOptions } from 'src/engine/core-modules/session-storage/session-storage.module-factory';
import { UnhandledExceptionFilter } from 'src/utils/apply-cors-to-exceptions';
import { AppModule } from './app.module';
import './instrument';
@ -48,7 +48,7 @@ const bootstrap = async () => {
// Use our logger
app.useLogger(logger);
app.useGlobalFilters(new ApplyCorsToExceptions());
app.useGlobalFilters(new UnhandledExceptionFilter());
// Apply validation pipes globally
app.useGlobalPipes(

View File

@ -11,7 +11,7 @@ import { Response } from 'express';
// the CORS headers are missing in the response.
// This class add CORS headers to exception response to avoid misleading CORS error
@Catch()
export class ApplyCorsToExceptions implements ExceptionFilter {
export class UnhandledExceptionFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
@ -20,6 +20,7 @@ export class ApplyCorsToExceptions implements ExceptionFilter {
return;
}
// TODO: Check if needed, remove otherwise.
response.header('Access-Control-Allow-Origin', '*');
response.header(
'Access-Control-Allow-Methods',

View File

@ -32,7 +32,14 @@ yarn command:prod upgrade-0.34
The `yarn database:migrate:prod` command will apply the migrations to the database structure (core and metadata schemas)
The `yarn command:prod upgrade-0.34` takes care of the data migration of all workspaces.
**Environment Variables**
- Removed: `FRONT_BASE_URL`
- Added: `FRONT_DOMAIN`, `FRONT_PROTOCOL`, `FRONT_PORT`
We have updated the way we handle the frontend URL.
You can now set the frontend URL using the `FRONT_DOMAIN`, `FRONT_PROTOCOL` and `FRONT_PORT` variables.
If FRONT_DOMAIN is not set, the frontend URL will fallback to `SERVER_URL`.
### v0.32.0 to v0.33.0