From cef68a95d9ffaaf0e5997e149b9a4f8c3a58eb8a Mon Sep 17 00:00:00 2001 From: momen Date: Tue, 10 Mar 2026 00:42:56 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20add=20comprehensive=20documentation=20?= =?UTF-8?q?and=20testing=20infrastructure=20##=20=E6=96=87=E6=A1=A3?= =?UTF-8?q?=EF=BC=88=E5=85=A8=E9=83=A8=E4=B8=AD=E6=96=87=EF=BC=89=20-=20AG?= =?UTF-8?q?ENTS.md=20-=20=E5=AE=8C=E6=95=B4=E7=9A=84=20AI=20=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E6=8C=87=E5=8D=97=EF=BC=88=E4=B8=AD=E6=96=87=E7=89=88?= =?UTF-8?q?=EF=BC=89=20-=20CONTRIBUTING.md=20-=20=E8=B4=A1=E7=8C=AE?= =?UTF-8?q?=E6=8C=87=E5=8D=97=20-=20DEVELOPMENT.md=20-=20=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E6=96=87=E6=A1=A3=20-=20DEPLOYMENT.md=20-=20=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E6=96=87=E6=A1=A3=20-=20TESTING.md=20-=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=96=87=E6=A1=A3=20-=20TROUBLESHOOTING.md=20-=20?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E6=8E=92=E6=9F=A5=E6=8C=87=E5=8D=97=20-=20FA?= =?UTF-8?q?Q.md=20-=20=E5=B8=B8=E8=A7=81=E9=97=AE=E9=A2=98=20-=20WORKFLOW.?= =?UTF-8?q?md=20-=20=E6=A0=87=E5=87=86=E5=BC=80=E5=8F=91=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E6=96=87=E6=A1=A3=20##=20AI=20=E5=B7=A5=E4=BD=9C=E6=B5=81?= =?UTF-8?q?=EF=BC=889=E4=B8=AA=E8=AF=A6=E7=BB=86=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81=EF=BC=89=20-=20feature-development.md=20-=20=E6=96=B0?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=BC=80=E5=8F=91=E6=B5=81=E7=A8=8B=20-=20bu?= =?UTF-8?q?g-fix.md=20-=20Bug=20=E4=BF=AE=E5=A4=8D=E6=B5=81=E7=A8=8B=20-?= =?UTF-8?q?=20code-review.md=20-=20=E4=BB=A3=E7=A0=81=E5=AE=A1=E6=9F=A5?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=20-=20testing.md=20-=20=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=BC=96=E5=86=99=E6=B5=81=E7=A8=8B=20-=20release.md=20-=20?= =?UTF-8?q?=E5=8F=91=E5=B8=83=E6=B5=81=E7=A8=8B=20-=20refactoring.md=20-?= =?UTF-8?q?=20=E4=BB=A3=E7=A0=81=E9=87=8D=E6=9E=84=E6=B5=81=E7=A8=8B=20-?= =?UTF-8?q?=20documentation.md=20-=20=E6=96=87=E6=A1=A3=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=20-=20performance-optimization.md=20-=20?= =?UTF-8?q?=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96=E6=B5=81=E7=A8=8B=20-=20se?= =?UTF-8?q?curity-audit.md=20-=20=E5=AE=89=E5=85=A8=E5=AE=A1=E8=AE=A1?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=20##=20=E6=B5=8B=E8=AF=95=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E8=AE=BE=E6=96=BD=20-=20vitest.config.ts=20-=20Vitest=20?= =?UTF-8?q?=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=E9=85=8D=E7=BD=AE=20-=20pl?= =?UTF-8?q?aywright.config.ts=20-=20Playwright=20E2E=20=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=20-=20src/=5F=5Ftests=5F=5F/setup.ts=20-=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83=E8=AE=BE=E7=BD=AE=20-=20sr?= =?UTF-8?q?c/=5F=5Ftests=5F=5F/unit/downloadStatus.test.ts=20-=20=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95=20-=20e2e/basic.s?= =?UTF-8?q?pec.ts=20-=20=E7=A4=BA=E4=BE=8B=20E2E=20=E6=B5=8B=E8=AF=95=20##?= =?UTF-8?q?=20CI/CD=20-=20.github/workflows/test.yml=20-=20=E6=96=B0?= =?UTF-8?q?=E5=BB=BA=E6=B5=8B=E8=AF=95=20CI=20=E5=B7=A5=E4=BD=9C=E6=B5=81?= =?UTF-8?q?=20-=20.github/workflows/build.yml=20-=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E5=B7=A5=E4=BD=9C=E6=B5=81=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=B5=8B=E8=AF=95=E6=AD=A5=E9=AA=A4=20##=20Issue=20?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=20-=20=E6=9B=B4=E6=96=B0=20bug=5Freport.md?= =?UTF-8?q?=20=E4=B8=BA=E6=A0=87=E5=87=86=20Bug=20=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=20-=20=E6=9B=B4=E6=96=B0=20help=5Fwanted.md?= =?UTF-8?q?=20=E4=B8=BA=E6=A0=87=E5=87=86=E5=8A=9F=E8=83=BD=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E6=A8=A1=E6=9D=BF=20##=20=E9=85=8D=E7=BD=AE=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20-=20package.json=20-=20=E6=B7=BB=E5=8A=A0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E4=BE=9D=E8=B5=96=E5=92=8C=207=20=E4=B8=AA=E6=96=B0?= =?UTF-8?q?=E7=9A=84=20npm=20=E8=84=9A=E6=9C=AC=20-=20.gitignore=20-=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B5=8B=E8=AF=95=E7=9B=B8=E5=85=B3=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E9=A1=B9=20##=20=E6=96=B0=E5=A2=9E=20npm=20=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=20-=20test=20-=20=E8=BF=90=E8=A1=8C=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=20-=20test:watch=20-=20=E7=9B=91=E5=90=AC?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=20-=20test:coverage=20-=20=E7=94=9F=E6=88=90?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E7=8E=87=E6=8A=A5=E5=91=8A=20-=20test:e2e=20?= =?UTF-8?q?-=20=E8=BF=90=E8=A1=8C=20E2E=20=E6=B5=8B=E8=AF=95=20-=20test:e2?= =?UTF-8?q?e:ui=20-=20E2E=20UI=20=E6=A8=A1=E5=BC=8F=20-=20test:e2e:debug?= =?UTF-8?q?=20-=20E2E=20=E8=B0=83=E8=AF=95=E6=A8=A1=E5=BC=8F=20-=20test:al?= =?UTF-8?q?l=20-=20=E8=BF=90=E8=A1=8C=E6=89=80=E6=9C=89=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=20##=20=E6=96=B0=E5=A2=9E=E6=B5=8B=E8=AF=95=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=20-=20@playwright/test=20^1.40.0=20-=20@testing-library/jest-d?= =?UTF-8?q?om=20^6.1.5=20-=20@testing-library/vue=20^8.0.1=20-=20@vitest/c?= =?UTF-8?q?overage-v8=20^1.0.0=20-=20@vue/test-utils=20^2.4.3=20-=20jsdom?= =?UTF-8?q?=20^23.0.1=20-=20vitest=20^1.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/workflows/1.md | 6 - .agents/workflows/bug-fix.md | 139 +++ .agents/workflows/code-review.md | 245 +++++ .agents/workflows/documentation.md | 264 ++++++ .agents/workflows/feature-development.md | 135 +++ .agents/workflows/performance-optimization.md | 333 +++++++ .agents/workflows/refactoring.md | 284 ++++++ .agents/workflows/release.md | 211 +++++ .agents/workflows/security-audit.md | 435 +++++++++ .agents/workflows/testing.md | 108 +++ .agents/workflows/代码审查.md | 6 - .github/ISSUE_TEMPLATE/bug_report.md | 55 +- .github/ISSUE_TEMPLATE/help_wanted.md | 27 +- .github/workflows/build.yml | 27 +- .github/workflows/test.yml | 83 ++ .gitignore | 9 + AGENTS.md | 856 ++++++++++-------- CONTRIBUTING.md | 247 +++++ DEPLOYMENT.md | 156 ++++ DEVELOPMENT.md | 380 ++++++++ FAQ.md | 154 ++++ PROJECT_ORGANIZATION_SUMMARY.md | 229 +++++ TESTING.md | 436 +++++++++ TROUBLESHOOTING.md | 160 ++++ WORKFLOW.md | 632 +++++++++++++ e2e/basic.spec.ts | 27 + package.json | 16 +- playwright.config.ts | 33 + src/__tests__/setup.ts | 30 + src/__tests__/unit/downloadStatus.test.ts | 67 ++ vitest.config.ts | 37 + 31 files changed, 5403 insertions(+), 424 deletions(-) delete mode 100644 .agents/workflows/1.md create mode 100644 .agents/workflows/bug-fix.md create mode 100644 .agents/workflows/code-review.md create mode 100644 .agents/workflows/documentation.md create mode 100644 .agents/workflows/feature-development.md create mode 100644 .agents/workflows/performance-optimization.md create mode 100644 .agents/workflows/refactoring.md create mode 100644 .agents/workflows/release.md create mode 100644 .agents/workflows/security-audit.md create mode 100644 .agents/workflows/testing.md delete mode 100644 .agents/workflows/代码审查.md create mode 100644 .github/workflows/test.yml create mode 100644 CONTRIBUTING.md create mode 100644 DEPLOYMENT.md create mode 100644 DEVELOPMENT.md create mode 100644 FAQ.md create mode 100644 PROJECT_ORGANIZATION_SUMMARY.md create mode 100644 TESTING.md create mode 100644 TROUBLESHOOTING.md create mode 100644 WORKFLOW.md create mode 100644 e2e/basic.spec.ts create mode 100644 playwright.config.ts create mode 100644 src/__tests__/setup.ts create mode 100644 src/__tests__/unit/downloadStatus.test.ts create mode 100644 vitest.config.ts diff --git a/.agents/workflows/1.md b/.agents/workflows/1.md deleted file mode 100644 index 4db78530..00000000 --- a/.agents/workflows/1.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -description: 审查 ---- - -审查是否符合代码规范 -是否有代码缺陷 \ No newline at end of file diff --git a/.agents/workflows/bug-fix.md b/.agents/workflows/bug-fix.md new file mode 100644 index 00000000..59bf93b1 --- /dev/null +++ b/.agents/workflows/bug-fix.md @@ -0,0 +1,139 @@ +--- +description: Bug 修复流程 +--- + +## 工作流说明 + +此工作流指导如何修复 Bug。 + +## 步骤 + +### 1. 复现 Bug + +- 根据 Issue 描述复现问题 +- 记录详细的复现步骤 +- 收集相关日志和错误信息 +- 确认环境信息 + +### 2. 分析问题 + +- 查看相关代码 +- 使用调试器定位问题 +- 检查日志输出 +- 识别根本原因 + +### 3. 创建修复分支 + +```bash +git checkout -b fix/your-bug-fix +``` + +### 4. 编写回归测试 + +先编写测试来复现 Bug: + +```typescript +// src/__tests__/unit/bugFix.test.ts +import { describe, it, expect } from "vitest"; +import { buggyFunction } from "@/modules/example"; + +describe("buggyFunction", () => { + it("should not crash with null input", () => { + expect(() => buggyFunction(null)).not.toThrow(); + }); +}); +``` + +### 5. 修复代码 + +- 最小化修改 +- 保持代码可读性 +- 添加必要的注释 +- 更新相关类型定义 + +### 6. 运行测试 + +```bash +# 确保新测试通过 +npm run test + +# 运行所有测试 +npm run test:all + +# 代码检查 +npm run lint +npm run format +``` + +### 7. 本地验证 + +- 验证 Bug 已修复 +- 测试相关功能 +- 检查是否引入新问题 +- 测试边界情况 + +### 8. 更新文档 + +- 更新 CHANGELOG.md(如果需要) +- 更新相关文档(如需要) + +### 9. 提交代码 + +```bash +git add . +git commit -m "fix(scope): describe the bug fix" -s +git push origin fix/your-bug-fix +``` + +### 10. 创建 Pull Request + +- 引用相关 Issue(`Fixes #123`) +- 描述修复方法 +- 说明复现步骤 +- 添加测试说明 + +### 11. 代码审查 + +- 响应审查意见 +- 进行必要的修改 +- 确保所有 CI 检查通过 + +### 12. 合并 + +- 等待审查批准 +- Squash 合并到 main 分支 +- 删除修复分支 + +## 注意事项 + +- ⚠️ 修复前先理解问题根源 +- ⚠️ 最小化修改范围 +- ⚠️ 添加回归测试防止复发 +- ⚠️ 考虑向后兼容性 +- ⚠️ 测试所有受影响的功能 + +## 常见 Bug 类型 + +### IPC 通信问题 + +- 检查事件名称是否匹配 +- 检查数据格式是否正确 +- 检查异步处理 + +### 状态管理问题 + +- 检查响应式依赖 +- 检查状态更新时机 +- 检查内存泄漏 + +### 类型错误 + +- 检查类型定义 +- 检查类型断言 +- 检查可选值处理 + +## 相关文档 + +- [CONTRIBUTING.md](../../CONTRIBUTING.md) - 贡献指南 +- [TESTING.md](../../TESTING.md) - 测试文档 +- [TROUBLESHOOTING.md](../../TROUBLESHOOTING.md) - 问题排查 diff --git a/.agents/workflows/code-review.md b/.agents/workflows/code-review.md new file mode 100644 index 00000000..5dc92cfb --- /dev/null +++ b/.agents/workflows/code-review.md @@ -0,0 +1,245 @@ +--- +description: 代码审查流程 +--- + +## 工作流说明 + +此工作流指导如何进行代码审查。 + +## 审查清单 + +### 代码质量 + +- [ ] 代码遵循项目规范 +- [ ] TypeScript 类型正确 +- [ ] 没有 `any` 类型(除非必要) +- [ ] ESLint 和 Prettier 通过 +- [ ] 代码可读性良好 + +### 功能实现 + +- [ ] 实现符合需求 +- [ ] 边界情况处理 +- [ ] 错误处理完善 +- [ ] 没有引入新 Bug + +### 测试 + +- [ ] 包含足够的测试 +- [ ] 测试覆盖率合理 +- [ ] 所有测试通过 +- [ ] E2E 测试(如需要) + +### 文档 + +- [ ] 更新了相关文档 +- [ ] 代码注释充分 +- [ ] API 文档(如需要) +- [ ] CHANGELOG.md(如需要) + +### 安全性 + +- [ ] 没有安全漏洞 +- [ ] 输入验证完善 +- [ ] 权限检查正确 +- [ ] 敏感信息保护 + +### 性能 + +- [ ] 没有明显的性能问题 +- [ ] 内存使用合理 +- [ ] 没有不必要的渲染 +- [ ] 资源加载优化 + +## 审查流程 + +### 1. 理解变更 + +- 阅读 PR 描述 +- 查看 Issue 链接 +- 理解变更目的 +- 检查变更范围 + +### 2. 代码审查 + +**主进程代码:** + +```bash +# 检查类型安全 +npx tsc --noEmit + +# 检查代码质量 +npm run lint +``` + +**渲染进程代码:** + +- 组件结构 +- 状态管理 +- 事件处理 +- 样式实现 + +### 3. 测试验证 + +```bash +# 运行单元测试 +npm run test + +# 运行 E2E 测试 +npm run test:e2e + +# 检查覆盖率 +npm run test:coverage +``` + +### 4. 提供反馈 + +**正面反馈:** + +- 好的实现 +- 优秀的代码 +- 有价值的贡献 + +**建设性反馈:** + +- 指出问题 +- 提出建议 +- 解释原因 + +**反馈格式:** + +````markdown +### 问题 + +**位置:** `src/components/AppCard.vue:45` + +**描述:** 这里缺少错误处理,可能导致应用崩溃。 + +**建议:** + +```typescript +try { + await installPackage(); +} catch (error) { + console.error("Install failed:", error); + showError(error.message); +} +``` +```` + +```` + +### 5. 批准或要求修改 + +**批准条件:** +- 所有审查项目通过 +- 所有测试通过 +- CI 检查通过 +- 没有阻塞问题 + +**要求修改:** +- 指出必须修复的问题 +- 给出明确的修改建议 +- 等待作者响应 + +## 审查原则 + +### 及时性 + +- 尽快响应 PR +- 设定响应时间预期 +- 优先处理紧急 PR + +### 建设性 + +- 提供具体的反馈 +- 给出改进建议 +- 解释审查理由 + +### 尊重 + +- 尊重作者的贡献 +- 使用礼貌的语言 +- 认可好的实现 + +### 一致性 + +- 遵循项目规范 +- 保持审查标准一致 +- 参考之前类似 PR + +## 常见问题 + +### 类型安全问题 + +**问题:** 使用了 `any` 类型 + +**建议:** +```typescript +// ❌ 避免 +const data: any = response; + +// ✅ 推荐 +interface ResponseData { + id: string; + name: string; +} +const data: ResponseData = response; +```` + +### 代码重复 + +**问题:** 代码重复 + +**建议:** + +```typescript +// 提取公共函数 +function formatSize(size: number): string { + return size > 1024 ? `${size / 1024} MB` : `${size} KB`; +} +``` + +### 错误处理 + +**问题:** 缺少错误处理 + +**建议:** + +```typescript +async function loadApps() { + try { + const response = await axios.get("/api/apps"); + return response.data; + } catch (error) { + logger.error({ err: error }, "Failed to load apps"); + throw error; + } +} +``` + +## 审查后操作 + +### 批准 + +- 点击 "Approve review" +- 添加评论(可选) +- 等待合并 + +### 要求修改 + +- 选择 "Request changes" +- 提供详细反馈 +- 等待作者更新 + +### 评论 + +- 选择 "Comment" +- 提供建议或问题 +- 不阻止合并 + +## 相关文档 + +- [CONTRIBUTING.md](../../CONTRIBUTING.md) - 贡献指南 +- [DEVELOPMENT.md](../../DEVELOPMENT.md) - 开发文档 +- [AGENTS.md](../../AGENTS.md) - AI 编码指南 diff --git a/.agents/workflows/documentation.md b/.agents/workflows/documentation.md new file mode 100644 index 00000000..472faade --- /dev/null +++ b/.agents/workflows/documentation.md @@ -0,0 +1,264 @@ +--- +description: 文档更新流程 +--- + +## 工作流说明 + +此工作流指导如何更新项目文档。 + +## 步骤 + +### 1. 确定需要更新的文档 + +根据变更内容确定需要更新的文档: + +- README.md - 主要说明 +- DEVELOPMENT.md - 开发指南 +- CONTRIBUTING.md - 贡献指南 +- TESTING.md - 测试文档 +- DEPLOYMENT.md - 部署文档 +- TROUBLESHOOTING.md - 问题排查 +- FAQ.md - 常见问题 +- AGENTS.md - AI 编码指南 +- CHANGELOG.md - 变更日志 + +### 2. 创建文档分支 + +```bash +git checkout -b docs/update-documentation +``` + +### 3. 更新文档 + +#### README.md + +添加新功能说明: + +```markdown +## 新功能 + +### 应用更新 + +现在支持一键更新所有可更新的应用。 + +### 下载管理 + +改进了下载队列管理,支持暂停和继续。 +``` + +#### DEVELOPMENT.md + +添加开发指南: + +```markdown +## 新功能开发 + +### 添加新功能步骤 + +1. 理解需求 +2. 设计方案 +3. 实现功能 +4. 编写测试 +5. 提交 PR +``` + +#### CONTRIBUTING.md + +更新贡献指南: + +```markdown +### 新功能贡献 + +- 遵循现有代码风格 +- 编写充分的测试 +- 更新相关文档 +``` + +#### TESTING.md + +添加测试示例: + +```typescript +describe("New Feature", () => { + it("should work correctly", () => { + // 测试代码 + }); +}); +``` + +#### CHANGELOG.md + +添加变更记录: + +```markdown +## [4.10.0](https://github.com/elysia-best/apm-app-store/compare/v4.9.9...v4.10.0) (2026-03-10) + +### Features + +- feat(download): add pause and resume for downloads +- feat(update): add batch update for apps + +### Bug Fixes + +- fix(ui): correct dark mode toggle persistence +``` + +### 4. 检查文档质量 + +- [ ] 语法正确 +- [ ] 格式统一 +- [ ] 链接有效 +- [ ] 内容准确 +- [ ] 示例可运行 + +### 5. 运行文档测试 + +```bash +# 如果有文档测试 +npm run test:docs + +# 检查链接 +npm run check-links +``` + +### 6. 本地预览 + +使用 Markdown 预览工具查看效果。 + +### 7. 提交文档 + +```bash +git add . +git commit -m "docs: update documentation for new features" -s +git push origin docs/update-documentation +``` + +### 8. 创建 Pull Request + +- 说明更新的内容 +- 提供预览截图(如需要) +- 引用相关 Issue + +### 9. 代码审查 + +- 响应审查意见 +- 确保文档质量 +- 合并到 main 分支 + +## 文档编写规范 + +### 格式规范 + +- 使用 Markdown +- 保持一致的标题层级 +- 使用代码块展示示例 +- 使用表格对比选项 + +### 语言规范 + +- 使用简洁清晰的语言 +- 避免技术术语(或解释) +- 保持中英文术语一致 +- 使用被动语态 + +### 示例规范 + +```typescript +// 好的示例 +import { ref } from "vue"; + +const count = ref(0); + +function increment() { + count.value++; +} +``` + +### 链接规范 + +```markdown +- 内部链接: [文档名](./document.md) +- 外部链接: [Vue 文档](https://vuejs.org/) +- 锚点链接: [章节](#section-name) +``` + +## 文档模板 + +### 新功能文档 + +````markdown +## 功能名称 + +### 描述 + +简要描述功能 + +### 使用方法 + +```typescript +// 示例代码 +``` +```` + +### 配置选项 + +| 选项 | 类型 | 默认值 | 说明 | +| ------ | ------ | --------- | -------- | +| option | string | 'default' | 选项说明 | + +### 注意事项 + +- 注意事项 1 +- 注意事项 2 + +```` + +### API 文档 + +```markdown +## API 函数名 + +### 签名 +```typescript +function functionName(param1: Type1, param2: Type2): ReturnType +```` + +### 参数 + +| 参数 | 类型 | 必填 | 说明 | +| ------ | ----- | ---- | -------- | +| param1 | Type1 | 是 | 参数说明 | +| param2 | Type2 | 否 | 参数说明 | + +### 返回值 + +| 类型 | 说明 | +| ---------- | ---------- | +| ReturnType | 返回值说明 | + +### 示例 + +```typescript +const result = functionName(arg1, arg2); +``` + +### 错误 + +抛出 `Error` 异常的情况说明。 + +``` + +## 注意事项 + +- ⚠️ 保持文档与代码同步 +- ⚠️ 更新示例代码 +- ⚠️ 检查链接有效性 +- ⚠️ 使用统一的格式 +- ⚠️ 提供清晰的说明 + +## 相关文档 + +- [CONTRIBUTING.md](../../CONTRIBUTING.md) - 贡献指南 +- [DEVELOPMENT.md](../../DEVELOPMENT.md) - 开发文档 +- [AGENTS.md](../../AGENTS.md) - AI 编码指南 +``` diff --git a/.agents/workflows/feature-development.md b/.agents/workflows/feature-development.md new file mode 100644 index 00000000..6ed4b69a --- /dev/null +++ b/.agents/workflows/feature-development.md @@ -0,0 +1,135 @@ +--- +description: 新功能开发流程 +--- + +## 工作流说明 + +此工作流指导如何开发新功能。 + +## 步骤 + +### 1. 理解需求 + +- 阅读 Issue 描述 +- 确认功能范围 +- 识别依赖关系 +- 设计 API 和数据结构 + +### 2. 设计方案 + +- 设计 UI/UX(如需要) +- 设计数据流 +- 确定 IPC 通信(如需要) +- 编写技术方案文档(可选) + +### 3. 创建功能分支 + +```bash +git checkout -b feature/your-feature-name +``` + +### 4. 更新类型定义 + +在 `src/global/typedefinition.ts` 中添加新的类型定义: + +```typescript +export interface NewFeatureData { + id: string; + name: string; + // ...其他字段 +} +``` + +### 5. 编写测试 + +先编写测试,遵循 TDD 原则: + +```typescript +// src/__tests__/unit/newFeature.test.ts +import { describe, it, expect } from "vitest"; +import { newFunction } from "@/modules/newFeature"; + +describe("newFunction", () => { + it("should work correctly", () => { + const result = newFunction(input); + expect(result).toBe(expected); + }); +}); +``` + +### 6. 实现功能 + +按照以下顺序实现: + +- 后端逻辑(Electron 主进程) +- 前端逻辑(Vue 组件) +- IPC 通信(如需要) +- 样式和布局 + +### 7. 运行测试 + +```bash +# 单元测试 +npm run test + +# E2E 测试 +npm run test:e2e + +# 代码检查 +npm run lint +npm run format +``` + +### 8. 本地测试 + +- 测试所有功能场景 +- 测试边界情况 +- 测试错误处理 +- 检查性能影响 + +### 9. 更新文档 + +- 更新 API 文档(如需要) +- 更新用户文档(如需要) +- 更新 CHANGELOG.md + +### 10. 提交代码 + +```bash +git add . +git commit -m "feat(scope): add new feature" -s +git push origin feature/your-feature-name +``` + +### 11. 创建 Pull Request + +- 使用 PR 模板 +- 引用相关 Issue +- 添加测试说明 +- 添加截图/录屏(UI 变更) + +### 12. 代码审查 + +- 响应审查意见 +- 进行必要的修改 +- 确保所有 CI 检查通过 + +### 13. 合并 + +- 等待审查批准 +- Squash 合并到 main 分支 +- 删除功能分支 + +## 注意事项 + +- ⚠️ 保持 PR 小而聚焦(建议 < 500 行) +- ⚠️ 确保 TypeScript 严格模式通过 +- ⚠️ 不引入 `any` 类型(必要时使用 `eslint-disable`) +- ⚠️ 所有新功能必须有测试 +- ⚠️ 遵循代码规范 + +## 相关文档 + +- [CONTRIBUTING.md](../../CONTRIBUTING.md) - 贡献指南 +- [DEVELOPMENT.md](../../DEVELOPMENT.md) - 开发文档 +- [TESTING.md](../../TESTING.md) - 测试文档 diff --git a/.agents/workflows/performance-optimization.md b/.agents/workflows/performance-optimization.md new file mode 100644 index 00000000..edbb3430 --- /dev/null +++ b/.agents/workflows/performance-optimization.md @@ -0,0 +1,333 @@ +--- +description: 性能优化流程 +--- + +## 工作流说明 + +此工作流指导如何优化应用性能。 + +## 步骤 + +### 1. 识别性能问题 + +使用工具分析性能: + +- Chrome DevTools Performance +- Vue DevTools +- Vite Build Analysis +- 内存分析工具 + +### 2. 分析瓶颈 + +确定性能瓶颈: + +- 渲染性能 +- 网络请求 +- 内存使用 +- CPU 使用 +- 磁盘 I/O + +### 3. 创建优化分支 + +```bash +git checkout -b perf/optimize-performance +``` + +### 4. 添加性能测试 + +```typescript +// src/__tests__/perf/performance.test.ts +import { describe, it, expect } from "vitest"; +import { heavyFunction } from "@/modules/example"; + +describe("heavyFunction", () => { + it("should complete within 100ms", () => { + const start = performance.now(); + heavyFunction(); + const duration = performance.now() - start; + expect(duration).toBeLessThan(100); + }); +}); +``` + +### 5. 实施优化 + +#### 渲染性能优化 + +```typescript +// 使用 computed 缓存计算结果 +const filteredApps = computed(() => { + return apps.value.filter(app => app.category === selectedCategory); +}); + +// 使用 v-memo 优化列表渲染 + + +// 防抖和节流 +import { debounce } from 'lodash-es'; + +const debouncedSearch = debounce((query: string) => { + searchApps(query); +}, 300); +``` + +#### 网络请求优化 + +```typescript +// 使用缓存 +const appCache = new Map(); + +async function fetchApps(category: string): Promise { + if (appCache.has(category)) { + return appCache.get(category)!; + } + + const apps = await axios.get(`/api/apps/${category}`); + appCache.set(category, apps.data); + return apps.data; +} + +// 并发请求 +const [apps1, apps2] = await Promise.all([ + fetchApps("category1"), + fetchApps("category2"), +]); +``` + +#### 内存优化 + +```typescript +// 及时清理事件监听 +onMounted(() => { + window.addEventListener("resize", handleResize); +}); + +onUnmounted(() => { + window.removeEventListener("resize", handleResize); +}); + +// 避免内存泄漏 +let timer: number; + +function startTimer() { + clearInterval(timer); + timer = setInterval(() => { + // 定时任务 + }, 1000); +} + +onUnmounted(() => { + clearInterval(timer); +}); +``` + +#### 代码分割 + +```typescript +// 动态导入组件 +const AppDetailModal = defineAsyncComponent( + () => import("@/components/AppDetailModal.vue"), +); + +// 路由懒加载 +const routes = [ + { + path: "/app/:id", + component: () => import("@/views/AppDetail.vue"), + }, +]; +``` + +### 6. 测试性能 + +```bash +# 运行性能测试 +npm run test:perf + +# 使用 DevTools 分析 +# 1. 打开 DevTools +# 2. 切换到 Performance 标签 +# 3. 点击 Record +# 4. 执行操作 +# 5. 停止录制并分析 +``` + +### 7. 对比优化效果 + +记录优化前后的数据: + +- 渲染时间 +- 内存使用 +- 网络请求数 +- 应用启动时间 + +### 8. 验证功能 + +```bash +# 确保功能正常 +npm run test + +# 手动测试主要流程 +``` + +### 9. 代码审查 + +检查优化是否: + +- 提升了性能 +- 没有破坏功能 +- 代码可读 +- 易于维护 + +### 10. 更新文档 + +- 记录优化内容 +- 更新性能指标 +- 添加优化说明 + +### 11. 提交代码 + +```bash +git add . +git commit -m "perf(scope): optimize performance" -s +git push origin perf/optimize-performance +``` + +### 12. 创建 Pull Request + +- 说明优化内容 +- 提供性能对比 +- 展示优化效果 + +## 性能优化清单 + +### 渲染性能 + +- [ ] 使用 computed 缓存 +- [ ] 使用 v-memo 优化 +- [ ] 避免不必要的重新渲染 +- [ ] 使用虚拟滚动(大数据集) +- [ ] 图片懒加载 + +### 网络性能 + +- [ ] 减少请求数量 +- [ ] 使用缓存 +- [ ] 压缩资源 +- [ ] 使用 CDN +- [ ] 并发请求 + +### 内存性能 + +- [ ] 清理事件监听 +- [ ] 避免内存泄漏 +- [ ] 释放不再使用的资源 +- [ ] 使用对象池(如需要) +- [ ] 优化数据结构 + +### 构建性能 + +- [ ] 代码分割 +- [ ] Tree shaking +- [ ] 压缩代码 +- [ ] 优化依赖 +- [ ] 使用缓存 + +## 性能监控 + +### 关键指标 + +- **FCP (First Contentful Paint):** < 1.5s +- **LCP (Largest Contentful Paint):** < 2.5s +- **TTI (Time to Interactive):** < 3.5s +- **CLS (Cumulative Layout Shift):** < 0.1 +- **FID (First Input Delay):** < 100ms + +### 监控工具 + +```typescript +// 使用 Performance API +const perfData = performance.getEntriesByType("navigation")[0]; +console.log("Page Load Time:", perfData.loadEventEnd - perfData.fetchStart); + +// 使用 Vue DevTools +// 监控组件渲染时间 +``` + +## 常见性能问题 + +### 1. 大列表渲染 + +**问题:** 渲染大量数据导致卡顿 + +**解决方案:** + +```vue + +``` + +### 2. 频繁的 DOM 更新 + +**问题:** 频繁更新 DOM 导致性能下降 + +**解决方案:** + +```typescript +// 使用 requestAnimationFrame +function animate() { + updatePosition(); + requestAnimationFrame(animate); +} +``` + +### 3. 内存泄漏 + +**问题:** 内存持续增长 + +**解决方案:** + +```typescript +// 及时清理 +onUnmounted(() => { + clearInterval(timer); + removeEventListener("resize", handleResize); + clearTimeout(timeout); +}); +``` + +### 4. 不必要的计算 + +**问题:** 重复计算相同结果 + +**解决方案:** + +```typescript +// 使用 computed +const expensiveValue = computed(() => { + return heavyCalculation(data.value); +}); +``` + +## 注意事项 + +- ⚠️ 不要过早优化 +- ⚠️ 先测量再优化 +- ⚠️ 保持代码可读 +- ⚠️ 避免过度优化 +- ⚠️ 持续监控性能 + +## 相关文档 + +- [DEVELOPMENT.md](../../DEVELOPMENT.md) - 开发文档 +- [TESTING.md](../../TESTING.md) - 测试文档 +- [TROUBLESHOOTING.md](../../TROUBLESHOOTING.md) - 问题排查 diff --git a/.agents/workflows/refactoring.md b/.agents/workflows/refactoring.md new file mode 100644 index 00000000..09f36201 --- /dev/null +++ b/.agents/workflows/refactoring.md @@ -0,0 +1,284 @@ +--- +description: 代码重构流程 +--- + +## 工作流说明 + +此工作流指导如何安全地重构代码。 + +## 步骤 + +### 1. 识别重构需求 + +分析代码中的问题: + +- 代码重复 +- 复杂度过高 +- 性能问题 +- 可读性差 +- 难以维护 + +### 2. 制定重构计划 + +- 确定重构范围 +- 列出具体改进点 +- 评估影响范围 +- 制定测试策略 + +### 3. 创建重构分支 + +```bash +git checkout -b refactor/your-refactor +``` + +### 4. 编写测试 + +如果代码缺少测试,先添加测试: + +```typescript +// src/__tests__/unit/refactorTarget.test.ts +import { describe, it, expect } from "vitest"; +import { functionToRefactor } from "@/modules/example"; + +describe("functionToRefactor", () => { + it("should maintain existing behavior", () => { + const result = functionToRefactor(input); + expect(result).toBe(expected); + }); +}); +``` + +### 5. 逐步重构 + +**原则:** + +- 小步迭代 +- 保持测试通过 +- 不改变外部行为 + +**示例:** + +```typescript +// 重构前 +function processApp(app: any) { + if (app) { + return { + name: app.name, + pkgname: app.pkgname, + version: app.version, + }; + } + return null; +} + +// 重构后 - 添加类型 +interface App { + name: string; + pkgname: string; + version: string; +} + +function processApp(app: App | null): App | null { + if (!app) return null; + + return { + name: app.name, + pkgname: app.pkgname, + version: app.version, + }; +} +``` + +### 6. 运行测试 + +```bash +# 每次重构后运行测试 +npm run test + +# 确保所有测试通过 +npm run test:all +``` + +### 7. 性能验证 + +如果重构涉及性能: + +```bash +# 运行性能测试 +npm run test:perf + +# 对比重构前后性能 +``` + +### 8. 代码审查 + +自我检查: + +- 代码更清晰 +- 性能未下降 +- 测试全部通过 +- 没有引入新问题 + +### 9. 更新文档 + +- 更新相关文档 +- 添加注释说明 +- 更新 CHANGELOG.md + +### 10. 提交代码 + +```bash +git add . +git commit -m "refactor(scope): describe the refactoring" -s +git push origin refactor/your-refactor +``` + +### 11. 创建 Pull Request + +- 说明重构原因 +- 展示改进效果 +- 提供性能对比(如需要) + +### 12. 代码审查 + +- 响应审查意见 +- 确保所有测试通过 +- 合并到 main 分支 + +## 重构原则 + +### 不改变外部行为 + +- 保持 API 兼容 +- 保持输出一致 +- 保持错误处理 + +### 小步迭代 + +- 每次只改一处 +- 频繁运行测试 +- 及时提交代码 + +### 测试驱动 + +- 先写测试 +- 重构代码 +- 确保通过 + +### 保持简单 + +- 减少复杂度 +- 提高可读性 +- 增强可维护性 + +## 常见重构模式 + +### 提取函数 + +```typescript +// 重构前 +function processApps(apps: App[]) { + for (const app of apps) { + if (app.installed) { + console.log(app.name + " is installed"); + } + } +} + +// 重构后 +function logInstalledApp(app: App) { + if (app.installed) { + console.log(`${app.name} is installed`); + } +} + +function processApps(apps: App[]) { + apps.forEach(logInstalledApp); +} +``` + +### 提取类型 + +```typescript +// 重构前 +function createDownload(data: any) { + return { + id: data.id, + name: data.name, + pkgname: data.pkgname, + }; +} + +// 重构后 +interface DownloadData { + id: number; + name: string; + pkgname: string; +} + +function createDownload(data: DownloadData): DownloadItem { + return { + id: data.id, + name: data.name, + pkgname: data.pkgname, + status: "queued", + progress: 0, + downloadedSize: 0, + totalSize: 0, + speed: 0, + timeRemaining: 0, + startTime: Date.now(), + logs: [], + source: "APM Store", + retry: false, + }; +} +``` + +### 简化条件 + +```typescript +// 重构前 +function getStatus(status: string): string { + if (status === "queued") { + return "Queued"; + } else if (status === "downloading") { + return "Downloading"; + } else if (status === "installing") { + return "Installing"; + } else if (status === "completed") { + return "Completed"; + } else if (status === "failed") { + return "Failed"; + } else { + return "Unknown"; + } +} + +// 重构后 +const statusMap: Record = { + queued: "Queued", + downloading: "Downloading", + installing: "Installing", + completed: "Completed", + failed: "Failed", +}; + +function getStatus(status: string): string { + return statusMap[status] || "Unknown"; +} +``` + +## 注意事项 + +- ⚠️ 不要在重构中引入新功能 +- ⚠️ 不要同时重构多处 +- ⚠️ 确保测试覆盖充分 +- ⚠️ 保持提交历史清晰 +- ⚠️ 及时回退有问题的重构 + +## 相关文档 + +- [CONTRIBUTING.md](../../CONTRIBUTING.md) - 贡献指南 +- [DEVELOPMENT.md](../../DEVELOPMENT.md) - 开发文档 +- [TESTING.md](../../TESTING.md) - 测试文档 diff --git a/.agents/workflows/release.md b/.agents/workflows/release.md new file mode 100644 index 00000000..be960e5d --- /dev/null +++ b/.agents/workflows/release.md @@ -0,0 +1,211 @@ +--- +description: 发布流程 +--- + +## 工作流说明 + +此工作流指导如何发布新版本。 + +## 步骤 + +### 1. 更新版本号 + +```bash +# 更新版本 +npm version patch # 1.0.0 → 1.0.1 +npm version minor # 1.0.0 → 1.1.0 +npm version major # 1.0.0 → 2.0.0 + +# 或手动编辑 package.json +``` + +### 2. 更新 CHANGELOG.md + +```bash +# 生成变更日志 +npm run changelog +``` + +或手动更新: + +```markdown +## [1.0.1](https://github.com/elysia-best/apm-app-store/compare/v1.0.0...v1.0.1) (2026-03-10) + +### Bug Fixes + +- fix(ui): correct dark mode toggle persistence (#123) + +### Features + +- feat(install): add retry mechanism for failed installations (#124) +``` + +### 3. 运行完整测试 + +```bash +# 运行所有测试 +npm run test:all + +# 运行代码检查 +npm run lint +npm run format + +# 构建项目 +npm run build:vite +``` + +### 4. 提交变更 + +```bash +git add . +git commit -m "chore(release): bump version to x.x.x" -s +git push origin main +``` + +### 5. 创建 Git 标签 + +```bash +# 创建标签 +git tag v{version} + +# 推送标签 +git push origin v{version} +``` + +### 6. 触发 CI 构建 + +推送标签后会自动触发 GitHub Actions 构建。 + +### 7. 验证构建 + +在 GitHub Actions 页面查看: + +- 所有测试通过 +- 构建成功 +- 构建产物生成 + +### 8. 检查 Release + +GitHub Actions 会自动创建 Release: + +- 访问 Releases 页面 +- 检查版本信息 +- 确认构建产物 + +### 9. 发布说明 + +如果需要,更新 Release 说明: + +- 添加主要变更 +- 添加已知问题 +- 添加升级说明 + +### 10. 通知用户 + +- 更新 README +- 发布公告 +- 通知用户 + +## 发布检查清单 + +### 代码质量 + +- [ ] 所有测试通过 +- [ ] 代码检查通过 +- [ ] 没有已知严重 Bug +- [ ] 性能测试通过 + +### 文档 + +- [ ] CHANGELOG.md 更新 +- [ ] README.md 更新(如需要) +- [ ] API 文档更新(如需要) + +### 构建 + +- [ ] 本地构建成功 +- [ ] CI 构建成功 +- [ ] 构建产物正确 + +### 发布 + +- [ ] 版本号正确 +- [ ] 标签已推送 +- [ ] Release 已创建 +- [ ] 构建产物已上传 + +## 版本号规范 + +遵循 [Semantic Versioning](https://semver.org/): + +- **MAJOR:** 不兼容的 API 变更 +- **MINOR:** 向后兼容的功能新增 +- **PATCH:** 向后兼容的 Bug 修复 + +### 示例 + +``` +4.9.9 → 4.9.10 (PATCH: Bug 修复) +4.9.9 → 4.10.0 (MINOR: 新功能) +4.9.9 → 5.0.0 (MAJOR: 重大变更) +``` + +## 发布后 + +### 更新开发分支 + +```bash +git checkout develop +git merge main +git push origin develop +``` + +### 监控反馈 + +- 收集用户反馈 +- 监控 Bug 报告 +- 记录性能数据 + +### 准备下一个版本 + +- 创建新的 Issue +- 规划新功能 +- 评估技术债务 + +## 回滚流程 + +如果发现严重问题: + +### 1. 立即停止推广 + +- 通知用户暂停升级 +- 更新下载页面 + +### 2. 修复问题 + +```bash +git checkout main +git checkout -b fix/critical-issue +# 修复问题 +git push origin fix/critical-issue +``` + +### 3. 紧急发布 + +```bash +npm version patch +git tag -a v{x.x.x} -m "Hotfix: description" +git push origin v{x.x.x} +``` + +### 4. 通知用户 + +- 发布新版本 +- 说明问题和修复 +- 提供升级说明 + +## 相关文档 + +- [DEPLOYMENT.md](../../DEPLOYMENT.md) - 部署文档 +- [CONTRIBUTING.md](../../CONTRIBUTING.md) - 贡献指南 +- [CHANGELOG.md](../../CHANGELOG.md) - 变更日志 diff --git a/.agents/workflows/security-audit.md b/.agents/workflows/security-audit.md new file mode 100644 index 00000000..fab0ada7 --- /dev/null +++ b/.agents/workflows/security-audit.md @@ -0,0 +1,435 @@ +--- +description: 安全审计流程 +--- + +## 工作流说明 + +此工作流指导如何进行安全审计。 + +## 步骤 + +### 1. 确定审计范围 + +确定需要审计的方面: + +- 代码安全 +- 依赖安全 +- 数据安全 +- 网络安全 +- 权限管理 + +### 2. 创建审计分支 + +```bash +git checkout -b security/security-audit +``` + +### 3. 代码安全审计 + +#### 检查 SQL 注入 + +```typescript +// ❌ 不安全 +const query = `SELECT * FROM apps WHERE name = '${appName}'`; + +// ✅ 安全 +const query = "SELECT * FROM apps WHERE name = ?"; +db.query(query, [appName]); +``` + +#### 检查 XSS 攻击 + +```typescript +// ❌ 不安全 +element.innerHTML = userInput; + +// ✅ 安全 +element.textContent = userInput; +// 或使用 DOMPurify +import DOMPurify from "dompurify"; +element.innerHTML = DOMPurify.sanitize(userInput); +``` + +#### 检查命令注入 + +```typescript +// ❌ 不安全 +const cmd = `apm install ${packageName}`; +exec(cmd); + +// ✅ 安全 +const args = ["apm", "install", packageName]; +spawn("apm", args); +``` + +#### 检查路径遍历 + +```typescript +// ❌ 不安全 +const filePath = path.join(basePath, userInput); + +// ✅ 安全 +const safePath = path.normalize(userInput).replace(/^(\.\.(\/|\\|$))+/, ""); +const filePath = path.join(basePath, safePath); +``` + +### 4. 依赖安全审计 + +```bash +# 检查依赖漏洞 +npm audit + +# 自动修复 +npm audit fix + +# 手动修复 +npm audit fix --force +``` + +#### 检查 package.json + +```json +{ + "dependencies": { + "axios": "^1.13.2", + "pino": "^10.3.0" + }, + "devDependencies": { + "@playwright/test": "^1.40.0" + } +} +``` + +### 5. 数据安全审计 + +#### 检查敏感信息 + +```typescript +// ❌ 不安全 - 硬编码密钥 +const apiKey = "sk-1234567890"; + +// ✅ 安全 - 使用环境变量 +const apiKey = process.env.API_KEY; + +// ❌ 不安全 - 记录敏感信息 +logger.info({ password: user.password }, "User logged in"); + +// ✅ 安全 - 不记录敏感信息 +logger.info({ userId: user.id }, "User logged in"); +``` + +#### 检查数据加密 + +```typescript +// 加密敏感数据 +import crypto from "crypto"; + +function encrypt(text: string, key: string): string { + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv("aes-256-cbc", key, iv); + let encrypted = cipher.update(text, "utf8", "hex"); + encrypted += cipher.final("hex"); + return iv.toString("hex") + ":" + encrypted; +} +``` + +### 6. 网络安全审计 + +#### 检查 HTTPS + +```typescript +// ❌ 不安全 - HTTP +const baseURL = "http://api.example.com"; + +// ✅ 安全 - HTTPS +const baseURL = "https://api.example.com"; +``` + +#### 检查证书验证 + +```typescript +// 配置 Axios 验证证书 +const axiosInstance = axios.create({ + httpsAgent: new https.Agent({ + rejectUnauthorized: true, + }), +}); +``` + +#### 检查 CORS + +```typescript +// 配置 CORS +app.use( + cors({ + origin: "https://yourdomain.com", + credentials: true, + }), +); +``` + +### 7. 权限管理审计 + +#### 检查权限提升 + +```typescript +// 检查 pkexec 可用性 +const checkSuperUserCommand = async (): Promise => { + if (process.getuid && process.getuid() !== 0) { + const { stdout } = await execAsync("which /usr/bin/pkexec"); + return stdout.trim().length > 0 ? "/usr/bin/pkexec" : ""; + } + return ""; +}; +``` + +#### 检查上下文隔离 + +```typescript +// electron/preload/index.ts +// ✅ 安全 - 启用上下文隔离 +contextBridge.exposeInMainWorld("ipcRenderer", { + send: (...args) => ipcRenderer.send(...args), + on: (...args) => ipcRenderer.on(...args), + invoke: (...args) => ipcRenderer.invoke(...args), +}); + +// ❌ 不安全 - 禁用上下文隔离 +contextIsolation: false; +``` + +### 8. 运行安全工具 + +```bash +# 使用 Snyk 扫描 +npx snyk test + +# 使用 npm audit +npm audit + +# 使用 ESLint 安全规则 +npm run lint +``` + +### 9. 修复安全问题 + +根据审计结果修复发现的问题: + +```typescript +// 修复示例 +function validateInput(input: string): boolean { + // 验证输入 + const regex = /^[a-zA-Z0-9-_]+$/; + return regex.test(input); +} + +function sanitizeInput(input: string): string { + // 清理输入 + return input.trim().replace(/[<>]/g, ""); +} +``` + +### 10. 安全测试 + +```typescript +// src/__tests__/security/security.test.ts +import { describe, it, expect } from "vitest"; +import { validateInput, sanitizeInput } from "@/modules/security"; + +describe("Security", () => { + describe("validateInput", () => { + it("should reject malicious input", () => { + expect(validateInput('')).toBe(false); + }); + + it("should accept valid input", () => { + expect(validateInput("valid-app-name")).toBe(true); + }); + }); + + describe("sanitizeInput", () => { + it("should remove dangerous characters", () => { + expect(sanitizeInput("")).toBe("scriptapp/script"); + }); + }); +}); +``` + +### 11. 更新文档 + +- 记录安全问题 +- 说明修复方法 +- 更新安全指南 + +### 12. 提交代码 + +```bash +git add . +git commit -m "security: fix security vulnerabilities" -s +git push origin security/security-audit +``` + +### 13. 创建 Pull Request + +- 说明安全问题 +- 展示修复方法 +- 提供安全测试结果 + +## 安全检查清单 + +### 代码安全 + +- [ ] 输入验证 +- [ ] 输出编码 +- [ ] 参数化查询 +- [ ] 错误处理 +- [ ] 日志安全 + +### 依赖安全 + +- [ ] 定期更新依赖 +- [ ] 使用 `npm audit` +- [ ] 检查已知漏洞 +- [ ] 使用可信源 + +### 数据安全 + +- [ ] 敏感数据加密 +- [ ] 不记录敏感信息 +- [ ] 使用环境变量 +- [ ] 安全存储 + +### 网络安全 + +- [ ] 使用 HTTPS +- [ ] 验证证书 +- [ ] 配置 CORS +- [ ] 防止 CSRF + +### 权限管理 + +- [ ] 最小权限原则 +- [ ] 上下文隔离 +- [ ] 权限检查 +- [ ] 审计日志 + +## 常见安全问题 + +### 1. XSS 攻击 + +**问题:** 用户输入包含恶意脚本 + +**解决方案:** + +```typescript +import DOMPurify from "dompurify"; + +function sanitizeHTML(html: string): string { + return DOMPurify.sanitize(html); +} +``` + +### 2. SQL 注入 + +**问题:** 恶意 SQL 代码注入 + +**解决方案:** + +```typescript +// 使用参数化查询 +db.query("SELECT * FROM apps WHERE name = ?", [appName]); +``` + +### 3. 命令注入 + +**问题:** 恶意命令注入 + +**解决方案:** + +```typescript +// 使用 spawn 而非 exec +const args = ["apm", "install", packageName]; +spawn("apm", args); +``` + +### 4. 路径遍历 + +**问题:** 访问未授权文件 + +**解决方案:** + +```typescript +// 验证路径 +const safePath = path.normalize(userPath).replace(/^(\.\.(\/|\\|$))+/, ""); +``` + +### 5. 敏感信息泄露 + +**问题:** 日志中包含敏感信息 + +**解决方案:** + +```typescript +// 不记录敏感信息 +logger.info({ userId: user.id }, "User logged in"); +``` + +## 安全最佳实践 + +### 1. 最小权限原则 + +只授予必要的权限,避免过度授权。 + +### 2. 深度防御 + +多层安全防护,不依赖单一安全措施。 + +### 3. 输入验证 + +验证所有输入,包括用户输入和 API 响应。 + +### 4. 输出编码 + +对输出进行编码,防止 XSS 攻击。 + +### 5. 定期审计 + +定期进行安全审计,及时发现和修复问题。 + +### 6. 安全更新 + +及时更新依赖和系统,修复已知漏洞。 + +## 安全工具 + +### 静态分析 + +- ESLint +- TypeScript +- SonarQube + +### 动态分析 + +- OWASP ZAP +- Burp Suite +- Snyk + +### 依赖扫描 + +- npm audit +- Snyk +- Dependabot + +## 注意事项 + +- ⚠️ 不要忽视安全问题 +- ⚠️ 及时修复漏洞 +- ⚠️ 定期更新依赖 +- ⚠️ 保持安全意识 +- ⚠️ 遵循安全最佳实践 + +## 相关文档 + +- [CONTRIBUTING.md](../../CONTRIBUTING.md) - 贡献指南 +- [DEVELOPMENT.md](../../DEVELOPMENT.md) - 开发文档 +- [SECURITY.md](../../SECURITY.md) - 安全政策 diff --git a/.agents/workflows/testing.md b/.agents/workflows/testing.md new file mode 100644 index 00000000..bea9e889 --- /dev/null +++ b/.agents/workflows/testing.md @@ -0,0 +1,108 @@ +--- +description: 测试编写流程 +--- + +## 工作流说明 + +此工作流指导如何为新功能或 Bug 修复编写测试。 + +## 步骤 + +### 1. 确定测试范围 + +分析需要测试的功能点: + +- 单元测试:测试独立函数/组件 +- 集成测试:测试模块间交互 +- E2E 测试:测试完整用户流程 + +### 2. 编写单元测试(Vitest) + +在 `src/__tests__/unit/` 目录下创建测试文件: + +```typescript +import { describe, it, expect } from "vitest"; +import { someFunction } from "@/modules/example"; + +describe("someFunction", () => { + it("should return expected result", () => { + const result = someFunction(input); + expect(result).toBe(expected); + }); +}); +``` + +### 3. 编写组件测试 + +```typescript +import { describe, it, expect } from "vitest"; +import { mount } from "@vue/test-utils"; +import AppCard from "@/components/AppCard.vue"; + +describe("AppCard", () => { + it("should render app name", () => { + const wrapper = mount(AppCard, { + props: { + app: { + name: "Test App", + pkgname: "test-app", + }, + }, + }); + expect(wrapper.text()).toContain("Test App"); + }); +}); +``` + +### 4. 编写 E2E 测试(Playwright) + +在 `e2e/` 目录下创建测试文件: + +```typescript +import { test, expect } from "@playwright/test"; + +test("install app from store", async ({ page }) => { + await page.goto("http://localhost:3344"); + await page.click("text=Test App"); + await page.click('button:has-text("安装")'); + await expect(page.locator(".install-progress")).toBeVisible(); +}); +``` + +### 5. 运行测试 + +```bash +# 运行单元测试 +npm run test + +# 运行测试并监听 +npm run test:watch + +# 运行 E2E 测试 +npm run test:e2e + +# 生成覆盖率报告 +npm run test:coverage +``` + +### 6. 确保测试通过 + +- 所有单元测试必须通过 +- E2E 测试覆盖主要用户流程 +- 测试覆盖率不低于 70% + +### 7. 提交代码 + +测试通过后,提交代码并创建 PR。 + +## 注意事项 + +- ⚠️ 不要测试第三方库的功能 +- ⚠️ 保持测试独立性和可重复性 +- ⚠️ 使用有意义的测试名称 +- ⚠️ Mock 外部依赖(APM 命令、API 调用) + +## 相关文档 + +- [TESTING.md](../../TESTING.md) - 测试框架和规范 +- [DEVELOPMENT.md](../../DEVELOPMENT.md) - 开发文档 diff --git a/.agents/workflows/代码审查.md b/.agents/workflows/代码审查.md deleted file mode 100644 index 4db78530..00000000 --- a/.agents/workflows/代码审查.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -description: 审查 ---- - -审查是否符合代码规范 -是否有代码缺陷 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2bc0b863..5761404b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,11 +1,52 @@ --- - -name: 🐞 Bug report -about: Create a report to help us improve -title: "[Bug] the title of bug report" +name: Bug 报告 +about: 创建一个 Bug 报告以帮助我们改进 +title: "[Bug] " labels: bug -assignees: '' - +assignees: "" --- -#### Describe the bug +## 描述 + +清晰简洁地描述这个 Bug 是什么。 + +## 复现步骤 + +1. 打开 '...' +2. 点击 '....' +3. 滚动到 '....' +4. 看到错误 + +## 期望行为 + +清晰简洁地描述你期望发生什么。 + +## 实际行为 + +清晰简洁地描述实际发生了什么。 + +## 截图 + +如果适用,添加截图以帮助解释你的问题。 + +## 环境信息 + +**操作系统:** [例如: Ubuntu 22.04] + +**APM 版本:** [例如: 1.0.0] + +**应用商店版本:** [例如: 4.9.9] + +**桌面环境:** [例如: GNOME, KDE] + +## 日志 + +如果相关,粘贴日志到以下区域(使用代码块): + +``` +粘贴日志内容 +``` + +## 额外上下文 + +添加任何其他关于问题的上下文信息。 diff --git a/.github/ISSUE_TEMPLATE/help_wanted.md b/.github/ISSUE_TEMPLATE/help_wanted.md index 6fba797e..8e24b4a5 100644 --- a/.github/ISSUE_TEMPLATE/help_wanted.md +++ b/.github/ISSUE_TEMPLATE/help_wanted.md @@ -1,10 +1,23 @@ --- -name: 🥺 Help wanted -about: Confuse about the use of electron-vue-vite -title: "[Help] the title of help wanted report" -labels: help wanted -assignees: '' - +name: 功能请求 / 帮助请求 +about: 为这个项目建议一个新想法 +title: "[Feature] " +labels: enhancement +assignees: "" --- -#### Describe the problem you confuse +## 你的功能请求是否与问题有关? + +清晰简洁地描述问题。例如:我在 [...] 时总是感到沮丧 + +## 你想要的解决方案是什么? + +清晰简洁地描述你想要发生什么。 + +## 你考虑过哪些替代方案? + +清晰简洁地描述你考虑过的任何替代解决方案或功能。 + +## 额外上下文 + +添加任何其他关于功能请求的上下文或截图。 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6dcf3a08..c45b8818 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,6 +15,7 @@ on: - ".gitignore" - ".github/**" - "!.github/workflows/build.yml" + - "!.github/workflows/test.yml" pull_request: branches: [main] paths-ignore: @@ -32,7 +33,28 @@ permissions: contents: write jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 20 + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm run test + + - name: Run lint + run: npm run lint + build: + needs: test runs-on: ${{ matrix.os }} container: ${{ matrix.docker_image }} @@ -46,7 +68,6 @@ jobs: docker_image: "debian:12" - package: rpm docker_image: "almalinux:8" - steps: - name: Install Build Dependencies @@ -71,7 +92,7 @@ jobs: node-version: 20 - name: Install Dependencies - run: | + run: | npm install - name: Download host-spawn @@ -119,4 +140,4 @@ jobs: files: | artifacts/**/*.deb artifacts/**/*.rpm - generate_release_notes: true \ No newline at end of file + generate_release_notes: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..6b23e95e --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,83 @@ +name: Test + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +jobs: + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 20 + + - name: Install dependencies + run: npm ci + + - name: Run unit tests + run: npm run test -- --coverage + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + files: ./coverage/lcov.info + flags: unittests + name: codecov-umbrella + + e2e-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 20 + + - name: Install dependencies + run: npm ci + + - name: Install Playwright Browsers + run: npx playwright install --with-deps chromium + + - name: Run E2E tests + run: npm run test:e2e + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v6 + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 + + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 20 + + - name: Install dependencies + run: npm ci + + - name: Run ESLint + run: npm run lint + + - name: Check formatting + run: npm run format -- --check diff --git a/.gitignore b/.gitignore index 4fa88e0d..c9dbc7d7 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,15 @@ dist-electron release *.local +# Test coverage +coverage +.nyc_output + +# Playwright +test-results +playwright-report +playwright/.cache + # Editor directories and files .vscode/.debug.env .idea diff --git a/AGENTS.md b/AGENTS.md index db3d87a6..71e08164 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,121 +1,128 @@ -# AI Coding Guidance for APM App Store +# APM 应用商店 - AI 编码指南 -**Repository:** elysia-best/apm-app-store -**Project Type:** Electron + Vue 3 + Vite Desktop Application -**Purpose:** Desktop app store client for APM (AmberPM) package manager -**License:** MulanPSL-2.0 +**仓库:** elysia-best/apm-app-store +**项目类型:** Electron + Vue 3 + Vite 桌面应用 +**用途:** APM (AmberPM) 包管理器的桌面应用商店客户端 +**许可证:** GPL-3.0 --- -If you are an AI coding agent working on this repo, make sure to follow the guidelines below: +如果你是 AI 编码代理,在此仓库工作时,请遵循以下指南: -## 🏗️ Project Architecture Overview +## 🏗️ 项目架构概览 -### Technology Stack -- **Frontend Framework:** Vue 3 with Composition API (` ``` -### Props and Events Pattern +### Props 和 Events 模式 ```typescript -// Props definition +// Props 定义 const props = defineProps<{ app: App | null; show: boolean; }>(); -// Emits definition +// Emits 定义 const emit = defineEmits<{ close: []; install: []; remove: []; }>(); -// Usage -emit('install'); +// 使用 +emit("install"); ``` -### IPC Event Listeners in Vue +### Vue 中的 IPC 事件监听 -**Always use in `onMounted` for proper cleanup:** +**始终在 `onMounted` 中使用以便正确清理:** ```typescript onMounted(() => { - window.ipcRenderer.on('install-complete', (_event: IpcRendererEvent, result: DownloadResult) => { - // Handle event - }); + window.ipcRenderer.on( + "install-complete", + (_event: IpcRendererEvent, result: DownloadResult) => { + // 处理事件 + }, + ); - window.ipcRenderer.on('install-log', (_event: IpcRendererEvent, log: InstallLog) => { - // Handle log - }); + window.ipcRenderer.on( + "install-log", + (_event: IpcRendererEvent, log: InstallLog) => { + // 处理日志 + }, + ); }); ``` --- -## 🔧 Main Process Patterns +## 🔧 主进程模式 -### Spawning APM Commands +### 生成 APM 命令 ```typescript -import { spawn } from 'node:child_process'; +import { spawn } from "node:child_process"; -// Check for privilege escalation +// 检查权限提升 const superUserCmd = await checkSuperUserCommand(); const execCommand = superUserCmd.length > 0 ? superUserCmd : SHELL_CALLER_PATH; -const execParams = superUserCmd.length > 0 - ? [SHELL_CALLER_PATH, 'apm', 'install', '-y', pkgname] - : ['apm', 'install', '-y', pkgname]; +const execParams = + superUserCmd.length > 0 + ? [SHELL_CALLER_PATH, "apm", "install", "-y", pkgname] + : ["apm", "install", "-y", pkgname]; -// Spawn process +// 生成进程 const child = spawn(execCommand, execParams, { shell: true, env: process.env, }); -// Stream output -child.stdout.on('data', (data) => { - webContents.send('install-log', { id, time: Date.now(), message: data.toString() }); +// 流式输出 +child.stdout.on("data", (data) => { + webContents.send("install-log", { + id, + time: Date.now(), + message: data.toString(), + }); }); -// Handle completion -child.on('close', (code) => { +// 处理完成 +child.on("close", (code) => { const success = code === 0; - webContents.send('install-complete', { id, success, exitCode: code, /* ... */ }); + webContents.send("install-complete", { + id, + success, + exitCode: code /* ... */, + }); }); ``` -### Parsing APM Output +### 解析 APM 输出 -**APM outputs are text-based with specific formats:** +**APM 输出是基于文本的特定格式:** ```typescript -// Installed packages format: "pkgname/repo,version arch [flags]" -// Example: "code/stable,1.108.2 amd64 [installed]" +// 已安装包格式: "pkgname/repo,version arch [flags]" +// 示例: "code/stable,1.108.2 amd64 [installed]" const parseInstalledList = (output: string) => { const apps: InstalledAppInfo[] = []; - const lines = output.split('\n'); + const lines = output.split("\n"); for (const line of lines) { - const match = line.trim().match(/^(\S+)\/\S+,\S+\s+(\S+)\s+(\S+)\s+\[(.+)\]$/); + const match = line + .trim() + .match(/^(\S+)\/\S+,\S+\s+(\S+)\s+(\S+)\s+\[(.+)\]$/); if (match) { apps.push({ pkgname: match[1], @@ -312,23 +339,23 @@ const parseInstalledList = (output: string) => { --- -## 🌐 API Integration +## 🌐 API 集成 -### Base Configuration +### 基础配置 -```typescript +````typescript // src/global/storeConfig.ts export const APM_STORE_BASE_URL = 'https://erotica.spark-app.store'; -// URL structure: -// /{arch}/{category}/applist.json - App list -// /{arch}/{category}/{pkgname}/icon.png - App icon -// /{arch}/{category}/{pkgname}/screen_N.png - Screenshots (1-5) -// /{arch}/categories.json - Categories mapping +// URL 结构: +// /{arch}/{category}/applist.json - 应用列表 +// /{arch}/{category}/{pkgname}/icon.png - 应用图标 +// /{arch}/{category}/{pkgname}/screen_N.png - 截图 (1-5) +// /{arch}/categories.json - 分类映射 -### Home (主页) 页面数据 +### 首页 (主页) 数据 -The store may provide a special `home` directory under `{arch}` for a localized homepage. Two JSON files are expected: +商店可能会在 `{arch}` 下提供一个特殊的 `home` 目录用于本地化首页。预期有两个 JSON 文件: - `homelinks.json` — 用于构建首页的轮播或链接块。每个条目示例: @@ -340,49 +367,58 @@ The store may provide a special `home` directory under `{arch}` for a localized "type": "_blank", "url": "https://bbs.spark-app.store/" } -``` +```` -- `homelist.json` — 描述若干推荐应用列表,每项引用一个 JSON 列表(`jsonUrl`): +- `homelist.json` — 描述若干推荐应用列表,每项引用一个 JSON 列表(`jsonUrl`): ```json [ - { "name":"装机必备", "type":"appList", "jsonUrl":"/home/lists/NecessaryforInstallation.json" } + { + "name": "装机必备", + "type": "appList", + "jsonUrl": "/home/lists/NecessaryforInstallation.json" + } ] ``` -Parsing rules used by the app: -- Resolve `imgUrl` by prefixing: `${APM_STORE_BASE_URL}/{arch}${imgUrl}`. +应用使用的解析规则: + +- 通过前缀解析 `imgUrl`: `${APM_STORE_BASE_URL}/{arch}${imgUrl}`。 - `type: _blank` → 使用系统浏览器打开链接;`type: _self` → 在当前页面打开。 -- For `homelist.json` entries with `type: "appList"`, fetch the referenced `jsonUrl` and map each item to the app shape used by the UI: +- 对于 `homelist.json` 中 `type: "appList"` 的条目,获取引用的 `jsonUrl` 并将每个项映射到 UI 使用的应用形状: - `Name` → `name` - `Pkgname` → `pkgname` - `Category` → `category` - `More` → `more` -Where to implement: -- Renderer: `src/App.vue` loads and normalizes `homelinks.json` and `homelist.json` on selecting the `home` category and exposes data to a new `HomeView` component. -- Component: `src/components/HomeView.vue` renders link cards and recommended app sections (re-uses `AppCard.vue`). +实现位置: -Notes: -- The `home` directory path is: `/{arch}/home/` under the configured `APM_STORE_BASE_URL`. -- Missing or partially invalid files are handled gracefully — individual failures don't block showing other home sections. -``` +- 渲染进程: `src/App.vue` 在选择 `home` 分类时加载并规范化 `homelinks.json` 和 `homelist.json`,并将数据暴露给新的 `HomeView` 组件。 +- 组件: `src/components/HomeView.vue` 渲染链接卡片和推荐应用部分(复用 `AppCard.vue`)。 -### Axios Usage +注意事项: + +- `home` 目录路径是: 配置的 `APM_STORE_BASE_URL` 下的 `/{arch}/home/`。 +- 缺失或部分无效的文件会被优雅地处理 — 个别失败不会阻止显示其他首页部分。 + +```` + +### Axios 使用 ```typescript const axiosInstance = axios.create({ baseURL: APM_STORE_BASE_URL, - timeout: 1000, // Note: Very short timeout! + timeout: 1000, // 注意: 非常短的超时时间! }); -// Loading apps by category +// 按分类加载应用 const response = await axiosInstance.get( `/${window.apm_store.arch}/${category}/applist.json` ); -``` +```` + +**开发代理 (vite.config.ts):** -**Development Proxy (vite.config.ts):** ```typescript server: { proxy: { @@ -397,373 +433,399 @@ server: { --- -## 🎯 Deep Link Protocol (SPK URI Scheme) +## 🎯 Deep Link 协议 (SPK URI Scheme) **URL Scheme:** `spk://` -### Supported SPK URI Format +### 支持的 SPK URI 格式 -Format: `spk://search/{pkgname}` +格式: `spk://search/{pkgname}` -**Examples:** -- `spk://search/code` - Search for and open "code" app -- `spk://search/steam` - Search for and open "steam" app -- `spk://search/store.spark-app.hmcl` - Search for and open "HMCL" game +**示例:** -### Implementation Pattern +- `spk://search/code` - 搜索并打开 "code" 应用 +- `spk://search/steam` - 搜索并打开 "steam" 应用 +- `spk://search/store.spark-app.hmcl` - 搜索并打开 "HMCL" 游戏 + +### 实现模式 ```typescript -// electron/main/deeplink.ts - Parse command line and route +// electron/main/deeplink.ts - 解析命令行并路由 export function handleCommandLine(commandLine: string[]) { - const deeplinkUrl = commandLine.find((arg) => - arg.startsWith('spk://') - ); + const deeplinkUrl = commandLine.find((arg) => arg.startsWith("spk://")); if (!deeplinkUrl) return; try { const url = new URL(deeplinkUrl); const action = url.hostname; // 'search' - if (action === 'search') { - // Format: spk://search/pkgname - // url.pathname will be '/pkgname' - const pkgname = url.pathname.split('/').filter(Boolean)[0]; + if (action === "search") { + // 格式: spk://search/pkgname + // url.pathname 将是 '/pkgname' + const pkgname = url.pathname.split("/").filter(Boolean)[0]; if (pkgname) { - listeners.emit('search', { pkgname }); + listeners.emit("search", { pkgname }); } } } catch (error) { - logger.error({ err: error }, 'Error parsing SPK URI'); + logger.error({ err: error }, "Error parsing SPK URI"); } } -// src/App.vue - Handle in renderer +// src/App.vue - 在渲染进程中处理 window.ipcRenderer.on( - 'deep-link-search', + "deep-link-search", (_event: IpcRendererEvent, data: { pkgname: string }) => { - // Trigger search with the pkgname + // 使用 pkgname 触发搜索 searchQuery.value = data.pkgname; - } + }, ); ``` --- -## 🛡️ Security Considerations +## 🛡️ 安全考虑 -### Privilege Escalation +### 权限提升 -**Always check for `pkexec` availability:** +**始终检查 `pkexec` 的可用性:** ```typescript const checkSuperUserCommand = async (): Promise => { if (process.getuid && process.getuid() !== 0) { - const { stdout } = await execAsync('which /usr/bin/pkexec'); - return stdout.trim().length > 0 ? '/usr/bin/pkexec' : ''; + const { stdout } = await execAsync("which /usr/bin/pkexec"); + return stdout.trim().length > 0 ? "/usr/bin/pkexec" : ""; } - return ''; + return ""; }; ``` -### Context Isolation +### 上下文隔离 -**Current Status:** Context isolation is **enabled** (default Electron behavior). +**当前状态:** 上下文隔离已 **启用** (Electron 默认行为)。 + +**通过 Preload 安全地暴露 IPC:** -**IPC Exposed via Preload (Safe):** ```typescript // electron/preload/index.ts -contextBridge.exposeInMainWorld('ipcRenderer', { +contextBridge.exposeInMainWorld("ipcRenderer", { on: (...args) => ipcRenderer.on(...args), send: (...args) => ipcRenderer.send(...args), invoke: (...args) => ipcRenderer.invoke(...args), }); ``` -**⚠️ Do NOT enable nodeIntegration or disable contextIsolation!** +**⚠️ 不要启用 nodeIntegration 或禁用 contextIsolation!** --- -## 🎨 UI/UX Patterns +## 🎨 UI/UX 模式 -### Tailwind CSS Usage +### Tailwind CSS 使用 + +**暗色模式支持:** -**Dark Mode Support:** ```vue
- +
``` -**Theme Toggle:** +**主题切换:** + ```typescript const isDarkTheme = ref(false); watch(isDarkTheme, (newVal) => { - localStorage.setItem('theme', newVal ? 'dark' : 'light'); - document.documentElement.classList.toggle('dark', newVal); + localStorage.setItem("theme", newVal ? "dark" : "light"); + document.documentElement.classList.toggle("dark", newVal); }); ``` -### Modal Pattern +### 模态框模式 ```vue ``` -### Loading States +### 加载状态 ```typescript const loading = ref(true); -// In template +// 在模板中
Loading...
{{ apps.length }} apps
``` --- -## 🧪 Testing & Quality +## 🧪 测试与质量 -### ESLint Configuration +### ESLint 配置 ```typescript // eslint.config.ts export default defineConfig([ - globalIgnores(['**/3rdparty/**', '**/node_modules/**', '**/dist/**', '**/dist-electron/**']), + globalIgnores([ + "**/3rdparty/**", + "**/node_modules/**", + "**/dist/**", + "**/dist-electron/**", + ]), tseslint.configs.recommended, - pluginVue.configs['flat/essential'], + pluginVue.configs["flat/essential"], eslintConfigPrettier, eslintPluginPrettierRecommended, ]); ``` -### TypeScript Configuration +### TypeScript 配置 ```json { "compilerOptions": { - "strict": true, // Strict mode enabled - "noEmit": true, // No emit (Vite handles build) + "strict": true, // 严格模式已启用 + "noEmit": true, // 不输出 (Vite 处理构建) "module": "ESNext", "target": "ESNext", - "jsx": "preserve", // Vue JSX - "resolveJsonModule": true // Import JSON files + "jsx": "preserve", // Vue JSX + "resolveJsonModule": true // 导入 JSON 文件 } } ``` -### Code Quality Commands +### 代码质量命令 ```bash -npm run lint # Run ESLint -npm run lint:fix # Auto-fix issues -npm run format # Format with Prettier +npm run lint # 运行 ESLint +npm run lint:fix # 自动修复问题 +npm run format # 使用 Prettier 格式化 ``` --- -## 🚀 Build & Development +## 🚀 构建与开发 -### Development Mode +### 开发模式 ```bash -npm run dev # Start dev server (Vite + Electron) +npm run dev # 启动开发服务器 (Vite + Electron) ``` -**Dev Server:** `http://127.0.0.1:3344/` (port from package.json) +**开发服务器:** `http://127.0.0.1:3344/` (来自 package.json) -### Production Build +### 生产构建 ```bash -npm run build # Build all (deb + rpm) -npm run build:deb # Build Debian package only -npm run build:rpm # Build RPM package only +npm run build # 构建所有 (deb + rpm) +npm run build:deb # 仅构建 Debian 包 +npm run build:rpm # 仅构建 RPM 包 ``` -**Build Output:** -- `dist-electron/` - Compiled Electron code -- `dist/` - Compiled renderer assets -- Packaged app in project root +**构建输出:** -### Build Configuration +- `dist-electron/` - 编译的 Electron 代码 +- `dist/` - 编译的渲染器资源 +- 打包的应用在项目根目录 + +### 构建配置 **electron-builder.yml:** + - App ID: `cn.eu.org.simplelinux.apmstore` -- Linux targets: deb, rpm -- Includes extras/ directory in resources -- Auto-update disabled (Linux package manager handles updates) +- Linux 目标: deb, rpm +- 包含 extras/ 目录在资源中 +- 自动更新禁用 (Linux 包管理器处理更新) --- -## 📦 Important Files to Understand +## 📦 重要文件说明 ### 1. electron/main/backend/install-manager.ts -**Purpose:** Core package management logic -**Key Responsibilities:** -- Task queue management -- APM command spawning -- Progress reporting -- Installed/upgradable list parsing -**Critical Functions:** -- `processNextInQueue()` - Task processor -- `parseInstalledList()` - Parse APM output -- `checkSuperUserCommand()` - Privilege escalation +**用途:** 核心包管理逻辑 +**主要职责:** + +- 任务队列管理 +- APM 命令生成 +- 进度报告 +- 已安装/可升级列表解析 + +**关键函数:** + +- `processNextInQueue()` - 任务处理器 +- `parseInstalledList()` - 解析 APM 输出 +- `checkSuperUserCommand()` - 权限提升 ### 2. src/App.vue -**Purpose:** Root component -**Key Responsibilities:** -- App state management -- Category/app loading -- Modal orchestration -- Deep link handling + +**用途:** 根组件 +**主要职责:** + +- 应用状态管理 +- 分类/应用加载 +- 模态框协调 +- Deep Link 处理 ### 3. src/global/downloadStatus.ts -**Purpose:** Download queue state -**Key Features:** -- Reactive download list -- Download item CRUD operations -- Change watchers for UI updates + +**用途:** 下载队列状态 +**关键特性:** + +- 响应式下载列表 +- 下载项 CRUD 操作 +- UI 更新的变化监听器 ### 4. electron/preload/index.ts -**Purpose:** Renderer-Main bridge -**Key Features:** -- IPC API exposure -- Architecture detection -- Loading animation + +**用途:** 渲染进程-主进程桥梁 +**关键特性:** + +- IPC API 暴露 +- 架构检测 +- 加载动画 ### 5. vite.config.ts -**Purpose:** Build configuration -**Key Features:** -- Electron plugin setup -- Dev server proxy -- Tailwind integration + +**用途:** 构建配置 +**关键特性:** + +- Electron 插件设置 +- 开发服务器代理 +- Tailwind 集成 --- -## 🐛 Common Pitfalls & Solutions +## 🐛 常见陷阱与解决方案 -### 1. Duplicate Task Handling +### 1. 重复任务处理 + +**问题:** 用户多次点击安装 +**解决方案:** -**Problem:** User clicks install multiple times -**Solution:** ```typescript if (tasks.has(id) && !download.retry) { - logger.warn('Task already exists, ignoring duplicate'); + logger.warn("Task already exists, ignoring duplicate"); return; } ``` -### 2. Window Close Behavior +### 2. 窗口关闭行为 + +**问题:** 任务运行时关闭窗口 +**解决方案:** -**Problem:** Closing window while tasks are running -**Solution:** ```typescript -win.on('close', (event) => { +win.on("close", (event) => { event.preventDefault(); if (tasks.size > 0) { - win.hide(); // Hide instead of closing + win.hide(); // 隐藏而不是关闭 win.setSkipTaskbar(true); } else { - win.destroy(); // Allow close if no tasks + win.destroy(); // 没有任务时允许关闭 } }); ``` -### 3. App Data Normalization +### 3. 应用数据规范化 + +**问题:** API 返回 PascalCase,应用使用 camelCase +**解决方案:** -**Problem:** API returns PascalCase, app uses camelCase -**Solution:** ```typescript const normalizedApp: App = { name: appJson.Name, pkgname: appJson.Pkgname, version: appJson.Version, - // ... map all fields + // ... 映射所有字段 }; ``` -### 4. Screenshot Loading +### 4. 截图加载 + +**问题:** 并非所有应用都有 5 张截图 +**解决方案:** -**Problem:** Not all apps have 5 screenshots -**Solution:** ```typescript for (let i = 1; i <= 5; i++) { const img = new Image(); img.src = screenshotUrl; img.onload = () => screenshots.value.push(screenshotUrl); - // No onerror handler - silently skip missing images + // 没有 onerror 处理器 - 静默跳过缺失的图片 } ``` --- -## 📚 Logging Best Practices +## 📚 日志最佳实践 -### Pino Logger Usage +### Pino Logger 使用 ```typescript -import pino from 'pino'; -const logger = pino({ name: 'module-name' }); +import pino from "pino"; +const logger = pino({ name: "module-name" }); -// Levels: trace, debug, info, warn, error, fatal -logger.info('Application started'); -logger.error({ err }, 'Failed to load apps'); +// 级别: trace, debug, info, warn, error, fatal +logger.info("Application started"); +logger.error({ err }, "Failed to load apps"); logger.warn(`Package ${pkgname} not found`); ``` -### Log Locations +### 日志位置 -**Development:** Console with `pino-pretty` -**Production:** Structured JSON to stdout +**开发:** 控制台使用 `pino-pretty` +**生产:** 结构化 JSON 到 stdout --- -## 🔄 State Management +## 🔄 状态管理 -### Global State (src/global/storeConfig.ts) +### 全局状态 (src/global/storeConfig.ts) ```typescript export const currentApp = ref(null); export const currentAppIsInstalled = ref(false); ``` -**Usage Pattern:** -```typescript -import { currentApp, currentAppIsInstalled } from '@/global/storeConfig'; +**使用模式:** -// Set current app +```typescript +import { currentApp, currentAppIsInstalled } from "@/global/storeConfig"; + +// 设置当前应用 currentApp.value = selectedApp; -// Check installation status -window.ipcRenderer.invoke('check-installed', app.pkgname) +// 检查安装状态 +window.ipcRenderer + .invoke("check-installed", app.pkgname) .then((isInstalled: boolean) => { currentAppIsInstalled.value = isInstalled; }); ``` -### Download Queue (src/global/downloadStatus.ts) +### 下载队列 (src/global/downloadStatus.ts) ```typescript export const downloads = ref([]); -// Add download +// 添加下载 downloads.value.push(newDownload); -// Remove download +// 移除下载 export const removeDownloadItem = (pkgname: string) => { - const index = downloads.value.findIndex(d => d.pkgname === pkgname); + const index = downloads.value.findIndex((d) => d.pkgname === pkgname); if (index !== -1) downloads.value.splice(index, 1); }; -// Watch changes +// 监听变化 export const watchDownloadsChange = (callback: () => void) => { watch(downloads, callback, { deep: true }); }; @@ -771,124 +833,130 @@ export const watchDownloadsChange = (callback: () => void) => { --- -## 🎯 Contribution Guidelines +## 🎯 贡献指南 -### When Adding New Features +### 添加新功能时 -1. **Add TypeScript types first** (src/global/typedefinition.ts) -2. **Update IPC handlers** if main-renderer communication needed -3. **Follow existing component patterns** (props, emits, setup) -4. **Test with actual APM commands** (don't mock in development) -5. **Update README TODO list** when completing tasks +1. **首先添加 TypeScript 类型** (src/global/typedefinition.ts) +2. **更新 IPC 处理器** (如果需要主进程-渲染进程通信) +3. **遵循现有组件模式** (props, emits, setup) +4. **使用实际的 APM 命令测试** (不要在开发中使用 mock) +5. **完成任务时更新 README TODO 列表** -### Code Style +### 代码风格 -- **Use TypeScript strict mode** - no `any` types without `eslint-disable` -- **Avoid used of eslint-disable directly** - use `undefined` instead if you really do not know its type. -- **Prefer Composition API** - `