init mixpanel (#227)

* #217 iinit mixpanel and track installation succes and failure

---------

Co-authored-by: neil <neil@neils-MacBook-Pro.local>
This commit is contained in:
Neil 2023-02-24 12:24:15 +08:00 committed by GitHub
parent cf0a2d1b8a
commit c61712fd6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 107 additions and 16 deletions

View file

@ -88,6 +88,7 @@ jobs:
if: startsWith(inputs.platform, 'darwin')
run: tea -SE xc dist
env:
PUBLIC_MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_PROJECT_TOKEN }}
DEBUG_BUILD: ${{ inputs.debug }}
PUBLIC_VERSION: ${{ steps.gui-version.outputs.version }}
USE_HARD_LINKS: false

View file

@ -160,7 +160,3 @@ ipcMain.handle("open-terminal", async (_, data) => {
console.error("elast:", error);
}
});
ipcMain.handle("get-lang", function () {
return app.getLocale();
});

View file

@ -3,6 +3,7 @@ import path from "path";
import fs from "fs";
import { getTeaPath } from "./tea-dir";
import * as v1Client from "./v1-client";
import { app } from "electron";
const sessionFilePath = path.join(getTeaPath(), "tea.xyz/gui/tmp.dat");
const sessionFolder = path.join(getTeaPath(), "tea.xyz/gui");
@ -11,6 +12,7 @@ interface Session {
device_id?: string;
key?: string;
user?: any;
locale?: string;
}
export async function initSessionData() {
@ -21,14 +23,16 @@ export async function initSessionData() {
}
export async function readSessionData(): Promise<Session> {
const locale = app.getLocale();
try {
const sessionBuffer = await fs.readFileSync(sessionFilePath);
const session = JSON.parse(sessionBuffer.toString()) as Session;
session.locale = locale;
return session;
} catch (error) {
console.error(error);
const req = await v1Client.get<{ deviceId: string }>("/auth/registerDevice");
const data = { device_id: req.deviceId };
const data = { device_id: req.deviceId, locale };
await writeSessionData(data);
return data;
}

View file

@ -41,6 +41,9 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/svelte": "^3.2.2",
"@types/testing-library__jest-dom": "^5.14.5",
"@types/bcryptjs": "^2.4.2",
"@types/js-yaml": "^4.0.5",
"@types/mixpanel-browser": "^2.38.1",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"autoprefixer": "^10.4.13",
@ -70,8 +73,6 @@
"type": "module",
"dependencies": {
"@electron/asar": "^3.2.3",
"@types/bcryptjs": "^2.4.2",
"@types/js-yaml": "^4.0.5",
"@vitest/coverage-c8": "^0.27.1",
"axios": "^1.3.2",
"bcryptjs": "^2.4.3",
@ -87,6 +88,7 @@
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"lorem-ipsum": "^2.0.8",
"mixpanel-browser": "^2.45.0",
"mkdirp": "^2.1.3",
"semver": "^7.3.8",
"svelte-markdown": "^0.2.3",

View file

@ -7,6 +7,7 @@
import { onMount } from 'svelte';
import { getPackageBottles } from '@native';
import { openTerminal } from '@native';
import { trackInstall, trackInstallFailed } from '$libs/analytics';
export let pkg: Package;
let bottles: Bottle[] = [];
@ -20,7 +21,14 @@
};
const onOpenTerminal = () => {
openTerminal(`sh <(curl tea.xyz) +${pkg.full_name}`);
try {
openTerminal(`sh <(curl tea.xyz) +${pkg.full_name}`);
trackInstall(pkg.full_name);
} catch (error) {
let message = 'Unknown Error'
if (error instanceof Error) message = error.message
trackInstallFailed(pkg.full_name, message || "unknown");
}
}
onMount(async () => {

View file

@ -7,6 +7,7 @@
import { packagesStore } from '$libs/stores';
import { installPackage } from '@native';
import { trackInstall, trackInstallFailed } from '$libs/analytics';
export let title = 'Packages';
export let category = ''; // filter
@ -45,9 +46,12 @@
try {
pkg.state = PackageStates.INSTALLING;
await installPackage(pkg.full_name);
trackInstall(pkg.full_name);
pkg.state = PackageStates.INSTALLED;
} catch (error) {
console.error(error);
let message = 'Unknown Error'
if (error instanceof Error) message = error.message
trackInstallFailed(pkg.full_name, message || "unknown");
}
}}
/>

View file

@ -0,0 +1,65 @@
import mixpanel from "mixpanel-browser";
import * as pub from "$env/static/public";
import { getSession } from "@native";
type DefaultMixpanelProps = {
device_id: string;
uid: string;
distinct_id: string;
locale: string;
gui_version: string;
};
let mixpanelDefaultData: DefaultMixpanelProps;
const getLocalSession = async (): Promise<DefaultMixpanelProps> => {
if (mixpanelDefaultData) return mixpanelDefaultData;
const session = await getSession();
mixpanelDefaultData = {
device_id: session?.device_id || "unregistered",
uid: session?.user?.developer_id || "unauthenticated",
distinct_id: session?.device_id || "unregistered",
locale: session?.locale || "unknown",
gui_version: pub.PUBLIC_VERSION
};
return mixpanelDefaultData;
};
mixpanel.init(pub.PUBLIC_MIXPANEL_TOKEN, { debug: true });
enum AnalyticsAction {
install = "INSTALL_ACTION",
install_failed = "INSTALL_ACTION_FAILED"
}
const trackAction = (action: AnalyticsAction, data?: { [key: string]: any }) => {
getLocalSession()
.then((props) => {
mixpanel.track(action, {
...(data || {}),
...props
});
})
.catch((error) => {
// TODO: log remote error stream
console.error(error);
});
};
/**
* save success installation event to mixpanel
* @param packageFullname full installation name of the package
* @returns void
*/
export const trackInstall = (packageFullname: string) =>
trackAction(AnalyticsAction.install, { pkg: packageFullname });
/**
* save failed installation event to mixpanel
* @param packageFullname full installation name of the package
* @param error error message
*/
export const trackInstallFailed = (packageFullname: string, error: string) => {
trackAction(AnalyticsAction.install_failed, {
pkg: packageFullname,
error
});
};

View file

@ -5,8 +5,6 @@ import type { Developer } from "@tea/ui/types";
import type { Session } from "$libs/types";
import { getSession as electronGetSession, updateSession as electronUpdateSession } from "@native";
const basePath = ".tea/tea.xyz/gui";
export let session: Session | null = null;
export const getSession = async (): Promise<Session | null> => {
session = await electronGetSession();

View file

@ -47,4 +47,5 @@ export interface Session {
device_id?: string;
key?: string;
user?: Developer;
locale?: string;
}

View file

@ -20,6 +20,7 @@ importers:
'@testing-library/svelte': ^3.2.2
'@types/bcryptjs': ^2.4.2
'@types/js-yaml': ^4.0.5
'@types/mixpanel-browser': ^2.38.1
'@types/testing-library__jest-dom': ^5.14.5
'@typescript-eslint/eslint-plugin': ^5.27.0
'@typescript-eslint/parser': ^5.27.0
@ -48,6 +49,7 @@ importers:
jsdom: ^21.0.0
lodash: ^4.17.21
lorem-ipsum: ^2.0.8
mixpanel-browser: ^2.45.0
mkdirp: ^2.1.3
postcss: ^8.4.19
prettier: ^2.7.1
@ -70,8 +72,6 @@ importers:
yaml: ^2.2.1
dependencies:
'@electron/asar': 3.2.3
'@types/bcryptjs': 2.4.2
'@types/js-yaml': 4.0.5
'@vitest/coverage-c8': 0.27.1_jsdom@21.0.0
axios: 1.3.2
bcryptjs: 2.4.3
@ -87,6 +87,7 @@ importers:
js-yaml: 4.1.0
lodash: 4.17.21
lorem-ipsum: 2.0.8
mixpanel-browser: 2.45.0
mkdirp: 2.1.3
semver: 7.3.8
svelte-markdown: 0.2.3_svelte@3.55.1
@ -105,6 +106,9 @@ importers:
'@tea/ui': link:../ui
'@testing-library/jest-dom': 5.16.5
'@testing-library/svelte': 3.2.2_svelte@3.55.1
'@types/bcryptjs': 2.4.2
'@types/js-yaml': 4.0.5
'@types/mixpanel-browser': 2.38.1
'@types/testing-library__jest-dom': 5.14.5
'@typescript-eslint/eslint-plugin': 5.43.0_wze2rj5tow7zwqpgbdx2buoy3m
'@typescript-eslint/parser': 5.43.0_e3uo4sehh4zr4i6m57mkkxxv7y
@ -3792,7 +3796,7 @@ packages:
peerDependencies:
'@sveltejs/kit': ^1.0.0
dependencies:
'@sveltejs/kit': 1.0.1_svelte@3.55.1+vite@4.1.1
'@sveltejs/kit': 1.0.1_svelte@3.55.0+vite@4.1.1
import-meta-resolve: 2.2.0
dev: true
@ -4079,7 +4083,7 @@ packages:
/@types/bcryptjs/2.4.2:
resolution: {integrity: sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==}
dev: false
dev: true
/@types/body-parser/1.19.2:
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
@ -4209,7 +4213,7 @@ packages:
/@types/js-yaml/4.0.5:
resolution: {integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==}
dev: false
dev: true
/@types/json-schema/7.0.11:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
@ -4250,6 +4254,10 @@ packages:
/@types/minimatch/5.1.2:
resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
/@types/mixpanel-browser/2.38.1:
resolution: {integrity: sha512-XzQbwgiOPsFXUQnjz3vSwcwrvJDbQ35bCiwa/1VXGrHvU1ti9+eqO1GY91DShzkEzKkkEEkxfNshS5dbBZqd7w==}
dev: true
/@types/ms/0.7.31:
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
dev: true
@ -9745,6 +9753,10 @@ packages:
is-extendable: 1.0.1
dev: true
/mixpanel-browser/2.45.0:
resolution: {integrity: sha512-PQ1DaTk68yyYtLA0iejmzPA9iNDhT4uIZpqZjRTw7HWpYfl123fydHb2laKanaKjm8YDmrGGz3+xZ4Q6joogyg==}
dev: false
/mkdirp/0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
hasBin: true