diff --git a/components/common/FilterLink.js b/components/common/FilterLink.js
new file mode 100644
index 00000000..459a8ae1
--- /dev/null
+++ b/components/common/FilterLink.js
@@ -0,0 +1,34 @@
+import React from 'react';
+import Link from 'next/link';
+import classNames from 'classnames';
+import usePageQuery from 'hooks/usePageQuery';
+import { safeDecodeURI } from 'lib/url';
+import Icon from './Icon';
+import External from 'assets/arrow-up-right-from-square.svg';
+import styles from './FilterLink.module.css';
+
+export default function FilterLink({ id, value, label, externalUrl }) {
+ const { resolve, query } = usePageQuery();
+ const active = query[id] !== undefined;
+ const selected = query[id] === value;
+
+ return (
+
+ );
+}
diff --git a/components/metrics/ReferrersTable.module.css b/components/common/FilterLink.module.css
similarity index 79%
rename from components/metrics/ReferrersTable.module.css
rename to components/common/FilterLink.module.css
index 238667f3..45b049da 100644
--- a/components/metrics/ReferrersTable.module.css
+++ b/components/common/FilterLink.module.css
@@ -1,19 +1,20 @@
-body .inactive {
+.row {
+ display: flex;
+ align-items: center;
+}
+
+.row .inactive {
color: var(--gray500);
}
-body .active {
+.row .active {
color: var(--gray900);
font-weight: 600;
}
-.row {
- display: flex;
- justify-content: space-between;
-}
-
.row .link {
display: none;
+ margin-left: 20px;
}
.row .label {
diff --git a/components/metrics/BrowsersTable.js b/components/metrics/BrowsersTable.js
index 12c1087b..60cb57d7 100644
--- a/components/metrics/BrowsersTable.js
+++ b/components/metrics/BrowsersTable.js
@@ -2,8 +2,13 @@ import React from 'react';
import { FormattedMessage } from 'react-intl';
import MetricsTable from './MetricsTable';
import { browserFilter } from 'lib/filters';
+import FilterLink from '../common/FilterLink';
export default function BrowsersTable({ websiteId, ...props }) {
+ function renderLink({ x: browser }) {
+ return ;
+ }
+
return (
}
websiteId={websiteId}
dataFilter={browserFilter}
+ renderLabel={renderLink}
/>
);
}
diff --git a/components/metrics/CountriesTable.js b/components/metrics/CountriesTable.js
index 01e7c7c7..17d9127a 100644
--- a/components/metrics/CountriesTable.js
+++ b/components/metrics/CountriesTable.js
@@ -2,6 +2,7 @@ import React from 'react';
import MetricsTable from './MetricsTable';
import { percentFilter } from 'lib/filters';
import { FormattedMessage } from 'react-intl';
+import FilterLink from 'components/common/FilterLink';
import useCountryNames from 'hooks/useCountryNames';
import useLocale from 'hooks/useLocale';
@@ -9,10 +10,16 @@ export default function CountriesTable({ websiteId, onDataLoad, ...props }) {
const { locale } = useLocale();
const countryNames = useCountryNames(locale);
- function renderLabel({ x }) {
+ function renderLink({ x: code }) {
return (
- {countryNames[x] ?? }
+
+ }
+ />
);
}
@@ -25,7 +32,7 @@ export default function CountriesTable({ websiteId, onDataLoad, ...props }) {
metric={}
websiteId={websiteId}
onDataLoad={data => onDataLoad?.(percentFilter(data))}
- renderLabel={renderLabel}
+ renderLabel={renderLink}
/>
);
}
diff --git a/components/metrics/DevicesTable.js b/components/metrics/DevicesTable.js
index d09774b9..c704e08d 100644
--- a/components/metrics/DevicesTable.js
+++ b/components/metrics/DevicesTable.js
@@ -1,9 +1,18 @@
import React from 'react';
import MetricsTable from './MetricsTable';
-import { FormattedMessage } from 'react-intl';
+import { useIntl, FormattedMessage } from 'react-intl';
import { getDeviceMessage } from 'components/messages';
+import FilterLink from 'components/common/FilterLink';
export default function DevicesTable({ websiteId, ...props }) {
+ const { formatMessage } = useIntl();
+
+ function renderLink({ x: device }) {
+ return (
+
+ );
+ }
+
return (
}
websiteId={websiteId}
- renderLabel={({ x }) => }
+ renderLabel={renderLink}
/>
);
}
diff --git a/components/metrics/MetricsBar.js b/components/metrics/MetricsBar.js
index f28aeec7..290cde81 100644
--- a/components/metrics/MetricsBar.js
+++ b/components/metrics/MetricsBar.js
@@ -18,7 +18,7 @@ export default function MetricsBar({ websiteId, className }) {
const { startDate, endDate, modified } = dateRange;
const [format, setFormat] = useState(true);
const {
- query: { url, referrer },
+ query: { url, referrer, os, browser, device, country },
} = usePageQuery();
const { data, error, loading } = useFetch(
@@ -29,10 +29,14 @@ export default function MetricsBar({ websiteId, className }) {
end_at: +endDate,
url,
referrer,
+ os,
+ browser,
+ device,
+ country,
},
headers: { [TOKEN_HEADER]: shareToken?.token },
},
- [modified, url, referrer],
+ [modified, url, referrer, os, browser, device, country],
);
const formatFunc = format
diff --git a/components/metrics/MetricsTable.js b/components/metrics/MetricsTable.js
index e1fa6891..48e004cb 100644
--- a/components/metrics/MetricsTable.js
+++ b/components/metrics/MetricsTable.js
@@ -30,7 +30,7 @@ export default function MetricsTable({
const {
resolve,
router,
- query: { url, referrer },
+ query: { url, referrer, os, browser, device, country },
} = usePageQuery();
const { data, loading, error } = useFetch(
@@ -42,12 +42,16 @@ export default function MetricsTable({
end_at: +endDate,
url,
referrer,
+ os,
+ browser,
+ device,
+ country,
},
onDataLoad,
delay: DEFAULT_ANIMATION_DURATION,
headers: { [TOKEN_HEADER]: shareToken?.token },
},
- [modified, url, referrer],
+ [modified, url, referrer, os, browser, device, country],
);
const filteredData = useMemo(() => {
diff --git a/components/metrics/OSTable.js b/components/metrics/OSTable.js
index c77ae074..18bc2499 100644
--- a/components/metrics/OSTable.js
+++ b/components/metrics/OSTable.js
@@ -1,14 +1,20 @@
import React from 'react';
import MetricsTable from './MetricsTable';
import { FormattedMessage } from 'react-intl';
+import FilterLink from 'components/common/FilterLink';
export default function OSTable({ websiteId, ...props }) {
+ function renderLink({ x: os }) {
+ return ;
+ }
+
return (
}
type="os"
metric={}
+ renderLabel={renderLink}
websiteId={websiteId}
/>
);
diff --git a/components/metrics/PagesTable.js b/components/metrics/PagesTable.js
index 6fe8c139..98a0fd72 100644
--- a/components/metrics/PagesTable.js
+++ b/components/metrics/PagesTable.js
@@ -1,23 +1,15 @@
import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';
-import classNames from 'classnames';
-import Link from 'next/link';
+import FilterLink from 'components/common/FilterLink';
import FilterButtons from 'components/common/FilterButtons';
import { urlFilter } from 'lib/filters';
-import { safeDecodeURI } from 'lib/url';
-import usePageQuery from 'hooks/usePageQuery';
import MetricsTable from './MetricsTable';
-import styles from './PagesTable.module.css';
export const FILTER_COMBINED = 0;
export const FILTER_RAW = 1;
export default function PagesTable({ websiteId, websiteDomain, showFilters, ...props }) {
const [filter, setFilter] = useState(FILTER_COMBINED);
- const {
- resolve,
- query: { url: currentUrl },
- } = usePageQuery();
const buttons = [
{
@@ -28,18 +20,7 @@ export default function PagesTable({ websiteId, websiteDomain, showFilters, ...p
];
const renderLink = ({ x: url }) => {
- return (
-
-
- {safeDecodeURI(url)}
-
-
- );
+ return ;
};
return (
diff --git a/components/metrics/PagesTable.module.css b/components/metrics/PagesTable.module.css
deleted file mode 100644
index 3c592a74..00000000
--- a/components/metrics/PagesTable.module.css
+++ /dev/null
@@ -1,8 +0,0 @@
-body .inactive {
- color: var(--gray500);
-}
-
-body .active {
- color: var(--gray900);
- font-weight: 600;
-}
diff --git a/components/metrics/ReferrersTable.js b/components/metrics/ReferrersTable.js
index 93cdb956..1a9e3f2d 100644
--- a/components/metrics/ReferrersTable.js
+++ b/components/metrics/ReferrersTable.js
@@ -2,14 +2,8 @@ import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import MetricsTable from './MetricsTable';
import FilterButtons from 'components/common/FilterButtons';
+import FilterLink from 'components/common/FilterLink';
import { refFilter } from 'lib/filters';
-import { safeDecodeURI } from 'lib/url';
-import Link from 'next/link';
-import classNames from 'classnames';
-import usePageQuery from 'hooks/usePageQuery';
-import External from 'assets/arrow-up-right-from-square.svg';
-import Icon from '../common/Icon';
-import styles from './ReferrersTable.module.css';
export const FILTER_DOMAIN_ONLY = 0;
export const FILTER_COMBINED = 1;
@@ -17,10 +11,6 @@ export const FILTER_RAW = 2;
export default function ReferrersTable({ websiteId, websiteDomain, showFilters, ...props }) {
const [filter, setFilter] = useState(FILTER_COMBINED);
- const {
- resolve,
- query: { referrer: currentRef },
- } = usePageQuery();
const buttons = [
{
@@ -34,24 +24,8 @@ export default function ReferrersTable({ websiteId, websiteDomain, showFilters,
{ label: , value: FILTER_RAW },
];
- const renderLink = ({ w: link, x: label }) => {
- return (
-
- );
+ const renderLink = ({ w: link, x: referrer }) => {
+ return ;
};
return (
diff --git a/components/metrics/WebsiteChart.js b/components/metrics/WebsiteChart.js
index 817addc1..03cc7b58 100644
--- a/components/metrics/WebsiteChart.js
+++ b/components/metrics/WebsiteChart.js
@@ -33,7 +33,7 @@ export default function WebsiteChart({
const {
router,
resolve,
- query: { url, referrer },
+ query: { url, referrer, os, browser, device, country },
} = usePageQuery();
const { get } = useApi();
@@ -47,11 +47,15 @@ export default function WebsiteChart({
tz: timezone,
url,
referrer,
+ os,
+ browser,
+ device,
+ country,
},
onDataLoad,
headers: { [TOKEN_HEADER]: shareToken?.token },
},
- [modified, url, referrer],
+ [modified, url, referrer, os, browser, device, country],
);
const chartData = useMemo(() => {
@@ -88,7 +92,10 @@ export default function WebsiteChart({
stickyClassName={styles.sticky}
enabled={stickyHeader}
>
-
+
diff --git a/lib/queries.js b/lib/queries.js
index 1b89ddea..25284428 100644
--- a/lib/queries.js
+++ b/lib/queries.js
@@ -21,24 +21,6 @@ export function getDatabase() {
return type;
}
-export async function runQuery(query) {
- return query.catch(e => {
- throw e;
- });
-}
-
-export async function rawQuery(query, params = []) {
- const db = getDatabase();
-
- if (db !== POSTGRESQL && db !== MYSQL) {
- return Promise.reject(new Error('Unknown database.'));
- }
-
- const sql = db === MYSQL ? query.replace(/\$[0-9]+/g, '?') : query;
-
- return prisma.$queryRawUnsafe.apply(prisma, [sql, ...params]);
-}
-
export function getDateQuery(field, unit, timezone) {
const db = getDatabase();
@@ -72,6 +54,79 @@ export function getTimestampInterval(field) {
}
}
+export function getFilterQuery(table, filters = {}, params = []) {
+ const query = Object.keys(filters).reduce((arr, key) => {
+ const value = filters[key];
+
+ if (value === undefined) {
+ return arr;
+ }
+
+ switch (key) {
+ case 'url':
+ if (table === 'session' || table === 'pageview') {
+ arr.push(`and ${table}.${key}=$${params.length + 1}`);
+ params.push(decodeURIComponent(value));
+ }
+ break;
+
+ case 'os':
+ case 'browser':
+ case 'device':
+ case 'country':
+ if (table === 'session') {
+ arr.push(`and ${table}.${key}=$${params.length + 1}`);
+ params.push(decodeURIComponent(value));
+ }
+ break;
+
+ case 'event_type':
+ if (table === 'event') {
+ arr.push(`and ${table}.${key}=$${params.length + 1}`);
+ params.push(decodeURIComponent(value));
+ }
+ break;
+
+ case 'referrer':
+ if (table === 'pageview') {
+ arr.push(`and ${table}.referrer like $${params.length + 1}`);
+ params.push(`%${decodeURIComponent(value)}%`);
+ }
+ break;
+
+ case 'domain':
+ if (table === 'pageview') {
+ arr.push(`and ${table}.referrer not like $${params.length + 1}`);
+ arr.push(`and ${table}.referrer not like '/%'`);
+ params.push(`%://${value}/%`);
+ }
+ break;
+ }
+
+ return arr;
+ }, []);
+
+ return query.join('\n');
+}
+
+export async function runQuery(query) {
+ return query.catch(e => {
+ throw e;
+ });
+}
+
+export async function rawQuery(query, params = []) {
+ const db = getDatabase();
+
+ if (db !== POSTGRESQL && db !== MYSQL) {
+ return Promise.reject(new Error('Unknown database.'));
+ }
+
+ const sql = db === MYSQL ? query.replace(/\$[0-9]+/g, '?') : query;
+
+ return runQuery(prisma.$queryRawUnsafe.apply(prisma, [sql, ...params]));
+}
+
export async function getWebsiteById(website_id) {
return runQuery(
prisma.website.findUnique({
@@ -344,19 +399,12 @@ export async function getEvents(websites, start_at) {
export function getWebsiteStats(website_id, start_at, end_at, filters = {}) {
const params = [website_id, start_at, end_at];
- const { url, referrer } = filters;
- let urlFilter = '';
- let refFilter = '';
+ const { url, referrer, os, browser, device, country } = filters;
- if (url) {
- urlFilter = `and url=$${params.length + 1}`;
- params.push(decodeURIComponent(url));
- }
-
- if (referrer) {
- refFilter = `and referrer like $${params.length + 1}`;
- params.push(`%${decodeURIComponent(referrer)}%`);
- }
+ const joinSession =
+ os || browser || device || country
+ ? 'inner join session on session.session_id = pageview.session_id'
+ : '';
return rawQuery(
`
@@ -365,15 +413,16 @@ export function getWebsiteStats(website_id, start_at, end_at, filters = {}) {
sum(case when t.c = 1 then 1 else 0 end) as "bounces",
sum(t.time) as "totaltime"
from (
- select session_id,
- ${getDateQuery('created_at', 'hour')},
+ select pageview.session_id,
+ ${getDateQuery('pageview.created_at', 'hour')},
count(*) c,
- ${getTimestampInterval('created_at')} as "time"
+ ${getTimestampInterval('pageview.created_at')} as "time"
from pageview
- where website_id=$1
- and created_at between $2 and $3
- ${urlFilter}
- ${refFilter}
+ ${joinSession}
+ where pageview.website_id=$1
+ and pageview.created_at between $2 and $3
+ ${getFilterQuery('pageview', { url, referrer }, params)}
+ ${getFilterQuery('session', { os, browser, device, country }, params)}
group by 1, 2
) t
`,
@@ -391,30 +440,22 @@ export function getPageviewStats(
filters = {},
) {
const params = [website_id, start_at, end_at];
- const { url, referrer } = filters;
-
- let urlFilter = '';
- let refFilter = '';
-
- if (url) {
- urlFilter = `and url=$${params.length + 1}`;
- params.push(decodeURIComponent(url));
- }
-
- if (referrer) {
- refFilter = `and referrer like $${params.length + 1}`;
- params.push(`%${decodeURIComponent(referrer)}%`);
- }
+ const { url, referrer, os, browser, device, country } = filters;
+ const joinSession =
+ os || browser || device || country
+ ? 'inner join session on session.session_id = pageview.session_id'
+ : '';
return rawQuery(
`
- select ${getDateQuery('created_at', unit, timezone)} t,
+ select ${getDateQuery('pageview.created_at', unit, timezone)} t,
count(${count}) y
from pageview
- where website_id=$1
- and created_at between $2 and $3
- ${urlFilter}
- ${refFilter}
+ ${joinSession}
+ where pageview.website_id=$1
+ and pageview.created_at between $2 and $3
+ ${getFilterQuery('pageview', { url, referrer }, params)}
+ ${getFilterQuery('session', { os, browser, device, country }, params)}
group by 1
order by 1
`,
@@ -424,32 +465,24 @@ export function getPageviewStats(
export function getSessionMetrics(website_id, start_at, end_at, field, filters = {}) {
const params = [website_id, start_at, end_at];
- const { url, referrer } = filters;
-
- let urlFilter = '';
- let refFilter = '';
-
- if (url) {
- urlFilter = `and url=$${params.length + 1}`;
- params.push(decodeURIComponent(url));
- }
-
- if (referrer) {
- refFilter = `and referrer like $${params.length + 1}`;
- params.push(`%${decodeURIComponent(referrer)}%`);
- }
+ const { url, referrer, os, browser, device, country } = filters;
+ const joinSession =
+ os || browser || device || country
+ ? 'inner join session on session.session_id = pageview.session_id'
+ : '';
return rawQuery(
`
select ${field} x, count(*) y
- from session
- where session_id in (
- select session_id
+ from session as x
+ where x.session_id in (
+ select pageview.session_id
from pageview
- where website_id=$1
- and created_at between $2 and $3
- ${urlFilter}
- ${refFilter}
+ ${joinSession}
+ where pageview.website_id=$1
+ and pageview.created_at between $2 and $3
+ ${getFilterQuery('pageview', { url, referrer }, params)}
+ ${getFilterQuery('session', { os, browser, device, country }, params)}
)
group by 1
order by 2 desc
@@ -460,36 +493,21 @@ export function getSessionMetrics(website_id, start_at, end_at, field, filters =
export function getPageviewMetrics(website_id, start_at, end_at, field, table, filters = {}) {
const params = [website_id, start_at, end_at];
- const { domain, url, referrer } = filters;
-
- let domainFilter = '';
- let urlFilter = '';
- let refFilter = '';
-
- if (domain) {
- domainFilter = `and referrer not like $${params.length + 1} and referrer not like '/%'`;
- params.push(`%://${domain}/%`);
- }
-
- if (url) {
- urlFilter = `and url=$${params.length + 1}`;
- params.push(decodeURIComponent(url));
- }
-
- if (referrer && table !== 'event') {
- refFilter = `and referrer like $${params.length + 1}`;
- params.push(`%${decodeURIComponent(referrer)}%`);
- }
+ const { domain, url, referrer, os, browser, device, country } = filters;
+ const joinSession =
+ (os || browser || device || country) && table === 'pageview'
+ ? 'inner join session on session.session_id = pageview.session_id'
+ : '';
return rawQuery(
`
select ${field} x, count(*) y
from ${table}
- where website_id=$1
- and created_at between $2 and $3
- ${domainFilter}
- ${urlFilter}
- ${refFilter}
+ ${joinSession}
+ where ${table}.website_id=$1
+ and ${table}.created_at between $2 and $3
+ ${getFilterQuery(table, { domain, url, referrer }, params)}
+ ${joinSession && getFilterQuery('session', { os, browser, device, country }, params)}
group by 1
order by 2 desc
`,
@@ -521,20 +539,6 @@ export function getEventMetrics(
filters = {},
) {
const params = [website_id, start_at, end_at];
- const { url, event_type } = filters;
-
- let urlFilter = '';
- let eventTypeFilter = '';
-
- if (url) {
- urlFilter = `and url=$${params.length + 1}`;
- params.push(decodeURIComponent(url));
- }
-
- if (event_type) {
- eventTypeFilter = `and event_type=$${params.length + 1}`;
- params.push(event_type);
- }
return rawQuery(
`
@@ -545,8 +549,7 @@ export function getEventMetrics(
from event
where website_id=$1
and created_at between $2 and $3
- ${urlFilter}
- ${eventTypeFilter}
+ ${getFilterQuery('event', filters, params)}
group by 1, 2
order by 2
`,
diff --git a/package.json b/package.json
index b93499d2..c7b40c23 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "umami",
- "version": "1.29.0",
- "description": "A simple, fast, website analytics alternative to Google Analytics.",
+ "version": "1.30.0",
+ "description": "A simple, fast, privacy-focused alternative to Google Analytics.",
"author": "Mike Cao ",
"license": "MIT",
"homepage": "https://umami.is",
diff --git a/pages/_app.js b/pages/_app.js
index 86edfcdc..1e03db15 100644
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -37,7 +37,7 @@ export default function App({ Component, pageProps }) {
diff --git a/pages/api/collect.js b/pages/api/collect.js
index 683a1c93..c9159858 100644
--- a/pages/api/collect.js
+++ b/pages/api/collect.js
@@ -14,8 +14,9 @@ export default async (req, res) => {
return ok(res);
}
- if (process.env.IGNORE_IP) {
- const ips = process.env.IGNORE_IP.split(',').map(n => n.trim());
+ const ignoreIps = process.env.IGNORE_IP;
+ if (ignoreIps) {
+ const ips = ignoreIps.split(',').map(n => n.trim());
const ip = getIpAddress(req);
const blocked = ips.find(i => {
if (i === ip) return true;
diff --git a/pages/api/website/[id]/metrics.js b/pages/api/website/[id]/metrics.js
index aa285a55..44001cfc 100644
--- a/pages/api/website/[id]/metrics.js
+++ b/pages/api/website/[id]/metrics.js
@@ -33,22 +33,31 @@ export default async (req, res) => {
return unauthorized(res);
}
- const { id, type, start_at, end_at, url, referrer } = req.query;
+ const { id, type, start_at, end_at, url, referrer, os, browser, device, country } = req.query;
const websiteId = +id;
const startDate = new Date(+start_at);
const endDate = new Date(+end_at);
if (sessionColumns.includes(type)) {
- let data = await getSessionMetrics(websiteId, startDate, endDate, type, { url, referrer });
+ let data = await getSessionMetrics(websiteId, startDate, endDate, type, {
+ os,
+ browser,
+ device,
+ country,
+ });
if (type === 'language') {
let combined = {};
for (let { x, y } of data) {
x = String(x).toLowerCase().split('-')[0];
- if (!combined[x]) combined[x] = { x, y };
- else combined[x].y += y;
+
+ if (!combined[x]) {
+ combined[x] = { x, y };
+ } else {
+ combined[x].y += y;
+ }
}
data = Object.values(combined);
@@ -77,8 +86,12 @@ export default async (req, res) => {
getTable(type),
{
domain,
- url: type !== 'url' && url,
- referrer,
+ url: type !== 'url' ? url : undefined,
+ referrer: type !== 'referrer' ? referrer : undefined,
+ os: type !== 'os' ? os : undefined,
+ browser: type !== 'browser' ? browser : undefined,
+ device: type !== 'device' ? device : undefined,
+ country: type !== 'country' ? country : undefined,
},
);
diff --git a/pages/api/website/[id]/pageviews.js b/pages/api/website/[id]/pageviews.js
index 41ea06eb..bc663ce1 100644
--- a/pages/api/website/[id]/pageviews.js
+++ b/pages/api/website/[id]/pageviews.js
@@ -14,7 +14,8 @@ export default async (req, res) => {
return unauthorized(res);
}
- const { id, start_at, end_at, unit, tz, url, referrer } = req.query;
+ const { id, start_at, end_at, unit, tz, url, referrer, os, browser, device, country } =
+ req.query;
const websiteId = +id;
const startDate = new Date(+start_at);
@@ -25,10 +26,20 @@ export default async (req, res) => {
}
const [pageviews, sessions] = await Promise.all([
- getPageviewStats(websiteId, startDate, endDate, tz, unit, '*', { url, referrer }),
- getPageviewStats(websiteId, startDate, endDate, tz, unit, 'distinct session_id', {
+ getPageviewStats(websiteId, startDate, endDate, tz, unit, '*', {
url,
referrer,
+ os,
+ browser,
+ device,
+ country,
+ }),
+ getPageviewStats(websiteId, startDate, endDate, tz, unit, 'distinct pageview.session_id', {
+ url,
+ os,
+ browser,
+ device,
+ country,
}),
]);
diff --git a/pages/api/website/[id]/stats.js b/pages/api/website/[id]/stats.js
index 7b1c5cf0..15cc45ad 100644
--- a/pages/api/website/[id]/stats.js
+++ b/pages/api/website/[id]/stats.js
@@ -11,7 +11,7 @@ export default async (req, res) => {
return unauthorized(res);
}
- const { id, start_at, end_at, url, referrer } = req.query;
+ const { id, start_at, end_at, url, referrer, os, browser, device, country } = req.query;
const websiteId = +id;
const startDate = new Date(+start_at);
@@ -21,10 +21,21 @@ export default async (req, res) => {
const prevStartDate = new Date(+start_at - distance);
const prevEndDate = new Date(+end_at - distance);
- const metrics = await getWebsiteStats(websiteId, startDate, endDate, { url, referrer });
+ const metrics = await getWebsiteStats(websiteId, startDate, endDate, {
+ url,
+ referrer,
+ os,
+ browser,
+ device,
+ country,
+ });
const prevPeriod = await getWebsiteStats(websiteId, prevStartDate, prevEndDate, {
url,
referrer,
+ os,
+ browser,
+ device,
+ country,
});
const stats = Object.keys(metrics[0]).reduce((obj, key) => {