more rules for frontend, all rules

This commit is contained in:
einhornimmond 2025-01-18 10:57:52 +01:00
parent 5ca51a9941
commit 833890e0ba
7 changed files with 195 additions and 76 deletions

View File

@ -1,13 +1,13 @@
const Joi = require('joi')
module.exports = Joi.object({
module.exports = {
BROWSER_PROTOCOL: Joi.string()
.valid('http', 'https')
.description(
'Protocol for all URLs in the browser, must be either http or https to prevent mixed content issues.',
)
.default('http')
.require(),
.required(),
COMMUNITY_URL: Joi.string()
.uri({ scheme: ['http', 'https'] })
@ -18,7 +18,7 @@ module.exports = Joi.object({
})
.description('The base URL of the community, should have the same scheme like frontend, admin and backend api to prevent mixed contend issues.')
.default('http://0.0.0.0')
.require(),
.required(),
GRAPHQL_URI: Joi.string()
.uri({ scheme: ['http', 'https'] })
@ -35,59 +35,61 @@ module.exports = Joi.object({
`,
)
.default('http://0.0.0.0/graphql')
.require(),
.required(),
COMMUNITY_NAME: Joi.string()
.min(3)
.max(100)
.description('The name of the community')
.default('Gradido Entwicklung')
.require(),
.required(),
COMMUNITY_DESCRIPTION: Joi.string()
.min(10)
.max(300)
.description('A short description of the community')
.default('Die lokale Entwicklungsumgebung von Gradido.')
.require(),
.required(),
COMMUNITY_SUPPORT_MAIL: Joi.string()
.email()
.description('The support email address for the community will be used in frontend and E-Mails')
.default('support@supportmail.com')
.require(),
.required(),
COMMUNITY_LOCATION: Joi.string()
.pattern(/^[-+]?[0-9]{1,2}(\.[0-9]+)?,\s?[-+]?[0-9]{1,3}(\.[0-9]+)?$/)
// TODO: ask chatgpt for the correct way
.when('GMS_ACTIVE', {
is: true,
then: Joi.string().require(),
then: Joi.string().required(),
otherwise: Joi.string().optional()
})
.description('Geographical location of the community in "latitude, longitude" format')
.default('49.280377, 9.690151')
.require(),
.default('49.280377, 9.690151'),
GRAPHIQL: Joi.boolean()
.description('Flag for enabling graphql playground for debugging.')
.default(false)
.require(), // todo: only allow true in development mode
GRAPHIQL: Joi.boolean()
.description('Flag for enabling GraphQL playground for debugging.')
.default(false)
.when('NODE_ENV', {
is: 'development',
then: Joi.boolean().valid(true).required(), // only allow true in development mode
otherwise: Joi.boolean().valid(false).required() // false in any other mode
}),
GMS_ACTIVE: Joi.boolean()
.description('Flag to indicate if the GMS (Geographic Member Search) service is used.')
.default(false)
.require(),
.required(),
HUMHUB_ACTIVE: Joi.boolean()
.description('Flag to indicate if the HumHub based Community Server is used.')
.default(false)
.require(),
.required(),
LOG_LEVEL: Joi.string()
.valid(['INFO', 'ERROR']) // TODO: lookup values
.valid('all', 'mark', 'trace', 'debug', 'info', 'warn', 'error', 'fatal', 'off')
.description('set log level')
.default('INFO')
.default('info')
.required(),
DB_HOST: Joi.string()
@ -96,27 +98,80 @@ module.exports = Joi.object({
.default('localhost')
.required(),
DB_PORT: Joi.number()
.integer()
.min(1024)
.max(49151)
.description('database port, default: 3306')
.default(3306)
.required(),
DB_PORT: Joi.number()
.integer()
.min(1024)
.max(49151)
.description('database port, default: 3306')
.default(3306)
.required(),
// TODO: check allowed users for mariadb
DB_USER: Joi.string()
.description('database user name like root (default) or gradido')
.default('root')
.required(),
DB_USER: Joi.string()
.pattern(/^[A-Za-z0-9]([A-Za-z0-9-_\.]*[A-Za-z0-9])?$/) // Validates MariaDB username rules
.min(1) // Minimum length 1
.max(16) // Maximum length 16
.message(
'Valid database username (letters, numbers, hyphens, underscores, dots allowed; no spaces, must not start or end with hyphen, dot, or underscore)'
)
.description('database username for mariadb')
.default('root')
.required(),
DB_PASSWORD: Joi.string()
.description('database password')
.default('')
.required(),
DB_PASSWORD: Joi.string()
.when(Joi.ref('NODE_ENV'), {
is: 'development',
then: Joi.string().allow(''),
otherwise: Joi.string()
.min(8)
.max(32)
.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>]).+$/)
.message(
'Password must be between 8 and 32 characters long, and contain at least one uppercase letter, one lowercase letter, one number, and one special character (e.g., !@#$%^&*).'
)
})
.description(
'Password for the database user. In development mode, an empty password is allowed. In other environments, a complex password is required.'
)
.default('')
.required(),
DB_DATABASE: Joi.string()
.description('database name like gradido_community')
.default('gradido_community')
.required(),
})
DB_DATABASE: Joi.string()
.pattern(/^[a-zA-Z][a-zA-Z0-9_-]{1,63}$/)
.description('Database name like gradido_community (must start with a letter, and can only contain letters, numbers, underscores, or dashes)')
.default('gradido_community')
.required(),
APP_VERSION: Joi.string()
.pattern(/^\d+\.\d+\.\d+$/)
.message('Version must be in the format "major.minor.patch" (e.g., "2.4.1")')
.description('App Version from package.json, alle modules share one version')
.required(),
BUILD_COMMIT: Joi.string()
.pattern(/^[0-9a-f]{40}$/)
.message('The commit hash must be a 40-character hexadecimal string.')
.description('The full git commit hash.')
.optional(),
BUILD_COMMIT_SHORT: Joi.string()
.pattern(/^[0-9a-f]{7}$/)
.message('The first 7 hexadecimal character from git commit hash.')
.description('A short version from the git commit hash.')
.required(),
NODE_ENV: Joi.string()
.valid('production', 'development')
.default('development')
.description('Specifies the environment in which the application is running, "production" or "development".'),
DEBUG: Joi.boolean()
.description('Indicates whether the application is in debugging mode. Set to true when NODE_ENV is not "production".')
.default(false)
.required(),
PRODUCTION: Joi.boolean()
.default(false)
.description('Indicates whether the application is running in production mode. Set to true when NODE_ENV is "production".')
.required(),
}

View File

@ -117,7 +117,6 @@ NGINX_SSL_INCLUDE=/etc/letsencrypt/options-ssl-nginx.conf
NGINX_REWRITE_LEGACY_URLS=false
# LEGACY
DEFAULT_PUBLISHER_ID=2896
WEBHOOK_ELOPAGE_SECRET=secret
# GMS

View File

@ -1,6 +1,3 @@
# Environment
DEFAULT_PUBLISHER_ID=2896
# Endpoints
GRAPHQL_PATH=/graphql
ADMIN_AUTH_PATH=/admin/authenticate?token={token}

View File

@ -1,8 +1,5 @@
CONFIG_VERSION=$FRONTEND_CONFIG_VERSION
# Environment
DEFAULT_PUBLISHER_ID=$DEFAULT_PUBLISHER_ID
# Endpoints
GRAPHQL_PATH=$GRAPHQL_PATH
ADMIN_AUTH_PATH=$ADMIN_AUTH_PATH

View File

@ -4,18 +4,19 @@
// Load Package Details for some default values
const pkg = require('../../package')
const schema = require('./schema')
const joi = require('joi')
// const joi = require('joi')
const constants = {
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
}
const version = {
BROWSER_PROTOCOL: process.env.BROWSER_PROTOCOL ?? 'http',
FRONTEND_MODULE_PROTOCOL: process.env.FRONTEND_MODULE_PROTOCOL ?? 'http',
FRONTEND_MODULE_HOST: process.env.FRONTEND_MODULE_HOST ?? '0.0.0.0',
FRONTEND_MODULE_PORT: process.env.FRONTEND_MODULE_PORT ?? '3000',
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),
}
@ -47,7 +48,6 @@ const environment = {
NODE_ENV: process.env.NODE_ENV,
DEBUG: process.env.NODE_ENV !== 'production' ?? false,
PRODUCTION: process.env.NODE_ENV === 'production' ?? false,
DEFAULT_PUBLISHER_ID: process.env.DEFAULT_PUBLISHER_ID ?? 2896,
}
// const COMMUNITY_HOST = process.env.COMMUNITY_HOST ?? 'localhost'
@ -91,7 +91,6 @@ const meta = {
}
const CONFIG = {
...constants,
...version,
...features,
...environment,
@ -100,8 +99,24 @@ const CONFIG = {
...meta,
}
// Check config
// Check config
// TODO: use validate and construct error message including description
joi.attempt(CONFIG, schema)
// joi.attempt(CONFIG, schema)
module.exports = CONFIG
const { error } = schema.validate(CONFIG, { stack: true, debug: true })
const schemaJson = schema.describe()
if (error) {
error.details.forEach((err) => {
const key = err.context.key
const description = schemaJson.keys[key]
? schema.describe().keys[key].flags.description
: 'No description available'
if (CONFIG[key] === undefined) {
throw new Error(`Environment Variable '${key}' is missing. ${description}`)
} else {
throw new Error(`Error on Environment Variable '${key}': ${err.message}. ${description}`)
}
})
}
module.exports = { ...CONFIG, ...constants }

View File

@ -1,12 +1,67 @@
const commonSchema = require('../../../config/common.schema')
import {
APP_VERSION,
BROWSER_PROTOCOL,
BUILD_COMMIT,
BUILD_COMMIT_SHORT,
COMMUNITY_DESCRIPTION,
COMMUNITY_NAME,
COMMUNITY_SUPPORT_MAIL,
COMMUNITY_LOCATION,
COMMUNITY_URL,
DEBUG,
GMS_ACTIVE,
GRAPHQL_URI,
HUMHUB_ACTIVE,
NODE_ENV,
PRODUCTION,
} from '../../../config/common.schema'
const Joi = require('joi')
module.exports = commonSchema.keys({
// console.log(commonSchema)
module.exports = Joi.object({
APP_VERSION,
BROWSER_PROTOCOL,
BUILD_COMMIT,
BUILD_COMMIT_SHORT,
COMMUNITY_DESCRIPTION,
COMMUNITY_NAME,
COMMUNITY_SUPPORT_MAIL,
COMMUNITY_LOCATION,
COMMUNITY_URL,
DEBUG,
GMS_ACTIVE,
GRAPHQL_URI,
HUMHUB_ACTIVE,
NODE_ENV,
PRODUCTION,
ADMIN_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 for admin-frontend')
.default('http://0.0.0.0/admin/authenticate?token=')
.required(),
COMMUNITY_REGISTER_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('URL for Register a new Account in frontend.')
.required(),
FRONTEND_MODULE_PROTOCOL: Joi.string()
.valid('http', 'https')
.when('BROWSER_PROTOCOL', {
is: 'https',
then: Joi.string().uri({ scheme: 'https' }),
then: Joi.string().uri({ scheme: 'https' }),
otherwise: Joi.string().uri({ scheme: 'http' }),
})
.description(
@ -14,7 +69,7 @@ module.exports = commonSchema.keys({
)
.default('http')
.required(),
FRONTEND_HOSTING: Joi.string()
.valid('nodejs')
.description('set to `nodejs` if frontend is hosted by vite with a own nodejs instance')
@ -26,13 +81,22 @@ module.exports = commonSchema.keys({
Joi.string()
.ip({ version: ['ipv4'] })
.messages({ 'string.ip': 'Must be a valid IPv4 address' }),
Joi.string().domain().messages({ 'string.domain': 'Must be a valid domain' }),
Joi.string().domain().messages({ 'string.domain': 'Must be a valid domain' })
)
.when('FRONTEND_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 frontend, default is 0.0.0.0 for local hosting during develop',
'Host (domain, IPv4, or localhost) for the frontend, default is 0.0.0.0 for local hosting during development.',
)
.default('0.0.0.0')
.required(), // required only if community_url isn't set or FRONTEND_HOSTING is nodejs
.default('0.0.0.0'),
FRONTEND_MODULE_PORT: Joi.number()
.integer()
@ -40,17 +104,11 @@ module.exports = commonSchema.keys({
.max(49151)
.description('Port for hosting Frontend with Vite as a Node.js instance, default: 3000')
.default(3000)
.required(), // required only if FRONTEND_HOSTING is nodejs
ADMIN_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 for admin-frontend')
.default('http://0.0.0.0/admin/authenticate?token='),
.when('FRONTEND_HOSTING', {
is: 'nodejs',
then: Joi.required(),
otherwise: Joi.optional(),
}),
META_URL: Joi.string()
.uri({ scheme: ['http', 'https'] })

View File

@ -15,8 +15,6 @@ dotenv.config() // load env vars from .env
const CONFIG = require('./src/config')
console.log(CONFIG)
// https://vitejs.dev/config/
export default defineConfig({
server: {
@ -79,7 +77,7 @@ export default defineConfig({
URL_PROTOCOL: null,
COMMUNITY_URL: null,
GRAPHQL_PATH: null,
EXTERNAL_BACKEND_URL: CONFIG.EXTERNAL_BACKEND_URL, // null,
GRAPHQL_URI: CONFIG.GRAPHQL_URI, // null,
ADMIN_AUTH_PATH: CONFIG.ADMIN_AUTH_PATH ?? null, // it is the only env without exported default
ADMIN_AUTH_URL: CONFIG.ADMIN_AUTH_URL, // null,
COMMUNITY_NAME: null,