Compare commits
No commits in common. "aa2cb7c4a0ffc0ef12bef61a09411aa7f5e58b51" and "fa7e48fe0256c1bd56774f06ca9e449b3e99f63c" have entirely different histories.
aa2cb7c4a0
...
fa7e48fe02
13 changed files with 2 additions and 296 deletions
|
|
@ -43,10 +43,3 @@ APP_PUBLIC_BASE_URL=http://localhost:3000
|
||||||
# Strong password; never use test1234. Dev seeds use test@bilhej.se instead.
|
# Strong password; never use test1234. Dev seeds use test@bilhej.se instead.
|
||||||
ADMIN_EMAIL=admin@bilhej.se
|
ADMIN_EMAIL=admin@bilhej.se
|
||||||
ADMIN_PASSWORD=change_me_to_a_strong_password
|
ADMIN_PASSWORD=change_me_to_a_strong_password
|
||||||
|
|
||||||
# ---------- Umami analytics (production frontend build only) ----------
|
|
||||||
# Baked into the frontend image at build time. Leave unset for local dev / docker compose up.
|
|
||||||
# Website ID from https://analytics.bilhej.se → Settings → Websites → BilHej
|
|
||||||
# See docs/umami-analytics.md
|
|
||||||
# VITE_UMAMI_WEBSITE_ID=
|
|
||||||
# VITE_UMAMI_SCRIPT_URL=https://analytics.bilhej.se/script.js
|
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ jobs:
|
||||||
MAIL_USERNAME: ${{ secrets.MAIL_USERNAME }}
|
MAIL_USERNAME: ${{ secrets.MAIL_USERNAME }}
|
||||||
MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
|
MAIL_PASSWORD: ${{ secrets.MAIL_PASSWORD }}
|
||||||
MAIL_FROM: ${{ secrets.MAIL_FROM }}
|
MAIL_FROM: ${{ secrets.MAIL_FROM }}
|
||||||
VITE_UMAMI_WEBSITE_ID: ${{ secrets.VITE_UMAMI_WEBSITE_ID }}
|
|
||||||
run: |
|
run: |
|
||||||
# Docker Compose treats $ as variable interpolation in .env files.
|
# Docker Compose treats $ as variable interpolation in .env files.
|
||||||
# Escape literal dollar signs (e.g. in passwords) as $$.
|
# Escape literal dollar signs (e.g. in passwords) as $$.
|
||||||
|
|
@ -68,7 +67,6 @@ jobs:
|
||||||
printf 'MAIL_USERNAME=%s\n' "$(escape "$MAIL_USERNAME")"
|
printf 'MAIL_USERNAME=%s\n' "$(escape "$MAIL_USERNAME")"
|
||||||
printf 'MAIL_PASSWORD=%s\n' "$(escape "$MAIL_PASSWORD")"
|
printf 'MAIL_PASSWORD=%s\n' "$(escape "$MAIL_PASSWORD")"
|
||||||
printf 'MAIL_FROM=%s\n' "$(escape "${MAIL_FROM:-noreply@bilhej.se}")"
|
printf 'MAIL_FROM=%s\n' "$(escape "${MAIL_FROM:-noreply@bilhej.se}")"
|
||||||
printf 'VITE_UMAMI_WEBSITE_ID=%s\n' "$(escape "$VITE_UMAMI_WEBSITE_ID")"
|
|
||||||
} > .env
|
} > .env
|
||||||
|
|
||||||
- name: Build and start production stack
|
- name: Build and start production stack
|
||||||
|
|
|
||||||
|
|
@ -342,7 +342,6 @@ Before the first deploy, complete these steps on the production server (`srvr.nu
|
||||||
| `MAIL_USERNAME` | `resend` (literal string) |
|
| `MAIL_USERNAME` | `resend` (literal string) |
|
||||||
| `MAIL_PASSWORD` | Resend API key (`re_...`; rotate if ever exposed) |
|
| `MAIL_PASSWORD` | Resend API key (`re_...`; rotate if ever exposed) |
|
||||||
| `MAIL_FROM` | `noreply@bilhej.se` (must be on verified domain) |
|
| `MAIL_FROM` | `noreply@bilhej.se` (must be on verified domain) |
|
||||||
| `VITE_UMAMI_WEBSITE_ID` | Umami website UUID for `bilhej.se` (see `docs/umami-analytics.md`) |
|
|
||||||
|
|
||||||
Passwords may contain `$` — the deploy workflow escapes these for Docker Compose.
|
Passwords may contain `$` — the deploy workflow escapes these for Docker Compose.
|
||||||
Production does **not** seed `test@bilhej.se` or demo orders. On first start, the
|
Production does **not** seed `test@bilhej.se` or demo orders. On first start, the
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,6 @@ services:
|
||||||
build:
|
build:
|
||||||
dockerfile: docker/frontend.prod.Dockerfile
|
dockerfile: docker/frontend.prod.Dockerfile
|
||||||
context: .
|
context: .
|
||||||
args:
|
|
||||||
VITE_UMAMI_WEBSITE_ID: ${VITE_UMAMI_WEBSITE_ID:-}
|
|
||||||
VITE_UMAMI_SCRIPT_URL: ${VITE_UMAMI_SCRIPT_URL:-https://analytics.bilhej.se/script.js}
|
|
||||||
container_name: bilhej-frontend-prod
|
container_name: bilhej-frontend-prod
|
||||||
ports:
|
ports:
|
||||||
- "3001:80"
|
- "3001:80"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
FROM node:24-alpine AS builder
|
FROM node:24-alpine AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ARG VITE_UMAMI_WEBSITE_ID=
|
|
||||||
ARG VITE_UMAMI_SCRIPT_URL=https://analytics.bilhej.se/script.js
|
|
||||||
ENV VITE_UMAMI_WEBSITE_ID=$VITE_UMAMI_WEBSITE_ID
|
|
||||||
ENV VITE_UMAMI_SCRIPT_URL=$VITE_UMAMI_SCRIPT_URL
|
|
||||||
COPY frontend/package.json frontend/package-lock.json ./
|
COPY frontend/package.json frontend/package-lock.json ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
COPY frontend/ .
|
COPY frontend/ .
|
||||||
|
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
# Umami analytics (BilHej app)
|
|
||||||
|
|
||||||
Privacy-friendly page analytics via self-hosted [Umami](https://umami.is/docs) on **`https://analytics.bilhej.se`**. Server install is live on the VPS; this doc is for **BilHej code and deploy config** only.
|
|
||||||
|
|
||||||
## Values (production)
|
|
||||||
|
|
||||||
| Item | Value |
|
|
||||||
|------|--------|
|
|
||||||
| Collector | `https://analytics.bilhej.se` |
|
|
||||||
| Tracker script | `https://analytics.bilhej.se/script.js` |
|
|
||||||
| Dashboard | `https://analytics.bilhej.se` (admin login) |
|
|
||||||
| Website in Umami | Name **BilHej**, domain **`bilhej.se`** |
|
|
||||||
| Website ID | `ce59614c-9f2a-4f99-8ba3-c5217f88c3f7` |
|
|
||||||
|
|
||||||
The Website ID is public in the browser (tracking snippet). Set it via **`VITE_UMAMI_WEBSITE_ID`** in production frontend build env — do not hardcode in source.
|
|
||||||
|
|
||||||
**Note:** Umami 3.1 on this server uses the default **`/script.js`** path. `TRACKER_SCRIPT_NAME` / custom `bilhej-stats.js` is not applied in this version.
|
|
||||||
|
|
||||||
### Example snippet (for reference)
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script
|
|
||||||
defer
|
|
||||||
src="https://analytics.bilhej.se/script.js"
|
|
||||||
data-website-id="ce59614c-9f2a-4f99-8ba3-c5217f88c3f7"
|
|
||||||
></script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Frontend env
|
|
||||||
|
|
||||||
Production builds read this from the **Forgejo Actions secret** `VITE_UMAMI_WEBSITE_ID`. The deploy workflow writes it into `.env` on the server, then `docker compose -f docker-compose.prod.yml build` bakes it into the frontend image.
|
|
||||||
|
|
||||||
**Forgejo → Repository → Settings → Actions → Secrets:**
|
|
||||||
|
|
||||||
| Secret | Value |
|
|
||||||
|--------|--------|
|
|
||||||
| `VITE_UMAMI_WEBSITE_ID` | `ce59614c-9f2a-4f99-8ba3-c5217f88c3f7` |
|
|
||||||
|
|
||||||
Not a high-risk secret (the same ID appears in the browser), but keeping it in Forgejo matches other deploy config.
|
|
||||||
|
|
||||||
Manual deploy on the server (without Forgejo) works the same way: put the line in the project `.env` before `docker compose ... up --build`.
|
|
||||||
|
|
||||||
Optional override (default matches production):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# VITE_UMAMI_SCRIPT_URL=https://analytics.bilhej.se/script.js
|
|
||||||
```
|
|
||||||
|
|
||||||
Leave `VITE_UMAMI_WEBSITE_ID` unset in local dev unless you intentionally send traffic to production Umami.
|
|
||||||
|
|
||||||
## Implementation checklist
|
|
||||||
|
|
||||||
- [x] Load `script.js` with `data-website-id` from `VITE_UMAMI_WEBSITE_ID` (only when set).
|
|
||||||
- [x] Send **SPA pageviews** on Vue Router `afterEach` (`data-auto-track="false"`).
|
|
||||||
- [x] Update **integritetspolicy** — analytics, country-level stats, no IP stored in BilHej DB.
|
|
||||||
- [x] Admin link **Webbstatistik** → Umami dashboard (prod builds only).
|
|
||||||
|
|
||||||
Umami derives **country** from the visitor IP at ingest and does not show IP lists in the UI. BilHej must not store visitor IPs for analytics.
|
|
||||||
|
|
||||||
## Verify after deploy
|
|
||||||
|
|
||||||
1. Browse `https://bilhej.se` (several routes).
|
|
||||||
2. Umami → **BilHej** → **Realtime** / **Countries**.
|
|
||||||
|
|
||||||
## Server layout (reference)
|
|
||||||
|
|
||||||
| Item | Actual on VPS |
|
|
||||||
|------|----------------|
|
|
||||||
| Compose project | `~/umami` (`/home/jocke/umami`) |
|
|
||||||
| Public access | nginx → `http://umami:3000` on Docker network `web` (host port 3000 used by open-webui) |
|
|
||||||
| Database | `umami-db` on internal network `umami-internal` only |
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/umami
|
|
||||||
docker compose ps
|
|
||||||
docker compose logs -f umami
|
|
||||||
docker compose pull && docker compose up -d # updates — read release notes first
|
|
||||||
docker exec umami-db pg_dump -U umami umami > ~/umami-backup-$(date +%F).sql
|
|
||||||
```
|
|
||||||
|
|
||||||
Country stats require nginx to pass **`X-Forwarded-For`** (already configured for this vhost).
|
|
||||||
|
|
@ -41,16 +41,6 @@ describe('PrivacyPolicyPage', () => {
|
||||||
expect(wrapper.text()).toContain('varken vi eller obehöriga')
|
expect(wrapper.text()).toContain('varken vi eller obehöriga')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('describes web analytics', () => {
|
|
||||||
const router = createTestRouter()
|
|
||||||
const wrapper = mount(PrivacyPolicyPage, {
|
|
||||||
global: { plugins: [router] },
|
|
||||||
})
|
|
||||||
expect(wrapper.text()).toContain('Webbstatistik')
|
|
||||||
expect(wrapper.text()).toContain('analytics.bilhej.se')
|
|
||||||
expect(wrapper.text()).toContain('IP-adresser')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('links to contact email and contact page', () => {
|
it('links to contact email and contact page', () => {
|
||||||
const router = createTestRouter()
|
const router = createTestRouter()
|
||||||
const wrapper = mount(PrivacyPolicyPage, {
|
const wrapper = mount(PrivacyPolicyPage, {
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
||||||
import { createRouter, createMemoryHistory } from 'vue-router'
|
|
||||||
import {
|
|
||||||
getUmamiConfig,
|
|
||||||
initUmamiAnalytics,
|
|
||||||
trackUmamiPageview,
|
|
||||||
} from '@/utils/umami'
|
|
||||||
|
|
||||||
describe('umami', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
document.head.innerHTML = ''
|
|
||||||
delete window.umami
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
vi.unstubAllEnvs()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns null when website id is unset', () => {
|
|
||||||
vi.stubEnv('VITE_UMAMI_WEBSITE_ID', '')
|
|
||||||
expect(getUmamiConfig()).toBeNull()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns config when website id is set', () => {
|
|
||||||
vi.stubEnv('VITE_UMAMI_WEBSITE_ID', '11111111-2222-3333-4444-555555555555')
|
|
||||||
vi.stubEnv('VITE_UMAMI_SCRIPT_URL', '')
|
|
||||||
expect(getUmamiConfig()).toEqual({
|
|
||||||
websiteId: '11111111-2222-3333-4444-555555555555',
|
|
||||||
scriptUrl: 'https://analytics.bilhej.se/script.js',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('uses custom script url when provided', () => {
|
|
||||||
vi.stubEnv('VITE_UMAMI_WEBSITE_ID', 'test-id')
|
|
||||||
vi.stubEnv('VITE_UMAMI_SCRIPT_URL', 'https://example.test/script.js')
|
|
||||||
expect(getUmamiConfig()?.scriptUrl).toBe('https://example.test/script.js')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('does not inject script when website id is unset', () => {
|
|
||||||
vi.stubEnv('VITE_UMAMI_WEBSITE_ID', '')
|
|
||||||
const router = createRouter({
|
|
||||||
history: createMemoryHistory(),
|
|
||||||
routes: [{ path: '/', component: { template: '<div />' } }],
|
|
||||||
})
|
|
||||||
initUmamiAnalytics(router)
|
|
||||||
expect(document.querySelector('script[data-website-id]')).toBeNull()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('injects script with auto-track disabled when configured', () => {
|
|
||||||
vi.stubEnv('VITE_UMAMI_WEBSITE_ID', 'test-id')
|
|
||||||
const router = createRouter({
|
|
||||||
history: createMemoryHistory(),
|
|
||||||
routes: [{ path: '/', component: { template: '<div />' } }],
|
|
||||||
})
|
|
||||||
initUmamiAnalytics(router)
|
|
||||||
const script = document.querySelector('script[data-website-id]')
|
|
||||||
expect(script?.getAttribute('data-website-id')).toBe('test-id')
|
|
||||||
expect(script?.getAttribute('data-auto-track')).toBe('false')
|
|
||||||
expect(script?.getAttribute('src')).toContain('script.js')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('trackUmamiPageview forwards url to umami', () => {
|
|
||||||
const track = vi.fn()
|
|
||||||
window.umami = { track }
|
|
||||||
trackUmamiPageview('/orders')
|
|
||||||
expect(track).toHaveBeenCalledOnce()
|
|
||||||
const mapper = track.mock.calls[0][0] as (
|
|
||||||
props: Record<string, unknown>,
|
|
||||||
) => Record<string, unknown>
|
|
||||||
expect(mapper({ referrer: 'x' })).toEqual({ referrer: 'x', url: '/orders' })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
22
frontend/src/env.d.ts
vendored
22
frontend/src/env.d.ts
vendored
|
|
@ -1,22 +0,0 @@
|
||||||
/// <reference types="vite/client" />
|
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
|
||||||
readonly VITE_API_URL?: string
|
|
||||||
readonly VITE_UMAMI_WEBSITE_ID?: string
|
|
||||||
readonly VITE_UMAMI_SCRIPT_URL?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ImportMeta {
|
|
||||||
readonly env: ImportMetaEnv
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Window {
|
|
||||||
umami?: {
|
|
||||||
track: (
|
|
||||||
input?:
|
|
||||||
| string
|
|
||||||
| Record<string, unknown>
|
|
||||||
| ((props: Record<string, unknown>) => Record<string, unknown>),
|
|
||||||
) => void
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,13 +2,11 @@ import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import { initUmamiAnalytics } from '@/utils/umami'
|
|
||||||
import './assets/styles/base.css'
|
import './assets/styles/base.css'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
app.use(router)
|
app.use(router)
|
||||||
initUmamiAnalytics(router)
|
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,6 @@ const {
|
||||||
handleNotesSave,
|
handleNotesSave,
|
||||||
} = useAdminOrderActions(orders, replaceOrder)
|
} = useAdminOrderActions(orders, replaceOrder)
|
||||||
|
|
||||||
const umamiDashboardUrl = import.meta.env.VITE_UMAMI_WEBSITE_ID
|
|
||||||
? 'https://analytics.bilhej.se'
|
|
||||||
: null
|
|
||||||
|
|
||||||
function handleModalKeydown(event: KeyboardEvent) {
|
function handleModalKeydown(event: KeyboardEvent) {
|
||||||
if (event.key === 'Escape' && messageModalOrder.value) {
|
if (event.key === 'Escape' && messageModalOrder.value) {
|
||||||
closeMessageModal()
|
closeMessageModal()
|
||||||
|
|
@ -59,18 +55,7 @@ onUnmounted(() => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="admin">
|
<div class="admin">
|
||||||
<header class="admin__header">
|
<h1 class="admin__title">Administration</h1>
|
||||||
<h1 class="admin__title">Administration</h1>
|
|
||||||
<a
|
|
||||||
v-if="umamiDashboardUrl"
|
|
||||||
class="admin__analytics-link"
|
|
||||||
:href="umamiDashboardUrl"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Webbstatistik
|
|
||||||
</a>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<p
|
<p
|
||||||
v-if="loading"
|
v-if="loading"
|
||||||
|
|
@ -152,33 +137,12 @@ onUnmounted(() => {
|
||||||
padding: 0 var(--space-lg);
|
padding: 0 var(--space-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin__header {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: var(--space-md);
|
|
||||||
margin-bottom: var(--space-xl);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin__title {
|
.admin__title {
|
||||||
margin: 0;
|
margin: 0 0 var(--space-xl) 0;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
color: var(--color-ink);
|
color: var(--color-ink);
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin__analytics-link {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-primary);
|
|
||||||
text-decoration: underline;
|
|
||||||
text-underline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin__analytics-link:hover {
|
|
||||||
color: var(--color-primary-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin__stats {
|
.admin__stats {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
|
|
||||||
|
|
@ -40,15 +40,6 @@ const sections = [
|
||||||
'Vi säljer inte personuppgifter och visar inte mottagarens identitet eller adress för dig som avsändare.',
|
'Vi säljer inte personuppgifter och visar inte mottagarens identitet eller adress för dig som avsändare.',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'webbanalys',
|
|
||||||
title: 'Webbstatistik',
|
|
||||||
paragraphs: [
|
|
||||||
'Vi använder självhostad webbanalys (Umami) på analytics.bilhej.se för att förstå hur webbplatsen används, till exempel vilka sidor som besöks och ungefärlig geografisk fördelning på landsnivå.',
|
|
||||||
'Analysen bygger på sidvisningar och teknisk information som webbläsaren skickar vid besök. Vi lagrar inte besökares IP-adresser i Bilhejs databas; Umami behandlar IP tillfälligt för att kunna visa land och tar inte emot personuppgifter som du skriver i brev eller konto.',
|
|
||||||
'Du kan begränsa spårning med webbläsarens spärrlistor eller “Do Not Track”. Kontakta oss om du har frågor om webbanalys.',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'lagring',
|
id: 'lagring',
|
||||||
title: 'Hur länge sparar vi uppgifterna?',
|
title: 'Hur länge sparar vi uppgifterna?',
|
||||||
|
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
import type { Router } from 'vue-router'
|
|
||||||
|
|
||||||
const DEFAULT_SCRIPT_URL = 'https://analytics.bilhej.se/script.js'
|
|
||||||
|
|
||||||
export type UmamiConfig = {
|
|
||||||
websiteId: string
|
|
||||||
scriptUrl: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUmamiConfig(): UmamiConfig | null {
|
|
||||||
const websiteId = import.meta.env.VITE_UMAMI_WEBSITE_ID?.trim()
|
|
||||||
if (!websiteId) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const scriptUrl =
|
|
||||||
import.meta.env.VITE_UMAMI_SCRIPT_URL?.trim() || DEFAULT_SCRIPT_URL
|
|
||||||
|
|
||||||
return { websiteId, scriptUrl }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackUmamiPageview(url: string): void {
|
|
||||||
window.umami?.track((props) => ({ ...props, url }))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initUmamiAnalytics(router: Router): void {
|
|
||||||
const config = getUmamiConfig()
|
|
||||||
if (!config) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const script = document.createElement('script')
|
|
||||||
script.defer = true
|
|
||||||
script.src = config.scriptUrl
|
|
||||||
script.setAttribute('data-website-id', config.websiteId)
|
|
||||||
script.setAttribute('data-auto-track', 'false')
|
|
||||||
script.onload = () => {
|
|
||||||
trackUmamiPageview(router.currentRoute.value.fullPath)
|
|
||||||
}
|
|
||||||
document.head.appendChild(script)
|
|
||||||
|
|
||||||
router.afterEach((to) => {
|
|
||||||
trackUmamiPageview(to.fullPath)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue