From 711aa221a562959f4e0322af3eba919cf5a20e51 Mon Sep 17 00:00:00 2001 From: xiongxiao Date: Fri, 13 Mar 2026 20:17:33 +0800 Subject: [PATCH] update 0.0.6 --- package.json | 2 +- src/project/manager.ts | 16 ++++++++++++++ src/project/project-search/index.ts | 2 ++ src/routes/file.ts | 33 +++++++++++++++++++++++++++++ src/routes/search.ts | 5 +++-- 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index e2a0b80..9fe1985 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/project-search", - "version": "0.0.5", + "version": "0.0.6", "description": "", "main": "index.js", "scripts": { diff --git a/src/project/manager.ts b/src/project/manager.ts index 581b907..7041eff 100644 --- a/src/project/manager.ts +++ b/src/project/manager.ts @@ -4,6 +4,7 @@ import { ProjectStore, ProjectDoc } from "./project-store"; import { ProjectListener } from "./project-listener/listener"; import { EventEmitter } from "eventemitter3"; import fs from 'node:fs'; +import path from 'node:path'; import { CustomError } from "@kevisual/router"; type Project = { @@ -171,6 +172,21 @@ export class ProjectManager implements ProjectManagerInterface { } } + async writeFile(filepath: string, content: string): Promise { + try { + const buffer = Buffer.from(content, 'base64'); + const dir = path.dirname(filepath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + fs.writeFileSync(filepath, buffer); + return true; + } catch (error) { + console.error('写入文件失败:', error); + return false; + } + } + async deleteFile(filepath: string): Promise { if (!fileIsExist(filepath)) { return false; diff --git a/src/project/project-search/index.ts b/src/project/project-search/index.ts index 1449842..dee636c 100644 --- a/src/project/project-search/index.ts +++ b/src/project/project-search/index.ts @@ -78,6 +78,7 @@ export class ProjectSearch { async searchFiles(query: string = '', options?: { projectPath?: string; repo?: string; + filepath?: string; title?: string; tags?: string | string[]; summary?: string; @@ -93,6 +94,7 @@ export class ProjectSearch { const filter: string[] = []; if (options?.projectPath) filter.push(`projectPath = "${options.projectPath}"`); if (options?.repo) filter.push(`repo = "${options.repo}"`); + if (options?.filepath) filter.push(`filepath = "${options.filepath}"`); if (options?.title) filter.push(`title = "${options.title}"`); if (options?.tags) { const tags = Array.isArray(options.tags) ? options.tags : [options.tags]; diff --git a/src/routes/file.ts b/src/routes/file.ts index 74686e1..959a233 100644 --- a/src/routes/file.ts +++ b/src/routes/file.ts @@ -23,6 +23,39 @@ app.route({ } }).addTo(app); +/** + * 更新文件内容(base64 编码) + */ +app.route({ + path: 'project-file', + key: 'update-content', + middleware: ['auth-admin'], + description: '将 base64 编码的内容写入指定文件路径,用于更新或创建文件', + metadata: { + args: { + filepath: z.string().nonempty().describe('要写入的文件绝对路径,必填'), + content: z.string().nonempty().describe('文件内容的 base64 编码,必填'), + } + } +}).define(async (ctx) => { + const { filepath, content } = ctx.query as { + filepath: string; + content: string; + }; + if (!filepath) ctx.throw(400, 'filepath 不能为空'); + if (!content) ctx.throw(400, 'content 不能为空'); + + try { + const success = await manager.writeFile(filepath, content); + if (!success) { + ctx.throw(500, '写入文件失败'); + } + ctx.body = { success: true }; + } catch (error) { + ctx.throw(500, '写入文件失败'); + } +}).addTo(app); + /** * 更新文件自定义信息(title/tags/summary/description/link) */ diff --git a/src/routes/search.ts b/src/routes/search.ts index 5a13312..b61087f 100644 --- a/src/routes/search.ts +++ b/src/routes/search.ts @@ -14,6 +14,7 @@ app args: { q: z.string().optional().describe('搜索关键词,选填;留空或不传则返回全部文件'), projectPath: z.string().optional().describe('按项目根目录路径过滤,仅返回该项目下的文件,选填'), + filepath: z.string().optional().describe('按文件绝对路径过滤,选填'), repo: z.string().optional().describe('按代码仓库标识过滤(如 owner/repo),选填'), title: z.string().optional().describe('按人工标注的标题字段过滤,选填'), tags: z.array(z.string()).optional().describe('按人工标注的标签列表过滤,选填'), @@ -27,13 +28,13 @@ app } }) .define(async (ctx) => { - let { q, projectPath, repo, title, tags, summary, description, link, sort, limit, getContent = false } = ctx.query as { q?: string; projectPath?: string; repo?: string; title?: string; tags?: string[]; summary?: string; description?: string; link?: string; sort?: string[]; limit?: number; getContent?: boolean }; + let { q, projectPath, filepath, repo, title, tags, summary, description, link, sort, limit, getContent = false } = ctx.query as { q?: string; projectPath?: string; filepath?: string; repo?: string; title?: string; tags?: string[]; summary?: string; description?: string; link?: string; sort?: string[]; limit?: number; getContent?: boolean }; if (!q) { sort = sort ?? ['projectPath:asc']; limit = limit ?? 1000; } const projectSearch = manager.projectSearch; - const hits = await projectSearch.searchFiles(q, { projectPath, repo, title, tags, summary, description, link, sort, limit, getContent }); + const hits = await projectSearch.searchFiles(q, { projectPath, filepath, repo, title, tags, summary, description, link, sort, limit, getContent }); ctx.body = { list: hits }; }) .addTo(app);