FiniCounter: A Website Vistor Counter
Static website such as Hexo/Hugo/Jekyll is very popular recent years. It is fast, easy to write, deploy and host. However, no free lunch: it is non-trivial to store dynamic information such as pageview counts and comments under the serverless architecture. This site uses Waline to implement article view count and comment system.
Accidently I found that we do not have a full site pageview counter. Waline has post-level counter instead of site-level one.
Just DIY: FiniCounter. Use Vercel serverless function as the web API framework, MongoDB as the storage. When a user comes to any page of the site, we invoke the count API through fetch API, increment the counter in the MongoDB, return the updated value and display in the page.
Initially I want to develop a tool for myself. After I finish it, I decide to make it as a public free service :-) . FiniCounter looks like this:
Backend Implementation
The backend logic is straightforward. The server received a Http GET request and increment the corresponding count in MongoDB.
The API format:
https://finicounter.eu.org/counter?host=xxx.com
It uses domain name host
as the key to count.
The main code is self-explained:
MONGODB_URL = os.environ.get("MONGODB_URL")
CORS_ORIGINS = os.environ.get("CORS_ORIGINS")
DB_NAME = os.environ.get("DB_NAME") if os.environ.get("DB_NAME") is not None else "MyCounter"
COLLECTION_NAME = "Counter"
cors_headers = {"Access-Control-Allow-Origin": CORS_ORIGINS, "Access-Control-Allow-Method": "POST, GET, OPTIONS"}
mongoClient = pymongo.MongoClient(MONGODB_URL)
db = mongoClient[DB_NAME]
collection= db[COLLECTION_NAME]
app = Sanic("finicounter")
@app.route("/counter")
async def counter(request):
args = request.get_args()
host = args["host"][0]
result = collection.find_one_and_update({"host": host}, {"$inc": {"views": 1}, "$set": { "updateTime": datetime.datetime.utcnow()}}, upsert = True, return_document = pymongo.collection.ReturnDocument.AFTER, maxTimeMS=50)
return response.json({"views": result['views']}, headers = cors_headers)
app.run(host="0.0.0.0", port=8000, fast=True)
Host By Yourself
Host FiniCounter yourself is easy. What you need is a Vercel account and a MongoDB.
Just fork https://github.com/finisky/finicounter
to your
GitHub account, create a new project in Vercel, import the private
repository and configure 3 environment variables:
Value | |
---|---|
CORS_ORIGINS | * (or your domain) |
DB_NAME | MongoDB DB name |
MONGODB_URL | MongoDB Connection String |
The data is stored in the Counter
Collection of DB
DB_NAME
.
Frontend Implementation
The fronted is implemented by fetch API. The innerText
of the element with id finicount_views
will be set to the
retrieved counter value. The script:
async function getViews() {
let url = "https://finicounter.eu.org/counter?host=" + window.location.hostname;
try {
let res = await fetch(url);
return await res.json();
} catch (error) {
console.log(error);
}
}
async function renderViews() {
let res = await getViews();
let elem = document.getElementById("finicount_views");
if (typeof elem !== 'undefined' && elem !== null) {
elem.innerText = formatNumber(res.views);
}
}
function formatNumber(num) {
return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
}
renderViews();
The function formatNumber
will format the counter like
12,345,678
. Feel free to customize your own script if
necessary.
How to Use
Basic Usage
- Add the following script to your websites:
<script async src="//finicounter.eu.org/finicounter.js"></script>
- Add a container with id
finicount_views
to show the view counts:
<span id="finicount_views"></span>
You are done! The view count will be shown on your site like this
12,345,678
.
Notice: the script should be added to every pages of your website. Otherwise, the page view counter might missing counts.
It is also welcome to directly use the counter API
https://finicounter.eu.org/counter?host=xxx.com
and
customize the javascript yourself.
Each counter will be automatically deleted if not accessed for more than 3 months.
Hexo NexT Theme Integration
For Hexo NexT theme, we can easily integrate FiniCounter by Injects. The total pageview can be displayed in the footer:
Just add a new js file totalpageview.js
in the Hexo root
folder scripts/
:
hexo.extend.filter.register('theme_inject', function(injects) {
injects.footer.raw('totalpageview', '<div><span><a href="https://finicounter.eu.org/" target="_blank">Total Pageview:</a></span><span id="finicount_views" style="display:inline;padding-left:5px;"></span><div> <script async src="//finicounter.eu.org/finicounter.js"></script>', {}, {cache: false});
});
Leave Hexo NexT Plugin as future work :-) .
Demo
This site is using FiniCounter :-) . Demo on this site .
Source Code
Source code is available here .