mirror of
https://github.com/modrinth/code.git
synced 2026-08-27 10:04:52 +00:00
feat: hosting access tab (#5995)
* feat: implement access tab with dummy data * fix: spacing * feat: qa * feat: implement backend * qa: qa pass * feat: fix user "search" * fix: lint * feat: change to bitfield * feat: fix fields * fix: lint * fix: lint * feat: hook up api * feat: fix permissions * feat: audit log table event start * feat: better mobile mode for audit log table * feat: i18n * feat: qa * feat: enforce permissions * feat: email template start * feat: qa * fix: tooltip bug * feat: qa * impl: sse support in api-client * feat: sse impl * fix: desync path * feat: time frame picker from analytics * feat: QA * fix: spacing * fix: permisison audit log entries * fix: hosting manage page shared server detection * fix: lint * feat: qa + lint * feat: audit log table sort by time * feat: finish frontend panel stuff * fix: lint * fix: backend alignment * fix: lint * fix: supress friend errors * feat: qa * fix: qa * fix: lint * fix: utils barrel * fix: safari cookies in dev * fix: pin nuxt * feat: fixes + notif fix * fix: notifications * feat: qa * fix: notification sync not happening immediately * fix: qa * fix: qa * feat: qa * blog + prepr * feat: toast shit * blog images * thumbnail update one last time * prepr * feat: use reinvite route * update images * fix: reinvite stuff * fix: lint * fix: alignment of save bar * fix: notif sizing * fix: split up access * fix: lint * fix: lint * fix: link --------- Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
@@ -23,6 +23,10 @@ function copy(id: string) {
|
||||
navigator.clipboard?.writeText(`/_internal/templates/email/${id}`).catch(() => {})
|
||||
}
|
||||
|
||||
function uncachedPreviewUrl(id: string) {
|
||||
return `/_internal/templates/email/${id}?preview=${Date.now()}`
|
||||
}
|
||||
|
||||
const previewModal = ref<{ hide: () => void; show: () => void } | null>(null)
|
||||
const previewTemplate = ref<string | null>(null)
|
||||
const previewLoading = ref(false)
|
||||
@@ -73,7 +77,7 @@ async function openPreview(id: string, event?: MouseEvent) {
|
||||
variableValues.value = {}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/_internal/templates/email/${id}`)
|
||||
const response = await fetch(uncachedPreviewUrl(id), { cache: 'no-store' })
|
||||
previewHtml.value = await response.text()
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -103,7 +107,7 @@ function openPopupPreview(id: string, offset = 0) {
|
||||
const left = window.screenX + (window.outerWidth - width) / 2 + ((offset * 28) % 320)
|
||||
const top = window.screenY + (window.outerHeight - height) / 2 + ((offset * 28) % 320)
|
||||
window.open(
|
||||
`/_internal/templates/email/${id}`,
|
||||
uncachedPreviewUrl(id),
|
||||
`email-${id}`,
|
||||
`popup=yes,width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes,menubar=no,toolbar=no,location=no,status=no`,
|
||||
)
|
||||
|
||||
@@ -218,7 +218,7 @@
|
||||
:level="notice.level"
|
||||
:message="notice.message"
|
||||
:dismissable="notice.dismissable"
|
||||
:title="notice.title"
|
||||
:title="notice.title ?? undefined"
|
||||
preview
|
||||
/>
|
||||
<div class="mt-4 flex items-center gap-2">
|
||||
@@ -260,6 +260,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { Archon } from '@modrinth/api-client'
|
||||
import { EditIcon, PlusIcon, SaveIcon, SettingsIcon, TrashIcon, XIcon } from '@modrinth/assets'
|
||||
import {
|
||||
ButtonStyled,
|
||||
@@ -267,6 +268,7 @@ import {
|
||||
commonMessages,
|
||||
CopyCode,
|
||||
defineMessages,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
NewModal,
|
||||
ServerNotice,
|
||||
@@ -278,14 +280,13 @@ import {
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { NOTICE_LEVELS } from '@modrinth/ui/src/utils/notices.ts'
|
||||
import type { ServerNotice as ServerNoticeType } from '@modrinth/utils'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import AssignNoticeModal from '~/components/ui/admin/AssignNoticeModal.vue'
|
||||
import { useServersFetch } from '~/composables/servers/servers-fetch.ts'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const client = injectModrinthClient()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
@@ -297,6 +298,8 @@ const formatDateTimeShortMonth = useFormatDateTime({
|
||||
dateStyle: 'medium',
|
||||
})
|
||||
|
||||
type ServerNoticeType = Archon.Notices.v0.ListedNotice
|
||||
|
||||
const notices = ref<ServerNoticeType[]>([])
|
||||
const createNoticeModal = ref<InstanceType<typeof NewModal>>()
|
||||
const assignNoticeModal = ref<InstanceType<typeof AssignNoticeModal>>()
|
||||
@@ -304,16 +307,14 @@ const assignNoticeModal = ref<InstanceType<typeof AssignNoticeModal>>()
|
||||
await refreshNotices()
|
||||
|
||||
async function refreshNotices() {
|
||||
await useServersFetch('notices').then((res) => {
|
||||
notices.value = res as ServerNoticeType[]
|
||||
notices.value.sort((a, b) => {
|
||||
const dateDiff = dayjs(b.announce_at).diff(dayjs(a.announce_at))
|
||||
if (dateDiff === 0) {
|
||||
return b.id - a.id
|
||||
}
|
||||
notices.value = await client.archon.notices_v0.list()
|
||||
notices.value.sort((a, b) => {
|
||||
const dateDiff = dayjs(b.announce_at).diff(dayjs(a.announce_at))
|
||||
if (dateDiff === 0) {
|
||||
return b.id - a.id
|
||||
}
|
||||
|
||||
return dateDiff
|
||||
})
|
||||
return dateDiff
|
||||
})
|
||||
}
|
||||
|
||||
@@ -347,7 +348,7 @@ function startEditing(notice: ServerNoticeType, assignments: boolean = false) {
|
||||
newNoticeLevel.value = levelOptions.find((x) => x.id === notice.level) ?? levelOptions[0]
|
||||
newNoticeDismissable.value = notice.dismissable
|
||||
newNoticeMessage.value = notice.message
|
||||
newNoticeTitle.value = notice.title
|
||||
newNoticeTitle.value = notice.title ?? undefined
|
||||
newNoticeScheduledDate.value = dayjs(notice.announce_at).format(DATE_TIME_FORMAT)
|
||||
newNoticeExpiresDate.value = notice.expires
|
||||
? dayjs(notice.expires).format(DATE_TIME_FORMAT)
|
||||
@@ -361,9 +362,8 @@ function startEditing(notice: ServerNoticeType, assignments: boolean = false) {
|
||||
}
|
||||
|
||||
async function deleteNotice(notice: ServerNoticeType) {
|
||||
await useServersFetch(`notices/${notice.id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
await client.archon.notices_v0
|
||||
.delete(notice.id)
|
||||
.then(() => {
|
||||
addNotification({
|
||||
title: `Successfully deleted notice #${notice.id}`,
|
||||
@@ -412,9 +412,10 @@ async function saveChanges() {
|
||||
return
|
||||
}
|
||||
|
||||
await useServersFetch(`notices/${editingNotice.value?.id}`, {
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
if (!editingNotice.value) return
|
||||
|
||||
await client.archon.notices_v0
|
||||
.update(editingNotice.value.id, {
|
||||
message: newNoticeMessage.value,
|
||||
title: newNoticeSurvey.value ? undefined : trimmedTitle.value,
|
||||
level: newNoticeLevel.value.id,
|
||||
@@ -425,14 +426,14 @@ async function saveChanges() {
|
||||
expires: newNoticeExpiresDate.value
|
||||
? dayjs(newNoticeExpiresDate.value).toISOString()
|
||||
: undefined,
|
||||
},
|
||||
}).catch((err) => {
|
||||
addNotification({
|
||||
title: 'Error saving changes to notice',
|
||||
text: err,
|
||||
type: 'error',
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
addNotification({
|
||||
title: 'Error saving changes to notice',
|
||||
text: err,
|
||||
type: 'error',
|
||||
})
|
||||
})
|
||||
await refreshNotices()
|
||||
createNoticeModal.value?.hide()
|
||||
}
|
||||
@@ -442,9 +443,8 @@ async function createNotice() {
|
||||
return
|
||||
}
|
||||
|
||||
await useServersFetch('notices', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
await client.archon.notices_v0
|
||||
.create({
|
||||
message: newNoticeMessage.value,
|
||||
title: newNoticeSurvey.value ? undefined : trimmedTitle.value,
|
||||
level: newNoticeLevel.value.id,
|
||||
@@ -455,14 +455,14 @@ async function createNotice() {
|
||||
expires: newNoticeExpiresDate.value
|
||||
? dayjs(newNoticeExpiresDate.value).toISOString()
|
||||
: undefined,
|
||||
},
|
||||
}).catch((err) => {
|
||||
addNotification({
|
||||
title: 'Error creating notice',
|
||||
text: err,
|
||||
type: 'error',
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
addNotification({
|
||||
title: 'Error creating notice',
|
||||
text: err,
|
||||
type: 'error',
|
||||
})
|
||||
})
|
||||
await refreshNotices()
|
||||
createNoticeModal.value?.hide()
|
||||
}
|
||||
|
||||
@@ -108,11 +108,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Archon } from '@modrinth/api-client'
|
||||
import { PlusIcon, XCircleIcon } from '@modrinth/assets'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
ConfirmModal,
|
||||
injectModrinthClient,
|
||||
injectNotificationManager,
|
||||
Pagination,
|
||||
TagItem,
|
||||
@@ -124,38 +126,16 @@ import dayjs from 'dayjs'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import TransferModal from '~/components/ui/admin/TransferModal.vue'
|
||||
import { useServersFetch } from '~/composables/servers/servers-fetch.ts'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const client = injectModrinthClient()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
// Types
|
||||
interface ProvisionOptions {
|
||||
region?: string | null
|
||||
node_tags?: string[]
|
||||
}
|
||||
|
||||
interface TransferBatch {
|
||||
id: number
|
||||
created_by: string
|
||||
created_at: string
|
||||
reason: string | null
|
||||
scheduled_at: string
|
||||
cancelled: boolean
|
||||
log_count: number
|
||||
provision_options: ProvisionOptions
|
||||
}
|
||||
|
||||
interface HistoryResponse {
|
||||
batches: TransferBatch[]
|
||||
total: number
|
||||
page: number
|
||||
page_size: number
|
||||
}
|
||||
type TransferBatch = Archon.Transfers.Internal.TransferLogBatchEntry
|
||||
|
||||
const transferModal = ref<InstanceType<typeof TransferModal>>()
|
||||
const cancelModal = ref<InstanceType<typeof ConfirmModal>>()
|
||||
@@ -178,10 +158,10 @@ async function refreshHistory() {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const data = await useServersFetch<HistoryResponse>(
|
||||
`/transfers/history?page=${currentPage.value}&page_size=${pageSize}`,
|
||||
{ version: 'internal' },
|
||||
)
|
||||
const data = await client.archon.transfers_internal.history({
|
||||
page: currentPage.value,
|
||||
page_size: pageSize,
|
||||
})
|
||||
batches.value = data.batches || []
|
||||
total.value = data.total || 0
|
||||
|
||||
@@ -283,12 +263,8 @@ function showCancelModal(batchId: number) {
|
||||
async function confirmCancel() {
|
||||
if (!cancellingBatchId.value) return
|
||||
try {
|
||||
await useServersFetch('/transfers/cancel', {
|
||||
version: 'internal',
|
||||
method: 'POST',
|
||||
body: {
|
||||
batch_ids: [cancellingBatchId.value],
|
||||
},
|
||||
await client.archon.transfers_internal.cancel({
|
||||
batch_ids: [cancellingBatchId.value],
|
||||
})
|
||||
addNotification({
|
||||
title: 'Transfer cancelled',
|
||||
|
||||
Reference in New Issue
Block a user