From b46f6d0141054df04e4cbdb7d3d8fcb09dece700 Mon Sep 17 00:00:00 2001 From: "Calum H." Date: Mon, 12 Jan 2026 17:08:30 +0000 Subject: [PATCH] feat: moderation locking (#5070) * feat: base locking impl * feat: lock logic in place in rev endpoint + fetch rev * feat: frontend impl and finalize * feat: auto skip if using the moderation queue page * fix: qa issues * fix: async state + locking fix * fix: lint * fix: fmt * fix: qa issue * fix: qa + redirect bug * fix: lint * feat: delete all locks endpoint for admins * fix: dedupe * fix: fmt * fix: project redirect move to middleware * fix: lint --- apps/frontend/nuxt.config.ts | 2 +- .../ui/moderation/ModerationQueueCard.vue | 18 +- .../checklist/ModerationChecklist.vue | 1083 ++++++++++++----- .../middleware/project-canonicalize.global.ts | 56 + apps/frontend/src/pages/[type]/[id].vue | 24 +- apps/frontend/src/pages/moderation/index.vue | 101 +- .../server/routes/api/tags/game-versions.ts | 2 +- apps/frontend/src/server/utils/api-client.ts | 24 +- apps/frontend/src/store/moderation.ts | 87 ++ ...ca2de7f5b8b742c0196e71ab2139174fcc12f.json | 46 + ...25b98efbb2f38fb2d113c2f894481f41dc24c.json | 14 + ...38fecfa2d81d93bc72602d14be0c61c1195e5.json | 15 + ...cb8b78a9b5d935097391247b0c14fa25f1118.json | 12 + ...92147c88570ba9757a8204861187f0da7dbb1.json | 15 + ...5561f625e7edfadb21f89a41d0c16cd25763a.json | 15 + ...368931e5f711c6ce4e4af6b0ad523600da425.json | 12 + .../20260106225635_moderation_locks.sql | 8 + apps/labrinth/src/database/models/mod.rs | 3 + .../database/models/moderation_lock_item.rs | 163 +++ .../src/routes/internal/moderation/mod.rs | 244 +++- apps/labrinth/src/routes/v3/projects.rs | 21 +- 21 files changed, 1644 insertions(+), 321 deletions(-) create mode 100644 apps/frontend/src/middleware/project-canonicalize.global.ts create mode 100644 apps/labrinth/.sqlx/query-15ce2cf3154ba3358461b375504ca2de7f5b8b742c0196e71ab2139174fcc12f.json create mode 100644 apps/labrinth/.sqlx/query-420453c85418f57da3f89396b0625b98efbb2f38fb2d113c2f894481f41dc24c.json create mode 100644 apps/labrinth/.sqlx/query-47df2d46f068e3158387ac8928238fecfa2d81d93bc72602d14be0c61c1195e5.json create mode 100644 apps/labrinth/.sqlx/query-60ee89bff8241dd00a1aa33d072cb8b78a9b5d935097391247b0c14fa25f1118.json create mode 100644 apps/labrinth/.sqlx/query-72c050caf23ce0e7d9f2dbe2eca92147c88570ba9757a8204861187f0da7dbb1.json create mode 100644 apps/labrinth/.sqlx/query-834f4aca2ff23ccd041cd1028145561f625e7edfadb21f89a41d0c16cd25763a.json create mode 100644 apps/labrinth/.sqlx/query-e5a83a4d578f76e6e3298054960368931e5f711c6ce4e4af6b0ad523600da425.json create mode 100644 apps/labrinth/migrations/20260106225635_moderation_locks.sql create mode 100644 apps/labrinth/src/database/models/moderation_lock_item.rs diff --git a/apps/frontend/nuxt.config.ts b/apps/frontend/nuxt.config.ts index 2d925c73..86e5abaf 100644 --- a/apps/frontend/nuxt.config.ts +++ b/apps/frontend/nuxt.config.ts @@ -311,7 +311,7 @@ export default defineNuxtConfig({ compatibilityDate: '2025-01-01', telemetry: false, experimental: { - asyncContext: true, + asyncContext: isProduction(), }, }) diff --git a/apps/frontend/src/components/ui/moderation/ModerationQueueCard.vue b/apps/frontend/src/components/ui/moderation/ModerationQueueCard.vue index 2c8ecbb8..b013bbca 100644 --- a/apps/frontend/src/components/ui/moderation/ModerationQueueCard.vue +++ b/apps/frontend/src/components/ui/moderation/ModerationQueueCard.vue @@ -127,11 +127,9 @@ import dayjs from 'dayjs' import { computed } from 'vue' import type { ModerationProject } from '~/helpers/moderation' -import { useModerationStore } from '~/store/moderation.ts' const { addNotification } = injectNotificationManager() const formatRelativeTime = useRelativeTime() -const moderationStore = useModerationStore() const baseId = useId() @@ -139,6 +137,10 @@ const props = defineProps<{ queueEntry: ModerationProject }>() +const emit = defineEmits<{ + startFromProject: [projectId: string] +}>() + function getDaysQueued(date: Date): number { const now = new Date() const diff = now.getTime() - date.getTime() @@ -201,16 +203,6 @@ const quickActions: OverflowMenuOption[] = [ ] function openProjectForReview() { - moderationStore.setSingleProject(props.queueEntry.project.id) - navigateTo({ - name: 'type-id', - params: { - type: 'project', - id: props.queueEntry.project.slug || props.queueEntry.project.id, - }, - state: { - showChecklist: true, - }, - }) + emit('startFromProject', props.queueEntry.project.id) } diff --git a/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue b/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue index d3dece98..449dc3aa 100644 --- a/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue +++ b/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue @@ -3,7 +3,7 @@

@@ -25,7 +25,7 @@ - @@ -38,298 +38,381 @@
-
-
-

- You are done moderating this project! - -

+ +
+
+ + + This project + {{ lockStatus.expired ? 'was being' : 'is currently being' }} + moderated + + + + @{{ lockStatus.lockedBy.username }} + + + Lock expires in {{ lockTimeRemaining }} +
-
-
- - - - -