|
11 | 11 | * Please see the LICENSE included with this distribution for details.
|
12 | 12 | */
|
13 | 13 | 'use strict';
|
| 14 | +var analyticsEvents = []; |
14 | 15 |
|
15 |
| -var afs = require('./fs'), |
16 |
| - auth = require('./auth'), |
17 |
| - async = require('async'), |
18 |
| - colors = require('colors'), |
19 |
| - fork = require('child_process').fork, // eslint-disable-line security/detect-child-process |
20 |
| - fs = require('fs-extra'), |
21 |
| - getOSInfo = require('./environ').getOSInfo, |
22 |
| - i18n = require('./i18n')(__dirname), |
23 |
| - mix = require('./util').mix, |
24 |
| - path = require('path'), |
25 |
| - request = require('request'), |
26 |
| - timestamp = require('./time').timestamp, |
27 |
| - urlEncode = require('./net').urlEncode, |
28 |
| - uuid = require('uuid'), |
29 |
| - __ = i18n.__, |
30 |
| - __n = i18n.__n, |
31 |
| - |
32 |
| - analyticsEvents = [], |
33 |
| - url = 'https://api.appcelerator.com/p/v1/app-track', |
34 |
| - sessionTimeout = 60 * 60 * 1000; // 1 hour |
35 |
| - |
36 |
| -/** |
37 |
| - * An array of events that are queued up to be sent. |
38 |
| - */ |
39 | 16 | exports.events = analyticsEvents;
|
40 | 17 |
|
41 |
| -/** |
42 |
| - * Adds an event to the array of events to be sent to the Appcelerator cloud. |
43 |
| - * @param {String} name - The name of the event |
44 |
| - * @param {*} data - The data payload |
45 |
| - * @param {String} type - The event type |
46 |
| - */ |
47 |
| -exports.addEvent = function addEvent(name, data, type) { |
48 |
| - analyticsEvents.push({ |
49 |
| - id: uuid.v4(), |
50 |
| - type: type || 'app.feature', |
51 |
| - name: name, |
52 |
| - ts: timestamp(), |
53 |
| - data: data |
54 |
| - }); |
55 |
| -}; |
56 |
| - |
57 |
| -/** |
58 |
| - * Forks another node process with this file, then sends the child a message |
59 |
| - * containing the queue of events that need to be sent. |
60 |
| - * @param {Object} args - Send arguments |
61 |
| - * @param {String} args.appId - The Titanium CLI id |
62 |
| - * @param {String} args.appName - The Titanium CLI name |
63 |
| - * @param {String} args.appGuid - The Titanium CLI guid |
64 |
| - * @param {String} args.titaniumHomeDir - The Titanium home directory where the session files are stored |
65 |
| - * @param {Boolean} [args.rejectUnauthorized=false] - Rejects unauthorized certificates |
66 |
| - * @param {String} args.analyticsUrl - An alternative URL to send analytics to instead of the default |
67 |
| - * @param {String} args.version - The Titanium CLI version |
68 |
| - * @param {String} args.deployType - The deploy type which is always production |
69 |
| - * @param {String} args.httpProxyServer - The proxy server to use |
70 |
| - * @param {String} args.uid - If logged in, the user's id |
71 |
| - * @param {String} args.guid - If logged in, the user's guid |
72 |
| - * @param {String} args.email - If logged in, the user's email address |
73 |
| - * @param {Object} [args.logger] - The logger instance |
74 |
| - * @param {Boolean} [args.debug=false] - Displays debug logging when true |
75 |
| - * @param {Function} [callback(err, childProcess)] - A function to call after the analytics have been saved, but not sent |
76 |
| - */ |
77 |
| -exports.send = function send(args, callback) { |
78 |
| - args = args || {}; |
79 |
| - var events = [].concat(analyticsEvents); |
80 |
| - analyticsEvents = []; |
81 |
| - |
82 |
| - var logger = { |
83 |
| - debug: args.debug && args.logger && args.logger.debug || function () {}, |
84 |
| - log: args.debug && args.logger && args.logger.log || function () {} |
85 |
| - }; |
86 |
| - |
87 |
| - var titaniumHomeDir; |
88 |
| - if (args.titaniumHomeDir) { |
89 |
| - titaniumHomeDir = afs.resolvePath(args.titaniumHomeDir); |
90 |
| - } else if (args.directory) { |
91 |
| - titaniumHomeDir = afs.resolvePath(args.directory); |
92 |
| - } |
93 |
| - if (!titaniumHomeDir || !fs.existsSync(titaniumHomeDir)) { |
94 |
| - titaniumHomeDir = afs.resolvePath('~/.titanium'); |
95 |
| - } |
96 |
| - |
97 |
| - logger.debug(__('Processing analytics')); |
98 |
| - logger.debug(__('Titanium home directory: %s', titaniumHomeDir.cyan)); |
99 |
| - |
100 |
| - // asynchronously get the machine id (mid) and os info |
101 |
| - async.parallel({ |
102 |
| - mid: function (cb) { |
103 |
| - auth.getMID(titaniumHomeDir, function (mid) { |
104 |
| - logger.debug(__('Machine ID: %s', String(mid).cyan)); |
105 |
| - cb(null, mid); |
106 |
| - }); |
107 |
| - }, |
108 |
| - |
109 |
| - osinfo: function (cb) { |
110 |
| - getOSInfo(function (info) { |
111 |
| - cb(null, info); |
112 |
| - }); |
113 |
| - } |
114 |
| - }, function (err, results) { |
115 |
| - var eventsDir = path.join(titaniumHomeDir, 'events'), |
116 |
| - sessionFile = path.join(titaniumHomeDir, 'analytics_session.json'), |
117 |
| - sid, |
118 |
| - sessionExpiration, |
119 |
| - now = Date.now(), |
120 |
| - restoredPreviousSession = false, |
121 |
| - mid = results.mid; |
122 |
| - |
123 |
| - // make sure the home directory exists |
124 |
| - if (!fs.existsSync(titaniumHomeDir)) { |
125 |
| - logger.debug(__('Creating Titanium home directory')); |
126 |
| - fs.mkdirsSync(titaniumHomeDir); |
127 |
| - } |
128 |
| - |
129 |
| - // make sure the events directory exists |
130 |
| - if (!fs.existsSync(eventsDir)) { |
131 |
| - logger.debug(__('Creating analytics events directory')); |
132 |
| - fs.mkdirsSync(eventsDir); |
133 |
| - } |
134 |
| - |
135 |
| - function writeEvent(type, event, id, ts, data) { |
136 |
| - var file = path.join(eventsDir, (new Date()).toISOString().replace(/:/g, '-') + '_' + (Math.floor(Math.random() * 9000) + 1000) + '.json'), |
137 |
| - json = mix({ |
138 |
| - event: event, |
139 |
| - type: type, |
140 |
| - sid: sid, |
141 |
| - guid: args.appGuid, |
142 |
| - mid: mid, |
143 |
| - mac_addr: '', |
144 |
| - ip: '', |
145 |
| - creator_user_id: null, |
146 |
| - app_name: args.appName, |
147 |
| - app_version: args.version, |
148 |
| - version: args.version, |
149 |
| - tz: (new Date()).getTimezoneOffset(), |
150 |
| - ver: '3', |
151 |
| - un: args.email, |
152 |
| - data: JSON.stringify(data), |
153 |
| - id: id || uuid.v4() |
154 |
| - }, results.osinfo); |
155 |
| - |
156 |
| - logger.debug(__('Writing event to file: %s', file.cyan)); |
157 |
| - logger.log(JSON.stringify(json, null, '\t').grey); |
158 |
| - |
159 |
| - fs.writeFileSync(file, JSON.stringify(json)); |
160 |
| - } |
161 |
| - |
162 |
| - // do we have a valid session |
163 |
| - if (fs.existsSync(sessionFile)) { |
164 |
| - try { |
165 |
| - logger.debug(__('Reading session file: %s', sessionFile.cyan)); |
166 |
| - var analyticsSession = JSON.parse(fs.readFileSync(sessionFile)); |
167 |
| - |
168 |
| - sid = analyticsSession.sid; |
169 |
| - sessionExpiration = analyticsSession.sessionExpiration; |
170 |
| - |
171 |
| - // if the expiration has expired, create a new one |
172 |
| - if (sid && sessionExpiration && sessionExpiration > now) { |
173 |
| - logger.debug(__('Session is valid')); |
174 |
| - restoredPreviousSession = true; |
175 |
| - } else { |
176 |
| - // add the ti.end event |
177 |
| - logger.debug(__('Session expired, adding ti.end event')); |
178 |
| - writeEvent('ti.end', 'ti.end', null, null); |
179 |
| - } |
180 |
| - } catch (e) {} // file was malformed, treat as if a new session |
181 |
| - } |
182 |
| - |
183 |
| - // if the previous session was not restored, create a new one |
184 |
| - if (!restoredPreviousSession) { |
185 |
| - logger.debug(__('Creating new session')); |
186 |
| - |
187 |
| - // need to generate a new session id |
188 |
| - fs.writeFileSync(sessionFile, JSON.stringify({ |
189 |
| - mid: mid, |
190 |
| - sid: sid = uuid.v4(), |
191 |
| - sessionExpiration: sessionExpiration = now + sessionTimeout |
192 |
| - })); |
| 18 | +exports.addEvent = function addEvent(_name, _data, _type) {}; |
193 | 19 |
|
194 |
| - logger.debug(__('Adding ti.start event')); |
195 |
| - writeEvent('ti.start', 'ti.start', null, null, null); |
196 |
| - } |
197 |
| - |
198 |
| - // write the events to disk |
199 |
| - logger.debug(__n('Adding %%s event', 'Adding %%s events', events.length, String(events.length).cyan)); |
200 |
| - events.forEach(function (evt) { |
201 |
| - writeEvent(evt.type, evt.name, evt.id, evt.ts, evt.data); |
202 |
| - }); |
203 |
| - |
204 |
| - // make sure we save the latest session expiration |
205 |
| - logger.debug(__('Updating session file')); |
206 |
| - fs.writeFileSync(sessionFile, JSON.stringify({ |
207 |
| - mid: mid, |
208 |
| - sid: sid, |
209 |
| - sessionExpiration: (new Date()).getTime() + sessionTimeout |
210 |
| - })); |
211 |
| - |
212 |
| - logger.debug(__('Forking analytics send process') + '\n'); |
213 |
| - |
214 |
| - var child = fork(module.filename); |
215 |
| - // note: the message cannot exceed 8,192 bytes |
216 |
| - child.send({ |
217 |
| - debug: args.debug && args.logger && !args.logger.silent, |
218 |
| - proxy: args.httpProxyServer, |
219 |
| - rejectUnauthorized: args.rejectUnauthorized, |
220 |
| - titaniumHomeDir: titaniumHomeDir, |
221 |
| - url: args.analyticsUrl || url |
222 |
| - }); |
223 |
| - child.unref(); |
224 |
| - |
225 |
| - callback && callback(null, child); |
226 |
| - }); |
| 20 | +exports.send = function send(_args, _callback) { |
| 21 | + _callback(null, null); |
227 | 22 | };
|
228 |
| - |
229 |
| -/** |
230 |
| - * When send() is called, fork() is called and a new Node.js instance will |
231 |
| - * trigger the "message" event where the event queue is sent to the Appcelerator |
232 |
| - * cloud servers. |
233 |
| - * @param {Object} args - Send arguments |
234 |
| - * @param {Boolean} [args.debug=false] - Displays debug logging when true |
235 |
| - * @param {String} args.proxy - The proxy server to use |
236 |
| - * @param {Boolean} [args.rejectUnauthorized=false] - Rejects unauthorized certificates |
237 |
| - * @param {String} args.titaniumHomeDir - The Titanium home directory where the session files are stored |
238 |
| - * @param {String} args.url - The URL to send analytics to |
239 |
| - */ |
240 |
| -process.on('message', function (args) { |
241 |
| - process.disconnect(); |
242 |
| - |
243 |
| - if (!args || !args.titaniumHomeDir || !args.url) { |
244 |
| - return; |
245 |
| - } |
246 |
| - |
247 |
| - // faux logger |
248 |
| - var logger = { |
249 |
| - debug: args.debug && function () { console.log.apply(null, [ '[DEBUG]'.magenta ].concat(Array.prototype.slice.call(arguments))); } || function () {}, |
250 |
| - log: args.debug && console.log || function () {} |
251 |
| - }; |
252 |
| - |
253 |
| - logger.log('\n'); |
254 |
| - logger.debug(__('Analytics send process')); |
255 |
| - |
256 |
| - var eventsDir = path.join(args.titaniumHomeDir, 'events'), |
257 |
| - pidFile = path.join(args.titaniumHomeDir, 'analytics.pid'); |
258 |
| - |
259 |
| - logger.debug(__('Analytics URL: %s', args.url.cyan)); |
260 |
| - logger.debug(__('Titanium home directory: %s', args.titaniumHomeDir.cyan)); |
261 |
| - logger.debug(__('Analytics events directory: %s', eventsDir.cyan)); |
262 |
| - logger.debug(__('pid file: %s', pidFile.cyan)); |
263 |
| - |
264 |
| - // check if there's already a pid |
265 |
| - while (fs.existsSync(pidFile)) { |
266 |
| - try { |
267 |
| - var pid = parseInt(fs.readFileSync(pidFile).toString().split('\n').shift()); |
268 |
| - logger.debug(__('pid file exists, check if pid %s is running', String(pid).cyan)); |
269 |
| - process.kill(pid, 0); |
270 |
| - |
271 |
| - // already running, exit |
272 |
| - logger.debug(__('Another analytics send process is running, exiting')); |
273 |
| - process.exit(0); |
274 |
| - } catch (e) { |
275 |
| - // not running, continue! |
276 |
| - logger.debug(__('Stale pid file found, continuing')); |
277 |
| - break; |
278 |
| - } |
279 |
| - } |
280 |
| - |
281 |
| - // write the pid file |
282 |
| - logger.debug(__('Writing new pid file: %s', String(process.pid).cyan)); |
283 |
| - fs.writeFileSync(pidFile, String(process.pid)); |
284 |
| - |
285 |
| - // send the analytics events, if any |
286 |
| - var files = fs.readdirSync(eventsDir); |
287 |
| - logger.debug(__n('Found %%s event', 'Found %%s events', files.length, String(files.length).cyan)); |
288 |
| - |
289 |
| - async.whilst( |
290 |
| - function (cb) { |
291 |
| - return cb(null, files.length !== 0); |
292 |
| - }, |
293 |
| - function (next) { |
294 |
| - var filename = files.shift(), |
295 |
| - file = path.join(eventsDir, filename), |
296 |
| - payload = null; |
297 |
| - |
298 |
| - if (!fs.existsSync(file) || !/\.json$/.test(filename)) { |
299 |
| - return next(); |
300 |
| - } |
301 |
| - |
302 |
| - try { |
303 |
| - payload = JSON.parse(fs.readFileSync(file)); |
304 |
| - } catch (ex) {} |
305 |
| - |
306 |
| - if (!payload) { |
307 |
| - fs.unlinkSync(file); |
308 |
| - return next(); |
309 |
| - } |
310 |
| - |
311 |
| - var params = { |
312 |
| - uri: args.url, |
313 |
| - method: 'POST', |
314 |
| - proxy: args.proxy || undefined, |
315 |
| - rejectUnauthorized: args.rejectUnauthorized === undefined ? true : !!args.rejectUnauthorized, |
316 |
| - body: urlEncode(payload) |
317 |
| - }; |
318 |
| - |
319 |
| - logger.debug(__('Sending event: %s', file.cyan)); |
320 |
| - logger.log(JSON.stringify(params, null, '\t').grey); |
321 |
| - |
322 |
| - request(params, function (error, response, body) { |
323 |
| - if (error) { |
324 |
| - logger.debug(__('Error sending event:')); |
325 |
| - logger.debug(error); |
326 |
| - } else if (response.statusCode === 204) { |
327 |
| - logger.debug(__('Event sent successfully')); |
328 |
| - if (fs.existsSync(file)) { |
329 |
| - logger.debug(__('Removing event: %s', file.cyan)); |
330 |
| - fs.unlinkSync(file); |
331 |
| - } |
332 |
| - } else { |
333 |
| - logger.debug(__('Event was not sent successfully')); |
334 |
| - logger.debug(__('Expected HTTP status code %s, got %s', 204, String(response.statusCode).cyan)); |
335 |
| - } |
336 |
| - next(); |
337 |
| - }); |
338 |
| - }, |
339 |
| - function () { |
340 |
| - // remove the pid file |
341 |
| - logger.debug(__('Deleting pid file: %s', pidFile.cyan)); |
342 |
| - fs.existsSync(pidFile) && fs.unlinkSync(pidFile); |
343 |
| - |
344 |
| - logger.debug(__('All events processed')); |
345 |
| - logger.log(); |
346 |
| - logger.debug(__('Press ENTER to redraw prompt')); |
347 |
| - logger.log(); |
348 |
| - } |
349 |
| - ); |
350 |
| -}); |
0 commit comments