Skip to content

Commit c44b3a1

Browse files
committed
feat: display file size in file scan results
1 parent ffe48f3 commit c44b3a1

3 files changed

Lines changed: 65 additions & 19 deletions

File tree

cmd/scan.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func init() {
4747

4848
type scanSummary struct {
4949
SHA256 string `json:"sha256"`
50+
Size int64 `json:"size"`
5051
Classification string `json:"classification"`
5152
FileFormat string `json:"file_format"`
5253
FileExtension string `json:"file_extension"`
@@ -61,6 +62,7 @@ type avSummary struct {
6162
func buildScanSummary(file entity.File) scanSummary {
6263
s := scanSummary{
6364
SHA256: file.SHA256,
65+
Size: file.Size,
6466
Classification: file.Classification,
6567
FileFormat: file.Format,
6668
FileExtension: file.Extension,

cmd/scanui.go

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,16 @@ const maxPollRetries = 120 // 120 * 5s = 10 minutes
3333

3434
// One row in the UI.
3535
type fileRow struct {
36-
filename string
37-
sha256 string
38-
state fileState
39-
spinner spinner.Model
40-
result *scanSummary
41-
err error
42-
pollCount int
36+
filename string
37+
sha256 string
38+
size int64 // file size in bytes (set from upload response for archives)
39+
state fileState
40+
spinner spinner.Model
41+
result *scanSummary
42+
err error
43+
pollCount int
44+
isArchive bool // true for ZIP containers with multiple files
45+
childCount int // number of extracted files
4346
}
4447

4548
// Top-level bubbletea model.
@@ -55,9 +58,12 @@ type scanModel struct {
5558
// --- Messages ---
5659

5760
type fileUploadedMsg struct {
58-
index int
59-
sha256 string
60-
err error
61+
index int
62+
sha256 string
63+
size int64
64+
err error
65+
isArchive bool
66+
childHashes []string
6167
}
6268

6369
type fileScanStatusMsg struct {
@@ -94,8 +100,15 @@ func uploadFileCmd(index int, web webapi.Service, filename, token string) tea.Cm
94100
}
95101
// Use the SHA256 from the server response. For single-file ZIPs,
96102
// the server extracts the file and returns the child's hash, not
97-
// the ZIP's hash.
98-
return fileUploadedMsg{index: index, sha256: file.SHA256}
103+
// the ZIP's hash. For multi-file ZIPs, the server returns the
104+
// archive doc with child hashes so we can track them individually.
105+
return fileUploadedMsg{
106+
index: index,
107+
sha256: file.SHA256,
108+
size: file.Size,
109+
isArchive: file.IsArchive,
110+
childHashes: file.ArchiveFiles,
111+
}
99112
} else if forceRescanFlag {
100113
err = web.Rescan(sha256, token, osFlag, enableDetonationFlag, timeoutFlag)
101114
if err != nil {
@@ -249,8 +262,34 @@ func (m scanModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
249262
return m, m.maybeQuitOrNext()
250263
}
251264
m.files[i].sha256 = msg.sha256
252-
m.files[i].state = stateScanning
253-
cmds = append(cmds, pollStatusCmd(i, m.web, msg.sha256))
265+
266+
if msg.isArchive && len(msg.childHashes) > 0 {
267+
// Archive container: mark it as done immediately and track children.
268+
m.files[i].state = stateDone
269+
m.files[i].isArchive = true
270+
m.files[i].childCount = len(msg.childHashes)
271+
m.files[i].size = msg.size
272+
273+
archiveName := filepath.Base(m.files[i].filename)
274+
for _, childHash := range msg.childHashes {
275+
s := spinner.New()
276+
s.Spinner = spinner.Dot
277+
m.files = append(m.files, fileRow{
278+
filename: archiveName + "/" + truncSha(childHash),
279+
sha256: childHash,
280+
state: stateScanning,
281+
spinner: s,
282+
})
283+
childIdx := len(m.files) - 1
284+
cmds = append(cmds,
285+
pollStatusCmd(childIdx, m.web, childHash),
286+
m.files[childIdx].spinner.Tick,
287+
)
288+
}
289+
} else {
290+
m.files[i].state = stateScanning
291+
cmds = append(cmds, pollStatusCmd(i, m.web, msg.sha256))
292+
}
254293

255294
case fileScanStatusMsg:
256295
i := msg.index
@@ -404,7 +443,11 @@ func (m scanModel) View() string {
404443
case stateDone:
405444
sha := truncSha(f.sha256)
406445
line := styleSuccess.Render("✓") + " " + name + " " + styleDim.Render(sha)
407-
if f.result != nil {
446+
if f.isArchive {
447+
line += " " + styleDim.Render(formatSize(f.size))
448+
line += " " + styleLabel.Render(fmt.Sprintf("archive (%d files)", f.childCount))
449+
} else if f.result != nil {
450+
line += " " + styleDim.Render(formatSize(f.result.Size))
408451
fmtStr := f.result.FileFormat
409452
if f.result.FileExtension != "" {
410453
fmtStr += "/" + f.result.FileExtension
@@ -454,3 +497,4 @@ func truncSha(sha string) string {
454497
}
455498
return sha
456499
}
500+

cmd/view.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,13 +286,13 @@ func printKV(key, value string) {
286286
func formatSize(size int64) string {
287287
switch {
288288
case size >= 1<<30:
289-
return fmt.Sprintf("%.2f GB (%d bytes)", float64(size)/float64(1<<30), size)
289+
return fmt.Sprintf("%.2f GB", float64(size)/float64(1<<30))
290290
case size >= 1<<20:
291-
return fmt.Sprintf("%.2f MB (%d bytes)", float64(size)/float64(1<<20), size)
291+
return fmt.Sprintf("%.2f MB", float64(size)/float64(1<<20))
292292
case size >= 1<<10:
293-
return fmt.Sprintf("%.2f KB (%d bytes)", float64(size)/float64(1<<10), size)
293+
return fmt.Sprintf("%.2f KB", float64(size)/float64(1<<10))
294294
default:
295-
return fmt.Sprintf("%d bytes", size)
295+
return fmt.Sprintf("%d B", size)
296296
}
297297
}
298298

0 commit comments

Comments
 (0)