mirror of
https://github.com/kremalicious/umami.git
synced 2025-01-04 19:15:09 +01:00
Created share token hook.
This commit is contained in:
parent
4119e80a9a
commit
5a73c224b7
@ -3,12 +3,15 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import Dot from 'components/common/Dot';
|
import Dot from 'components/common/Dot';
|
||||||
|
import { TOKEN_HEADER } from 'lib/constants';
|
||||||
|
import useShareToken from 'hooks/useShareToken';
|
||||||
import styles from './ActiveUsers.module.css';
|
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`, {
|
const { data } = useFetch(`/api/website/${websiteId}/active`, {
|
||||||
params: { token },
|
|
||||||
interval: 60000,
|
interval: 60000,
|
||||||
|
headers: { [TOKEN_HEADER]: shareToken?.token },
|
||||||
});
|
});
|
||||||
const count = useMemo(() => {
|
const count = useMemo(() => {
|
||||||
return data?.[0]?.x || 0;
|
return data?.[0]?.x || 0;
|
||||||
|
@ -65,7 +65,7 @@ export default function BarChart({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderYLabel(label) {
|
function renderYLabel(label) {
|
||||||
return +label > 1 ? formatLongNumber(label) : label;
|
return +label > 1000 ? formatLongNumber(label) : label;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTooltip(model) {
|
function renderTooltip(model) {
|
||||||
|
@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
import MetricsTable from './MetricsTable';
|
import MetricsTable from './MetricsTable';
|
||||||
import { browserFilter } from 'lib/filters';
|
import { browserFilter } from 'lib/filters';
|
||||||
|
|
||||||
export default function BrowsersTable({ websiteId, token, ...props }) {
|
export default function BrowsersTable({ websiteId, ...props }) {
|
||||||
return (
|
return (
|
||||||
<MetricsTable
|
<MetricsTable
|
||||||
{...props}
|
{...props}
|
||||||
@ -11,7 +11,6 @@ export default function BrowsersTable({ websiteId, token, ...props }) {
|
|||||||
type="browser"
|
type="browser"
|
||||||
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
|
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
|
||||||
websiteId={websiteId}
|
websiteId={websiteId}
|
||||||
token={token}
|
|
||||||
dataFilter={browserFilter}
|
dataFilter={browserFilter}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
import useCountryNames from 'hooks/useCountryNames';
|
import useCountryNames from 'hooks/useCountryNames';
|
||||||
import useLocale from 'hooks/useLocale';
|
import useLocale from 'hooks/useLocale';
|
||||||
|
|
||||||
export default function CountriesTable({ websiteId, token, onDataLoad, ...props }) {
|
export default function CountriesTable({ websiteId, onDataLoad, ...props }) {
|
||||||
const [locale] = useLocale();
|
const [locale] = useLocale();
|
||||||
const countryNames = useCountryNames(locale);
|
const countryNames = useCountryNames(locale);
|
||||||
|
|
||||||
@ -20,7 +20,6 @@ export default function CountriesTable({ websiteId, token, onDataLoad, ...props
|
|||||||
type="country"
|
type="country"
|
||||||
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
|
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
|
||||||
websiteId={websiteId}
|
websiteId={websiteId}
|
||||||
token={token}
|
|
||||||
onDataLoad={data => onDataLoad?.(percentFilter(data))}
|
onDataLoad={data => onDataLoad?.(percentFilter(data))}
|
||||||
renderLabel={renderLabel}
|
renderLabel={renderLabel}
|
||||||
/>
|
/>
|
||||||
|
@ -4,7 +4,7 @@ import { deviceFilter } from 'lib/filters';
|
|||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { getDeviceMessage } from 'components/messages';
|
import { getDeviceMessage } from 'components/messages';
|
||||||
|
|
||||||
export default function DevicesTable({ websiteId, token, ...props }) {
|
export default function DevicesTable({ websiteId, ...props }) {
|
||||||
return (
|
return (
|
||||||
<MetricsTable
|
<MetricsTable
|
||||||
{...props}
|
{...props}
|
||||||
@ -12,7 +12,6 @@ export default function DevicesTable({ websiteId, token, ...props }) {
|
|||||||
type="device"
|
type="device"
|
||||||
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
|
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
|
||||||
websiteId={websiteId}
|
websiteId={websiteId}
|
||||||
token={token}
|
|
||||||
dataFilter={deviceFilter}
|
dataFilter={deviceFilter}
|
||||||
renderLabel={({ x }) => getDeviceMessage(x)}
|
renderLabel={({ x }) => getDeviceMessage(x)}
|
||||||
/>
|
/>
|
||||||
|
@ -6,13 +6,15 @@ import useFetch from 'hooks/useFetch';
|
|||||||
import useDateRange from 'hooks/useDateRange';
|
import useDateRange from 'hooks/useDateRange';
|
||||||
import useTimezone from 'hooks/useTimezone';
|
import useTimezone from 'hooks/useTimezone';
|
||||||
import usePageQuery from 'hooks/usePageQuery';
|
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 }) {
|
export default function EventsChart({ websiteId, className, token }) {
|
||||||
const [dateRange] = useDateRange(websiteId);
|
const [dateRange] = useDateRange(websiteId);
|
||||||
const { startDate, endDate, unit, modified } = dateRange;
|
const { startDate, endDate, unit, modified } = dateRange;
|
||||||
const [timezone] = useTimezone();
|
const [timezone] = useTimezone();
|
||||||
const { query } = usePageQuery();
|
const { query } = usePageQuery();
|
||||||
|
const shareToken = useShareToken();
|
||||||
|
|
||||||
const { data } = useFetch(
|
const { data } = useFetch(
|
||||||
`/api/website/${websiteId}/events`,
|
`/api/website/${websiteId}/events`,
|
||||||
@ -25,6 +27,7 @@ export default function EventsChart({ websiteId, className, token }) {
|
|||||||
url: query.url,
|
url: query.url,
|
||||||
token,
|
token,
|
||||||
},
|
},
|
||||||
|
headers: { [TOKEN_HEADER]: shareToken?.token },
|
||||||
},
|
},
|
||||||
[modified],
|
[modified],
|
||||||
);
|
);
|
||||||
|
@ -3,7 +3,7 @@ import { FormattedMessage } from 'react-intl';
|
|||||||
import MetricsTable from './MetricsTable';
|
import MetricsTable from './MetricsTable';
|
||||||
import Tag from 'components/common/Tag';
|
import Tag from 'components/common/Tag';
|
||||||
|
|
||||||
export default function EventsTable({ websiteId, token, ...props }) {
|
export default function EventsTable({ websiteId, ...props }) {
|
||||||
return (
|
return (
|
||||||
<MetricsTable
|
<MetricsTable
|
||||||
{...props}
|
{...props}
|
||||||
@ -11,7 +11,6 @@ export default function EventsTable({ websiteId, token, ...props }) {
|
|||||||
type="event"
|
type="event"
|
||||||
metric={<FormattedMessage id="metrics.actions" defaultMessage="Actions" />}
|
metric={<FormattedMessage id="metrics.actions" defaultMessage="Actions" />}
|
||||||
websiteId={websiteId}
|
websiteId={websiteId}
|
||||||
token={token}
|
|
||||||
renderLabel={({ x }) => <Label value={x} />}
|
renderLabel={({ x }) => <Label value={x} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -5,12 +5,15 @@ import Loading from 'components/common/Loading';
|
|||||||
import ErrorMessage from 'components/common/ErrorMessage';
|
import ErrorMessage from 'components/common/ErrorMessage';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import useDateRange from 'hooks/useDateRange';
|
import useDateRange from 'hooks/useDateRange';
|
||||||
import { formatShortTime, formatNumber, formatLongNumber } from 'lib/format';
|
|
||||||
import usePageQuery from 'hooks/usePageQuery';
|
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 MetricCard from './MetricCard';
|
||||||
import styles from './MetricsBar.module.css';
|
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 [dateRange] = useDateRange(websiteId);
|
||||||
const { startDate, endDate, modified } = dateRange;
|
const { startDate, endDate, modified } = dateRange;
|
||||||
const [format, setFormat] = useState(true);
|
const [format, setFormat] = useState(true);
|
||||||
@ -25,8 +28,8 @@ export default function MetricsBar({ websiteId, token, className }) {
|
|||||||
start_at: +startDate,
|
start_at: +startDate,
|
||||||
end_at: +endDate,
|
end_at: +endDate,
|
||||||
url,
|
url,
|
||||||
token,
|
|
||||||
},
|
},
|
||||||
|
headers: { [TOKEN_HEADER]: shareToken?.token },
|
||||||
},
|
},
|
||||||
[modified],
|
[modified],
|
||||||
);
|
);
|
||||||
|
@ -8,15 +8,15 @@ import Arrow from 'assets/arrow-right.svg';
|
|||||||
import { percentFilter } from 'lib/filters';
|
import { percentFilter } from 'lib/filters';
|
||||||
import useDateRange from 'hooks/useDateRange';
|
import useDateRange from 'hooks/useDateRange';
|
||||||
import usePageQuery from 'hooks/usePageQuery';
|
import usePageQuery from 'hooks/usePageQuery';
|
||||||
|
import useShareToken from 'hooks/useShareToken';
|
||||||
import ErrorMessage from 'components/common/ErrorMessage';
|
import ErrorMessage from 'components/common/ErrorMessage';
|
||||||
import DataTable from './DataTable';
|
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';
|
import styles from './MetricsTable.module.css';
|
||||||
|
|
||||||
export default function MetricsTable({
|
export default function MetricsTable({
|
||||||
websiteId,
|
websiteId,
|
||||||
websiteDomain,
|
websiteDomain,
|
||||||
token,
|
|
||||||
title,
|
title,
|
||||||
metric,
|
metric,
|
||||||
type,
|
type,
|
||||||
@ -30,6 +30,7 @@ export default function MetricsTable({
|
|||||||
onDataLoad,
|
onDataLoad,
|
||||||
...props
|
...props
|
||||||
}) {
|
}) {
|
||||||
|
const shareToken = useShareToken();
|
||||||
const [dateRange] = useDateRange(websiteId);
|
const [dateRange] = useDateRange(websiteId);
|
||||||
const { startDate, endDate, modified } = dateRange;
|
const { startDate, endDate, modified } = dateRange;
|
||||||
const {
|
const {
|
||||||
@ -47,10 +48,10 @@ export default function MetricsTable({
|
|||||||
end_at: +endDate,
|
end_at: +endDate,
|
||||||
domain: websiteDomain,
|
domain: websiteDomain,
|
||||||
url,
|
url,
|
||||||
token,
|
|
||||||
},
|
},
|
||||||
onDataLoad,
|
onDataLoad,
|
||||||
delay: DEFAULT_ANIMATION_DURATION,
|
delay: DEFAULT_ANIMATION_DURATION,
|
||||||
|
headers: { [TOKEN_HEADER]: shareToken?.token },
|
||||||
},
|
},
|
||||||
[modified],
|
[modified],
|
||||||
);
|
);
|
||||||
|
@ -3,15 +3,14 @@ import MetricsTable from './MetricsTable';
|
|||||||
import { osFilter } from 'lib/filters';
|
import { osFilter } from 'lib/filters';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
export default function OSTable({ websiteId, token, limit }) {
|
export default function OSTable({ websiteId, ...props }) {
|
||||||
return (
|
return (
|
||||||
<MetricsTable
|
<MetricsTable
|
||||||
|
{...props}
|
||||||
title={<FormattedMessage id="metrics.operating-systems" defaultMessage="Operating system" />}
|
title={<FormattedMessage id="metrics.operating-systems" defaultMessage="Operating system" />}
|
||||||
type="os"
|
type="os"
|
||||||
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
|
metric={<FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />}
|
||||||
websiteId={websiteId}
|
websiteId={websiteId}
|
||||||
token={token}
|
|
||||||
limit={limit}
|
|
||||||
dataFilter={osFilter}
|
dataFilter={osFilter}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,7 @@ import usePageQuery from 'hooks/usePageQuery';
|
|||||||
import MetricsTable from './MetricsTable';
|
import MetricsTable from './MetricsTable';
|
||||||
import styles from './PagesTable.module.css';
|
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 [filter, setFilter] = useState(FILTER_COMBINED);
|
||||||
const {
|
const {
|
||||||
resolve,
|
resolve,
|
||||||
@ -48,7 +48,6 @@ export default function PagesTable({ websiteId, token, websiteDomain, showFilter
|
|||||||
type="url"
|
type="url"
|
||||||
metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />}
|
metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />}
|
||||||
websiteId={websiteId}
|
websiteId={websiteId}
|
||||||
token={token}
|
|
||||||
dataFilter={urlFilter}
|
dataFilter={urlFilter}
|
||||||
filterOptions={{ domain: websiteDomain, raw: filter === FILTER_RAW }}
|
filterOptions={{ domain: websiteDomain, raw: filter === FILTER_RAW }}
|
||||||
renderLabel={renderLink}
|
renderLabel={renderLink}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import MetricsTable from './MetricsTable';
|
import MetricsTable from './MetricsTable';
|
||||||
import { refFilter } from 'lib/filters';
|
|
||||||
import ButtonGroup from 'components/common/ButtonGroup';
|
import ButtonGroup from 'components/common/ButtonGroup';
|
||||||
|
import ButtonLayout from 'components/layout/ButtonLayout';
|
||||||
import { FILTER_DOMAIN_ONLY, FILTER_COMBINED, FILTER_RAW } from 'lib/constants';
|
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 [filter, setFilter] = useState(FILTER_COMBINED);
|
||||||
|
|
||||||
const buttons = [
|
const buttons = [
|
||||||
@ -41,7 +41,6 @@ export default function ReferrersTable({ websiteId, websiteDomain, token, showFi
|
|||||||
metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />}
|
metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />}
|
||||||
websiteId={websiteId}
|
websiteId={websiteId}
|
||||||
websiteDomain={websiteDomain}
|
websiteDomain={websiteDomain}
|
||||||
token={token}
|
|
||||||
dataFilter={refFilter}
|
dataFilter={refFilter}
|
||||||
filterOptions={{
|
filterOptions={{
|
||||||
domain: websiteDomain,
|
domain: websiteDomain,
|
||||||
|
@ -14,15 +14,17 @@ import { getDateArray, getDateLength } from 'lib/date';
|
|||||||
import Times from 'assets/times.svg';
|
import Times from 'assets/times.svg';
|
||||||
import styles from './WebsiteChart.module.css';
|
import styles from './WebsiteChart.module.css';
|
||||||
import ErrorMessage from '../common/ErrorMessage';
|
import ErrorMessage from '../common/ErrorMessage';
|
||||||
|
import useShareToken from '../../hooks/useShareToken';
|
||||||
|
import { TOKEN_HEADER } from '../../lib/constants';
|
||||||
|
|
||||||
export default function WebsiteChart({
|
export default function WebsiteChart({
|
||||||
websiteId,
|
websiteId,
|
||||||
token,
|
|
||||||
title,
|
title,
|
||||||
stickyHeader = false,
|
stickyHeader = false,
|
||||||
showLink = false,
|
showLink = false,
|
||||||
onDataLoad = () => {},
|
onDataLoad = () => {},
|
||||||
}) {
|
}) {
|
||||||
|
const shareToken = useShareToken();
|
||||||
const [dateRange, setDateRange] = useDateRange(websiteId);
|
const [dateRange, setDateRange] = useDateRange(websiteId);
|
||||||
const { startDate, endDate, unit, value, modified } = dateRange;
|
const { startDate, endDate, unit, value, modified } = dateRange;
|
||||||
const [timezone] = useTimezone();
|
const [timezone] = useTimezone();
|
||||||
@ -41,9 +43,9 @@ export default function WebsiteChart({
|
|||||||
unit,
|
unit,
|
||||||
tz: timezone,
|
tz: timezone,
|
||||||
url,
|
url,
|
||||||
token,
|
|
||||||
},
|
},
|
||||||
onDataLoad,
|
onDataLoad,
|
||||||
|
headers: { [TOKEN_HEADER]: shareToken?.token },
|
||||||
},
|
},
|
||||||
[modified],
|
[modified],
|
||||||
);
|
);
|
||||||
@ -64,7 +66,7 @@ export default function WebsiteChart({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<WebsiteHeader websiteId={websiteId} token={token} title={title} showLink={showLink} />
|
<WebsiteHeader websiteId={websiteId} title={title} showLink={showLink} />
|
||||||
<div className={classNames(styles.header, 'row')}>
|
<div className={classNames(styles.header, 'row')}>
|
||||||
<StickyHeader
|
<StickyHeader
|
||||||
className={classNames(styles.metrics, 'col row')}
|
className={classNames(styles.metrics, 'col row')}
|
||||||
@ -73,7 +75,7 @@ export default function WebsiteChart({
|
|||||||
>
|
>
|
||||||
{url && <PageFilter url={url} onClick={handleCloseFilter} />}
|
{url && <PageFilter url={url} onClick={handleCloseFilter} />}
|
||||||
<div className="col-12 col-lg-9">
|
<div className="col-12 col-lg-9">
|
||||||
<MetricsBar websiteId={websiteId} token={token} />
|
<MetricsBar websiteId={websiteId} />
|
||||||
</div>
|
</div>
|
||||||
<div className={classNames(styles.filter, 'col-12 col-lg-3')}>
|
<div className={classNames(styles.filter, 'col-12 col-lg-3')}>
|
||||||
<DateFilter
|
<DateFilter
|
||||||
|
@ -8,11 +8,11 @@ import ActiveUsers from './ActiveUsers';
|
|||||||
import Arrow from 'assets/arrow-right.svg';
|
import Arrow from 'assets/arrow-right.svg';
|
||||||
import styles from './WebsiteHeader.module.css';
|
import styles from './WebsiteHeader.module.css';
|
||||||
|
|
||||||
export default function WebsiteHeader({ websiteId, token, title, showLink = false }) {
|
export default function WebsiteHeader({ websiteId, title, showLink = false }) {
|
||||||
return (
|
return (
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<div className={styles.title}>{title}</div>
|
<div className={styles.title}>{title}</div>
|
||||||
<ActiveUsers className={styles.active} websiteId={websiteId} token={token} />
|
<ActiveUsers className={styles.active} websiteId={websiteId} />
|
||||||
<ButtonLayout align="right">
|
<ButtonLayout align="right">
|
||||||
<RefreshButton websiteId={websiteId} />
|
<RefreshButton websiteId={websiteId} />
|
||||||
{showLink && (
|
{showLink && (
|
||||||
|
@ -13,6 +13,7 @@ import useFetch from 'hooks/useFetch';
|
|||||||
import useLocale from 'hooks/useLocale';
|
import useLocale from 'hooks/useLocale';
|
||||||
import useCountryNames from 'hooks/useCountryNames';
|
import useCountryNames from 'hooks/useCountryNames';
|
||||||
import { percentFilter } from 'lib/filters';
|
import { percentFilter } from 'lib/filters';
|
||||||
|
import { TOKEN_HEADER } from 'lib/constants';
|
||||||
import styles from './RealtimeDashboard.module.css';
|
import styles from './RealtimeDashboard.module.css';
|
||||||
|
|
||||||
const REALTIME_RANGE = 30;
|
const REALTIME_RANGE = 30;
|
||||||
@ -39,7 +40,7 @@ export default function RealtimeDashboard() {
|
|||||||
params: { start_at: data?.timestamp },
|
params: { start_at: data?.timestamp },
|
||||||
disabled: !init?.websites?.length || !data,
|
disabled: !init?.websites?.length || !data,
|
||||||
interval: REALTIME_INTERVAL,
|
interval: REALTIME_INTERVAL,
|
||||||
headers: { 'x-umami-token': init?.token },
|
headers: { [TOKEN_HEADER]: init?.token },
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderCountryName = useCallback(({ x }) => countryNames[x], []);
|
const renderCountryName = useCallback(({ x }) => countryNames[x], []);
|
||||||
|
@ -20,7 +20,8 @@ import EventsTable from '../metrics/EventsTable';
|
|||||||
import EventsChart from '../metrics/EventsChart';
|
import EventsChart from '../metrics/EventsChart';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import usePageQuery from 'hooks/usePageQuery';
|
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 = {
|
const views = {
|
||||||
url: PagesTable,
|
url: PagesTable,
|
||||||
@ -32,8 +33,11 @@ const views = {
|
|||||||
event: EventsTable,
|
event: EventsTable,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function WebsiteDetails({ websiteId, token }) {
|
export default function WebsiteDetails({ websiteId }) {
|
||||||
const { data } = useFetch(`/api/website/${websiteId}`, { params: { token } });
|
const shareToken = useShareToken();
|
||||||
|
const { data } = useFetch(`/api/website/${websiteId}`, {
|
||||||
|
headers: { [TOKEN_HEADER]: shareToken?.token },
|
||||||
|
});
|
||||||
const [chartLoaded, setChartLoaded] = useState(false);
|
const [chartLoaded, setChartLoaded] = useState(false);
|
||||||
const [countryData, setCountryData] = useState();
|
const [countryData, setCountryData] = useState();
|
||||||
const [eventsData, setEventsData] = useState();
|
const [eventsData, setEventsData] = useState();
|
||||||
@ -93,7 +97,6 @@ export default function WebsiteDetails({ websiteId, token }) {
|
|||||||
|
|
||||||
const tableProps = {
|
const tableProps = {
|
||||||
websiteId,
|
websiteId,
|
||||||
token,
|
|
||||||
websiteDomain: data?.domain,
|
websiteDomain: data?.domain,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
};
|
};
|
||||||
@ -116,7 +119,6 @@ export default function WebsiteDetails({ websiteId, token }) {
|
|||||||
<div className={classNames(styles.chart, 'col')}>
|
<div className={classNames(styles.chart, 'col')}>
|
||||||
<WebsiteChart
|
<WebsiteChart
|
||||||
websiteId={websiteId}
|
websiteId={websiteId}
|
||||||
token={token}
|
|
||||||
title={data.name}
|
title={data.name}
|
||||||
onDataLoad={handleDataLoad}
|
onDataLoad={handleDataLoad}
|
||||||
showLink={false}
|
showLink={false}
|
||||||
@ -159,7 +161,7 @@ export default function WebsiteDetails({ websiteId, token }) {
|
|||||||
<EventsTable {...tableProps} onDataLoad={setEventsData} />
|
<EventsTable {...tableProps} onDataLoad={setEventsData} />
|
||||||
</GridColumn>
|
</GridColumn>
|
||||||
<GridColumn xs={12} md={12} lg={8}>
|
<GridColumn xs={12} md={12} lg={8}>
|
||||||
<EventsChart className={styles.eventschart} websiteId={websiteId} token={token} />
|
<EventsChart className={styles.eventschart} websiteId={websiteId} />
|
||||||
</GridColumn>
|
</GridColumn>
|
||||||
</GridRow>
|
</GridRow>
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
|
25
hooks/useShareToken.js
Normal file
25
hooks/useShareToken.js
Normal file
@ -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;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { parse } from 'cookie';
|
import { parse } from 'cookie';
|
||||||
import { parseSecureToken, parseToken } from './crypto';
|
import { parseSecureToken, parseToken } from './crypto';
|
||||||
import { AUTH_COOKIE_NAME } from './constants';
|
import { AUTH_COOKIE_NAME, TOKEN_HEADER } from './constants';
|
||||||
import { getWebsiteById } from './queries';
|
import { getWebsiteById } from './queries';
|
||||||
|
|
||||||
export async function getAuthToken(req) {
|
export async function getAuthToken(req) {
|
||||||
@ -26,7 +26,8 @@ export async function isValidToken(token, validation) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function allowQuery(req, skipToken) {
|
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 websiteId = +id;
|
||||||
|
|
||||||
const website = await getWebsiteById(websiteId);
|
const website = await getWebsiteById(websiteId);
|
||||||
|
@ -4,6 +4,7 @@ export const TIMEZONE_CONFIG = 'umami.timezone';
|
|||||||
export const DATE_RANGE_CONFIG = 'umami.date-range';
|
export const DATE_RANGE_CONFIG = 'umami.date-range';
|
||||||
export const THEME_CONFIG = 'umami.theme';
|
export const THEME_CONFIG = 'umami.theme';
|
||||||
export const VERSION_CHECK = 'umami.version-check';
|
export const VERSION_CHECK = 'umami.version-check';
|
||||||
|
export const TOKEN_HEADER = 'x-umami-token';
|
||||||
|
|
||||||
export const THEME_COLORS = {
|
export const THEME_COLORS = {
|
||||||
light: {
|
light: {
|
||||||
|
@ -2,6 +2,7 @@ import { useAuth } from 'lib/middleware';
|
|||||||
import { ok, methodNotAllowed, badRequest } from 'lib/response';
|
import { ok, methodNotAllowed, badRequest } from 'lib/response';
|
||||||
import { getRealtimeData } from 'lib/queries';
|
import { getRealtimeData } from 'lib/queries';
|
||||||
import { parseToken } from 'lib/crypto';
|
import { parseToken } from 'lib/crypto';
|
||||||
|
import { TOKEN_HEADER } from 'lib/constants';
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
@ -9,7 +10,7 @@ export default async (req, res) => {
|
|||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
const { start_at } = req.query;
|
const { start_at } = req.query;
|
||||||
|
|
||||||
const token = req.headers['x-umami-token'];
|
const token = req.headers[TOKEN_HEADER];
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return badRequest(res);
|
return badRequest(res);
|
||||||
|
@ -2,23 +2,23 @@ import React from 'react';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Layout from 'components/layout/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
import WebsiteDetails from 'components/pages/WebsiteDetails';
|
import WebsiteDetails from 'components/pages/WebsiteDetails';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useShareToken from 'hooks/useShareToken';
|
||||||
|
|
||||||
export default function SharePage() {
|
export default function SharePage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { id } = router.query;
|
const { id } = router.query;
|
||||||
const shareId = id?.[0];
|
const shareId = id?.[0];
|
||||||
const { data } = useFetch(`/api/share/${shareId}`, { disabled: !shareId });
|
const shareToken = useShareToken(shareId);
|
||||||
|
|
||||||
if (!data) {
|
if (!shareToken) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { websiteId, token } = data;
|
const { websiteId } = shareToken;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<WebsiteDetails websiteId={websiteId} token={token} />
|
<WebsiteDetails websiteId={websiteId} />
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ const app = createSlice({
|
|||||||
latest: null,
|
latest: null,
|
||||||
hasUpdate: false,
|
hasUpdate: false,
|
||||||
},
|
},
|
||||||
|
shareToken: null,
|
||||||
},
|
},
|
||||||
reducers: {
|
reducers: {
|
||||||
setLocale(state, action) {
|
setLocale(state, action) {
|
||||||
@ -33,10 +34,14 @@ const app = createSlice({
|
|||||||
state.versions = action.payload;
|
state.versions = action.payload;
|
||||||
return state;
|
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;
|
export default app.reducer;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user