diff --git a/docker-compose-speckle.yml b/docker-compose-speckle.yml index d1f6790f9..446204c58 100644 --- a/docker-compose-speckle.yml +++ b/docker-compose-speckle.yml @@ -1,4 +1,3 @@ -version: '2.4' services: speckle-ingress: build: @@ -22,12 +21,16 @@ services: restart: always environment: NUXT_PUBLIC_SERVER_NAME: 'local' + #TODO: Change this to the URL of your server. This is the URL of the server as accessed by users. NUXT_PUBLIC_API_ORIGIN: 'http://127.0.0.1' + #TODO: Change this to the URL of your server. This is the URL of the server as accessed by users. NUXT_PUBLIC_BASE_URL: 'http://127.0.0.1' + # This is the URL of the server as accessed via this docker compose network. NUXT_PUBLIC_BACKEND_API_ORIGIN: 'http://speckle-server:3000' NUXT_PUBLIC_LOG_LEVEL: 'warn' NUXT_REDIS_URL: 'redis://redis' LOG_LEVEL: 'info' + LOG_PRETTY: 'true' speckle-server: build: @@ -47,14 +50,20 @@ services: retries: 3 start_period: 90s environment: - # TODO: Change this to the URL of the speckle server, as accessed from the network + # TODO. Change this to the url of your server. This is the URL of the server as accessed by users. CANONICAL_URL: 'http://127.0.0.1' + # This is the URL of the server as accessed by other Speckle services within this docker compose network, such as preview-service. + # This will be the same value as NUXT_PUBLIC_BACKEND_API_ORIGIN as defined in the frontend-2 service. + PRIVATE_SERVER_URL: 'http://speckle-server:3000' # TODO: Change this to a unique secret for this server SESSION_SECRET: 'TODO:Replace' + # This is the authentication strategy to use. Local (i.e. username & password) is the default strategy. STRATEGY_LOCAL: 'true' + LOG_LEVEL: 'info' + LOG_PRETTY: 'true' POSTGRES_URL: 'postgres' POSTGRES_USER: 'speckle' @@ -62,6 +71,7 @@ services: POSTGRES_DB: 'speckle' REDIS_URL: 'redis://redis' + PREVIEW_SERVICE_REDIS_URL: 'redis://redis' S3_ENDPOINT: 'http://minio:9000' S3_ACCESS_KEY: 'minioadmin' @@ -85,10 +95,11 @@ services: mem_limit: '3000m' memswap_limit: '3000m' environment: - HOST: '127.0.0.1' # Only accept connections from localhost, as preview service does not need to be exposed outside the container. - METRICS_HOST: '127.0.0.1' # Amend if you want to expose Prometheus metrics outside of the container + HOST: '127.0.0.1' # The preview service does not need to be exposed outside the container. + PORT: '3001' LOG_LEVEL: 'info' - PG_CONNECTION_STRING: 'postgres://speckle:speckle@postgres/speckle' + LOG_PRETTY: 'true' + REDIS_URL: 'redis://redis' webhook-service: build: @@ -99,6 +110,7 @@ services: restart: always environment: LOG_LEVEL: 'info' + LOG_PRETTY: 'true' PG_CONNECTION_STRING: 'postgres://speckle:speckle@postgres/speckle' fileimport-service: @@ -110,6 +122,7 @@ services: restart: always environment: LOG_LEVEL: 'info' + LOG_PRETTY: 'true' PG_CONNECTION_STRING: 'postgres://speckle:speckle@postgres/speckle' SPECKLE_SERVER_URL: 'http://speckle-server:3000' FILE_IMPORT_TIME_LIMIT_MIN: 10 diff --git a/packages/server/modules/previews/rest/router.ts b/packages/server/modules/previews/rest/router.ts index 964b7279b..92374d73b 100644 --- a/packages/server/modules/previews/rest/router.ts +++ b/packages/server/modules/previews/rest/router.ts @@ -37,7 +37,10 @@ import { storeTokenScopesFactory, storeUserServerAppTokenFactory } from '@/modules/core/repositories/tokens' -import { getServerOrigin } from '@/modules/shared/helpers/envHelper' +import { + getPrivateServerOrigin, + getServerOrigin +} from '@/modules/shared/helpers/envHelper' import { requestObjectPreviewFactory } from '@/modules/previews/queues/previews' import type { Queue } from 'bull' import type { Knex } from 'knex' @@ -61,7 +64,8 @@ const buildCreateObjectPreviewFunction = ({ queue: previewRequestQueue, responseQueue: responseQueueName }), - serverOrigin: getServerOrigin(), + // use the private server origin if defined, otherwise use the public server origin + serverOrigin: getPrivateServerOrigin() || getServerOrigin(), storeObjectPreview: storeObjectPreviewFactory({ db: projectDb }), getStreamCollaborators: getStreamCollaboratorsFactory({ db }), createAppToken: createAppTokenFactory({ diff --git a/packages/server/modules/shared/helpers/envHelper.ts b/packages/server/modules/shared/helpers/envHelper.ts index 7303fd2c0..9d398f0d9 100644 --- a/packages/server/modules/shared/helpers/envHelper.ts +++ b/packages/server/modules/shared/helpers/envHelper.ts @@ -1,7 +1,7 @@ import { MisconfiguredEnvironmentError } from '@/modules/shared/errors' import { trimEnd } from 'lodash' import * as Environment from '@speckle/shared/dist/commonjs/environment/index.js' -import { ensureError } from '@speckle/shared' +import { ensureError, Nullable } from '@speckle/shared' export function getStringFromEnv( envVarKey: string, @@ -28,6 +28,32 @@ export function getBooleanFromEnv(envVarKey: string, aDefault = false): boolean return ['1', 'true', true].includes(process.env[envVarKey] || aDefault.toString()) } +function mustGetUrlFromEnv(name: string, trimTrailingSlash: boolean = false): URL { + const url = getUrlFromEnv(name, trimTrailingSlash) + if (!url) throw new MisconfiguredEnvironmentError(`${name} env var not configured`) + return url +} + +function getUrlFromEnv( + name: string, + trimTrailingSlash: boolean = false +): Nullable { + const value = process.env[name] + if (!value) { + return null + } + try { + return new URL(trimTrailingSlash ? trimEnd(value, '/') : value) + } catch (e: unknown) { + const err = ensureError(e, 'Unknown error parsing URL') + if (err instanceof TypeError && err.message === 'Invalid URL') + throw new MisconfiguredEnvironmentError(`${name} has to be a valid URL`, { + cause: err + }) + throw new MisconfiguredEnvironmentError(`Error parsing ${name} URL`, { cause: err }) + } +} + export function getSessionSecret() { if (!process.env.SESSION_SECRET) { throw new MisconfiguredEnvironmentError('SESSION_SECRET env var not configured') @@ -198,29 +224,16 @@ export function getFrontendOrigin() { * Get server app origin/base URL */ export function getServerOrigin() { - if (!process.env.CANONICAL_URL) { - throw new MisconfiguredEnvironmentError( - 'Server origin environment variable (CANONICAL_URL) not configured' - ) - } + return mustGetUrlFromEnv('CANONICAL_URL', true).origin +} +export function getPrivateServerOrigin() { try { - return new URL(trimEnd(process.env.CANONICAL_URL, '/')).origin - } catch (e) { - const err = ensureError(e) - if (e instanceof TypeError && e.message === 'Invalid URL') { - throw new MisconfiguredEnvironmentError( - `Server origin environment variable (CANONICAL_URL) is not a valid URL: ${process.env.CANONICAL_URL} ${err.message}`, - { - cause: e, - info: { - value: process.env.CANONICAL_URL - } - } - ) - } - - throw err + const url = getUrlFromEnv('PRIVATE_SERVER_URL', true) + if (!url) return url + return url.origin + } catch { + return null } } @@ -239,26 +252,12 @@ export function isSSLServer() { return /^https:\/\//.test(getServerOrigin()) } -function parseUrlVar(value: string, name: string) { - try { - return new URL(value) - } catch (err: unknown) { - if (err instanceof TypeError && err.message === 'Invalid URL') - throw new MisconfiguredEnvironmentError(`${name} has to be a valid URL`) - throw err - } -} - export function getServerMovedFrom() { - const value = process.env.MIGRATION_SERVER_MOVED_FROM - if (!value) return value - return parseUrlVar(value, 'MIGRATION_SERVER_MOVED_FROM') + return getUrlFromEnv('MIGRATION_SERVER_MOVED_FROM') } export function getServerMovedTo() { - const value = process.env.MIGRATION_SERVER_MOVED_TO - if (!value) return value - return parseUrlVar(value, 'MIGRATION_SERVER_MOVED_TO') + return getUrlFromEnv('MIGRATION_SERVER_MOVED_TO') } export function adminOverrideEnabled() {