Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/node_modules/
|
||||||
20
.vscode/launch.json
vendored
Normal file
20
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
// 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": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Program",
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**"
|
||||||
|
],
|
||||||
|
"program": "${workspaceFolder}\\dist\\Main.js",
|
||||||
|
"args": [
|
||||||
|
// "--no-output-log"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
39
README.md
Normal file
39
README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# NTSH Camera Runner
|
||||||
|
|
||||||
|
A process monitoring service for the Nothing to See Here installation, designed to work with the main [NTSH-Control](https://git.morphix.nl/Nothing-to-See-Here/NTSH-Control) system.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Camera Runner monitors and manages the camera executable process, providing:
|
||||||
|
|
||||||
|
- Automatic process startup and crash recovery
|
||||||
|
- WebSocket communication on port `6301` for integration with [NTSH-Control](https://git.morphix.nl/Nothing-to-See-Here/NTSH-Control)
|
||||||
|
- Real-time process status and output logging
|
||||||
|
- Remote restart and system reboot capabilities
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Configuration is stored in `~/MorphixProductions/NTSHControl/CameraRunner/config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"socketServer": {
|
||||||
|
"port": 6301
|
||||||
|
},
|
||||||
|
"executable": {
|
||||||
|
"path": "",
|
||||||
|
"arguments": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration
|
||||||
|
|
||||||
|
This service is controlled by the main [NTSH-Control](https://git.morphix.nl/Nothing-to-See-Here/NTSH-Control) system via WebSocket communication. The main control system connects to `127.0.0.1:6301` to monitor status and send restart commands.
|
||||||
|
|
||||||
|
## Process States
|
||||||
|
|
||||||
|
- `STARTING` - Process launching
|
||||||
|
- `RUNNING` - Process active and healthy
|
||||||
|
- `STOPPED` - Process terminated or crashed
|
||||||
|
- `PROBLEM` - Error condition detected
|
||||||
138
dist/Configuration/ConfigurationManager.js
vendored
Normal file
138
dist/Configuration/ConfigurationManager.js
vendored
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||||
|
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.ConfigurationManager = void 0;
|
||||||
|
var fs_extra_1 = require("fs-extra");
|
||||||
|
var path_1 = require("path");
|
||||||
|
var DefaultConfiguration_1 = require("./DefaultConfiguration");
|
||||||
|
var PREFIX = '[ConfigurationManager]';
|
||||||
|
var ConfigurationManager = /** @class */ (function () {
|
||||||
|
function ConfigurationManager(Main) {
|
||||||
|
this._Main = Main;
|
||||||
|
}
|
||||||
|
ConfigurationManager.prototype.load = function () {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var configPath, configExists, config, _a;
|
||||||
|
return __generator(this, function (_b) {
|
||||||
|
switch (_b.label) {
|
||||||
|
case 0:
|
||||||
|
console.log(PREFIX, 'Loading');
|
||||||
|
return [4 /*yield*/, (0, fs_extra_1.ensureDir)((0, path_1.join)(this._Main.dataPath))];
|
||||||
|
case 1:
|
||||||
|
_b.sent();
|
||||||
|
configPath = (0, path_1.join)(this._Main.dataPath, 'config.json');
|
||||||
|
return [4 /*yield*/, (0, fs_extra_1.pathExists)(configPath)];
|
||||||
|
case 2:
|
||||||
|
configExists = _b.sent();
|
||||||
|
if (!!configExists) return [3 /*break*/, 4];
|
||||||
|
return [4 /*yield*/, (0, fs_extra_1.writeFile)(configPath, JSON.stringify(DefaultConfiguration_1.DefaultConfiguration, null, 4))];
|
||||||
|
case 3:
|
||||||
|
_b.sent();
|
||||||
|
this._Main.Config = DefaultConfiguration_1.DefaultConfiguration;
|
||||||
|
console.log(PREFIX, 'Written default configuration');
|
||||||
|
return [3 /*break*/, 7];
|
||||||
|
case 4: return [4 /*yield*/, (0, fs_extra_1.readJSON)(configPath)];
|
||||||
|
case 5:
|
||||||
|
config = _b.sent();
|
||||||
|
_a = this._Main;
|
||||||
|
return [4 /*yield*/, this.validateConfig(config)];
|
||||||
|
case 6:
|
||||||
|
_a.Config = _b.sent();
|
||||||
|
console.log(PREFIX, 'Loaded configuration');
|
||||||
|
_b.label = 7;
|
||||||
|
case 7: return [2 /*return*/];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
ConfigurationManager.prototype.validateConfig = function (config) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var normalizedConfig, hasChanges, configPath;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
normalizedConfig = this.normalizeConfig(config, DefaultConfiguration_1.DefaultConfiguration);
|
||||||
|
hasChanges = JSON.stringify(config) !== JSON.stringify(normalizedConfig);
|
||||||
|
if (!hasChanges) return [3 /*break*/, 2];
|
||||||
|
this._Main.Config = normalizedConfig;
|
||||||
|
configPath = (0, path_1.join)(this._Main.dataPath, 'config.json');
|
||||||
|
return [4 /*yield*/, (0, fs_extra_1.writeFile)(configPath, JSON.stringify(normalizedConfig, null, 4))];
|
||||||
|
case 1:
|
||||||
|
_a.sent();
|
||||||
|
console.log(PREFIX, 'Configuration updated and saved');
|
||||||
|
_a.label = 2;
|
||||||
|
case 2: return [2 /*return*/, normalizedConfig];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
ConfigurationManager.prototype.normalizeConfig = function (current, template) {
|
||||||
|
var _this = this;
|
||||||
|
if (template === null || template === undefined) {
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
if (typeof template !== 'object' || Array.isArray(template)) {
|
||||||
|
if (current !== null &&
|
||||||
|
current !== undefined &&
|
||||||
|
typeof current === typeof template) {
|
||||||
|
if (Array.isArray(template) && Array.isArray(current)) {
|
||||||
|
if (template.length > 0 &&
|
||||||
|
typeof template[0] === 'object' &&
|
||||||
|
!Array.isArray(template[0])) {
|
||||||
|
return current.map(function (item) {
|
||||||
|
return _this.normalizeConfig(item, template[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
var result = {};
|
||||||
|
for (var key in template) {
|
||||||
|
if (template.hasOwnProperty(key)) {
|
||||||
|
result[key] = this.normalizeConfig(current === null || current === void 0 ? void 0 : current[key], template[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
return ConfigurationManager;
|
||||||
|
}());
|
||||||
|
exports.ConfigurationManager = ConfigurationManager;
|
||||||
|
//# sourceMappingURL=ConfigurationManager.js.map
|
||||||
1
dist/Configuration/ConfigurationManager.js.map
vendored
Normal file
1
dist/Configuration/ConfigurationManager.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"ConfigurationManager.js","sourceRoot":"","sources":["../../src/Configuration/ConfigurationManager.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAAsE;AAEtE,6BAA4B;AAC5B,+DAA8D;AAE9D,IAAM,MAAM,GAAG,wBAAwB,CAAC;AACxC;IAGC,8BAAY,IAAU;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACnB,CAAC;IAEK,mCAAI,GAAV;;;;;;wBACC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;wBAE/B,qBAAM,IAAA,oBAAS,EAAC,IAAA,WAAI,EAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAA;;wBAA1C,SAA0C,CAAC;wBAEvC,UAAU,GAAG,IAAA,WAAI,EAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;wBACvC,qBAAM,IAAA,qBAAU,EAAC,UAAU,CAAC,EAAA;;wBAA3C,YAAY,GAAG,SAA4B;6BAC3C,CAAC,YAAY,EAAb,wBAAa;wBAChB,qBAAM,IAAA,oBAAS,EACd,UAAU,EACV,IAAI,CAAC,SAAS,CAAC,2CAAoB,EAAE,IAAI,EAAE,CAAC,CAAC,CAC7C,EAAA;;wBAHD,SAGC,CAAC;wBACF,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,2CAAoB,CAAC;wBACzC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,+BAA+B,CAAC,CAAC;;4BAEhC,qBAAM,IAAA,mBAAQ,EAAC,UAAU,CAAC,EAAA;;wBAA3C,MAAM,GAAW,SAA0B;wBAE/C,KAAA,IAAI,CAAC,KAAK,CAAA;wBAAU,qBAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAA;;wBAArD,GAAW,MAAM,GAAG,SAAiC,CAAC;wBACtD,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;;;;;;KAE7C;IAEK,6CAAc,GAApB,UAAqB,MAAc;;;;;;wBAC5B,gBAAgB,GAAW,IAAI,CAAC,eAAe,CACpD,MAAM,EACN,2CAAoB,CACpB,CAAC;wBACI,UAAU,GACf,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;6BAEzD,UAAU,EAAV,wBAAU;wBACb,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,gBAAgB,CAAC;wBAE/B,UAAU,GAAG,IAAA,WAAI,EAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;wBAC5D,qBAAM,IAAA,oBAAS,EACd,UAAU,EACV,IAAI,CAAC,SAAS,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC,CACzC,EAAA;;wBAHD,SAGC,CAAC;wBACF,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iCAAiC,CAAC,CAAC;;4BAGxD,sBAAO,gBAAgB,EAAC;;;;KACxB;IAEO,8CAAe,GAAvB,UAAwB,OAAY,EAAE,QAAa;QAAnD,iBAwCC;QAvCA,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YACjD,OAAO,QAAQ,CAAC;QACjB,CAAC;QAED,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7D,IACC,OAAO,KAAK,IAAI;gBAChB,OAAO,KAAK,SAAS;gBACrB,OAAO,OAAO,KAAK,OAAO,QAAQ,EACjC,CAAC;gBACF,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;oBACvD,IACC,QAAQ,CAAC,MAAM,GAAG,CAAC;wBACnB,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,QAAQ;wBAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAC1B,CAAC;wBACF,OAAO,OAAO,CAAC,GAAG,CAAC,UAAC,IAAI;4BACvB,OAAA,KAAI,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;wBAAvC,CAAuC,CACvC,CAAC;oBACH,CAAC;oBACD,OAAO,OAAO,CAAC;gBAChB,CAAC;gBACD,OAAO,OAAO,CAAC;YAChB,CAAC;YACD,OAAO,QAAQ,CAAC;QACjB,CAAC;QAED,IAAM,MAAM,GAAQ,EAAE,CAAC;QAEvB,KAAK,IAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC5B,IAAI,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,eAAe,CACjC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAG,GAAG,CAAC,EACd,QAAQ,CAAC,GAAG,CAAC,CACb,CAAC;YACH,CAAC;QACF,CAAC;QAED,OAAO,MAAM,CAAC;IACf,CAAC;IACF,2BAAC;AAAD,CAAC,AA5FD,IA4FC;AA5FY,oDAAoB"}
|
||||||
13
dist/Configuration/DefaultConfiguration.js
vendored
Normal file
13
dist/Configuration/DefaultConfiguration.js
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.DefaultConfiguration = void 0;
|
||||||
|
exports.DefaultConfiguration = {
|
||||||
|
socketServer: {
|
||||||
|
port: 6301,
|
||||||
|
},
|
||||||
|
executable: {
|
||||||
|
path: '',
|
||||||
|
arguments: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=DefaultConfiguration.js.map
|
||||||
1
dist/Configuration/DefaultConfiguration.js.map
vendored
Normal file
1
dist/Configuration/DefaultConfiguration.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"DefaultConfiguration.js","sourceRoot":"","sources":["../../src/Configuration/DefaultConfiguration.ts"],"names":[],"mappings":";;;AAEa,QAAA,oBAAoB,GAAW;IAC3C,YAAY,EAAE;QACb,IAAI,EAAE,IAAI;KACV;IACD,UAAU,EAAE;QACX,IAAI,EAAE,EAAE;QACR,SAAS,EAAE,EAAE;KACb;CACD,CAAC"}
|
||||||
79
dist/Main.js
vendored
Normal file
79
dist/Main.js
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||||
|
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.Main = void 0;
|
||||||
|
var path_1 = require("path");
|
||||||
|
var ConfigurationManager_1 = require("./Configuration/ConfigurationManager");
|
||||||
|
var os_1 = require("os");
|
||||||
|
var SocketServer_1 = require("./SocketServer");
|
||||||
|
var processWatcher_1 = require("./processWatcher");
|
||||||
|
var Utils_1 = require("./Utils");
|
||||||
|
var Main = /** @class */ (function () {
|
||||||
|
function Main() {
|
||||||
|
this.dataPath = (0, path_1.join)((0, os_1.homedir)(), 'MorphixProductions', 'NTSHControl', 'CameraRunner');
|
||||||
|
this.ConfigurationManager = new ConfigurationManager_1.ConfigurationManager(this);
|
||||||
|
this.SocketServer = new SocketServer_1.SocketServer(this);
|
||||||
|
this.ProcessWatcher = new processWatcher_1.ProcessWatcher(this);
|
||||||
|
}
|
||||||
|
Main.prototype.start = function () {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0: return [4 /*yield*/, this.ConfigurationManager.load()];
|
||||||
|
case 1:
|
||||||
|
_a.sent();
|
||||||
|
return [4 /*yield*/, this.SocketServer.listen()];
|
||||||
|
case 2:
|
||||||
|
_a.sent();
|
||||||
|
return [4 /*yield*/, (0, Utils_1.delay)(5000)];
|
||||||
|
case 3:
|
||||||
|
_a.sent();
|
||||||
|
return [4 /*yield*/, this.ProcessWatcher.start()];
|
||||||
|
case 4:
|
||||||
|
_a.sent();
|
||||||
|
return [2 /*return*/];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return Main;
|
||||||
|
}());
|
||||||
|
exports.Main = Main;
|
||||||
|
var _Main = new Main();
|
||||||
|
_Main.start();
|
||||||
|
//# sourceMappingURL=Main.js.map
|
||||||
1
dist/Main.js.map
vendored
Normal file
1
dist/Main.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"Main.js","sourceRoot":"","sources":["../src/Main.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6BAA4B;AAC5B,6EAG8C;AAC9C,yBAA6B;AAC7B,+CAA8C;AAC9C,mDAAkD;AAClD,iCAAgC;AAEhC;IAAA;QACC,aAAQ,GAAG,IAAA,WAAI,EACd,IAAA,YAAO,GAAE,EACT,oBAAoB,EACpB,aAAa,EACb,cAAc,CACd,CAAC;QAEF,yBAAoB,GAAG,IAAI,2CAAoB,CAAC,IAAI,CAAC,CAAC;QACtD,iBAAY,GAAG,IAAI,2BAAY,CAAC,IAAI,CAAC,CAAC;QACtC,mBAAc,GAAG,IAAI,+BAAc,CAAC,IAAI,CAAC,CAAC;IAY3C,CAAC;IARM,oBAAK,GAAX;;;;4BACC,qBAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,EAAE,EAAA;;wBAAtC,SAAsC,CAAC;wBACvC,qBAAM,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAA;;wBAAhC,SAAgC,CAAC;wBAEjC,qBAAM,IAAA,aAAK,EAAC,IAAI,CAAC,EAAA;;wBAAjB,SAAiB,CAAC;wBAElB,qBAAM,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,EAAA;;wBAAjC,SAAiC,CAAC;;;;;KAClC;IACF,WAAC;AAAD,CAAC,AAtBD,IAsBC;AAtBY,oBAAI;AAwBjB,IAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;AACzB,KAAK,CAAC,KAAK,EAAE,CAAC"}
|
||||||
49
dist/Reboot.js
vendored
Normal file
49
dist/Reboot.js
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.reboot = reboot;
|
||||||
|
var child_process_1 = require("child_process");
|
||||||
|
function reboot() {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
return rebootWindows();
|
||||||
|
}
|
||||||
|
else if (process.platform === 'linux') {
|
||||||
|
return rebootLinux();
|
||||||
|
}
|
||||||
|
return Promise.resolve({
|
||||||
|
succeed: false,
|
||||||
|
message: 'Platform not supported',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function rebootWindows() {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
(0, child_process_1.exec)('shutdown /r /t 3', function (error, stdout, stderr) {
|
||||||
|
if (error) {
|
||||||
|
console.error("Error shutting down Windows: ".concat(error.message));
|
||||||
|
return resolve({ succeed: false, message: error.message });
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.error("Error shutting down Windows: ".concat(stderr));
|
||||||
|
return resolve({ succeed: false, message: stderr });
|
||||||
|
}
|
||||||
|
console.log("Windows shutdown command executed: ".concat(stdout));
|
||||||
|
resolve({ succeed: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function rebootLinux() {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
(0, child_process_1.exec)('shutdown -r now', function (error, stdout, stderr) {
|
||||||
|
if (error) {
|
||||||
|
console.error("Error shutting down Linux: ".concat(error.message));
|
||||||
|
return resolve({ succeed: false, message: error.message });
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.error("Error shutting down Linux: ".concat(stderr));
|
||||||
|
return resolve({ succeed: false, message: stderr });
|
||||||
|
}
|
||||||
|
console.log("Linux shutdown command executed: ".concat(stdout));
|
||||||
|
resolve({ succeed: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=Reboot.js.map
|
||||||
1
dist/Reboot.js.map
vendored
Normal file
1
dist/Reboot.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"Reboot.js","sourceRoot":"","sources":["../src/Reboot.ts"],"names":[],"mappings":";;AAEA,wBAWC;AAbD,+CAAqC;AAErC,SAAgB,MAAM;IACrB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClC,OAAO,aAAa,EAAE,CAAC;IACxB,CAAC;SAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzC,OAAO,WAAW,EAAE,CAAC;IACtB,CAAC;IAED,OAAO,OAAO,CAAC,OAAO,CAAC;QACtB,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,wBAAwB;KACjC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa;IACrB,OAAO,IAAI,OAAO,CACjB,UAAC,OAAO,EAAE,MAAM;QACf,IAAA,oBAAI,EAAC,kBAAkB,EAAE,UAAC,KAAK,EAAE,MAAM,EAAE,MAAM;YAC9C,IAAI,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CACZ,uCAAgC,KAAK,CAAC,OAAO,CAAE,CAC/C,CAAC;gBACF,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,CAAC;YACD,IAAI,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,KAAK,CAAC,uCAAgC,MAAM,CAAE,CAAC,CAAC;gBACxD,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YACrD,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,6CAAsC,MAAM,CAAE,CAAC,CAAC;YAC5D,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACJ,CAAC,CACD,CAAC;AACH,CAAC;AAED,SAAS,WAAW;IACnB,OAAO,IAAI,OAAO,CACjB,UAAC,OAAO,EAAE,MAAM;QACf,IAAA,oBAAI,EAAC,iBAAiB,EAAE,UAAC,KAAK,EAAE,MAAM,EAAE,MAAM;YAC7C,IAAI,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CACZ,qCAA8B,KAAK,CAAC,OAAO,CAAE,CAC7C,CAAC;gBACF,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,CAAC;YACD,IAAI,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,KAAK,CAAC,qCAA8B,MAAM,CAAE,CAAC,CAAC;gBACtD,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YACrD,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,2CAAoC,MAAM,CAAE,CAAC,CAAC;YAC1D,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACJ,CAAC,CACD,CAAC;AACH,CAAC"}
|
||||||
49
dist/Shutdown.js
vendored
Normal file
49
dist/Shutdown.js
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.shutdown = shutdown;
|
||||||
|
var child_process_1 = require("child_process");
|
||||||
|
function shutdown() {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
return shutdownWindows();
|
||||||
|
}
|
||||||
|
else if (process.platform === 'linux') {
|
||||||
|
return shutdownLinux();
|
||||||
|
}
|
||||||
|
return Promise.resolve({
|
||||||
|
succeed: false,
|
||||||
|
message: 'Platform not supported',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function shutdownWindows() {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
(0, child_process_1.exec)('shutdown /s /t 3', function (error, stdout, stderr) {
|
||||||
|
if (error) {
|
||||||
|
console.error("Error shutting down Windows: ".concat(error.message));
|
||||||
|
return resolve({ succeed: false, message: error.message });
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.error("Error shutting down Windows: ".concat(stderr));
|
||||||
|
return resolve({ succeed: false, message: stderr });
|
||||||
|
}
|
||||||
|
console.log("Windows shutdown command executed: ".concat(stdout));
|
||||||
|
resolve({ succeed: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function shutdownLinux() {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
(0, child_process_1.exec)('shutdown now', function (error, stdout, stderr) {
|
||||||
|
if (error) {
|
||||||
|
console.error("Error shutting down Linux: ".concat(error.message));
|
||||||
|
return resolve({ succeed: false, message: error.message });
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.error("Error shutting down Linux: ".concat(stderr));
|
||||||
|
return resolve({ succeed: false, message: stderr });
|
||||||
|
}
|
||||||
|
console.log("Linux shutdown command executed: ".concat(stdout));
|
||||||
|
resolve({ succeed: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=Shutdown.js.map
|
||||||
1
dist/Shutdown.js.map
vendored
Normal file
1
dist/Shutdown.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"Shutdown.js","sourceRoot":"","sources":["../src/Shutdown.ts"],"names":[],"mappings":";;AAEA,4BAWC;AAbD,+CAAqC;AAErC,SAAgB,QAAQ;IACvB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClC,OAAO,eAAe,EAAE,CAAC;IAC1B,CAAC;SAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzC,OAAO,aAAa,EAAE,CAAC;IACxB,CAAC;IAED,OAAO,OAAO,CAAC,OAAO,CAAC;QACtB,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,wBAAwB;KACjC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,eAAe;IACvB,OAAO,IAAI,OAAO,CACjB,UAAC,OAAO,EAAE,MAAM;QACf,IAAA,oBAAI,EAAC,kBAAkB,EAAE,UAAC,KAAK,EAAE,MAAM,EAAE,MAAM;YAC9C,IAAI,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CACZ,uCAAgC,KAAK,CAAC,OAAO,CAAE,CAC/C,CAAC;gBACF,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,CAAC;YACD,IAAI,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,KAAK,CAAC,uCAAgC,MAAM,CAAE,CAAC,CAAC;gBACxD,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YACrD,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,6CAAsC,MAAM,CAAE,CAAC,CAAC;YAC5D,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACJ,CAAC,CACD,CAAC;AACH,CAAC;AAED,SAAS,aAAa;IACrB,OAAO,IAAI,OAAO,CACjB,UAAC,OAAO,EAAE,MAAM;QACf,IAAA,oBAAI,EAAC,cAAc,EAAE,UAAC,KAAK,EAAE,MAAM,EAAE,MAAM;YAC1C,IAAI,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CACZ,qCAA8B,KAAK,CAAC,OAAO,CAAE,CAC7C,CAAC;gBACF,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,CAAC;YACD,IAAI,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,KAAK,CAAC,qCAA8B,MAAM,CAAE,CAAC,CAAC;gBACtD,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YACrD,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,2CAAoC,MAAM,CAAE,CAAC,CAAC;YAC1D,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACJ,CAAC,CACD,CAAC;AACH,CAAC"}
|
||||||
87
dist/SocketServer.js
vendored
Normal file
87
dist/SocketServer.js
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||||
|
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.SocketServer = void 0;
|
||||||
|
var socket_io_1 = require("socket.io");
|
||||||
|
var Reboot_1 = require("./Reboot");
|
||||||
|
var PREFIX = '[SocketServer]';
|
||||||
|
var SocketServer = /** @class */ (function () {
|
||||||
|
function SocketServer(Main) {
|
||||||
|
this._Main = Main;
|
||||||
|
this.prepare();
|
||||||
|
}
|
||||||
|
SocketServer.prototype.prepare = function () {
|
||||||
|
var _this = this;
|
||||||
|
this.socket = new socket_io_1.Server();
|
||||||
|
this.socket.on('connection', function (socket) {
|
||||||
|
socket.on('getStatus', function (callback) {
|
||||||
|
if (typeof callback !== 'function')
|
||||||
|
return;
|
||||||
|
callback(_this._Main.ProcessWatcher.getStatus());
|
||||||
|
});
|
||||||
|
socket.on('restart', function (callback) {
|
||||||
|
if (typeof callback !== 'function')
|
||||||
|
return;
|
||||||
|
callback(_this._Main.ProcessWatcher.requestRestart());
|
||||||
|
});
|
||||||
|
socket.on('reboot', function (callback) { return __awaiter(_this, void 0, void 0, function () {
|
||||||
|
var _a;
|
||||||
|
return __generator(this, function (_b) {
|
||||||
|
switch (_b.label) {
|
||||||
|
case 0:
|
||||||
|
if (typeof callback !== 'function')
|
||||||
|
return [2 /*return*/];
|
||||||
|
_a = callback;
|
||||||
|
return [4 /*yield*/, (0, Reboot_1.reboot)()];
|
||||||
|
case 1:
|
||||||
|
_a.apply(void 0, [_b.sent()]);
|
||||||
|
return [2 /*return*/];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}); });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
SocketServer.prototype.listen = function () {
|
||||||
|
var port = this._Main.Config.socketServer.port;
|
||||||
|
this.socket.listen(port);
|
||||||
|
console.log(PREFIX, "Listening on port http://127.0.0.1:".concat(port));
|
||||||
|
};
|
||||||
|
return SocketServer;
|
||||||
|
}());
|
||||||
|
exports.SocketServer = SocketServer;
|
||||||
|
//# sourceMappingURL=SocketServer.js.map
|
||||||
1
dist/SocketServer.js.map
vendored
Normal file
1
dist/SocketServer.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"SocketServer.js","sourceRoot":"","sources":["../src/SocketServer.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAA6D;AAE7D,mCAAkC;AAElC,IAAM,MAAM,GAAG,gBAAgB,CAAC;AAChC;IAKC,sBAAY,IAAU;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAElB,IAAI,CAAC,OAAO,EAAE,CAAC;IAChB,CAAC;IAEO,8BAAO,GAAf;QAAA,iBAsCC;QArCA,IAAI,CAAC,MAAM,GAAG,IAAI,kBAAc,EAAE,CAAC;QAEnC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,UAAC,MAAc;YAC3C,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,UAAC,QAAkB;gBACzC,IAAI,OAAO,QAAQ,KAAK,UAAU;oBAAE,OAAO;gBAE3C,QAAQ,CAAC,KAAI,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,CAAC;YACjD,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,EAAE,CACR,SAAS,EACT,UACC,QAGU;gBAEV,IAAI,OAAO,QAAQ,KAAK,UAAU;oBAAE,OAAO;gBAE3C,QAAQ,CAAC,KAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,EAAE,CAAC,CAAC;YACtD,CAAC,CACD,CAAC;YAEF,MAAM,CAAC,EAAE,CACR,QAAQ,EACR,UACC,QAGU;;;;;4BAEV,IAAI,OAAO,QAAQ,KAAK,UAAU;gCAAE,sBAAO;4BAE3C,KAAA,QAAQ,CAAA;4BAAC,qBAAM,IAAA,eAAM,GAAE,EAAA;;4BAAvB,kBAAS,SAAc,EAAC,CAAC;;;;iBACzB,CACD,CAAC;QACH,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,6BAAM,GAAN;QACC,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,6CAAsC,IAAI,CAAE,CAAC,CAAC;IACnE,CAAC;IACF,mBAAC;AAAD,CAAC,AAxDD,IAwDC;AAxDY,oCAAY"}
|
||||||
22
dist/SocketWerver.js
vendored
Normal file
22
dist/SocketWerver.js
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.WebServer = void 0;
|
||||||
|
var socket_io_1 = require("socket.io");
|
||||||
|
var PREFIX = '[WebServer]';
|
||||||
|
var WebServer = /** @class */ (function () {
|
||||||
|
function WebServer(Main) {
|
||||||
|
this._Main = Main;
|
||||||
|
this.prepare();
|
||||||
|
}
|
||||||
|
WebServer.prototype.prepare = function () {
|
||||||
|
this.socket = new socket_io_1.Server();
|
||||||
|
};
|
||||||
|
WebServer.prototype.listen = function () {
|
||||||
|
var port = this._Main.Config.webServer.port;
|
||||||
|
this.socket.listen(port);
|
||||||
|
console.log(PREFIX, "Listening on port http://127.0.0.1:".concat(port));
|
||||||
|
};
|
||||||
|
return WebServer;
|
||||||
|
}());
|
||||||
|
exports.WebServer = WebServer;
|
||||||
|
//# sourceMappingURL=SocketWerver.js.map
|
||||||
1
dist/SocketWerver.js.map
vendored
Normal file
1
dist/SocketWerver.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"SocketWerver.js","sourceRoot":"","sources":["../src/SocketWerver.ts"],"names":[],"mappings":";;;AAAA,uCAA6D;AAG7D,IAAM,MAAM,GAAG,aAAa,CAAC;AAC7B;IAKC,mBAAY,IAAU;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAElB,IAAI,CAAC,OAAO,EAAE,CAAC;IAChB,CAAC;IAEO,2BAAO,GAAf;QACC,IAAI,CAAC,MAAM,GAAG,IAAI,kBAAc,EAAE,CAAC;IACpC,CAAC;IAED,0BAAM,GAAN;QACC,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,6CAAsC,IAAI,CAAE,CAAC,CAAC;IACnE,CAAC;IACF,gBAAC;AAAD,CAAC,AApBD,IAoBC;AApBY,8BAAS"}
|
||||||
7
dist/Utils.js
vendored
Normal file
7
dist/Utils.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.delay = delay;
|
||||||
|
function delay(duration) {
|
||||||
|
return new Promise(function (resolve) { return setTimeout(resolve, duration); });
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=Utils.js.map
|
||||||
1
dist/Utils.js.map
vendored
Normal file
1
dist/Utils.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"Utils.js","sourceRoot":"","sources":["../src/Utils.ts"],"names":[],"mappings":";;AAAA,sBAEC;AAFD,SAAgB,KAAK,CAAC,QAAgB;IACrC,OAAO,IAAI,OAAO,CAAC,UAAC,OAAO,IAAK,OAAA,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,EAA7B,CAA6B,CAAC,CAAC;AAChE,CAAC"}
|
||||||
22
dist/WebServer.js
vendored
Normal file
22
dist/WebServer.js
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.WebServer = void 0;
|
||||||
|
var socket_io_1 = require("socket.io");
|
||||||
|
var PREFIX = '[WebServer]';
|
||||||
|
var WebServer = /** @class */ (function () {
|
||||||
|
function WebServer(Main) {
|
||||||
|
this._Main = Main;
|
||||||
|
this.prepare();
|
||||||
|
}
|
||||||
|
WebServer.prototype.prepare = function () {
|
||||||
|
this.socket = new socket_io_1.Server();
|
||||||
|
};
|
||||||
|
WebServer.prototype.listen = function () {
|
||||||
|
var port = this._Main.Config.webServer.port;
|
||||||
|
this.socket.listen(port);
|
||||||
|
console.log(PREFIX, "Listening on port http://127.0.0.1:".concat(port));
|
||||||
|
};
|
||||||
|
return WebServer;
|
||||||
|
}());
|
||||||
|
exports.WebServer = WebServer;
|
||||||
|
//# sourceMappingURL=WebServer.js.map
|
||||||
1
dist/WebServer.js.map
vendored
Normal file
1
dist/WebServer.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"WebServer.js","sourceRoot":"","sources":["../src/WebServer.ts"],"names":[],"mappings":";;;AAAA,uCAA6D;AAG7D,IAAM,MAAM,GAAG,aAAa,CAAC;AAC7B;IAKC,mBAAY,IAAU;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAElB,IAAI,CAAC,OAAO,EAAE,CAAC;IAChB,CAAC;IAEO,2BAAO,GAAf;QACC,IAAI,CAAC,MAAM,GAAG,IAAI,kBAAc,EAAE,CAAC;IACpC,CAAC;IAED,0BAAM,GAAN;QACC,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,6CAAsC,IAAI,CAAE,CAAC,CAAC;IACnE,CAAC;IACF,gBAAC;AAAD,CAAC,AApBD,IAoBC;AApBY,8BAAS"}
|
||||||
205
dist/processWatcher.js
vendored
Normal file
205
dist/processWatcher.js
vendored
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
||||||
|
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
||||||
|
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
||||||
|
if (ar || !(i in from)) {
|
||||||
|
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
||||||
|
ar[i] = from[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return to.concat(ar || Array.prototype.slice.call(from));
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.ProcessWatcher = void 0;
|
||||||
|
var fs_extra_1 = require("fs-extra");
|
||||||
|
var child_process_1 = require("child_process");
|
||||||
|
var Utils_1 = require("./Utils");
|
||||||
|
var PREFIX = '[ProcessWatcher]';
|
||||||
|
var LOG_OUTPUT = !process.argv.includes('--no-output-log');
|
||||||
|
var ProcessWatcher = /** @class */ (function () {
|
||||||
|
function ProcessWatcher(Main) {
|
||||||
|
this.state = 'STOPPED';
|
||||||
|
this.startTime = null;
|
||||||
|
this.output = { current: [], last: [] };
|
||||||
|
this.restartTriggered = false;
|
||||||
|
this._Main = Main;
|
||||||
|
}
|
||||||
|
ProcessWatcher.prototype.requestRestart = function () {
|
||||||
|
if (this.state !== 'RUNNING')
|
||||||
|
return {
|
||||||
|
succeed: false,
|
||||||
|
message: 'Cannot restart when process is not running. It is probably restarting already.',
|
||||||
|
};
|
||||||
|
this.restart(true);
|
||||||
|
return { succeed: true };
|
||||||
|
};
|
||||||
|
ProcessWatcher.prototype.broadcastState = function () {
|
||||||
|
this._Main.SocketServer.socket.emit('simpleStatus', {
|
||||||
|
state: this.state,
|
||||||
|
message: this.message,
|
||||||
|
error: this.error,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
ProcessWatcher.prototype.setInfo = function (message, error, state) {
|
||||||
|
if (state === void 0) { state = 'PROBLEM'; }
|
||||||
|
this.message = message;
|
||||||
|
this.error = error;
|
||||||
|
this.state = state;
|
||||||
|
this.broadcastState();
|
||||||
|
this.output.current.push("[".concat(new Date().toLocaleTimeString('nl-NL'), "] [System] [").concat(state, "] ").concat(message !== null && message !== void 0 ? message : error));
|
||||||
|
if (state == 'PROBLEM' || state == 'STOPPED')
|
||||||
|
console.warn(PREFIX, message !== null && message !== void 0 ? message : error);
|
||||||
|
else
|
||||||
|
console.log(PREFIX, message !== null && message !== void 0 ? message : error);
|
||||||
|
};
|
||||||
|
ProcessWatcher.prototype.restart = function () {
|
||||||
|
return __awaiter(this, arguments, void 0, function (instant) {
|
||||||
|
if (instant === void 0) { instant = false; }
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
if (this.restartTriggered)
|
||||||
|
return [2 /*return*/];
|
||||||
|
this.restartTriggered = true;
|
||||||
|
this.startTime = -1;
|
||||||
|
this.broadcastState();
|
||||||
|
if (instant)
|
||||||
|
this.setInfo('Process will restart shortly...', null, 'STOPPED');
|
||||||
|
if (!(this.process != null)) return [3 /*break*/, 2];
|
||||||
|
this.process.kill('SIGTERM');
|
||||||
|
return [4 /*yield*/, (0, Utils_1.delay)(3000)];
|
||||||
|
case 1:
|
||||||
|
_a.sent();
|
||||||
|
if (!this.process.killed && this.process.exitCode === null) {
|
||||||
|
this.process.kill('SIGKILL');
|
||||||
|
console.log(PREFIX, 'Sent SIGKILL to process.');
|
||||||
|
}
|
||||||
|
_a.label = 2;
|
||||||
|
case 2:
|
||||||
|
this.startTime = -1;
|
||||||
|
if (!!instant) return [3 /*break*/, 4];
|
||||||
|
this.setInfo("Restarting process in 5 seconds...", null, 'STOPPED');
|
||||||
|
return [4 /*yield*/, (0, Utils_1.delay)(5000)];
|
||||||
|
case 3:
|
||||||
|
_a.sent();
|
||||||
|
_a.label = 4;
|
||||||
|
case 4: return [4 /*yield*/, this.start()];
|
||||||
|
case 5:
|
||||||
|
_a.sent();
|
||||||
|
return [2 /*return*/];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
ProcessWatcher.prototype.start = function () {
|
||||||
|
var _this = this;
|
||||||
|
var _a, _b;
|
||||||
|
this.output.last = __spreadArray([], this.output.current, true);
|
||||||
|
this.output.current = [];
|
||||||
|
this.startTime = Date.now();
|
||||||
|
this.restartTriggered = false;
|
||||||
|
this.broadcastState();
|
||||||
|
var path = this._Main.Config.executable.path;
|
||||||
|
if (path == null || !(0, fs_extra_1.pathExistsSync)(path)) {
|
||||||
|
this.setInfo('Executable problem', "Executable path is not set or does not exist: ".concat(path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var fileName = path.split(/(\/|\\)/).pop();
|
||||||
|
this.setInfo("Starting executable: ".concat(fileName), null, 'STARTING');
|
||||||
|
this.process = (0, child_process_1.spawn)(path, this._Main.Config.executable.arguments, {
|
||||||
|
stdio: 'pipe',
|
||||||
|
});
|
||||||
|
this.process.on('exit', function (code, signal) {
|
||||||
|
if (_this.restartTriggered)
|
||||||
|
return;
|
||||||
|
_this.setInfo('Process exited', "Process exited with code ".concat(code, " and signal ").concat(signal), 'STOPPED');
|
||||||
|
_this.restart();
|
||||||
|
});
|
||||||
|
this.process.on('error', function (err) {
|
||||||
|
_this.setInfo('Process error', err.message);
|
||||||
|
_this.restart();
|
||||||
|
});
|
||||||
|
(_a = this.process.stdout) === null || _a === void 0 ? void 0 : _a.on('data', function (data) {
|
||||||
|
var lines = data
|
||||||
|
.toString()
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.filter(function (line) { return line.length > 0; });
|
||||||
|
lines.forEach(function (line) {
|
||||||
|
var formattedLine = "[".concat(new Date().toLocaleTimeString('nl-NL'), "] [").concat(fileName, "] ").concat(line);
|
||||||
|
if (LOG_OUTPUT)
|
||||||
|
console.log(PREFIX, formattedLine);
|
||||||
|
_this.output.current.push(formattedLine);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
(_b = this.process.stderr) === null || _b === void 0 ? void 0 : _b.on('data', function (data) {
|
||||||
|
var lines = data
|
||||||
|
.toString()
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.filter(function (line) { return line.length > 0; });
|
||||||
|
lines.forEach(function (line) {
|
||||||
|
var formattedLine = "[".concat(new Date().toLocaleTimeString('nl-NL'), "] [").concat(fileName, "] [ERROR] ").concat(line);
|
||||||
|
if (LOG_OUTPUT)
|
||||||
|
console.error(PREFIX, formattedLine);
|
||||||
|
_this.output.current.push(formattedLine);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setTimeout(function () {
|
||||||
|
var _a, _b;
|
||||||
|
if (_this.process == null ||
|
||||||
|
((_a = _this.process) === null || _a === void 0 ? void 0 : _a.killed) ||
|
||||||
|
((_b = _this.process) === null || _b === void 0 ? void 0 : _b.exitCode) != null ||
|
||||||
|
_this.restartTriggered)
|
||||||
|
return;
|
||||||
|
_this.setInfo('Running', '', 'RUNNING');
|
||||||
|
}, 5000);
|
||||||
|
};
|
||||||
|
ProcessWatcher.prototype.getStatus = function () {
|
||||||
|
return {
|
||||||
|
state: this.state,
|
||||||
|
message: this.message,
|
||||||
|
error: this.error,
|
||||||
|
startTime: this.startTime,
|
||||||
|
output: this.output,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return ProcessWatcher;
|
||||||
|
}());
|
||||||
|
exports.ProcessWatcher = ProcessWatcher;
|
||||||
|
//# sourceMappingURL=processWatcher.js.map
|
||||||
1
dist/processWatcher.js.map
vendored
Normal file
1
dist/processWatcher.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
315
package-lock.json
generated
Normal file
315
package-lock.json
generated
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
{
|
||||||
|
"name": "ntshcamerarunner",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "ntshcamerarunner",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/fs-extra": "^11.0.4",
|
||||||
|
"@types/node": "^24.9.1",
|
||||||
|
"fs-extra": "^11.3.2",
|
||||||
|
"socket.io": "^4.8.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/cors": {
|
||||||
|
"version": "2.8.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
|
||||||
|
"integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/fs-extra": {
|
||||||
|
"version": "11.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz",
|
||||||
|
"integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/jsonfile": "*",
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/jsonfile": {
|
||||||
|
"version": "6.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz",
|
||||||
|
"integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "24.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
|
||||||
|
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/accepts": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-types": "~2.1.34",
|
||||||
|
"negotiator": "0.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/base64id": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^4.5.0 || >= 5.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cookie": {
|
||||||
|
"version": "0.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||||
|
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cors": {
|
||||||
|
"version": "2.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||||
|
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io": {
|
||||||
|
"version": "6.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
|
||||||
|
"integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/cors": "^2.8.12",
|
||||||
|
"@types/node": ">=10.0.0",
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "2.0.0",
|
||||||
|
"cookie": "~0.7.2",
|
||||||
|
"cors": "~2.8.5",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.2.1",
|
||||||
|
"ws": "~8.17.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-parser": {
|
||||||
|
"version": "5.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||||
|
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "11.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz",
|
||||||
|
"integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/graceful-fs": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/negotiator": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io": {
|
||||||
|
"version": "4.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
|
||||||
|
"integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": "~1.3.4",
|
||||||
|
"base64id": "~2.0.0",
|
||||||
|
"cors": "~2.8.5",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io": "~6.6.0",
|
||||||
|
"socket.io-adapter": "~2.5.2",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-adapter": {
|
||||||
|
"version": "2.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
|
||||||
|
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "~4.3.4",
|
||||||
|
"ws": "~8.17.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
|
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vary": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
package.json
Normal file
17
package.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "ntshcamerarunner",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "dist/Main.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node ."
|
||||||
|
},
|
||||||
|
"author": "Mees van der Wijk - Morphix Productions",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/fs-extra": "^11.0.4",
|
||||||
|
"@types/node": "^24.9.1",
|
||||||
|
"fs-extra": "^11.3.2",
|
||||||
|
"socket.io": "^4.8.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
113
src/Configuration/ConfigurationManager.ts
Normal file
113
src/Configuration/ConfigurationManager.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import { ensureDir, pathExists, readJSON, writeFile } from 'fs-extra';
|
||||||
|
import { Main } from '../Main';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { DefaultConfiguration } from './DefaultConfiguration';
|
||||||
|
|
||||||
|
const PREFIX = '[ConfigurationManager]';
|
||||||
|
export class ConfigurationManager {
|
||||||
|
private _Main: Main;
|
||||||
|
|
||||||
|
constructor(Main: Main) {
|
||||||
|
this._Main = Main;
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
console.log(PREFIX, 'Loading');
|
||||||
|
|
||||||
|
await ensureDir(join(this._Main.dataPath));
|
||||||
|
|
||||||
|
let configPath = join(this._Main.dataPath, 'config.json');
|
||||||
|
let configExists = await pathExists(configPath);
|
||||||
|
if (!configExists) {
|
||||||
|
await writeFile(
|
||||||
|
configPath,
|
||||||
|
JSON.stringify(DefaultConfiguration, null, 4)
|
||||||
|
);
|
||||||
|
this._Main.Config = DefaultConfiguration;
|
||||||
|
console.log(PREFIX, 'Written default configuration');
|
||||||
|
} else {
|
||||||
|
var config: Config = await readJSON(configPath);
|
||||||
|
|
||||||
|
this._Main.Config = await this.validateConfig(config);
|
||||||
|
console.log(PREFIX, 'Loaded configuration');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateConfig(config: Config): Promise<Config> {
|
||||||
|
const normalizedConfig: Config = this.normalizeConfig(
|
||||||
|
config,
|
||||||
|
DefaultConfiguration
|
||||||
|
);
|
||||||
|
const hasChanges =
|
||||||
|
JSON.stringify(config) !== JSON.stringify(normalizedConfig);
|
||||||
|
|
||||||
|
if (hasChanges) {
|
||||||
|
this._Main.Config = normalizedConfig;
|
||||||
|
|
||||||
|
const configPath = join(this._Main.dataPath, 'config.json');
|
||||||
|
await writeFile(
|
||||||
|
configPath,
|
||||||
|
JSON.stringify(normalizedConfig, null, 4)
|
||||||
|
);
|
||||||
|
console.log(PREFIX, 'Configuration updated and saved');
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private normalizeConfig(current: any, template: any): any {
|
||||||
|
if (template === null || template === undefined) {
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof template !== 'object' || Array.isArray(template)) {
|
||||||
|
if (
|
||||||
|
current !== null &&
|
||||||
|
current !== undefined &&
|
||||||
|
typeof current === typeof template
|
||||||
|
) {
|
||||||
|
if (Array.isArray(template) && Array.isArray(current)) {
|
||||||
|
if (
|
||||||
|
template.length > 0 &&
|
||||||
|
typeof template[0] === 'object' &&
|
||||||
|
!Array.isArray(template[0])
|
||||||
|
) {
|
||||||
|
return current.map((item) =>
|
||||||
|
this.normalizeConfig(item, template[0])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: any = {};
|
||||||
|
|
||||||
|
for (const key in template) {
|
||||||
|
if (template.hasOwnProperty(key)) {
|
||||||
|
result[key] = this.normalizeConfig(
|
||||||
|
current?.[key],
|
||||||
|
template[key]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Config {
|
||||||
|
socketServer: ConfigSocketServer;
|
||||||
|
executable: ConfigExecutable;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConfigSocketServer {
|
||||||
|
port: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ConfigExecutable {
|
||||||
|
path: string;
|
||||||
|
arguments: string[];
|
||||||
|
}
|
||||||
11
src/Configuration/DefaultConfiguration.ts
Normal file
11
src/Configuration/DefaultConfiguration.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { Config } from './ConfigurationManager';
|
||||||
|
|
||||||
|
export const DefaultConfiguration: Config = {
|
||||||
|
socketServer: {
|
||||||
|
port: 6301,
|
||||||
|
},
|
||||||
|
executable: {
|
||||||
|
path: '',
|
||||||
|
arguments: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
36
src/Main.ts
Normal file
36
src/Main.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { join } from 'path';
|
||||||
|
import {
|
||||||
|
Config,
|
||||||
|
ConfigurationManager,
|
||||||
|
} from './Configuration/ConfigurationManager';
|
||||||
|
import { homedir } from 'os';
|
||||||
|
import { SocketServer } from './SocketServer';
|
||||||
|
import { ProcessWatcher } from './processWatcher';
|
||||||
|
import { delay } from './Utils';
|
||||||
|
|
||||||
|
export class Main {
|
||||||
|
dataPath = join(
|
||||||
|
homedir(),
|
||||||
|
'MorphixProductions',
|
||||||
|
'NTSHControl',
|
||||||
|
'CameraRunner'
|
||||||
|
);
|
||||||
|
|
||||||
|
ConfigurationManager = new ConfigurationManager(this);
|
||||||
|
SocketServer = new SocketServer(this);
|
||||||
|
ProcessWatcher = new ProcessWatcher(this);
|
||||||
|
|
||||||
|
Config: Config;
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
await this.ConfigurationManager.load();
|
||||||
|
await this.SocketServer.listen();
|
||||||
|
|
||||||
|
await delay(5000);
|
||||||
|
|
||||||
|
await this.ProcessWatcher.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Main = new Main();
|
||||||
|
_Main.start();
|
||||||
56
src/Reboot.ts
Normal file
56
src/Reboot.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { exec } from 'child_process';
|
||||||
|
|
||||||
|
export function reboot(): Promise<{ succeed: boolean; message?: string }> {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
return rebootWindows();
|
||||||
|
} else if (process.platform === 'linux') {
|
||||||
|
return rebootLinux();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
succeed: false,
|
||||||
|
message: 'Platform not supported',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebootWindows(): Promise<{ succeed: boolean; message?: string }> {
|
||||||
|
return new Promise<{ succeed: boolean; message?: string }>(
|
||||||
|
(resolve, reject) => {
|
||||||
|
exec('shutdown /r /t 3', (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(
|
||||||
|
`Error shutting down Windows: ${error.message}`
|
||||||
|
);
|
||||||
|
return resolve({ succeed: false, message: error.message });
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`Error shutting down Windows: ${stderr}`);
|
||||||
|
return resolve({ succeed: false, message: stderr });
|
||||||
|
}
|
||||||
|
console.log(`Windows shutdown command executed: ${stdout}`);
|
||||||
|
resolve({ succeed: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebootLinux(): Promise<{ succeed: boolean; message?: string }> {
|
||||||
|
return new Promise<{ succeed: boolean; message?: string }>(
|
||||||
|
(resolve, reject) => {
|
||||||
|
exec('shutdown -r now', (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(
|
||||||
|
`Error shutting down Linux: ${error.message}`
|
||||||
|
);
|
||||||
|
return resolve({ succeed: false, message: error.message });
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`Error shutting down Linux: ${stderr}`);
|
||||||
|
return resolve({ succeed: false, message: stderr });
|
||||||
|
}
|
||||||
|
console.log(`Linux shutdown command executed: ${stdout}`);
|
||||||
|
resolve({ succeed: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
62
src/SocketServer.ts
Normal file
62
src/SocketServer.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { Socket, Server as SocketIOServer } from 'socket.io';
|
||||||
|
import { Main } from './Main';
|
||||||
|
import { reboot } from './Reboot';
|
||||||
|
|
||||||
|
const PREFIX = '[SocketServer]';
|
||||||
|
export class SocketServer {
|
||||||
|
private _Main: Main;
|
||||||
|
|
||||||
|
socket: SocketIOServer;
|
||||||
|
|
||||||
|
constructor(Main: Main) {
|
||||||
|
this._Main = Main;
|
||||||
|
|
||||||
|
this.prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
private prepare() {
|
||||||
|
this.socket = new SocketIOServer();
|
||||||
|
|
||||||
|
this.socket.on('connection', (socket: Socket) => {
|
||||||
|
socket.on('getStatus', (callback: Function) => {
|
||||||
|
if (typeof callback !== 'function') return;
|
||||||
|
|
||||||
|
callback(this._Main.ProcessWatcher.getStatus());
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on(
|
||||||
|
'restart',
|
||||||
|
(
|
||||||
|
callback: (response: {
|
||||||
|
succeed: boolean;
|
||||||
|
message?: string;
|
||||||
|
}) => void
|
||||||
|
) => {
|
||||||
|
if (typeof callback !== 'function') return;
|
||||||
|
|
||||||
|
callback(this._Main.ProcessWatcher.requestRestart());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
socket.on(
|
||||||
|
'reboot',
|
||||||
|
async (
|
||||||
|
callback: (response: {
|
||||||
|
succeed: boolean;
|
||||||
|
message?: string;
|
||||||
|
}) => void
|
||||||
|
) => {
|
||||||
|
if (typeof callback !== 'function') return;
|
||||||
|
|
||||||
|
callback(await reboot());
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
listen() {
|
||||||
|
const port = this._Main.Config.socketServer.port;
|
||||||
|
this.socket.listen(port);
|
||||||
|
console.log(PREFIX, `Listening on port http://127.0.0.1:${port}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/Utils.ts
Normal file
3
src/Utils.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function delay(duration: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, duration));
|
||||||
|
}
|
||||||
185
src/processWatcher.ts
Normal file
185
src/processWatcher.ts
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
import { pathExistsSync } from 'fs-extra';
|
||||||
|
import { Main } from './Main';
|
||||||
|
import { ChildProcess, exec, spawn } from 'child_process';
|
||||||
|
import { delay } from './Utils';
|
||||||
|
|
||||||
|
const PREFIX = '[ProcessWatcher]';
|
||||||
|
|
||||||
|
const LOG_OUTPUT = !process.argv.includes('--no-output-log');
|
||||||
|
|
||||||
|
export class ProcessWatcher {
|
||||||
|
private _Main: Main;
|
||||||
|
|
||||||
|
state: CameraRunnerState = 'STOPPED';
|
||||||
|
message?: string;
|
||||||
|
error?: string;
|
||||||
|
|
||||||
|
startTime: number = null;
|
||||||
|
output: { current: string[]; last: string[] } = { current: [], last: [] };
|
||||||
|
|
||||||
|
process: ChildProcess;
|
||||||
|
|
||||||
|
constructor(Main: Main) {
|
||||||
|
this._Main = Main;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestRestart(): { succeed: boolean; message?: string } {
|
||||||
|
if (this.state !== 'RUNNING')
|
||||||
|
return {
|
||||||
|
succeed: false,
|
||||||
|
message:
|
||||||
|
'Cannot restart when process is not running. It is probably restarting already.',
|
||||||
|
};
|
||||||
|
|
||||||
|
this.restart(true);
|
||||||
|
return { succeed: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastState() {
|
||||||
|
this._Main.SocketServer.socket.emit('simpleStatus', {
|
||||||
|
state: this.state,
|
||||||
|
message: this.message,
|
||||||
|
error: this.error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setInfo(
|
||||||
|
message: string,
|
||||||
|
error: string,
|
||||||
|
state: CameraRunnerState = 'PROBLEM'
|
||||||
|
) {
|
||||||
|
this.message = message;
|
||||||
|
this.error = error;
|
||||||
|
this.state = state;
|
||||||
|
this.broadcastState();
|
||||||
|
|
||||||
|
this.output.current.push(
|
||||||
|
`[${new Date().toLocaleTimeString('nl-NL')}] [System] [${state}] ${
|
||||||
|
message ?? error
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (state == 'PROBLEM' || state == 'STOPPED')
|
||||||
|
console.warn(PREFIX, message ?? error);
|
||||||
|
else console.log(PREFIX, message ?? error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private restartTriggered: boolean = false;
|
||||||
|
async restart(instant: boolean = false) {
|
||||||
|
if (this.restartTriggered) return;
|
||||||
|
this.restartTriggered = true;
|
||||||
|
this.startTime = -1;
|
||||||
|
this.broadcastState();
|
||||||
|
|
||||||
|
if (instant)
|
||||||
|
this.setInfo('Process will restart shortly...', null, 'STOPPED');
|
||||||
|
|
||||||
|
if (this.process != null) {
|
||||||
|
this.process.kill('SIGTERM');
|
||||||
|
|
||||||
|
await delay(3000);
|
||||||
|
|
||||||
|
if (!this.process.killed && this.process.exitCode === null) {
|
||||||
|
this.process.kill('SIGKILL');
|
||||||
|
console.log(PREFIX, 'Sent SIGKILL to process.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.startTime = -1;
|
||||||
|
|
||||||
|
if (!instant) {
|
||||||
|
this.setInfo(`Restarting process in 5 seconds...`, null, 'STOPPED');
|
||||||
|
await delay(5000);
|
||||||
|
}
|
||||||
|
await this.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.output.last = [...this.output.current];
|
||||||
|
this.output.current = [];
|
||||||
|
this.startTime = Date.now();
|
||||||
|
this.restartTriggered = false;
|
||||||
|
this.broadcastState();
|
||||||
|
|
||||||
|
const path = this._Main.Config.executable.path;
|
||||||
|
if (path == null || !pathExistsSync(path)) {
|
||||||
|
this.setInfo(
|
||||||
|
'Executable problem',
|
||||||
|
`Executable path is not set or does not exist: ${path}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = path.split(/(\/|\\)/).pop();
|
||||||
|
|
||||||
|
this.setInfo(`Starting executable: ${fileName}`, null, 'STARTING');
|
||||||
|
|
||||||
|
this.process = spawn(path, this._Main.Config.executable.arguments, {
|
||||||
|
stdio: 'pipe',
|
||||||
|
});
|
||||||
|
|
||||||
|
this.process.on('exit', (code, signal) => {
|
||||||
|
if (this.restartTriggered) return;
|
||||||
|
this.setInfo(
|
||||||
|
'Process exited',
|
||||||
|
`Process exited with code ${code} and signal ${signal}`,
|
||||||
|
'STOPPED'
|
||||||
|
);
|
||||||
|
this.restart();
|
||||||
|
});
|
||||||
|
this.process.on('error', (err) => {
|
||||||
|
this.setInfo('Process error', err.message);
|
||||||
|
this.restart();
|
||||||
|
});
|
||||||
|
this.process.stdout?.on('data', (data) => {
|
||||||
|
const lines = data
|
||||||
|
.toString()
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.filter((line) => line.length > 0);
|
||||||
|
lines.forEach((line) => {
|
||||||
|
const formattedLine = `[${new Date().toLocaleTimeString(
|
||||||
|
'nl-NL'
|
||||||
|
)}] [${fileName}] ${line}`;
|
||||||
|
if (LOG_OUTPUT) console.log(PREFIX, formattedLine);
|
||||||
|
this.output.current.push(formattedLine);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.process.stderr?.on('data', (data) => {
|
||||||
|
const lines = data
|
||||||
|
.toString()
|
||||||
|
.trim()
|
||||||
|
.split('\n')
|
||||||
|
.filter((line) => line.length > 0);
|
||||||
|
lines.forEach((line) => {
|
||||||
|
const formattedLine = `[${new Date().toLocaleTimeString(
|
||||||
|
'nl-NL'
|
||||||
|
)}] [${fileName}] [ERROR] ${line}`;
|
||||||
|
if (LOG_OUTPUT) console.error(PREFIX, formattedLine);
|
||||||
|
this.output.current.push(formattedLine);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
if (
|
||||||
|
this.process == null ||
|
||||||
|
this.process?.killed ||
|
||||||
|
this.process?.exitCode != null ||
|
||||||
|
this.restartTriggered
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
this.setInfo('Running', '', 'RUNNING');
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatus() {
|
||||||
|
return {
|
||||||
|
state: this.state,
|
||||||
|
message: this.message,
|
||||||
|
error: this.error,
|
||||||
|
|
||||||
|
startTime: this.startTime,
|
||||||
|
output: this.output,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CameraRunnerState = 'RUNNING' | 'STOPPED' | 'STARTING' | 'PROBLEM';
|
||||||
9
tsconfig.json
Normal file
9
tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist",
|
||||||
|
"sourceMap": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user