diff --git a/.claude/skills/gitnexus/gitnexus-cli/SKILL.md b/.claude/skills/gitnexus/gitnexus-cli/SKILL.md new file mode 100644 index 0000000..989c082 --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-cli/SKILL.md @@ -0,0 +1,85 @@ +--- +name: gitnexus-cli +description: "Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki, or list indexed repos. Examples: \"Index this repo\", \"Reanalyze the codebase\", \"Generate a wiki\"" +--- + +# GitNexus CLI Commands + +Commands below use `node .gitnexus/run.cjs ` — the project-local runner `gitnexus analyze` drops next to the index. It auto-selects an available runner at call time (global `gitnexus`, else `pnpm dlx`, else `npx`), so no package-manager assumption and no global install is required. + +> **Not analyzed yet, or `node .gitnexus/run.cjs` reports `Cannot find module`** (the gitignored runner is absent — e.g. a fresh clone or `git clean`)? (Re)generate it with `npx gitnexus analyze` from the project root. On **npm 11.x**, if `npx` crashes during install (`node.target is null`), install once with `npm i -g gitnexus` (then `gitnexus analyze`) or use `pnpm --allow-build=@ladybugdb/core --allow-build=gitnexus --allow-build=tree-sitter dlx gitnexus@latest analyze`. See [#1939](https://github.com/abhigyanpatwari/GitNexus/issues/1939). + +## Commands + +### analyze — Build or refresh the index + +```bash +node .gitnexus/run.cjs analyze +``` + +Run from the project root. This parses all source files, builds the knowledge graph, writes it to `.gitnexus/`, and generates CLAUDE.md / AGENTS.md context files. + +| Flag | Effect | +| -------------- | ---------------------------------------------------------------- | +| `--force` | Force full re-index even if up to date | +| `--embeddings` | Enable embedding generation for semantic search (off by default) | +| `--drop-embeddings` | Drop existing embeddings on rebuild. By default, an `analyze` without `--embeddings` preserves them. | + +**When to run:** First time in a project, after major code changes, or when `gitnexus://repo/{name}/context` reports the index is stale. In Claude Code, a PostToolUse hook detects staleness after `git commit` and `git merge` and notifies the agent to run `analyze` — the hook does not run analyze itself, to avoid blocking the agent for up to 120s and risking KuzuDB corruption on timeout. + +### status — Check index freshness + +```bash +node .gitnexus/run.cjs status +``` + +Shows whether the current repo has a GitNexus index, when it was last updated, and symbol/relationship counts. Use this to check if re-indexing is needed. + +### clean — Delete the index + +```bash +node .gitnexus/run.cjs clean +``` + +Deletes the `.gitnexus/` directory and unregisters the repo from the global registry. Use before re-indexing if the index is corrupt or after removing GitNexus from a project. + +| Flag | Effect | +| --------- | ------------------------------------------------- | +| `--force` | Skip confirmation prompt | +| `--all` | Clean all indexed repos, not just the current one | + +### wiki — Generate documentation from the graph + +```bash +node .gitnexus/run.cjs wiki +``` + +Generates repository documentation from the knowledge graph using an LLM. Requires an API key (saved to `~/.gitnexus/config.json` on first use). + +| Flag | Effect | +| ------------------- | ----------------------------------------- | +| `--force` | Force full regeneration | +| `--model ` | LLM model (default: minimax/minimax-m2.5) | +| `--base-url ` | LLM API base URL | +| `--api-key ` | LLM API key | +| `--concurrency ` | Parallel LLM calls (default: 3) | +| `--gist` | Publish wiki as a public GitHub Gist | + +### list — Show all indexed repos + +```bash +node .gitnexus/run.cjs list +``` + +Lists all repositories registered in `~/.gitnexus/registry.json`. The MCP `list_repos` tool provides the same information. + +## After Indexing + +1. **Read `gitnexus://repo/{name}/context`** to verify the index loaded +2. Use the other GitNexus skills (`exploring`, `debugging`, `impact-analysis`, `refactoring`) for your task + +## Troubleshooting + +- **"Not inside a git repository"**: Run from a directory inside a git repo +- **Index is stale after re-analyzing**: Restart Claude Code to reload the MCP server +- **Embeddings slow**: Omit `--embeddings` (it's off by default) or set `OPENAI_API_KEY` for faster API-based embedding diff --git a/.claude/skills/gitnexus/gitnexus-debugging/SKILL.md b/.claude/skills/gitnexus/gitnexus-debugging/SKILL.md new file mode 100644 index 0000000..9834f94 --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-debugging/SKILL.md @@ -0,0 +1,89 @@ +--- +name: gitnexus-debugging +description: "Use when the user is debugging a bug, tracing an error, or asking why something fails. Examples: \"Why is X failing?\", \"Where does this error come from?\", \"Trace this bug\"" +--- + +# Debugging with GitNexus + +## When to Use + +- "Why is this function failing?" +- "Trace where this error comes from" +- "Who calls this method?" +- "This endpoint returns 500" +- Investigating bugs, errors, or unexpected behavior + +## Workflow + +``` +1. query({query: ""}) → Find related execution flows +2. context({name: ""}) → See callers/callees/processes +3. READ gitnexus://repo/{name}/process/{name} → Trace execution flow +4. cypher({query: "MATCH path..."}) → Custom traces if needed +``` + +> If "Index is stale" → run `node .gitnexus/run.cjs analyze` in terminal. + +## Checklist + +``` +- [ ] Understand the symptom (error message, unexpected behavior) +- [ ] query for error text or related code +- [ ] Identify the suspect function from returned processes +- [ ] context to see callers and callees +- [ ] Trace execution flow via process resource if applicable +- [ ] cypher for custom call chain traces if needed +- [ ] Read source files to confirm root cause +``` + +## Debugging Patterns + +| Symptom | GitNexus Approach | +| -------------------- | ---------------------------------------------------------- | +| Error message | `query` for error text → `context` on throw sites | +| Wrong return value | `context` on the function → trace callees for data flow | +| Intermittent failure | `context` → look for external calls, async deps | +| Performance issue | `context` → find symbols with many callers (hot paths) | +| Recent regression | `detect_changes` to see what your changes affect | + +## Tools + +**query** — find code related to error: + +``` +query({query: "payment validation error"}) +→ Processes: CheckoutFlow, ErrorHandling +→ Symbols: validatePayment, handlePaymentError, PaymentException +``` + +**context** — full context for a suspect: + +``` +context({name: "validatePayment"}) +→ Incoming calls: processCheckout, webhookHandler +→ Outgoing calls: verifyCard, fetchRates (external API!) +→ Processes: CheckoutFlow (step 3/7) +``` + +**cypher** — custom call chain traces: + +```cypher +MATCH path = (a)-[:CodeRelation {type: 'CALLS'}*1..2]->(b:Function {name: "validatePayment"}) +RETURN [n IN nodes(path) | n.name] AS chain +``` + +## Example: "Payment endpoint returns 500 intermittently" + +``` +1. query({query: "payment error handling"}) + → Processes: CheckoutFlow, ErrorHandling + → Symbols: validatePayment, handlePaymentError + +2. context({name: "validatePayment"}) + → Outgoing calls: verifyCard, fetchRates (external API!) + +3. READ gitnexus://repo/my-app/process/CheckoutFlow + → Step 3: validatePayment → calls fetchRates (external) + +4. Root cause: fetchRates calls external API without proper timeout +``` diff --git a/.claude/skills/gitnexus/gitnexus-exploring/SKILL.md b/.claude/skills/gitnexus/gitnexus-exploring/SKILL.md new file mode 100644 index 0000000..ccf684c --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-exploring/SKILL.md @@ -0,0 +1,78 @@ +--- +name: gitnexus-exploring +description: "Use when the user asks how code works, wants to understand architecture, trace execution flows, or explore unfamiliar parts of the codebase. Examples: \"How does X work?\", \"What calls this function?\", \"Show me the auth flow\"" +--- + +# Exploring Codebases with GitNexus + +## When to Use + +- "How does authentication work?" +- "What's the project structure?" +- "Show me the main components" +- "Where is the database logic?" +- Understanding code you haven't seen before + +## Workflow + +``` +1. READ gitnexus://repos → Discover indexed repos +2. READ gitnexus://repo/{name}/context → Codebase overview, check staleness +3. query({query: ""}) → Find related execution flows +4. context({name: ""}) → Deep dive on specific symbol +5. READ gitnexus://repo/{name}/process/{name} → Trace full execution flow +``` + +> If step 2 says "Index is stale" → run `node .gitnexus/run.cjs analyze` in terminal. + +## Checklist + +``` +- [ ] READ gitnexus://repo/{name}/context +- [ ] query for the concept you want to understand +- [ ] Review returned processes (execution flows) +- [ ] context on key symbols for callers/callees +- [ ] READ process resource for full execution traces +- [ ] Read source files for implementation details +``` + +## Resources + +| Resource | What you get | +| --------------------------------------- | ------------------------------------------------------- | +| `gitnexus://repo/{name}/context` | Stats, staleness warning (~150 tokens) | +| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores (~300 tokens) | +| `gitnexus://repo/{name}/cluster/{name}` | Area members with file paths (~500 tokens) | +| `gitnexus://repo/{name}/process/{name}` | Step-by-step execution trace (~200 tokens) | + +## Tools + +**query** — find execution flows related to a concept: + +``` +query({query: "payment processing"}) +→ Processes: CheckoutFlow, RefundFlow, WebhookHandler +→ Symbols grouped by flow with file locations +``` + +**context** — 360-degree view of a symbol: + +``` +context({name: "validateUser"}) +→ Incoming calls: loginHandler, apiMiddleware +→ Outgoing calls: checkToken, getUserById +→ Processes: LoginFlow (step 2/5), TokenRefresh (step 1/3) +``` + +## Example: "How does payment processing work?" + +``` +1. READ gitnexus://repo/my-app/context → 918 symbols, 45 processes +2. query({query: "payment processing"}) + → CheckoutFlow: processPayment → validateCard → chargeStripe + → RefundFlow: initiateRefund → calculateRefund → processRefund +3. context({name: "processPayment"}) + → Incoming: checkoutHandler, webhookHandler + → Outgoing: validateCard, chargeStripe, saveTransaction +4. Read src/payments/processor.ts for implementation details +``` diff --git a/.claude/skills/gitnexus/gitnexus-guide/SKILL.md b/.claude/skills/gitnexus/gitnexus-guide/SKILL.md new file mode 100644 index 0000000..b81900b --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-guide/SKILL.md @@ -0,0 +1,64 @@ +--- +name: gitnexus-guide +description: "Use when the user asks about GitNexus itself — available tools, how to query the knowledge graph, MCP resources, graph schema, or workflow reference. Examples: \"What GitNexus tools are available?\", \"How do I use GitNexus?\"" +--- + +# GitNexus Guide + +Quick reference for all GitNexus MCP tools, resources, and the knowledge graph schema. + +## Always Start Here + +For any task involving code understanding, debugging, impact analysis, or refactoring: + +1. **Read `gitnexus://repo/{name}/context`** — codebase overview + check index freshness +2. **Match your task to a skill below** and **read that skill file** +3. **Follow the skill's workflow and checklist** + +> If step 1 warns the index is stale, run `node .gitnexus/run.cjs analyze` in the terminal first. + +## Skills + +| Task | Skill to read | +| -------------------------------------------- | ------------------- | +| Understand architecture / "How does X work?" | `gitnexus-exploring` | +| Blast radius / "What breaks if I change X?" | `gitnexus-impact-analysis` | +| Trace bugs / "Why is X failing?" | `gitnexus-debugging` | +| Rename / extract / split / refactor | `gitnexus-refactoring` | +| Tools, resources, schema reference | `gitnexus-guide` (this file) | +| Index, status, clean, wiki CLI commands | `gitnexus-cli` | + +## Tools Reference + +| Tool | What it gives you | +| ---------------- | ------------------------------------------------------------------------ | +| `query` | Process-grouped code intelligence — execution flows related to a concept | +| `context` | 360-degree symbol view — categorized refs, processes it participates in | +| `impact` | Symbol blast radius — what breaks at depth 1/2/3 with confidence | +| `detect_changes` | Git-diff impact — what do your current changes affect | +| `rename` | Multi-file coordinated rename with confidence-tagged edits | +| `cypher` | Raw graph queries (read `gitnexus://repo/{name}/schema` first) | +| `list_repos` | Discover indexed repos | + +## Resources Reference + +Lightweight reads (~100-500 tokens) for navigation: + +| Resource | Content | +| ---------------------------------------------- | ----------------------------------------- | +| `gitnexus://repo/{name}/context` | Stats, staleness check | +| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores | +| `gitnexus://repo/{name}/cluster/{clusterName}` | Area members | +| `gitnexus://repo/{name}/processes` | All execution flows | +| `gitnexus://repo/{name}/process/{processName}` | Step-by-step trace | +| `gitnexus://repo/{name}/schema` | Graph schema for Cypher | + +## Graph Schema + +**Nodes:** File, Function, Class, Interface, Method, Community, Process +**Edges (via CodeRelation.type):** CALLS, IMPORTS, EXTENDS, IMPLEMENTS, DEFINES, MEMBER_OF, STEP_IN_PROCESS + +```cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"}) +RETURN caller.name, caller.filePath +``` diff --git a/.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md b/.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md new file mode 100644 index 0000000..45eb7ce --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md @@ -0,0 +1,97 @@ +--- +name: gitnexus-impact-analysis +description: "Use when the user wants to know what will break if they change something, or needs safety analysis before editing code. Examples: \"Is it safe to change X?\", \"What depends on this?\", \"What will break?\"" +--- + +# Impact Analysis with GitNexus + +## When to Use + +- "Is it safe to change this function?" +- "What will break if I modify X?" +- "Show me the blast radius" +- "Who uses this code?" +- Before making non-trivial code changes +- Before committing — to understand what your changes affect + +## Workflow + +``` +1. impact({target: "X", direction: "upstream"}) → What depends on this +2. READ gitnexus://repo/{name}/processes → Check affected execution flows +3. detect_changes() → Map current git changes to affected flows +4. Assess risk and report to user +``` + +> If "Index is stale" → run `node .gitnexus/run.cjs analyze` in terminal. + +## Checklist + +``` +- [ ] impact({target, direction: "upstream"}) to find dependents +- [ ] Review d=1 items first (these WILL BREAK) +- [ ] Check high-confidence (>0.8) dependencies +- [ ] READ processes to check affected execution flows +- [ ] detect_changes() for pre-commit check +- [ ] Assess risk level and report to user +``` + +## Understanding Output + +| Depth | Risk Level | Meaning | +| ----- | ---------------- | ------------------------ | +| d=1 | **WILL BREAK** | Direct callers/importers | +| d=2 | LIKELY AFFECTED | Indirect dependencies | +| d=3 | MAY NEED TESTING | Transitive effects | + +## Risk Assessment + +| Affected | Risk | +| ------------------------------ | -------- | +| <5 symbols, few processes | LOW | +| 5-15 symbols, 2-5 processes | MEDIUM | +| >15 symbols or many processes | HIGH | +| Critical path (auth, payments) | CRITICAL | + +## Tools + +**impact** — the primary tool for symbol blast radius: + +``` +impact({ + target: "validateUser", + direction: "upstream", + minConfidence: 0.8, + maxDepth: 3 +}) + +→ d=1 (WILL BREAK): + - loginHandler (src/auth/login.ts:42) [CALLS, 100%] + - apiMiddleware (src/api/middleware.ts:15) [CALLS, 100%] + +→ d=2 (LIKELY AFFECTED): + - authRouter (src/routes/auth.ts:22) [CALLS, 95%] +``` + +**detect_changes** — git-diff based impact analysis: + +``` +detect_changes({scope: "staged"}) + +→ Changed: 5 symbols in 3 files +→ Affected: LoginFlow, TokenRefresh, APIMiddlewarePipeline +→ Risk: MEDIUM +``` + +## Example: "What breaks if I change validateUser?" + +``` +1. impact({target: "validateUser", direction: "upstream"}) + → d=1: loginHandler, apiMiddleware (WILL BREAK) + → d=2: authRouter, sessionManager (LIKELY AFFECTED) + +2. READ gitnexus://repo/my-app/processes + → LoginFlow and TokenRefresh touch validateUser + +3. Risk: 2 direct callers, 2 processes = MEDIUM +``` diff --git a/.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md b/.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md new file mode 100644 index 0000000..e13c04e --- /dev/null +++ b/.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md @@ -0,0 +1,121 @@ +--- +name: gitnexus-refactoring +description: "Use when the user wants to rename, extract, split, move, or restructure code safely. Examples: \"Rename this function\", \"Extract this into a module\", \"Refactor this class\", \"Move this to a separate file\"" +--- + +# Refactoring with GitNexus + +## When to Use + +- "Rename this function safely" +- "Extract this into a module" +- "Split this service" +- "Move this to a new file" +- Any task involving renaming, extracting, splitting, or restructuring code + +## Workflow + +``` +1. impact({target: "X", direction: "upstream"}) → Map all dependents +2. query({query: "X"}) → Find execution flows involving X +3. context({name: "X"}) → See all incoming/outgoing refs +4. Plan update order: interfaces → implementations → callers → tests +``` + +> If "Index is stale" → run `node .gitnexus/run.cjs analyze` in terminal. + +## Checklists + +### Rename Symbol + +``` +- [ ] rename({symbol_name: "oldName", new_name: "newName", dry_run: true}) — preview all edits +- [ ] Review graph edits (high confidence) and ast_search edits (review carefully) +- [ ] If satisfied: rename({..., dry_run: false}) — apply edits +- [ ] detect_changes() — verify only expected files changed +- [ ] Run tests for affected processes +``` + +### Extract Module + +``` +- [ ] context({name: target}) — see all incoming/outgoing refs +- [ ] impact({target, direction: "upstream"}) — find all external callers +- [ ] Define new module interface +- [ ] Extract code, update imports +- [ ] detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +### Split Function/Service + +``` +- [ ] context({name: target}) — understand all callees +- [ ] Group callees by responsibility +- [ ] impact({target, direction: "upstream"}) — map callers to update +- [ ] Create new functions/services +- [ ] Update callers +- [ ] detect_changes() — verify affected scope +- [ ] Run tests for affected processes +``` + +## Tools + +**rename** — automated multi-file rename: + +``` +rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) +→ 12 edits across 8 files +→ 10 graph edits (high confidence), 2 ast_search edits (review) +→ Changes: [{file_path, edits: [{line, old_text, new_text, confidence}]}] +``` + +**impact** — map all dependents first: + +``` +impact({target: "validateUser", direction: "upstream"}) +→ d=1: loginHandler, apiMiddleware, testUtils +→ Affected Processes: LoginFlow, TokenRefresh +``` + +**detect_changes** — verify your changes after refactoring: + +``` +detect_changes({scope: "all"}) +→ Changed: 8 files, 12 symbols +→ Affected processes: LoginFlow, TokenRefresh +→ Risk: MEDIUM +``` + +**cypher** — custom reference queries: + +```cypher +MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "validateUser"}) +RETURN caller.name, caller.filePath ORDER BY caller.filePath +``` + +## Risk Rules + +| Risk Factor | Mitigation | +| ------------------- | ----------------------------------------- | +| Many callers (>5) | Use rename for automated updates | +| Cross-area refs | Use detect_changes after to verify scope | +| String/dynamic refs | query to find them | +| External/public API | Version and deprecate properly | + +## Example: Rename `validateUser` to `authenticateUser` + +``` +1. rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true}) + → 12 edits: 10 graph (safe), 2 ast_search (review) + → Files: validator.ts, login.ts, middleware.ts, config.json... + +2. Review ast_search edits (config.json: dynamic reference!) + +3. rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: false}) + → Applied 12 edits across 8 files + +4. detect_changes({scope: "all"}) + → Affected: LoginFlow, TokenRefresh + → Risk: MEDIUM — run tests for these flows +``` diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..893c93e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,377 @@ +# 仓库指南 + +## 项目概述 + +NetBird Dashboard 是 NetBird 管理服务的 Web 界面。这是一个 Next.js 应用程序,为 NetBird 网络提供网络管理、对等节点监控、访问控制和配置功能。 + +**在线版本:** https://app.netbird.io/ +**源代码:** https://github.com/netbirdio/dashboard + +## 架构与数据流 + +### 技术栈 +- **框架:** Next.js 13+ 使用 App Router +- **语言:** TypeScript +- **样式:** Tailwind CSS + shadcn/ui 组件 +- **状态管理:** React Context + SWR 用于服务器状态 +- **认证:** OIDC 通过 @axa-fr/react-oidc +- **国际化:** next-intl +- **测试:** Cypress (E2E) +- **部署:** Docker + Nginx + +### 高级结构 + +``` +src/ +├── app/ # Next.js App Router 页面 +│ ├── (dashboard)/ # 主仪表板路由(分组布局) +│ ├── (remote-access)/ # 远程访问路由 +│ ├── install/ # 安装向导 +│ ├── invite/ # 用户邀请流程 +│ └── setup/ # 初始设置流程 +├── assets/ # 静态资源(图标、图片、字体) +├── auth/ # OIDC 认证组件 +├── components/ # 共享 UI 组件(基于 shadcn/ui) +├── contexts/ # React Context 提供者 +├── hooks/ # 自定义 React 钩子 +├── i18n/ # 国际化配置和消息 +├── interfaces/ # TypeScript 类型定义 +├── layouts/ # 布局组件 +├── modules/ # 功能模块(领域特定) +└── utils/ # 工具函数 +``` + +### 数据流 + +1. **认证:** OIDC 提供者处理认证 → 令牌存储在内存中 +2. **API 调用:** `useFetchApi` 钩子 → SWR → OIDC 请求 → 管理 API +3. **状态:** 服务器状态通过 SWR 缓存,UI 状态通过 React Context +4. **渲染:** 默认使用服务器组件,需要时使用客户端组件 + +## 关键目录 + +### `src/app/` - 页面和路由 +- 使用 Next.js App Router 和路由分组 +- `(dashboard)/` 包含主要应用页面和共享布局 +- 每个路由有 `page.tsx` 和可选的 `layout.tsx` +- 通过 `error/page.tsx` 实现错误边界 + +### `src/modules/` - 功能模块 +按功能组织的领域特定组件: +- `peers/` - 对等节点管理组件 +- `networks/` - 网络配置 +- `access-control/` - ACL 策略 +- `dns/` - DNS 管理 +- `routes/` - 网络路由 +- `users/` - 用户管理 +- `groups/` - 分组管理 +- `setup-keys/` - 设置密钥管理 +- `activity/` - 活动日志 +- `settings/` - 账户设置 + +### `src/components/` - 共享 UI 组件 +基于 shadcn/ui 构建,具有自定义变体: +- `Input.tsx` - 带验证的表单输入 +- `Select.tsx` - 下拉选择 +- `Dialog.tsx` - 模态对话框 +- `Table.tsx` - 数据表格 +- `Button.tsx` - 操作按钮 +- `Badge.tsx` - 状态徽章 +- `Tooltip.tsx` - 信息提示 + +### `src/contexts/` - 状态提供者 +全局状态的 React Context 提供者: +- `ApplicationProvider.tsx` - 应用级配置 +- `PeersProvider.tsx` - 对等节点数据 +- `GroupsProvider.tsx` - 分组数据 +- `RoutesProvider.tsx` - 路由数据 +- `PoliciesProvider.tsx` - ACL 策略 +- `PermissionsProvider.tsx` - 用户权限 +- `GlobalThemeProvider.tsx` - 主题管理 +- `LocaleProvider.tsx` - 语言/区域设置 + +### `src/hooks/` - 自定义钩子 +可复用的 React 钩子: +- `useLocalStorage.tsx` - 持久化本地存储 +- `useDebounce.tsx` - 防抖值 +- `useSearch.ts` - 搜索功能 +- `useCopyToClipboard.ts` - 剪贴板操作 +- `useElementSize.ts` - DOM 元素尺寸 +- `useIntersectionObserver.ts` - 可见性检测 + +### `src/interfaces/` - 类型定义 +领域模型的 TypeScript 接口: +- `Peer.ts` - 网络对等节点 +- `Group.ts` - 对等节点分组 +- `Route.ts` - 网络路由 +- `Nameserver.ts` - DNS 名称服务器 +- `Account.ts` - 用户账户 +- `SetupKey.ts` - 设置密钥 +- `AccessToken.ts` - API 访问令牌 + +### `src/utils/` - 工具函数 +辅助函数: +- `api.tsx` - 集成 SWR 的 API 客户端 +- `helpers.ts` - 通用工具(cn, randomString 等) +- `config.ts` - 配置加载器 +- `ip.ts` - IP 地址工具 +- `wireguard.ts` - WireGuard 辅助函数 +- `version.ts` - 版本比较 + +## 开发命令 + +```bash +# 安装依赖 +npm install + +# 启动开发服务器(端口 3000) +npm run dev + +# 使用 Turbopack 启动(更快) +npm run turbo + +# 构建生产版本 +npm run build + +# 启动生产服务器 +npm start + +# 运行代码检查 +npm run lint + +# 打开 Cypress 测试运行器 +npm run cypress:open + +# 复制 OIDC 服务工作者(认证必需) +npm run copy +npm run copytrusted +``` + +## 代码规范和常见模式 + +### 组件模式 +```tsx +// 使用 shadcn/ui 和 class-variance-authority 实现变体 +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "@utils/helpers"; + +const buttonVariants = cva("base-classes", { + variants: { + variant: { + default: "default-classes", + destructive: "destructive-classes", + }, + }, +}); + +interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps {} + +export function Button({ className, variant, ...props }: ButtonProps) { + return ( + + - diff --git a/src/modules/peer/PeerRemoteJobsSection.tsx b/src/modules/peer/PeerRemoteJobsSection.tsx index 4a45c6e..7e85b7a 100644 --- a/src/modules/peer/PeerRemoteJobsSection.tsx +++ b/src/modules/peer/PeerRemoteJobsSection.tsx @@ -21,7 +21,7 @@ export const PeerRemoteJobsSection = ({ peerID }: Props) => { const tCommon = useTranslations("common"); const { data: jobs, isLoading } = useFetchApi(`/peers/${peerID}/jobs`); -return ( + return (
diff --git a/src/modules/routes/RouteModal.tsx b/src/modules/routes/RouteModal.tsx index 83e9f1a..6acb68a 100644 --- a/src/modules/routes/RouteModal.tsx +++ b/src/modules/routes/RouteModal.tsx @@ -160,7 +160,8 @@ export function RouteModalContent({ isFirstExitNode = false, distributionGroups, }: ModalProps) { - const t = useTranslations("common"); + const t = useTranslations("networks"); + const tCommon = useTranslations("common"); const { createRoute } = useRoutes(); const [tab, setTab] = useState( exitNode && peer ? "access-control" : "network", @@ -365,13 +366,13 @@ export function RouteModalContent({ const networkIdentifierError = useMemo(() => { return (networkIdentifier?.length || 0) > 40 - ? "Network Identifier must be less than 40 characters" + ? t("networkIdentifier") + " must be less than 40 characters" : ""; }, [networkIdentifier]); const metricError = useMemo(() => { return parseInt(metric) < 1 || parseInt(metric) > 9999 - ? "Metric must be between 1 and 9999" + ? t("metric") + " must be between 1 and 9999" : ""; }, [metric]); @@ -465,16 +466,15 @@ export function RouteModalContent({ "text-nb-gray-500 group-data-[state=active]/trigger:text-netbird transition-all" } /> - Additional Settings + {t("additionalSettings")}
- + - Select your route type to add either a network range or a list - of domains. + {t("routeTypeHelp")}
@@ -484,7 +484,7 @@ export function RouteModalContent({ className={"w-full"} > - Network Range + {t("routeTypeNetworkRange")} - Domains + {t("routeTypeDomains")}
@@ -504,12 +504,12 @@ export function RouteModalContent({ routeType !== "ip-range" && "hidden", )} > - - Add a private IPv4 or IPv6 address or range + + {t("networkRangeHelp")} } - placeholder={"e.g., 172.16.0.1, 172.16.0.0/16, 2001:db8::1 or 2001:db8::/64"} + placeholder={t("networkRangePlaceholder")} value={networkRange} data-testid={"network-range"} className={"font-mono !text-[13px]"} @@ -521,7 +521,7 @@ export function RouteModalContent({
- + Add domains that dynamically resolve to one or more IPv4 addresses.
A maximum of 32 domains can be added. @@ -670,7 +670,7 @@ export function RouteModalContent({
- + {exitNode ? peer @@ -701,7 +701,7 @@ export function RouteModalContent({
- + Add a unique network identifier that is assigned to each device. @@ -774,7 +774,7 @@ export function RouteModalContent({
- + A lower metric indicates higher priority routes. @@ -822,7 +822,7 @@ export function RouteModalContent({
{(tab == "network" || (tab == "access-control" && exitNode)) && ( - + )} diff --git a/src/modules/setup-keys/SetupKeyModal.tsx b/src/modules/setup-keys/SetupKeyModal.tsx index 9bdc762..8ee02ef 100644 --- a/src/modules/setup-keys/SetupKeyModal.tsx +++ b/src/modules/setup-keys/SetupKeyModal.tsx @@ -58,7 +58,6 @@ export default function SetupKeyModal({ showOnlyRoutingPeerOS, groups, }: Readonly) { - const t = useTranslations("common"); const [successModal, setSuccessModal] = useState(false); const [setupKey, setSetupKey] = useState(); const [installModal, setInstallModal] = useState(false); @@ -172,7 +171,8 @@ export function SetupKeyModalContent({ predefinedName = "", groups, }: Readonly) { - const t = useTranslations("common"); + const t = useTranslations("setupKeys"); + const tCommon = useTranslations("common"); const setupKeyRequest = useApiCall("/setup-keys", true); const { mutate } = useSWRConfig(); @@ -230,8 +230,8 @@ export function SetupKeyModalContent({ } - title={"Create New Setup Key"} - description={"Use this key to register new machines in your network"} + title={t("createTitle")} + description={t("createDescription")} color={"netbird"} /> @@ -240,10 +240,10 @@ export function SetupKeyModalContent({
{/* Name Field */}
- - Set an easily identifiable name for your key + + {t("nameHelp")} setName(e.target.value)} @@ -268,9 +268,9 @@ export function SetupKeyModalContent({ {/* Usage Limit */}
- + - For example, set to 30 if you want to enroll 30 peers + {t("usageLimitHelp")}
@@ -286,18 +286,18 @@ export function SetupKeyModalContent({ customPrefix={ } - customSuffix={"Peer(s)"} + customSuffix={t("usageLimitSuffix")} />
{/* Expires in Days */}
- + - Days until the key expires. + {t("expiresInHelp")}
- Leave empty for no expiration. + {t("expiresInHelpEmpty")}
} - customSuffix={"Day(s)"} + customSuffix={t("expiresInSuffix")} />
@@ -383,7 +383,7 @@ export function SetupKeyModalContent({
- +