* fix(navigation): use replaceState for project filters to prevent history pollution * fix: add replace prop to NavTabs and enable it on project and discover pages * style: run pnpm run fix on affected files * enable NavTabs replace prop on collection, user, and org pages * fix: guard project middleware on client * fix: lint --------- Co-authored-by: Calum H. (IMB11) <contact@cal.engineer> Co-authored-by: Truman Gao <106889354+tdgao@users.noreply.github.com>
81 lines
2.5 KiB
TypeScript
81 lines
2.5 KiB
TypeScript
import { useGeneratedState } from '~/composables/generated'
|
|
import { projectQueryOptions } from '~/composables/queries/project'
|
|
import { useAppQueryClient } from '~/composables/query-client'
|
|
import { getProjectTypeForUrlShorthand } from '~/helpers/projects.js'
|
|
import { useServerModrinthClient } from '~/server/utils/api-client'
|
|
|
|
// All valid project type URL segments
|
|
const PROJECT_TYPES = [
|
|
'project',
|
|
'mod',
|
|
'plugin',
|
|
'datapack',
|
|
'shader',
|
|
'resourcepack',
|
|
'modpack',
|
|
'server',
|
|
'minecraft_java_server',
|
|
]
|
|
|
|
export default defineNuxtRouteMiddleware(async (to) => {
|
|
// Only run this middleware on the server - it relies on server-only runtime config
|
|
if (import.meta.client) return
|
|
// Only handle project routes
|
|
if (!to.params.id || !PROJECT_TYPES.includes(to.params.type as string)) {
|
|
return
|
|
}
|
|
|
|
const queryClient = useAppQueryClient()
|
|
const authToken = useCookie('auth-token')
|
|
const client = useServerModrinthClient({ authToken: authToken.value || undefined })
|
|
const tags = useGeneratedState()
|
|
const projectId = to.params.id as string
|
|
|
|
try {
|
|
// Fetch v2 project for redirect check AND cache it for the page
|
|
// Using fetchQuery ensures the page's useQuery gets this cached result
|
|
const project = await queryClient.fetchQuery(projectQueryOptions.v2(projectId, client))
|
|
const projectV3 = await queryClient.fetchQuery(projectQueryOptions.v3(projectId, client))
|
|
|
|
// Let page handle 404
|
|
if (!project) return
|
|
|
|
// Cache by slug if we looked up by ID (or vice versa)
|
|
if (projectId !== project.slug) {
|
|
queryClient.setQueryData(['project', 'v2', project.slug], project)
|
|
}
|
|
if (projectId !== project.id) {
|
|
queryClient.setQueryData(['project', 'v2', project.id], project)
|
|
}
|
|
|
|
const projectType = projectV3.minecraft_server != null ? 'server' : project.project_type
|
|
// Determine the correct URL type
|
|
const correctType = getProjectTypeForUrlShorthand(projectType, project.loaders, tags.value)
|
|
|
|
// Preserve the rest of the path (subpages like /versions, /settings, etc.)
|
|
const pathParts = to.path.split('/')
|
|
pathParts.splice(0, 3) // Remove '', type, and id
|
|
const remainder = pathParts.filter((x) => x).join('/')
|
|
|
|
// Build the canonical path
|
|
const canonicalPath = `/${correctType}/${project.slug}${remainder ? `/${remainder}` : ''}`
|
|
|
|
// Only redirect if the path actually changed
|
|
if (to.path !== canonicalPath) {
|
|
return navigateTo(
|
|
{
|
|
path: canonicalPath,
|
|
query: to.query,
|
|
hash: to.hash,
|
|
},
|
|
{
|
|
redirectCode: 301,
|
|
replace: true,
|
|
},
|
|
)
|
|
}
|
|
} catch {
|
|
// Let the page handle 404s and other errors
|
|
}
|
|
})
|