1.3.1: current visitors

This commit is contained in:
Klemek
2021-03-30 20:16:15 +02:00
parent 4a3b8267ec
commit f7167a85a8
5 changed files with 83 additions and 37 deletions
+1 -1
View File
@@ -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": {
+2 -8
View File
@@ -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);
@@ -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);
+21 -5
View File
@@ -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)
@@ -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),
});
});
}
+10 -2
View File
@@ -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();
});
});
@@ -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();
});
});
+49 -21
View File
@@ -37,14 +37,21 @@ 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 });
@@ -52,12 +59,13 @@ describe('read()', () => {
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);
@@ -65,12 +73,13 @@ describe('read()', () => {
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, {});
@@ -78,22 +87,41 @@ describe('read()', () => {
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('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()', () => {
beforeEach(() => {
mockClient.multi = () => mockClient;
mockClient.hincrby = () => mockClient;
mockClient.exec = (cb) => {
cb();
};
config['hit_counter']['unique_visitor_timeout'] = -1;
});
test('simple visit', (done) => {
let multiCalled = false;
let execCalled = false;