Updated share token logic. Closes #1113.

This commit is contained in:
Mike Cao 2022-05-05 19:04:28 -07:00
parent b5de6b997e
commit e735a1c50d
12 changed files with 44 additions and 42 deletions

View File

@ -3,20 +3,16 @@ 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, className, value, interval = 60000 }) { export default function ActiveUsers({ websiteId, className, value, interval = 60000 }) {
const shareToken = useShareToken();
const url = websiteId ? `/website/${websiteId}/active` : null; const url = websiteId ? `/website/${websiteId}/active` : null;
const { data } = useFetch(url, { const { data } = useFetch(url, {
interval, interval,
headers: { [TOKEN_HEADER]: shareToken?.token },
}); });
const count = useMemo(() => { const count = useMemo(() => {
if (websiteId) { if (websiteId) {
return data?.[0]?.x || 0 return data?.[0]?.x || 0;
} }
return value !== undefined ? value : 0; return value !== undefined ? value : 0;

View File

@ -6,8 +6,7 @@ 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 useShareToken from 'hooks/useShareToken'; import { EVENT_COLORS } from 'lib/constants';
import { EVENT_COLORS, TOKEN_HEADER } from 'lib/constants';
export default function EventsChart({ websiteId, className, token }) { export default function EventsChart({ websiteId, className, token }) {
const [{ startDate, endDate, unit, modified }] = useDateRange(websiteId); const [{ startDate, endDate, unit, modified }] = useDateRange(websiteId);
@ -15,7 +14,6 @@ export default function EventsChart({ websiteId, className, token }) {
const { const {
query: { url, eventType }, query: { url, eventType },
} = usePageQuery(); } = usePageQuery();
const shareToken = useShareToken();
const { data, loading } = useFetch( const { data, loading } = useFetch(
`/website/${websiteId}/events`, `/website/${websiteId}/events`,
@ -29,7 +27,6 @@ export default function EventsChart({ websiteId, className, token }) {
event_type: eventType, event_type: eventType,
token, token,
}, },
headers: { [TOKEN_HEADER]: shareToken?.token },
}, },
[modified, eventType], [modified, eventType],
); );

View File

@ -6,14 +6,11 @@ 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 usePageQuery from 'hooks/usePageQuery'; import usePageQuery from 'hooks/usePageQuery';
import useShareToken from 'hooks/useShareToken';
import { formatShortTime, formatNumber, formatLongNumber } from 'lib/format'; 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, 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);
@ -34,7 +31,6 @@ export default function MetricsBar({ websiteId, className }) {
device, device,
country, country,
}, },
headers: { [TOKEN_HEADER]: shareToken?.token },
}, },
[modified, url, referrer, os, browser, device, country], [modified, url, referrer, os, browser, device, country],
); );

View File

@ -9,10 +9,9 @@ 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, TOKEN_HEADER } from 'lib/constants'; import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
import styles from './MetricsTable.module.css'; import styles from './MetricsTable.module.css';
export default function MetricsTable({ export default function MetricsTable({
@ -25,7 +24,6 @@ export default function MetricsTable({
onDataLoad, onDataLoad,
...props ...props
}) { }) {
const shareToken = useShareToken();
const [{ startDate, endDate, modified }] = useDateRange(websiteId); const [{ startDate, endDate, modified }] = useDateRange(websiteId);
const { const {
resolve, resolve,
@ -49,7 +47,6 @@ export default function MetricsTable({
}, },
onDataLoad, onDataLoad,
delay: DEFAULT_ANIMATION_DURATION, delay: DEFAULT_ANIMATION_DURATION,
headers: { [TOKEN_HEADER]: shareToken?.token },
}, },
[modified, url, referrer, os, browser, device, country], [modified, url, referrer, os, browser, device, country],
); );

View File

@ -12,9 +12,7 @@ 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 { getDateArray, getDateLength, getDateRangeValues } from 'lib/date'; import { getDateArray, getDateLength, getDateRangeValues } from 'lib/date';
import useShareToken from 'hooks/useShareToken';
import useApi from 'hooks/useApi'; import useApi from 'hooks/useApi';
import { TOKEN_HEADER } from 'lib/constants';
import styles from './WebsiteChart.module.css'; import styles from './WebsiteChart.module.css';
export default function WebsiteChart({ export default function WebsiteChart({
@ -26,7 +24,6 @@ export default function WebsiteChart({
showChart = true, showChart = true,
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();
@ -53,7 +50,6 @@ export default function WebsiteChart({
country, country,
}, },
onDataLoad, onDataLoad,
headers: { [TOKEN_HEADER]: shareToken?.token },
}, },
[modified, url, referrer, os, browser, device, country], [modified, url, referrer, os, browser, device, country],
); );

View File

@ -14,7 +14,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, REALTIME_RANGE, REALTIME_INTERVAL } from 'lib/constants'; import { SHARE_TOKEN_HEADER, REALTIME_RANGE, REALTIME_INTERVAL } from 'lib/constants';
import styles from './RealtimeDashboard.module.css'; import styles from './RealtimeDashboard.module.css';
function mergeData(state, data, time) { function mergeData(state, data, time) {
@ -38,7 +38,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: { [TOKEN_HEADER]: init?.token }, headers: { [SHARE_TOKEN_HEADER]: init?.token },
}); });
const renderCountryName = useCallback( const renderCountryName = useCallback(

View File

@ -20,8 +20,7 @@ import EventsTable from 'components/metrics/EventsTable';
import EventsChart from 'components/metrics/EventsChart'; import EventsChart from 'components/metrics/EventsChart';
import useFetch from 'hooks/useFetch'; import useFetch from 'hooks/useFetch';
import usePageQuery from 'hooks/usePageQuery'; import usePageQuery from 'hooks/usePageQuery';
import useShareToken from 'hooks/useShareToken'; import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
import { DEFAULT_ANIMATION_DURATION, TOKEN_HEADER } from 'lib/constants';
import styles from './WebsiteDetails.module.css'; import styles from './WebsiteDetails.module.css';
const views = { const views = {
@ -36,10 +35,7 @@ const views = {
}; };
export default function WebsiteDetails({ websiteId }) { export default function WebsiteDetails({ websiteId }) {
const shareToken = useShareToken(); const { data } = useFetch(`/website/${websiteId}`);
const { data } = useFetch(`/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();

View File

@ -1,13 +1,18 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { get, post, put, del, getItem } from 'lib/web'; import { get, post, put, del, getItem } from 'lib/web';
import { AUTH_TOKEN } from 'lib/constants'; import { AUTH_TOKEN, SHARE_TOKEN_HEADER } from 'lib/constants';
import useStore from 'store/app';
function includeAuthToken(headers = {}) { const selector = state => state.shareToken;
const authToken = getItem(AUTH_TOKEN);
function parseHeaders(headers = {}, { authToken, shareToken }) {
if (authToken) { if (authToken) {
headers.Authorization = `Bearer ${authToken}`; headers.authorization = `Bearer ${authToken}`;
}
if (shareToken) {
headers[SHARE_TOKEN_HEADER] = shareToken.token;
} }
return headers; return headers;
@ -15,32 +20,50 @@ function includeAuthToken(headers = {}) {
export default function useApi() { export default function useApi() {
const { basePath } = useRouter(); const { basePath } = useRouter();
const authToken = getItem(AUTH_TOKEN);
const shareToken = useStore(selector);
return { return {
get: useCallback( get: useCallback(
async (url, params, headers) => { async (url, params, headers) => {
return get(`${basePath}/api${url}`, params, includeAuthToken(headers)); return get(
`${basePath}/api${url}`,
params,
parseHeaders(headers, { authToken, shareToken }),
);
}, },
[get], [get],
), ),
post: useCallback( post: useCallback(
async (url, params, headers) => { async (url, params, headers) => {
return post(`${basePath}/api${url}`, params, includeAuthToken(headers)); return post(
`${basePath}/api${url}`,
params,
parseHeaders(headers, { authToken, shareToken }),
);
}, },
[post], [post],
), ),
put: useCallback( put: useCallback(
async (url, params, headers) => { async (url, params, headers) => {
return put(`${basePath}/api${url}`, params, includeAuthToken(headers)); return put(
`${basePath}/api${url}`,
params,
parseHeaders(headers, { authToken, shareToken }),
);
}, },
[put], [put],
), ),
del: useCallback( del: useCallback(
async (url, params, headers) => { async (url, params, headers) => {
return del(`${basePath}/api${url}`, params, includeAuthToken(headers)); return del(
`${basePath}/api${url}`,
params,
parseHeaders(headers, { authToken, shareToken }),
);
}, },
[del], [del],
), ),

View File

@ -6,6 +6,7 @@ const selector = state => state.shareToken;
export default function useShareToken(shareId) { export default function useShareToken(shareId) {
const shareToken = useStore(selector); const shareToken = useStore(selector);
console.log({ shareToken });
const { get } = useApi(); const { get } = useApi();
async function loadToken(id) { async function loadToken(id) {

View File

@ -1,5 +1,5 @@
import { parseSecureToken, parseToken } from './crypto'; import { parseSecureToken, parseToken } from './crypto';
import { TOKEN_HEADER } from './constants'; import { SHARE_TOKEN_HEADER } from './constants';
import { getWebsiteById } from './queries'; import { getWebsiteById } from './queries';
export async function getAuthToken(req) { export async function getAuthToken(req) {
@ -30,7 +30,7 @@ export async function isValidToken(token, validation) {
export async function allowQuery(req, skipToken) { export async function allowQuery(req, skipToken) {
const { id } = req.query; const { id } = req.query;
const token = req.headers[TOKEN_HEADER]; const token = req.headers[SHARE_TOKEN_HEADER];
const websiteId = +id; const websiteId = +id;
const website = await getWebsiteById(websiteId); const website = await getWebsiteById(websiteId);

View File

@ -5,7 +5,7 @@ export const DATE_RANGE_CONFIG = 'umami.date-range';
export const THEME_CONFIG = 'umami.theme'; export const THEME_CONFIG = 'umami.theme';
export const DASHBOARD_CONFIG = 'umami.dashboard'; export const DASHBOARD_CONFIG = 'umami.dashboard';
export const VERSION_CHECK = 'umami.version-check'; export const VERSION_CHECK = 'umami.version-check';
export const TOKEN_HEADER = 'x-umami-token'; export const SHARE_TOKEN_HEADER = 'x-umami-share-token';
export const HOMEPAGE_URL = 'https://umami.is'; export const HOMEPAGE_URL = 'https://umami.is';
export const VERSION_URL = 'https://github.com/mikecao/umami/releases'; export const VERSION_URL = 'https://github.com/mikecao/umami/releases';

View File

@ -2,7 +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'; import { SHARE_TOKEN_HEADER } from 'lib/constants';
export default async (req, res) => { export default async (req, res) => {
await useAuth(req, res); await useAuth(req, res);
@ -10,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[TOKEN_HEADER]; const token = req.headers[SHARE_TOKEN_HEADER];
if (!token) { if (!token) {
return badRequest(res); return badRequest(res);