Skip to content

Commit

Permalink
1.3.1: current visitors
Browse files Browse the repository at this point in the history
  • Loading branch information
Klemek committed Mar 30, 2021
1 parent 4a3b826 commit f7167a8
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 37 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gitblog.md",
"version": "1.3.0",
"version": "1.3.1",
"description": "A static blog using Markdown pulled from your git repository.",
"main": "src/server.js",
"dependencies": {
Expand Down
10 changes: 2 additions & 8 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,7 @@ module.exports = (config) => {
app.get('/stats', (req, res) => {
if (config['modules']['hit_counter']) {
hc.read('/', (data) => {
res.json({
hits: data.hits,
visitors: data.visitors,
});
res.json(data);
});
} else {
showError(req, res, 404);
Expand Down Expand Up @@ -268,10 +265,7 @@ module.exports = (config) => {
} else if (req.path.endsWith('stats')) {
if (config['modules']['hit_counter']) {
hc.read(articlePath, (data) => {
res.json({
hits: data.hits,
visitors: data.visitors,
});
res.json(data);
});
} else {
showError(req, res, 404);
Expand Down
26 changes: 21 additions & 5 deletions src/hit_counter.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ module.exports = (config, onConnect, onError) => {
cb();
} else {
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const key = path + ':' + ip;
visitors[path] = (visitors[path] || {});
const now = Date.now();
const isNewVisitor = (now - (visitors[key] || 0)) > config['hit_counter']['unique_visitor_timeout'];
visitors[key] = now;
const isNewVisitor = (now - (visitors[path][ip] || 0)) > config['hit_counter']['unique_visitor_timeout'];
visitors[path][ip] = now;
client
.multi()
.hincrby(path, 'h', 1)
Expand All @@ -25,17 +25,33 @@ module.exports = (config, onConnect, onError) => {
}
};

const cleanVisitors = (path) => {
visitors[path] = (visitors[path] || {});
const now = Date.now();
let count = 0;
for (let ip in visitors[path]) {
if ((now - visitors[path][ip]) > config['hit_counter']['unique_visitor_timeout']) {
delete visitors[path][ip];
} else {
count++;
}
}
return count;
};

const read = (path, cb) => {
if (!client.connected) {
cb({
hits: 0,
visitors: 0,
total_visitors: 0,
current_visitors: 0,
});
} else {
client.hgetall(path, (_, value) => {
cb({
hits: value ? value.h || 0 : 0,
visitors: value ? value.v || 0 : 0,
total_visitors: value ? value.v || 0 : 0,
current_visitors: cleanVisitors(path),
});
});
}
Expand Down
12 changes: 10 additions & 2 deletions test/app.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,11 @@ describe('Test root path', () => {
request(app).get('/stats')
.then((response) => {
expect(response.statusCode).toBe(200);
expect(response.body).toEqual({ hits: 0, visitors: 0 });
expect(response.body).toEqual({
hits: 0,
total_visitors: 0,
current_visitors: 0,
});
done();
});
});
Expand Down Expand Up @@ -478,7 +482,11 @@ describe('Test articles rendering', () => {
request(app).get('/2019/05/05/anything/stats')
.then((response) => {
expect(response.statusCode).toBe(200);
expect(response.body).toEqual({ hits: 0, visitors: 0 });
expect(response.body).toEqual({
hits: 0,
total_visitors: 0,
current_visitors: 0,
});
done();
});
});
Expand Down
70 changes: 49 additions & 21 deletions test/hit_counter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,63 +37,91 @@ test('options passed to redis', () => {
expect(mockClient.options).toEqual(config['redis']);
});

describe('read()', () => {
beforeEach(() => {
mockClient.hgetall = (_, cb) => {
cb();
};
});

test('read path', (done) => {
beforeEach(() => {
mockClient.hgetall = (_, cb) => {
cb();
};
mockClient.multi = () => mockClient;
mockClient.hincrby = () => mockClient;
mockClient.exec = (cb) => {
cb();
};
config['hit_counter']['unique_visitor_timeout'] = -1;
});

describe('read()', () => {
test('normal', (done) => {
mockClient.hgetall = (path, cb) => {
expect(path).toBe('/test/path/');
cb(undefined, { h: 12, v: 34 });
};
hc.read('/test/path/', (data) => {
expect(data).toBeDefined();
expect(data.hits).toBe(12);
expect(data.visitors).toBe(34);
expect(data.total_visitors).toBe(34);
expect(data.current_visitors).toBe(0);
done();
});
});

test('read path with error', (done) => {
test('with error', (done) => {
mockClient.hgetall = (path, cb) => {
expect(path).toBe('/test/path/');
cb('error', undefined);
};
hc.read('/test/path/', (data) => {
expect(data).toBeDefined();
expect(data.hits).toBe(0);
expect(data.visitors).toBe(0);
expect(data.total_visitors).toBe(0);
expect(data.current_visitors).toBe(0);
done();
});
});

test('read path with error 2', (done) => {
test('with error 2', (done) => {
mockClient.hgetall = (path, cb) => {
expect(path).toBe('/test/path/');
cb(undefined, {});
};
hc.read('/test/path/', (data) => {
expect(data).toBeDefined();
expect(data.hits).toBe(0);
expect(data.visitors).toBe(0);
expect(data.total_visitors).toBe(0);
expect(data.current_visitors).toBe(0);
done();
});
});
});

describe('count()', () => {
beforeEach(() => {
mockClient.multi = () => mockClient;
mockClient.hincrby = () => mockClient;
mockClient.exec = (cb) => {
cb();
};
config['hit_counter']['unique_visitor_timeout'] = -1;
test('1 visitor', (done) => {
config['hit_counter']['unique_visitor_timeout'] = 1000;
hc.count({
headers: {},
connection: { remoteAddress: 'test1' },
}, '/test/path/5', () => {
hc.read('/test/path/5', (data) => {
expect(data).toBeDefined();
expect(data.current_visitors).toBe(1);
done();
});
});
});

test('cleaned old visitor', (done) => {
hc.count({
headers: {},
connection: { remoteAddress: 'test1' },
}, '/test/path/5', () => {
hc.read('/test/path/5', (data) => {
expect(data).toBeDefined();
expect(data.current_visitors).toBe(0);
done();
});
});
});
});

describe('count()', () => {
test('simple visit', (done) => {
let multiCalled = false;
let execCalled = false;
Expand Down

0 comments on commit f7167a8

Please sign in to comment.