Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement analytics in Nuxt #844

Merged
merged 28 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
38f7041
Add Plausible to the default development environment
dhruvkb Mar 1, 2023
f78a345
Install dependency for `vue-plausible`
dhruvkb Mar 5, 2023
7850997
Configure Plausible in Nuxt
dhruvkb Mar 5, 2023
738e0f9
Create composable for firing events
dhruvkb Mar 5, 2023
77e4eb6
Record viewport dimensions in cookies
dhruvkb Mar 5, 2023
9e6628b
fixup! Create composable for firing events
dhruvkb Mar 6, 2023
a668c37
Create a utility to send events without `$plausible`
dhruvkb Mar 6, 2023
77715bf
Create a middleware to log `VIEW_PAGE` and `SERVER_RENDERED` events
dhruvkb Mar 6, 2023
e07796a
Fix failing tests
dhruvkb Mar 6, 2023
5309edc
Add RFC to use Plausible
dhruvkb Mar 7, 2023
34a74db
Merge branch 'main' of https://github.com/WordPress/openverse into pl…
dhruvkb Mar 7, 2023
c3ef0f4
Add info about cloud to Plausible RFC
dhruvkb Mar 7, 2023
49e6da1
Add a note on pricing
dhruvkb Mar 8, 2023
6f76bec
Add a colon before list.
dhruvkb Mar 8, 2023
087243f
Merge branch 'plausible' of https://github.com/WordPress/openverse in…
dhruvkb Mar 9, 2023
0512b9e
Proxy Plausible calls via the Nuxt server
dhruvkb Mar 9, 2023
1a4362a
Move static field outside runtime config
dhruvkb Mar 9, 2023
64623e0
Remove page-views middleware
dhruvkb Mar 9, 2023
e13457f
Remove function for sending events using Axios
dhruvkb Mar 9, 2023
7e06869
Remove code for storing dimensions in cookies
dhruvkb Mar 9, 2023
d0b441f
Simplify analytics composable
dhruvkb Mar 9, 2023
164e622
Drop redundant events
dhruvkb Mar 9, 2023
1adb039
Set `apiHost` instead of blank string which doesn't work
dhruvkb Mar 9, 2023
70f0dcd
Configure proxy using env var
dhruvkb Mar 10, 2023
5493ed9
Merge branch 'main' of https://github.com/WordPress/openverse into pl…
dhruvkb Mar 15, 2023
d2be96c
Toggle `plausible_ignore` with analytics flag
dhruvkb Mar 15, 2023
3466fe7
Include breakpoint in custom event props
dhruvkb Mar 15, 2023
2b41df4
Merge branch 'main' of https://github.com/WordPress/openverse into pl…
dhruvkb Mar 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions clickhouse/clickhouse-config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<clickhouse>
<logger>
<level>warning</level>
<console>true</console>
</logger>

<!-- Stop all the unnecessary logging -->
<query_thread_log remove="remove"/>
<query_log remove="remove"/>
<text_log remove="remove"/>
<trace_log remove="remove"/>
<metric_log remove="remove"/>
<asynchronous_metric_log remove="remove"/>
<session_log remove="remove"/>
<part_log remove="remove"/>
</clickhouse>
8 changes: 8 additions & 0 deletions clickhouse/clickhouse-user-config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<clickhouse>
<profiles>
<default>
<log_queries>0</log_queries>
<log_query_threads>0</log_query_threads>
</default>
</profiles>
</clickhouse>
39 changes: 37 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ services:
image: postgres:13.2-alpine
ports:
- "50254:5432"
volumes:
- api-postgres:/var/lib/postgresql/data
env_file:
- postgres/env.docker
healthcheck:
test: "pg_isready -U deploy -d openledger"
volumes:
- api-postgres:/var/lib/postgresql/data

upstream_db:
image: postgres:13.2-alpine
Expand All @@ -23,6 +23,39 @@ services:
healthcheck:
test: "pg_isready -U deploy -d openledger"

plausible_db:
image: postgres:13.2-alpine
expose:
- "5432"
volumes:
- plausible-postgres:/var/lib/postgresql/data
env_file:
- postgres/plausible.env.docker
healthcheck:
test: "pg_isready -U deploy -d plausible"

plausible_ch:
image: clickhouse/clickhouse-server:22.6-alpine
volumes:
- plausible-clickhouse:/var/lib/clickhouse
- ./clickhouse/clickhouse-config.xml:/etc/clickhouse-server/config.d/logging.xml:ro
- ./clickhouse/clickhouse-user-config.xml:/etc/clickhouse-server/users.d/logging.xml:ro
ulimits:
nofile:
soft: 262144
hard: 262144

plausible:
image: plausible/analytics:latest
ports:
- "50288:8000"
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
depends_on:
- plausible_db
- plausible_ch
env_file:
- plausible/env.docker

cache:
image: redis:4.0.10
ports:
Expand Down Expand Up @@ -134,4 +167,6 @@ services:
volumes:
api-postgres:
catalog-postgres:
plausible-postgres:
plausible-clickhouse:
es-data:
22 changes: 21 additions & 1 deletion frontend/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ const openverseLocales = [
...(locales ?? []),
].filter((l) => Boolean(l.iso)) as LocaleObject[]

const port = process.env.PORT || 8443

const config: NuxtConfig = {
// eslint-disable-next-line no-undef
version: pkg.version, // used to purge cache :)
Expand All @@ -161,7 +163,7 @@ const config: NuxtConfig = {
srcDir: "src/",
modern: "client",
server: {
port: process.env.PORT || 8443,
port,
https: process.env.LOCAL_SSL
? {
key: fs.readFileSync(path.resolve(__dirname, "localhost+1-key.pem")),
Expand Down Expand Up @@ -203,9 +205,11 @@ const config: NuxtConfig = {
modules: [
"portal-vue/nuxt",
"@nuxtjs/i18n",
"@nuxtjs/proxy",
"@nuxtjs/redirect-module",
"@nuxtjs/sentry",
"cookie-universal-nuxt",
"vue-plausible",
"~/modules/prometheus.ts",
// Sitemap must be last to ensure that even routes created by other modules are added
"@nuxtjs/sitemap",
Expand Down Expand Up @@ -328,6 +332,22 @@ const config: NuxtConfig = {
},
},
},
proxy: {
"/api/event": "http://localhost:50288", // the key is appended as the path
sarayourfriend marked this conversation as resolved.
Show resolved Hide resolved
},
plausible: {
trackLocalhost: !isProd,
},
publicRuntimeConfig: {
plausible: {
// This is the current domain of the site.
domain: process.env.SITE_DOMAIN ?? isProd ? "openverse.org" : "localhost",
apiHost:
process.env.SITE_DOMAIN ?? isProd
? "https://openverse.org"
: `http://localhost:${port}`,
},
},
}

export default config
4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"@nuxt/vue-app": "^2.15.8",
"@nuxtjs/composition-api": "^0.33.1",
"@nuxtjs/i18n": "^7.0.3",
"@nuxtjs/proxy": "^2.1.0",
"@nuxtjs/redirect-module": "^0.3.1",
"@nuxtjs/sentry": "^5.1.7",
"@nuxtjs/sitemap": "^2.4.0",
Expand Down Expand Up @@ -100,7 +101,8 @@
"throttle-debounce": "^5.0.0",
"uuid": "^8.3.2",
"vue": "^2.7.10",
"vue-i18n": "^8.26.7"
"vue-i18n": "^8.26.7",
"vue-plausible": "^1.3.2"
},
"devDependencies": {
"@babel/core": "^7.20.12",
Expand Down
28 changes: 22 additions & 6 deletions frontend/src/components/VHomeGallery/VHomeGallery.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
:class="idx >= imageCount ? 'hidden' : 'block'"
:style="{ '--delay': `${idx * 0.05}s` }"
:href="image.url"
@click="handleClick(image.identifier)"
>
<img
:height="dimens"
Expand All @@ -49,6 +50,7 @@ import { computed, defineComponent, PropType, ref } from "vue"
import { useContext, useRouter } from "@nuxtjs/composition-api"

import { useReducedMotion } from "~/composables/use-media-query"
import { useAnalytics } from "~/composables/use-analytics"
import useResizeObserver from "~/composables/use-resize-observer"

import VLink from "~/components/VLink.vue"
Expand Down Expand Up @@ -101,14 +103,18 @@ export default defineComponent({
const el = ref<HTMLElement | null>(null) // template ref
const { dimens: gridDimens } = useResizeObserver(el)

const imageSet = computed(() =>
props.set === "random"
? imageInfo.sets[Math.floor(Math.random() * imageInfo.sets.length)]
: imageInfo.sets.find((item) => (item.key = props.set)) ??
imageInfo.sets[0]
)
const imageList = computed(() => {
const imageSet =
props.set === "random"
? imageInfo.sets[Math.floor(Math.random() * imageInfo.sets.length)]
: imageInfo.sets.find((item) => (item.key = props.set))
return imageSet?.images.map((image, idx) => ({
return imageSet.value.images.map((image, idx) => ({
...image,
src: require(`~/assets/homepage_images/${imageSet.key}/${idx + 1}.png`),
src: require(`~/assets/homepage_images/${imageSet.value.key}/${
idx + 1
}.png`),
url: router.resolve(
app.localePath({
name: "image-id",
Expand All @@ -119,6 +125,14 @@ export default defineComponent({
})
const imageCount = computed(() => columnCount.value * rowCount)

const { sendCustomEvent } = useAnalytics()
const handleClick = (identifier: string) => {
sendCustomEvent("CLICK_HOME_GALLERY_IMAGE", {
set: imageSet.value.key,
identifier,
})
}

return {
el,

Expand All @@ -130,6 +144,8 @@ export default defineComponent({
imageList,

prefersReducedMotion,

handleClick,
}
},
})
Expand Down
70 changes: 70 additions & 0 deletions frontend/src/composables/use-analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { computed } from "vue"
import { useContext } from "@nuxtjs/composition-api"

import type { Events, EventName } from "~/types/analytics"

/**
* The `ctx` parameter must be supplied if using this composable outside the
* bounds of the composition API.
*/
export const useAnalytics = () => {
const { $plausible, $ua, i18n } = useContext()

/**
* the Plausible props that work identically on the server-side and the
* client-side; This excludes props that need `window`.
*/
const isomorphicProps = computed(() => ({
timestamp: new Date().toISOString(),
language: i18n.locale,
...($ua
? {
ua: $ua.source,
os: $ua.os,
platform: $ua.platform,
browser: $ua.browser,
version: $ua.version,
}
: {}),
}))

/**
* the Plausible props that work only on the client-side; This only includes
* props that need `window`.
*/
const windowProps = computed(() =>
window
? {
origin: window.location.origin,
pathname: window.location.pathname,
referrer: window.document.referrer,
width: window.innerWidth,
height: window.innerHeight,
}
: {}
)

/**
* Send a custom event to Plausible. Mandatory props are automatically merged
* with the event-specific props.
*
* @param name - the name of the event being recorded
* @param payload - the additional information to record about the event
*/
const sendCustomEvent = <T extends EventName>(
name: T,
payload: Events[T]
) => {
$plausible.trackEvent(name, {
props: {
...isomorphicProps.value,
...windowProps.value,
...payload, // can override mandatory props
},
})
}

return {
sendCustomEvent,
}
}
31 changes: 31 additions & 0 deletions frontend/src/types/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* compound type of all custom events sent from the site; Index with `EventName`
* to get the type of the payload for a specific event.
*
* Conventions:
* - Names should be in SCREAMING_SNAKE_CASE.
* - Names should be imperative for events associated with user action.
* - Names should be in past tense for events not associated with user action.
* - Documentation must be the step to emit the event, followed by a line break.
* - Questions that are answered by the event must be listed as bullet points.
*/
export type Events = {
/**
* Click on one of the images in the gallery on the homepage.
*
* - Do users know homepage images are links?
* - Do users find these images interesting?
* - Which set is most interesting for the users?
*/
CLICK_HOME_GALLERY_IMAGE: {
/** the set to which the image belongs */
set: string
/** the identifier of the image */
identifier: string
}
}

/**
* the name of a custom event sent from the site
*/
export type EventName = keyof Events
2 changes: 2 additions & 0 deletions frontend/typings/nuxt__types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CookieSerializeOptions } from "cookie"
import type { OpenverseCookieState } from "~/types/cookies"

import type { Details as UADetails } from "express-useragent"
import type { Plausible } from "plausible-tracker"

export interface SetParams<Key extends keyof OpenverseCookieState> {
name: Key
Expand Down Expand Up @@ -35,6 +36,7 @@ declare module "@nuxt/types" {
export interface Context {
$ua: UADetails | null
$cookies: NuxtCookies
$plausible: ReturnType<typeof Plausible>
}
export interface NuxtAppOptions {
$ua: UADetails | null
Expand Down
1 change: 1 addition & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ env:
# Load sample data into the Docker Compose services
init: _all-up
./load_sample_data.sh
./setup_plausible.sh

##########
# Docker #
Expand Down
4 changes: 4 additions & 0 deletions plausible/env.docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
BASE_URL=http://localhost:50288
SECRET_KEY_BASE=MN00EYOjQegVd5Z2NkMoSk3o1cklN/37YINtjYtM3yxsoXUhRNnvgWu3pgnEibKR126HmlGeHu7KHpPaYj/Awg==
DATABASE_URL=postgres://deploy:deploy@plausible_db:5432/plausible
CLICKHOUSE_DATABASE_URL=http://plausible_ch:8123/plausible_ch
Loading