mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 01:46:07 +00:00
copy new setup from frontend to admin
This commit is contained in:
parent
43f982bd5a
commit
5061c83f0c
@ -11,7 +11,7 @@ ENV BUILD_DATE="1970-01-01T00:00:00.00Z"
|
||||
## We cannot do $(npm run version).${BUILD_NUMBER} here so we default to 0.0.0.0
|
||||
ENV BUILD_VERSION="0.0.0.0"
|
||||
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
|
||||
ENV BUILD_COMMIT="0000000"
|
||||
ENV BUILD_COMMIT_SHORT="0000000"
|
||||
## SET NODE_ENV
|
||||
ARG NODE_ENV="production"
|
||||
## App relevant Envs
|
||||
|
||||
@ -74,6 +74,7 @@
|
||||
"eslint-plugin-prettier": "5.2.1",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"eslint-plugin-vue": "8.7.1",
|
||||
"joi": "^17.13.3",
|
||||
"jsdom": "^25.0.0",
|
||||
"mock-apollo-client": "^1.2.1",
|
||||
"postcss": "^8.4.8",
|
||||
|
||||
@ -3,15 +3,13 @@
|
||||
|
||||
// Load Package Details for some default values
|
||||
const pkg = require('../../package')
|
||||
const schema = require('./schema')
|
||||
const joi = require('joi')
|
||||
|
||||
const version = {
|
||||
ADMIN_MODULE_PROTOCOL: process.env.ADMIN_MODULE_PROTOCOL ?? 'http',
|
||||
ADMIN_MODULE_HOST: process.env.ADMIN_MODULE_HOST ?? '0.0.0.0',
|
||||
ADMIN_MODULE_PORT: process.env.ADMIN_MODULE_PORT ?? '8080',
|
||||
APP_VERSION: pkg.version,
|
||||
BUILD_COMMIT: process.env.BUILD_COMMIT ?? null,
|
||||
BUILD_COMMIT: process.env.BUILD_COMMIT ?? undefined,
|
||||
// self reference of `version.BUILD_COMMIT` is not possible at this point, hence the duplicate code
|
||||
BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT ?? '0000000').slice(0, 7),
|
||||
}
|
||||
@ -58,10 +56,8 @@ const CONFIG = {
|
||||
...environment,
|
||||
...endpoints,
|
||||
...debug,
|
||||
ADMIN_MODULE_URL,
|
||||
COMMUNITY_URL,
|
||||
}
|
||||
|
||||
// Check config version
|
||||
// TODO: use validate and construct error message including description
|
||||
joi.attempt(CONFIG, schema)
|
||||
|
||||
module.exports = CONFIG
|
||||
|
||||
@ -1,25 +1,59 @@
|
||||
const commonSchema = require('../../../config/common.schema')
|
||||
const {
|
||||
browserUrls,
|
||||
APP_VERSION,
|
||||
BUILD_COMMIT,
|
||||
BUILD_COMMIT_SHORT,
|
||||
COMMUNITY_URL,
|
||||
DEBUG,
|
||||
GRAPHQL_URI,
|
||||
NODE_ENV,
|
||||
PRODUCTION,
|
||||
} = require('../../../config/common.schema') // from '../../../config/common.schema'
|
||||
const Joi = require('joi')
|
||||
|
||||
module.exports = commonSchema.keys({
|
||||
module.exports = Joi.object({
|
||||
browserUrls,
|
||||
APP_VERSION,
|
||||
BUILD_COMMIT,
|
||||
BUILD_COMMIT_SHORT,
|
||||
COMMUNITY_URL,
|
||||
DEBUG,
|
||||
GRAPHQL_URI,
|
||||
NODE_ENV,
|
||||
PRODUCTION,
|
||||
|
||||
ADMIN_HOSTING: Joi.string()
|
||||
.valid('nodejs', 'nginx')
|
||||
.description('set to `nodejs` if admin is hosted by vite with a own nodejs instance')
|
||||
.optional(),
|
||||
|
||||
ADMIN_MODULE_URL: Joi.string()
|
||||
.uri({ scheme: ['http', 'https'] })
|
||||
.when('COMMUNITY_URL', {
|
||||
is: Joi.exist(),
|
||||
then: Joi.optional(), // not required if COMMUNITY_URL is provided
|
||||
otherwise: Joi.required(), // required if COMMUNITY_URL is missing
|
||||
})
|
||||
.description("Base Url for reaching admin in browser, only needed if COMMUNITY_URL wasn't set")
|
||||
.optional(), // optional in general, but conditionally required
|
||||
|
||||
ADMIN_MODULE_PROTOCOL: Joi.string()
|
||||
.valid('http', 'https')
|
||||
.when('BROWSER_PROTOCOL', {
|
||||
is: 'https',
|
||||
then: Joi.string().uri({ scheme: 'https' }),
|
||||
otherwise: Joi.string().uri({ scheme: 'http' }),
|
||||
.when('ADMIN_HOSTING', {
|
||||
is: Joi.valid('nodejs'),
|
||||
then: Joi.valid('http').required(),
|
||||
otherwise: Joi.valid('http', 'https').required(),
|
||||
})
|
||||
.description(
|
||||
'Protocol for admin module hosting, has to be the same as for backend api url and admin to prevent mixed block errors'
|
||||
`
|
||||
Protocol for admin module hosting
|
||||
- it has to be the same as for backend api url and frontend to prevent mixed block errors,
|
||||
- if admin is served with nodejs:
|
||||
is have to be http or setup must be updated to include a ssl certificate
|
||||
`,
|
||||
)
|
||||
.default('http')
|
||||
.required(),
|
||||
|
||||
ADMIN_HOSTING: Joi.string()
|
||||
.valid('nodejs')
|
||||
.description('set to `nodejs` if admin is hosted by vite with a own nodejs instance')
|
||||
.optional(),
|
||||
|
||||
ADMIN_MODULE_HOST: Joi.alternatives()
|
||||
.try(
|
||||
Joi.string().valid('localhost').messages({ 'any.invalid': 'Must be localhost' }),
|
||||
@ -28,11 +62,20 @@ module.exports = commonSchema.keys({
|
||||
.messages({ 'string.ip': 'Must be a valid IPv4 address' }),
|
||||
Joi.string().domain().messages({ 'string.domain': 'Must be a valid domain' }),
|
||||
)
|
||||
.when('ADMIN_HOSTING', {
|
||||
is: 'nodejs',
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional(),
|
||||
})
|
||||
.when('COMMUNITY_URL', {
|
||||
is: null,
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional(),
|
||||
})
|
||||
.description(
|
||||
'Host (domain, IPv4, or localhost) for the admin, default is 0.0.0.0 for local hosting during develop',
|
||||
)
|
||||
.default('0.0.0.0')
|
||||
.required(), // required only if community_url isn't set or ADMIN_HOSTING is nodejs
|
||||
.default('0.0.0.0'),
|
||||
|
||||
ADMIN_MODULE_PORT: Joi.number()
|
||||
.integer()
|
||||
@ -40,26 +83,20 @@ module.exports = commonSchema.keys({
|
||||
.max(49151)
|
||||
.description('Port for hosting Admin with Vite as a Node.js instance, default: 8080')
|
||||
.default(8080)
|
||||
.required(), // required only if ADMIN_HOSTING is nodejs
|
||||
.when('ADMIN_HOSTING', {
|
||||
is: 'nodejs',
|
||||
then: Joi.required(),
|
||||
otherwise: Joi.optional(),
|
||||
}),
|
||||
|
||||
WALLET_AUTH_URL: Joi.string()
|
||||
.uri({ scheme: ['http', 'https'] })
|
||||
.when('browser_protocol', {
|
||||
is: 'https',
|
||||
then: Joi.string().uri({ scheme: 'https' }),
|
||||
otherwise: Joi.string().uri({ scheme: 'http' }),
|
||||
})
|
||||
.description('Extern Url from wallet-frontend for forwarding from admin')
|
||||
.default('http://0.0.0.0/authenticate?token=')
|
||||
.required(),
|
||||
|
||||
WALLET_LOGIN_URL: Joi.string()
|
||||
.uri({ scheme: ['http', 'https'] })
|
||||
.when('browser_protocol', {
|
||||
is: 'https',
|
||||
then: Joi.string().uri({ scheme: 'https' }),
|
||||
otherwise: Joi.string().uri({ scheme: 'http' }),
|
||||
})
|
||||
.description('Extern Url from wallet-frontend for forwarding after logout')
|
||||
.default('http://0.0.0.0/login')
|
||||
.required(),
|
||||
@ -68,4 +105,4 @@ module.exports = commonSchema.keys({
|
||||
.description('Flag for disable authorization during development')
|
||||
.default(false)
|
||||
.optional(), // true is only allowed in not-production setup
|
||||
})
|
||||
})
|
||||
|
||||
@ -6,6 +6,7 @@ import Components from 'unplugin-vue-components/vite'
|
||||
import IconsResolve from 'unplugin-icons/resolver'
|
||||
import { BootstrapVueNextResolver } from 'bootstrap-vue-next'
|
||||
import EnvironmentPlugin from 'vite-plugin-environment'
|
||||
import schema from './src/config/schema'
|
||||
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
@ -15,55 +16,93 @@ const CONFIG = require('./src/config')
|
||||
|
||||
const path = require('path')
|
||||
|
||||
export default defineConfig({
|
||||
base: '/admin/',
|
||||
server: {
|
||||
host: CONFIG.ADMIN_MODULE_HOST, // '0.0.0.0',
|
||||
port: CONFIG.ADMIN_MODULE_PORT, // 8080,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
assets: path.join(__dirname, 'src/assets'),
|
||||
},
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||
},
|
||||
export default defineConfig(({ command }) => {
|
||||
if (command === 'serve') {
|
||||
CONFIG.ADMIN_HOSTING = 'nodejs'
|
||||
} else {
|
||||
CONFIG.ADMIN_HOSTING = 'nginx'
|
||||
}
|
||||
// Check config
|
||||
const configDataForValidation = {
|
||||
...CONFIG,
|
||||
// make sure that all urls used in browser have the same protocol to prevent mixed content errors
|
||||
browserUrls: [
|
||||
CONFIG.WALLET_AUTH_URL,
|
||||
CONFIG.COMMUNITY_URL,
|
||||
CONFIG.WALLET_LOGIN_URL,
|
||||
CONFIG.GRAPHQL_URI,
|
||||
CONFIG.ADMIN_MODULE_URL,
|
||||
],
|
||||
}
|
||||
|
||||
plugins: [
|
||||
vue({
|
||||
template: {
|
||||
compilerOptions: {
|
||||
compatConfig: {
|
||||
MODE: 2,
|
||||
const { error } = schema.validate(configDataForValidation, { stack: true })
|
||||
const schemaJson = schema.describe()
|
||||
if (error) {
|
||||
error.details.forEach((err) => {
|
||||
const key = err.context.key
|
||||
const value = err.context.value
|
||||
const description = schemaJson.keys[key]
|
||||
? schema.describe().keys[key].flags.description
|
||||
: 'No description available'
|
||||
if (configDataForValidation[key] === undefined) {
|
||||
throw new Error(`Environment Variable '${key}' is missing. ${description}`)
|
||||
} else {
|
||||
throw new Error(
|
||||
`Error on Environment Variable ${key} with value = ${value}: ${err.message}. ${description}`,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
base: '/admin/',
|
||||
server: {
|
||||
host: CONFIG.ADMIN_MODULE_HOST, // '0.0.0.0',
|
||||
port: CONFIG.ADMIN_MODULE_PORT, // 8080,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
assets: path.join(__dirname, 'src/assets'),
|
||||
},
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||
},
|
||||
|
||||
plugins: [
|
||||
vue({
|
||||
template: {
|
||||
compilerOptions: {
|
||||
compatConfig: {
|
||||
MODE: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Components({
|
||||
resolvers: [IconsResolve(), BootstrapVueNextResolver()],
|
||||
dts: true,
|
||||
}),
|
||||
Icons({
|
||||
compiler: 'vue3',
|
||||
}),
|
||||
EnvironmentPlugin({
|
||||
BUILD_COMMIT: null,
|
||||
PORT: CONFIG.ADMIN_MODULE_PORT, // null,
|
||||
COMMUNITY_HOST: CONFIG.ADMIN_MODULE_HOST, // null,
|
||||
URL_PROTOCOL: CONFIG.ADMIN_MODULE_PROTOCOL, // null,
|
||||
WALLET_URL: CONFIG.WALLET_AUTH_URL, // null,
|
||||
GRAPHQL_URL: CONFIG.GRAPHQL_URI, // null,
|
||||
GRAPHQL_PATH: process.env.GRAPHQL_PATH ?? '/graphql', // null,
|
||||
WALLET_AUTH_PATH: CONFIG.WALLET_AUTH_URL, // null,
|
||||
WALLET_LOGIN_PATH: CONFIG.WALLET_LOGIN_URL, // null,
|
||||
DEBUG_DISABLE_AUTH: CONFIG.DEBUG_DISABLE_AUTH, // null,
|
||||
// CONFIG_VERSION: CONFIG.CONFIG_VERSION, // null,
|
||||
}),
|
||||
commonjs(),
|
||||
],
|
||||
build: {
|
||||
outDir: path.resolve(__dirname, './build'),
|
||||
chunkSizeWarningLimit: 1600,
|
||||
},
|
||||
publicDir: '/admin',
|
||||
}),
|
||||
Components({
|
||||
resolvers: [IconsResolve(), BootstrapVueNextResolver()],
|
||||
dts: true,
|
||||
}),
|
||||
Icons({
|
||||
compiler: 'vue3',
|
||||
}),
|
||||
EnvironmentPlugin({
|
||||
BUILD_COMMIT: null,
|
||||
PORT: CONFIG.ADMIN_MODULE_PORT, // null,
|
||||
COMMUNITY_HOST: CONFIG.ADMIN_MODULE_HOST, // null,
|
||||
URL_PROTOCOL: CONFIG.ADMIN_MODULE_PROTOCOL, // null,
|
||||
WALLET_URL: CONFIG.WALLET_AUTH_URL, // null,
|
||||
GRAPHQL_URL: CONFIG.GRAPHQL_URI, // null,
|
||||
GRAPHQL_PATH: process.env.GRAPHQL_PATH ?? '/graphql', // null,
|
||||
WALLET_AUTH_PATH: CONFIG.WALLET_AUTH_URL, // null,
|
||||
WALLET_LOGIN_PATH: CONFIG.WALLET_LOGIN_URL, // null,
|
||||
DEBUG_DISABLE_AUTH: CONFIG.DEBUG_DISABLE_AUTH, // null,
|
||||
}),
|
||||
commonjs(),
|
||||
],
|
||||
build: {
|
||||
outDir: path.resolve(__dirname, './build'),
|
||||
chunkSizeWarningLimit: 1600,
|
||||
},
|
||||
publicDir: '/admin',
|
||||
}
|
||||
})
|
||||
|
||||
@ -1072,6 +1072,18 @@
|
||||
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
|
||||
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==
|
||||
|
||||
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
|
||||
version "9.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
|
||||
integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==
|
||||
|
||||
"@hapi/topo@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
|
||||
integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@humanwhocodes/config-array@^0.11.14":
|
||||
version "0.11.14"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
|
||||
@ -1471,6 +1483,23 @@
|
||||
resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
|
||||
integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==
|
||||
|
||||
"@sideway/address@^4.1.5":
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
|
||||
integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@sideway/formula@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
|
||||
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
|
||||
|
||||
"@sideway/pinpoint@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
|
||||
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
|
||||
|
||||
"@types/estree@1.0.6", "@types/estree@^1.0.0":
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
|
||||
@ -4712,6 +4741,17 @@ jest-canvas-mock@~2.5.2:
|
||||
cssfontparser "^1.2.1"
|
||||
moo-color "^1.0.2"
|
||||
|
||||
joi@^17.13.3:
|
||||
version "17.13.3"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.3.tgz#0f5cc1169c999b30d344366d384b12d92558bcec"
|
||||
integrity sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.3.0"
|
||||
"@hapi/topo" "^5.1.0"
|
||||
"@sideway/address" "^4.1.5"
|
||||
"@sideway/formula" "^3.0.1"
|
||||
"@sideway/pinpoint" "^2.0.0"
|
||||
|
||||
js-beautify@^1.14.9:
|
||||
version "1.15.1"
|
||||
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.15.1.tgz#4695afb508c324e1084ee0b952a102023fc65b64"
|
||||
|
||||
@ -27,7 +27,7 @@ services:
|
||||
build:
|
||||
target: development
|
||||
environment:
|
||||
- NODE_ENV="development"
|
||||
- NODE_ENV=development
|
||||
# - DEBUG=true
|
||||
volumes:
|
||||
# This makes sure the docker container has its own node modules.
|
||||
|
||||
@ -54,7 +54,9 @@ services:
|
||||
# - BUILD_DATE="1970-01-01T00:00:00.00Z"
|
||||
# - BUILD_VERSION="0.0.0.0"
|
||||
# - BUILD_COMMIT="0000000"
|
||||
- NODE_ENV="production"
|
||||
- NODE_ENV=production
|
||||
volumes:
|
||||
- ./config:/config
|
||||
# env_file:
|
||||
# - ./.env
|
||||
# - ./admin/.env
|
||||
|
||||
@ -18,8 +18,7 @@ const version = {
|
||||
BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT ?? '0000000').slice(0, 7),
|
||||
}
|
||||
|
||||
// in case of hosting the frontend module with a nginx
|
||||
let FRONTEND_MODULE_URL = version.FRONTEND_MODULE_PROTOCOL + '://' + version.FRONTEND_MODULE_HOST
|
||||
let FRONTEND_MODULE_URL
|
||||
|
||||
// in case of hosting the frontend module with a nodejs-instance
|
||||
if (process.env.FRONTEND_HOSTING === 'nodejs') {
|
||||
@ -29,6 +28,9 @@ if (process.env.FRONTEND_HOSTING === 'nodejs') {
|
||||
version.FRONTEND_MODULE_HOST +
|
||||
':' +
|
||||
version.FRONTEND_MODULE_PORT
|
||||
} else {
|
||||
// in case of hosting the frontend module with a nginx
|
||||
FRONTEND_MODULE_URL = version.FRONTEND_MODULE_PROTOCOL + '://' + version.FRONTEND_MODULE_HOST
|
||||
}
|
||||
|
||||
// const FRONTEND_MODULE_URI = version.FRONTEND_MODULE_PROTOCOL + '://' + version.FRONTEND_MODULE_HOST // +
|
||||
|
||||
@ -49,6 +49,11 @@ module.exports = Joi.object({
|
||||
.description('URL for Register a new Account in frontend.')
|
||||
.required(),
|
||||
|
||||
FRONTEND_HOSTING: Joi.string()
|
||||
.valid('nodejs', 'nginx')
|
||||
.description('set to `nodejs` if frontend is hosted by vite with a own nodejs instance')
|
||||
.optional(),
|
||||
|
||||
FRONTEND_MODULE_URL: Joi.string()
|
||||
.uri({ scheme: ['http', 'https'] })
|
||||
.when('COMMUNITY_URL', {
|
||||
@ -78,11 +83,6 @@ module.exports = Joi.object({
|
||||
.default('http')
|
||||
.required(),
|
||||
|
||||
FRONTEND_HOSTING: Joi.string()
|
||||
.valid('nodejs', 'nginx')
|
||||
.description('set to `nodejs` if frontend is hosted by vite with a own nodejs instance')
|
||||
.optional(),
|
||||
|
||||
FRONTEND_MODULE_HOST: Joi.alternatives()
|
||||
.try(
|
||||
Joi.string().valid('localhost').messages({ 'any.invalid': 'Must be localhost' }),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user