From a2ddfba1cfd56e49e33205adb8dbfeeab926f951 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 2 May 2026 22:46:52 +0300 Subject: [PATCH] fix: detect Edge and Opera before Chrome This fixes browser detection in visit stats by checking Edge and Opera user agents before Chrome. No database schema changes or stats aggregation changes were made. This is limited to browser detection logic in server/queues/visit.js. --- server/queues/visit.js | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/server/queues/visit.js b/server/queues/visit.js index a05acd19b..fdfbf91d1 100644 --- a/server/queues/visit.js +++ b/server/queues/visit.js @@ -4,32 +4,38 @@ const URL = require("node:url"); const { removeWww } = require("../utils"); const query = require("../queries"); - -const browsersList = ["IE", "Firefox", "Chrome", "Opera", "Safari", "Edge"]; const osList = ["Windows", "Mac OS", "Linux", "Android", "iOS"]; -function filterInBrowser(agent) { - return function(item) { - return agent.family.toLowerCase().includes(item.toLocaleLowerCase()); - } -} - function filterInOs(agent) { return function(item) { return agent.os.family.toLowerCase().includes(item.toLocaleLowerCase()); } } +function detectBrowser(userAgent, agent) { + const source = (userAgent || "").toLowerCase(); + const family = agent.family.toLowerCase(); + + // Check the more specific Chromium variants before generic Chrome/Safari. + if (/edg(e|a|ios)?\//i.test(source) || family.includes("edge")) return "Edge"; + if (/opr\//i.test(source) || family.includes("opera")) return "Opera"; + if (/firefox\/|fxios\//i.test(source) || family.includes("firefox")) return "Firefox"; + if (/msie|trident\//i.test(source) || family.includes("ie")) return "IE"; + if (/chrome\/|crios\//i.test(source) || family.includes("chrome")) return "Chrome"; + if (/safari\//i.test(source) || family.includes("safari")) return "Safari"; + + return "Other"; +} + module.exports = function({ data }) { const tasks = []; tasks.push(query.link.incrementVisit({ id: data.link.id })); - // the following line is for backward compatibility // used to send the whole header to get the user agent const userAgent = data.userAgent || data.headers?.["user-agent"]; const agent = useragent.parse(userAgent); - const [browser = "Other"] = browsersList.filter(filterInBrowser(agent)); + const browser = detectBrowser(userAgent, agent); const [os = "Other"] = osList.filter(filterInOs(agent)); const referrer = data.referrer && removeWww(URL.parse(data.referrer).hostname); @@ -48,4 +54,4 @@ module.exports = function({ data }) { ); return Promise.all(tasks); -} \ No newline at end of file +}