diff --git a/.eslintrc.json b/.eslintrc.json index a77ed5bd87..9d747b8796 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,6 +4,13 @@ "es2020": true, "node": true }, + "extends": [ + "eslint:recommended", + "plugin:prettier/recommended", + "plugin:import/recommended", + "plugin:@typescript-eslint/recommended", + "next" + ], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaFeatures": { @@ -12,13 +19,6 @@ "ecmaVersion": 11, "sourceType": "module" }, - "extends": [ - "eslint:recommended", - "plugin:prettier/recommended", - "plugin:import/recommended", - "plugin:@typescript-eslint/recommended", - "next" - ], "plugins": ["@typescript-eslint", "prettier"], "settings": { "import/resolver": { diff --git a/package.json b/package.json index fe3fa664c8..edc6b1e0d9 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,8 @@ "@prisma/client": "5.4.2", "@react-spring/web": "^9.7.3", "@tanstack/react-query": "^4.33.0", - "@umami/prisma-client": "^0.3.0", - "@umami/redis-client": "^0.16.0", + "@umami/prisma-client": "^0.5.0", + "@umami/redis-client": "^0.17.0", "chalk": "^4.1.1", "chart.js": "^4.2.1", "chartjs-adapter-date-fns": "^3.0.0", diff --git a/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js b/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js index 9c2ae7bd31..c83ec3d08e 100644 --- a/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js +++ b/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js @@ -2,11 +2,13 @@ import useApi from 'components/hooks/useApi'; import { useState } from 'react'; import { Button, Form, FormButtons, GridColumn, Loading, SubmitButton, Toggle } from 'react-basics'; import useMessages from 'components/hooks/useMessages'; -import WebsitesDataTable from '../../websites/WebsitesDataTable'; +import WebsitesDataTable from 'app/(main)/settings/websites/WebsitesDataTable'; import Empty from 'components/common/Empty'; import { setValue } from 'store/cache'; +import { useUser } from 'components/hooks'; export function TeamWebsiteAddForm({ teamId, onSave, onClose }) { + const { user } = useUser(); const { formatMessage, labels } = useMessages(); const { get, post, useQuery, useMutation } = useApi(); const { mutate, error } = useMutation(data => post(`/teams/${teamId}/websites`, data)); @@ -37,7 +39,7 @@ export function TeamWebsiteAddForm({ teamId, onSave, onClose }) { {!isLoading && !hasData && } {hasData && (
- + {row => ( + + + + ); +} diff --git a/src/app/(main)/settings/websites/WebsitesDataTable.tsx b/src/app/(main)/settings/websites/WebsitesDataTable.tsx index 441ae56da9..fc6dd0c0e2 100644 --- a/src/app/(main)/settings/websites/WebsitesDataTable.tsx +++ b/src/app/(main)/settings/websites/WebsitesDataTable.tsx @@ -1,13 +1,13 @@ 'use client'; import { ReactNode } from 'react'; import WebsitesTable from 'app/(main)/settings/websites/WebsitesTable'; -import useUser from 'components/hooks/useUser'; import useApi from 'components/hooks/useApi'; import DataTable from 'components/common/DataTable'; import useFilterQuery from 'components/hooks/useFilterQuery'; import useCache from 'store/cache'; export interface WebsitesDataTableProps { + userId: string; allowEdit?: boolean; allowView?: boolean; showActions?: boolean; @@ -17,25 +17,25 @@ export interface WebsitesDataTableProps { children?: ReactNode; } -function useWebsites({ includeTeams, onlyTeams }) { - const { user } = useUser(); +function useWebsites(userId: string, { includeTeams, onlyTeams }) { const { get } = useApi(); const modified = useCache((state: any) => state?.websites); return useFilterQuery( ['websites', { includeTeams, onlyTeams, modified }], (params: any) => { - return get(`/users/${user?.id}/websites`, { + return get(`/users/${userId}/websites`, { includeTeams, onlyTeams, ...params, }); }, - { enabled: !!user }, + { enabled: !!userId }, ); } export function WebsitesDataTable({ + userId, allowEdit = true, allowView = true, showActions = true, @@ -44,7 +44,7 @@ export function WebsitesDataTable({ onlyTeams, children, }: WebsitesDataTableProps) { - const queryResult = useWebsites({ includeTeams, onlyTeams }); + const queryResult = useWebsites(userId, { includeTeams, onlyTeams }); return ( diff --git a/src/app/(main)/settings/websites/page.tsx b/src/app/(main)/settings/websites/page.tsx index 2c83dce0c6..d6d11898c5 100644 --- a/src/app/(main)/settings/websites/page.tsx +++ b/src/app/(main)/settings/websites/page.tsx @@ -1,14 +1,8 @@ -import WebsitesDataTable from './WebsitesDataTable'; -import WebsitesHeader from './WebsitesHeader'; import { Metadata } from 'next'; +import Websites from './Websites'; export default function () { - return ( - <> - - - - ); + return ; } export const metadata: Metadata = { diff --git a/src/app/(main)/websites/WebsitesBrowse.js b/src/app/(main)/websites/WebsitesBrowse.js index f1bab7bf2a..3e8df2b261 100644 --- a/src/app/(main)/websites/WebsitesBrowse.js +++ b/src/app/(main)/websites/WebsitesBrowse.js @@ -1,6 +1,6 @@ 'use client'; import WebsitesDataTable from '../settings/websites/WebsitesDataTable'; -import { useMessages } from 'components/hooks'; +import { useMessages, useUser } from 'components/hooks'; import { useState } from 'react'; import { Item, Tabs } from 'react-basics'; @@ -10,6 +10,7 @@ const TABS = { }; export function WebsitesBrowse() { + const { user } = useUser(); const { formatMessage, labels } = useMessages(); const [tab, setTab] = useState(TABS.myWebsites); const allowEdit = !process.env.cloudMode; @@ -20,9 +21,14 @@ export function WebsitesBrowse() { {formatMessage(labels.myWebsites)} {formatMessage(labels.teamWebsites)} - {tab === TABS.myWebsites && } + {tab === TABS.myWebsites && } {tab === TABS.teamWebsites && ( - + )} ); diff --git a/src/components/hooks/useRequireLogin.ts b/src/components/hooks/useRequireLogin.ts index 76460a5574..68de411bfe 100644 --- a/src/components/hooks/useRequireLogin.ts +++ b/src/components/hooks/useRequireLogin.ts @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import useApi from 'components/hooks/useApi'; import useUser from 'components/hooks/useUser'; -export function useRequireLogin(handler?: (data?: object) => void) { +export function useRequireLogin() { const { get } = useApi(); const { user, setUser } = useUser(); @@ -11,9 +11,9 @@ export function useRequireLogin(handler?: (data?: object) => void) { try { const data = await get('/auth/verify'); - setUser(typeof handler === 'function' ? handler(data) : (data as any)?.user); + setUser(data); } catch { - location.href = `${process.env.basePath || ''}/login`; + window.location.href = `${process.env.basePath || ''}/login`; } } diff --git a/src/declaration.d.ts b/src/declaration.d.ts new file mode 100644 index 0000000000..42a5eeeda7 --- /dev/null +++ b/src/declaration.d.ts @@ -0,0 +1 @@ +declare module 'debug'; diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 4a42d85de7..c218cef9aa 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -7,14 +7,15 @@ import { createSecureToken, ensureArray, getRandomChars, parseToken } from 'next import { findTeamWebsiteByUserId, getTeamUser, getTeamWebsite } from 'queries'; import { loadWebsite } from './load'; import { Auth } from './types'; +import { NextApiRequest } from 'next'; const log = debug('umami:auth'); const cloudMode = process.env.CLOUD_MODE; -export async function setAuthKey(user, expire = 0) { +export async function setAuthKey(data: any, expire = 0) { const authKey = `auth:${getRandomChars(32)}`; - await redis.set(authKey, user); + await redis.set(authKey, data); if (expire) { await redis.expire(authKey, expire); @@ -23,7 +24,7 @@ export async function setAuthKey(user, expire = 0) { return createSecureToken({ authKey }, secret()); } -export function getAuthToken(req) { +export function getAuthToken(req: NextApiRequest) { try { return req.headers.authorization.split(' ')[1]; } catch { @@ -31,7 +32,7 @@ export function getAuthToken(req) { } } -export function parseShareToken(req) { +export function parseShareToken(req: Request) { try { return parseToken(req.headers[SHARE_TOKEN_HEADER], secret()); } catch (e) { diff --git a/src/lib/client.ts b/src/lib/client.ts index 8c69d23d86..7810c44ad0 100644 --- a/src/lib/client.ts +++ b/src/lib/client.ts @@ -5,7 +5,7 @@ export function getClientAuthToken() { return getItem(AUTH_TOKEN); } -export function setClientAuthToken(token) { +export function setClientAuthToken(token: string) { setItem(AUTH_TOKEN, token); } diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 4c468c1c1d..0c894634b1 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -13,7 +13,7 @@ export const REPO_URL = 'https://github.com/umami-software/umami'; export const UPDATES_URL = 'https://api.umami.is/v1/updates'; export const TELEMETRY_PIXEL = 'https://i.umami.is/a.png'; -export const DEFAULT_LOCALE = process.env.defaultLocale ?? 'en-US'; +export const DEFAULT_LOCALE = process.env.defaultLocale || 'en-US'; export const DEFAULT_THEME = 'light'; export const DEFAULT_ANIMATION_DURATION = 300; export const DEFAULT_DATE_RANGE = '24hour'; diff --git a/src/pages/api/auth/verify.ts b/src/pages/api/auth/verify.ts index 9eb9ea4885..a302c69b13 100644 --- a/src/pages/api/auth/verify.ts +++ b/src/pages/api/auth/verify.ts @@ -6,5 +6,5 @@ import { ok } from 'next-basics'; export default async (req: NextApiRequestAuth, res: NextApiResponse) => { await useAuth(req, res); - return ok(res, req.auth); + return ok(res, req.auth.user); }; diff --git a/yarn.lock b/yarn.lock index d7596fa4e4..aa13ffdacb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2596,18 +2596,18 @@ "@typescript-eslint/types" "6.8.0" eslint-visitor-keys "^3.4.1" -"@umami/prisma-client@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@umami/prisma-client/-/prisma-client-0.3.0.tgz#fea35a44c76af0e4ce58288107cda3ee76fc80ba" - integrity sha512-88y/WJX2TEZaUfP+PTretGUL6YdwZCBbhaoeC87eTF3l1aG0Lv3TsmW0lJy5rbKpVqrFJ8zrtvCMP/vt7WeIjg== +"@umami/prisma-client@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@umami/prisma-client/-/prisma-client-0.5.0.tgz#e2287debbf21f9344c989b9e7192491df88513bf" + integrity sha512-BkStMrvxYZQPwEIyy30JJPucTTsmQqb4jD8+ciSHxcBc7039cW0XyX3TL/u9ebZmANzIuNO0XiBArwjWulGIjg== dependencies: chalk "^4.1.2" debug "^4.3.4" -"@umami/redis-client@^0.16.0": - version "0.16.0" - resolved "https://registry.yarnpkg.com/@umami/redis-client/-/redis-client-0.16.0.tgz#0050d1f93338d88691c983f3c0cd4a62da20212b" - integrity sha512-fE08lkMvhXbkXSdSRpG0R/9a3xIiTvwD6f+hKERFZrpfvJJlH3Uf4Jod8Ahg/+TmD03ihSQPooUT3T9Ig3dfaQ== +"@umami/redis-client@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@umami/redis-client/-/redis-client-0.17.0.tgz#a24db0cb561a9c18b0ecaf6a3342c28525abe1e5" + integrity sha512-rX0xB+QkhMoHnKcj8jzbp1CUMKB/qk2HaYWpNP0Stztkvw7wjFYXsLTLidW3t1a06H5qApx6mJvUjxefLUsX7w== dependencies: debug "^4.3.4" redis "^4.5.1"