From 4e07a002047784a49d05a29217c7be38ccf4e12e Mon Sep 17 00:00:00 2001 From: Krzysztof Kocot Date: Tue, 16 Dec 2025 17:40:25 +0100 Subject: [PATCH] Add route guards to prevent unnecessary API calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additional fixes for #768: - Add hive- prefix check for getCommunity, getSubscribers, getAccountNotifications - Add early return guards in user-profile and post routes to prevent API calls when route params don't match expected username format (@/%40 prefix) - Prevents WaxError exceptions from invalid WASM calls on non-user routes Files changed: - community-layout.tsx: Add enabled guard for 3 useQuery hooks - prefetch-component.tsx: Change condition from if(community) to startsWith check - post-form.tsx: Add hive- prefix validation to enabled condition - user-profile/layout.tsx: Add early returns for non-@ params in generateMetadata and Layout - [p2]/[permlink]/layout.tsx: Add early return for non-@ p2 params - [p2]/[permlink]/page.tsx: Add notFound() for non-@ p2 params 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- apps/blog/app/[param]/(user-profile)/layout.tsx | 17 +++++++++++++++-- .../blog/app/[param]/[p2]/[permlink]/layout.tsx | 9 ++++++++- apps/blog/app/[param]/[p2]/[permlink]/page.tsx | 7 ++++++- .../layouts/community/community-layout.tsx | 9 ++++++--- .../layouts/community/prefetch-component.tsx | 2 +- apps/blog/features/post-editor/post-form.tsx | 2 +- 6 files changed, 37 insertions(+), 9 deletions(-) diff --git a/apps/blog/app/[param]/(user-profile)/layout.tsx b/apps/blog/app/[param]/(user-profile)/layout.tsx index c470497bd..581bcf3a9 100644 --- a/apps/blog/app/[param]/(user-profile)/layout.tsx +++ b/apps/blog/app/[param]/(user-profile)/layout.tsx @@ -13,7 +13,14 @@ const logger = getLogger('app'); export async function generateMetadata({ params }: { params: { param: string } }): Promise { const raw = params.param; - const username = raw.startsWith('%40') ? raw.replace('%40', '') : raw; + // Only process if it looks like a username (starts with @ or %40) + if (!raw.startsWith('@') && !raw.startsWith('%40')) { + return { + title: 'Hive', + description: 'Hive: Communities Without Borders.' + }; + } + const username = raw.startsWith('%40') ? raw.replace('%40', '') : raw.replace('@', ''); const queryClient = getQueryClient(); try { const account = await queryClient.fetchQuery({ @@ -57,7 +64,13 @@ export async function generateMetadata({ params }: { params: { param: string } } const Layout = async ({ children, params }: { children: ReactNode; params: { param: string } }) => { const queryClient = getQueryClient(); const { param } = params; - const username = param.startsWith('%40') ? param.replace('%40', '') : param; + + // Only process if it looks like a username (starts with @ or %40) + if (!param.startsWith('@') && !param.startsWith('%40')) { + notFound(); + } + + const username = param.startsWith('%40') ? param.replace('%40', '') : param.replace('@', ''); const valid = await isUsernameValid(username); if (!valid) { diff --git a/apps/blog/app/[param]/[p2]/[permlink]/layout.tsx b/apps/blog/app/[param]/[p2]/[permlink]/layout.tsx index 9538b3b91..68101be81 100644 --- a/apps/blog/app/[param]/[p2]/[permlink]/layout.tsx +++ b/apps/blog/app/[param]/[p2]/[permlink]/layout.tsx @@ -12,7 +12,14 @@ export async function generateMetadata({ }: { params: { param: string; p2: string; permlink: string }; }): Promise { - const author = params?.p2?.replace('%40', ''); + // p2 should start with @ or %40 for valid post URLs + if (!params?.p2?.startsWith('@') && !params?.p2?.startsWith('%40')) { + return { + title: 'Hive', + description: 'Hive: Communities Without Borders.' + }; + } + const author = params.p2.replace('%40', '').replace('@', ''); const permlink = params?.permlink; const observer = getObserverFromCookies(); diff --git a/apps/blog/app/[param]/[p2]/[permlink]/page.tsx b/apps/blog/app/[param]/[p2]/[permlink]/page.tsx index a4558d640..b1f0d27a2 100644 --- a/apps/blog/app/[param]/[p2]/[permlink]/page.tsx +++ b/apps/blog/app/[param]/[p2]/[permlink]/page.tsx @@ -18,8 +18,13 @@ const PostPage = async ({ }: { params: { param: string; p2: string; permlink: string }; }) => { + // p2 should start with @ or %40 for valid post URLs + if (!p2?.startsWith('@') && !p2?.startsWith('%40')) { + notFound(); + } + const queryClient = getQueryClient(); - const username = p2.replace('%40', ''); + const username = p2.replace('%40', '').replace('@', ''); const community = param; const validUser = await isUsernameValid(username); if (!validUser) notFound(); diff --git a/apps/blog/features/layouts/community/community-layout.tsx b/apps/blog/features/layouts/community/community-layout.tsx index 0f54386fd..adf9146e8 100644 --- a/apps/blog/features/layouts/community/community-layout.tsx +++ b/apps/blog/features/layouts/community/community-layout.tsx @@ -27,11 +27,13 @@ const CommunityLayout = ({ children, community }: { children: ReactNode; communi const isRolesPage = pathname?.includes('/roles/'); const { data: subsData } = useQuery({ queryKey: ['subscribers', community], - queryFn: () => getSubscribers(community ?? '') + queryFn: () => getSubscribers(community ?? ''), + enabled: community?.startsWith('hive-') }); const { data: notificationData } = useQuery({ queryKey: ['AccountNotification', community], - queryFn: () => getAccountNotifications(community ?? '') + queryFn: () => getAccountNotifications(community ?? ''), + enabled: community?.startsWith('hive-') }); const { data: mySubsData } = useQuery({ @@ -42,7 +44,8 @@ const CommunityLayout = ({ children, community }: { children: ReactNode; communi const { data: communityData } = useQuery({ queryKey: ['community', community], - queryFn: () => getCommunity(community, observer) + queryFn: () => getCommunity(community, observer), + enabled: community?.startsWith('hive-') }); return ( diff --git a/apps/blog/features/layouts/community/prefetch-component.tsx b/apps/blog/features/layouts/community/prefetch-component.tsx index d90b9094e..575043267 100644 --- a/apps/blog/features/layouts/community/prefetch-component.tsx +++ b/apps/blog/features/layouts/community/prefetch-component.tsx @@ -24,7 +24,7 @@ const PrefetchComponent = async ({ children, community }: { children: ReactNode; queryKey: ['communitiesList', sort], queryFn: () => getCommunities(sort, query, observer) }); - if (community) { + if (community.startsWith('hive-')) { await queryClient.prefetchQuery({ queryKey: ['community', community], queryFn: async () => await getCommunity(community, observer) diff --git a/apps/blog/features/post-editor/post-form.tsx b/apps/blog/features/post-editor/post-form.tsx index 9c07b9db9..e7b110502 100644 --- a/apps/blog/features/post-editor/post-form.tsx +++ b/apps/blog/features/post-editor/post-form.tsx @@ -110,7 +110,7 @@ export default function PostForm({ const { data: communityData } = useQuery({ queryKey: ['community', categoryParam], queryFn: () => getCommunity(categoryParam ?? storedPost.category, observer), - enabled: Boolean(categoryParam) || Boolean(storedPost.category) + enabled: categoryParam?.startsWith('hive-') || storedPost.category?.startsWith('hive-') }); const { data: mySubsData } = useQuery({ queryKey: ['subscriptions', username], -- GitLab