mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 09:04:55 +00:00
feat: show warning for broken mrpack in moderation (#7136)
* feat: show warning if modpack has a version during time when modpack export broke * fix type * fix: dont lazy load versions v3 for staff and convert times to utc * pnpm prepr
This commit is contained in:
@@ -400,10 +400,10 @@ defineExpose({ show, hide })
|
||||
</Combobox>
|
||||
<IconButton
|
||||
v-tooltip="formatMessage(messages.deleteAllGroups)"
|
||||
type="quiet"
|
||||
color="red"
|
||||
type="base"
|
||||
:label="formatMessage(messages.deleteAllGroups)"
|
||||
:disabled="titleButtonsDisabled"
|
||||
class="[&:not(:disabled):focus-visible>svg]:!text-red [&:not(:disabled):hover>svg]:!text-red"
|
||||
@click="showConfirmClearGroups"
|
||||
>
|
||||
<TrashIcon v-if="!isClearing" aria-hidden="true" />
|
||||
|
||||
@@ -3458,6 +3458,12 @@
|
||||
"project.actions.back-to-project-page": {
|
||||
"message": "Back to project page"
|
||||
},
|
||||
"project.actions.check-modpack-archives": {
|
||||
"message": "Check modpack unzip"
|
||||
},
|
||||
"project.actions.checking-modpack-archives": {
|
||||
"message": "Checking..."
|
||||
},
|
||||
"project.actions.create-server": {
|
||||
"message": "Create a server"
|
||||
},
|
||||
@@ -3743,9 +3749,24 @@
|
||||
"project.moderation.title": {
|
||||
"message": "Moderation"
|
||||
},
|
||||
"project.modpack-archive-warning.description": {
|
||||
"message": "Importing this .mrpack might be broken."
|
||||
},
|
||||
"project.modpack-archive-warning.title": {
|
||||
"message": "This modpack was published during export bug"
|
||||
},
|
||||
"project.navigation.changelog": {
|
||||
"message": "Changelog"
|
||||
},
|
||||
"project.notification.check-modpack-archives.failed": {
|
||||
"message": "Some modpack files could not be unzipped"
|
||||
},
|
||||
"project.notification.check-modpack-archives.no-files": {
|
||||
"message": "No .mrpack files were found for this project."
|
||||
},
|
||||
"project.notification.check-modpack-archives.success": {
|
||||
"message": "{count, plural, one {The modpack file can be unzipped.} other {All # modpack files can be unzipped.}}"
|
||||
},
|
||||
"project.notification.icon-updated.message": {
|
||||
"message": "Your project's icon has been updated."
|
||||
},
|
||||
|
||||
@@ -405,6 +405,36 @@
|
||||
:auth="auth"
|
||||
:tags="tags"
|
||||
/>
|
||||
<Admonition
|
||||
v-if="
|
||||
auth.user &&
|
||||
tags.staffRoles.includes(auth.user.role) &&
|
||||
project.actualProjectType === 'modpack' &&
|
||||
hasModpackArchiveInWarningWindow
|
||||
"
|
||||
type="warning"
|
||||
:header="formatMessage(messages.modpackArchiveWarningTitle)"
|
||||
class="mt-3"
|
||||
>
|
||||
{{ formatMessage(messages.modpackArchiveWarningDescription) }}
|
||||
<template #actions>
|
||||
<Button
|
||||
type="colored"
|
||||
color="orange"
|
||||
:loading="isCheckingModpackArchives"
|
||||
@click="checkModpackArchives"
|
||||
>
|
||||
<FileArchiveIcon />
|
||||
{{
|
||||
formatMessage(
|
||||
isCheckingModpackArchives
|
||||
? messages.checkingModpackArchives
|
||||
: messages.checkModpackArchives,
|
||||
)
|
||||
}}
|
||||
</Button>
|
||||
</template>
|
||||
</Admonition>
|
||||
<Admonition
|
||||
v-if="
|
||||
currentMember &&
|
||||
@@ -529,6 +559,7 @@ import {
|
||||
ClipboardCopyIcon,
|
||||
CompassIcon,
|
||||
DownloadIcon,
|
||||
FileArchiveIcon,
|
||||
FolderSearchIcon,
|
||||
HeartIcon,
|
||||
LeftArrowIcon,
|
||||
@@ -663,6 +694,7 @@ const downloadModal = ref()
|
||||
const openInAppModal = ref()
|
||||
const overTheTopDownloadAnimation = ref()
|
||||
const scanModal = ref()
|
||||
const isCheckingModpackArchives = ref(false)
|
||||
|
||||
const projectV3Loaded = computed(() => !projectV3Pending.value || projectV3.value != null)
|
||||
const isServerProject = computed(() => projectV3.value?.minecraft_server != null)
|
||||
@@ -818,6 +850,35 @@ const messages = defineMessages({
|
||||
id: 'project.actions.rescan-modpack',
|
||||
defaultMessage: 'Rescan modpack',
|
||||
},
|
||||
checkModpackArchives: {
|
||||
id: 'project.actions.check-modpack-archives',
|
||||
defaultMessage: 'Check modpack unzip',
|
||||
},
|
||||
checkingModpackArchives: {
|
||||
id: 'project.actions.checking-modpack-archives',
|
||||
defaultMessage: 'Checking...',
|
||||
},
|
||||
checkModpackArchivesSuccess: {
|
||||
id: 'project.notification.check-modpack-archives.success',
|
||||
defaultMessage:
|
||||
'{count, plural, one {The modpack file can be unzipped.} other {All # modpack files can be unzipped.}}',
|
||||
},
|
||||
checkModpackArchivesFailed: {
|
||||
id: 'project.notification.check-modpack-archives.failed',
|
||||
defaultMessage: 'Some modpack files could not be unzipped',
|
||||
},
|
||||
checkModpackArchivesNoFiles: {
|
||||
id: 'project.notification.check-modpack-archives.no-files',
|
||||
defaultMessage: 'No .mrpack files were found for this project.',
|
||||
},
|
||||
modpackArchiveWarningTitle: {
|
||||
id: 'project.modpack-archive-warning.title',
|
||||
defaultMessage: 'This modpack was published during export bug',
|
||||
},
|
||||
modpackArchiveWarningDescription: {
|
||||
id: 'project.modpack-archive-warning.description',
|
||||
defaultMessage: 'Importing this .mrpack might be broken.',
|
||||
},
|
||||
serversPromoDescription: {
|
||||
id: 'project.actions.servers-promo.description',
|
||||
defaultMessage: 'Modrinth Hosting is the easiest way to play with your friends without hassle!',
|
||||
@@ -1135,7 +1196,7 @@ const {
|
||||
|
||||
const dependencies = computed(() => dependenciesRaw.value ?? null)
|
||||
|
||||
// V3 Versions - lazy loaded client-side only
|
||||
// V3 Versions - lazy loaded client-side only (except for staff, who need v3 versions for moderation)
|
||||
const versionsEnabled = ref(false)
|
||||
const {
|
||||
data: versionsV3,
|
||||
@@ -1149,7 +1210,7 @@ const {
|
||||
apiVersion: 3,
|
||||
}),
|
||||
staleTime: STALE_TIME_LONG,
|
||||
enabled: computed(() => !!projectId.value && versionsEnabled.value),
|
||||
enabled: computed(() => !!projectId.value && (versionsEnabled.value || isStaff(auth.value.user))),
|
||||
})
|
||||
|
||||
// Organization
|
||||
@@ -1749,6 +1810,90 @@ const showProjectHeaderCreateServerAction = computed(
|
||||
const projectHeaderCreateServerTo = computed(() =>
|
||||
project.value ? `/hosting?project=${project.value.id}#plan` : '/hosting',
|
||||
)
|
||||
|
||||
const MRPACK_ARCHIVE_WARNING_START = new Date('2026-08-10T17:00:00.000Z').getTime()
|
||||
const MRPACK_ARCHIVE_WARNING_END = new Date('2026-08-13T20:00:00.000Z').getTime()
|
||||
const hasModpackArchiveInWarningWindow = computed(() =>
|
||||
(versionsV3.value ?? []).some((version) => {
|
||||
const publishedAt = new Date(version.date_published).getTime()
|
||||
return (
|
||||
version.files.some((file) => file.filename.toLowerCase().endsWith('.mrpack')) &&
|
||||
publishedAt >= MRPACK_ARCHIVE_WARNING_START &&
|
||||
publishedAt <= MRPACK_ARCHIVE_WARNING_END
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
async function checkModpackArchives() {
|
||||
if (!project.value || isCheckingModpackArchives.value) return
|
||||
|
||||
isCheckingModpackArchives.value = true
|
||||
startLoading()
|
||||
|
||||
try {
|
||||
const versions = await client.labrinth.versions_v2.getProjectVersions(project.value.id)
|
||||
const filesByUrl = new Map(
|
||||
versions
|
||||
.flatMap((version) => version.files)
|
||||
.filter((file) => file.filename.toLowerCase().endsWith('.mrpack'))
|
||||
.map((file) => [file.url, file]),
|
||||
)
|
||||
const files = [...filesByUrl.values()]
|
||||
|
||||
if (files.length === 0) {
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: formatMessage(messages.checkModpackArchivesNoFiles),
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const { default: JSZip } = await import('jszip')
|
||||
const failures = []
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const response = await fetch(file.url)
|
||||
if (!response.ok) {
|
||||
throw new Error(`Download failed (${response.status} ${response.statusText})`)
|
||||
}
|
||||
|
||||
await JSZip.loadAsync(await response.blob(), { checkCRC32: true })
|
||||
} catch (error) {
|
||||
failures.push({
|
||||
filename: file.filename,
|
||||
error: error?.message ?? String(error),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.length > 0) {
|
||||
addNotification({
|
||||
title: formatMessage(messages.checkModpackArchivesFailed),
|
||||
text: failures.map((failure) => `${failure.filename}: ${failure.error}`).join('\n'),
|
||||
type: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.successLabel),
|
||||
text: formatMessage(messages.checkModpackArchivesSuccess, { count: files.length }),
|
||||
type: 'success',
|
||||
})
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
title: formatMessage(commonMessages.errorNotificationTitle),
|
||||
text: error?.data?.description ?? error?.message ?? String(error),
|
||||
type: 'error',
|
||||
})
|
||||
} finally {
|
||||
isCheckingModpackArchives.value = false
|
||||
stopLoading()
|
||||
}
|
||||
}
|
||||
|
||||
const projectHeaderMoreActions = computed(() => {
|
||||
const isStaff = !!(auth.value.user && tags.value.staffRoles.includes(auth.value.user.role))
|
||||
|
||||
@@ -1787,6 +1932,19 @@ const projectHeaderMoreActions = computed(() => {
|
||||
tone: 'orange',
|
||||
shown: !!auth.value.user && isStaff && project.value?.actualProjectType === 'modpack',
|
||||
},
|
||||
{
|
||||
id: 'moderation-modpack-check-archives',
|
||||
label: formatMessage(
|
||||
isCheckingModpackArchives.value
|
||||
? messages.checkingModpackArchives
|
||||
: messages.checkModpackArchives,
|
||||
),
|
||||
icon: FileArchiveIcon,
|
||||
action: checkModpackArchives,
|
||||
tone: 'orange',
|
||||
disabled: isCheckingModpackArchives.value,
|
||||
shown: !!auth.value.user && isStaff && project.value?.actualProjectType === 'modpack',
|
||||
},
|
||||
{ type: 'divider', shown: !!auth.value.user && isStaff },
|
||||
{
|
||||
id: 'report',
|
||||
|
||||
Reference in New Issue
Block a user