diff --git a/apps/renterd/components/Contracts/ContractsBulkMenu/ContractsRescanHosts.tsx b/apps/renterd/components/Contracts/ContractsBulkMenu/ContractsRescanHosts.tsx
new file mode 100644
index 000000000..930359bd4
--- /dev/null
+++ b/apps/renterd/components/Contracts/ContractsBulkMenu/ContractsRescanHosts.tsx
@@ -0,0 +1,15 @@
+import { useMemo } from 'react'
+import { useContracts } from '../../../contexts/contracts'
+import { BulkRescanHosts } from '../../bulkActions/BulkRescanHosts'
+
+export function ContractsRescanHosts() {
+ const { multiSelect } = useContracts()
+
+ const publicKeys = useMemo(
+ () =>
+ Object.entries(multiSelect.selection).map(([_, item]) => item.hostKey),
+ [multiSelect.selection]
+ )
+
+ return
+}
diff --git a/apps/renterd/components/Contracts/ContractsBulkMenu/index.tsx b/apps/renterd/components/Contracts/ContractsBulkMenu/index.tsx
index 3d26a754c..b9c1e7e11 100644
--- a/apps/renterd/components/Contracts/ContractsBulkMenu/index.tsx
+++ b/apps/renterd/components/Contracts/ContractsBulkMenu/index.tsx
@@ -5,6 +5,7 @@ import { ContractsAddBlocklist } from './ContractsAddBlocklist'
import { ContractsAddAllowlist } from './ContractsAddAllowlist'
import { ContractsRemoveBlocklist } from './ContractsRemoveBlocklist'
import { ContractsRemoveAllowlist } from './ContractsRemoveAllowlist'
+import { ContractsRescanHosts } from './ContractsRescanHosts'
export function ContractsBulkMenu() {
const { multiSelect } = useContracts()
@@ -19,6 +20,7 @@ export function ContractsBulkMenu() {
+
)
diff --git a/apps/renterd/components/Hosts/HostsBulkMenu/HostsRescan.tsx b/apps/renterd/components/Hosts/HostsBulkMenu/HostsRescan.tsx
new file mode 100644
index 000000000..e366f7a8b
--- /dev/null
+++ b/apps/renterd/components/Hosts/HostsBulkMenu/HostsRescan.tsx
@@ -0,0 +1,15 @@
+import { useMemo } from 'react'
+import { BulkRescanHosts } from '../../bulkActions/BulkRescanHosts'
+import { useHosts } from '../../../contexts/hosts'
+
+export function HostsRescan() {
+ const { multiSelect } = useHosts()
+
+ const publicKeys = useMemo(
+ () =>
+ Object.entries(multiSelect.selection).map(([_, item]) => item.publicKey),
+ [multiSelect.selection]
+ )
+
+ return
+}
diff --git a/apps/renterd/components/Hosts/HostsBulkMenu/index.tsx b/apps/renterd/components/Hosts/HostsBulkMenu/index.tsx
index c020341a6..5c5f5751d 100644
--- a/apps/renterd/components/Hosts/HostsBulkMenu/index.tsx
+++ b/apps/renterd/components/Hosts/HostsBulkMenu/index.tsx
@@ -5,6 +5,7 @@ import { HostsAddAllowlist } from './HostsAddAllowlist'
import { HostsRemoveBlocklist } from './HostsRemoveBlocklist'
import { HostsRemoveAllowlist } from './HostsRemoveAllowlist'
import { useHosts } from '../../../contexts/hosts'
+import { HostsRescan } from './HostsRescan'
export function HostsBulkMenu() {
const { multiSelect } = useHosts()
@@ -19,6 +20,7 @@ export function HostsBulkMenu() {
+
)
diff --git a/apps/renterd/components/bulkActions/BulkRescanHosts.tsx b/apps/renterd/components/bulkActions/BulkRescanHosts.tsx
new file mode 100644
index 000000000..0b58fa949
--- /dev/null
+++ b/apps/renterd/components/bulkActions/BulkRescanHosts.tsx
@@ -0,0 +1,52 @@
+import {
+ Button,
+ handleBatchOperation,
+ MultiSelect,
+ MultiSelectRow,
+} from '@siafoundation/design-system'
+import { DataView16 } from '@siafoundation/react-icons'
+import { useHostScan } from '@siafoundation/renterd-react'
+import { useCallback } from 'react'
+import { pluralize, secondsInMilliseconds } from '@siafoundation/units'
+
+export function BulkRescanHosts({
+ multiSelect,
+ publicKeys,
+}: {
+ multiSelect: MultiSelect
+ publicKeys: string[]
+}) {
+ const scan = useHostScan()
+ const scanAll = useCallback(async () => {
+ await handleBatchOperation(
+ publicKeys.map((publicKey) =>
+ scan.post({
+ params: {
+ hostkey: publicKey,
+ },
+ payload: {
+ timeout: secondsInMilliseconds(30),
+ },
+ })
+ ),
+ {
+ toastError: ({ successCount, errorCount, totalCount }) => ({
+ title: `Rescanning ${pluralize(successCount, 'host')}`,
+ body: `Error starting rescan for ${errorCount}/${totalCount} of total hosts.`,
+ }),
+ toastSuccess: ({ totalCount }) => ({
+ title: `Rescanning ${pluralize(totalCount, 'host')}`,
+ }),
+ after: () => {
+ multiSelect.deselectAll()
+ },
+ }
+ )
+ }, [multiSelect, publicKeys, scan])
+
+ return (
+
+ )
+}