diff --git a/apps/frontend/src/components/ui/moderation/ModpackScanModal.vue b/apps/frontend/src/components/ui/moderation/ModpackScanModal.vue
new file mode 100644
index 0000000000..42ecfad45d
--- /dev/null
+++ b/apps/frontend/src/components/ui/moderation/ModpackScanModal.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+ Modpack Scan ({{ scans.size }}/{{ files.length }} Files)
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Pack File Name |
+ New Files |
+ New Groups |
+
+
+
+
+
+ |
+ {{ file.filename }}
+ |
+
+
+ ...
+ {{ scans.get(file.id!!)?.new_attribution_files!!! }}
+ |
+
+
+ ...
+ {{ scans.get(file.id!!)?.new_attribution_groups!!! }}
+ |
+
+
+
+
+
+ Override Files ({{ scans.get(file.id!!)?.scanned_file_names?.length || 0 }})
+
+
+
+ {{ name }}
+
+
+
+ |
+
+
+
+
+
+
+
diff --git a/apps/frontend/src/pages/[type]/[project].vue b/apps/frontend/src/pages/[type]/[project].vue
index 7cde8e8cdd..b21380e860 100644
--- a/apps/frontend/src/pages/[type]/[project].vue
+++ b/apps/frontend/src/pages/[type]/[project].vue
@@ -95,6 +95,8 @@
@download="triggerDownloadAnimation"
/>
+
+
{{ formatMessage(messages.reviewProject) }}
Tech review
+
+ Rescan modpack
+
{{ formatMessage(commonMessages.reportButton) }}
@@ -726,6 +734,7 @@ import {
ClipboardCopyIcon,
DownloadIcon,
ExternalIcon,
+ FolderSearchIcon,
HeartIcon,
ListIcon,
MoreVerticalIcon,
@@ -785,6 +794,7 @@ import CollectionCreateModal from '~/components/ui/create/CollectionCreateModal.
import MessageBanner from '~/components/ui/MessageBanner.vue'
import ModerationChecklist from '~/components/ui/moderation/checklist/ModerationChecklist.vue'
import ModerationProjectNags from '~/components/ui/moderation/ModerationProjectNags.vue'
+import ModpackScanModal from '~/components/ui/moderation/ModpackScanModal.vue'
import ProjectDownloadModal from '~/components/ui/ProjectDownloadModal/index.vue'
import ProjectMemberHeader from '~/components/ui/ProjectMemberHeader.vue'
import { getSignInRouteObj } from '~/composables/auth.ts'
@@ -852,6 +862,7 @@ const debug = useDebugLogger('DownloadModal')
const downloadModal = ref()
const openInAppModal = ref()
const overTheTopDownloadAnimation = ref()
+const scanModal = ref()
const projectV3Loaded = computed(() => !projectV3Pending.value || projectV3.value != null)
const isServerProject = computed(() => projectV3.value?.minecraft_server != null)
diff --git a/packages/api-client/src/modules/labrinth/attribution/internal.ts b/packages/api-client/src/modules/labrinth/attribution/internal.ts
index 36773c77e1..1ee316ecf6 100644
--- a/packages/api-client/src/modules/labrinth/attribution/internal.ts
+++ b/packages/api-client/src/modules/labrinth/attribution/internal.ts
@@ -100,4 +100,21 @@ export class LabrinthAttributionInternalModule extends AbstractModule {
body,
})
}
+
+ /**
+ * Scan a file for attribution information.
+ * POST /_internal/attribution/file/{file_id}/scan
+ *
+ * @param fileId - The file ID to scan.
+ */
+ public async scanFile(fileId: string): Promise {
+ return this.client.request(
+ `/attribution/file/${fileId}/scan`,
+ {
+ api: 'labrinth',
+ version: 'internal',
+ method: 'POST',
+ },
+ )
+ }
}
diff --git a/packages/api-client/src/modules/labrinth/types.ts b/packages/api-client/src/modules/labrinth/types.ts
index bd0c90d20a..056508282b 100644
--- a/packages/api-client/src/modules/labrinth/types.ts
+++ b/packages/api-client/src/modules/labrinth/types.ts
@@ -397,6 +397,12 @@ export namespace Labrinth {
sha1: string
project_id: string
}
+
+ export type FileScanResponse = {
+ new_attribution_groups: number
+ new_attribution_files: number
+ scanned_file_names: string[]
+ }
}
}
@@ -1333,6 +1339,7 @@ export namespace Labrinth {
}
export type VersionFile = {
+ id?: string
hashes: VersionFileHash
url: string
filename: string
@@ -1454,6 +1461,7 @@ export namespace Labrinth {
}
export interface VersionFile {
+ id?: string
hashes: VersionFileHash
url: string
filename: string