Skip to content

Commit

Permalink
feat: Create standalone binaries for Win, macOS, Linux
Browse files Browse the repository at this point in the history
Implements #383, #381
  • Loading branch information
mountaindude committed Apr 1, 2022
1 parent 167bf34 commit 6ae4e43
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 1,401 deletions.
45 changes: 23 additions & 22 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable global-require */
// const Fastify = require('fastify');

const path = require('path');
Expand Down Expand Up @@ -81,9 +82,9 @@ async function build(opts = {}) {
globals.logger.info('--------------------------------------');

// Log info about what Qlik Sense certificates are being used
globals.logger.info(`Client cert: ${certFile}`);
globals.logger.info(`Client cert key: ${keyFile}`);
globals.logger.info(`CA cert: ${caFile}`);
globals.logger.info(`Client cert : ${certFile}`);
globals.logger.info(`Client cert key : ${keyFile}`);
globals.logger.info(`Client cert CA : ${caFile}`);

// Set up anon telemetry reports, if enabled
if (
Expand Down Expand Up @@ -118,6 +119,25 @@ async function build(opts = {}) {
globals.logger.error(`CONFIG: Error initiating host info: ${err}`);
}

// This loads all plugins defined in plugins.
// Those should be support plugins that are reused through your application
restServer.register(require('./plugins/sensible'), { options: Object.assign({}, opts) });
restServer.register(require('./plugins/support'), { options: Object.assign({}, opts) });

// Loads all plugins defined in routes
restServer.register(require('./routes/api'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/baseConversion'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/butlerPing'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/disk_utils'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/keyValueStore'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/mqttPublishMessage'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/scheduler'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/senseApp'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/senseAppDump'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/senseListApps'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/senseStartTask'), { options: Object.assign({}, opts) });
restServer.register(require('./routes/slackPostMessage'), { options: Object.assign({}, opts) });

restServer.register(FastifySwagger, {
routePrefix: '/documentation',
swagger: {
Expand Down Expand Up @@ -219,25 +239,6 @@ async function build(opts = {}) {

dockerHealthCheckServer.register(FastifyHealthcheck);

// Do not touch the following lines

// This loads all plugins defined in plugins
// those should be support plugins that are reused
// through your application
restServer.register(AutoLoad, {
dir: path.join(__dirname, 'plugins'),
// eslint-disable-next-line prefer-object-spread
options: Object.assign({}, opts),
});

// This loads all plugins defined in routes
// define your routes in one of these
restServer.register(AutoLoad, {
dir: path.join(__dirname, 'routes'),
// eslint-disable-next-line prefer-object-spread
options: Object.assign({}, opts),
});

return { restServer, proxyRestServer, dockerHealthCheckServer };
}

Expand Down
3 changes: 0 additions & 3 deletions src/butler.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// Add dependencies
// const restServer = require('fastify')({ logger: true });
// const proxyRestServer = require('fastify')({ logger: true });
const mqtt = require('mqtt');
const dgram = require('dgram');

Expand All @@ -10,7 +8,6 @@ const globals = require('./globals');
const build = require('./app');
const udp = require('./udp');

// async function mainScript() {
const start = async () => {
const apps = await build({});

Expand Down
88 changes: 86 additions & 2 deletions src/globals.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const config = require('config');
const fs = require('fs-extra');
const path = require('path');
const Influx = require('influx');
Expand All @@ -7,15 +6,94 @@ const si = require('systeminformation');
const os = require('os');
const crypto = require('crypto');

// Add dependencies
const { Command, Option } = require('commander');

const winston = require('winston');
require('winston-daily-rotate-file');

// Variable holding info about all defined schedules
const configSchedule = [];

function checkFileExistsSync(filepath) {
let flag = true;
try {
fs.accessSync(filepath, fs.constants.F_OK);
} catch (e) {
flag = false;
}
return flag;
}

// Get app version from package.json file
const appVersion = require('./package.json').version;

// Command line parameters
const program = new Command();
program
.version(appVersion)
.name('butler')
.description(
'Butler gives superpowers to client-managed Qlik Sense Enterprise on Windows!\nAdvanced reload failure alerts, task scheduler, key-value store, file system access and much more.'
)
.option('-c, --configfile <file>', 'path to config file')
.addOption(
new Option('-l, --loglevel <level>', 'log level').choices([
'error',
'warn',
'info',
'verbose',
'debug',
'silly',
])
);

// Parse command line params
program.parse(process.argv);
const options = program.opts();

// Is there a config file specified on the command line?
let configFileOption;
let configFileExpanded;
let configFilePath;
let configFileBasename;
let configFileExtension;
if (options.configfile && options.configfile.length > 0) {
configFileOption = options.configfile;
configFileExpanded = path.resolve(options.configfile);
configFilePath = path.dirname(configFileExpanded);
configFileExtension = path.extname(configFileExpanded);
configFileBasename = path.basename(configFileExpanded, configFileExtension);

if (configFileExtension.toLowerCase() !== '.yaml') {
console.log('Error: Config file extension must be yaml');
process.exit(1);
}

if (checkFileExistsSync(options.configfile)) {
process.env.NODE_CONFIG_DIR = configFilePath;
process.env.NODE_ENV = configFileBasename;
} else {
console.log('Error: Specified config file does not exist');
process.exit(1);
}
}

// Are we running as standalone app or not?
const isPkg = typeof process.pkg !== 'undefined';
if (isPkg && configFileOption === undefined) {
// Show help if running as standalone app and mandatory options (e.g. config file) are specified
program.help({ error: true });
}

// eslint-disable-next-line import/order
const config = require('config');

// Is there a log level file specified on the command line?
if (options.loglevel && options.loglevel.length > 0) {
config.Butler.logLevel = options.loglevel;
}

// Set up logger with timestamps and colors, and optional logging to disk file
const logTransports = [];

Expand All @@ -32,10 +110,12 @@ logTransports.push(
})
);

const execPath = isPkg ? path.dirname(process.execPath) : __dirname;

if (config.get('Butler.fileLogging')) {
logTransports.push(
new winston.transports.DailyRotateFile({
dirname: path.join(__dirname, config.get('Butler.logDirectory')),
dirname: path.join(execPath, config.get('Butler.logDirectory')),
filename: 'butler.%DATE%.log',
level: config.get('Butler.logLevel'),
datePattern: 'YYYY-MM-DD',
Expand All @@ -55,6 +135,9 @@ const logger = winston.createLogger({
// Function to get current logging level
const getLoggingLevel = () => logTransports.find((transport) => transport.name === 'console').level;

// Are we running as standalone app or not?
logger.verbose(`Running as standalone app: ${isPkg}`);

// Helper function to read the contents of the certificate files:
const readCert = (filename) => fs.readFileSync(filename);

Expand Down Expand Up @@ -419,4 +502,5 @@ module.exports = {
endpointsEnabled,
initHostInfo,
hostInfo,
isPkg,
};
Loading

0 comments on commit 6ae4e43

Please sign in to comment.