improving tracking. now using internal tracking

This commit is contained in:
orangecoding
2025-09-12 13:38:53 +02:00
parent 7372e5313f
commit 85cea66051
7 changed files with 56 additions and 72 deletions

View File

@@ -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 youd allow me to collect some analytical data. Fredy is completely free (and will always remain free). However, it would be a huge help if youd allow me to collect some analytical data.
Before you freak out, let me explain... 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.</p> 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.</p>
**Thanks**🤘 **Thanks**🤘

View File

@@ -3,7 +3,6 @@ import * as jobStorage from '../../services/storage/jobStorage.js';
import * as userStorage from '../../services/storage/userStorage.js'; import * as userStorage from '../../services/storage/userStorage.js';
import { config } from '../../utils.js'; import { config } from '../../utils.js';
import { isAdmin } from '../security.js'; import { isAdmin } from '../security.js';
import { trackDemoJobCreated } from '../../services/tracking/Tracker.js';
const service = restana(); const service = restana();
const jobRouter = service.newRouter(); const jobRouter = service.newRouter();
function doesJobBelongsToUser(job, req) { function doesJobBelongsToUser(job, req) {
@@ -46,11 +45,6 @@ jobRouter.post('/', async (req, res) => {
res.send(new Error(error)); res.send(new Error(error));
console.error(error); console.error(error);
} }
trackDemoJobCreated({
name,
provider,
adapter: notificationAdapter,
});
res.send(); res.send();
}); });
jobRouter.delete('', async (req, res) => { jobRouter.delete('', async (req, res) => {

View File

@@ -27,7 +27,7 @@ loginRouter.post('/', async (req, res) => {
} }
if (user.password === hasher.hash(password)) { if (user.password === hasher.hash(password)) {
if (config.demoMode) { if (config.demoMode) {
trackDemoAccessed(); await trackDemoAccessed();
} }
req.session.currentUser = user.id; req.session.currentUser = user.id;

View File

@@ -1,65 +1,77 @@
import Mixpanel from 'mixpanel';
import { getJobs } from '../storage/jobStorage.js'; import { getJobs } from '../storage/jobStorage.js';
import { getUniqueId } from './uniqueId.js'; import { getUniqueId } from './uniqueId.js';
import { config, inDevMode } from '../../utils.js'; import { config, inDevMode } from '../../utils.js';
import os from 'os'; import os from 'os';
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { packageUp } from 'package-up'; import { packageUp } from 'package-up';
import fetch from 'node-fetch';
const mixpanelTracker = Mixpanel.init('718670ef1c58c0208256c1e408a3d75e'); const deviceId = getUniqueId() || 'N/A';
const distinct_id = getUniqueId() || 'N/A';
const version = await getPackageVersion(); const version = await getPackageVersion();
const FREDY_TRACKING_URL = 'https://fredy.orange-coding.net/tracking';
export const track = function () { let cached = null;
//only send tracking information if the user allowed to do so. let lastSent = 0;
if (config.analyticsEnabled && !inDevMode()) { const SIX_HOURS = 6 * 3_600_000;
const activeProvider = new Set();
const activeAdapter = new Set();
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) { const jobs = getJobs();
jobs.forEach((job) => {
job.provider.forEach((provider) => { if (jobs != null && jobs.length > 0) {
activeProvider.add(provider.id); 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( const trackingObj = enrichTrackingObject({
'fredy_tracking',
enrichTrackingObject({
adapter: Array.from(activeAdapter), adapter: Array.from(activeAdapter),
provider: Array.from(activeProvider), 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 * 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) { if (config.analyticsEnabled && !inDevMode() && config.demoMode) {
mixpanelTracker.track('demoJobCreated', enrichTrackingObject(jobData)); try {
} await fetch(`${FREDY_TRACKING_URL}/demo/accessed`, {
} method: 'POST',
headers: { 'Content-Type': 'application/json' },
/** });
* Note, this will only be used when Fredy runs in demo mode } catch (error) {
*/ console.warn('Error sending tracking data', error);
export function trackDemoAccessed() { }
if (config.analyticsEnabled && !inDevMode() && config.demoMode) {
mixpanelTracker.track('demoAccessed', enrichTrackingObject({}));
} }
} }
function enrichTrackingObject(trackingObject) { function enrichTrackingObject(trackingObject) {
const operating_system = os.platform(); const operatingSystem = os.platform();
const os_version = os.release(); const osVersion = os.release();
const arch = process.arch; const arch = process.arch;
const language = process.env.LANG || 'en'; const language = process.env.LANG || 'en';
const nodeVersion = process.version || 'N/A'; const nodeVersion = process.version || 'N/A';
@@ -67,13 +79,13 @@ function enrichTrackingObject(trackingObject) {
return { return {
...trackingObject, ...trackingObject,
isDemo: config.demoMode, isDemo: config.demoMode,
operating_system, operatingSystem,
os_version, osVersion,
arch, arch,
nodeVersion, nodeVersion,
language, language,
distinct_id, deviceId,
fredy_version: version, version,
}; };
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "fredy", "name": "fredy",
"version": "11.6.0", "version": "11.6.1",
"description": "[F]ind [R]eal [E]states [d]amn eas[y].", "description": "[F]ind [R]eal [E]states [d]amn eas[y].",
"scripts": { "scripts": {
"prepare": "husky", "prepare": "husky",
@@ -70,7 +70,6 @@
"lodash": "4.17.21", "lodash": "4.17.21",
"lowdb": "7.0.1", "lowdb": "7.0.1",
"markdown": "^0.5.0", "markdown": "^0.5.0",
"mixpanel": "^0.18.1",
"nanoid": "5.1.5", "nanoid": "5.1.5",
"node-fetch": "3.3.2", "node-fetch": "3.3.2",
"node-mailjet": "6.0.9", "node-mailjet": "6.0.9",

View File

@@ -43,7 +43,8 @@ export default function TrackingModal() {
</p> </p>
<p> <p>
However, it would be a huge help if youd allow me to collect some analytical data. Wait, before you click However, it would be a huge help if youd 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)
</p> </p>
<p> <p>
The data includes: names of active adapters/providers, OS, architecture, Node version, and language. The The data includes: names of active adapters/providers, OS, architecture, Node version, and language. The

View File

@@ -1979,13 +1979,6 @@ acorn@^8.0.0, acorn@^8.15.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== 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: agent-base@^7.1.0, agent-base@^7.1.2:
version "7.1.4" version "7.1.4"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" 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" agent-base "^7.1.0"
debug "^4.3.4" 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: https-proxy-agent@^7.0.6:
version "7.0.6" version "7.0.6"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" 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" for-in "^0.1.3"
is-extendable "^0.1.1" 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: mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
version "0.5.3" version "0.5.3"
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"