Skip to content

Commit 6394847

Browse files
committed
Merge branch 'master' into next
# Conflicts: # api/parts/mgmt/apps.js # plugins/server-stats/api/api.js
2 parents 769c3ee + dff88b9 commit 6394847

File tree

57 files changed

+2912
-396
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2912
-396
lines changed

CHANGELOG.md

+49
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,52 @@
1+
## Version 18.08.2
2+
3+
**Fixes**
4+
5+
* [crashes] improved type check
6+
* [ui] fixed "Email value too long" issue.
7+
* [slipping-away-users] fix style issue
8+
* [crashes] fix escaping in crash error and stacktrace
9+
* [events] updated compare_arrays function for events to have more checks if both passed are arrays
10+
* [install] fix permission issue
11+
* [systemlogs] fixed system logs plugin table sorting issue
12+
* [push] Fix for upload of APN credentials from windows / no mime-aware systems
13+
* [config] fixes for relative path changes
14+
* [db]fixed replacement of db name in mongodb connection string
15+
* [push] Fix for race condition in message status updates
16+
* [frontend] fix title if not available
17+
* [data-migration] log redirect url in logger
18+
* [logger] improve info column formatting
19+
* [ui] drop down fix
20+
* [api] deeper escaping of objects
21+
* [api] more error checks and handling
22+
* [api] check correctly for finished none http requests
23+
* [api] fixes for handling unparsable period
24+
* [api] regular expression checks
25+
* [security] more cross site scripting preventions
26+
* [populator] fixed campaign session issue for web apps in populator
27+
* [nginx] remove server flag
28+
* [feedback] device_id fix and script for correcting data
29+
* [frontend] fix not using data on init for today period
30+
* [frontend] correctly genersate ticks for month buckets
31+
* [appmanagement] load configuration on new app(on new server)
32+
* [push] Deny APN app settings update if no file is selected
33+
34+
**Enterprise fixes**
35+
36+
* [push] Approver update
37+
* [drill] correctly check projection key result type
38+
* [drill] fix filter render bug
39+
* [revenue] use user estimation correction
40+
* [drill] fix switching bucket UI
41+
42+
**New Features**
43+
* [api] support for multiple errors message
44+
45+
**New Enterprise Features**
46+
* [drill] added no_map param to display plain country data
47+
* [dashboard] disabling sharing dashboards
48+
49+
150
## Version 18.08.1
251

352
**Fixes**

Gruntfile.js

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ module.exports = function(grunt) {
113113
'frontend/express/public/javascripts/utils/webfont.js',
114114
'frontend/express/public/javascripts/utils/selectize.min.js',
115115
'frontend/express/public/javascripts/utils/vue.min.js',
116+
'frontend/express/public/javascripts/utils/jquery.xss.js',
116117
'frontend/express/public/javascripts/countly/countly.common.js'
117118

118119
],

README.md

+48-14
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,51 @@
11

2-
# Countly Analytics [![Build Status](https://api.travis-ci.org/Countly/countly-server.png?branch=master)](https://travis-ci.org/Countly/countly-server) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/98c2726f2d734697a5f1ac0d453f0a06)](https://app.codacy.com/app/ar2rsawseen/countly-server?utm_source=github.com&utm_medium=referral&utm_content=Countly/countly-server&utm_campaign=Badge_Grade_Dashboard) [![Install Countly on DigitalOcean](https://do.count.ly/button.svg?v2)](http://do.count.ly)
2+
<h2 align="center"> Countly Analytics </h2>
33

4-
<br/>
4+
<p align="center">
55

6-
![header2](https://count.ly/github/countly-editions.png?v2)
6+
[![Build Status](https://api.travis-ci.org/Countly/countly-server.png?branch=master)](https://travis-ci.org/Countly/countly-server) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/98c2726f2d734697a5f1ac0d453f0a06)](https://app.codacy.com/app/ar2rsawseen/countly-server?utm_source=github.com&utm_medium=referral&utm_content=Countly/countly-server&utm_campaign=Badge_Grade_Dashboard) [![Install Countly on DigitalOcean](https://do.count.ly/button.svg?v2)](http://do.count.ly)
7+
8+
</p>
79

8-
<br/>
10+
<p align="center">
911

10-
* **We're hiring:** Countly is looking for full stack developers (remote work). [Check out available openings here](https://angel.co/countly/jobs).
11-
* **Slack user?** [Join our Slack community](https://slack.count.ly)
12-
* **Questions?** [Ask in our Community forum](http://community.count.ly)
12+
![header2](https://count.ly/github/countly-editions.png?v2)
1313

14-
## What's Countly?
14+
</p>
15+
16+
<p align="center">
17+
<strong>
18+
<a href="https://count.ly/">Website</a>
19+
20+
<a href="https://resources.count.ly">Docs</a>
21+
22+
<a href="https://count.ly/try">Try demo</a>
23+
24+
<a href="https://slack.count.ly">Slack group</a>
25+
26+
<a href="https://community.count.ly">Community forum</a>
27+
</strong>
28+
</p>
29+
30+
31+
## Table of Contents
32+
33+
- [What is Countly?](#what-is-countly)
34+
- [What is included?](#what-is-included)
35+
- [What can Countly track?](#what-can-countly-track)
36+
- [What components does Countly have?](#built-with)
37+
- [Security](#security)
38+
- [What makes Countly unique?](#what-makes-countly-unique)
39+
- [Differences between Community Edition & Enterprise Edition](#differences-between-community-edition--enterprise-edition)
40+
- [Installing and upgrading Countly server](#installing-and-upgrading-countly-server)
41+
- [API and Frontend](#api-and-frontend)
42+
- [Extensibility and plugins](#extensibility-and-plugins)
43+
- [How can I help you with your efforts?](#how-can-i-help-you-with-your-efforts)
44+
- [Badges](#badges)
45+
- [Links](#links)
46+
47+
48+
## What is Countly?
1549
[Countly](http://count.ly) is an innovative, real-time, open source [mobile](https://count.ly/mobile-analytics) & [web analytics](http://count.ly/web-analytics), [rich push notifications](https://count.ly/plugins/rich-push-notifications) and [crash reporting](https://count.ly/plugins/crash-analytics) platform powering more than 2500 web sites, 14000 mobile applications and several tens of thousands of desktop applications as of 2017 Q3. It collects data from mobile, desktop, web apps including Apple Watch, TvOS and other internet-connected devices, and visualizes this information to analyze application usage and end-user behavior.
1650

1751
With the help of [Javascript SDK](http://github.com/countly/countly-sdk-web), Countly is a web analytics platform with features on par with mobile SDKs. For more information about web analytics capabilities, [see this link](http://count.ly/web-analytics).
@@ -23,7 +57,7 @@ Click on the below image for a 1 minute video introduction to Countly (opens You
2357
[![Countly Community Edition - Video](https://count.ly/github/countly-community-1min.png)](https://youtu.be/htKeh9bsZwA)
2458

2559

26-
## What is included?
60+
## What is included?
2761

2862
This repository includes server-side part of Countly, with following features:
2963

@@ -38,7 +72,7 @@ This repository includes server-side part of Countly, with following features:
3872

3973
![content](https://count.ly/github/countly-highlights.png)
4074

41-
## What can Countly track?
75+
## What can Countly track?
4276

4377
[Countly](https://count.ly) can collect and visualize data from iOS, Android, Windows Phone devices, desktop applications (Windows, Mac OS) and web applications. You can find a list of [official and community supported Countly SDK libraries here](https://resources.count.ly/docs/downloading-sdks). Each SDK has its own installation instructions.
4478

@@ -55,7 +89,7 @@ Plus lots of [open source libraries](http://resources.count.ly/docs/list-of-open
5589

5690
Security is very important to us. If you discover any issue regarding security, please disclose the information responsibly by sending an email to [email protected] and **not by creating a GitHub issue**.
5791

58-
## What makes Countly unique?
92+
## What makes Countly unique?
5993

6094
Countly is a privacy-focused and 360-degree analytics approach with several, unique values:
6195

@@ -74,7 +108,7 @@ Countly is a privacy-focused and 360-degree analytics approach with several, uni
74108
* **Guarantee:** Community Edition is on the bleeding edge regarding version upgrades and with no bugfix guarantee. Enterprise Edition has bugfix guarantee, immediate resolution, verified builds, on-site and automatic version upgrades.
75109
* **Features:** Enterprise Edition has more features compared to Countly, with a focus on end-to-end analytics and marketing platform. Additional features include but not limited to automated push notifications, drilling on the raw data, user profiles, in-app purchase analytics, retention & engagement, user flows, cohorts and custom dashboards.
76110

77-
## Installing & upgrading Countly server
111+
## Installing and upgrading Countly server
78112

79113
Countly installation script assumes it is running on a fresh, decent Ubuntu/CentOS/RHEL Linux without any services listening on port 80 or 443 (which should also be open to incoming traffic), and takes care of every library and software required to be installed for Countly to run.
80114

@@ -92,11 +126,11 @@ There are several ways to install Countly:
92126

93127
If you want to upgrade Countly from a previous version, please take a look at [upgrading documentation](http://resources.count.ly/v1.0/docs/upgrading-countly-server).
94128

95-
## API & Frontend
129+
## API and Frontend
96130

97131
Countly has a [well defined API](http://resources.count.ly), that reads and writes data from/to the Countly backend. Dashboard is built using the read API, so it's possible to fetch any information you see on the dashboard using the Countly API. If you are interested in creating new reports or visualisations in the UI, we recommend checking out the next section for creating plugins.
98132

99-
## Extensibility & plugins
133+
## Extensibility and plugins
100134

101135
Countly is extensible using the [plugin architecture](https://count.ly/plugins). We suggest [you read this document](http://resources.count.ly/docs/plugins-development-introduction) before creating your plugin. We provide support to companies with know-how in need to create their own plugins.
102136

api/api.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ const passToMaster = (worker) => {
201201
};
202202

203203
if (cluster.isMaster) {
204+
console.log("Starting master");
204205
common.db = plugins.dbConnection();
205206

206207
const workerCount = (countlyConfig.api.workers)
@@ -235,6 +236,7 @@ if (cluster.isMaster) {
235236
}, 10000);
236237
}
237238
else {
239+
console.log("Starting worker", process.pid, "parent:", process.ppid);
238240
const taskManager = require('./utils/taskmanager.js');
239241
common.db = plugins.dbConnection(countlyConfig);
240242
//since process restarted mark running tasks as errored
@@ -251,6 +253,11 @@ else {
251253
}
252254
});
253255

256+
process.on('exit', () => {
257+
console.log('Exiting due to master exited');
258+
process.exit(1);
259+
});
260+
254261
plugins.dispatch("/worker", {common: common});
255262

256263
http.Server((req, res) => {
@@ -277,7 +284,7 @@ else {
277284
}
278285
});
279286
}
280-
else if (req.method === 'OPTIONS') {
287+
else if (req.method.toLowerCase() === 'options') {
281288
const headers = {};
282289
headers["Access-Control-Allow-Origin"] = "*";
283290
headers["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS";
@@ -286,9 +293,12 @@ else {
286293
res.end();
287294
}
288295
//attempt process GET request
289-
else {
296+
else if (req.method.toLowerCase() === 'get') {
290297
processRequest(params);
291298
}
299+
else {
300+
common.returnMessage(params, 405, "Method not allowed");
301+
}
292302
}).listen(common.config.api.port, common.config.api.host || '').timeout = common.config.api.timeout || 120000;
293303

294304
plugins.loadConfigs(common.db);

api/parts/data/fetch.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1513,7 +1513,7 @@ function getPeriodObj(params) {
15131513
}
15141514
catch (SyntaxError) {
15151515
console.log('Parse period JSON failed');
1516-
return false;
1516+
params.qstring.period = "30days";
15171517
}
15181518
}
15191519

api/parts/jobs/executor.js

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ process.on('unhandledRejection', (reason, p) => {
1111
console.log('Unhandled rejection for %j with reason %j stack ', p, reason, reason ? reason.stack : undefined);
1212
});
1313

14+
process.on('exit', () => {
15+
console.log('Exiting due to master exited');
16+
process.exit(1);
17+
});
18+
1419
/**
1520
* Entry point for child_process.fork to run a corresponding resource / job.
1621
*/

api/parts/mgmt/apps.js

+36-28
Original file line numberDiff line numberDiff line change
@@ -241,35 +241,40 @@ appsApi.createApp = function(params) {
241241
newApp.seq = 0;
242242

243243
common.db.collection('apps').insert(newApp, function(err, app) {
244-
var appKey = common.sha1Hash(app.ops[0]._id, true);
245-
246-
common.db.collection('apps').update({'_id': app.ops[0]._id}, {$set: {key: appKey}}, function() {});
247-
248-
newApp._id = app.ops[0]._id;
249-
newApp.key = appKey;
250-
251-
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({ls: -1}, { background: true }, function() {});
252-
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({"uid": 1}, { background: true }, function() {});
253-
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({"sc": 1}, { background: true }, function() {});
254-
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({
255-
"lac": 1,
256-
"ls": 1
257-
}, { background: true }, function() {});
258-
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({"tsd": 1}, { background: true }, function() {});
259-
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({"did": 1}, { background: true }, function() {});
260-
common.db.collection('app_user_merges' + app.ops[0]._id).ensureIndex({cd: 1}, {
261-
expireAfterSeconds: 60 * 60 * 3,
262-
background: true
263-
}, function() {});
264-
common.db.collection('metric_changes' + app.ops[0]._id).ensureIndex({ts: -1}, { background: true }, function() {});
265-
common.db.collection('metric_changes' + app.ops[0]._id).ensureIndex({uid: 1}, { background: true }, function() {});
266-
plugins.dispatch("/i/apps/create", {
267-
params: params,
268-
appId: app.ops[0]._id,
269-
data: newApp
270-
});
244+
if (!err && app && app.ops && app.ops[0] && app.ops[0]._id) {
245+
var appKey = common.sha1Hash(app.ops[0]._id, true);
246+
247+
common.db.collection('apps').update({'_id': app.ops[0]._id}, {$set: {key: appKey}}, function() {});
248+
249+
newApp._id = app.ops[0]._id;
250+
newApp.key = appKey;
251+
252+
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({ls: -1}, { background: true }, function() {});
253+
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({"uid": 1}, { background: true }, function() {});
254+
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({"sc": 1}, { background: true }, function() {});
255+
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({
256+
"lac": 1,
257+
"ls": 1
258+
}, { background: true }, function() {});
259+
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({"tsd": 1}, { background: true }, function() {});
260+
common.db.collection('app_users' + app.ops[0]._id).ensureIndex({"did": 1}, { background: true }, function() {});
261+
common.db.collection('app_user_merges' + app.ops[0]._id).ensureIndex({cd: 1}, {
262+
expireAfterSeconds: 60 * 60 * 3,
263+
background: true
264+
}, function() {});
265+
common.db.collection('metric_changes' + app.ops[0]._id).ensureIndex({ts: -1}, { background: true }, function() {});
266+
common.db.collection('metric_changes' + app.ops[0]._id).ensureIndex({uid: 1}, { background: true }, function() {});
267+
plugins.dispatch("/i/apps/create", {
268+
params: params,
269+
appId: app.ops[0]._id,
270+
data: newApp
271+
});
271272
iconUpload(Object.assign({}, params, {app_id: app.ops[0]._id}));
272-
common.returnOutput(params, newApp);
273+
common.returnOutput(params, newApp);
274+
}
275+
else {
276+
common.returnMessage(params, 500, "Error creating App: " + err);
277+
}
273278
});
274279
};
275280

@@ -619,6 +624,9 @@ appsApi.resetApp = function(params) {
619624
});
620625
}
621626
}
627+
else {
628+
common.returnMessage(params, 404, 'App not found');
629+
}
622630
});
623631

624632
return true;

api/parts/mgmt/users.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ usersApi.deleteUser = function(params) {
451451
else {
452452
common.db.collection('auth_tokens').remove({ 'owner': userIds[i] });
453453
common.db.collection('members').findAndModify({ '_id': common.db.ObjectID(userIds[i]) }, {}, {}, { remove: true }, function(err, user) {
454-
if (!err && user && user.ok) {
454+
if (!err && user && user.ok && user.value) {
455455
plugins.dispatch("/i/users/delete", {
456456
params: params,
457457
data: user.value

api/utils/common.js

+15-6
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ common.escape_html = function(string, more) {
8080
* @returns {vary} escaped value
8181
**/
8282
function escape_html_entities(key, value, more) {
83-
if (typeof value === 'object' && value) {
83+
if (typeof value === 'object' && value && (value.constructor === Object || value.constructor === Array)) {
8484
if (Array.isArray(value)) {
8585
let replacement = [];
8686
for (let k = 0; k < value.length; k++) {
@@ -94,7 +94,7 @@ function escape_html_entities(key, value, more) {
9494
}
9595
}
9696
else {
97-
replacement[k] = value[k];
97+
replacement[k] = escape_html_entities(k, value[k], more);
9898
}
9999
}
100100
return replacement;
@@ -113,7 +113,7 @@ function escape_html_entities(key, value, more) {
113113
}
114114
}
115115
else {
116-
replacement[common.escape_html(k, more)] = value[k];
116+
replacement[common.escape_html(k, more)] = escape_html_entities(k, value[k], more);
117117
}
118118
}
119119
}
@@ -969,7 +969,10 @@ common.unblockResponses = function(params) {
969969
*/
970970
common.returnRaw = function(params, returnCode, body, heads) {
971971
if (params && params.APICallback && typeof params.APICallback === 'function') {
972-
if (!params.blockResponses && !params.res.finished) {
972+
if (!params.blockResponses && (!params.res || !params.res.finished)) {
973+
if (!params.res) {
974+
params.res = {};
975+
}
973976
params.res.finished = true;
974977
params.APICallback(returnCode === 200, body, heads, returnCode, params);
975978
}
@@ -1007,7 +1010,10 @@ common.returnRaw = function(params, returnCode, body, heads) {
10071010
*/
10081011
common.returnMessage = function(params, returnCode, message, heads) {
10091012
if (params && params.APICallback && typeof params.APICallback === 'function') {
1010-
if (!params.blockResponses && !params.res.finished) {
1013+
if (!params.blockResponses && (!params.res || !params.res.finished)) {
1014+
if (!params.res) {
1015+
params.res = {};
1016+
}
10111017
params.res.finished = true;
10121018
params.APICallback(returnCode === 200, JSON.stringify({result: message}), heads, returnCode, params);
10131019
}
@@ -1062,7 +1068,10 @@ common.returnMessage = function(params, returnCode, message, heads) {
10621068
*/
10631069
common.returnOutput = function(params, output, noescape, heads) {
10641070
if (params && params.APICallback && typeof params.APICallback === 'function') {
1065-
if (!params.blockResponses && !params.res.finished) {
1071+
if (!params.blockResponses && (!params.res || !params.res.finished)) {
1072+
if (!params.res) {
1073+
params.res = {};
1074+
}
10661075
params.res.finished = true;
10671076
params.APICallback(true, output, heads, 200, params);
10681077
}

0 commit comments

Comments
 (0)