mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-22 01:36:29 -05:00
refactor: move base-url-from-env-extractor into commons
This is done to use it in next.config.js Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
d675cc9ed9
commit
f6a6f6b086
15 changed files with 268 additions and 180 deletions
112
commons/src/parse-url/base-url-from-env-extractor.spec.ts
Normal file
112
commons/src/parse-url/base-url-from-env-extractor.spec.ts
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { BaseUrlFromEnvExtractor } from './base-url-from-env-extractor.js'
|
||||||
|
import { describe, it, expect } from '@jest/globals'
|
||||||
|
import { NoSubdirectoryAllowedError, NoValidUrlError } from './errors.js'
|
||||||
|
import { BaseUrls } from './base-urls.types.js'
|
||||||
|
|
||||||
|
describe('BaseUrlFromEnvExtractor', () => {
|
||||||
|
it('should return the correctly parsed values if all are set', () => {
|
||||||
|
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
||||||
|
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org/'
|
||||||
|
process.env.HD_SSR_API_URL = 'https://internal.example.org/'
|
||||||
|
const sut = new BaseUrlFromEnvExtractor()
|
||||||
|
const result = sut.extractBaseUrls()
|
||||||
|
|
||||||
|
expect(result).toStrictEqual({
|
||||||
|
editor: 'https://editor.example.org/',
|
||||||
|
renderer: 'https://renderer.example.org/',
|
||||||
|
ssrApi: 'https://internal.example.org/'
|
||||||
|
} as BaseUrls)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if no base url is set', () => {
|
||||||
|
process.env.HD_BASE_URL = undefined
|
||||||
|
const sut = new BaseUrlFromEnvExtractor()
|
||||||
|
|
||||||
|
expect(() => sut.extractBaseUrls()).toThrow(NoValidUrlError)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error if editor base url isn't an URL", () => {
|
||||||
|
process.env.HD_BASE_URL = 'bibedibabedibu'
|
||||||
|
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org/'
|
||||||
|
process.env.HD_SSR_API_URL = 'https://internal.example.org/'
|
||||||
|
const sut = new BaseUrlFromEnvExtractor()
|
||||||
|
|
||||||
|
expect(() => sut.extractBaseUrls()).toThrow(NoValidUrlError)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if renderer base url is set but no valid URL', () => {
|
||||||
|
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
||||||
|
process.env.HD_RENDERER_BASE_URL = 'bibedibabedibu'
|
||||||
|
process.env.HD_SSR_API_URL = 'https://internal.example.org/'
|
||||||
|
const sut = new BaseUrlFromEnvExtractor()
|
||||||
|
|
||||||
|
expect(() => sut.extractBaseUrls()).toThrow(NoValidUrlError)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if ssr api base url is set but no valid URL', () => {
|
||||||
|
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
||||||
|
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org/'
|
||||||
|
process.env.HD_SSR_API_URL = 'bibedibabedibu'
|
||||||
|
const sut = new BaseUrlFromEnvExtractor()
|
||||||
|
|
||||||
|
expect(() => sut.extractBaseUrls()).toThrow(NoValidUrlError)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if editor base url contains a path', () => {
|
||||||
|
process.env.HD_BASE_URL = 'https://editor.example.org/subpath/'
|
||||||
|
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org/'
|
||||||
|
const sut = new BaseUrlFromEnvExtractor()
|
||||||
|
|
||||||
|
expect(() => sut.extractBaseUrls()).toThrow(NoSubdirectoryAllowedError)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if renderer base url contains a path', () => {
|
||||||
|
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
||||||
|
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org/subpath/'
|
||||||
|
const sut = new BaseUrlFromEnvExtractor()
|
||||||
|
|
||||||
|
expect(() => sut.extractBaseUrls()).toThrow(NoSubdirectoryAllowedError)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error if ssr api url contains a path', () => {
|
||||||
|
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
||||||
|
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org/'
|
||||||
|
process.env.HD_SSR_API_URL = 'https://internal.example.org/subpath/'
|
||||||
|
const sut = new BaseUrlFromEnvExtractor()
|
||||||
|
|
||||||
|
expect(() => sut.extractBaseUrls()).toThrow(NoSubdirectoryAllowedError)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should copy editor base url to renderer base url if renderer base url is omitted', () => {
|
||||||
|
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
||||||
|
delete process.env.HD_RENDERER_BASE_URL
|
||||||
|
process.env.HD_SSR_API_URL = 'https://internal.example.org/'
|
||||||
|
const sut = new BaseUrlFromEnvExtractor()
|
||||||
|
const result = sut.extractBaseUrls()
|
||||||
|
|
||||||
|
expect(result).toStrictEqual({
|
||||||
|
editor: 'https://editor.example.org/',
|
||||||
|
renderer: 'https://editor.example.org/',
|
||||||
|
ssrApi: 'https://internal.example.org/'
|
||||||
|
} as BaseUrls)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should copy editor base url to ssr api url if ssr api url is omitted', () => {
|
||||||
|
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
||||||
|
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org/'
|
||||||
|
delete process.env.HD_SSR_API_URL
|
||||||
|
const sut = new BaseUrlFromEnvExtractor()
|
||||||
|
const result = sut.extractBaseUrls()
|
||||||
|
|
||||||
|
expect(result).toStrictEqual({
|
||||||
|
editor: 'https://editor.example.org/',
|
||||||
|
renderer: 'https://renderer.example.org/',
|
||||||
|
ssrApi: 'https://editor.example.org/'
|
||||||
|
} as BaseUrls)
|
||||||
|
})
|
||||||
|
})
|
98
commons/src/parse-url/base-url-from-env-extractor.ts
Normal file
98
commons/src/parse-url/base-url-from-env-extractor.ts
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
import { isTestMode, isBuildTime, Logger } from '../utils/index.js'
|
||||||
|
import { parseUrl } from './parse-url.js'
|
||||||
|
import { NoValidUrlError } from './errors.js'
|
||||||
|
import { Optional } from '@mrdrogdrog/optional'
|
||||||
|
import { BaseUrls } from './base-urls.types.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts and caches the editor and renderer base urls from the environment variables.
|
||||||
|
*/
|
||||||
|
export class BaseUrlFromEnvExtractor {
|
||||||
|
private baseUrls: BaseUrls | undefined
|
||||||
|
private readonly logger: Logger | undefined
|
||||||
|
|
||||||
|
constructor(withLogging: boolean = true) {
|
||||||
|
this.logger = withLogging ? new Logger('Base URL Configuration') : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractEditorBaseUrlFromEnv(): URL {
|
||||||
|
return parseUrl(process.env.HD_BASE_URL).orElseThrow(
|
||||||
|
() => new NoValidUrlError('HD_BASE_URL')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractExtraUrlFromEnv(envVarName: string, editorBaseUrl: URL): URL {
|
||||||
|
if (isTestMode) {
|
||||||
|
this.logger?.info('Test mode activated. Using editor base url.')
|
||||||
|
return editorBaseUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
const rendererBaseUrl = Optional.ofNullable(process.env[envVarName])
|
||||||
|
.filter((value) => value !== '')
|
||||||
|
.map((value) =>
|
||||||
|
parseUrl(value).orElseThrow(() => new NoValidUrlError(envVarName))
|
||||||
|
)
|
||||||
|
.orElse(undefined)
|
||||||
|
|
||||||
|
if (rendererBaseUrl === undefined) {
|
||||||
|
this.logger?.info(
|
||||||
|
`${envVarName} is unset. Using editor base url for renderer.`
|
||||||
|
)
|
||||||
|
return editorBaseUrl
|
||||||
|
} else {
|
||||||
|
return rendererBaseUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private renewBaseUrls(): BaseUrls {
|
||||||
|
const editorBaseUrl = this.extractEditorBaseUrlFromEnv()
|
||||||
|
const rendererBaseUrl = this.extractExtraUrlFromEnv(
|
||||||
|
'HD_RENDERER_BASE_URL',
|
||||||
|
editorBaseUrl
|
||||||
|
)
|
||||||
|
const ssrApiUrl = this.extractExtraUrlFromEnv(
|
||||||
|
'HD_SSR_API_URL',
|
||||||
|
editorBaseUrl
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
editor: editorBaseUrl.toString(),
|
||||||
|
renderer: rendererBaseUrl.toString(),
|
||||||
|
ssrApi: ssrApiUrl.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the editor and renderer base urls from the environment variables.
|
||||||
|
*
|
||||||
|
* @return An {@link Optional} with the base urls.
|
||||||
|
*/
|
||||||
|
public extractBaseUrls(): BaseUrls {
|
||||||
|
if (isBuildTime) {
|
||||||
|
return {
|
||||||
|
editor: 'https://example.org/',
|
||||||
|
renderer: 'https://example.org/',
|
||||||
|
ssrApi: 'https://example.org/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.baseUrls === undefined) {
|
||||||
|
this.baseUrls = this.renewBaseUrls()
|
||||||
|
this.logBaseUrls()
|
||||||
|
}
|
||||||
|
return this.baseUrls
|
||||||
|
}
|
||||||
|
|
||||||
|
private logBaseUrls() {
|
||||||
|
if (this.baseUrls === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.logger?.info('Editor base URL', this.baseUrls.editor.toString())
|
||||||
|
this.logger?.info('Renderer base URL', this.baseUrls.renderer.toString())
|
||||||
|
}
|
||||||
|
}
|
14
commons/src/parse-url/base-urls.types.ts
Normal file
14
commons/src/parse-url/base-urls.types.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the URLs that are returned by the {@link BaseUrlFromEnvExtractor}.
|
||||||
|
*/
|
||||||
|
export interface BaseUrls {
|
||||||
|
renderer: string
|
||||||
|
editor: string
|
||||||
|
ssrApi: string
|
||||||
|
}
|
|
@ -4,14 +4,29 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown if an {@link URL} contains a subdirectory.
|
||||||
|
*/
|
||||||
export class NoSubdirectoryAllowedError extends Error {
|
export class NoSubdirectoryAllowedError extends Error {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('Subdirectories are not allowed')
|
super('Subdirectories are not allowed')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown if the protocol of an {@link URL} isn't https or http.
|
||||||
|
*/
|
||||||
export class WrongProtocolError extends Error {
|
export class WrongProtocolError extends Error {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('Protocol must be HTTP or HTTPS')
|
super('Protocol must be HTTP or HTTPS')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown if a value isn't a valid {@link URL}.
|
||||||
|
*/
|
||||||
|
export class NoValidUrlError extends Error {
|
||||||
|
constructor(varName: string) {
|
||||||
|
super(`${varName} is no valid URL`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,3 +6,5 @@
|
||||||
|
|
||||||
export { parseUrl } from './parse-url.js'
|
export { parseUrl } from './parse-url.js'
|
||||||
export { NoSubdirectoryAllowedError, WrongProtocolError } from './errors.js'
|
export { NoSubdirectoryAllowedError, WrongProtocolError } from './errors.js'
|
||||||
|
export { BaseUrlFromEnvExtractor } from './base-url-from-env-extractor.js'
|
||||||
|
export type { BaseUrls } from './base-urls.types.js'
|
||||||
|
|
|
@ -33,7 +33,7 @@ describe('validate url', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('trailing slash', () => {
|
describe('subpaths', () => {
|
||||||
it('accepts urls with just domain with trailing slash', () => {
|
it('accepts urls with just domain with trailing slash', () => {
|
||||||
expect(parseUrl('http://example.org/').get().toString()).toEqual(
|
expect(parseUrl('http://example.org/').get().toString()).toEqual(
|
||||||
'http://example.org/'
|
'http://example.org/'
|
||||||
|
|
|
@ -45,4 +45,15 @@ rm -f dist/frontend/.env
|
||||||
rm -rf dist/frontend/public/public
|
rm -rf dist/frontend/public/public
|
||||||
rm -rf dist/frontend/src
|
rm -rf dist/frontend/src
|
||||||
|
|
||||||
|
echo "🦔 > Patching env var check into prod build"
|
||||||
|
|
||||||
|
cat << EOF > dist/frontend/server.js.new
|
||||||
|
const { BaseUrlFromEnvExtractor } = require('@hedgedoc/commons')
|
||||||
|
new BaseUrlFromEnvExtractor(true).extractBaseUrls()
|
||||||
|
|
||||||
|
EOF
|
||||||
|
cat dist/frontend/server.js >> dist/frontend/server.js.new
|
||||||
|
rm dist/frontend/server.js
|
||||||
|
mv dist/frontend/server.js.new dist/frontend/server.js
|
||||||
|
|
||||||
echo "🦔 > Done! You can run the build by going into the dist directory and executing \`node frontend/server.js\`"
|
echo "🦔 > Done! You can run the build by going into the dist directory and executing \`node frontend/server.js\`"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
const { isMockMode, isTestMode, isProfilingMode, isBuildTime, Logger } = require('@hedgedoc/commons')
|
const { isMockMode, isTestMode, isProfilingMode, isBuildTime, Logger, BaseUrlFromEnvExtractor } = require('@hedgedoc/commons')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||||
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||||
|
@ -13,6 +13,7 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||||
const logger = new Logger('Bootstrap')
|
const logger = new Logger('Bootstrap')
|
||||||
|
|
||||||
logger.info('Node environment is', process.env.NODE_ENV)
|
logger.info('Node environment is', process.env.NODE_ENV)
|
||||||
|
new BaseUrlFromEnvExtractor(false).extractBaseUrls()
|
||||||
|
|
||||||
if (isTestMode) {
|
if (isTestMode) {
|
||||||
logger.warn(`This build runs in test mode. This means:
|
logger.warn(`This build runs in test mode. This means:
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
*/
|
*/
|
||||||
import { getNote } from '../../../api/notes'
|
import { getNote } from '../../../api/notes'
|
||||||
import { redirect } from 'next/navigation'
|
import { redirect } from 'next/navigation'
|
||||||
import { baseUrlFromEnvExtractor } from '../../../utils/base-url-from-env-extractor'
|
|
||||||
import { notFound } from 'next/navigation'
|
import { notFound } from 'next/navigation'
|
||||||
|
import { baseUrlFromEnvExtractor } from '../../../utils/base-url-singelton'
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: { id: string | undefined }
|
params: { id: string | undefined }
|
||||||
|
|
|
@ -12,12 +12,12 @@ import { DarkMode } from '../../components/layout/dark-mode/dark-mode'
|
||||||
import { ExpectedOriginBoundary } from '../../components/layout/expected-origin-boundary'
|
import { ExpectedOriginBoundary } from '../../components/layout/expected-origin-boundary'
|
||||||
import { UiNotificationBoundary } from '../../components/notifications/ui-notification-boundary'
|
import { UiNotificationBoundary } from '../../components/notifications/ui-notification-boundary'
|
||||||
import { StoreProvider } from '../../redux/store-provider'
|
import { StoreProvider } from '../../redux/store-provider'
|
||||||
import { baseUrlFromEnvExtractor } from '../../utils/base-url-from-env-extractor'
|
|
||||||
import { configureLuxon } from '../../utils/configure-luxon'
|
import { configureLuxon } from '../../utils/configure-luxon'
|
||||||
import type { Metadata } from 'next'
|
import type { Metadata } from 'next'
|
||||||
import type { PropsWithChildren } from 'react'
|
import type { PropsWithChildren } from 'react'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { getConfig } from '../../api/config'
|
import { getConfig } from '../../api/config'
|
||||||
|
import { baseUrlFromEnvExtractor } from '../../utils/base-url-singelton'
|
||||||
|
|
||||||
configureLuxon()
|
configureLuxon()
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ import { BaseUrlContextProvider } from '../../components/common/base-url/base-ur
|
||||||
import { FrontendConfigContextProvider } from '../../components/common/frontend-config-context/frontend-config-context-provider'
|
import { FrontendConfigContextProvider } from '../../components/common/frontend-config-context/frontend-config-context-provider'
|
||||||
import { ExpectedOriginBoundary } from '../../components/layout/expected-origin-boundary'
|
import { ExpectedOriginBoundary } from '../../components/layout/expected-origin-boundary'
|
||||||
import { StoreProvider } from '../../redux/store-provider'
|
import { StoreProvider } from '../../redux/store-provider'
|
||||||
import { baseUrlFromEnvExtractor } from '../../utils/base-url-from-env-extractor'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { getConfig } from '../../api/config'
|
import { getConfig } from '../../api/config'
|
||||||
|
import { baseUrlFromEnvExtractor } from '../../utils/base-url-singelton'
|
||||||
|
|
||||||
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
const baseUrls = baseUrlFromEnvExtractor.extractBaseUrls()
|
const baseUrls = baseUrlFromEnvExtractor.extractBaseUrls()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
import type { BaseUrls } from '@hedgedoc/commons'
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
*
|
*
|
||||||
|
@ -7,11 +8,6 @@
|
||||||
import type { PropsWithChildren } from 'react'
|
import type { PropsWithChildren } from 'react'
|
||||||
import React, { createContext } from 'react'
|
import React, { createContext } from 'react'
|
||||||
|
|
||||||
export interface BaseUrls {
|
|
||||||
renderer: string
|
|
||||||
editor: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BaseUrlContextProviderProps {
|
interface BaseUrlContextProviderProps {
|
||||||
baseUrls?: BaseUrls
|
baseUrls?: BaseUrls
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
import { BaseUrlFromEnvExtractor } from './base-url-from-env-extractor'
|
|
||||||
|
|
||||||
describe('BaseUrlFromEnvExtractor', () => {
|
|
||||||
it('should return the base urls if both are valid urls', () => {
|
|
||||||
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
|
||||||
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org/'
|
|
||||||
const sut = new BaseUrlFromEnvExtractor()
|
|
||||||
|
|
||||||
expect(sut.extractBaseUrls()).toStrictEqual({
|
|
||||||
renderer: 'https://renderer.example.org/',
|
|
||||||
editor: 'https://editor.example.org/'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return an empty optional if no var is set', () => {
|
|
||||||
process.env.HD_BASE_URL = undefined
|
|
||||||
process.env.HD_RENDERER_BASE_URL = undefined
|
|
||||||
const sut = new BaseUrlFromEnvExtractor()
|
|
||||||
|
|
||||||
expect(() => sut.extractBaseUrls()).toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return an empty optional if editor base url isn't an URL", () => {
|
|
||||||
process.env.HD_BASE_URL = 'bibedibabedibu'
|
|
||||||
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org/'
|
|
||||||
const sut = new BaseUrlFromEnvExtractor()
|
|
||||||
|
|
||||||
expect(() => sut.extractBaseUrls()).toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return an empty optional if renderer base url isn't an URL", () => {
|
|
||||||
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
|
||||||
process.env.HD_RENDERER_BASE_URL = 'bibedibabedibu'
|
|
||||||
const sut = new BaseUrlFromEnvExtractor()
|
|
||||||
|
|
||||||
expect(() => sut.extractBaseUrls()).toThrow()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return an optional if editor base url isn't ending with a slash", () => {
|
|
||||||
process.env.HD_BASE_URL = 'https://editor.example.org'
|
|
||||||
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org/'
|
|
||||||
const sut = new BaseUrlFromEnvExtractor()
|
|
||||||
|
|
||||||
expect(sut.extractBaseUrls()).toStrictEqual({
|
|
||||||
renderer: 'https://renderer.example.org/',
|
|
||||||
editor: 'https://editor.example.org/'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should return an optional if renderer base url isn't ending with a slash", () => {
|
|
||||||
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
|
||||||
process.env.HD_RENDERER_BASE_URL = 'https://renderer.example.org'
|
|
||||||
const sut = new BaseUrlFromEnvExtractor()
|
|
||||||
|
|
||||||
expect(sut.extractBaseUrls()).toStrictEqual({
|
|
||||||
renderer: 'https://renderer.example.org/',
|
|
||||||
editor: 'https://editor.example.org/'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should copy editor base url to renderer base url if renderer base url is omitted', () => {
|
|
||||||
process.env.HD_BASE_URL = 'https://editor.example.org/'
|
|
||||||
delete process.env.HD_RENDERER_BASE_URL
|
|
||||||
const sut = new BaseUrlFromEnvExtractor()
|
|
||||||
|
|
||||||
expect(sut.extractBaseUrls()).toStrictEqual({
|
|
||||||
renderer: 'https://editor.example.org/',
|
|
||||||
editor: 'https://editor.example.org/'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,94 +0,0 @@
|
||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
import type { BaseUrls } from '../components/common/base-url/base-url-context-provider'
|
|
||||||
import { NoSubdirectoryAllowedError, parseUrl, Logger, isTestMode, isBuildTime } from '@hedgedoc/commons'
|
|
||||||
import { Optional } from '@mrdrogdrog/optional'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts and caches the editor and renderer base urls from the environment variables.
|
|
||||||
*/
|
|
||||||
export class BaseUrlFromEnvExtractor {
|
|
||||||
private baseUrls: BaseUrls | undefined
|
|
||||||
private readonly logger = new Logger('Base URL Configuration')
|
|
||||||
|
|
||||||
private extractUrlFromEnvVar(envVarName: string, envVarValue: string | undefined): Optional<URL> {
|
|
||||||
try {
|
|
||||||
return parseUrl(envVarValue)
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof NoSubdirectoryAllowedError) {
|
|
||||||
this.logger.error(error.message)
|
|
||||||
return Optional.empty()
|
|
||||||
} else {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extractEditorBaseUrlFromEnv(): Optional<URL> {
|
|
||||||
const envValue = this.extractUrlFromEnvVar('HD_BASE_URL', process.env.HD_BASE_URL)
|
|
||||||
if (envValue.isEmpty()) {
|
|
||||||
this.logger.error("HD_BASE_URL isn't a valid URL!")
|
|
||||||
}
|
|
||||||
return envValue
|
|
||||||
}
|
|
||||||
|
|
||||||
private extractRendererBaseUrlFromEnv(editorBaseUrl: URL): Optional<URL> {
|
|
||||||
if (isTestMode) {
|
|
||||||
this.logger.info('Test mode activated. Using editor base url for renderer.')
|
|
||||||
return Optional.of(editorBaseUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!process.env.HD_RENDERER_BASE_URL) {
|
|
||||||
this.logger.info('HD_RENDERER_BASE_URL is unset. Using editor base url for renderer.')
|
|
||||||
return Optional.of(editorBaseUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.extractUrlFromEnvVar('HD_RENDERER_BASE_URL', process.env.HD_RENDERER_BASE_URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
private renewBaseUrls(): BaseUrls {
|
|
||||||
return this.extractEditorBaseUrlFromEnv()
|
|
||||||
.flatMap((editorBaseUrl) =>
|
|
||||||
this.extractRendererBaseUrlFromEnv(editorBaseUrl).map((rendererBaseUrl) => {
|
|
||||||
return {
|
|
||||||
editor: editorBaseUrl.toString(),
|
|
||||||
renderer: rendererBaseUrl.toString()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.orElseThrow(() => new Error('couldnt parse env vars'))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts the editor and renderer base urls from the environment variables.
|
|
||||||
*
|
|
||||||
* @return An {@link Optional} with the base urls.
|
|
||||||
*/
|
|
||||||
public extractBaseUrls(): BaseUrls {
|
|
||||||
if (isBuildTime) {
|
|
||||||
return {
|
|
||||||
editor: 'https://example.org/',
|
|
||||||
renderer: 'https://example.org/'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.baseUrls === undefined) {
|
|
||||||
this.baseUrls = this.renewBaseUrls()
|
|
||||||
this.logBaseUrls()
|
|
||||||
}
|
|
||||||
return this.baseUrls
|
|
||||||
}
|
|
||||||
|
|
||||||
private logBaseUrls() {
|
|
||||||
if (this.baseUrls === undefined) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.logger.info('Editor base URL', this.baseUrls.editor.toString())
|
|
||||||
this.logger.info('Renderer base URL', this.baseUrls.renderer.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const baseUrlFromEnvExtractor = new BaseUrlFromEnvExtractor()
|
|
9
frontend/src/utils/base-url-singelton.ts
Normal file
9
frontend/src/utils/base-url-singelton.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BaseUrlFromEnvExtractor } from '@hedgedoc/commons'
|
||||||
|
|
||||||
|
export const baseUrlFromEnvExtractor = new BaseUrlFromEnvExtractor()
|
Loading…
Reference in a new issue