diff --git a/components/metrics/ActiveUsers.js b/components/metrics/ActiveUsers.js
index 3b691c10..6dabd3d5 100644
--- a/components/metrics/ActiveUsers.js
+++ b/components/metrics/ActiveUsers.js
@@ -3,12 +3,15 @@ import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import useFetch from 'hooks/useFetch';
import Dot from 'components/common/Dot';
+import { TOKEN_HEADER } from 'lib/constants';
+import useShareToken from 'hooks/useShareToken';
import styles from './ActiveUsers.module.css';
-export default function ActiveUsers({ websiteId, token, className }) {
+export default function ActiveUsers({ websiteId, className }) {
+ const shareToken = useShareToken();
const { data } = useFetch(`/api/website/${websiteId}/active`, {
- params: { token },
interval: 60000,
+ headers: { [TOKEN_HEADER]: shareToken?.token },
});
const count = useMemo(() => {
return data?.[0]?.x || 0;
diff --git a/components/metrics/BarChart.js b/components/metrics/BarChart.js
index 05183e1b..a31a6f40 100644
--- a/components/metrics/BarChart.js
+++ b/components/metrics/BarChart.js
@@ -65,7 +65,7 @@ export default function BarChart({
}
function renderYLabel(label) {
- return +label > 1 ? formatLongNumber(label) : label;
+ return +label > 1000 ? formatLongNumber(label) : label;
}
function renderTooltip(model) {
diff --git a/components/metrics/BrowsersTable.js b/components/metrics/BrowsersTable.js
index 3481449c..12c1087b 100644
--- a/components/metrics/BrowsersTable.js
+++ b/components/metrics/BrowsersTable.js
@@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
import MetricsTable from './MetricsTable';
import { browserFilter } from 'lib/filters';
-export default function BrowsersTable({ websiteId, token, ...props }) {
+export default function BrowsersTable({ websiteId, ...props }) {
return (
}
websiteId={websiteId}
- token={token}
dataFilter={browserFilter}
/>
);
diff --git a/components/metrics/CountriesTable.js b/components/metrics/CountriesTable.js
index 5b44bd23..59d17dfb 100644
--- a/components/metrics/CountriesTable.js
+++ b/components/metrics/CountriesTable.js
@@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
import useCountryNames from 'hooks/useCountryNames';
import useLocale from 'hooks/useLocale';
-export default function CountriesTable({ websiteId, token, onDataLoad, ...props }) {
+export default function CountriesTable({ websiteId, onDataLoad, ...props }) {
const [locale] = useLocale();
const countryNames = useCountryNames(locale);
@@ -20,7 +20,6 @@ export default function CountriesTable({ websiteId, token, onDataLoad, ...props
type="country"
metric={}
websiteId={websiteId}
- token={token}
onDataLoad={data => onDataLoad?.(percentFilter(data))}
renderLabel={renderLabel}
/>
diff --git a/components/metrics/DevicesTable.js b/components/metrics/DevicesTable.js
index e71355db..52b6b5fc 100644
--- a/components/metrics/DevicesTable.js
+++ b/components/metrics/DevicesTable.js
@@ -4,7 +4,7 @@ import { deviceFilter } from 'lib/filters';
import { FormattedMessage } from 'react-intl';
import { getDeviceMessage } from 'components/messages';
-export default function DevicesTable({ websiteId, token, ...props }) {
+export default function DevicesTable({ websiteId, ...props }) {
return (
}
websiteId={websiteId}
- token={token}
dataFilter={deviceFilter}
renderLabel={({ x }) => getDeviceMessage(x)}
/>
diff --git a/components/metrics/EventsChart.js b/components/metrics/EventsChart.js
index e401402d..9a5827b2 100644
--- a/components/metrics/EventsChart.js
+++ b/components/metrics/EventsChart.js
@@ -6,13 +6,15 @@ import useFetch from 'hooks/useFetch';
import useDateRange from 'hooks/useDateRange';
import useTimezone from 'hooks/useTimezone';
import usePageQuery from 'hooks/usePageQuery';
-import { EVENT_COLORS } from 'lib/constants';
+import useShareToken from 'hooks/useShareToken';
+import { EVENT_COLORS, TOKEN_HEADER } from 'lib/constants';
export default function EventsChart({ websiteId, className, token }) {
const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, unit, modified } = dateRange;
const [timezone] = useTimezone();
const { query } = usePageQuery();
+ const shareToken = useShareToken();
const { data } = useFetch(
`/api/website/${websiteId}/events`,
@@ -25,6 +27,7 @@ export default function EventsChart({ websiteId, className, token }) {
url: query.url,
token,
},
+ headers: { [TOKEN_HEADER]: shareToken?.token },
},
[modified],
);
diff --git a/components/metrics/EventsTable.js b/components/metrics/EventsTable.js
index 73df5028..c415a3e9 100644
--- a/components/metrics/EventsTable.js
+++ b/components/metrics/EventsTable.js
@@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
import MetricsTable from './MetricsTable';
import Tag from 'components/common/Tag';
-export default function EventsTable({ websiteId, token, ...props }) {
+export default function EventsTable({ websiteId, ...props }) {
return (
}
websiteId={websiteId}
- token={token}
renderLabel={({ x }) => }
/>
);
diff --git a/components/metrics/MetricsBar.js b/components/metrics/MetricsBar.js
index 3b159327..33b6eaad 100644
--- a/components/metrics/MetricsBar.js
+++ b/components/metrics/MetricsBar.js
@@ -5,12 +5,15 @@ import Loading from 'components/common/Loading';
import ErrorMessage from 'components/common/ErrorMessage';
import useFetch from 'hooks/useFetch';
import useDateRange from 'hooks/useDateRange';
-import { formatShortTime, formatNumber, formatLongNumber } from 'lib/format';
import usePageQuery from 'hooks/usePageQuery';
+import useShareToken from 'hooks/useShareToken';
+import { formatShortTime, formatNumber, formatLongNumber } from 'lib/format';
+import { TOKEN_HEADER } from 'lib/constants';
import MetricCard from './MetricCard';
import styles from './MetricsBar.module.css';
-export default function MetricsBar({ websiteId, token, className }) {
+export default function MetricsBar({ websiteId, className }) {
+ const shareToken = useShareToken();
const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, modified } = dateRange;
const [format, setFormat] = useState(true);
@@ -25,8 +28,8 @@ export default function MetricsBar({ websiteId, token, className }) {
start_at: +startDate,
end_at: +endDate,
url,
- token,
},
+ headers: { [TOKEN_HEADER]: shareToken?.token },
},
[modified],
);
diff --git a/components/metrics/MetricsTable.js b/components/metrics/MetricsTable.js
index 9a117afd..5d96ed41 100644
--- a/components/metrics/MetricsTable.js
+++ b/components/metrics/MetricsTable.js
@@ -8,15 +8,15 @@ import Arrow from 'assets/arrow-right.svg';
import { percentFilter } from 'lib/filters';
import useDateRange from 'hooks/useDateRange';
import usePageQuery from 'hooks/usePageQuery';
+import useShareToken from 'hooks/useShareToken';
import ErrorMessage from 'components/common/ErrorMessage';
import DataTable from './DataTable';
-import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
+import { DEFAULT_ANIMATION_DURATION, TOKEN_HEADER } from 'lib/constants';
import styles from './MetricsTable.module.css';
export default function MetricsTable({
websiteId,
websiteDomain,
- token,
title,
metric,
type,
@@ -30,6 +30,7 @@ export default function MetricsTable({
onDataLoad,
...props
}) {
+ const shareToken = useShareToken();
const [dateRange] = useDateRange(websiteId);
const { startDate, endDate, modified } = dateRange;
const {
@@ -47,10 +48,10 @@ export default function MetricsTable({
end_at: +endDate,
domain: websiteDomain,
url,
- token,
},
onDataLoad,
delay: DEFAULT_ANIMATION_DURATION,
+ headers: { [TOKEN_HEADER]: shareToken?.token },
},
[modified],
);
diff --git a/components/metrics/OSTable.js b/components/metrics/OSTable.js
index 63d4739c..c1790e17 100644
--- a/components/metrics/OSTable.js
+++ b/components/metrics/OSTable.js
@@ -3,15 +3,14 @@ import MetricsTable from './MetricsTable';
import { osFilter } from 'lib/filters';
import { FormattedMessage } from 'react-intl';
-export default function OSTable({ websiteId, token, limit }) {
+export default function OSTable({ websiteId, ...props }) {
return (
}
type="os"
metric={}
websiteId={websiteId}
- token={token}
- limit={limit}
dataFilter={osFilter}
/>
);
diff --git a/components/metrics/PagesTable.js b/components/metrics/PagesTable.js
index 0740992c..978dd22c 100644
--- a/components/metrics/PagesTable.js
+++ b/components/metrics/PagesTable.js
@@ -10,7 +10,7 @@ import usePageQuery from 'hooks/usePageQuery';
import MetricsTable from './MetricsTable';
import styles from './PagesTable.module.css';
-export default function PagesTable({ websiteId, token, websiteDomain, showFilters, ...props }) {
+export default function PagesTable({ websiteId, websiteDomain, showFilters, ...props }) {
const [filter, setFilter] = useState(FILTER_COMBINED);
const {
resolve,
@@ -48,7 +48,6 @@ export default function PagesTable({ websiteId, token, websiteDomain, showFilter
type="url"
metric={}
websiteId={websiteId}
- token={token}
dataFilter={urlFilter}
filterOptions={{ domain: websiteDomain, raw: filter === FILTER_RAW }}
renderLabel={renderLink}
diff --git a/components/metrics/ReferrersTable.js b/components/metrics/ReferrersTable.js
index 0543f17e..2e04b6dc 100644
--- a/components/metrics/ReferrersTable.js
+++ b/components/metrics/ReferrersTable.js
@@ -1,12 +1,12 @@
import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import MetricsTable from './MetricsTable';
-import { refFilter } from 'lib/filters';
import ButtonGroup from 'components/common/ButtonGroup';
+import ButtonLayout from 'components/layout/ButtonLayout';
import { FILTER_DOMAIN_ONLY, FILTER_COMBINED, FILTER_RAW } from 'lib/constants';
-import ButtonLayout from '../layout/ButtonLayout';
+import { refFilter } from 'lib/filters';
-export default function ReferrersTable({ websiteId, websiteDomain, token, showFilters, ...props }) {
+export default function ReferrersTable({ websiteId, websiteDomain, showFilters, ...props }) {
const [filter, setFilter] = useState(FILTER_COMBINED);
const buttons = [
@@ -41,7 +41,6 @@ export default function ReferrersTable({ websiteId, websiteDomain, token, showFi
metric={}
websiteId={websiteId}
websiteDomain={websiteDomain}
- token={token}
dataFilter={refFilter}
filterOptions={{
domain: websiteDomain,
diff --git a/components/metrics/WebsiteChart.js b/components/metrics/WebsiteChart.js
index 5a8e7dbf..07ba5161 100644
--- a/components/metrics/WebsiteChart.js
+++ b/components/metrics/WebsiteChart.js
@@ -14,15 +14,17 @@ import { getDateArray, getDateLength } from 'lib/date';
import Times from 'assets/times.svg';
import styles from './WebsiteChart.module.css';
import ErrorMessage from '../common/ErrorMessage';
+import useShareToken from '../../hooks/useShareToken';
+import { TOKEN_HEADER } from '../../lib/constants';
export default function WebsiteChart({
websiteId,
- token,
title,
stickyHeader = false,
showLink = false,
onDataLoad = () => {},
}) {
+ const shareToken = useShareToken();
const [dateRange, setDateRange] = useDateRange(websiteId);
const { startDate, endDate, unit, value, modified } = dateRange;
const [timezone] = useTimezone();
@@ -41,9 +43,9 @@ export default function WebsiteChart({
unit,
tz: timezone,
url,
- token,
},
onDataLoad,
+ headers: { [TOKEN_HEADER]: shareToken?.token },
},
[modified],
);
@@ -64,7 +66,7 @@ export default function WebsiteChart({
return (
-
+
{url && }
-
+
{title}
-
+
{showLink && (
diff --git a/components/pages/RealtimeDashboard.js b/components/pages/RealtimeDashboard.js
index 33689086..2dbf858c 100644
--- a/components/pages/RealtimeDashboard.js
+++ b/components/pages/RealtimeDashboard.js
@@ -13,6 +13,7 @@ import useFetch from 'hooks/useFetch';
import useLocale from 'hooks/useLocale';
import useCountryNames from 'hooks/useCountryNames';
import { percentFilter } from 'lib/filters';
+import { TOKEN_HEADER } from 'lib/constants';
import styles from './RealtimeDashboard.module.css';
const REALTIME_RANGE = 30;
@@ -39,7 +40,7 @@ export default function RealtimeDashboard() {
params: { start_at: data?.timestamp },
disabled: !init?.websites?.length || !data,
interval: REALTIME_INTERVAL,
- headers: { 'x-umami-token': init?.token },
+ headers: { [TOKEN_HEADER]: init?.token },
});
const renderCountryName = useCallback(({ x }) => countryNames[x], []);
diff --git a/components/pages/WebsiteDetails.js b/components/pages/WebsiteDetails.js
index ddbbbc21..b808f44f 100644
--- a/components/pages/WebsiteDetails.js
+++ b/components/pages/WebsiteDetails.js
@@ -20,7 +20,8 @@ import EventsTable from '../metrics/EventsTable';
import EventsChart from '../metrics/EventsChart';
import useFetch from 'hooks/useFetch';
import usePageQuery from 'hooks/usePageQuery';
-import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
+import useShareToken from 'hooks/useShareToken';
+import { DEFAULT_ANIMATION_DURATION, TOKEN_HEADER } from 'lib/constants';
const views = {
url: PagesTable,
@@ -32,8 +33,11 @@ const views = {
event: EventsTable,
};
-export default function WebsiteDetails({ websiteId, token }) {
- const { data } = useFetch(`/api/website/${websiteId}`, { params: { token } });
+export default function WebsiteDetails({ websiteId }) {
+ const shareToken = useShareToken();
+ const { data } = useFetch(`/api/website/${websiteId}`, {
+ headers: { [TOKEN_HEADER]: shareToken?.token },
+ });
const [chartLoaded, setChartLoaded] = useState(false);
const [countryData, setCountryData] = useState();
const [eventsData, setEventsData] = useState();
@@ -93,7 +97,6 @@ export default function WebsiteDetails({ websiteId, token }) {
const tableProps = {
websiteId,
- token,
websiteDomain: data?.domain,
limit: 10,
};
@@ -116,7 +119,6 @@ export default function WebsiteDetails({ websiteId, token }) {
-
+
diff --git a/hooks/useShareToken.js b/hooks/useShareToken.js
new file mode 100644
index 00000000..16ee11d0
--- /dev/null
+++ b/hooks/useShareToken.js
@@ -0,0 +1,25 @@
+import { useEffect } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { get } from 'lib/web';
+import { setShareToken } from 'redux/actions/app';
+
+export default function useShareToken(shareId) {
+ const dispatch = useDispatch();
+ const shareToken = useSelector(state => state.app.shareToken);
+
+ async function loadToken(id) {
+ const { data } = await get(`/api/share/${id}`);
+
+ if (data) {
+ dispatch(setShareToken(data));
+ }
+ }
+
+ useEffect(() => {
+ if (shareId) {
+ loadToken(shareId);
+ }
+ }, [shareId]);
+
+ return shareToken;
+}
diff --git a/lib/auth.js b/lib/auth.js
index 370db1c4..379f6777 100644
--- a/lib/auth.js
+++ b/lib/auth.js
@@ -1,6 +1,6 @@
import { parse } from 'cookie';
import { parseSecureToken, parseToken } from './crypto';
-import { AUTH_COOKIE_NAME } from './constants';
+import { AUTH_COOKIE_NAME, TOKEN_HEADER } from './constants';
import { getWebsiteById } from './queries';
export async function getAuthToken(req) {
@@ -26,7 +26,8 @@ export async function isValidToken(token, validation) {
}
export async function allowQuery(req, skipToken) {
- const { id, token } = req.query;
+ const { id } = req.query;
+ const token = req.headers[TOKEN_HEADER];
const websiteId = +id;
const website = await getWebsiteById(websiteId);
diff --git a/lib/constants.js b/lib/constants.js
index 80d8eed1..72f7fce6 100644
--- a/lib/constants.js
+++ b/lib/constants.js
@@ -4,6 +4,7 @@ export const TIMEZONE_CONFIG = 'umami.timezone';
export const DATE_RANGE_CONFIG = 'umami.date-range';
export const THEME_CONFIG = 'umami.theme';
export const VERSION_CHECK = 'umami.version-check';
+export const TOKEN_HEADER = 'x-umami-token';
export const THEME_COLORS = {
light: {
diff --git a/pages/api/realtime/update.js b/pages/api/realtime/update.js
index 15582f81..730219ae 100644
--- a/pages/api/realtime/update.js
+++ b/pages/api/realtime/update.js
@@ -2,6 +2,7 @@ import { useAuth } from 'lib/middleware';
import { ok, methodNotAllowed, badRequest } from 'lib/response';
import { getRealtimeData } from 'lib/queries';
import { parseToken } from 'lib/crypto';
+import { TOKEN_HEADER } from 'lib/constants';
export default async (req, res) => {
await useAuth(req, res);
@@ -9,7 +10,7 @@ export default async (req, res) => {
if (req.method === 'GET') {
const { start_at } = req.query;
- const token = req.headers['x-umami-token'];
+ const token = req.headers[TOKEN_HEADER];
if (!token) {
return badRequest(res);
diff --git a/pages/share/[...id].js b/pages/share/[...id].js
index 012e778a..ff5e4a6e 100644
--- a/pages/share/[...id].js
+++ b/pages/share/[...id].js
@@ -2,23 +2,23 @@ import React from 'react';
import { useRouter } from 'next/router';
import Layout from 'components/layout/Layout';
import WebsiteDetails from 'components/pages/WebsiteDetails';
-import useFetch from 'hooks/useFetch';
+import useShareToken from 'hooks/useShareToken';
export default function SharePage() {
const router = useRouter();
const { id } = router.query;
const shareId = id?.[0];
- const { data } = useFetch(`/api/share/${shareId}`, { disabled: !shareId });
+ const shareToken = useShareToken(shareId);
- if (!data) {
+ if (!shareToken) {
return null;
}
- const { websiteId, token } = data;
+ const { websiteId } = shareToken;
return (
-
+
);
}
diff --git a/redux/actions/app.js b/redux/actions/app.js
index 900be56d..f10d162a 100644
--- a/redux/actions/app.js
+++ b/redux/actions/app.js
@@ -19,6 +19,7 @@ const app = createSlice({
latest: null,
hasUpdate: false,
},
+ shareToken: null,
},
reducers: {
setLocale(state, action) {
@@ -33,10 +34,14 @@ const app = createSlice({
state.versions = action.payload;
return state;
},
+ setShareToken(state, action) {
+ state.shareToken = action.payload;
+ return state;
+ },
},
});
-export const { setLocale, setTheme, setVersions } = app.actions;
+export const { setLocale, setTheme, setVersions, setShareToken } = app.actions;
export default app.reducer;