feat(web): sort file search results (#1109)

This commit is contained in:
Ananovo
2026-07-24 10:58:07 +08:00
committed by GitHub
parent ee5b0239cb
commit df36cec01e
12 changed files with 233 additions and 31 deletions
@@ -63,4 +63,32 @@ describe('directory RPC handlers', () => {
expect(link?.type).toBe('other')
expect(link?.size).toBeUndefined()
})
it('returns metadata for a batch of searched files', async () => {
const response = await rpc.handleRequest({
method: 'session-test:statFiles',
params: JSON.stringify({ paths: ['README.md', 'src/index.ts', 'missing.txt'] })
})
const parsed = JSON.parse(response) as {
success: boolean
entries?: Array<{ path: string; size?: number; modified?: number }>
}
expect(parsed.success).toBe(true)
expect(parsed.entries).toHaveLength(3)
expect(parsed.entries?.[0]).toMatchObject({ path: 'README.md', size: 6 })
expect(parsed.entries?.[0]?.modified).toBeTypeOf('number')
expect(parsed.entries?.[2]).toEqual({ path: 'missing.txt' })
})
it('rejects stat paths outside the session working directory', async () => {
const response = await rpc.handleRequest({
method: 'session-test:statFiles',
params: JSON.stringify({ paths: ['../outside.txt'] })
})
const parsed = JSON.parse(response) as { success: boolean; error?: string }
expect(parsed.success).toBe(false)
expect(parsed.error).toContain('outside the working directory')
})
})
+34 -1
View File
@@ -1,7 +1,7 @@
import { logger } from '@/ui/logger'
import { readdir, stat } from 'fs/promises'
import { basename, join, resolve } from 'path'
import type { DirectoryEntry, ListDirectoryResponse } from '@hapi/protocol/apiTypes'
import type { DirectoryEntry, ListDirectoryResponse, StatFilesResponse } from '@hapi/protocol/apiTypes'
import { RPC_METHODS } from '@hapi/protocol/rpcMethods'
import type { RpcHandlerManager } from '@/api/rpc/RpcHandlerManager'
import { validatePath } from '../pathSecurity'
@@ -16,6 +16,10 @@ interface GetDirectoryTreeRequest {
maxDepth: number
}
interface StatFilesRequest {
paths: string[]
}
interface TreeNode {
name: string
path: string
@@ -93,6 +97,35 @@ export function registerDirectoryHandlers(rpcHandlerManager: RpcHandlerManager,
}
})
rpcHandlerManager.registerHandler<StatFilesRequest, StatFilesResponse>(RPC_METHODS.StatFiles, async (data) => {
if (!Array.isArray(data.paths) || data.paths.length > 500) {
return rpcError('Invalid file paths')
}
for (const path of data.paths) {
const validation = validatePath(path, workingDirectory)
if (!validation.valid) {
return rpcError(validation.error ?? 'Invalid file path')
}
}
const entries = await Promise.all(data.paths.map(async (path) => {
try {
const stats = await stat(resolve(workingDirectory, path))
return {
path,
size: stats.size,
modified: stats.mtime.getTime()
}
} catch (error) {
logger.debug(`Failed to stat ${path}:`, error)
return { path }
}
}))
return { success: true, entries }
})
rpcHandlerManager.registerHandler<GetDirectoryTreeRequest, GetDirectoryTreeResponse>(RPC_METHODS.GetDirectoryTree, async (data) => {
logger.debug('Get directory tree request:', data.path, 'maxDepth:', data.maxDepth)