mirror of
https://github.com/modrinth/code.git
synced 2026-09-02 13:05:50 +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>
|
</Combobox>
|
||||||
<IconButton
|
<IconButton
|
||||||
v-tooltip="formatMessage(messages.deleteAllGroups)"
|
v-tooltip="formatMessage(messages.deleteAllGroups)"
|
||||||
type="quiet"
|
type="base"
|
||||||
color="red"
|
|
||||||
:label="formatMessage(messages.deleteAllGroups)"
|
:label="formatMessage(messages.deleteAllGroups)"
|
||||||
:disabled="titleButtonsDisabled"
|
:disabled="titleButtonsDisabled"
|
||||||
|
class="[&:not(:disabled):focus-visible>svg]:!text-red [&:not(:disabled):hover>svg]:!text-red"
|
||||||
@click="showConfirmClearGroups"
|
@click="showConfirmClearGroups"
|
||||||
>
|
>
|
||||||
<TrashIcon v-if="!isClearing" aria-hidden="true" />
|
<TrashIcon v-if="!isClearing" aria-hidden="true" />
|
||||||
|
|||||||
@@ -3458,6 +3458,12 @@
|
|||||||
"project.actions.back-to-project-page": {
|
"project.actions.back-to-project-page": {
|
||||||
"message": "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": {
|
"project.actions.create-server": {
|
||||||
"message": "Create a server"
|
"message": "Create a server"
|
||||||
},
|
},
|
||||||
@@ -3743,9 +3749,24 @@
|
|||||||
"project.moderation.title": {
|
"project.moderation.title": {
|
||||||
"message": "Moderation"
|
"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": {
|
"project.navigation.changelog": {
|
||||||
"message": "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": {
|
"project.notification.icon-updated.message": {
|
||||||
"message": "Your project's icon has been updated."
|
"message": "Your project's icon has been updated."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -405,6 +405,36 @@
|
|||||||
:auth="auth"
|
:auth="auth"
|
||||||
:tags="tags"
|
: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
|
<Admonition
|
||||||
v-if="
|
v-if="
|
||||||
currentMember &&
|
currentMember &&
|
||||||
@@ -529,6 +559,7 @@ import {
|
|||||||
ClipboardCopyIcon,
|
ClipboardCopyIcon,
|
||||||
CompassIcon,
|
CompassIcon,
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
|
FileArchiveIcon,
|
||||||
FolderSearchIcon,
|
FolderSearchIcon,
|
||||||
HeartIcon,
|
HeartIcon,
|
||||||
LeftArrowIcon,
|
LeftArrowIcon,
|
||||||
@@ -663,6 +694,7 @@ const downloadModal = ref()
|
|||||||
const openInAppModal = ref()
|
const openInAppModal = ref()
|
||||||
const overTheTopDownloadAnimation = ref()
|
const overTheTopDownloadAnimation = ref()
|
||||||
const scanModal = ref()
|
const scanModal = ref()
|
||||||
|
const isCheckingModpackArchives = ref(false)
|
||||||
|
|
||||||
const projectV3Loaded = computed(() => !projectV3Pending.value || projectV3.value != null)
|
const projectV3Loaded = computed(() => !projectV3Pending.value || projectV3.value != null)
|
||||||
const isServerProject = computed(() => projectV3.value?.minecraft_server != null)
|
const isServerProject = computed(() => projectV3.value?.minecraft_server != null)
|
||||||
@@ -818,6 +850,35 @@ const messages = defineMessages({
|
|||||||
id: 'project.actions.rescan-modpack',
|
id: 'project.actions.rescan-modpack',
|
||||||
defaultMessage: '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: {
|
serversPromoDescription: {
|
||||||
id: 'project.actions.servers-promo.description',
|
id: 'project.actions.servers-promo.description',
|
||||||
defaultMessage: 'Modrinth Hosting is the easiest way to play with your friends without hassle!',
|
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)
|
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 versionsEnabled = ref(false)
|
||||||
const {
|
const {
|
||||||
data: versionsV3,
|
data: versionsV3,
|
||||||
@@ -1149,7 +1210,7 @@ const {
|
|||||||
apiVersion: 3,
|
apiVersion: 3,
|
||||||
}),
|
}),
|
||||||
staleTime: STALE_TIME_LONG,
|
staleTime: STALE_TIME_LONG,
|
||||||
enabled: computed(() => !!projectId.value && versionsEnabled.value),
|
enabled: computed(() => !!projectId.value && (versionsEnabled.value || isStaff(auth.value.user))),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Organization
|
// Organization
|
||||||
@@ -1749,6 +1810,90 @@ const showProjectHeaderCreateServerAction = computed(
|
|||||||
const projectHeaderCreateServerTo = computed(() =>
|
const projectHeaderCreateServerTo = computed(() =>
|
||||||
project.value ? `/hosting?project=${project.value.id}#plan` : '/hosting',
|
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 projectHeaderMoreActions = computed(() => {
|
||||||
const isStaff = !!(auth.value.user && tags.value.staffRoles.includes(auth.value.user.role))
|
const isStaff = !!(auth.value.user && tags.value.staffRoles.includes(auth.value.user.role))
|
||||||
|
|
||||||
@@ -1787,6 +1932,19 @@ const projectHeaderMoreActions = computed(() => {
|
|||||||
tone: 'orange',
|
tone: 'orange',
|
||||||
shown: !!auth.value.user && isStaff && project.value?.actualProjectType === 'modpack',
|
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 },
|
{ type: 'divider', shown: !!auth.value.user && isStaff },
|
||||||
{
|
{
|
||||||
id: 'report',
|
id: 'report',
|
||||||
|
|||||||
Reference in New Issue
Block a user