atlassian-connect-express
Version:
Library for building Atlassian Add-ons on top of Express
271 lines (233 loc) • 6.52 kB
JavaScript
const _ = require("lodash");
const os = require("os");
const utils = require("./utils");
const URI = require("urijs");
const env = process.env;
const PRODUCTS = {
JIRA: {
id: "jira"
},
CONFLUENCE: {
id: "confluence"
},
BITBUCKET: {
id: "bitbucket"
}
};
const isProductChecker = function (productKey) {
return function () {
return this.id === productKey;
};
};
const concatAppKey = function (userAgent, appKey) {
// eslint-disable-next-line prefer-template
return appKey ? userAgent + " " + appKey : userAgent;
};
_.each(PRODUCTS, p => {
p.isJIRA = isProductChecker("jira").call(p);
p.isConfluence = isProductChecker("confluence").call(p);
p.isBitbucket = isProductChecker("bitbucket").call(p);
});
module.exports = function (config, mode, overrides) {
config = _.merge(config, overrides);
const globalValues = utils.replaceAll(config, env);
const modeValues = utils.replaceAll(config[mode] || config.development, env);
function get(values, key, envKey, vars) {
let value = env[envKey] || values[key] || defaults[key];
if (vars && _.isString(value)) {
value = utils.replaceStr(value, vars);
}
return value;
}
function wrap(values) {
return _.fromPairs(
Object.keys(values).map(k => {
return [
k,
function () {
return values[k];
}
];
})
);
}
function includeCredentialsHosts() {
const creds = utils.loadJSON("credentials.json");
const host_obj = creds.hosts;
if (!host_obj) {
return [];
}
const hosts = Object.keys(host_obj);
const urls = [];
for (const i in hosts) {
const host = hosts[i];
const url = new URI(host);
url
.protocol("https")
.username(host_obj[host].username)
.password(host_obj[host].password);
if (
typeof host_obj[host].product === "string" &&
host_obj[host].product.toLowerCase() === "confluence"
) {
url.segment("wiki");
}
urls.push(url.toString());
}
return urls;
}
return _.extend({}, wrap(globalValues), wrap(modeValues), {
// override simple accessors with more intelligent ones, and add others
expressErrorHandling() {
return modeValues.expressErrorHandling === true
? true
: defaults.expressErrorHandling;
},
errorTemplate() {
return modeValues.errorTemplate === true ? true : defaults.errorTemplate;
},
getErrorTemplateName() {
return modeValues.errorTemplateName || defaults.errorTemplateName;
},
getErrorTemplateObject() {
return modeValues.errorTemplateObject || defaults.errorTemplateObject;
},
setupInstallRoute() {
return modeValues.setupInstallRoute === false
? false
: defaults.setupInstallRoute;
},
watch() {
return modeValues.watch === false ? false : defaults.watch;
},
validateDescriptor() {
return modeValues.validateDescriptor === true
? true
: defaults.validateDescriptor;
},
port() {
return get(modeValues, "port", "PORT");
},
localBaseUrl() {
return get(modeValues, "localBaseUrl", "AC_LOCAL_BASE_URL", {
port: this.port()
});
},
hostScriptUrl() {
return get(modeValues, "hostScriptUrl", "AC_HOST_SCRIPT_URL");
},
allowedBaseUrls() {
let baseUrls =
get(modeValues, "allowedBaseUrls", "AC_ALLOWED_BASE_URLS") || [];
if (_.isString(baseUrls)) {
baseUrls = baseUrls.split(",").map(aud => aud.trim());
}
return _.compact([this.localBaseUrl(), ...baseUrls]);
},
environment() {
return get(modeValues, "environment", "NODE_ENV");
},
appKey() {
return get(modeValues, "appKey", "AC_APP_KEY");
},
store() {
return modeValues.store || defaults.store;
},
product() {
const configProduct =
env.AC_PRODUCT ||
modeValues.product ||
globalValues.product ||
defaults.product;
const product = _.find(PRODUCTS, { id: configProduct });
if (!product) {
throw new Error(
`Product ${configProduct} not supported. Valid values: [${_.map(
PRODUCTS,
"id"
).join(", ")}]`
);
}
return product;
},
hosts() {
return get(modeValues, "hosts").concat(includeCredentialsHosts());
},
ngrok() {
const creds = utils.loadJSON("credentials.json");
if (!creds["ngrok"]) {
console.error(`\n*** credentials.json file is missing ngrok ***\n`);
}
return creds["ngrok"];
},
jwt() {
return get(modeValues, "jwt");
},
// Returns the maximum age of a token in milliseconds.
// The configuration value represents seconds.
maxTokenAge() {
return get(modeValues, "maxTokenAge") * 1000;
},
whitelist() {
let list = get(modeValues, "whitelist", "AC_HOST_WHITELIST");
if (!list) {
if (mode === "production") {
const product = this.product();
if (product.isJIRA || product.isConfluence) {
list = "*.atlassian.net";
}
} else {
list = "";
}
}
if (_.isString(list)) {
list = list.split(",").map(glob => {
return glob.trim();
});
}
return list;
},
whitelistRegexp() {
return this.whitelist().map(glob => {
return glob !== ""
? new RegExp(
`^${glob.replace(/\./g, "\\.").replace(/\*/g, "[^.]*")}$`
)
: new RegExp(".*");
});
},
userAgent(appKey) {
return (
modeValues.userAgent ||
globalValues.userAgent ||
concatAppKey(defaults.userAgent, appKey)
);
},
appId() {
return env.AC_APP_ID || modeValues.appId || globalValues.appId;
}
});
};
const defaults = {
expressErrorHandling: false,
errorTemplate: false,
errorTemplateName: "unauthorized",
errorTemplateObject: {},
setupInstallRoute: true,
watch: true,
validateDescriptor: false,
port: 3000,
localBaseUrl: `http://${os.hostname()}:$port`,
environment: "development",
store: {
adapter: "sequelize",
type: "memory"
},
jwt: {
validityInMinutes: 3
},
product: "jira", // Can be: 'jira', 'confluence', 'bitbucket'
hosts: [],
maxTokenAge: 15 * 60,
userAgent: `atlassian-connect-express/${utils.packageVersion()}`
};