diff --git a/.env.example b/.env.example
index 89d630e..65080b4 100644
--- a/.env.example
+++ b/.env.example
@@ -5,22 +5,22 @@ WEB_PORT=3000
API_PORT=4000
# DB
-POSTGRES_DB=
-POSTGRES_USER=
+POSTGRES_DB=flint
+POSTGRES_USER=flint
POSTGRES_PASSWORD=
# REDIS
REDIS_PASSWORD=
# URLS PROD
-WEB_URL=https://yourdomain.com
+WEB_URL=http://localhost:3000
API_BASE_URL=http://api:4000
-NEXT_PUBLIC_API_BASE_URL=https://api.yourdomain.com
+NEXT_PUBLIC_API_BASE_URL=http://localhost:4000
# DISCORD
DISCORD_CLIENT_ID=
DISCORD_CLIENT_SECRET=
-DISCORD_REDIRECT_URI=https://api.yourdomain.com/auth/discord/callback
+DISCORD_REDIRECT_URI=http://localhost:4000/auth/discord/callback
# Session/auth
JWT_SECRET=
@@ -34,3 +34,8 @@ TOKEN_ENCRYPTION_KEY=
# Multi-tenant API limits
TENANT_RATE_LIMIT_MAX=120
TENANT_RATE_LIMIT_WINDOW_SECONDS=60
+
+# Matomo Analytics (optional - leave empty to disable)
+# NEXT_PUBLIC_MATOMO_URL=
+# NEXT_PUBLIC_MATOMO_SITE_ID=
+
diff --git a/apps/web/app/[locale]/layout.tsx b/apps/web/app/[locale]/layout.tsx
index da9c5b7..420c068 100644
--- a/apps/web/app/[locale]/layout.tsx
+++ b/apps/web/app/[locale]/layout.tsx
@@ -4,6 +4,7 @@ import { hasLocale, NextIntlClientProvider } from "next-intl";
import { getMessages, getTranslations } from "next-intl/server";
import { notFound } from "next/navigation";
+import { MatomoScript } from "../../components/MatomoScript";
import { RTL_LOCALES, routing } from "../../i18n/routing";
import "../globals.css";
@@ -130,6 +131,7 @@ export default async function LocaleLayout({
+
{children}
diff --git a/apps/web/components/MatomoScript.tsx b/apps/web/components/MatomoScript.tsx
new file mode 100644
index 0000000..0975016
--- /dev/null
+++ b/apps/web/components/MatomoScript.tsx
@@ -0,0 +1,50 @@
+'use client';
+
+import { useEffect } from 'react';
+
+/**
+ * Matomo Analytics Script Component
+ * Injects the Matomo tracking script only if MATOMO_URL and MATOMO_SITE_ID are configured
+ *
+ * Environment variables:
+ * - NEXT_PUBLIC_MATOMO_URL: Base URL of Matomo instance (e.g., https://analytics.arthurp.fr/)
+ * - NEXT_PUBLIC_MATOMO_SITE_ID: Site ID in Matomo (e.g., 9)
+ */
+export function MatomoScript() {
+ useEffect(() => {
+ // Check if Matomo is properly configured
+ const matomoUrl = process.env.NEXT_PUBLIC_MATOMO_URL?.trim();
+ const matomoSiteId = process.env.NEXT_PUBLIC_MATOMO_SITE_ID?.trim();
+
+ // Skip if not configured
+ if (!matomoUrl || !matomoSiteId) {
+ if (process.env.NODE_ENV === 'development') {
+ console.debug('[Matomo] Not configured. Skipping analytics.');
+ }
+ return;
+ }
+
+ // Initialize Matomo tracking array
+ (window as any)._paq = (window as any)._paq || [];
+ const _paq = (window as any)._paq;
+
+ // Set up tracking
+ _paq.push(['trackPageView']);
+ _paq.push(['enableLinkTracking']);
+ _paq.push(['setTrackerUrl', `${matomoUrl}matomo.php`]);
+ _paq.push(['setSiteId', matomoSiteId]);
+
+ // Inject the Matomo script
+ const script = document.createElement('script');
+ script.async = true;
+ script.src = `${matomoUrl}matomo.js`;
+ script.defer = true;
+
+ const firstScript = document.getElementsByTagName('script')[0];
+ if (firstScript && firstScript.parentNode) {
+ firstScript.parentNode.insertBefore(script, firstScript);
+ }
+ }, []);
+
+ return null;
+}