@getanthill/datastore
Version:
Event-Sourced Datastore
291 lines • 13.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.envConfig = void 0;
const constants_1 = require("../constants");
const models = {
models: [],
};
/**
* Datastore environment variable prefix name
*/
const DATASTORE_ENV_PREFIX = process.env.DATASTORE_ENV_PREFIX || constants_1.DEFAULT_ENV_PREFIX;
/**
* Database name
*/
const DB_NAME = process.env.DATASTORE_DB_NAME || constants_1.DEFAULT_DATABASE_NAME;
function getEnv(name, defaultValue) {
return (process.env[`${DATASTORE_ENV_PREFIX}_${name}`] ||
process.env[name] ||
defaultValue);
}
function mapTokens(name, level) {
return getEnv(name, '')
.split(',')
.map((t) => t.trim())
.filter((t) => !!t)
.map((token) => ({
id: token.slice(0, 10),
level,
token,
}));
}
const configPath = getEnv('ENV_FILE', '');
function getAMQPUrl() {
const url = getEnv('AMQP_URL', 'amqp://guest:guest@localhost:5672');
/* istanbul ignore next */
if (url[0] === '[') {
return JSON.parse(url);
}
return url;
}
/* istanbul ignore next */
exports.envConfig = {
exitOnUnavailabilityAfterMilliseconds: parseInt(getEnv('EXIT_ON_UNVAILABILITY_AFTER_MILLISECONDS', '10000')),
port: getEnv('PORT', 3001),
mode: getEnv('NODE_ENV', 'production'),
exitTimeout: parseInt(getEnv('EXIT_TIMEOUT', '1000'), 10),
security: {
tokens: [
...JSON.parse(getEnv('TOKENS', '[]')),
...mapTokens('READ_ACCESS_TOKENS', 'read'),
...mapTokens('DECRYPT_ACCESS_TOKENS', 'decrypt'),
...mapTokens('WRITE_ACCESS_TOKENS', 'write'),
...mapTokens('ADMIN_ACCESS_TOKENS', 'admin'),
],
apiSecret: getEnv('API_DOC_SECRET', 'api-docs'),
encryptionKeys: JSON.parse(getEnv('SECURITY_ENCRYPTION_KEYS', '{}')),
activeNumberEncryptionKeys: parseInt(getEnv('SECURITY_ACTIVE_NUMBER_ENCRYPTION_KEYS', '1'), 10),
accessTokenByCookie: getEnv('SECURITY_ACCESS_TOKEN_BY_COOKIE') === 'true',
},
features: {
properties: {
is_readonly: getEnv('FEATURE_PROPERTY_IS_READONLY', 'is_readonly'),
is_archived: getEnv('FEATURE_PROPERTY_IS_ARCHIVED', 'is_archived'),
is_deleted: getEnv('FEATURE_PROPERTY_IS_DELETED', 'is_deleted'),
},
cookies: {
options: {
httpOnly: getEnv('FEATURE_COOKIES_HTTP_ONLY') !== 'false', // true by default
domain: getEnv('FEATURE_COOKIES_DOMAIN'),
secure: getEnv('FEATURE_COOKIES_SECURE') !== 'false', // true by default
sameSite: getEnv('FEATURE_COOKIES_SAME_SITE', 'none'),
maxAge: parseInt(getEnv('FEATURE_COOKIES_MAX_AGE', '172800'), 10),
},
maxAges: JSON.parse(getEnv('FEATURE_COOKIES_MAX_AGES', '{}')),
},
cors: {
isEnabled: getEnv('FEATURE_CORS_ENABLED') !== 'false',
allowCredentials: getEnv('FEATURE_CORS_ALLOW_CREDENTIALS'),
allowHeaders: getEnv('FEATURE_CORS_ALLOW_HEADERS', ''),
allowMethods: getEnv('FEATURE_CORS_ALLOW_METHODS'),
allowOrigin: getEnv('FEATURE_CORS_ALLOW_ORIGIN'),
exposeHeaders: getEnv('FEATURE_CORS_EXPOSE_HEADERS'),
requestHeaders: getEnv('FEATURE_CORS_REQUEST_HEADERS'),
requestMethod: getEnv('FEATURE_CORS_REQUEST_METHOD'),
},
cache: {
isEnabled: getEnv('FEATURE_CACHE_ENABLED') === 'true',
scope: getEnv('FEATURE_CACHE_SCOPE', 'ds'),
},
api: {
timeout: {
models: parseInt(getEnv('FEATURE_API_TIMEOUT_MODELS_IN_MILLISECONDS', '5000'), 10),
aggregate: parseInt(getEnv('FEATURE_API_TIMEOUT_AGGREGATE_IN_MILLISECONDS', '30000'), 10),
},
/**
* Are examples models templates exposed in API or not?
*/
templates: getEnv('FEATURE_API_TEMPLATES') === 'true',
/**
* Are administration routes enabled or not?
*/
admin: getEnv('FEATURE_API_ADMIN') === 'true',
aggregate: getEnv('FEATURE_API_AGGREGATE') === 'true',
graphql: getEnv('FEATURE_API_GRAPHQL') === 'true', // Disabled by default
openAPI: {
isEnabled: getEnv('FEATURE_API_OPEN_API_ENABLED') !== 'false',
warnOnInvalidSpecificationOnly: getEnv('FEATURE_API_OPENAPI_WARN_ON_INVALID_SPECIFICATION_ONLY') ===
'true',
},
json: {
limit: getEnv('FEATURE_API_JSON_LIMIT', '50mb'),
},
updateSpecOnModelsChange: getEnv('FEATURE_API_UPDATE_SPEC_ON_MODELS_CHANGE') === 'true',
sseKeepAliveTimeout: parseInt(getEnv('FEATURE_API_SERVER_SENT_EVENTS_KEEP_ALIVE_TIMEOUT_IN_MILLISECONDS', '60000'), 10),
checkProcessingAuthorization: getEnv('FEATURE_API_CHECK_PROCESSING_AUTHORIZATION') === 'true',
stream: {
reconnectDelayOnError: parseInt(getEnv('FEATURE_API_STREAM_RECONNECT_DELAY_ON_ERROR', '1'), 10),
maxWaitOnReconnectInMilliseconds: parseInt(getEnv('FEATURE_API_STREAM_MAX_WAIT_ON_RECONNECT_IN_MILLISECONDS', '30000'), 10),
},
},
events: {
throwOnInvalidEvent: getEnv('FEATURE_EVENTS_THROW_ON_INVALID_EVENT', 'true') !== 'false',
},
fhe: {
isEnabled: getEnv('FEATURE_FHE_IS_ENABLED', 'false') === 'true',
},
mqtt: {
isEnabled: getEnv('FEATURE_MQTT_IS_ENABLED', 'false') === 'true',
},
amqp: {
isEnabled: getEnv('FEATURE_AMQP_IS_ENABLED', 'false') === 'true',
},
mongodb: {
maxTimeMS: parseInt(getEnv('FEATURE_MONGODB_MAX_TIME_MS', '5000'), 10),
explain: getEnv('FEATURE_MONGODB_EXPLAIN', 'false') === 'true',
slowQueryThresholdInMilliseconds: parseInt(getEnv('FEATURE_MONGODB_SLOW_QUERY_THRESHOLD_IN_MILLISECONDS', '1000'), 10),
},
mustWaitStatePersistence: getEnv('FEATURE_MUST_STATE_PERSISTENCE', 'true') !== 'false',
retryDuration: parseInt(getEnv('FEATURE_RETRY_DURATION_IN_MILLISECONDS', getEnv('DEFAULT_RETRY_DURATION_IN_MILLISECONDS', '0')), 10),
deleteAfterArchiveDurationInSeconds: parseInt(getEnv('FEATURE_DELETE_AFTER_ARCHIVE_DURATION_IN_SECONDS', '1209600'), // 14 days per default
10),
initInternalModels: getEnv('FEATURE_INIT_INTERNAL_MODELS', 'false') === 'true',
loadOnlyModels: JSON.parse(getEnv('FEATURE_LOAD_ONLY_MODELS', 'null')),
},
authz: {
isEnabled: getEnv('AUTHORIZATION_ENABLED') === 'true',
noPolicyVerb: getEnv('AUTHORIZATION_NO_POLICY_VERB', 'allow'),
skipModels: getEnv('AUTHORIZATION_SKIP_MODELS', '')
.split(',')
.map((t) => t.trim())
.filter((t) => !!t),
onlyModels: getEnv('AUTHORIZATION_ONLY_MODELS', '')
.split(',')
.map((t) => t.trim())
.filter((t) => !!t),
},
graphql: {
http: JSON.parse(getEnv('GRAPHQL_HTTP_CONFIG', '{}')),
openApiToGraphQL: JSON.parse(getEnv('GRAPHQL_OPENAPI_TO_GRAPHQL_CONFIG', '{}')),
},
fhe: {
scheme: getEnv('FHE_SCHEME', 'bgv'),
polyModulusDegree: parseInt(getEnv('FHE_POLY_MODULUS_DEGREE', '4096'), 10),
bitSize: parseInt(getEnv('FHE_BIT_SIZE', '20'), 10),
bitSizes: JSON.parse(getEnv('FHE_BIT_SIZES', '[36, 36, 37]')),
precision: parseInt(getEnv('FHE_PRECISION', '2'), 10),
},
mqtt: {
namespace: getEnv('MQTT_NAMESPACE', ''),
url: getEnv('MQTT_URL', 'mqtt://localhost:1883'),
options: JSON.parse(getEnv('MQTT_OPTIONS', '{}')),
group: getEnv('FEATURE_MQTT_GROUP', 'datastore'),
},
amqp: {
namespace: getEnv('AMQP_NAMESPACE', ''),
url: getAMQPUrl(),
options: JSON.parse(getEnv('AMQP_OPTIONS', '{}')),
failover: {
reconnectionTimeoutInMilliseconds: parseInt(getEnv('AMQP_FAILOVER_RECONNECTION_TIMEOUT_IN_MILLISECONDS', '1000'), 10),
},
channel: {
prefetch: parseInt(getEnv('AMQP_CHANNEL_PREFETCH', '100'), 10),
},
exchange: {
consumer: {
name: getEnv('AMQP_EXCHANGE_CONSUMER_NAME', 'datastore'),
type: getEnv('AMQP_EXCHANGE_CONSUMER_TYPE', 'topic'),
options: JSON.parse(getEnv('AMQP_EXCHANGE_CONSUMER_OPTIONS', '{}')),
},
producer: {
name: getEnv('AMQP_EXCHANGE_PRODUCER_NAME', 'datastore'),
type: getEnv('AMQP_EXCHANGE_PRODUCER_TYPE', 'topic'),
options: JSON.parse(getEnv('AMQP_EXCHANGE_PRODUCER_OPTIONS', '{}')),
},
},
queue: {
consumer: {
name: getEnv('AMQP_QUEUE_CONSUMER_NAME', 'datastore'),
options: JSON.parse(getEnv('AMQP_QUEUE_CONSUMER_OPTIONS', '{}')),
},
errors: {
isEnabled: getEnv('AMQP_QUEUE_ERRORS_IS_ENABLED', 'false') === 'true',
name: getEnv('AMQP_QUEUE_ERRORS_NAME', 'errors'),
options: JSON.parse(getEnv('AMQP_QUEUE_ERRORS_OPTIONS', '{}')),
},
},
headers: JSON.parse(getEnv('AMQP_HEADERS', '{}')),
},
mongodb: {
databases: [
{
/**
* @see
*
* https://mongodb.github.io/node-mongodb-native/4.5/interfaces/ConnectionOptions.html
* https://mongodb.github.io/node-mongodb-native/4.3/interfaces/MongoClientOptions.html
*
* @note
*
* Replicaset configuration to test the stream API:
* > docker run -d --name mongo4.4 -p 27017:27017 mongo:4.4 mongod --replSet "rs"
* > docker exec -it mongo4.4 mongo
* >
* > db = (new Mongo('localhost:27017')).getDB('test')
* > config={"_id":"rs","members":[{"_id":0,"host":"localhost:27017"}]}
* > rs.initiate(config)
*/
name: `${DB_NAME}_write`,
url: getEnv('MONGO_WRITE_URL') ||
getEnv('MONGO_URL') ||
'mongodb://localhost:27017/datastore',
options: {
connectTimeoutMS: parseInt(getEnv('MONGO_CONNECT_TIMEOUT_IN_MILLISECONDS', '10000'), 10),
heartbeatFrequencyMS: parseInt(getEnv('MONGO_HEARTBEAT_FREQUENCY_IN_MILLISECONDS', '1000'), 10),
minHeartbeatFrequencyMS: parseInt(getEnv('MONGO_MIN_HEARTBEAT_FREQUENCY_IN_MILLISECONDS', '1000'), 10),
serverApi: getEnv('MONGO_SERVER_API'),
ssl: getEnv('MONGO_SSL') === 'true',
tlsAllowInvalidCertificates: getEnv('MONGO_SSL_VALIDATE') !== 'false',
...(getEnv('MONGO_USERNAME') || getEnv('MONGO_PASSWORD')
? {
auth: {
username: getEnv('MONGO_USERNAME'),
password: getEnv('MONGO_PASSWORD'),
},
}
: {}),
...JSON.parse(getEnv('MONGO_WRITE_OPTIONS', '{}')),
},
},
{
name: `${DB_NAME}_read`,
url: getEnv('MONGO_READ_URL') ||
getEnv('MONGO_URL') ||
'mongodb://localhost:27017/datastore',
options: {
connectTimeoutMS: parseInt(getEnv('MONGO_CONNECT_TIMEOUT_IN_MILLISECONDS', '10000'), 10),
heartbeatFrequencyMS: parseInt(getEnv('MONGO_HEARTBEAT_FREQUENCY_IN_MILLISECONDS', '1000'), 10),
minHeartbeatFrequencyMS: parseInt(getEnv('MONGO_MIN_HEARTBEAT_FREQUENCY_IN_MILLISECONDS', '1000'), 10),
serverApi: getEnv('MONGO_SERVER_API'),
ssl: getEnv('MONGO_SSL') === 'true',
tlsAllowInvalidCertificates: getEnv('MONGO_SSL_VALIDATE') !== 'false',
...(getEnv('MONGO_USERNAME') || getEnv('MONGO_PASSWORD')
? {
auth: {
username: getEnv('MONGO_USERNAME'),
password: getEnv('MONGO_PASSWORD'),
},
}
: {}),
...JSON.parse(getEnv('MONGO_READ_OPTIONS', '{}')),
},
},
],
ensureIndexInBackground: true,
},
pg: {
namespace: getEnv('PG_NAMESPACE', ''),
client: {
connectionString: getEnv('PG_CONNECTION_STRING', 'postgresql://postgres:password@localhost:5432/datastore'),
},
},
models,
datastores: JSON.parse(getEnv('DATASTORE_CONFIGS', '[]')),
openApi: {
spec: JSON.parse(getEnv('OPENAPI_SPEC', '{}')),
},
};
/* istanbul ignore next */
const config = configPath !== '' ? require(configPath) : exports.envConfig;
exports.default = config;
//# sourceMappingURL=index.js.map