https://community.parseplatform.org/t/server-log-retention/867/2 Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null). I'll run this in production for a few days. I assume it works. Useful for saving disk space. Update winston-daily-rotate-file package to 4.4.0
280 lines
8.6 KiB
JavaScript
280 lines
8.6 KiB
JavaScript
/**
|
|
* Parse Server Configuration Builder
|
|
*
|
|
* This module builds the definitions file (src/Options/Definitions.js)
|
|
* from the src/Options/index.js options interfaces.
|
|
* The Definitions.js module is responsible for the default values as well
|
|
* as the mappings for the CLI.
|
|
*
|
|
* To rebuild the definitions file, run
|
|
* `$ node resources/buildConfigDefinitions.js`
|
|
*/
|
|
const parsers = require('../src/Options/parsers');
|
|
|
|
function last(array) {
|
|
return array[array.length - 1];
|
|
}
|
|
|
|
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
function toENV(key) {
|
|
let str = '';
|
|
let previousIsUpper = false;
|
|
for(let i = 0; i < key.length; i++) {
|
|
const char = key[i];
|
|
if (letters.indexOf(char) >= 0) {
|
|
if (!previousIsUpper) {
|
|
str += '_';
|
|
previousIsUpper = true;
|
|
}
|
|
} else {
|
|
previousIsUpper = false;
|
|
}
|
|
str += char;
|
|
}
|
|
return str.toUpperCase();
|
|
}
|
|
|
|
function getCommentValue(comment) {
|
|
if (!comment) { return }
|
|
return comment.value.trim();
|
|
}
|
|
|
|
function getENVPrefix(iface) {
|
|
if (iface.id.name === 'ParseServerOptions') {
|
|
return 'PARSE_SERVER_';
|
|
}
|
|
if (iface.id.name === 'CustomPagesOptions') {
|
|
return 'PARSE_SERVER_CUSTOM_PAGES_';
|
|
}
|
|
if (iface.id.name === 'LiveQueryServerOptions') {
|
|
return 'PARSE_LIVE_QUERY_SERVER_';
|
|
}
|
|
if (iface.id.name === 'LiveQueryOptions') {
|
|
return 'PARSE_SERVER_LIVEQUERY_';
|
|
}
|
|
}
|
|
|
|
function processProperty(property, iface) {
|
|
const firstComment = getCommentValue(last(property.leadingComments || []));
|
|
const name = property.key.name;
|
|
const prefix = getENVPrefix(iface);
|
|
|
|
if (!firstComment) {
|
|
return;
|
|
}
|
|
const lines = firstComment.split('\n').map((line) => line.trim());
|
|
let help = '';
|
|
let envLine;
|
|
let defaultLine;
|
|
lines.forEach((line) => {
|
|
if (line.indexOf(':ENV:') === 0) {
|
|
envLine = line;
|
|
} else if (line.indexOf(':DEFAULT:') === 0) {
|
|
defaultLine = line;
|
|
} else {
|
|
help += line;
|
|
}
|
|
});
|
|
let env;
|
|
if (envLine) {
|
|
env = envLine.split(' ')[1];
|
|
} else {
|
|
env = (prefix + toENV(name));
|
|
}
|
|
let defaultValue;
|
|
if (defaultLine) {
|
|
defaultValue = defaultLine.split(' ')[1];
|
|
}
|
|
let type = property.value.type;
|
|
let isRequired = true;
|
|
if (type == 'NullableTypeAnnotation') {
|
|
isRequired = false;
|
|
type = property.value.typeAnnotation.type;
|
|
}
|
|
return {
|
|
name,
|
|
env,
|
|
help,
|
|
type,
|
|
defaultValue,
|
|
types: property.value.types,
|
|
typeAnnotation: property.value.typeAnnotation,
|
|
required: isRequired
|
|
};
|
|
}
|
|
|
|
|
|
function doInterface(iface) {
|
|
return iface.body.properties
|
|
.sort((a, b) => a.key.name.localeCompare(b.key.name))
|
|
.map((prop) => processProperty(prop, iface))
|
|
.filter((e) => e !== undefined);
|
|
}
|
|
|
|
function mapperFor(elt, t) {
|
|
const p = t.identifier('parsers');
|
|
const wrap = (identifier) => t.memberExpression(p, identifier);
|
|
|
|
if (t.isNumberTypeAnnotation(elt)) {
|
|
return t.callExpression(wrap(t.identifier('numberParser')), [t.stringLiteral(elt.name)]);
|
|
} else if (t.isArrayTypeAnnotation(elt)) {
|
|
return wrap(t.identifier('arrayParser'));
|
|
} else if (t.isAnyTypeAnnotation(elt)) {
|
|
return wrap(t.identifier('objectParser'));
|
|
} else if (t.isBooleanTypeAnnotation(elt)) {
|
|
return wrap(t.identifier('booleanParser'));
|
|
} else if (t.isGenericTypeAnnotation(elt)) {
|
|
const type = elt.typeAnnotation.id.name;
|
|
if (type == 'Adapter') {
|
|
return wrap(t.identifier('moduleOrObjectParser'));
|
|
}
|
|
if (type == 'NumberOrBoolean') {
|
|
return wrap(t.identifier('numberOrBooleanParser'));
|
|
}
|
|
return wrap(t.identifier('objectParser'));
|
|
}
|
|
}
|
|
|
|
function parseDefaultValue(elt, value, t) {
|
|
let literalValue;
|
|
if (t.isStringTypeAnnotation(elt)) {
|
|
if (value == '""' || value == "''") {
|
|
literalValue = t.stringLiteral('');
|
|
} else {
|
|
literalValue = t.stringLiteral(value);
|
|
}
|
|
} else if (t.isNumberTypeAnnotation(elt)) {
|
|
literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value));
|
|
} else if (t.isArrayTypeAnnotation(elt)) {
|
|
const array = parsers.objectParser(value);
|
|
literalValue = t.arrayExpression(array.map((value) => {
|
|
if (typeof value == 'string') {
|
|
return t.stringLiteral(value);
|
|
} else {
|
|
throw new Error('Unable to parse array');
|
|
}
|
|
}));
|
|
} else if (t.isAnyTypeAnnotation(elt)) {
|
|
literalValue = t.arrayExpression([]);
|
|
} else if (t.isBooleanTypeAnnotation(elt)) {
|
|
literalValue = t.booleanLiteral(parsers.booleanParser(value));
|
|
} else if (t.isGenericTypeAnnotation(elt)) {
|
|
const type = elt.typeAnnotation.id.name;
|
|
if (type == 'NumberOrBoolean') {
|
|
literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value));
|
|
}
|
|
if (type == 'CustomPagesOptions') {
|
|
const object = parsers.objectParser(value);
|
|
const props = Object.keys(object).map((key) => {
|
|
return t.objectProperty(key, object[value]);
|
|
});
|
|
literalValue = t.objectExpression(props);
|
|
}
|
|
if (type == 'ProtectedFields') {
|
|
const prop = t.objectProperty(
|
|
t.stringLiteral('_User'), t.objectPattern([
|
|
t.objectProperty(t.stringLiteral('*'), t.arrayExpression([t.stringLiteral('email')]))
|
|
])
|
|
);
|
|
literalValue = t.objectExpression([prop]);
|
|
}
|
|
}
|
|
return literalValue;
|
|
}
|
|
|
|
function inject(t, list) {
|
|
let comments = '';
|
|
const results = list.map((elt) => {
|
|
if (!elt.name) {
|
|
return;
|
|
}
|
|
const props = ['env', 'help'].map((key) => {
|
|
if (elt[key]) {
|
|
return t.objectProperty(t.stringLiteral(key), t.stringLiteral(elt[key]));
|
|
}
|
|
}).filter((e) => e !== undefined);
|
|
if (elt.required) {
|
|
props.push(t.objectProperty(t.stringLiteral('required'), t.booleanLiteral(true)))
|
|
}
|
|
const action = mapperFor(elt, t);
|
|
if (action) {
|
|
props.push(t.objectProperty(t.stringLiteral('action'), action))
|
|
}
|
|
if (elt.defaultValue) {
|
|
const parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
|
|
if (parsedValue) {
|
|
props.push(t.objectProperty(t.stringLiteral('default'), parsedValue));
|
|
} else {
|
|
throw new Error(`Unable to parse value for ${elt.name} `);
|
|
}
|
|
}
|
|
let type = elt.type.replace('TypeAnnotation', '');
|
|
if (type === 'Generic') {
|
|
type = elt.typeAnnotation.id.name;
|
|
}
|
|
if (type === 'Array') {
|
|
type = `${elt.typeAnnotation.elementType.type.replace('TypeAnnotation', '')}[]`;
|
|
}
|
|
if (type === 'NumberOrBoolean') {
|
|
type = 'Number|Boolean';
|
|
}
|
|
if (type === 'NumberOrString') {
|
|
type = 'Number|String';
|
|
}
|
|
if (type === 'Adapter') {
|
|
const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name;
|
|
type = `Adapter<${adapterType}>`;
|
|
}
|
|
comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`;
|
|
const obj = t.objectExpression(props);
|
|
return t.objectProperty(t.stringLiteral(elt.name), obj);
|
|
}).filter((elt) => {
|
|
return elt != undefined;
|
|
});
|
|
return { results, comments };
|
|
}
|
|
|
|
const makeRequire = function(variableName, module, t) {
|
|
const decl = t.variableDeclarator(t.identifier(variableName), t.callExpression(t.identifier('require'), [t.stringLiteral(module)]));
|
|
return t.variableDeclaration('var', [decl])
|
|
}
|
|
let docs = ``;
|
|
const plugin = function (babel) {
|
|
const t = babel.types;
|
|
const moduleExports = t.memberExpression(t.identifier('module'), t.identifier('exports'));
|
|
return {
|
|
visitor: {
|
|
ImportDeclaration: function(path) {
|
|
path.remove();
|
|
},
|
|
Program: function(path) {
|
|
// Inject the parser's loader
|
|
path.unshiftContainer('body', makeRequire('parsers', './parsers', t));
|
|
},
|
|
ExportDeclaration: function(path) {
|
|
// Export declaration on an interface
|
|
if (path.node && path.node.declaration && path.node.declaration.type == 'InterfaceDeclaration') {
|
|
const { results, comments } = inject(t, doInterface(path.node.declaration));
|
|
const id = path.node.declaration.id.name;
|
|
const exports = t.memberExpression(moduleExports, t.identifier(id));
|
|
docs += `/**\n * @interface ${id}\n${comments} */\n\n`;
|
|
path.replaceWith(
|
|
t.assignmentExpression('=', exports, t.objectExpression(results))
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const auxiliaryCommentBefore = `
|
|
**** GENERATED CODE ****
|
|
This code has been generated by resources/buildConfigDefinitions.js
|
|
Do not edit manually, but update Options/index.js
|
|
`
|
|
|
|
const babel = require("@babel/core");
|
|
const res = babel.transformFileSync('./src/Options/index.js', { plugins: [ plugin, '@babel/transform-flow-strip-types' ], babelrc: false, auxiliaryCommentBefore, sourceMaps: false });
|
|
require('fs').writeFileSync('./src/Options/Definitions.js', res.code + '\n');
|
|
require('fs').writeFileSync('./src/Options/docs.js', docs);
|