Add View Counter to Hexo in Express

in Series, Building Blog, Technology, Nodejs Sitevisits: -- Pageviews: --

I planned to add a site visit counter and page view counter into my blog (Hexo + Express.js). And by searching online, it seems that there is very limited information can be found on how to add such things on a Hexo Blog where:

  • The Hexo blog is integrated into an existing Express server, i.e. the blog should be visited at express_site/blog
  • The theme I used for the blog does not have such feature added, means I cannot just add something to the _config.yml of the theme and the view counter will work as a charm. I will need to customize it.

There was several options that I had in mind:

  • Only customize Hexo blog and the theme –> I realize it is not feasible. I do not know how to let Hexo or the theme load customised .js file under Hexo + Express structure;
  • Load js from Express side –> I finally use this method. Let Express loading related js file as Express static files, the js file will add the data to the view counter in Hexo pages. Blow is what I got in the end.
    Trulli
    View counter in blog post

Now I will introduce how I achive it in details.

Set up view counter

  • Set up Firebase (There are tons of tutorial out there, so I am not going to repeat it:))

    There is only one thing I would like to emphasize is the database set up. The database should be Realtime Database, and the “rules” is set as below. I will explain in details later why this will not cause security issues.

Trulli
Firebase Realtime Databse Rules
  • After register and setting up the Firebase, add the following code to the hexo_blog_directory/themes/{your theme}/layout/xxx.ejs where you want to show the view counter.
1
2
<span id="site-visits">Sitevisits: <span class="count">--</span></span>
<span id="page-views">Pageviews: <span class="count">--</span></span>
  • Add the following .js code to express_proj/public/javascripts/:

    Note:

    • I used Firebase SDK 9
    • The details of database read/write APIs could be found at Firebase Database APIs
    • You could of course test data read / write sepeartely before using the whole block
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.8.3/firebase-app.js';
import {
getDatabase, ref, get, set, child,
// eslint-disable-next-line import/no-unresolved
} from 'https://www.gstatic.com/firebasejs/9.8.3/firebase-database.js';

const viewCounter = async () => {
// The web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: 'your api key',
authDomain: 'your settings ',
databaseURL: 'xxx',
projectId: 'xxx',
storageBucket: 'xxx',
messagingSenderId: 'xxx',
appId: 'xxx',
measurementId: 'xxx',
};

// Initialize Firebase
const firebase = initializeApp(firebaseConfig);
const db = getDatabase(firebase, firebaseConfig.databaseURL);
const oriUrl = window.location.host;
const curUrl = oriUrl + window.location.pathname;

const readVisits = async (url, selector) => {
const dbKey = decodeURI(url.replace(/\/|\./g, '_'));
let count = 1;
const res = await get(child(ref(db), dbKey));
if (res.exists()) {
count = parseInt(res.val() || 0, 10) + 1;
}
await await set(ref(db, dbKey), count);
if (selector.length > 0) {
// eslint-disable-next-line no-param-reassign
selector[0].innerText = count;
}
};

readVisits(oriUrl, document.querySelectorAll('.post-meta #site-visits .count'));
if (curUrl && curUrl !== '_') {
readVisits(`page/${curUrl}`, document.querySelectorAll('.post-meta #page-views .count'));
}
};

viewCounter();
  • After the above steps, the view counter is ready to go :)

Extra issue: is it okay to expose Firebase apiKey?

When you see that I put apiKey etc. in a static .js file, you must have a question in mind: is that a security issue here? I had the same question. Then I found the following article: Is it safe to expose Firebase apiKey to the public?

The answer is a bit long, so I will sum up here: for a small size and not a critical site, it is okay to do thing like this. The apiKey here in Firebase is not like the traditional api keys we know, it is okay to expose it. All you need to do is to set up whitelist site for Firebase (that is why I mentioend earlier in the article that you could set the Firebase database rules for opening read/write public). Here is how you can do it in details:

  • Go to Firebase Console
  • Check Authentication Menu -> Sign-in method tab
  • Scroll down to Authorized domains
  • Click “Add Domain” button and add your domains (localhost is added as default, and I do not delete as it is needed for local testing)
Trulli
Set up whitelist site

The above should be okay for a small site. I am not going to over-engineering it, but you can always tailor the security rules accoriding to your business requirements :)

Comment and share

  • page 1 of 1
Author's picture

Jingjie Jiang


Find a place I love the most