refactor(domain-manager): simplify frontend URL configuration (#10194)
Replaced multiple environment variables for frontend URL construction with a single FRONTEND_URL variable. This change reduces complexity and improves clarity by consolidating frontend URL handling into one source. Updated relevant validations and removed unused variables like FRONT_PROTOCOL and FRONT_PORT. Fix #10016
This commit is contained in:
@ -20,7 +20,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
environment:
|
environment:
|
||||||
PORT: 3000
|
NODE_PORT: 3000
|
||||||
PG_DATABASE_URL: postgres://${PG_DATABASE_USER:-postgres}:${PG_DATABASE_PASSWORD:-postgres}@${PG_DATABASE_HOST:-db}:${PG_DATABASE_PORT:-5432}/default
|
PG_DATABASE_URL: postgres://${PG_DATABASE_USER:-postgres}:${PG_DATABASE_PASSWORD:-postgres}@${PG_DATABASE_HOST:-db}:${PG_DATABASE_PORT:-5432}/default
|
||||||
SERVER_URL: ${SERVER_URL}
|
SERVER_URL: ${SERVER_URL}
|
||||||
REDIS_URL: ${REDIS_URL:-redis://redis:6379}
|
REDIS_URL: ${REDIS_URL:-redis://redis:6379}
|
||||||
|
|||||||
@ -33,7 +33,7 @@ spec:
|
|||||||
image: twentycrm/twenty:latest
|
image: twentycrm/twenty:latest
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
env:
|
env:
|
||||||
- name: PORT
|
- name: NODE_PORT
|
||||||
value: 3000
|
value: 3000
|
||||||
- name: SERVER_URL
|
- name: SERVER_URL
|
||||||
value: "https://crm.example.com:443"
|
value: "https://crm.example.com:443"
|
||||||
|
|||||||
@ -38,7 +38,7 @@ resource "kubernetes_deployment" "twentycrm_server" {
|
|||||||
tty = true
|
tty = true
|
||||||
|
|
||||||
env {
|
env {
|
||||||
name = "PORT"
|
name = "NODE_PORT"
|
||||||
value = "3000"
|
value = "3000"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,9 +5,7 @@ REDIS_URL=redis://localhost:6379
|
|||||||
APP_SECRET=replace_me_with_a_random_string
|
APP_SECRET=replace_me_with_a_random_string
|
||||||
SIGN_IN_PREFILLED=true
|
SIGN_IN_PREFILLED=true
|
||||||
|
|
||||||
FRONT_PROTOCOL=http
|
FRONTEND_URL=http://localhost:3001
|
||||||
FRONT_DOMAIN=localhost
|
|
||||||
FRONT_PORT=3001
|
|
||||||
|
|
||||||
# ———————— Optional ————————
|
# ———————— Optional ————————
|
||||||
# PORT=3000
|
# PORT=3000
|
||||||
|
|||||||
@ -15,8 +15,7 @@ describe('DomainManagerService', () => {
|
|||||||
.spyOn(environmentService, 'get')
|
.spyOn(environmentService, 'get')
|
||||||
.mockImplementation((key: string) => {
|
.mockImplementation((key: string) => {
|
||||||
const env = {
|
const env = {
|
||||||
FRONT_PROTOCOL: 'https',
|
FRONTEND_URL: 'https://example.com',
|
||||||
FRONT_DOMAIN: 'example.com',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return env[key];
|
return env[key];
|
||||||
@ -39,8 +38,7 @@ describe('DomainManagerService', () => {
|
|||||||
.spyOn(environmentService, 'get')
|
.spyOn(environmentService, 'get')
|
||||||
.mockImplementation((key: string) => {
|
.mockImplementation((key: string) => {
|
||||||
const env = {
|
const env = {
|
||||||
FRONT_PROTOCOL: 'https',
|
FRONTEND_URL: 'https://example.com',
|
||||||
FRONT_DOMAIN: 'example.com',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return env[key];
|
return env[key];
|
||||||
@ -84,13 +82,12 @@ describe('DomainManagerService', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('buildBaseUrl', () => {
|
describe('buildBaseUrl', () => {
|
||||||
it('should build the base URL with protocol and domain from environment variables', () => {
|
it('should build the base URL from environment variables', () => {
|
||||||
jest
|
jest
|
||||||
.spyOn(environmentService, 'get')
|
.spyOn(environmentService, 'get')
|
||||||
.mockImplementation((key: string) => {
|
.mockImplementation((key: string) => {
|
||||||
const env = {
|
const env = {
|
||||||
FRONT_PROTOCOL: 'https',
|
FRONTEND_URL: 'https://example.com',
|
||||||
FRONT_DOMAIN: 'example.com',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return env[key];
|
return env[key];
|
||||||
@ -106,8 +103,8 @@ describe('DomainManagerService', () => {
|
|||||||
.spyOn(environmentService, 'get')
|
.spyOn(environmentService, 'get')
|
||||||
.mockImplementation((key: string) => {
|
.mockImplementation((key: string) => {
|
||||||
const env = {
|
const env = {
|
||||||
FRONT_PROTOCOL: 'https',
|
FRONTEND_URL: 'https://example.com',
|
||||||
FRONT_DOMAIN: 'example.com',
|
|
||||||
IS_MULTIWORKSPACE_ENABLED: true,
|
IS_MULTIWORKSPACE_ENABLED: true,
|
||||||
DEFAULT_SUBDOMAIN: 'test',
|
DEFAULT_SUBDOMAIN: 'test',
|
||||||
};
|
};
|
||||||
@ -119,24 +116,6 @@ describe('DomainManagerService', () => {
|
|||||||
|
|
||||||
expect(result.toString()).toBe('https://test.example.com/');
|
expect(result.toString()).toBe('https://test.example.com/');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should append port if FRONT_PORT is set', () => {
|
|
||||||
jest
|
|
||||||
.spyOn(environmentService, 'get')
|
|
||||||
.mockImplementation((key: string) => {
|
|
||||||
const env = {
|
|
||||||
FRONT_PROTOCOL: 'https',
|
|
||||||
FRONT_DOMAIN: 'example.com',
|
|
||||||
FRONT_PORT: '8080',
|
|
||||||
};
|
|
||||||
|
|
||||||
return env[key];
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = domainManagerService.getBaseUrl();
|
|
||||||
|
|
||||||
expect(result.toString()).toBe('https://example.com:8080/');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('buildWorkspaceURL', () => {
|
describe('buildWorkspaceURL', () => {
|
||||||
@ -145,8 +124,8 @@ describe('DomainManagerService', () => {
|
|||||||
.spyOn(environmentService, 'get')
|
.spyOn(environmentService, 'get')
|
||||||
.mockImplementation((key: string) => {
|
.mockImplementation((key: string) => {
|
||||||
const env = {
|
const env = {
|
||||||
FRONT_PROTOCOL: 'https',
|
FRONTEND_URL: 'https://example.com',
|
||||||
FRONT_DOMAIN: 'example.com',
|
|
||||||
IS_MULTIWORKSPACE_ENABLED: true,
|
IS_MULTIWORKSPACE_ENABLED: true,
|
||||||
DEFAULT_SUBDOMAIN: 'default',
|
DEFAULT_SUBDOMAIN: 'default',
|
||||||
};
|
};
|
||||||
@ -170,8 +149,7 @@ describe('DomainManagerService', () => {
|
|||||||
.spyOn(environmentService, 'get')
|
.spyOn(environmentService, 'get')
|
||||||
.mockImplementation((key: string) => {
|
.mockImplementation((key: string) => {
|
||||||
const env = {
|
const env = {
|
||||||
FRONT_PROTOCOL: 'https',
|
FRONTEND_URL: 'https://example.com',
|
||||||
FRONT_DOMAIN: 'example.com',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return env[key];
|
return env[key];
|
||||||
@ -194,8 +172,7 @@ describe('DomainManagerService', () => {
|
|||||||
.spyOn(environmentService, 'get')
|
.spyOn(environmentService, 'get')
|
||||||
.mockImplementation((key: string) => {
|
.mockImplementation((key: string) => {
|
||||||
const env = {
|
const env = {
|
||||||
FRONT_PROTOCOL: 'https',
|
FRONTEND_URL: 'https://example.com',
|
||||||
FRONT_DOMAIN: 'example.com',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return env[key];
|
return env[key];
|
||||||
|
|||||||
@ -21,28 +21,10 @@ export class DomainManagerService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
getFrontUrl() {
|
getFrontUrl() {
|
||||||
let baseUrl: URL;
|
return new URL(
|
||||||
const frontPort = this.environmentService.get('FRONT_PORT');
|
this.environmentService.get('FRONTEND_URL') ??
|
||||||
const frontDomain = this.environmentService.get('FRONT_DOMAIN');
|
this.environmentService.get('SERVER_URL'),
|
||||||
const frontProtocol = this.environmentService.get('FRONT_PROTOCOL');
|
);
|
||||||
|
|
||||||
const serverUrl = this.environmentService.get('SERVER_URL');
|
|
||||||
|
|
||||||
if (!frontDomain) {
|
|
||||||
baseUrl = new URL(serverUrl);
|
|
||||||
} else {
|
|
||||||
baseUrl = new URL(`${frontProtocol}://${frontDomain}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frontPort) {
|
|
||||||
baseUrl.port = frontPort.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frontProtocol) {
|
|
||||||
baseUrl.protocol = frontProtocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getBaseUrl(): URL {
|
getBaseUrl(): URL {
|
||||||
|
|||||||
@ -551,11 +551,11 @@ export class EnvironmentVariables {
|
|||||||
|
|
||||||
@EnvironmentVariablesMetadata({
|
@EnvironmentVariablesMetadata({
|
||||||
group: EnvironmentVariablesGroup.ServerConfig,
|
group: EnvironmentVariablesGroup.ServerConfig,
|
||||||
description: 'Domain for the frontend application',
|
description: 'Url for the frontend application',
|
||||||
})
|
})
|
||||||
@IsString()
|
@IsUrl({ require_tld: false, require_protocol: true })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
FRONT_DOMAIN?: string;
|
FRONTEND_URL: string;
|
||||||
|
|
||||||
@EnvironmentVariablesMetadata({
|
@EnvironmentVariablesMetadata({
|
||||||
group: EnvironmentVariablesGroup.ServerConfig,
|
group: EnvironmentVariablesGroup.ServerConfig,
|
||||||
@ -566,23 +566,6 @@ export class EnvironmentVariables {
|
|||||||
@ValidateIf((env) => env.IS_MULTIWORKSPACE_ENABLED)
|
@ValidateIf((env) => env.IS_MULTIWORKSPACE_ENABLED)
|
||||||
DEFAULT_SUBDOMAIN = 'app';
|
DEFAULT_SUBDOMAIN = 'app';
|
||||||
|
|
||||||
@EnvironmentVariablesMetadata({
|
|
||||||
group: EnvironmentVariablesGroup.ServerConfig,
|
|
||||||
description: 'Protocol for the frontend application (http or https)',
|
|
||||||
})
|
|
||||||
@IsString()
|
|
||||||
@IsOptional()
|
|
||||||
FRONT_PROTOCOL?: 'http' | 'https' = 'http';
|
|
||||||
|
|
||||||
@EnvironmentVariablesMetadata({
|
|
||||||
group: EnvironmentVariablesGroup.ServerConfig,
|
|
||||||
description: 'Port for the frontend application',
|
|
||||||
})
|
|
||||||
@CastToPositiveNumber()
|
|
||||||
@IsNumber()
|
|
||||||
@IsOptional()
|
|
||||||
FRONT_PORT?: number;
|
|
||||||
|
|
||||||
@EnvironmentVariablesMetadata({
|
@EnvironmentVariablesMetadata({
|
||||||
group: EnvironmentVariablesGroup.Other,
|
group: EnvironmentVariablesGroup.Other,
|
||||||
description: 'ID for the Chrome extension',
|
description: 'ID for the Chrome extension',
|
||||||
|
|||||||
@ -155,8 +155,7 @@ yarn command:prod cron:calendar:ongoing-stale
|
|||||||
['FRONT_DOMAIN', 'localhost', 'Domain of the hosted frontend'],
|
['FRONT_DOMAIN', 'localhost', 'Domain of the hosted frontend'],
|
||||||
['DEFAULT_SUBDOMAIN', 'app', 'The default subdomain name when multiworkspace mode is enabled'],
|
['DEFAULT_SUBDOMAIN', 'app', 'The default subdomain name when multiworkspace mode is enabled'],
|
||||||
['SERVER_URL', 'http://localhost:3000', 'Url to the hosted server'],
|
['SERVER_URL', 'http://localhost:3000', 'Url to the hosted server'],
|
||||||
['FRONT_PROTOCOL', 'http', 'protocol of the frontend server. Could be `http` or `https`'],
|
['FRONTEND_URL', '$SERVER_URL', 'Url to the frontend server. Same as SERVER_URL by default'],
|
||||||
['FRONT_PORT', '3001', 'Port of the frontend server.'],
|
|
||||||
['PORT', '3000', 'Port of the backend server'],
|
['PORT', '3000', 'Port of the backend server'],
|
||||||
['CACHE_STORAGE_TTL', '3600 * 24 * 7', 'Cache TTL in seconds']
|
['CACHE_STORAGE_TTL', '3600 * 24 * 7', 'Cache TTL in seconds']
|
||||||
]}></ArticleTable>
|
]}></ArticleTable>
|
||||||
|
|||||||
@ -26,7 +26,14 @@ If you want to upgrade your instance by few versions, e.g. from 0.33.0 to 0.35.0
|
|||||||
|
|
||||||
## Version-specific upgrade steps
|
## Version-specific upgrade steps
|
||||||
|
|
||||||
|
### v0.41.0 to v0.42.0
|
||||||
|
|
||||||
|
Upgrade your Twenty instance to use v0.42.0 image
|
||||||
|
|
||||||
|
**Environment Variables**
|
||||||
|
|
||||||
|
- Removed: `FRONT_PORT`, `FRONT_PROTOCOL`, `FRONT_DOMAIN`, `PORT`
|
||||||
|
- Added: `FRONTEND_URL`, `NODE_PORT`
|
||||||
|
|
||||||
### v0.40.0 to v0.41.0
|
### v0.40.0 to v0.41.0
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user