diff --git a/packages/client/src/api/analytics.js b/packages/client/src/api/analytics.js new file mode 100644 index 0000000000..5a089eaa21 --- /dev/null +++ b/packages/client/src/api/analytics.js @@ -0,0 +1,10 @@ +import API from "./api" + +/** + * Notifies that an end user client app has been loaded. + */ +export const pingEndUser = async () => { + return await API.post({ + url: `/api/analytics/ping`, + }) +} diff --git a/packages/client/src/api/index.js b/packages/client/src/api/index.js index d83ede61f0..1fd5b18139 100644 --- a/packages/client/src/api/index.js +++ b/packages/client/src/api/index.js @@ -9,3 +9,4 @@ export * from "./routes" export * from "./queries" export * from "./app" export * from "./automations" +export * from "./analytics" diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 7d231b3762..462daf7021 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -41,6 +41,8 @@ dataLoaded = true if ($builderStore.inBuilder) { builderStore.actions.notifyLoaded() + } else { + builderStore.actions.pingEndUser() } }) diff --git a/packages/client/src/stores/builder.js b/packages/client/src/stores/builder.js index 0d738e4a11..2740a4551c 100644 --- a/packages/client/src/stores/builder.js +++ b/packages/client/src/stores/builder.js @@ -1,6 +1,7 @@ import { writable, derived } from "svelte/store" import Manifest from "manifest.json" import { findComponentById, findComponentPathById } from "../utils/components" +import { pingEndUser } from "../api" const dispatchEvent = (type, data = {}) => { window.dispatchEvent( @@ -64,6 +65,9 @@ const createBuilderStore = () => { notifyLoaded: () => { dispatchEvent("preview-loaded") }, + pingEndUser: () => { + pingEndUser() + }, setSelectedPath: path => { writableStore.update(state => { state.selectedPath = path diff --git a/packages/server/package.json b/packages/server/package.json index 8e7c4d4547..b3f1303cb7 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -104,6 +104,7 @@ "open": "7.3.0", "pg": "8.5.1", "pino-pretty": "4.0.0", + "posthog-node": "^1.1.4", "pouchdb": "7.2.1", "pouchdb-adapter-memory": "^7.2.1", "pouchdb-all-dbs": "1.0.2", diff --git a/packages/server/src/api/controllers/analytics.js b/packages/server/src/api/controllers/analytics.js index eb64bc87b9..ef410aace5 100644 --- a/packages/server/src/api/controllers/analytics.js +++ b/packages/server/src/api/controllers/analytics.js @@ -1,7 +1,27 @@ const env = require("../../environment") +const PostHog = require("posthog-node") + +let posthogClient + +if (env.POSTHOG_TOKEN && env.ENABLE_ANALYTICS && !env.SELF_HOSTED) { + posthogClient = new PostHog(env.POSTHOG_TOKEN) +} exports.isEnabled = async function (ctx) { ctx.body = { enabled: !env.SELF_HOSTED && env.ENABLE_ANALYTICS === "true", } } + +exports.endUserPing = async (ctx, next) => { + if (!posthogClient) return next() + + posthogClient.capture("budibase:end_user_ping", { + userId: ctx.user && ctx.user._id, + appId: ctx.appId, + }) + + ctx.body = { + ping: true, + } +} diff --git a/packages/server/src/api/routes/analytics.js b/packages/server/src/api/routes/analytics.js index 781a959acb..632e99d031 100644 --- a/packages/server/src/api/routes/analytics.js +++ b/packages/server/src/api/routes/analytics.js @@ -4,5 +4,6 @@ const controller = require("../controllers/analytics") const router = Router() router.get("/api/analytics", controller.isEnabled) +router.post("/api/analytics/ping", controller.endUserPing) module.exports = router diff --git a/packages/server/src/environment.js b/packages/server/src/environment.js index f528a78729..e7bc35b04b 100644 --- a/packages/server/src/environment.js +++ b/packages/server/src/environment.js @@ -53,6 +53,7 @@ module.exports = { AUTOMATION_BUCKET: process.env.AUTOMATION_BUCKET, SENDGRID_API_KEY: process.env.SENDGRID_API_KEY, DYNAMO_ENDPOINT: process.env.DYNAMO_ENDPOINT, + POSTHOG_TOKEN: process.env.POSTHOG_TOKEN, // old - to remove CLIENT_ID: process.env.CLIENT_ID, BUDIBASE_DIR: process.env.BUDIBASE_DIR, diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 938077b90d..a041dbf44e 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -943,10 +943,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/auth@^0.9.146-alpha.3": - version "0.9.146" - resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.146.tgz#920fe02a78ca17903b72ccde307ca3e82b4176ad" - integrity sha512-T7DhI3WIolD0CjO2pRCEZfJBpJce4cmZWTFRIZ8lBnKe/6dxkK9fNrkZDYRhRkMwQbDQXoARADZM1hAfgUsSMg== +"@budibase/auth@^0.9.147-alpha.0": + version "0.9.147" + resolved "https://registry.yarnpkg.com/@budibase/auth/-/auth-0.9.147.tgz#8b959f5dae586ac4e210c7b8d1d3212859a664bc" + integrity sha512-DL5kXc+fU6pteTWiaJG2/MYEra/gwuT3ThTHfaUIinNta1VVAFfwLWHOg5fcKUaaXvQO9GWMJPbHT9b6hJnqFA== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -1015,10 +1015,10 @@ svelte-flatpickr "^3.1.0" svelte-portal "^1.0.0" -"@budibase/bbui@^0.9.146": - version "0.9.146" - resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.146.tgz#7689b2c0f148321e62969181e3f6549f03dd3e78" - integrity sha512-Mq0oMyaN18Dg5e0IPtPXSGmu/TS4B74gW+l2ypJDNTzSRm934DOAPghDgkb53rFNZhsovCYjixJZmesUcv2o3g== +"@budibase/bbui@^0.9.147": + version "0.9.147" + resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-0.9.147.tgz#b2442a4d2259afdcbf14db6223153e4e561a249e" + integrity sha512-7GL45a9VMaxmHdbXh0xSBM+Mzw6YJCVRsAtoi0oMkd34U80M7xD58CZykb+0+4JZ8CMZqQnjBvv7QrgeWcWRaA== dependencies: "@adobe/spectrum-css-workflow-icons" "^1.2.1" "@spectrum-css/actionbutton" "^1.0.1" @@ -1064,14 +1064,14 @@ svelte-flatpickr "^3.1.0" svelte-portal "^1.0.0" -"@budibase/client@^0.9.146-alpha.3": - version "0.9.146" - resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.146.tgz#d3b1bbd67245ab5a3870ccb580b9fc76f0344fd6" - integrity sha512-vd/bMmiQVghFH3Pa9jrGXjYAAKo+lGrwWyfUSdXAb4XP6gCSnMK5BXf8NliNrQzQVmruYT+2rGMsnc+9q4lW1g== +"@budibase/client@^0.9.147-alpha.0": + version "0.9.147" + resolved "https://registry.yarnpkg.com/@budibase/client/-/client-0.9.147.tgz#c9171a52d15ce99df433e977c7ea716e8aad062e" + integrity sha512-v9AnWJIs+1wesW65vbFab/fPmaWtiyIJKiEoS2ff0pANbCOVOBL3c1PWYM+BwK6r8vH9J54h/ReMCCGcaXzyGQ== dependencies: - "@budibase/bbui" "^0.9.146" + "@budibase/bbui" "^0.9.147" "@budibase/standard-components" "^0.9.139" - "@budibase/string-templates" "^0.9.146" + "@budibase/string-templates" "^0.9.147" regexparam "^1.3.0" shortid "^2.2.15" svelte-spa-router "^3.0.5" @@ -1122,10 +1122,10 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/string-templates@^0.9.146", "@budibase/string-templates@^0.9.146-alpha.3": - version "0.9.146" - resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.146.tgz#85249c7a8777a5f0c280af6f6d0e3d3ff0bf20b5" - integrity sha512-4f91SVUaTKseB+j7ycWbP54XiqiFZ6bZvcKgzsg1mLF+VVJ1/ALUsLvCRaj6SlcSHrhhALiGVR1z18KOyBWoKw== +"@budibase/string-templates@^0.9.147", "@budibase/string-templates@^0.9.147-alpha.0": + version "0.9.147" + resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-0.9.147.tgz#48d0815c15bf3b0905f54463e309d47c0274595a" + integrity sha512-wuj20uMRXvpw5P4ScHen9n0kDfoVO0F9yi9HEPZpHF/pcAeTfL2+ohBr9irb/H4W9nHpMS0X5G/fGPEc5zqsUw== dependencies: "@budibase/handlebars-helpers" "^0.11.4" dayjs "^1.10.4" @@ -2994,6 +2994,13 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios-retry@^3.1.9: + version "3.2.0" + resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.2.0.tgz#eb48e72f90b177fde62329b2896aa8476cfb90ba" + integrity sha512-RK2cLMgIsAQBDhlIsJR5dOhODPigvel18XUv1dDXW+4k1FzebyfRk+C+orot6WPZOYFKSfhLwHPwVmTVOODQ5w== + dependencies: + is-retry-allowed "^1.1.0" + axios@^0.21.1: version "0.21.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" @@ -3574,6 +3581,11 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= + chmodr@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/chmodr/-/chmodr-1.2.0.tgz#720e96caa09b7f1cdbb01529b7d0ab6bc5e118b9" @@ -3784,6 +3796,11 @@ component-emitter@^1.2.0, component-emitter@^1.2.1: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +component-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9" + integrity sha1-ikeQFwAjjk/DIml3EjAibyS0Fak= + compressible@^2.0.0: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -3946,6 +3963,11 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= + crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -6127,7 +6149,7 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5: +is-buffer@^1.1.5, is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -7299,6 +7321,11 @@ joi@17.2.1: "@hapi/pinpoint" "^2.0.0" "@hapi/topo" "^5.0.0" +join-component@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" + integrity sha1-uEF7dQZho5K+4sJTfGiyqdSXfNU= + joycon@^2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/joycon/-/joycon-2.2.5.tgz#8d4cf4cbb2544d7b7583c216fcdfec19f6be1615" @@ -8149,6 +8176,15 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -8356,7 +8392,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: +ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -9249,6 +9285,20 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" +posthog-node@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-1.1.4.tgz#314043b2d4e20010890f74854512447d717e71d3" + integrity sha512-XKyIzWIz6xJh8RfIpFPNFmFye/cDtfq2pcUEaLb/X5fX1KZJoiQ57OBTIrEjExIFjEU4bUrsrEA0F7FyA1sbFg== + dependencies: + axios "^0.21.1" + axios-retry "^3.1.9" + component-type "^1.2.1" + join-component "^1.1.0" + md5 "^2.3.0" + ms "^2.1.3" + remove-trailing-slash "^0.1.1" + uuid "^8.3.2" + pouch-stream@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/pouch-stream/-/pouch-stream-0.4.1.tgz#0c6d8475c9307677627991a2f079b301c3b89bdd" @@ -9949,6 +9999,11 @@ remove-trailing-separator@^1.0.1: resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= +remove-trailing-slash@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" + integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== + repeat-element@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9"