fix: allow admin role to by pass error validator check

This commit is contained in:
tdgao
2026-08-26 13:27:47 -06:00
parent 621d189698
commit 89b392dc3d
8 changed files with 61 additions and 37 deletions
@@ -48,7 +48,7 @@ import {
usePageLeaveSafety,
useSavable,
} from '@modrinth/ui'
import { TeamMemberPermission } from '@modrinth/utils'
import { isAdmin, TeamMemberPermission } from '@modrinth/utils'
import { computed, useTemplateRef } from 'vue'
import AiImageWarningModal from '~/components/ui/AiImageWarningModal.vue'
@@ -81,11 +81,13 @@ const {
const { confirmLeaveModal } = usePageLeaveSafety(hasChanges)
const isAdminUser = computed(() => isAdmin(currentMember.value?.user))
const hasPermission = computed(
() =>
!!currentMember.value &&
(currentMember.value.permissions & TeamMemberPermission.EDIT_BODY) ===
TeamMemberPermission.EDIT_BODY,
isAdminUser.value ||
(!!currentMember.value &&
(currentMember.value.permissions & TeamMemberPermission.EDIT_BODY) ===
TeamMemberPermission.EDIT_BODY),
)
const { pending: descriptionLinksPending, validation: descriptionValidation } =
useProjectDescriptionValidation(() => current.value.description)
@@ -93,7 +95,9 @@ const hasValidationIssues = computed(() =>
descriptionValidation.value.some((validation) => validation.severity === 'error'),
)
const canSave = computed(
() => hasPermission.value && !hasValidationIssues.value && !descriptionLinksPending.value,
() =>
hasPermission.value &&
(isAdminUser.value || (!hasValidationIssues.value && !descriptionLinksPending.value)),
)
async function save() {
@@ -15,7 +15,7 @@ import {
useSavable,
useVIntl,
} from '@modrinth/ui'
import { isStaff, TeamMemberPermission } from '@modrinth/utils'
import { isAdmin, isStaff, TeamMemberPermission } from '@modrinth/utils'
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { computed, watch } from 'vue'
@@ -172,8 +172,11 @@ function resolveUpdatedBy(userId: string | null | undefined): DisclosureUpdatedB
)
}
const isAdminUser = computed(() => isAdmin(currentMember.value?.user))
const hasPermission = computed(
() => !!((currentMember.value?.permissions ?? 0) & TeamMemberPermission.EDIT_DETAILS),
() =>
isAdminUser.value ||
!!((currentMember.value?.permissions ?? 0) & TeamMemberPermission.EDIT_DETAILS),
)
const {
@@ -249,7 +252,9 @@ watch(
const issues = computed(() => getDisclosureFormIssues(current.value, projectTypes.value))
const canSave = computed(() => hasPermission.value && issues.value.length === 0)
const canSave = computed(
() => hasPermission.value && (isAdminUser.value || issues.value.length === 0),
)
const saveDisabledReason = computed(() => {
if (!hasPermission.value) {
@@ -13,6 +13,7 @@ import {
useSavable,
useVIntl,
} from '@modrinth/ui'
import { isAdmin } from '@modrinth/utils'
import ValidationMessage from '~/components/ValidationMessage.vue'
import SlugSuggestions from '~/components/ui/SlugSuggestions.vue'
@@ -27,7 +28,7 @@ import {
const { formatMessage } = useVIntl()
const { allMembers, projectV2: project, patchProject } = injectProjectPageContext()
const { allMembers, currentMember, projectV2: project, patchProject } = injectProjectPageContext()
useProjectSettingsHeadTitle(commonProjectSettingsMessages.general)
@@ -61,7 +62,10 @@ const taglineValidation = useProjectSummaryValidation(
() => current.value.tagline,
() => current.value.title,
)
const canSave = computed(() => !titleValidation.value && !taglineValidation.value)
const isAdminUser = computed(() => isAdmin(currentMember.value?.user))
const canSave = computed(
() => isAdminUser.value || (!titleValidation.value && !taglineValidation.value),
)
const {
onFocusIn: onSlugSuggestionFocusIn,
onFocusOut: onSlugSuggestionFocusOut,
@@ -327,7 +327,7 @@ import {
usePageLeaveSafety,
useVIntl,
} from '@modrinth/ui'
import { fileIsValid, formatProjectStatus } from '@modrinth/utils'
import { fileIsValid, formatProjectStatus, isAdmin } from '@modrinth/utils'
import AiImageWarningModal from '~/components/ui/AiImageWarningModal.vue'
import SlugSuggestions from '~/components/ui/SlugSuggestions.vue'
@@ -422,9 +422,12 @@ const bannerFile = ref(null)
const bannerGalleryImage = computed(() =>
project.value.gallery?.find((img) => img.name === MC_SERVER_BANNER_NAME),
)
const isAdminUser = computed(() => isAdmin(currentMember.value?.user))
const hasPermission = computed(() => {
const EDIT_DETAILS = 1 << 2
return ((currentMember.value?.permissions ?? 0) & EDIT_DETAILS) === EDIT_DETAILS
return (
isAdminUser.value || ((currentMember.value?.permissions ?? 0) & EDIT_DETAILS) === EDIT_DETAILS
)
})
const nameValidation = useProjectTitleValidation(name)
@@ -162,7 +162,7 @@ import {
usePageLeaveSafety,
useSavable,
} from '@modrinth/ui'
import { builtinLicenses, formatProjectType, TeamMemberPermission } from '@modrinth/utils'
import { builtinLicenses, formatProjectType, isAdmin, TeamMemberPermission } from '@modrinth/utils'
import { computed } from 'vue'
import ValidationMessage from '@/components/ValidationMessage.vue'
@@ -250,19 +250,23 @@ const selectedLicense = computed({
},
})
const hasPermission = computed(() => {
return (currentMember.value?.permissions ?? 0) & TeamMemberPermission.EDIT_DETAILS
})
const isAdminUser = computed(() => isAdmin(currentMember.value?.user))
const hasPermission = computed(
() =>
isAdminUser.value ||
Boolean((currentMember.value?.permissions ?? 0) & TeamMemberPermission.EDIT_DETAILS),
)
const canSave = computed(
() =>
Boolean(hasPermission.value) &&
!(
current.value.license.friendly === 'Custom' &&
(current.value.license.short === '' || current.value.licenseUrl === '')
) &&
effectiveLicenseCheck.value?.severity !== 'error' &&
!isLinkCheckPending(licenseContext.value),
hasPermission.value &&
(isAdminUser.value ||
(!(
current.value.license.friendly === 'Custom' &&
(current.value.license.short === '' || current.value.licenseUrl === '')
) &&
effectiveLicenseCheck.value?.severity !== 'error' &&
!isLinkCheckPending(licenseContext.value))),
)
async function save() {
@@ -213,6 +213,7 @@ import {
usePageLeaveSafety,
useSavable,
} from '@modrinth/ui'
import { isAdmin } from '@modrinth/utils'
import ValidationMessage from '@/components/ValidationMessage.vue'
@@ -362,9 +363,11 @@ watch(
{ deep: true, immediate: true },
)
const isAdminUser = computed(() => isAdmin(currentMember.value?.user))
const hasPermission = computed(() => {
const EDIT_DETAILS = 1 << 2
return (currentMember.value?.permissions & EDIT_DETAILS) === EDIT_DETAILS
return isAdminUser.value || (currentMember.value?.permissions & EDIT_DETAILS) === EDIT_DETAILS
})
function donationsMapFromLinkUrls(linkUrls) {
@@ -443,6 +446,9 @@ const patchData = computed(() => {
})
const canSave = computed(() => {
if (!hasPermission.value || Object.keys(patchData.value).length === 0) return false
if (isAdminUser.value) return true
const checks = isServerProject.value
? [siteCheck, storeCheck, wikiCheck, discordInviteCheck]
: [issuesCheck, sourceCheck, wikiCheck, discordInviteCheck]
@@ -461,14 +467,7 @@ const canSave = computed(() => {
(donationCheckTimers.size > 0 ||
donationLinks.value.some((row) => isLinkCheckPending(donationContext(row))))
return (
hasPermission.value &&
!fieldsInvalid &&
!fieldsPending &&
!donationsInvalid &&
!donationsPending &&
Object.keys(patchData.value).length > 0
)
return !fieldsInvalid && !fieldsPending && !donationsInvalid && !donationsPending
})
const saving = ref(false)
@@ -186,6 +186,7 @@ import {
usePageLeaveSafety,
useVIntl,
} from '@modrinth/ui'
import { isAdmin } from '@modrinth/utils'
import CompatibilityCard from '~/components/ui/project-settings/CompatibilityCard.vue'
@@ -298,9 +299,12 @@ watch(javaAddress, () => {
}, 500)
})
const isAdminUser = computed(() => isAdmin(currentMember.value?.user))
const hasPermission = computed(() => {
const EDIT_DETAILS = 1 << 2
return ((currentMember.value?.permissions ?? 0) & EDIT_DETAILS) === EDIT_DETAILS
return (
isAdminUser.value || ((currentMember.value?.permissions ?? 0) & EDIT_DETAILS) === EDIT_DETAILS
)
})
async function pingJavaServer() {
@@ -478,7 +482,7 @@ function resetChanges() {
}
async function handleSave() {
if (javaAddress.value.trim() && !javaPingResult.value?.online) {
if (!isAdminUser.value && javaAddress.value.trim() && !javaPingResult.value?.online) {
addNotification({
title: formatMessage(messages.cannotSaveTitle),
text: formatMessage(messages.cannotSaveText),
@@ -19,7 +19,7 @@ import {
useSavable,
useVIntl,
} from '@modrinth/ui'
import { capitalizeString, sortedCategories } from '@modrinth/utils'
import { capitalizeString, isAdmin, sortedCategories } from '@modrinth/utils'
import { computed } from 'vue'
interface Category {
@@ -145,7 +145,7 @@ const groupDescriptionMessages: Record<string, MessageDescriptor> = {
'performance impact': messages.performanceImpactDescription,
}
const { projectV2: project, projectV3, patchProject } = injectProjectPageContext()
const { currentMember, projectV2: project, projectV3, patchProject } = injectProjectPageContext()
useProjectSettingsHeadTitle(messages.title)
@@ -310,7 +310,8 @@ const isFeaturedLimitReached = computed(
() => current.value.featuredTags.length >= MAX_FEATURED_TAGS,
)
const canSave = computed(() => current.value.featuredTags.length > 0)
const isAdminUser = computed(() => isAdmin(currentMember.value?.user))
const canSave = computed(() => isAdminUser.value || current.value.featuredTags.length > 0)
const tooManyTagsWarning = computed(() => {
const tagCount = current.value.selectedTags.length