From 85cea66051a7e53425b29f94f43fd6737a50d488 Mon Sep 17 00:00:00 2001
From: orangecoding
Date: Fri, 12 Sep 2025 13:38:53 +0200
Subject: [PATCH] improving tracking. now using internal tracking
---
README.md | 2 +-
lib/api/routes/jobRouter.js | 6 --
lib/api/routes/loginRoute.js | 2 +-
lib/services/tracking/Tracker.js | 90 +++++++++++---------
package.json | 3 +-
ui/src/components/tracking/TrackingModal.jsx | 3 +-
yarn.lock | 22 -----
7 files changed, 56 insertions(+), 72 deletions(-)
diff --git a/README.md b/README.md
index 01ff3a7..b43ecf8 100755
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ Immoscout has implemented advanced bot detection. In order to work around this,
Fredy is completely free (and will always remain free). However, it would be a huge help if you’d allow me to collect some analytical data.
Before you freak out, let me explain...
-If you agree, Fredy will send a ping to my Mixpanel project each time it runs.
+If you agree, Fredy will send a ping once every 6 hours to my internal tracking project (Will be open sourced soon).
The data includes: names of active adapters/providers, OS, architecture, Node version, and language. The information is entirely anonymous and helps me understand which adapters/providers are most frequently used.
**Thanks**🤘
diff --git a/lib/api/routes/jobRouter.js b/lib/api/routes/jobRouter.js
index 077aa48..f906444 100644
--- a/lib/api/routes/jobRouter.js
+++ b/lib/api/routes/jobRouter.js
@@ -3,7 +3,6 @@ import * as jobStorage from '../../services/storage/jobStorage.js';
import * as userStorage from '../../services/storage/userStorage.js';
import { config } from '../../utils.js';
import { isAdmin } from '../security.js';
-import { trackDemoJobCreated } from '../../services/tracking/Tracker.js';
const service = restana();
const jobRouter = service.newRouter();
function doesJobBelongsToUser(job, req) {
@@ -46,11 +45,6 @@ jobRouter.post('/', async (req, res) => {
res.send(new Error(error));
console.error(error);
}
- trackDemoJobCreated({
- name,
- provider,
- adapter: notificationAdapter,
- });
res.send();
});
jobRouter.delete('', async (req, res) => {
diff --git a/lib/api/routes/loginRoute.js b/lib/api/routes/loginRoute.js
index 0a2626d..061b274 100644
--- a/lib/api/routes/loginRoute.js
+++ b/lib/api/routes/loginRoute.js
@@ -27,7 +27,7 @@ loginRouter.post('/', async (req, res) => {
}
if (user.password === hasher.hash(password)) {
if (config.demoMode) {
- trackDemoAccessed();
+ await trackDemoAccessed();
}
req.session.currentUser = user.id;
diff --git a/lib/services/tracking/Tracker.js b/lib/services/tracking/Tracker.js
index 04e6b60..e99ad65 100644
--- a/lib/services/tracking/Tracker.js
+++ b/lib/services/tracking/Tracker.js
@@ -1,65 +1,77 @@
-import Mixpanel from 'mixpanel';
import { getJobs } from '../storage/jobStorage.js';
import { getUniqueId } from './uniqueId.js';
import { config, inDevMode } from '../../utils.js';
import os from 'os';
import { readFileSync } from 'fs';
import { packageUp } from 'package-up';
+import fetch from 'node-fetch';
-const mixpanelTracker = Mixpanel.init('718670ef1c58c0208256c1e408a3d75e');
-const distinct_id = getUniqueId() || 'N/A';
+const deviceId = getUniqueId() || 'N/A';
const version = await getPackageVersion();
+const FREDY_TRACKING_URL = 'https://fredy.orange-coding.net/tracking';
-export const track = function () {
- //only send tracking information if the user allowed to do so.
- if (config.analyticsEnabled && !inDevMode()) {
- const activeProvider = new Set();
- const activeAdapter = new Set();
+let cached = null;
+let lastSent = 0;
+const SIX_HOURS = 6 * 3_600_000;
- const jobs = getJobs();
+export const track = async () => {
+ try {
+ if (config.analyticsEnabled && !inDevMode()) {
+ const activeProvider = new Set();
+ const activeAdapter = new Set();
- if (jobs != null && jobs.length > 0) {
- jobs.forEach((job) => {
- job.provider.forEach((provider) => {
- activeProvider.add(provider.id);
+ const jobs = getJobs();
+
+ if (jobs != null && jobs.length > 0) {
+ jobs.forEach((job) => {
+ job.provider.forEach((provider) => activeProvider.add(provider.id));
+ job.notificationAdapter.forEach((adapter) => activeAdapter.add(adapter.id));
});
- job.notificationAdapter.forEach((adapter) => {
- activeAdapter.add(adapter.id);
- });
- });
- mixpanelTracker.track(
- 'fredy_tracking',
- enrichTrackingObject({
+ const trackingObj = enrichTrackingObject({
adapter: Array.from(activeAdapter),
provider: Array.from(activeProvider),
- }),
- );
+ });
+
+ const stringify = JSON.stringify(trackingObj);
+ const now = Date.now();
+
+ // send if changed OR six hours passed since last send
+ if (stringify !== cached || now - lastSent >= SIX_HOURS) {
+ await fetch(`${FREDY_TRACKING_URL}/main`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: stringify,
+ });
+ cached = stringify;
+ lastSent = now;
+ }
+ }
}
+ } catch (error) {
+ console.warn('Error sending tracking data', error);
}
};
/**
* Note, this will only be used when Fredy runs in demo mode
*/
-export function trackDemoJobCreated(jobData) {
+export async function trackDemoAccessed() {
if (config.analyticsEnabled && !inDevMode() && config.demoMode) {
- mixpanelTracker.track('demoJobCreated', enrichTrackingObject(jobData));
- }
-}
-
-/**
- * Note, this will only be used when Fredy runs in demo mode
- */
-export function trackDemoAccessed() {
- if (config.analyticsEnabled && !inDevMode() && config.demoMode) {
- mixpanelTracker.track('demoAccessed', enrichTrackingObject({}));
+ try {
+ await fetch(`${FREDY_TRACKING_URL}/demo/accessed`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ });
+ } catch (error) {
+ console.warn('Error sending tracking data', error);
+ }
}
}
function enrichTrackingObject(trackingObject) {
- const operating_system = os.platform();
- const os_version = os.release();
+ const operatingSystem = os.platform();
+ const osVersion = os.release();
const arch = process.arch;
const language = process.env.LANG || 'en';
const nodeVersion = process.version || 'N/A';
@@ -67,13 +79,13 @@ function enrichTrackingObject(trackingObject) {
return {
...trackingObject,
isDemo: config.demoMode,
- operating_system,
- os_version,
+ operatingSystem,
+ osVersion,
arch,
nodeVersion,
language,
- distinct_id,
- fredy_version: version,
+ deviceId,
+ version,
};
}
diff --git a/package.json b/package.json
index a711d70..ff92ef8 100755
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "fredy",
- "version": "11.6.0",
+ "version": "11.6.1",
"description": "[F]ind [R]eal [E]states [d]amn eas[y].",
"scripts": {
"prepare": "husky",
@@ -70,7 +70,6 @@
"lodash": "4.17.21",
"lowdb": "7.0.1",
"markdown": "^0.5.0",
- "mixpanel": "^0.18.1",
"nanoid": "5.1.5",
"node-fetch": "3.3.2",
"node-mailjet": "6.0.9",
diff --git a/ui/src/components/tracking/TrackingModal.jsx b/ui/src/components/tracking/TrackingModal.jsx
index 80383b5..7ec8460 100644
--- a/ui/src/components/tracking/TrackingModal.jsx
+++ b/ui/src/components/tracking/TrackingModal.jsx
@@ -43,7 +43,8 @@ export default function TrackingModal() {
However, it would be a huge help if you’d allow me to collect some analytical data. Wait, before you click
- "no", let me explain. If you agree, Fredy will send a ping to my Mixpanel project each time it runs.
+ "no", let me explain. If you agree, Fredy will send a ping once every 6 hours to my internal tracking project.
+ (Will be open-sourced soon)
The data includes: names of active adapters/providers, OS, architecture, Node version, and language. The
diff --git a/yarn.lock b/yarn.lock
index 33a8f1c..58da8d9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1979,13 +1979,6 @@ acorn@^8.0.0, acorn@^8.15.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
-agent-base@6:
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
- integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
- dependencies:
- debug "4"
-
agent-base@^7.1.0, agent-base@^7.1.2:
version "7.1.4"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8"
@@ -4056,14 +4049,6 @@ http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1:
agent-base "^7.1.0"
debug "^4.3.4"
-https-proxy-agent@5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
- integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==
- dependencies:
- agent-base "6"
- debug "4"
-
https-proxy-agent@^7.0.6:
version "7.0.6"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9"
@@ -5450,13 +5435,6 @@ mixin-object@^2.0.1:
for-in "^0.1.3"
is-extendable "^0.1.1"
-mixpanel@^0.18.1:
- version "0.18.1"
- resolved "https://registry.yarnpkg.com/mixpanel/-/mixpanel-0.18.1.tgz#beefdce6c260165f4e2059c8cdd34c5c557162f7"
- integrity sha512-YD1xfn6WP6ZLQ6Pmgh0KgdXhueJEsrodThMTsHzHMH0VbWa9ck8s+ynDtM83OSgt+yQ61W/SQNrH8Y4wIwocGg==
- dependencies:
- https-proxy-agent "5.0.0"
-
mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
version "0.5.3"
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"