1 | #!/usr/bin/env node
|
2 |
|
3 | const execSync = require('child_process').execSync;
|
4 | const args = require('minimist')(process.argv.slice(2));
|
5 | const chokidar = require('chokidar');
|
6 | const WebSocket = require('ws');
|
7 | const bytes = require('bytes');
|
8 | const queue = require('async/queue');
|
9 | const RawIPC = require('node-ipc').IPC;
|
10 | const ipc = new RawIPC();
|
11 | const pingClient = new RawIPC();
|
12 | const WebSocketServer = WebSocket.Server;
|
13 | const library = require("./src/library");
|
14 | const CONSTANTS = require("./constants");
|
15 |
|
16 | const BRACKET_END = CONSTANTS.BRACKET_END;
|
17 | const SERVER_PORT = CONSTANTS.SERVER_PORT;
|
18 | const APP_NAME = CONSTANTS.SERVER_APP_NAME;
|
19 |
|
20 | var watcherEnabled = true;
|
21 | var latestWriteData = "";
|
22 |
|
23 | pingClient.config.id = 'sfLibraryManagerPingClient';
|
24 | pingClient.config.encoding = "utf8";
|
25 |
|
26 | pingClient.config.logger = function () { };
|
27 |
|
28 | ipc.config.id = APP_NAME;
|
29 | ipc.config.retry = 5000;
|
30 | ipc.config.encoding = "utf8";
|
31 | ipc.config.maxConnections = 15;
|
32 | ipc.config.logger = function (a) { args.verbose && console.log(a) };
|
33 | let sockets = [];
|
34 | let wses = [];
|
35 | let subscribersForRead = [];
|
36 | let watcher = null;
|
37 | let _libraryFolder = CONSTANTS.LIBRARY_DIRECTORY;
|
38 |
|
39 | process.on("uncaughtException", logError);
|
40 | process.on('unhandledRejection', logError);
|
41 |
|
42 | if (args.restart) {
|
43 | try {
|
44 | execSync(`fuser -k ${SERVER_PORT}`);
|
45 | }
|
46 | catch (ex) {
|
47 |
|
48 | }
|
49 | }
|
50 |
|
51 | function logError(reason) {
|
52 | console.error("├─── ! ☠️ ! ──────── LibraryManager ───── Unhandled Error ─────────────────────────┤\n" +
|
53 | reason.stack ? reason.stack.toString("utf8") : reason.toString("utf8"));
|
54 | }
|
55 |
|
56 | function initLibraryTask(data, cb) {
|
57 | library.createLibDirFile(_libraryFolder, data.initialLibraryData, e => {
|
58 | e && console.error(e);
|
59 | cb && cb(e);
|
60 | watch(_libraryFolder);
|
61 | });
|
62 | }
|
63 |
|
64 | function watch(libraryFolder) {
|
65 | watcher && watcher.close();
|
66 | watcher = chokidar.watch(`${libraryFolder}/*.!(pgx)cpx`, {
|
67 | ignoreInitial: true
|
68 | });
|
69 | var readCounter = 0;
|
70 | watcher.on("all", (e, filename) => {
|
71 | if (!watcherEnabled)
|
72 | return;
|
73 | ++readCounter;
|
74 | setTimeout(() => {
|
75 | --readCounter;
|
76 | if (readCounter !== 0)
|
77 | return;
|
78 | console.log("Watcher ..> ", e, " . ", filename);
|
79 | readTask((e, res) => {
|
80 | notifyBrowsers("Watcher -> ReadDone: ", e, res);
|
81 | notifySubscribers("Watcher -> ReadDone notify subscriber : ", e, res);
|
82 | });
|
83 | }, 800);
|
84 | });
|
85 | }
|
86 |
|
87 | function readTask(cb) {
|
88 | cb = typeof cb === "function" ? cb : function () { };
|
89 | library.read(_libraryFolder, function (err, comps) {
|
90 | if (err) {
|
91 | console.error(err);
|
92 | cb(err);
|
93 | }
|
94 | else {
|
95 | return cb(null, JSON.stringify(comps));
|
96 | }
|
97 | });
|
98 | }
|
99 |
|
100 | function writeTask(data, _cb) {
|
101 | const rawData = data.rawData || data;
|
102 | if (latestWriteData === rawData) {
|
103 | console.log("Library already saved!");
|
104 | return _cb(null);
|
105 | }
|
106 | var cb = err => {
|
107 | if (err)
|
108 | return _cb(err);
|
109 | latestWriteData = rawData;
|
110 | _cb();
|
111 | };
|
112 | let componentsWithChildren = data.parsedData;
|
113 | if (!data.parsedData) {
|
114 | try {
|
115 | componentsWithChildren = JSON.parse(data);
|
116 | }
|
117 | catch (e) {
|
118 | console.error(e);
|
119 | return cb(e);
|
120 | }
|
121 | }
|
122 | library.saveComponents(_libraryFolder, componentsWithChildren, err => {
|
123 | if (err) {
|
124 | console.error(err);
|
125 | return cb(err);
|
126 | }
|
127 | notifySubscribersAndwriteUninitializedComp(componentsWithChildren, cb);
|
128 | });
|
129 | }
|
130 |
|
131 | function notifySubscribersAndwriteUninitializedComp(componentsWithChildren, cb) {
|
132 | var unInitializedComps = [];
|
133 | componentsWithChildren.forEach(comps => {
|
134 | if (!comps[0].initialized) {
|
135 | console.log("Uninitialized comp: ", comps[0].props.name);
|
136 | unInitializedComps.push(comps);
|
137 | }
|
138 | });
|
139 | library.getLibraryPageWithChildren(_libraryFolder, componentsWithChildren, (err, comps) => {
|
140 | if (comps) {
|
141 | notifySubscribers("ReceivedNewData -> notify subscribers :", err, JSON.stringify(comps));
|
142 | }
|
143 | if (unInitializedComps.length) {
|
144 | unInitializedComps.forEach(comps => comps[0].initialized = true);
|
145 | notifyBrowsers("Initialized components -> ", err, JSON.stringify(comps));
|
146 | library.saveComponents(_libraryFolder, unInitializedComps, cb, true);
|
147 | }
|
148 | else {
|
149 | cb();
|
150 | }
|
151 | });
|
152 | }
|
153 |
|
154 | function notifySubscribers(title, err, res) {
|
155 | subscribersForRead.forEach(s => {
|
156 | console.log(title, s.name, " ", bytes(res ? res.length : 0, { unitSeparator: ' ' }), " send.");
|
157 | ipc.server.emit(
|
158 | s,
|
159 | err ? 'error' : 'read',
|
160 | err ? err.toString() : res
|
161 | );
|
162 | });
|
163 | }
|
164 |
|
165 | function notifyBrowsers(title, err, res) {
|
166 | sockets.forEach(s => {
|
167 | console.log(title, s.name, " ", bytes(res ? res.length : 0, { unitSeparator: ' ' }), " send.");
|
168 | ipc.server.emit(
|
169 | s,
|
170 | err ? 'error' : 'read',
|
171 | err ? err.toString() : res
|
172 | );
|
173 | });
|
174 | if (!err) {
|
175 | wses.forEach((ws) => {
|
176 | ws.send(res);
|
177 | ws.send(`\r\n${BRACKET_END}`);
|
178 | });
|
179 | }
|
180 | }
|
181 |
|
182 | var writeTaskQueue = queue((data, next) => {
|
183 | watcherEnabled = false;
|
184 | writeTask(data, next);
|
185 | }, 1);
|
186 |
|
187 | writeTaskQueue.drain = () => setTimeout(() => (watcherEnabled = true), 1000);
|
188 |
|
189 | ipc.serveNet(
|
190 | SERVER_PORT,
|
191 | () => {
|
192 | console.log(`Smartface libraryManager (${SERVER_PORT}) is up and running...`);
|
193 | ipc.server.on(
|
194 | 'writeTask',
|
195 | (data, socket) => {
|
196 | console.log("WriteTask: ", bytes(data.length, { unitSeparator: ' ' }));
|
197 | writeTaskQueue.push(data);
|
198 | }
|
199 | );
|
200 |
|
201 | ipc.server.on(
|
202 | 'readTask',
|
203 | (data, socket) => {
|
204 | console.log("ReadTask ", data.name);
|
205 | _libraryFolder = _libraryFolder || data.libraryFolder;
|
206 | socket.name = data && data.name;
|
207 | readTask((e, res) => {
|
208 | ipc.server.emit(
|
209 | socket,
|
210 | 'read',
|
211 | res
|
212 | );
|
213 | });
|
214 | }
|
215 | );
|
216 | ipc.server.on(
|
217 | 'subscribeForRead',
|
218 | (data, socket) => {
|
219 | console.log("SubscribeForRead: ", data.name);
|
220 | _libraryFolder = _libraryFolder || data.libraryFolder;
|
221 | socket.name = data && data.name;
|
222 | subscribersForRead.push(socket);
|
223 | readTask((e, res) => {
|
224 | ipc.server.emit(
|
225 | socket,
|
226 | 'read',
|
227 | res
|
228 | );
|
229 | });
|
230 | }
|
231 | );
|
232 |
|
233 | ipc.server.on(
|
234 | 'init',
|
235 | (data, socket) => {
|
236 | console.log("Init ", data.name);
|
237 | socket.name = data && data.name;
|
238 | _libraryFolder = data.libraryFolder;
|
239 | sockets.push(socket);
|
240 | initLibraryTask(data, e => {
|
241 | readTask((e, res) => {
|
242 | console.log("ReadDone: ", socket.name, " ", bytes(res ? res.length : 0, { unitSeparator: ' ' }));
|
243 | ipc.server.emit(
|
244 | socket,
|
245 | 'read',
|
246 | res
|
247 | );
|
248 | });
|
249 | });
|
250 | }
|
251 | );
|
252 |
|
253 | ipc.server.on(
|
254 | "ping",
|
255 | (data, socket) => {
|
256 | ipc.server.emit(socket, 'ping', "OK");
|
257 | });
|
258 |
|
259 | ipc.server.on(
|
260 | 'socket.disconnected',
|
261 | (socket) => {
|
262 | sockets = sockets.filter(s => s !== socket);
|
263 | subscribersForRead = subscribersForRead.filter(s => s !== socket);
|
264 | socket.name && console.log('DISCONNECTED ', socket.name);
|
265 | }
|
266 | );
|
267 | }
|
268 | );
|
269 |
|
270 | ipc.server.on(
|
271 | 'error',
|
272 | (err) => {
|
273 | console.log('Got an ERROR!', err);
|
274 | }
|
275 | );
|
276 | ipc.server.start();
|
277 |
|
278 |
|
279 | function handleWsTask(parsedData, ws) {
|
280 | if (parsedData.task === 'init') {
|
281 | initLibraryTask(parsedData, () => {
|
282 | readTask((e, res) => {
|
283 | ws.send(res);
|
284 | ws.send(`\r\n${BRACKET_END}`);
|
285 | });
|
286 | })
|
287 | }
|
288 | }
|
289 |
|
290 | const wsServer = new WebSocketServer({ port: CONSTANTS.SERVER_WS_PORT });
|
291 | console.log(`Smartface libraryManager (${CONSTANTS.SERVER_WS_PORT}) ws server is up and running...`);
|
292 |
|
293 | wsServer.on('connection', (ws) => {
|
294 |
|
295 | wses.push(ws);
|
296 | let msgData = '';
|
297 | ws.on('message', (msg) => {
|
298 | msgData += msg;
|
299 | console.log("RECEIVED:> ", msg);
|
300 | if (msgData.endsWith(BRACKET_END)) {
|
301 | const splitted = msgData.split(BRACKET_END);
|
302 | const rawData = splitted[splitted.length - 2];
|
303 | const parsedData = JSON.parse(rawData);
|
304 | if (parsedData.task) {
|
305 | handleWsTask(parsedData, ws);
|
306 | } else {
|
307 | writeTaskQueue.push({ rawData, parsedData });
|
308 | }
|
309 | msgData = '';
|
310 | }
|
311 | });
|
312 |
|
313 | ws.on('close', () => {
|
314 | wses = wses.filter(w => w !== ws);
|
315 | console.info('ws on Close');
|
316 | });
|
317 |
|
318 | });
|
319 |
|
320 | var counter = 0;
|
321 | var aliveCounter = 0;
|
322 | setInterval(_ => {
|
323 | pingClient.connectToNet(
|
324 | APP_NAME,
|
325 | CONSTANTS.SERVER_PORT,
|
326 | function () {
|
327 | pingClient.of.sfLibraryManager.on(
|
328 | 'connect',
|
329 | _ => {
|
330 | ((++aliveCounter) % 15 == 0)
|
331 | counter = 0;
|
332 | pingClient.disconnect(APP_NAME);
|
333 | }
|
334 | );
|
335 | pingClient.of.sfLibraryManager.on(
|
336 | 'error',
|
337 | (data) => {
|
338 | if (++counter < 3)
|
339 | return;
|
340 | process.stderr.write(data.toString());
|
341 | process.exit(1);
|
342 | }
|
343 | );
|
344 | }
|
345 | );
|
346 | }, 5000);
|