Compare commits
No commits in common. "361a46daa69b0e1e9c477d8759aa6bfea52d162e" and "739c52fc84e004c92add3f7b72c27027028df4c3" have entirely different histories.
361a46daa6
...
739c52fc84
14 changed files with 100 additions and 303 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1 @@
|
||||||
config.json
|
|
||||||
node_modules/
|
node_modules/
|
||||||
npm-debug.log
|
|
1
.nvmrc
1
.nvmrc
|
@ -1 +0,0 @@
|
||||||
17
|
|
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
|
@ -3,16 +3,13 @@
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"type": "pwa-node",
|
"type": "pwa-node",
|
||||||
"runtimeVersion": "17",
|
"runtimeVersion": "16",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "remex",
|
"name": "remex",
|
||||||
"skipFiles": [
|
"skipFiles": [
|
||||||
"<node_internals>/**"
|
"<node_internals>/**"
|
||||||
],
|
],
|
||||||
"program": "${workspaceFolder}/remex.js",
|
"program": "${workspaceFolder}/remex.js"
|
||||||
"args": [
|
|
||||||
"${workspaceFolder}/example_config.json"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
20
LICENSE.md
20
LICENSE.md
|
@ -1,20 +0,0 @@
|
||||||
# MIT License
|
|
||||||
**Copyright (c) 2022 Daniel Sommer \<daniel.sommer@velvettear.de\>**
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is furnished
|
|
||||||
to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice (including the next
|
|
||||||
paragraph) shall be included in all copies or substantial portions of the
|
|
||||||
Software.
|
|
||||||
|
|
||||||
**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
|
||||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
||||||
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.**
|
|
67
README.md
67
README.md
|
@ -1,70 +1,3 @@
|
||||||
# remex
|
# remex
|
||||||
|
|
||||||
execute local commands remotely via http requests
|
execute local commands remotely via http requests
|
||||||
|
|
||||||
## requirements
|
|
||||||
|
|
||||||
- node.js
|
|
||||||
- [nvm](https://github.com/nvm-sh/nvm)
|
|
||||||
|
|
||||||
## setup (as root)
|
|
||||||
|
|
||||||
- install nvm
|
|
||||||
|
|
||||||
- to load nvm restart your terminal or `source ~/.nvm/nvm.sh`
|
|
||||||
|
|
||||||
- clone the project (to '/opt/remex')
|
|
||||||
`git clone https://git.velvettear.de/velvettear/remex.git /opt/remex`
|
|
||||||
|
|
||||||
- install and switch to a supported node.js version (automatically done via .nvmrc file)
|
|
||||||
`nvm install`
|
|
||||||
|
|
||||||
- install the required modules
|
|
||||||
`npm install`
|
|
||||||
|
|
||||||
- switch back to your system's default node.js version
|
|
||||||
`nvm deactivate`
|
|
||||||
|
|
||||||
- execute remex
|
|
||||||
`nvm run remex.js`
|
|
||||||
|
|
||||||
## systemd
|
|
||||||
|
|
||||||
**for security reasons it is highly recommended to not run remex with root permissions!**
|
|
||||||
|
|
||||||
- create a new system user
|
|
||||||
`useradd -U -r -s /usr/bin/nologin node`
|
|
||||||
|
|
||||||
- make your install of nvm available to the new user
|
|
||||||
`cp -R ~/.nvm /opt/nvm`
|
|
||||||
`chown -R node /opt/nvm`
|
|
||||||
|
|
||||||
- symlink the provided systemd-service file and modify it according to your needs
|
|
||||||
`ln -s /opt/remex/remex.service /etc/systemd/system/remex.service`
|
|
||||||
|
|
||||||
- reload systemd-services
|
|
||||||
`systemctl daemon-reload`
|
|
||||||
|
|
||||||
- enable and start remex as a systemd-service
|
|
||||||
`systemctl enable --now remex`
|
|
||||||
|
|
||||||
## configuration
|
|
||||||
|
|
||||||
configuration is done entirely within the file `config.json`.
|
|
||||||
|
|
||||||
### server: [*object*]
|
|
||||||
- address: [*string*] server address to listen on (`0.0.0.0` to listen on all interfaces)
|
|
||||||
- port: [*number*] port to listen on
|
|
||||||
|
|
||||||
### log: [*object*]
|
|
||||||
- level: [*string*] verbosity of the log; either `debug`, `info`, `warning` or `error`
|
|
||||||
- timestamp: [*string*] format string for the timestamp; review [moment.js](https://momentjs.com/docs/#/displaying/format/) for further information
|
|
||||||
|
|
||||||
### api: [*object-array*]
|
|
||||||
- url: [*string*] url of the endpoint
|
|
||||||
- type: [*string*] http request method for the endpoint (either `get` or `post`)
|
|
||||||
- command: [*string*] command / path to script to execute
|
|
||||||
- args: [*string-array*] arguments to pass to the executed command
|
|
||||||
- timeout [*number*] max amount of time in milliseconds until the executed command times out and gets killed
|
|
||||||
- detach [*boolean*] detach from the executed command
|
|
||||||
- unique [*boolean* or *string*] if set to `true` the command can not be executed again until it has finished; if set to `restart` the command will be killed (if active) and started again; if set to `toggle` the command will either be killed if active or started if not active
|
|
||||||
|
|
28
config.json
Normal file
28
config.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"listen": "0.0.0.0",
|
||||||
|
"port": 3000
|
||||||
|
},
|
||||||
|
"log": {
|
||||||
|
"level": "debug",
|
||||||
|
"timestamp": "DD.MM.YYYY HH:mm:ss:SS"
|
||||||
|
},
|
||||||
|
"api": [
|
||||||
|
{
|
||||||
|
"url": "/tail",
|
||||||
|
"method": "get",
|
||||||
|
"command": "tail",
|
||||||
|
"args": [
|
||||||
|
"-f", "/tmp/test"
|
||||||
|
],
|
||||||
|
"detach": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "/uptime",
|
||||||
|
"method": "get",
|
||||||
|
"command": "uptime",
|
||||||
|
"args": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,60 +0,0 @@
|
||||||
{
|
|
||||||
"server": {
|
|
||||||
"listen": "0.0.0.0",
|
|
||||||
"port": 3000
|
|
||||||
},
|
|
||||||
"log": {
|
|
||||||
"level": "debug",
|
|
||||||
"timestamp": "DD.MM.YYYY HH:mm:ss:SS"
|
|
||||||
},
|
|
||||||
"api": [
|
|
||||||
{
|
|
||||||
"url": "/watch",
|
|
||||||
"method": "get",
|
|
||||||
"command": "watch",
|
|
||||||
"args": [
|
|
||||||
"-n",
|
|
||||||
"3",
|
|
||||||
"ls"
|
|
||||||
],
|
|
||||||
"options": {
|
|
||||||
"detach": true,
|
|
||||||
"unique": "restart",
|
|
||||||
"timeout": 10000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/uptime",
|
|
||||||
"method": "get",
|
|
||||||
"command": "uptime",
|
|
||||||
"args": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/systemctl/example",
|
|
||||||
"method": "get",
|
|
||||||
"command": "systemctl",
|
|
||||||
"args": [
|
|
||||||
"is-active",
|
|
||||||
"example"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/systemctl/example/start",
|
|
||||||
"method": "post",
|
|
||||||
"command": "systemctl",
|
|
||||||
"args": [
|
|
||||||
"start",
|
|
||||||
"example"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"url": "/systemctl/example/stop",
|
|
||||||
"method": "post",
|
|
||||||
"command": "systemctl",
|
|
||||||
"args": [
|
|
||||||
"stop",
|
|
||||||
"example"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
125
libs/commands.js
125
libs/commands.js
|
@ -1,146 +1,89 @@
|
||||||
const logger = require('./logger.js');
|
const logger = require('./logger.js');
|
||||||
const { spawn } = require('child_process');
|
const { spawn } = require('child_process')
|
||||||
|
|
||||||
const STATE_OK = 'ok';
|
|
||||||
const STATE_DETACHED = 'detached';
|
|
||||||
const STATE_REJECTED = 'rejected';
|
|
||||||
const STATE_KILLED = 'killed';
|
|
||||||
const STATE_ERROR = 'error';
|
|
||||||
|
|
||||||
const cmds = new Map();
|
const cmds = new Map();
|
||||||
let cmdId = -1;
|
|
||||||
|
|
||||||
async function execute(endpoint) {
|
async function execute(endpoint) {
|
||||||
if (endpoint === undefined) {
|
if (endpoint === undefined) {
|
||||||
return createResult(STATE_REJECTED, undefined, 'endpoint is not defined');
|
return;
|
||||||
}
|
}
|
||||||
let unique = endpoint.options?.unique?.toString();
|
if (isCommandActive(endpoint)) {
|
||||||
if (unique !== undefined && isCommandActive(endpoint)) {
|
logger.info('not executing command \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\') because it is already active');
|
||||||
unique = unique.toLowerCase();
|
throw new Error('command is already active');
|
||||||
switch (unique.toLowerCase()) {
|
|
||||||
case 'true':
|
|
||||||
logger.info('not executing unique command \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\') because it is already active');
|
|
||||||
return createResult(STATE_REJECTED, undefined, 'unique command is already active');
|
|
||||||
case 'restart':
|
|
||||||
logger.info('killing and restarting unique command \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\')...');
|
|
||||||
await killCommand(endpoint);
|
|
||||||
break;
|
|
||||||
case 'toggle':
|
|
||||||
logger.info('stopping unique command \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\')...');
|
|
||||||
await killCommand(endpoint);
|
|
||||||
return createResult(STATE_KILLED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
cmdId++;
|
logger.info('executing command \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\')...');
|
||||||
logger.info('executing command #' + cmdId + ' \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\')...');
|
var cmd = spawn(endpoint.command, endpoint.args);
|
||||||
let cmd = spawn(endpoint.command, endpoint.args);
|
|
||||||
cmd.id = cmdId;
|
|
||||||
cmd.timestamp = new Date().getTime();
|
cmd.timestamp = new Date().getTime();
|
||||||
cmd.data = '';
|
let result = '';
|
||||||
cmd.error = '';
|
let error = '';
|
||||||
cmd.stdout.on('data', (data) => {
|
cmd.stdout.on('data', (data) => {
|
||||||
cmd.data += data;
|
result += data;
|
||||||
});
|
});
|
||||||
cmd.stderr.on('data', (err) => {
|
cmd.stderr.on('data', (data) => {
|
||||||
if (err.toString().toLowerCase().contains('warning')) {
|
error += data;
|
||||||
cmd.data += err;
|
|
||||||
}
|
|
||||||
cmd.error += err;
|
|
||||||
});
|
});
|
||||||
cmd.on('spawn', () => {
|
cmd.on('spawn', () => {
|
||||||
logger.info('spawned command #' + cmd.id + ' \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\')...');
|
logger.info('spawned command \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\')');
|
||||||
addCommand(cmd, endpoint);
|
addCommand(cmd, endpoint);
|
||||||
if (endpoint.options?.timeout && !isNaN(endpoint.options?.timeout)) {
|
if (endpoint.detach === true) {
|
||||||
setTimeout(async () => {
|
resolve();
|
||||||
if (!cmds.has(endpoint)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.warn('killing timed out command #' + cmd.id + ' \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\') after ' + endpoint.options.timeout + 'ms...');
|
|
||||||
await killCommand(endpoint);
|
|
||||||
}, endpoint.options.timeout);
|
|
||||||
}
|
|
||||||
if (endpoint.options?.detach) {
|
|
||||||
return resolve(createResult(STATE_DETACHED, cmd.data, cmd.error));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cmd.on('error', (err) => {
|
cmd.on('error', (err) => {
|
||||||
cmd.error += err;
|
error += err;
|
||||||
removeCommand(endpoint);
|
removeCommand(endpoint);
|
||||||
if (endpoint.options?.detach) {
|
if (endpoint.detach === true) {
|
||||||
reject(createResult(STATE_ERROR, cmd.data, cmd.error));
|
reject();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cmd.on('exit', (code) => {
|
cmd.on('close', (code) => {
|
||||||
if (code === null) {
|
|
||||||
code = 0;
|
|
||||||
}
|
|
||||||
removeCommand(endpoint);
|
removeCommand(endpoint);
|
||||||
let fn = logger.info;
|
let fn = logger.info;
|
||||||
let msg = 'command #' + cmd.id + ' \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\') finished with exit code ' + code + ' after ' + (new Date().getTime() - cmd.timestamp) + 'ms';
|
let msg = 'command \'' + endpoint.command + '\' (args: \'' + endpoint.args + '\') finished with exit code ' + code + ' after ' + (new Date().getTime() - cmd.timestamp) + 'ms';
|
||||||
if (cmd.error !== undefined && cmd.error.length > 0) {
|
if (error !== undefined && error.length > 0) {
|
||||||
cmd.error = error.trim();
|
error = error.trim();
|
||||||
msg += ' > error: ' + cmd.error;
|
msg += ' > error: ' + error;
|
||||||
fn = logger.error;
|
fn = logger.error;
|
||||||
reject(createResult(STATE_ERROR, cmd.data, cmd.error));
|
reject(error);
|
||||||
}
|
}
|
||||||
if (cmd.data !== undefined && cmd.data.length > 0) {
|
if (result !== undefined && result.length > 0) {
|
||||||
cmd.data = cmd.data.trim();
|
result = result.trim();
|
||||||
msg += ' > data: ' + cmd.data;
|
msg += ' > data: ' + result;
|
||||||
}
|
}
|
||||||
fn(msg);
|
fn(msg);
|
||||||
resolve(createResult(STATE_OK, cmd.data, cmd.error));
|
resolve(result);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createResult(state, data, error) {
|
|
||||||
if (state === undefined ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
state,
|
|
||||||
data,
|
|
||||||
error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addCommand(command, endpoint) {
|
function addCommand(command, endpoint) {
|
||||||
if (command === undefined || endpoint === undefined) {
|
if (command === undefined || endpoint === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cmds.set(endpoint, command);
|
cmds.set(JSON.stringify(endpoint), command);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeCommand(endpoint) {
|
function removeCommand(endpoint) {
|
||||||
if (endpoint === undefined) {
|
if (endpoint === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cmds.delete(endpoint);
|
cmds.delete(JSON.stringify(endpoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCommandActive(endpoint) {
|
function isCommandActive(endpoint) {
|
||||||
return endpoint !== undefined && cmds.has(endpoint);
|
return endpoint !== undefined && cmds.has(JSON.stringify(endpoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function killCommand(endpoint) {
|
function killCommand(endpoint) {
|
||||||
if (endpoint === undefined) {
|
if (endpoint === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const command = cmds.get(endpoint);
|
const command = cmds.get(JSON.stringify(endpoint));
|
||||||
if (command === undefined) {
|
if (command === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
process.kill(command.pid, 'SIGINT');
|
process.kill(command.pid);
|
||||||
while (isCommandActive(endpoint)) {
|
|
||||||
await sleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sleep(milliseconds) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
setTimeout(resolve, milliseconds);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const logger = require('./logger.js');
|
const logger = require('./logger.js');
|
||||||
const commands = require('./commands.js');
|
const commands = require('./commands.js');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
const { config } = require('process');
|
||||||
|
|
||||||
let server;
|
let server;
|
||||||
let api;
|
let api;
|
||||||
|
@ -15,7 +16,7 @@ async function start() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
server.listen(port, listen)
|
server.listen(port, listen)
|
||||||
.on('listening', function () {
|
.on('listening', function () {
|
||||||
logger.info('server listening on ' + listen + ':' + port + '...');
|
logger.info('server listening on ' + global.config.server.listen + ':' + global.config.server.port + '...');
|
||||||
handleRequests();
|
handleRequests();
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
@ -34,24 +35,33 @@ async function respond(request, response) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let endpoint = api[request.method]?.[request.url];
|
let endpoint = api[request.method]?.[request.url];
|
||||||
let result = await commands.execute(endpoint);
|
if (endpoint === undefined) {
|
||||||
result.command = endpoint.command;
|
return finishRequest(request, response, { error: 'endpoint not defined' }, 501);
|
||||||
result.args = endpoint.args;
|
}
|
||||||
return finishRequest(request, response, result);
|
try {
|
||||||
|
endpoint.result = await commands.execute(endpoint);
|
||||||
|
} catch (err) {
|
||||||
|
endpoint.error = err.toString();
|
||||||
|
return finishRequest(request, response, endpoint, 501);
|
||||||
|
}
|
||||||
|
return finishRequest(request, response, endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishRequest(request, response, result) {
|
function finishRequest(request, response, data, code) {
|
||||||
if (response === undefined) {
|
if (response === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
result.time = (new Date().getTime() - request.timestamp) + 'ms';
|
if (code === undefined) {
|
||||||
let code = result.code || 200;
|
code = 200;
|
||||||
if (result.error !== undefined && result.error.length > 0) {
|
|
||||||
code = 501;
|
|
||||||
}
|
}
|
||||||
delete result.code;
|
if (code === 200) {
|
||||||
|
data.status = 'ok';
|
||||||
|
} else {
|
||||||
|
data.status = 'error';
|
||||||
|
}
|
||||||
|
data.time = (new Date().getTime() - request.timestamp) + 'ms';
|
||||||
response.writeHead(code);
|
response.writeHead(code);
|
||||||
const json = JSON.stringify(result);
|
const json = JSON.stringify(data);
|
||||||
response.end(json);
|
response.end(json);
|
||||||
logger.http({ request: request, code: code, data: json });
|
logger.http({ request: request, code: code, data: json });
|
||||||
}
|
}
|
||||||
|
|
28
package-lock.json
generated
28
package-lock.json
generated
|
@ -1,28 +0,0 @@
|
||||||
{
|
|
||||||
"name": "remex",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"lockfileVersion": 2,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "remex",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"moment": "^2.29.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/moment": {
|
|
||||||
"version": "2.29.1",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"moment": {
|
|
||||||
"version": "2.29.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,8 @@
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "execute local commands remotely via http requests",
|
"description": "execute local commands remotely via http requests",
|
||||||
"main": "remex.js",
|
"main": "remex.js",
|
||||||
"scripts": {},
|
"scripts": {
|
||||||
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"scripts",
|
"scripts",
|
||||||
"commands",
|
"commands",
|
||||||
|
|
2
remex.js
2
remex.js
|
@ -8,7 +8,7 @@ const INTERRUPTS = ['beforeExit', 'SIGINT', 'SIGTERM'];
|
||||||
main();
|
main();
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
let configPath = path.resolve(process.argv[2] || __dirname + '/config.json');
|
let configPath = path.resolve('./config.json');
|
||||||
try {
|
try {
|
||||||
global.config = require(configPath);
|
global.config = require(configPath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=remex
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=root
|
|
||||||
Group=root
|
|
||||||
WorkingDirectory=/opt/remex
|
|
||||||
ExecStart=/opt/nvm/nvm-exec node remex.js
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
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