Skip to content

Commit

Permalink
Merge pull request #1826 from filipedeschamps/feat/Umami-Analytics
Browse files Browse the repository at this point in the history
Feat/umami analytics
  • Loading branch information
aprendendofelipe authored Dec 19, 2024
2 parents 4d422bb + 8235c6e commit 2448219
Show file tree
Hide file tree
Showing 9 changed files with 427 additions and 11 deletions.
6 changes: 6 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ EMAIL_HTTP_PORT=1080
EMAIL_USER=
EMAIL_PASSWORD=
UNDER_MAINTENANCE={"methodsAndPaths":["POST /api/v1/under-maintenance-test$"]}

# Umami Analytics
UMAMI_DB=umami
UMAMI_PORT=3001
UMAMI_ENDPOINT=http://localhost:$UMAMI_PORT
NEXT_PUBLIC_UMAMI_WEBSITE_ID=0d54205e-06f1-49b0-89e2-1fc412e52a80
17 changes: 17 additions & 0 deletions infra/docker-compose.development.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ services:
- '${POSTGRES_PORT}:5432'
volumes:
- postgres_data:/data/postgres
- ./scripts:/docker-entrypoint-initdb.d
restart: unless-stopped
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}']
interval: 5s
timeout: 5s
retries: 5
mailcatcher:
container_name: mailcatcher
image: sj26/mailcatcher
Expand All @@ -19,5 +25,16 @@ services:
ports:
- '${EMAIL_SMTP_PORT}:1025'
- '${EMAIL_HTTP_PORT}:1080'
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
ports:
- '${UMAMI_PORT}:3000'
environment:
DATABASE_URL: postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres_dev:5432/$UMAMI_DB
depends_on:
postgres_dev:
condition: service_healthy
init: true
restart: unless-stopped
volumes:
postgres_data:
1 change: 1 addition & 0 deletions infra/scripts/01-create-umami-database.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CREATE DATABASE umami;
93 changes: 93 additions & 0 deletions infra/scripts/config-umami.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* eslint-disable no-console */
const { Client } = require('pg');

const endpoint = process.env.UMAMI_ENDPOINT;
const websiteDomain = `${process.env.NEXT_PUBLIC_WEBSERVER_HOST}:${process.env.NEXT_PUBLIC_WEBSERVER_PORT}`;
const websiteId = process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID;
const connectionString = `postgres://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@${process.env.POSTGRES_HOST}:${process.env.POSTGRES_PORT}/${process.env.UMAMI_DB}`;
const username = process.env.UMAMI_API_CLIENT_USERNAME || 'admin';
const password = process.env.UMAMI_API_CLIENT_PASSWORD || 'umami';

const client = new Client({
connectionString,
connectionTimeoutMillis: 5000,
idleTimeoutMillis: 30000,
allowExitOnIdle: false,
});

configUmami();

async function configUmami() {
console.log('\n> Waiting for Umami Server to start...');
console.log('> Endpoint:', endpoint);

await waitForServer();

console.log('> Creating Umami configuration...');

const token = await fetch(`${endpoint}/api/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
username,
password,
}),
})
.then((res) => res.json())
.then((data) => data.token);

console.log('> Token:', token);

const websites = await fetch(`${endpoint}/api/websites`, {
headers: {
Authorization: `Bearer ${token}`,
},
})
.then((res) => res.json())
.then((data) => data.data);

let existDevWebisite;

if (websites.length) {
existDevWebisite = websites.some((site) => site.id === websiteId);
}

await client.connect();

if (!existDevWebisite) {
const website = await fetch(`${endpoint}/api/websites`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
name: 'TabNews Dev',
domain: websiteDomain,
}),
}).then((res) => res.json());

await client.query('UPDATE website SET website_id = $1 WHERE website_id = $2;', [websiteId, website.id]);
}

await client.end();

console.log('> Umami configuration created!');
}

async function waitForServer(attempts = 5) {
try {
return await fetch(`${endpoint}/api/heartbeat`);
} catch (error) {
if (attempts > 1) {
console.log('> Umami is not ready, waiting...');
await new Promise((resolve) => setTimeout(resolve, 1000));
return waitForServer(attempts - 1);
}

console.error('🔴 Umami is not ready, exiting...');
process.exit(1);
}
}
4 changes: 4 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ module.exports = {
source: '/recentes/rss',
destination: '/api/v1/contents/rss',
},
{
source: '/api/v1/analytics',
destination: `${process.env.UMAMI_ENDPOINT}/api/send`,
},
];
},
headers() {
Expand Down
13 changes: 2 additions & 11 deletions pages/_app.public.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Analytics } from '@vercel/analytics/react';
import { RevalidateProvider } from 'next-swr';
import { SWRConfig } from 'swr';

import { ThemeProvider, Turnstile } from '@/TabNewsUI';
import { DefaultHead, UserProvider } from 'pages/interface';
import { Analytics, DefaultHead, UserProvider } from 'pages/interface';

async function SWRFetcher(resource, init) {
const response = await fetch(resource, init);
Expand All @@ -30,15 +29,7 @@ function MyApp({ Component, pageProps }) {
<Component {...pageProps} />
</RevalidateProvider>
</SWRConfig>
<Analytics
beforeSend={(event) => {
const { pathname } = new URL(event.url);
if (['/', '/publicar'].includes(pathname)) {
return null;
}
return event;
}}
/>
<Analytics />
</UserProvider>
</ThemeProvider>
);
Expand Down
26 changes: 26 additions & 0 deletions pages/interface/components/Analytics/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Analytics as VercelAnalytics } from '@vercel/analytics/react';
import Script from 'next/script';

export default function Analytics() {
return (
<>
<Script
id="umami-script"
src="/analytics.js"
data-website-id={process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID}
data-path-matcher="^(/login|/cadastro|/publicar)$"
data-exclude-search="true"
strategy="lazyOnload"
/>
<VercelAnalytics
beforeSend={(event) => {
const { pathname } = new URL(event.url);
if (['/', '/publicar'].includes(pathname)) {
return null;
}
return event;
}}
/>
</>
);
}
1 change: 1 addition & 0 deletions pages/interface/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as Analytics } from './components/Analytics';
export { DefaultHead, default as Head } from './components/Head';
export { default as useCollapse } from './hooks/useCollapse';
export { default as useMediaQuery } from './hooks/useMediaQuery';
Expand Down
Loading

0 comments on commit 2448219

Please sign in to comment.