fix: malformed versions causing versions list page to crash (#6315)

This commit is contained in:
Truman Gao
2026-06-05 03:49:59 -06:00
committed by GitHub
parent 352a196795
commit c653228fe7
7 changed files with 61 additions and 28 deletions
+10 -4
View File
@@ -1874,12 +1874,18 @@ const isSettings = computed(() => route.name.startsWith('type-project-settings')
// Transform versionsV3 to be same shape as versionsV2 for compatibility in project pages
const versionsRaw = computed(() => {
return (versionsV3.value ?? []).map((v) => {
const isModpack = v.project_types?.includes('modpack')
return (versionsV3.value ?? []).map((version) => {
const files = Array.isArray(version.files) ? version.files : []
const gameVersions = Array.isArray(version.game_versions) ? version.game_versions : []
const loaders = Array.isArray(version.loaders) ? version.loaders : []
const isModpack = version.project_types?.includes('modpack')
const mrpackLoaders = Array.isArray(version.mrpack_loaders) ? version.mrpack_loaders : []
return {
...v,
loaders: isModpack && v.mrpack_loaders ? v.mrpack_loaders : v.loaders,
...version,
files,
game_versions: gameVersions,
loaders: isModpack && mrpackLoaders.length ? mrpackLoaders : loaders,
}
})
})
@@ -53,11 +53,11 @@
{{ formatDate(version.date_published) }}</span
>
</div>
<ButtonStyled color="brand" type="transparent">
<ButtonStyled v-if="getPrimaryFile(version)" color="brand" type="transparent">
<a
class="ml-auto"
:href="createDownloadUrl(version)"
:download="getPrimaryFile(version).filename"
:download="getPrimaryFile(version)?.filename"
:title="`Download ${version.name}`"
>
<DownloadIcon aria-hidden="true" />
@@ -145,10 +145,10 @@ const filteredVersions = computed(() => {
(projectVersion) =>
(selectedGameVersions.length === 0 ||
selectedGameVersions.some((gameVersion) =>
projectVersion.game_versions.includes(gameVersion),
getVersionGameVersions(projectVersion).includes(gameVersion),
)) &&
(selectedLoaders.length === 0 ||
selectedLoaders.some((loader) => projectVersion.loaders.includes(loader))) &&
selectedLoaders.some((loader) => getVersionLoaders(projectVersion).includes(loader))) &&
(selectedVersionTypes.length === 0 ||
selectedVersionTypes.includes(projectVersion.version_type)),
)
@@ -233,14 +233,25 @@ watch(
)
function getPrimaryFile(version) {
return version.files.find((x) => x.primary) || version.files[0]
return version.files?.find((x) => x.primary) || version.files?.[0]
}
function createDownloadUrl(version) {
return createProjectDownloadUrl(getPrimaryFile(version).url, {
const file = getPrimaryFile(version)
if (!file?.url) return undefined
return createProjectDownloadUrl(file.url, {
reason: cdnDownloadReason.value,
})
}
function getVersionGameVersions(version) {
return Array.isArray(version.game_versions) ? version.game_versions : []
}
function getVersionLoaders(version) {
return Array.isArray(version.loaders) ? version.loaders : []
}
</script>
<style lang="scss">
@@ -111,10 +111,11 @@
color: 'primary',
hoverFilled: true,
link: createDownloadUrl(version),
download: getPrimaryFile(version).filename,
download: getPrimaryFile(version)?.filename,
action: () => {
emit('onDownload')
},
shown: !!getPrimaryFile(version),
},
{
id: 'new-tab',
@@ -402,7 +403,7 @@ const emit = defineEmits(['onDownload'])
const baseDropdownId = useId()
function getPrimaryFile(version: Labrinth.Versions.v3.Version) {
return version.files.find((x) => x.primary) || version.files[0]
return version.files?.find((x) => x.primary) || version.files?.[0]
}
watch(
@@ -417,7 +418,10 @@ watch(
)
function createDownloadUrl(version: Labrinth.Versions.v3.Version) {
return createProjectDownloadUrl(getPrimaryFile(version).url, {
const file = getPrimaryFile(version)
if (!file?.url) return undefined
return createProjectDownloadUrl(file.url, {
reason: cdnDownloadReason.value,
})
}
@@ -43,11 +43,11 @@
:open-modal="currentMember ? () => handleOpenCreateVersionModal() : undefined"
>
<template #actions="{ version }">
<ButtonStyled circular type="transparent">
<ButtonStyled v-if="getPrimaryFile(version)" circular type="transparent">
<a
v-tooltip="`Download`"
:href="createDownloadUrl(version)"
:download="getPrimaryFile(version).filename"
:download="getPrimaryFile(version)?.filename"
class="hover:!bg-button-bg [&>svg]:!text-green"
aria-label="Download"
@click="emit('onDownload')"
@@ -102,10 +102,11 @@
color: 'primary',
hoverFilled: true,
link: createDownloadUrl(version),
download: getPrimaryFile(version).filename,
download: getPrimaryFile(version)?.filename,
action: () => {
emit('onDownload')
},
shown: !!getPrimaryFile(version),
},
{
id: 'new-tab',
@@ -318,7 +319,7 @@ const emit = defineEmits(['onDownload', 'deleteVersion'])
const baseDropdownId = useId()
function getPrimaryFile(version) {
return version.files.find((x) => x.primary) || version.files[0]
return version.files?.find((x) => x.primary) || version.files?.[0]
}
watch(
@@ -333,7 +334,10 @@ watch(
)
function createDownloadUrl(version) {
return createProjectDownloadUrl(getPrimaryFile(version).url, {
const file = getPrimaryFile(version)
if (!file?.url) return undefined
return createProjectDownloadUrl(file.url, {
reason: cdnDownloadReason.value,
})
}
@@ -83,7 +83,6 @@ const props = withDefaults(
{
page: 1,
count: 1,
linkFunction: (page: number) => void page,
},
)
@@ -327,15 +327,22 @@ const props = withDefaults(
)
function getModpackLoaders(version: VersionWithDisplayUrlEnding): string[] {
const loaders = Array.isArray(version.loaders) ? version.loaders : []
if (props.project.project_type !== 'modpack') {
return version.loaders
return loaders
}
if (version.mrpack_loaders?.length) {
return version.mrpack_loaders
const mrpackLoaders = Array.isArray(version.mrpack_loaders) ? version.mrpack_loaders : []
if (mrpackLoaders.length) {
return mrpackLoaders
}
return version.loaders.filter((loader) => loader !== 'mrpack')
return loaders.filter((loader) => loader !== 'mrpack')
}
function getGameVersions(version: VersionWithDisplayUrlEnding): string[] {
return Array.isArray(version.game_versions) ? version.game_versions : []
}
function hasNoModLoader(loaders: string[]): boolean {
@@ -350,10 +357,12 @@ function hasNoModLoader(loaders: string[]): boolean {
const normalizedVersions = computed<DisplayVersion[]>(() =>
props.versions.map((version) => {
const loaders = getModpackLoaders(version)
const gameVersions = getGameVersions(version)
const noModLoader = hasNoModLoader(loaders)
return {
...version,
game_versions: gameVersions,
loaders: noModLoader ? [] : loaders,
noModLoader,
}
@@ -109,15 +109,15 @@ const filterOptions = computed(() => {
platform: [],
}
const platformSet = new Set()
const gameVersionSet = new Set()
const channelSet = new Set()
const platformSet = new Set<Filter>()
const gameVersionSet = new Set<Filter>()
const channelSet = new Set<Filter>()
for (const version of props.versions) {
for (const loader of version.loaders) {
for (const loader of Array.isArray(version.loaders) ? version.loaders : []) {
platformSet.add(loader)
}
for (const gameVersion of version.game_versions) {
for (const gameVersion of Array.isArray(version.game_versions) ? version.game_versions : []) {
gameVersionSet.add(gameVersion)
}
channelSet.add(version.version_type)