diff --git a/src/App.vue b/src/App.vue index 0c23bf0..b43b783 100644 --- a/src/App.vue +++ b/src/App.vue @@ -153,7 +153,11 @@ onMounted(async () => { } }); - // Check for updates automatically on startup + if (authState.value.is_authenticated) { + await loadPresenceData(); + startPresenceRefresh(); + } + checkForUpdatesAndInstall(); }); @@ -292,24 +296,22 @@ async function loadHackatimeInfo() { } async function loadPresenceData() { - const now = Date.now(); if (presenceFetchInProgress.value) { return; } + + const now = Date.now(); if (now < nextPresenceFetchAllowedAt.value) { return; } - if (now - lastPresenceFetchAt.value < 60_000) { - return; - } presenceFetchInProgress.value = true; try { - await api.initialize(); presenceData.value = await invoke("get_latest_heartbeat", { apiConfig: apiConfig.value }); lastPresenceFetchAt.value = Date.now(); + console.log("Heartbeat data fetched from backend:", presenceData.value); } catch (error: any) { console.error("Failed to load presence data:", error); const message = error?.message || ""; @@ -328,6 +330,7 @@ function startPresenceRefresh() { presenceRefreshInterval.value = null; } presenceRefreshInterval.value = setInterval(loadPresenceData, 60000); + console.log("Started heartbeat refresh interval (every 60 seconds)"); } function stopPresenceRefresh() { diff --git a/src/composables/usePostHog.ts b/src/composables/usePostHog.ts new file mode 100644 index 0000000..64a4735 --- /dev/null +++ b/src/composables/usePostHog.ts @@ -0,0 +1,70 @@ +import posthog from 'posthog-js' + +export function usePostHog() { + posthog.init('phc_xwC3ygQBfstlwaJ3i1lRtz6bON6CR5lHFz7UhlSW6SZ', { + api_host: 'https://at.leafd.dev', + ui_host: 'https://at.leafd.dev', + person_profiles: 'identified_only', + session_recording: { + + + maskAllInputs: true, + maskInputOptions: { + password: true, + email: true, + }, + + maskTextSelector: '*', + maskTextFn: (text, element) => { + + if (text.trim().length === 0) { + return text + } + + + if (element?.dataset?.['record'] === 'true') { + return text + } + + + const emailRegex = /(\S+)@(\S+\.\S+)/g + if (emailRegex.test(text)) { + return text.replace(emailRegex, (_match, g1, g2) => { + return '*'.repeat(g1.length) + '@' + '*'.repeat(g2.length) + }) + } + + + const tokenRegex = /[a-zA-Z0-9_-]{20,}/g + if (tokenRegex.test(text)) { + return text.replace(tokenRegex, (match) => '*'.repeat(match.length)) + } + + return text + }, + maskInputFn: (text, element) => { + + const passwordRelated = ['password', 'pwd', 'pass'] + const elementId = (element?.attributes?.['id' as any]?.value as string)?.toLowerCase() || '' + const elementName = (element?.attributes?.['name' as any]?.value as string)?.toLowerCase() || '' + + if ( + passwordRelated.some(p => elementId.includes(p) || elementName.includes(p)) + ) { + return '*'.repeat(text.length) + } + + + if (element?.attributes?.['type' as any]?.value === 'search') { + return text + } + + + return '*'.repeat(text.length) + }, + }, + }) + + return { posthog } +} + diff --git a/src/composables/usePostHogInstance.ts b/src/composables/usePostHogInstance.ts new file mode 100644 index 0000000..208ade9 --- /dev/null +++ b/src/composables/usePostHogInstance.ts @@ -0,0 +1,13 @@ +import { inject } from 'vue' +import type { PostHog } from 'posthog-js' + +export function usePostHogInstance() { + const posthog = inject('posthog') + + if (!posthog) { + throw new Error('PostHog is not initialized') + } + + return posthog +} + diff --git a/src/main.ts b/src/main.ts index fe5bae3..7a9b211 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,30 @@ import { createApp } from 'vue' import App from './App.vue' import './style.css' +import * as Sentry from '@sentry/vue' +import { usePostHog } from './composables/usePostHog' -createApp(App).mount('#app') +const app = createApp(App) + + +const { posthog } = usePostHog() + + +app.provide('posthog', posthog) + +Sentry.init({ + app, + dsn: "https://d67e5cceba1b80139ca09c806efc616a@o4509680631087104.ingest.us.sentry.io/4510156240060417", + sendDefaultPii: true, + integrations: [ + Sentry.browserTracingIntegration() + ], + + tracesSampleRate: 1.0, + + tracePropagationTargets: ["localhost", /^https:\/\/yourserver\.io\/api/], + + enableLogs: true +}) + +app.mount('#app')