fix(docker compose): preview service can be used with docker compose

This commit is contained in:
Iain Sproat
2025-03-15 10:43:40 +00:00
parent 5c22e90fb0
commit 6e170d0c4f
3 changed files with 61 additions and 45 deletions
+18 -5
View File
@@ -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
@@ -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({
@@ -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<URL> {
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() {