From 32436121c7868e7598f06b541e5013536fb5d0b9 Mon Sep 17 00:00:00 2001 From: Faisal Amir Date: Sun, 26 Jan 2025 20:42:03 +0700 Subject: [PATCH] feat: remove umami (#4520) --- .../workflows/template-build-linux-x64.yml | 4 +- .github/workflows/template-build-macos.yml | 4 +- .../workflows/template-build-windows-x64.yml | 4 +- docs/src/pages/about/handbook/analytics.mdx | 4 +- docs/src/pages/privacy.mdx | 15 +- web/containers/Providers/index.tsx | 3 - web/public/umami_script.js | 210 ------------------ web/utils/umami.tsx | 67 ------ 8 files changed, 8 insertions(+), 303 deletions(-) delete mode 100644 web/public/umami_script.js delete mode 100644 web/utils/umami.tsx diff --git a/.github/workflows/template-build-linux-x64.yml b/.github/workflows/template-build-linux-x64.yml index 9d12c4394..85b050e62 100644 --- a/.github/workflows/template-build-linux-x64.yml +++ b/.github/workflows/template-build-linux-x64.yml @@ -103,7 +103,7 @@ jobs: # check public_provider is true or not echo "public_provider is ${{ inputs.public_provider }}" if [ "${{ inputs.public_provider }}" == "none" ]; then - make build + make build else make build-and-publish fi @@ -122,8 +122,6 @@ jobs: make build-and-publish env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ANALYTICS_ID: ${{ secrets.JAN_APP_UMAMI_PROJECT_API_KEY }} - ANALYTICS_HOST: ${{ secrets.JAN_APP_UMAMI_URL }} POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }} POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }} diff --git a/.github/workflows/template-build-macos.yml b/.github/workflows/template-build-macos.yml index b415d665d..2eabd9ce2 100644 --- a/.github/workflows/template-build-macos.yml +++ b/.github/workflows/template-build-macos.yml @@ -134,7 +134,7 @@ jobs: # check public_provider is true or not echo "public_provider is ${{ inputs.public_provider }}" if [ "${{ inputs.public_provider }}" == "none" ]; then - make build + make build else make build-and-publish fi @@ -168,8 +168,6 @@ jobs: APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} APP_PATH: '.' DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }} - ANALYTICS_ID: ${{ secrets.JAN_APP_UMAMI_PROJECT_API_KEY }} - ANALYTICS_HOST: ${{ secrets.JAN_APP_UMAMI_URL }} POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }} POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }} diff --git a/.github/workflows/template-build-windows-x64.yml b/.github/workflows/template-build-windows-x64.yml index 52ff22ce3..a317b4960 100644 --- a/.github/workflows/template-build-windows-x64.yml +++ b/.github/workflows/template-build-windows-x64.yml @@ -136,7 +136,7 @@ jobs: # check public_provider is true or not echo "public_provider is ${{ inputs.public_provider }}" if [ "${{ inputs.public_provider }}" == "none" ]; then - make build + make build else make build-and-publish fi @@ -160,8 +160,6 @@ jobs: make build-and-publish env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ANALYTICS_ID: ${{ secrets.JAN_APP_UMAMI_PROJECT_API_KEY }} - ANALYTICS_HOST: ${{ secrets.JAN_APP_UMAMI_URL }} AZURE_KEY_VAULT_URI: ${{ secrets.AZURE_KEY_VAULT_URI }} AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} diff --git a/docs/src/pages/about/handbook/analytics.mdx b/docs/src/pages/about/handbook/analytics.mdx index 9e7833e32..5cc34209d 100644 --- a/docs/src/pages/about/handbook/analytics.mdx +++ b/docs/src/pages/about/handbook/analytics.mdx @@ -23,6 +23,4 @@ Adhering to Jan's privacy preserving philosophy, our analytics philosophy is to ## What is tracked 1. By default, Github tracks downloads and device metadata for all public GitHub repositories. This helps us troubleshoot & ensure cross-platform support. -2. We use [Umami](https://umami.is/) to collect, analyze, and understand application data while maintaining visitor privacy and data ownership. We are using the Umami Cloud in Europe to ensure GDPR compliance. Please see [Umami Privacy Policy](https://umami.is/privacy) for more details. -3. We use Umami to track a single `app.opened` event without additional user metadata, in order to understand retention. In addition, we track `app.version` to understand app version usage. -4. Additionally, we plan to enable a `Settings` feature for users to turn off all tracking. +2. Additionally, we plan to enable a `Settings` feature for users to turn off all tracking. diff --git a/docs/src/pages/privacy.mdx b/docs/src/pages/privacy.mdx index bb77327e6..44127a37a 100644 --- a/docs/src/pages/privacy.mdx +++ b/docs/src/pages/privacy.mdx @@ -10,27 +10,20 @@ Homebrew Computer Company is committed to protecting your privacy and ensuring t ## Data Collection -Jan, Cortex, and all Homebrew Computer Company products do not collect personally identifying information. You can read about [our philosophy](/about#philosophy) here and audit our open-source codebases. +Jan, Cortex, and all Homebrew Computer Company products do not collect personally identifying information. You can read about [our philosophy](/about#philosophy) here and audit our open-source codebases. ### When you voluntarily provide data -We -do- collect personal information you voluntarily provide us, e.g., when you sign up for our newsletter, join our Discord, or contact us via email. +We -do- collect personal information you voluntarily provide us, e.g., when you sign up for our newsletter, join our Discord, or contact us via email. -### Jan +### Jan -Jan runs with privacy by default and is used 100% offline on your own computer. Your data (e.g., conversation history, usage logs) are stored locally and never leave your computer. +Jan runs with privacy by default and is used 100% offline on your own computer. Your data (e.g., conversation history, usage logs) are stored locally and never leave your computer. If you use a Remote AI API (e.g., OpenAI API, Groq API), your data will naturally travel to their servers. They will be subject to the privacy policy of the respective API provider. -Jan uses [Umami](https://umami.is/) for analytics, which is a privacy-focused, GDPR-compliant analytics tool that does not track personal information. We use this to get aggregate reports on OS and hardware types and prioritize our engineering roadmap. As per [Umami's Privacy Policy](https://umami.is/privacy), Umami uses the following data points to generate its reports: - -- OS and device characteristics -- IP address - -Jan does not get any of this data, and we do not track IP addresses or other identifying information. We are actively looking into more privacy-respecting ways to handle analytics, crash reports, and telemetry and would love to work with the community on this. - ### Cortex Cortex is a library that runs large language models (LLMs) locally on your computer. Cortex does not collect any personal information. diff --git a/web/containers/Providers/index.tsx b/web/containers/Providers/index.tsx index 84e0860ea..5d14ea95a 100644 --- a/web/containers/Providers/index.tsx +++ b/web/containers/Providers/index.tsx @@ -9,8 +9,6 @@ import JotaiWrapper from '@/containers/Providers/Jotai' import ThemeWrapper from '@/containers/Providers/Theme' -import Umami from '@/utils/umami' - import { CoreConfigurator } from './CoreConfigurator' import DataLoader from './DataLoader' @@ -26,7 +24,6 @@ const Providers = ({ children }: PropsWithChildren) => { - <> diff --git a/web/public/umami_script.js b/web/public/umami_script.js deleted file mode 100644 index b9db0b024..000000000 --- a/web/public/umami_script.js +++ /dev/null @@ -1,210 +0,0 @@ -!(function () { - 'use strict' - !(function (t) { - var e = t.screen, - n = e.width, - r = e.height, - a = t.navigator.language, - i = t.location, - o = t.localStorage, - u = t.document, - c = t.history, - f = 'jan.ai', - s = 'mainpage', - l = i.search, - d = u.currentScript - if (d) { - var m = 'data-', - h = d.getAttribute.bind(d), - v = h(m + 'website-id'), - p = h(m + 'host-url'), - g = 'false' !== h(m + 'auto-track'), - y = h(m + 'do-not-track'), - b = h(m + 'domains') || '', - S = b.split(',').map(function (t) { - return t.trim() - }), - k = - (p ? p.replace(/\/$/, '') : d.src.split('/').slice(0, -1).join('/')) + - '/api/send', - w = n + 'x' + r, - N = /data-umami-event-([\w-_]+)/, - T = m + 'umami-event', - j = 300, - A = function (t, e, n) { - var r = t[e] - return function () { - for (var e = [], a = arguments.length; a--; ) e[a] = arguments[a] - return n.apply(null, e), r.apply(t, e) - } - }, - x = function () { - return { - website: v, - hostname: f, - screen: w, - language: a, - title: M, - url: I, - referrer: J, - } - }, - E = function () { - return ( - (o && o.getItem('umami.disabled')) || - (y && - (function () { - var e = t.doNotTrack, - n = t.navigator, - r = t.external, - a = 'msTrackingProtectionEnabled', - i = - e || - n.doNotTrack || - n.msDoNotTrack || - (r && a in r && r[a]()) - return '1' == i || 'yes' === i - })()) || - (b && !S.includes(f)) - ) - }, - O = function (t, e, n) { - n && - ((J = I), - (I = (function (t) { - try { - return new URL(t).pathname - } catch (e) { - return t - } - })(n.toString())) !== J && setTimeout(D, j)) - }, - L = function (t, e) { - if ((void 0 === e && (e = 'event'), !E())) { - var n = { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'Content-Type': 'application/json', - } - return ( - void 0 !== K && (n['x-umami-cache'] = K), - fetch(k, { - method: 'POST', - body: JSON.stringify({ - type: e, - payload: t, - }), - headers: n, - }) - .then(function (t) { - return t.text() - }) - .then(function (t) { - return (K = t) - }) - .catch(function () {}) - ) - } - }, - D = function (t, e) { - return L( - 'string' == typeof t - ? Object.assign({}, x(), { - name: t, - data: 'object' == typeof e ? e : void 0, - }) - : 'object' == typeof t - ? t - : 'function' == typeof t - ? t(x()) - : x() - ) - } - t.umami || - (t.umami = { - track: D, - identify: function (t) { - return L( - Object.assign({}, x(), { - data: t, - }), - 'identify' - ) - }, - }) - var K, - P, - _, - q, - C, - I = '' + s + l, - J = u.referrer, - M = u.title - if (g && !E()) { - ;(c.pushState = A(c, 'pushState', O)), - (c.replaceState = A(c, 'replaceState', O)), - (C = function (t) { - var e = t.getAttribute.bind(t), - n = e(T) - if (n) { - var r = {} - return ( - t.getAttributeNames().forEach(function (t) { - var n = t.match(N) - n && (r[n[1]] = e(t)) - }), - D(n, r) - ) - } - return Promise.resolve() - }), - u.addEventListener( - 'click', - function (t) { - var e = t.target, - n = - 'A' === e.tagName - ? e - : (function (t, e) { - for (var n = t, r = 0; r < e; r++) { - if ('A' === n.tagName) return n - if (!(n = n.parentElement)) return null - } - return null - })(e, 10) - if (n) { - var r = n.href, - a = - '_blank' === n.target || - t.ctrlKey || - t.shiftKey || - t.metaKey || - (t.button && 1 === t.button) - if (n.getAttribute(T) && r) - return ( - a || t.preventDefault(), - C(n).then(function () { - a || (i.href = r) - }) - ) - } else C(e) - }, - !0 - ), - (_ = new MutationObserver(function (t) { - var e = t[0] - M = e && e.target ? e.target.text : void 0 - })), - (q = u.querySelector('head > title')) && - _.observe(q, { - subtree: !0, - characterData: !0, - childList: !0, - }) - var R = function () { - 'complete' !== u.readyState || P || (D(), (P = !0)) - } - u.addEventListener('readystatechange', R, !0), R() - } - } - })(window) -})() diff --git a/web/utils/umami.tsx b/web/utils/umami.tsx deleted file mode 100644 index dc406a7d2..000000000 --- a/web/utils/umami.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useEffect } from 'react' - -import Script from 'next/script' - -// Define the type for the umami data object -interface UmamiData { - version: string -} - -declare global { - interface Window { - umami: - | { - track: (event: string, data?: UmamiData) => void - } - | undefined - } -} - -const Umami = () => { - const appVersion = VERSION - const analyticsScriptPath = './umami_script.js' - const analyticsId = ANALYTICS_ID - - useEffect(() => { - if (!appVersion || !analyticsScriptPath || !analyticsId) return - - const ping = () => { - // Check if umami is defined before ping - if (window.umami !== null && typeof window.umami !== 'undefined') { - window.umami.track(appVersion, { - version: appVersion, - }) - } - } - - // Wait for umami to be defined before ping - if (window.umami !== null && typeof window.umami !== 'undefined') { - ping() - } else { - // Listen for umami script load event - document.addEventListener('umami:loaded', ping) - } - - // Cleanup function to remove event listener if the component unmounts - return () => { - document.removeEventListener('umami:loaded', ping) - } - }, [appVersion, analyticsScriptPath, analyticsId]) - - return ( - <> - {appVersion && analyticsScriptPath && analyticsId && ( -