One document. Simple tracking. Privacy-first analytics.
pm-abc123-1706425600000)Add this to your websiteβs <head> section:
<script src="https://your-domain.com/pm.js"
data-code="YOUR_TRACKING_CODE_HERE">
</script>
Thatβs it. No configuration, no API keys, no cookies.
Privacy-first: No cookies, no fingerprinting, no personal data.
# Clone repository
git clone <repo>
cd Privacy-Focused-Web-Analytics-Dashboard
# Install dependencies
pnpm install
# Start dev servers (frontend + backend)
pnpm dev
Servers will start on:
# With server running, open:
http://localhost:8080/test-debug.html
Then:
YOUR_TRACKING_CODE with your actual code<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
<script src="http://localhost:8080/pm.js"
data-code="YOUR_CODE"
data-api="http://localhost:3000/api/v1/track">
</script>
</head>
<body>
<h1>Testing PrivacyMetrics</h1>
<p>Open DevTools (F12) β Network tab to see POST requests</p>
</body>
</html>
$body = @{
code = "your-tracking-code"
sid = "pm_session_test"
vid = "pm_visitor_test"
url = "http://example.com/test"
ref = "http://google.com"
t = [int64](Get-Date -UFormat %s) * 1000
} | ConvertTo-Json
$response = Invoke-WebRequest `
-Uri "http://localhost:3000/api/v1/track" `
-Method POST `
-Body $body `
-ContentType "application/json"
Write-Host "Status: $($response.StatusCode)" # Should be 204
import { useEffect } from 'react';
import { useRouter } from 'next/router';
export default function App() {
const router = useRouter();
useEffect(() => {
// Load tracker
const script = document.createElement('script');
script.src = 'https://your-domain.com/pm.js';
script.setAttribute('data-code', 'pm-your-code');
document.head.appendChild(script);
}, []);
useEffect(() => {
// Track route changes
if (window.pm) {
window.pm.track();
}
}, [router.pathname]);
return <>{/* Your app */}</>;
}
<script setup>
import { onMounted, watch } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
onMounted(() => {
// Load tracker
const script = document.createElement('script');
script.src = 'https://your-domain.com/pm.js';
script.setAttribute('data-code', 'pm-your-code');
document.head.appendChild(script);
});
watch(() => route.path, () => {
if (window.pm) window.pm.track();
});
</script>
<!DOCTYPE html>
<html>
<head>
<title>My Site</title>
<script src="https://your-domain.com/pm.js"
data-code="pm-your-code">
</script>
</head>
<body>
<!-- Your content -->
</body>
</html>
// Should both have values
console.log(sessionStorage.getItem('_pm_sid')); // Session ID
console.log(sessionStorage.getItem('_pm_vid')); // Visitor ID
/api/v1/track<script src="https://your-domain.com/pm.js"
data-code="pm-your-code"
data-api="https://custom-api.com/track">
</script>
// Track button clicks, form submissions, etc.
if (window.pm) {
window.pm.track(); // Send another event with current page data
}
<!-- Site A -->
<script src="https://analytics.com/pm.js" data-code="pm-site-a"></script>
<!-- Site B -->
<script src="https://analytics.com/pm.js" data-code="pm-site-b"></script>
Privacy-Focused-Web-Analytics-Dashboard/
βββ public/
β βββ pm.js # Tracking script
β βββ test-simple.html # Simple test page
β βββ test-debug.html # Debug test page
βββ client/
β βββ pages/
β β βββ Index.tsx # Home page
β β βββ Login.tsx # Login
β β βββ Register.tsx # Sign up
β β βββ Dashboard.tsx # Analytics dashboard
β β βββ WebsiteManagement.tsx # Manage websites
β βββ hooks/
β β βββ useDashboardData.ts # Dashboard data fetching
β βββ components/ # UI components
βββ server/
β βββ routes/
β β βββ auth.ts # Authentication
β β βββ websites.ts # Website CRUD
β β βββ tracking.ts # Tracking endpoint
β β βββ dashboard.ts # Dashboard data
β βββ services/
β β βββ tracking.ts # Event processing
β β βββ aggregation.ts # Data aggregation
β βββ middleware/
β βββ auth.ts # JWT authentication
βββ prisma/
β βββ schema.prisma # Database schema
βββ docs/ # Additional documentation
Create .env file:
# Database
DATABASE_URL=file:./prisma/dev.db
# Server
PORT=3000
NODE_ENV=development
# Frontend
VITE_API_URL=http://localhost:3000
Fix: Add data-code attribute to script tag:
β <script src="/pm.js"></script>
β
<script src="/pm.js" data-code="pm-abc123"></script>
Fix: Make sure backend is running and CORS is configured:
Backend: http://localhost:3000/api/v1/track
Frontend: http://localhost:8080
Check:
# Reset database (dev only)
rm prisma/dev.db
pnpm dev
POST /api/v1/track - Track page view/eventPOST /api/v1/auth/login - User loginPOST /api/v1/auth/register - User registrationGET /api/v1/websites - List userβs websitesPOST /api/v1/websites - Create websiteDELETE /api/v1/websites/:id - Delete websiteGET /api/v1/dashboard/metrics - Dashboard metricsGET /api/v1/dashboard/pageviews - Pageview chart dataGET /api/v1/dashboard/top-pages - Top pagesGET /api/v1/dashboard/referrers - Referrer sourcespnpm build
# Deploy dist/spa folder to Netlify
Push to GitHub, connect repository to Railway:
pnpm build:prodpnpm startdocker build -t privacy-metrics .
docker run -p 3000:3000 -p 8080:8080 privacy-metrics
| Feature | PrivacyMetrics | Google Analytics | Plausible |
|---|---|---|---|
| Self-hosted | β | β | β οΈ |
| No cookies | β | β | β |
| No tracking code required | β | β | β |
| Open source | β | β | β |
| Own your data | β | β | β |
| GDPR compliant | β | β | β |
| Simple implementation | β | β | β |
git checkout -b feature/your-featuregit commit -am 'Add feature'git push origin feature/your-featureMIT License - see LICENSE file for details
Before deploying:
Questions? Check the troubleshooting section above or open an issue.
Ready to track? Grab your tracking code and add one line to your site! π