UNPKG

5.39 kBJavaScriptView Raw
1//
2// index.js
3// Resitail
4//
5// Copyright 2017-present Muflihun Labs
6//
7// Author: @abumusamq
8//
9// Licensed under the Apache License, Version 2.0 (the "License");
10// you may not use this file except in compliance with the License.
11// You may obtain a copy of the License at
12//
13// http://www.apache.org/licenses/LICENSE-2.0
14//
15// Unless required by applicable law or agreed to in writing, software
16// distributed under the License is distributed on an "AS IS" BASIS,
17// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18// See the License for the specific language governing permissions and
19// limitations under the License.
20//
21
22"use strict";
23
24const fs = require('fs');
25const net = require('net');
26const isEmpty = require('lodash.isempty');
27const includes = require('lodash.includes');
28const tail = require('./lib/tail');
29const residue_crypt = require('./lib/residue_crypt');
30const proc = require('./lib/option_parser');
31
32proc.parse(process.argv);
33
34if (proc.config === false) {
35 console.error('ERR: No config file provided\nUsage: resitail --config <config>\n');
36 process.exit();
37}
38
39const config = JSON.parse(fs.readFileSync(proc.config));
40
41if (!config.residue_config) {
42 console.error('Invalid configuration. Missing: residue_config');
43 process.exit();
44}
45
46const hooks = [];
47
48config.hooks.forEach((h) => {
49 const hook = require(h.path);
50 try {
51 if (h.enabled) {
52 const hookObj = hook(h.config);
53 hooks.push(hookObj);
54 hookObj.name = h.name;
55 console.log(`Hooked [${h.name}]`);
56 }
57 } catch (e) {
58 console.error(`Error while loading hook ${h.name}: ${e}`);
59 process.exit();
60 }
61});
62
63if (isEmpty(hooks)) {
64 console.error('No hooks enabled');
65 process.exit();
66}
67
68const residue_config = JSON.parse(fs.readFileSync(config.residue_config));
69const crypt = residue_crypt(residue_config);
70const packet_delimiter = '\r\n\r\n';
71
72const sendData = (evt, type, line, controller) => {
73 if (controller) {
74 hooks.forEach((hook) => {
75 if (hook.config.channels.to_logger) {
76 if (!includes(hook.config.loggers_ignore_list, controller.logger_id)) {
77 hook.send({
78 line,
79 'channel': 'logger',
80 'channel_name': controller.logger_id,
81 'client_id': controller.client_id
82 });
83 }
84 }
85 if (hook.config.channels.to_client) {
86 if (!includes(hook.config.clients_ignore_list, controller.client_id)) {
87 hook.send({
88 line,
89 'channel': 'client',
90 'channel_name': controller.client_id,
91 'logger_id': controller.logger_id
92 });
93 }
94 }
95 });
96 }
97}
98
99if (isEmpty(residue_config.known_clients)) {
100 console.error('ERR: No known clients specified in residue config');
101 process.exit();
102}
103
104const admin_socket = new net.Socket();
105admin_socket.connect(residue_config.admin_port, '127.0.0.1');
106
107const active_processes = [];
108
109const startTail = (clientId) => {
110 console.log(`Start client [${clientId}]`);
111 const request = {
112 _t: parseInt((new Date()).getTime() / 1000, 10),
113 type: 5,
114 client_id: clientId,
115 };
116
117 const encryptedRequest = crypt.encrypt(request);
118 admin_socket.write(encryptedRequest, 'utf-8');
119}
120
121const processResponse = (response) => {
122 if (response.trim().length === 0) {
123 return;
124 }
125 let decrypted = '<failed>';
126 try {
127 decrypted = crypt.decrypt(response);
128 const resp = JSON.parse(decrypted);
129 for (let i = 0; i < resp.length; ++i) {
130 const list = resp[i].files;
131
132 const controller = {
133 logger_id: resp[i].logger_id,
134 client_id: resp[i].client_id,
135 };
136
137 const files = [];
138
139 for (let j = 0; j < list.length; ++j) {
140 if (fs.existsSync(list[j])) {
141 files.push(list[j]);
142 }
143 }
144
145 const tail_process = tail(files, {
146 buffer: 0,
147 });
148
149 tail_process.on('line', txt => {
150 sendData('resitail:line', 'log', txt, controller);
151 });
152
153 tail_process.on('info', txt => {
154 sendData('resitail:line', 'info', txt, controller);
155 });
156
157 tail_process.on('error', error => {
158 sendData('resitail:err', 'err', error, controller);
159 });
160
161 if (!active_processes[controller.client_id]) {
162 active_processes[controller.client_id] = [];
163 }
164
165 active_processes[controller.client_id].push(tail_process);
166 }
167 } catch (err) {
168 sendData('resitail:err', 'err', `error occurred, details: ${decrypted}`);
169 console.log(err);
170 }
171}
172
173admin_socket.on('data', (data, cb) => {
174 const responses = data.toString().split(packet_delimiter);
175 for (let i = 0; i < responses.length; ++i) {
176 processResponse(responses[i] + packet_delimiter);
177 }
178});
179
180// start all the tails
181for (let i = 0; i < residue_config.known_clients.length; ++i) {
182 startTail(residue_config.known_clients[i].client_id);
183}