UNPKG

8.51 kBJavaScriptView Raw
1/**
2 * Copyright 2013-2021 the PM2 project authors. All rights reserved.
3 * Use of this source code is governed by a license that
4 * can be found in the LICENSE file.
5 */
6
7/**
8 * Common Utilities ONLY USED IN ->DAEMON<-
9 */
10
11var fclone = require('fclone');
12var fs = require('fs');
13var cst = require('../constants.js');
14var waterfall = require('async/waterfall');
15var util = require('util');
16var url = require('url');
17var dayjs = require('dayjs');
18var findPackageJson = require('./tools/find-package-json')
19
20var Utility = module.exports = {
21 findPackageVersion : function(fullpath) {
22 var version
23
24 try {
25 version = findPackageJson(fullpath).next().value.version
26 } catch(e) {
27 version = 'N/A'
28 }
29 return version
30 },
31 getDate : function() {
32 return Date.now();
33 },
34 extendExtraConfig : function(proc, opts) {
35 if (opts.env && opts.env.current_conf) {
36 if (opts.env.current_conf.env &&
37 typeof(opts.env.current_conf.env) === 'object' &&
38 Object.keys(opts.env.current_conf.env).length === 0)
39 delete opts.env.current_conf.env
40
41 Utility.extendMix(proc.pm2_env, opts.env.current_conf);
42 delete opts.env.current_conf;
43 }
44 },
45 formatCLU : function(process) {
46 if (!process.pm2_env) {
47 return process;
48 }
49
50 var obj = Utility.clone(process.pm2_env);
51 delete obj.env;
52
53 return obj;
54 },
55 extend : function(destination, source){
56 if (!source || typeof source != 'object') return destination;
57
58 Object.keys(source).forEach(function(new_key) {
59 if (source[new_key] != '[object Object]')
60 destination[new_key] = source[new_key];
61 });
62
63 return destination;
64 },
65 // Same as extend but drop value with 'null'
66 extendMix : function(destination, source){
67 if (!source || typeof source != 'object') return destination;
68
69 Object.keys(source).forEach(function(new_key) {
70 if (source[new_key] == 'null')
71 delete destination[new_key];
72 else
73 destination[new_key] = source[new_key]
74 });
75
76 return destination;
77 },
78
79 whichFileExists : function(file_arr) {
80 var f = null;
81
82 file_arr.some(function(file) {
83 try {
84 fs.statSync(file);
85 } catch(e) {
86 return false;
87 }
88 f = file;
89 return true;
90 });
91 return f;
92 },
93 clone : function(obj) {
94 if (obj === null || obj === undefined) return {};
95 return fclone(obj);
96 },
97 overrideConsole : function(bus) {
98 if (cst.PM2_LOG_DATE_FORMAT && typeof cst.PM2_LOG_DATE_FORMAT == 'string') {
99 // Generate timestamp prefix
100 function timestamp(){
101 return `${dayjs(Date.now()).format('YYYY-MM-DDTHH:mm:ss')}:`;
102 }
103
104 var hacks = ['info', 'log', 'error', 'warn'], consoled = {};
105
106 // store console functions.
107 hacks.forEach(function(method){
108 consoled[method] = console[method];
109 });
110
111 hacks.forEach(function(k){
112 console[k] = function(){
113 if (bus) {
114 bus.emit('log:PM2', {
115 process : {
116 pm_id : 'PM2',
117 name : 'PM2',
118 rev : null
119 },
120 at : Utility.getDate(),
121 data : util.format.apply(this, arguments) + '\n'
122 });
123 }
124 // do not destroy variable insertion
125 arguments[0] && (arguments[0] = timestamp() + ' PM2 ' + k + ': ' + arguments[0]);
126 consoled[k].apply(console, arguments);
127 };
128 });
129 }
130 },
131 startLogging : function(stds, callback) {
132 /**
133 * Start log outgoing messages
134 * @method startLogging
135 * @param {} callback
136 * @return
137 */
138 // Make sure directories of `logs` and `pids` exist.
139 // try {
140 // ['logs', 'pids'].forEach(function(n){
141 // console.log(n);
142 // (function(_path){
143 // !fs.existsSync(_path) && fs.mkdirSync(_path, '0755');
144 // })(path.resolve(cst.PM2_ROOT_PATH, n));
145 // });
146 // } catch(err) {
147 // return callback(new Error('can not create directories (logs/pids):' + err.message));
148 // }
149
150 // waterfall.
151 var flows = [];
152 // types of stdio, should be sorted as `std(entire log)`, `out`, `err`.
153 var types = Object.keys(stds).sort(function(x, y){
154 return -x.charCodeAt(0) + y.charCodeAt(0);
155 });
156
157 // Create write streams.
158 (function createWS(io){
159 if(io.length != 1){
160 return false;
161 }
162 io = io[0];
163
164 // If `std` is a Stream type, try next `std`.
165 // compatible with `pm2 reloadLogs`
166 if(typeof stds[io] == 'object' && !isNaN(stds[io].fd)){
167 return createWS(types.splice(0, 1));
168 }
169
170 flows.push(function(next){
171 var file = stds[io];
172
173 // if file contains ERR or /dev/null, dont try to create stream since he dont want logs
174 if (!file || file.indexOf('NULL') > -1 || file.indexOf('/dev/null') > -1)
175 return next();
176
177 stds[io] = fs.createWriteStream(file, {flags: 'a'})
178 .once('error', next)
179 .on('open', function(){
180 stds[io].removeListener('error', next);
181
182 stds[io].on('error', function(err) {
183 console.error(err);
184 });
185
186 next();
187 });
188 stds[io]._file = file;
189 });
190 return createWS(types.splice(0, 1));
191 })(types.splice(0, 1));
192
193 waterfall(flows, callback);
194 },
195
196 /**
197 * Function parse the module name and returns it as canonic:
198 * - Makes the name based on installation filename.
199 * - Removes the Github author, module version and git branch from original name.
200 *
201 * @param {string} module_name
202 * @returns {string} Canonic module name (without trimed parts).
203 * @example Always returns 'pm2-slack' for inputs 'ma-zal/pm2-slack', 'ma-zal/pm2-slack#own-branch',
204 * 'pm2-slack-1.0.0.tgz' or 'pm2-slack@1.0.0'.
205 */
206 getCanonicModuleName: function(module_name) {
207 if (typeof module_name !== 'string') return null;
208 var canonic_module_name = module_name;
209
210 // Returns the module name from a .tgz package name (or the original name if it is not a valid pkg).
211 // Input: The package name (e.g. "foo.tgz", "foo-1.0.0.tgz", "folder/foo.tgz")
212 // Output: The module name
213 if (canonic_module_name.match(/\.tgz($|\?)/)) {
214 if (canonic_module_name.match(/^(.+\/)?([^\/]+)\.tgz($|\?)/)) {
215 canonic_module_name = canonic_module_name.match(/^(.+\/)?([^\/]+)\.tgz($|\?)/)[2];
216 if (canonic_module_name.match(/^(.+)-[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9_]+\.[0-9]+)?$/)) {
217 canonic_module_name = canonic_module_name.match(/^(.+)-[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9_]+\.[0-9]+)?$/)[1];
218 }
219 }
220 }
221
222 //pm2 install git+https://github.com/user/module
223 if(canonic_module_name.indexOf('git+') !== -1) {
224 canonic_module_name = canonic_module_name.split('/').pop();
225 }
226
227 //pm2 install https://github.com/user/module
228 if(canonic_module_name.indexOf('http') !== -1) {
229 var uri = url.parse(canonic_module_name);
230 canonic_module_name = uri.pathname.split('/').pop();
231 }
232
233 //pm2 install file:///home/user/module
234 else if(canonic_module_name.indexOf('file://') === 0) {
235 canonic_module_name = canonic_module_name.replace(/\/$/, '').split('/').pop();
236 }
237
238 //pm2 install username/module
239 else if(canonic_module_name.indexOf('/') !== -1) {
240 if (canonic_module_name.charAt(0) !== "@"){
241 canonic_module_name = canonic_module_name.split('/')[1];
242 }
243 }
244
245 //pm2 install @somescope/module@2.1.0-beta
246 if(canonic_module_name.lastIndexOf('@') > 0) {
247 canonic_module_name = canonic_module_name.substr(0,canonic_module_name.lastIndexOf("@"));
248 }
249
250 //pm2 install module#some-branch
251 if(canonic_module_name.indexOf('#') !== -1) {
252 canonic_module_name = canonic_module_name.split('#')[0];
253 }
254
255 if (canonic_module_name.indexOf('.git') !== -1) {
256 canonic_module_name = canonic_module_name.replace('.git', '');
257 }
258
259 return canonic_module_name;
260 },
261
262 checkPathIsNull: function(path) {
263 return path === 'NULL' || path === '/dev/null' || path === '\\\\.\\NUL';
264 },
265
266 generateUUID: function () {
267 var s = [];
268 var hexDigits = "0123456789abcdef";
269 for (var i = 0; i < 36; i++) {
270 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
271 }
272 s[14] = "4";
273 s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
274 s[8] = s[13] = s[18] = s[23] = "-";
275 return s.join("");
276 }
277
278};