Skip to content

Commit 0d790e6

Browse files
committed
refactor(renterd): migrate hosts apis
1 parent 0950598 commit 0d790e6

23 files changed

+375
-279
lines changed

.changeset/early-bobcats-fry.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'renterd': patch
3+
'@siafoundation/renterd-react': patch
4+
---
5+
6+
Fixed a bug optimistically updating last scan information when initiating a host scan.

.changeset/eighty-eagles-taste.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'renterd': minor
3+
---
4+
5+
The hosts explorer now uses the new combined hosts API.

.changeset/fair-carrots-reflect.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@siafoundation/renterd-js': minor
3+
'@siafoundation/renterd-react': minor
4+
'@siafoundation/renterd-types': minor
5+
---
6+
7+
Added the bus list autopilots API.

.changeset/late-onions-hammer.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@siafoundation/renterd-js': minor
3+
'@siafoundation/renterd-react': minor
4+
'@siafoundation/renterd-types': minor
5+
---
6+
7+
Added new combined hosts API.

.changeset/tiny-mails-guess.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@siafoundation/renterd-js': minor
3+
'@siafoundation/renterd-react': minor
4+
'@siafoundation/renterd-types': minor
5+
---
6+
7+
Removed deprecated search hosts and autopilot hosts APIs.
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { test, expect } from '@playwright/test'
2+
import { navigateToHosts } from '../fixtures/navigate'
3+
import { afterTest, beforeTest } from '../fixtures/beforeTest'
4+
import { getHostRowByIndex } from '../fixtures/hosts'
5+
6+
test.beforeEach(async ({ page }) => {
7+
await beforeTest(page, {
8+
hostdCount: 3,
9+
})
10+
})
11+
12+
test.afterEach(async () => {
13+
await afterTest()
14+
})
15+
16+
test('hosts explorer shows all hosts', async ({ page }) => {
17+
await navigateToHosts({ page })
18+
19+
const row1 = await getHostRowByIndex(page, 0)
20+
const row2 = await getHostRowByIndex(page, 1)
21+
const row3 = await getHostRowByIndex(page, 2)
22+
await expect(row1).toBeVisible()
23+
await expect(row2).toBeVisible()
24+
await expect(row3).toBeVisible()
25+
})

apps/renterd/contexts/config/useOnValid.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { transformUp } from './transformUp'
1414
import { delay, useMutate } from '@siafoundation/react-core'
1515
import { Resources } from './resources'
1616
import { useSyncContractSet } from './useSyncContractSet'
17-
import { autopilotHostsRoute } from '@siafoundation/renterd-types'
17+
import { busHostsRoute } from '@siafoundation/renterd-types'
1818

1919
export function useOnValid({
2020
resources,
@@ -133,9 +133,9 @@ export function useOnValid({
133133
if (firstTimeSettingConfig) {
134134
const refreshHostsAfterDelay = async () => {
135135
await delay(5_000)
136-
mutate((key) => key.startsWith(autopilotHostsRoute))
136+
mutate((key) => key.startsWith(busHostsRoute))
137137
await delay(5_000)
138-
mutate((key) => key.startsWith(autopilotHostsRoute))
138+
mutate((key) => key.startsWith(busHostsRoute))
139139
}
140140
refreshHostsAfterDelay()
141141
}

apps/renterd/contexts/hosts/columns.tsx

+10-11
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import { format, formatDistance, formatRelative } from 'date-fns'
2121
import { HostContextMenu } from '../../components/Hosts/HostContextMenu'
2222
import { useWorkflows } from '@siafoundation/react-core'
2323
import {
24-
AutopilotHost,
24+
HostPriceTable,
25+
HostSettings,
2526
RhpScanPayload,
2627
workerRhpScanRoute,
2728
} from '@siafoundation/renterd-types'
@@ -135,15 +136,15 @@ export const columns: HostsTableColumn[] = (
135136
return (
136137
<Tooltip
137138
side="right"
138-
content={data.usable ? 'Host is usable' : 'Host is not usable'}
139+
content={data.isUsable ? 'Host is usable' : 'Host is not usable'}
139140
>
140141
<div className="flex gap-2 items-center">
141142
<div className="mt-[5px]">
142143
<Text
143-
aria-label={data.usable ? 'usable' : 'not usable'}
144-
color={data.usable ? 'green' : 'red'}
144+
aria-label={data.isUsable ? 'usable' : 'not usable'}
145+
color={data.isUsable ? 'green' : 'red'}
145146
>
146-
{data.usable ? (
147+
{data.isUsable ? (
147148
<CheckboxCheckedFilled16 />
148149
) : (
149150
<WarningSquareFilled16 />
@@ -182,15 +183,15 @@ export const columns: HostsTableColumn[] = (
182183
<Tooltip
183184
side="right"
184185
content={
185-
data.gouging
186+
data.isGouging
186187
? 'Host is price gouging'
187188
: 'Host is not price gouging'
188189
}
189190
>
190191
<div className="flex gap-2 items-center">
191192
<div className="mt-[5px]">
192-
<Text color={!data.gouging ? 'subtle' : 'red'}>
193-
{!data.gouging ? (
193+
<Text color={!data.isGouging ? 'subtle' : 'red'}>
194+
{!data.isGouging ? (
194195
<UndefinedFilled16 />
195196
) : (
196197
<WarningSquareFilled16 />
@@ -1078,9 +1079,7 @@ function getFullLabelAndTip(col: HostsTableColumn): {
10781079
}
10791080
}
10801081

1081-
type Key =
1082-
| keyof AutopilotHost['host']['priceTable']
1083-
| keyof AutopilotHost['host']['settings']
1082+
type Key = keyof HostPriceTable | keyof HostSettings
10841083

10851084
function makeRenderSc(section: 'priceTable' | 'settings', name: Key) {
10861085
return memo(function RenderPriceTableNumber({ data }: { data: HostData }) {

apps/renterd/contexts/hosts/dataset.ts

+122-66
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,28 @@
11
import { useMemo } from 'react'
22
import BigNumber from 'bignumber.js'
33
import { HostData } from './types'
4-
import { Host } from '@siafoundation/renterd-types'
4+
import { Host, HostAutopilotChecks } from '@siafoundation/renterd-types'
55
import {
6-
useAutopilotHostsSearch,
76
useHostsAllowlist,
87
useHostsBlocklist,
9-
useHostsSearch,
8+
useHosts,
109
} from '@siafoundation/renterd-react'
1110
import { ContractData } from '../contracts/types'
12-
import { useApp } from '../app'
1311
import { SiaCentralHost } from '@siafoundation/sia-central-types'
12+
import { objectEntries } from '@siafoundation/design-system'
1413

1514
export function useDataset({
16-
autopilotStatus,
17-
regularResponse,
18-
autopilotResponse,
15+
response,
1916
allContracts,
17+
autopilotID,
2018
allowlist,
2119
blocklist,
2220
isAllowlistActive,
2321
geoHosts,
2422
onHostSelect,
2523
}: {
26-
autopilotStatus: ReturnType<typeof useApp>['autopilot']['status']
27-
regularResponse: ReturnType<typeof useHostsSearch>
28-
autopilotResponse: ReturnType<typeof useAutopilotHostsSearch>
24+
response: ReturnType<typeof useHosts>
25+
autopilotID: string
2926
allContracts: ContractData[]
3027
allowlist: ReturnType<typeof useHostsAllowlist>
3128
blocklist: ReturnType<typeof useHostsBlocklist>
@@ -34,51 +31,28 @@ export function useDataset({
3431
onHostSelect: (publicKey: string, location?: [number, number]) => void
3532
}) {
3633
return useMemo<HostData[] | null>(() => {
37-
if (autopilotStatus === 'off') {
38-
return (
39-
regularResponse.data?.map((host) => {
40-
const sch = geoHosts.find((gh) => gh.public_key === host.publicKey)
41-
return {
42-
onClick: () => onHostSelect(host.publicKey, sch?.location),
43-
...getHostFields(host, allContracts),
44-
...getAllowedFields({
45-
host,
46-
allowlist: allowlist.data,
47-
blocklist: blocklist.data,
48-
isAllowlistActive,
49-
}),
50-
...getAutopilotFields(),
51-
location: sch?.location,
52-
countryCode: sch?.country_code,
53-
}
54-
}) || null
55-
)
56-
} else if (autopilotStatus === 'on') {
57-
return (
58-
autopilotResponse.data?.map((ah) => {
59-
const sch = geoHosts.find((gh) => gh.public_key === ah.host.publicKey)
60-
return {
61-
onClick: () => onHostSelect(ah.host.publicKey, sch?.location),
62-
...getHostFields(ah.host, allContracts),
63-
...getAllowedFields({
64-
host: ah.host,
65-
allowlist: allowlist.data,
66-
blocklist: blocklist.data,
67-
isAllowlistActive,
68-
}),
69-
...getAutopilotFields(ah.checks),
70-
location: sch?.location,
71-
countryCode: sch?.country_code,
72-
}
73-
}) || null
74-
)
75-
}
76-
return null
34+
return (
35+
response.data?.map((host) => {
36+
const sch = geoHosts.find((gh) => gh.public_key === host.publicKey)
37+
return {
38+
onClick: () => onHostSelect(host.publicKey, sch?.location),
39+
...getHostFields(host, allContracts),
40+
...getAllowedFields({
41+
host,
42+
allowlist: allowlist.data,
43+
blocklist: blocklist.data,
44+
isAllowlistActive,
45+
}),
46+
...getAutopilotFields(host.checks?.[autopilotID]),
47+
location: sch?.location,
48+
countryCode: sch?.country_code,
49+
}
50+
}) || null
51+
)
7752
}, [
7853
onHostSelect,
79-
autopilotStatus,
80-
regularResponse.data,
81-
autopilotResponse.data,
54+
autopilotID,
55+
response.data,
8256
allContracts,
8357
allowlist.data,
8458
blocklist.data,
@@ -160,25 +134,65 @@ function getAllowedFields({
160134

161135
function getAutopilotFields(ahc?: {
162136
score: number
163-
gougingBreakdown: {
164-
contractErr?: string
165-
downloadErr?: string
166-
gougingErr?: string
167-
uploadErr?: string
168-
}
169-
gouging: boolean
137+
usable: boolean
170138
scoreBreakdown: {
171139
age: number
172140
collateral: number
173141
interactions: number
174-
prices: number
175142
storageRemaining: number
143+
prices: number
176144
uptime: number
177145
version: number
178146
}
147+
gougingBreakdown: {
148+
contractErr?: string
149+
downloadErr?: string
150+
gougingErr?: string
151+
uploadErr?: string
152+
pruneErr?: string
153+
}
154+
usabilityBreakdown: {
155+
blocked: boolean
156+
gouging: boolean
157+
lowScore: boolean
158+
notAcceptingContracts: boolean
159+
notAnnounced: boolean
160+
notCompletingScan: boolean
161+
offline: boolean
162+
redundantIP: boolean
163+
}
164+
}): {
165+
score: BigNumber
166+
scoreBreakdown: {
167+
age: BigNumber
168+
collateral: BigNumber
169+
interactions: BigNumber
170+
prices: BigNumber
171+
storageRemaining: BigNumber
172+
uptime: BigNumber
173+
version: BigNumber
174+
}
175+
isGouging: boolean
176+
isUsable: boolean
177+
gougingBreakdown: {
178+
contractErr?: string
179+
downloadErr?: string
180+
gougingErr?: string
181+
uploadErr?: string
182+
pruneErr?: string
183+
}
184+
usabilityBreakdown: {
185+
blocked: boolean
186+
gouging: boolean
187+
lowScore: boolean
188+
notAcceptingContracts: boolean
189+
notAnnounced: boolean
190+
notCompletingScan: boolean
191+
offline: boolean
192+
redundantIP: boolean
193+
}
179194
unusableReasons: string[]
180-
usable: boolean
181-
}) {
195+
} {
182196
return {
183197
score: new BigNumber(ahc?.score || 0),
184198
scoreBreakdown: {
@@ -192,9 +206,51 @@ function getAutopilotFields(ahc?: {
192206
uptime: new BigNumber(ahc?.scoreBreakdown.uptime || 0),
193207
version: new BigNumber(ahc?.scoreBreakdown.version || 0),
194208
},
209+
isGouging: Object.values(ahc?.gougingBreakdown || {}).some((v) => v),
210+
isUsable: !!ahc?.usable,
195211
gougingBreakdown: ahc?.gougingBreakdown || {},
196-
gouging: ahc?.gouging,
197-
unusableReasons: ahc?.unusableReasons || [],
198-
usable: ahc?.usable,
212+
usabilityBreakdown: ahc?.usabilityBreakdown || {
213+
blocked: false,
214+
gouging: false,
215+
lowScore: false,
216+
notAcceptingContracts: false,
217+
notAnnounced: false,
218+
notCompletingScan: false,
219+
offline: false,
220+
redundantIP: false,
221+
},
222+
unusableReasons: ahc
223+
? objectEntries(ahc.usabilityBreakdown).reduce((acc, [key, value]) => {
224+
if (value) {
225+
return acc.concat(getUnusableReasonLabel(key))
226+
}
227+
return acc
228+
}, [])
229+
: [],
230+
}
231+
}
232+
233+
function getUnusableReasonLabel(
234+
key: keyof HostAutopilotChecks['usabilityBreakdown']
235+
): string {
236+
switch (key) {
237+
case 'blocked':
238+
return 'Host is blocked'
239+
case 'gouging':
240+
return 'Host is gouging'
241+
case 'lowScore':
242+
return 'Host has low score'
243+
case 'notAcceptingContracts':
244+
return 'Host is not accepting contracts'
245+
case 'notAnnounced':
246+
return 'Host is not announced'
247+
case 'notCompletingScan':
248+
return 'Host is not completing scan'
249+
case 'offline':
250+
return 'Host is offline'
251+
case 'redundantIP':
252+
return 'Host has redundant IP'
253+
default:
254+
return 'Unknown'
199255
}
200256
}

0 commit comments

Comments
 (0)