Skip to content

Commit 1fd0a57

Browse files
frontend: Use new disk speed test API
Signed-off-by: Patrick José Pereira <patrickelectric@gmail.com>
1 parent 4fc5d1d commit 1fd0a57

File tree

3 files changed

+126
-29
lines changed

3 files changed

+126
-29
lines changed

core/frontend/src/store/disk.ts

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import {
33
} from 'vuex-module-decorators'
44

55
import store from '@/store'
6-
import { DiskSpeedResult, DiskUsageQuery, DiskUsageResponse } from '@/types/disk'
6+
import { DiskSpeedResult, DiskSpeedTestPoint, DiskUsageQuery, DiskUsageResponse } from '@/types/disk'
77
import back_axios, { isBackendOffline } from '@/utils/api'
8+
import { parseStreamingResponse } from '@/utils/streaming'
89

910
@Module({ dynamic: true, store, name: 'disk' })
1011
class DiskStore extends VuexModule {
@@ -20,8 +21,12 @@ class DiskStore extends VuexModule {
2021

2122
speedResult: DiskSpeedResult | null = null
2223

24+
speedResults: DiskSpeedTestPoint[] = []
25+
2326
speedTesting = false
2427

28+
speedTestProgress = ''
29+
2530
speedError: string | null = null
2631

2732
@Mutation
@@ -49,11 +54,26 @@ class DiskStore extends VuexModule {
4954
this.speedResult = value
5055
}
5156

57+
@Mutation
58+
setSpeedResults(value: DiskSpeedTestPoint[]): void {
59+
this.speedResults = value
60+
}
61+
62+
@Mutation
63+
addSpeedResult(point: DiskSpeedTestPoint): void {
64+
this.speedResults = [...this.speedResults, point]
65+
}
66+
5267
@Mutation
5368
setSpeedTesting(value: boolean): void {
5469
this.speedTesting = value
5570
}
5671

72+
@Mutation
73+
setSpeedTestProgress(value: string): void {
74+
this.speedTestProgress = value
75+
}
76+
5777
@Mutation
5878
setSpeedError(message: string | null): void {
5979
this.speedError = message
@@ -158,6 +178,57 @@ class DiskStore extends VuexModule {
158178
this.setSpeedTesting(false)
159179
})
160180
}
181+
182+
@Action
183+
async runMultiSizeSpeedTest(): Promise<void> {
184+
this.setSpeedTesting(true)
185+
this.setSpeedError(null)
186+
this.setSpeedResults([])
187+
this.setSpeedTestProgress('Starting tests...')
188+
189+
let processedFragments = 0
190+
191+
try {
192+
await back_axios({
193+
method: 'get',
194+
url: `${this.API_URL}/speed/stream`,
195+
timeout: 600000,
196+
onDownloadProgress: (progressEvent: { currentTarget?: { response?: string } }) => {
197+
const response = progressEvent.currentTarget?.response
198+
if (!response) return
199+
200+
const fragments = parseStreamingResponse(response)
201+
const validFragments = fragments.filter((f) => f.fragment >= 0 && f.status === 200 && f.data)
202+
203+
for (let i = processedFragments; i < validFragments.length; i++) {
204+
const fragment = validFragments[i]
205+
if (!fragment.data) continue
206+
207+
try {
208+
const point = JSON.parse(fragment.data) as DiskSpeedTestPoint
209+
this.addSpeedResult(point)
210+
this.setSpeedTestProgress(`Tested ${point.size_mb} MB`)
211+
} catch (e) {
212+
console.error('Failed to parse speed test point:', e)
213+
}
214+
}
215+
processedFragments = validFragments.length
216+
},
217+
})
218+
219+
this.setSpeedTestProgress('Test complete')
220+
} catch (error) {
221+
if (isBackendOffline(error)) {
222+
this.setSpeedTesting(false)
223+
return
224+
}
225+
const axiosError = error as { response?: { data?: { detail?: string } }; message?: string }
226+
const message = axiosError.response?.data?.detail || axiosError.message || 'Unknown error'
227+
this.setSpeedError(`Speed test failed: ${message}`)
228+
} finally {
229+
this.setSpeedTesting(false)
230+
}
231+
}
161232
}
162233

163234
const disk_store = getModule(DiskStore)

core/frontend/src/types/disk.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,9 @@ export interface DiskSpeedResult {
2929
success: boolean
3030
error: string | null
3131
}
32+
33+
export interface DiskSpeedTestPoint {
34+
size_mb: number
35+
write_speed: number | null
36+
read_speed: number | null
37+
}

core/frontend/src/views/Disk.vue

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@
149149
<!-- Speed Test Tab -->
150150
<v-tab-item>
151151
<v-container class="d-flex flex-row align-center justify-center fluid">
152-
<v-card min-width="390px" class="ma-5">
152+
<v-card max-width="500px" class="ma-5">
153153
<v-card-title class="justify-center">
154154
Disk Speed Test
155155
</v-card-title>
@@ -159,57 +159,55 @@
159159
<v-col>
160160
<v-col class="d-flex justify-center">
161161
<v-progress-circular
162-
v-if="speedTesting"
163162
:rotate="90"
164163
:width="15"
165-
:indeterminate="speedTesting"
164+
:value="speedResults.length * 100 / 4"
165+
:indeterminate="speedTesting && speedResults.length === 0"
166166
ratio="1"
167167
color="primary"
168168
size="200"
169169
>
170-
<span v-if="speedTesting">Testing...</span>
170+
<span v-if="speedTesting">{{ speedTestProgress }}</span>
171+
<span v-else-if="hasResults">{{ speedResults.length }}/4</span>
171172
<span v-else>Click to start</span>
172173
</v-progress-circular>
173174

174-
<v-list v-else>
175+
<v-list>
175176
<v-list-item>
176177
<v-list-item-icon>
177-
<v-icon v-tooltip="'Write speed'">
178-
mdi-alpha-w-box-outline
178+
<v-icon v-tooltip="'Write speed (avg)'">
179+
mdi-pencil
179180
</v-icon>
180181
</v-list-item-icon>
181182
<v-list-item-subtitle>
182-
{{ speedResult?.write_speed_mbps ? speedResult.write_speed_mbps.toFixed(2) : '...' }} MiB/s
183+
{{ avgWriteSpeed }} MiB/s
183184
</v-list-item-subtitle>
184185
</v-list-item>
185186

186187
<v-list-item>
187188
<v-list-item-icon>
188-
<v-icon v-tooltip="'Read speed'">
189-
mdi-alpha-r-box-outline
189+
<v-icon v-tooltip="'Read speed (avg)'">
190+
mdi-book-open-page-variant
190191
</v-icon>
191192
</v-list-item-icon>
192193
<v-list-item-subtitle>
193-
{{ speedResult?.read_speed_mbps ? speedResult.read_speed_mbps.toFixed(2) : '...' }} MiB/s
194+
{{ avgReadSpeed }} MiB/s
194195
</v-list-item-subtitle>
195196
</v-list-item>
196197
</v-list>
197198
</v-col>
198199

200+
<v-col v-if="hasResults">
201+
<disk-speed-graph
202+
style="width: 100%"
203+
:data="speedResults"
204+
/>
205+
</v-col>
206+
199207
<v-alert v-if="speedError" type="error" dense outlined class="ma-4">
200208
{{ speedError }}
201209
</v-alert>
202210

203-
<v-alert
204-
v-if="speedResult && !speedResult.success"
205-
type="warning"
206-
dense
207-
outlined
208-
class="ma-4"
209-
>
210-
Test failed: {{ speedResult.error }}
211-
</v-alert>
212-
213211
<div
214212
class="transition-swing pa-6"
215213
align="center"
@@ -238,12 +236,16 @@
238236
<script lang="ts">
239237
import Vue from 'vue'
240238
239+
import DiskSpeedGraph from '@/components/disk/DiskSpeedGraph.vue'
241240
import disk_store from '@/store/disk'
242-
import { DiskNode, DiskSpeedResult } from '@/types/disk'
241+
import { DiskNode, DiskSpeedTestPoint } from '@/types/disk'
243242
import { prettifySize } from '@/utils/helper_functions'
244243
245244
export default Vue.extend({
246245
name: 'DiskView',
246+
components: {
247+
DiskSpeedGraph,
248+
},
247249
data() {
248250
return {
249251
activeTab: 0,
@@ -267,12 +269,15 @@ export default Vue.extend({
267269
error(): string | null {
268270
return disk_store.error
269271
},
270-
speedResult(): DiskSpeedResult | null {
271-
return disk_store.speedResult
272+
speedResults(): DiskSpeedTestPoint[] {
273+
return disk_store.speedResults
272274
},
273275
speedTesting(): boolean {
274276
return disk_store.speedTesting
275277
},
278+
speedTestProgress(): string {
279+
return disk_store.speedTestProgress
280+
},
276281
speedError(): string | null {
277282
return disk_store.speedError
278283
},
@@ -302,16 +307,31 @@ export default Vue.extend({
302307
},
303308
state(): string {
304309
if (this.speedTesting) {
305-
return 'Running test...'
310+
return this.speedTestProgress || 'Running test...'
306311
}
307-
if (this.speedResult?.success) {
312+
if (this.speedResults.length === 4) {
308313
return 'Test complete'
309314
}
310-
if (this.speedResult && !this.speedResult.success) {
315+
if (this.speedError) {
311316
return 'Test failed'
312317
}
313318
return 'Click to start'
314319
},
320+
hasResults(): boolean {
321+
return this.speedResults.length > 0
322+
},
323+
avgWriteSpeed(): string {
324+
const speeds = this.speedResults.map((r) => r.write_speed).filter((s): s is number => s !== null)
325+
if (speeds.length === 0) return '...'
326+
const avg = speeds.reduce((a, b) => a + b, 0) / speeds.length
327+
return avg.toFixed(2)
328+
},
329+
avgReadSpeed(): string {
330+
const speeds = this.speedResults.map((r) => r.read_speed).filter((s): s is number => s !== null)
331+
if (speeds.length === 0) return '...'
332+
const avg = speeds.reduce((a, b) => a + b, 0) / speeds.length
333+
return avg.toFixed(2)
334+
},
315335
},
316336
mounted(): void {
317337
this.fetchUsage()
@@ -395,7 +415,7 @@ export default Vue.extend({
395415
return sizeBytes / parentSize * 100
396416
},
397417
async runSpeedTest(): Promise<void> {
398-
await disk_store.runSpeedTest(1024 * 1024 * 1024)
418+
await disk_store.runMultiSizeSpeedTest()
399419
},
400420
prettifySize,
401421
},

0 commit comments

Comments
 (0)