From 787c7e7ed97392dcce07dd914b435f246f11b0cb Mon Sep 17 00:00:00 2001 From: Innei Date: Thu, 11 Apr 2024 23:53:08 +0800 Subject: [PATCH] fix: if access hidden note when loggin Signed-off-by: Innei --- next.config.mjs | 11 ++-- .../(topic-detail)/topics/[slug]/layout.tsx | 4 +- src/app/(app)/(page-detail)/[slug]/layout.tsx | 4 +- src/app/(app)/api.tsx | 4 +- src/app/(app)/categories/[slug]/api.tsx | 4 +- src/app/(app)/notes/[id]/api.tsx | 7 ++- src/app/(app)/notes/[id]/page.tsx | 11 +++- src/app/(app)/notes/page.tsx | 51 ++++++++----------- .../(post-detail)/[category]/[slug]/api.tsx | 4 +- src/app/(app)/timeline/layout.tsx | 4 +- src/components/modules/note/NoteBanner.tsx | 18 +++++-- src/lib/attach-ua.ts | 20 +++++++- src/lib/cookie.ts | 7 ++- src/lib/define-metadata.ts | 4 +- src/lib/request.server.ts | 4 +- src/lib/request.ts | 9 +++- src/queries/definition/note.ts | 22 ++++++-- 17 files changed, 118 insertions(+), 70 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index e3a823abc3..8dd17bfabd 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -23,11 +23,12 @@ if (repoInfo) { /** @type {import('next').NextConfig} */ // eslint-disable-next-line import/no-mutable-exports let nextConfig = { - logging: { - fetches: { - fullUrl: true, - }, - }, + // logging: { + // fetches: { + + // // fullUrl: true, + // }, + // }, env: { APP_VERSION: pkg.version, COMMIT_HASH: commitHash, diff --git a/src/app/(app)/(note-topic)/notes/(topic-detail)/topics/[slug]/layout.tsx b/src/app/(app)/(note-topic)/notes/(topic-detail)/topics/[slug]/layout.tsx index 7814a58d14..32c146c2b6 100644 --- a/src/app/(app)/(note-topic)/notes/(topic-detail)/topics/[slug]/layout.tsx +++ b/src/app/(app)/(note-topic)/notes/(topic-detail)/topics/[slug]/layout.tsx @@ -3,7 +3,7 @@ import type { Metadata } from 'next' import { QueryHydrate } from '~/components/common/QueryHydrate' import { NormalContainer } from '~/components/layout/container/Normal' -import { attachUAAndRealIp } from '~/lib/attach-ua' +import { attachServerFetch } from '~/lib/attach-ua' import { isShallowEqualArray } from '~/lib/lodash' import { getQueryClient } from '~/lib/query-client.server' import { definePrerenderPage } from '~/lib/request.server' @@ -16,7 +16,7 @@ export const generateMetadata = async ( slug: string }>, ) => { - attachUAAndRealIp() + attachServerFetch() const queryClient = getQueryClient() const query = getTopicQuery(props.params.slug) diff --git a/src/app/(app)/(page-detail)/[slug]/layout.tsx b/src/app/(app)/(page-detail)/[slug]/layout.tsx index 5bf02eb0e8..5f4cff941c 100644 --- a/src/app/(app)/(page-detail)/[slug]/layout.tsx +++ b/src/app/(app)/(page-detail)/[slug]/layout.tsx @@ -14,7 +14,7 @@ import { BottomToUpTransitionView, } from '~/components/ui/transition' import { OnlyMobile } from '~/components/ui/viewport/OnlyMobile' -import { attachUAAndRealIp } from '~/lib/attach-ua' +import { attachServerFetch } from '~/lib/attach-ua' import { getOgUrl } from '~/lib/helper.server' import { getSummaryFromMd } from '~/lib/markdown' import { apiClient } from '~/lib/request' @@ -33,7 +33,7 @@ import { export const dynamic = 'force-dynamic' const getData = cache(async (params: PageParams) => { - attachUAAndRealIp() + attachServerFetch() const data = await apiClient.page .getBySlug(params.slug) .catch(requestErrorHandler) diff --git a/src/app/(app)/api.tsx b/src/app/(app)/api.tsx index 6ec92b73b5..9760566c3d 100644 --- a/src/app/(app)/api.tsx +++ b/src/app/(app)/api.tsx @@ -5,7 +5,7 @@ import type { AggregateRoot } from '@mx-space/api-client' import { simpleCamelcaseKeys } from '@mx-space/api-client' import { appStaticConfig } from '~/app.static.config' -import { attachUAAndRealIp } from '~/lib/attach-ua' +import { attachServerFetch } from '~/lib/attach-ua' import { getQueryClient } from '~/lib/query-client.server' import { apiClient } from '~/lib/request' @@ -13,7 +13,7 @@ const cacheTime = appStaticConfig.cache.enabled ? appStaticConfig.cache.ttl.aggregation : 1 export const fetchAggregationData = cache(async () => { - attachUAAndRealIp() + attachServerFetch() const queryClient = getQueryClient() const fetcher = async () => (await $fetch< diff --git a/src/app/(app)/categories/[slug]/api.tsx b/src/app/(app)/categories/[slug]/api.tsx index 7b71b11f1f..0ceb056da5 100644 --- a/src/app/(app)/categories/[slug]/api.tsx +++ b/src/app/(app)/categories/[slug]/api.tsx @@ -1,11 +1,11 @@ import { cache } from 'react' -import { attachUAAndRealIp } from '~/lib/attach-ua' +import { attachServerFetch } from '~/lib/attach-ua' import { apiClient } from '~/lib/request' import { requestErrorHandler } from '~/lib/request.server' export const getData = cache(async (params: { slug: string }) => { - attachUAAndRealIp() + attachServerFetch() return await apiClient.category .getCategoryByIdOrSlug(params.slug) .catch(requestErrorHandler) diff --git a/src/app/(app)/notes/[id]/api.tsx b/src/app/(app)/notes/[id]/api.tsx index 18ddd74341..a4e18807e6 100644 --- a/src/app/(app)/notes/[id]/api.tsx +++ b/src/app/(app)/notes/[id]/api.tsx @@ -2,19 +2,22 @@ import { cache } from 'react' import { headers } from 'next/dist/client/components/headers' import { REQUEST_QUERY } from '~/constants/system' -import { attachUAAndRealIp } from '~/lib/attach-ua' +import { attachServerFetch } from '~/lib/attach-ua' import { getQueryClient } from '~/lib/query-client.server' import { requestErrorHandler } from '~/lib/request.server' import { queries } from '~/queries/definition' export const getData = cache(async (params: { id: string }) => { - attachUAAndRealIp() + attachServerFetch() + const header = headers() const searchParams = new URLSearchParams(header.get(REQUEST_QUERY) || '') const id = params.id + const token = searchParams.get('token') const query = queries.note.byNid( id, searchParams.get('password') || undefined, + token ? `${token}` : undefined, ) const data = await getQueryClient() .fetchQuery(query) diff --git a/src/app/(app)/notes/[id]/page.tsx b/src/app/(app)/notes/[id]/page.tsx index 1acbcdb4c4..585e343aee 100644 --- a/src/app/(app)/notes/[id]/page.tsx +++ b/src/app/(app)/notes/[id]/page.tsx @@ -12,7 +12,10 @@ import { NoteMetaReadingCount, NoteTopic, } from '~/components/modules/note' -import { NoteRootBanner } from '~/components/modules/note/NoteBanner' +import { + NoteBanner, + NoteRootBanner, +} from '~/components/modules/note/NoteBanner' import { ArticleRightAside } from '~/components/modules/shared/ArticleRightAside' import { BanCopyWrapper } from '~/components/modules/shared/BanCopyWrapper' import { ReadIndicatorForMobile } from '~/components/modules/shared/ReadIndicator' @@ -59,6 +62,12 @@ export default async function Page(props: { + {data.hide && ( + + )} diff --git a/src/app/(app)/notes/page.tsx b/src/app/(app)/notes/page.tsx index 8c68f3ac3b..dafec544c8 100644 --- a/src/app/(app)/notes/page.tsx +++ b/src/app/(app)/notes/page.tsx @@ -1,33 +1,24 @@ -'use client' +import { cookies } from 'next/headers' +import { redirect } from 'next/navigation' -import { useQuery } from '@tanstack/react-query' -import { useEffect } from 'react' -import { useRouter } from 'next/navigation' - -import { FullPageLoading } from '~/components/ui/loading' +import { attachServerFetchAuth, detachServerFetchAuth } from '~/lib/attach-ua' +import { AuthKeyNames } from '~/lib/cookie' import { apiClient } from '~/lib/request' +import { definePrerenderPage } from '~/lib/request.server' -export default function Page() { - const { - data: nid, - isError, - isLoading, - } = useQuery({ - queryFn: async () => { - return apiClient.note.getLatest() - }, - queryKey: ['note-latest'], - select(data) { - return data.data.nid - }, - }) - - const router = useRouter() - useEffect(() => { - if (!nid) return - - router.replace(`/notes/${nid}`) - }, [nid, router]) - - return -} +export default definePrerenderPage()({ + async fetcher() { + attachServerFetchAuth() + const { data } = await apiClient.note.getLatest() + detachServerFetchAuth() + return data + }, + Component: ({ data: { nid, hide } }) => { + const jwt = cookies().get(AuthKeyNames[0])?.value + if (hide) { + return redirect(`/notes/${nid}?token=${jwt}`) + } else { + redirect(`/notes/${nid}`) + } + }, +}) diff --git a/src/app/(app)/posts/(post-detail)/[category]/[slug]/api.tsx b/src/app/(app)/posts/(post-detail)/[category]/[slug]/api.tsx index c21a765be8..cdf0727d9a 100644 --- a/src/app/(app)/posts/(post-detail)/[category]/[slug]/api.tsx +++ b/src/app/(app)/posts/(post-detail)/[category]/[slug]/api.tsx @@ -1,6 +1,6 @@ import { cache } from 'react' -import { attachUAAndRealIp } from '~/lib/attach-ua' +import { attachServerFetch } from '~/lib/attach-ua' import { getQueryClient } from '~/lib/query-client.server' import { requestErrorHandler } from '~/lib/request.server' import { queries } from '~/queries/definition' @@ -11,7 +11,7 @@ export interface PageParams { } export const getData = cache(async (params: PageParams) => { const { category, slug } = params - attachUAAndRealIp() + attachServerFetch() const data = await getQueryClient() .fetchQuery(queries.post.bySlug(category, slug)) .catch(requestErrorHandler) diff --git a/src/app/(app)/timeline/layout.tsx b/src/app/(app)/timeline/layout.tsx index 5b0f9426bb..1c1eacc446 100644 --- a/src/app/(app)/timeline/layout.tsx +++ b/src/app/(app)/timeline/layout.tsx @@ -7,7 +7,7 @@ import { TimelineType } from '@mx-space/api-client' import { QueryHydrate } from '~/components/common/QueryHydrate' import { SearchFAB } from '~/components/modules/shared/SearchFAB' import { REQUEST_QUERY } from '~/constants/system' -import { attachUAAndRealIp } from '~/lib/attach-ua' +import { attachServerFetch } from '~/lib/attach-ua' import { getQueryClient } from '~/lib/query-client.server' import { apiClient } from '~/lib/request' @@ -16,7 +16,7 @@ export const metadata = { } export const dynamic = 'force-dynamic' export default async (props: PropsWithChildren) => { - attachUAAndRealIp() + attachServerFetch() const header = headers() const query = header.get(REQUEST_QUERY) diff --git a/src/components/modules/note/NoteBanner.tsx b/src/components/modules/note/NoteBanner.tsx index 6b4313d69e..2441ad7188 100644 --- a/src/components/modules/note/NoteBanner.tsx +++ b/src/components/modules/note/NoteBanner.tsx @@ -17,7 +17,7 @@ const useNoteBanner = () => { const meta = useCurrentNoteDataSelector((n) => n?.data.meta) let banner = meta?.banner as { - type: string + type: keyof typeof bannerClassNames message: string className: string style?: any @@ -31,7 +31,7 @@ const useNoteBanner = () => { type: 'info', message: banner, className: bannerClassNames.info, - } + } as any } banner = { ...banner } banner.type ??= 'info' @@ -47,7 +47,7 @@ export const NoteRootBanner = () => { if (!banner) return null return ( -
+
) @@ -55,12 +55,20 @@ export const NoteRootBanner = () => { export const NoteBanner: FC<{ style?: any - className: string + className?: string message: string + type?: keyof typeof bannerClassNames }> = (banner) => { return (
{banner.message} diff --git a/src/lib/attach-ua.ts b/src/lib/attach-ua.ts index d2d805a110..b2b60fb608 100644 --- a/src/lib/attach-ua.ts +++ b/src/lib/attach-ua.ts @@ -1,9 +1,10 @@ -import { headers } from 'next/headers' +import { cookies, headers } from 'next/headers' import PKG from '../../package.json' +import { AuthKeyNames } from './cookie' import { attachFetchHeader } from './request' -export const attachUAAndRealIp = () => { +export const attachServerFetch = () => { const { get } = headers() const ua = get('user-agent') @@ -22,3 +23,18 @@ export const attachUAAndRealIp = () => { `${ua} NextJS/v${PKG.dependencies.next} ${PKG.name}/${PKG.version}`, ) } + +export const attachServerFetchAuth = () => { + const cookie = cookies() + const jwt = cookie.get(AuthKeyNames[0]) + + if (jwt) { + attachFetchHeader('Authorization', `Bearer ${jwt.value}`) + } else { + attachFetchHeader('Authorization', '') + } +} + +export const detachServerFetchAuth = () => { + attachFetchHeader('Authorization', null) +} diff --git a/src/lib/cookie.ts b/src/lib/cookie.ts index 1306ce68bf..a90835a8dc 100644 --- a/src/lib/cookie.ts +++ b/src/lib/cookie.ts @@ -1,12 +1,15 @@ import dayjs from 'dayjs' import Cookies from 'js-cookie' -export const TokenKey = 'mx-token' +const TokenKey = 'mx-token' + +const ClerkCookieKey = '__session' +export const AuthKeyNames = [TokenKey, ClerkCookieKey] export function getToken(): string | null { // FUCK clerk constants not export, and mark it internal and can not custom // packages/backend/src/constants.ts - const clerkJwt = Cookies.get('__session') + const clerkJwt = Cookies.get(ClerkCookieKey) const token = Cookies.get(TokenKey) || clerkJwt diff --git a/src/lib/define-metadata.ts b/src/lib/define-metadata.ts index 99390d00e4..48f81ad7ac 100644 --- a/src/lib/define-metadata.ts +++ b/src/lib/define-metadata.ts @@ -6,7 +6,7 @@ import type { Metadata } from 'next' import { getQueryClient } from '~/lib/query-client.server' import { queries } from '~/queries/definition' -import { attachUAAndRealIp } from './attach-ua' +import { attachServerFetch } from './attach-ua' export const defineMetadata = >( fn: ( @@ -17,7 +17,7 @@ export const defineMetadata = >( const handler = async ({ params }: { params: T }): Promise => { const getData = async () => { const queryClient = getQueryClient() - attachUAAndRealIp() + attachServerFetch() return await queryClient.fetchQuery({ ...queries.aggregation.root(), }) diff --git a/src/lib/request.server.ts b/src/lib/request.server.ts index b53791d54e..5fc3b02c76 100644 --- a/src/lib/request.server.ts +++ b/src/lib/request.server.ts @@ -9,7 +9,7 @@ import { RequestError } from '@mx-space/api-client' import { BizErrorPage } from '~/components/common/BizErrorPage' import { NormalContainer } from '~/components/layout/container/Normal' -import { attachUAAndRealIp } from './attach-ua' +import { attachServerFetch } from './attach-ua' import { getErrorMessageFromRequestError } from './request.shared' export const requestErrorHandler = (error: Error | RequestError) => { @@ -61,7 +61,7 @@ export const definePrerenderPage = return async (props: any) => { const { params, searchParams } = props as NextPageParams try { - attachUAAndRealIp() + attachServerFetch() const data = await fetcher({ ...params, ...searchParams, diff --git a/src/lib/request.ts b/src/lib/request.ts index bcf54cae8a..525dc279da 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -32,6 +32,7 @@ const $fetch = createFetch({ if (token) { headers['Authorization'] = `bearer ${token}` } + headers['x-session-uuid'] = globalThis?.sessionStorage?.getItem(uuidStorageKey) ?? uuid @@ -100,9 +101,13 @@ export const apiClient = createClient(fetchAdapter)(API_URL, { }, }) -export const attachFetchHeader = (key: string, value: string) => { +export const attachFetchHeader = (key: string, value: string | null) => { const original = globalConfigureHeader[key] - globalConfigureHeader[key] = value + if (value === null) { + delete globalConfigureHeader[key] + } else { + globalConfigureHeader[key] = value + } return () => { if (typeof original === 'undefined') { diff --git a/src/queries/definition/note.ts b/src/queries/definition/note.ts index 9bcc584d04..249fc596ee 100644 --- a/src/queries/definition/note.ts +++ b/src/queries/definition/note.ts @@ -1,6 +1,10 @@ import { useMutation } from '@tanstack/react-query' import dayjs from 'dayjs' -import type { NoteModel, NoteWrappedPayload } from '@mx-space/api-client' +import type { + NoteModel, + NoteWrappedPayload, + NoteWrappedWithLikedPayload, +} from '@mx-space/api-client' import type { NoteDto } from '~/models/writing' import { useResetAutoSaverData } from '~/components/modules/dashboard/writing/BaseWritingProvider' @@ -13,9 +17,9 @@ import { defineQuery } from '../helper' const LATEST_KEY = 'latest' export const note = { - byNid: (nid: string, password?: string | null) => + byNid: (nid: string, password?: string | null, token?: string) => defineQuery({ - queryKey: ['note', nid], + queryKey: ['note', nid, token], meta: { hydrationRoutePath: routeBuilder(Routes.Note, { id: nid }), shouldHydration: (data: NoteWrappedPayload) => { @@ -23,7 +27,7 @@ export const note = { const isSecret = note?.publicAt ? dayjs(note?.publicAt).isAfter(new Date()) : false - return !isSecret + return !isSecret && !data.data.hide }, }, queryFn: async ({ queryKey }) => { @@ -32,7 +36,15 @@ export const note = { if (id === LATEST_KEY) { return (await apiClient.note.getLatest()).$serialized } - const data = await apiClient.note.getNoteById(+queryKey[1], password!) + // const data = await apiClient.note.getNoteById(+queryKey[1], password!) + const data = await apiClient.note.proxy + .nid(id) + .get({ + params: { + password, + token, + }, + }) return { ...data } as NoteWrappedPayload },