feat: remove umami (#4520)
This commit is contained in:
parent
cdc7d2ba47
commit
32436121c7
@ -103,7 +103,7 @@ jobs:
|
|||||||
# check public_provider is true or not
|
# check public_provider is true or not
|
||||||
echo "public_provider is ${{ inputs.public_provider }}"
|
echo "public_provider is ${{ inputs.public_provider }}"
|
||||||
if [ "${{ inputs.public_provider }}" == "none" ]; then
|
if [ "${{ inputs.public_provider }}" == "none" ]; then
|
||||||
make build
|
make build
|
||||||
else
|
else
|
||||||
make build-and-publish
|
make build-and-publish
|
||||||
fi
|
fi
|
||||||
@ -122,8 +122,6 @@ jobs:
|
|||||||
make build-and-publish
|
make build-and-publish
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
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_KEY: ${{ secrets.POSTHOG_KEY }}
|
||||||
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
|
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/template-build-macos.yml
vendored
4
.github/workflows/template-build-macos.yml
vendored
@ -134,7 +134,7 @@ jobs:
|
|||||||
# check public_provider is true or not
|
# check public_provider is true or not
|
||||||
echo "public_provider is ${{ inputs.public_provider }}"
|
echo "public_provider is ${{ inputs.public_provider }}"
|
||||||
if [ "${{ inputs.public_provider }}" == "none" ]; then
|
if [ "${{ inputs.public_provider }}" == "none" ]; then
|
||||||
make build
|
make build
|
||||||
else
|
else
|
||||||
make build-and-publish
|
make build-and-publish
|
||||||
fi
|
fi
|
||||||
@ -168,8 +168,6 @@ jobs:
|
|||||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||||
APP_PATH: '.'
|
APP_PATH: '.'
|
||||||
DEVELOPER_ID: ${{ secrets.DEVELOPER_ID }}
|
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_KEY: ${{ secrets.POSTHOG_KEY }}
|
||||||
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
|
POSTHOG_HOST: ${{ secrets.POSTHOG_HOST }}
|
||||||
|
|
||||||
|
|||||||
@ -136,7 +136,7 @@ jobs:
|
|||||||
# check public_provider is true or not
|
# check public_provider is true or not
|
||||||
echo "public_provider is ${{ inputs.public_provider }}"
|
echo "public_provider is ${{ inputs.public_provider }}"
|
||||||
if [ "${{ inputs.public_provider }}" == "none" ]; then
|
if [ "${{ inputs.public_provider }}" == "none" ]; then
|
||||||
make build
|
make build
|
||||||
else
|
else
|
||||||
make build-and-publish
|
make build-and-publish
|
||||||
fi
|
fi
|
||||||
@ -160,8 +160,6 @@ jobs:
|
|||||||
make build-and-publish
|
make build-and-publish
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
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_KEY_VAULT_URI: ${{ secrets.AZURE_KEY_VAULT_URI }}
|
||||||
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
|
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
|
||||||
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
||||||
|
|||||||
@ -23,6 +23,4 @@ Adhering to Jan's privacy preserving philosophy, our analytics philosophy is to
|
|||||||
## What is tracked
|
## 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.
|
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.
|
2. Additionally, we plan to enable a `Settings` feature for users to turn off all tracking.
|
||||||
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.
|
|
||||||
|
|||||||
@ -10,27 +10,20 @@ Homebrew Computer Company is committed to protecting your privacy and ensuring t
|
|||||||
|
|
||||||
## Data Collection
|
## 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
|
### 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.
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">
|
||||||
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.
|
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.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
Cortex is a library that runs large language models (LLMs) locally on your computer. Cortex does not collect any personal information.
|
Cortex is a library that runs large language models (LLMs) locally on your computer. Cortex does not collect any personal information.
|
||||||
|
|||||||
@ -9,8 +9,6 @@ import JotaiWrapper from '@/containers/Providers/Jotai'
|
|||||||
|
|
||||||
import ThemeWrapper from '@/containers/Providers/Theme'
|
import ThemeWrapper from '@/containers/Providers/Theme'
|
||||||
|
|
||||||
import Umami from '@/utils/umami'
|
|
||||||
|
|
||||||
import { CoreConfigurator } from './CoreConfigurator'
|
import { CoreConfigurator } from './CoreConfigurator'
|
||||||
import DataLoader from './DataLoader'
|
import DataLoader from './DataLoader'
|
||||||
|
|
||||||
@ -26,7 +24,6 @@ const Providers = ({ children }: PropsWithChildren) => {
|
|||||||
<SWRConfigProvider>
|
<SWRConfigProvider>
|
||||||
<ThemeWrapper>
|
<ThemeWrapper>
|
||||||
<JotaiWrapper>
|
<JotaiWrapper>
|
||||||
<Umami />
|
|
||||||
<CoreConfigurator>
|
<CoreConfigurator>
|
||||||
<>
|
<>
|
||||||
<Responsive />
|
<Responsive />
|
||||||
|
|||||||
@ -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)
|
|
||||||
})()
|
|
||||||
@ -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 && (
|
|
||||||
<Script
|
|
||||||
src={analyticsScriptPath}
|
|
||||||
data-website-id={analyticsId}
|
|
||||||
data-cache="true"
|
|
||||||
data-host-url="https://eu.umami.is"
|
|
||||||
defer
|
|
||||||
onLoad={() => document.dispatchEvent(new Event('umami:loaded'))}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Umami
|
|
||||||
Loading…
x
Reference in New Issue
Block a user