initial commit
This commit is contained in:
commit
2479abb1b1
10 changed files with 275 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules/
|
17
.vscode/launch.json
vendored
Normal file
17
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "pwa-node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Program",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"program": "${workspaceFolder}/remex.js"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
0
README.md
Normal file
0
README.md
Normal file
22
config.json
Normal file
22
config.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"port": 3000,
|
||||||
|
"timestamp": "DD.MM.YYYY HH:mm:ss:SS"
|
||||||
|
},
|
||||||
|
"log": {
|
||||||
|
"level": "debug"
|
||||||
|
},
|
||||||
|
"api": [
|
||||||
|
{
|
||||||
|
"url": "/uptime",
|
||||||
|
"type": "get",
|
||||||
|
"command": "uptime123",
|
||||||
|
"args": [
|
||||||
|
"-V",
|
||||||
|
"-p"
|
||||||
|
],
|
||||||
|
"passargs": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
27
libs/commands.js
Normal file
27
libs/commands.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
const logger = require('../libs/logger.js');
|
||||||
|
const { spawn } = require('child_process')
|
||||||
|
|
||||||
|
function execute(endpoint) {
|
||||||
|
if (!endpoint || !endpoint.command) {
|
||||||
|
logger.warn('no command defined');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.debug('executing command \'' + endpoint.command + '\' with args \'' + endpoint.args + '\'...');
|
||||||
|
var cmd = spawn(endpoint.command, endpoint.args);
|
||||||
|
cmd.stdout.on('data', function(data) {
|
||||||
|
logger.debug(data);
|
||||||
|
});
|
||||||
|
cmd.stderr.on('data', function(data) {
|
||||||
|
logger.error(data);
|
||||||
|
});
|
||||||
|
cmd.on('close', function(code) {
|
||||||
|
logger.debug('command \'' + endpoint.command + '\' with args \'' + endpoint.args + '\' finished with exit code ' + code);
|
||||||
|
});
|
||||||
|
cmd.on('error', function(err) {
|
||||||
|
logger.error('command \'' + endpoint.command + '\' with args \'' + endpoint.args + '\' encountered an error >>> ' + err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
execute
|
||||||
|
}
|
110
libs/logger.js
Normal file
110
libs/logger.js
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
const config = require("../config.json");
|
||||||
|
const moment = require("moment");
|
||||||
|
|
||||||
|
// constants
|
||||||
|
const LOG_PREFIX_DEBUG = "debug";
|
||||||
|
const LOG_PREFIX_INFO = "info";
|
||||||
|
const LOG_PREFIX_WARNING = "warning";
|
||||||
|
const LOG_PREFIX_ERROR = "error";
|
||||||
|
const LOGLEVEL_DEBUG = 0;
|
||||||
|
const LOGLEVEL_INFO = 1;
|
||||||
|
const LOGLEVEL_WARNING = 2;
|
||||||
|
const LOGLEVEL_ERROR = 3;
|
||||||
|
|
||||||
|
// set loglevel on "require"
|
||||||
|
const loglevel = function() {
|
||||||
|
switch (config.log.level) {
|
||||||
|
case LOG_PREFIX_DEBUG:
|
||||||
|
case LOGLEVEL_DEBUG:
|
||||||
|
return LOGLEVEL_DEBUG;
|
||||||
|
case LOG_PREFIX_INFO:
|
||||||
|
case LOGLEVEL_INFO:
|
||||||
|
return LOGLEVEL_INFO;
|
||||||
|
case LOG_PREFIX_WARNING:
|
||||||
|
case LOGLEVEL_WARNING:
|
||||||
|
return LOGLEVEL_WARNING;
|
||||||
|
case LOG_PREFIX_ERROR:
|
||||||
|
case LOGLEVEL_ERROR:
|
||||||
|
return LOGLEVEL_ERROR;
|
||||||
|
default:
|
||||||
|
return LOGLEVEL_INFO;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
// log a http request
|
||||||
|
function request(request) {
|
||||||
|
let message = "[" + request.method + "] url: \"" + request.url + "\"";
|
||||||
|
let counter = 1;
|
||||||
|
for (let param in request.body) {
|
||||||
|
message += ", parameter " + counter + ": \"" + param + "=" + request.body[param] + "\"";
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
debug(message.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefix log with "info"
|
||||||
|
function info(message) {
|
||||||
|
if (loglevel > LOGLEVEL_INFO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
trace(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefix log with "info"
|
||||||
|
function warn(message) {
|
||||||
|
if (loglevel > LOGLEVEL_WARNING) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
trace(message, "warning");
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefix log with "debug"
|
||||||
|
function debug(message) {
|
||||||
|
if (loglevel > LOGLEVEL_DEBUG) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
trace(message, "debug");
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefix log with "error"
|
||||||
|
function error(message) {
|
||||||
|
if (loglevel > LOGLEVEL_ERROR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
trace(message, "error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// default logging function
|
||||||
|
function trace(message, prefix) {
|
||||||
|
if (message === undefined || message === null || message.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (prefix === undefined || prefix === null || prefix.length === 0) {
|
||||||
|
prefix = "info";
|
||||||
|
}
|
||||||
|
let print;
|
||||||
|
switch (prefix) {
|
||||||
|
case "error":
|
||||||
|
print = console.error;
|
||||||
|
break;
|
||||||
|
case "debug":
|
||||||
|
print = console.debug;
|
||||||
|
break;
|
||||||
|
case "warning":
|
||||||
|
print = console.warn;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print = console.log;
|
||||||
|
}
|
||||||
|
message = moment().format(config.server.timestamp) + " | " + prefix + " > " + message;
|
||||||
|
print(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// exports
|
||||||
|
module.exports = {
|
||||||
|
info,
|
||||||
|
warn,
|
||||||
|
debug,
|
||||||
|
error,
|
||||||
|
request
|
||||||
|
};
|
55
libs/server.js
Normal file
55
libs/server.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
const config = require('../config.json');
|
||||||
|
const logger = require('../libs/logger.js');
|
||||||
|
const commands = require('../libs/commands.js');
|
||||||
|
const http = require('http');
|
||||||
|
|
||||||
|
var server;
|
||||||
|
var api;
|
||||||
|
|
||||||
|
function start(callback) {
|
||||||
|
buildAPI();
|
||||||
|
if (!server) {
|
||||||
|
server = http.createServer();
|
||||||
|
}
|
||||||
|
server.listen(config.server.port, config.server.listen)
|
||||||
|
.on('listening', function () {
|
||||||
|
logger.debug('server listening on ' + config.server.listen + ':' + config.server.port + '...');
|
||||||
|
handleRequests();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRequests() {
|
||||||
|
server.on('request', function (request, response) {
|
||||||
|
logger.request(request);
|
||||||
|
var endpoint = api.get(request.url);
|
||||||
|
if (!endpoint) {
|
||||||
|
response.writeHead(501);
|
||||||
|
response.end('endpoint not defined\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (request.method.toLowerCase() != (endpoint.type.toLowerCase())) {
|
||||||
|
response.writeHead(405);
|
||||||
|
response.end('endpoint does not support ' + request.method + ' requests\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
commands.execute(endpoint);
|
||||||
|
response.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAPI(callback) {
|
||||||
|
if (!config.api) {
|
||||||
|
logger.warn('no api defined');
|
||||||
|
}
|
||||||
|
api = new Map();
|
||||||
|
config.api.forEach(function(endpoint) {
|
||||||
|
var url = endpoint.url;
|
||||||
|
var tmp = endpoint;
|
||||||
|
delete tmp.url;
|
||||||
|
api.set(url, tmp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
start
|
||||||
|
}
|
24
package.json
Normal file
24
package.json
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "remex",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "execute local commands remotely via http requests",
|
||||||
|
"main": "remex.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"scripts",
|
||||||
|
"commands",
|
||||||
|
"remote",
|
||||||
|
"api"
|
||||||
|
],
|
||||||
|
"author": "Daniel Sommer <daniel.sommer@velvettear.de>",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.velvettear.de/velvettear/remex.git"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"moment": "^2.29.1"
|
||||||
|
}
|
||||||
|
}
|
11
remex.js
Normal file
11
remex.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
const logger = require('./libs/logger.js');
|
||||||
|
const packageJSON = require('./package.json');
|
||||||
|
const server = require('./libs/server.js')
|
||||||
|
|
||||||
|
logger.info("launching " + packageJSON.name + " " + packageJSON.version)
|
||||||
|
server.start(function(err) {
|
||||||
|
if (err) {
|
||||||
|
logger.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
8
yarn.lock
Normal file
8
yarn.lock
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
moment@^2.29.1:
|
||||||
|
version "2.29.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||||
|
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
Loading…
Reference in a new issue