fix(i18n): localize RouteModal, SetupKeyModal, CreateAccessTokenModal
- RouteModal: routeType, networkRange, domains, distributionGroups, metric - SetupKeyModal: createTitle, nameHelp, usageLimitHelp, expiresIn - CreateAccessTokenModal: tokenName, tokenNameHelp, tokenExpiresIn - Add corresponding keys to en.ts and zh.ts
This commit is contained in:
85
.claude/skills/gitnexus/gitnexus-cli/SKILL.md
Normal file
85
.claude/skills/gitnexus/gitnexus-cli/SKILL.md
Normal file
@@ -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 <command>` — 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 <model>` | LLM model (default: minimax/minimax-m2.5) |
|
||||
| `--base-url <url>` | LLM API base URL |
|
||||
| `--api-key <key>` | LLM API key |
|
||||
| `--concurrency <n>` | 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
|
||||
89
.claude/skills/gitnexus/gitnexus-debugging/SKILL.md
Normal file
89
.claude/skills/gitnexus/gitnexus-debugging/SKILL.md
Normal file
@@ -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: "<error or symptom>"}) → Find related execution flows
|
||||
2. context({name: "<suspect>"}) → 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
|
||||
```
|
||||
78
.claude/skills/gitnexus/gitnexus-exploring/SKILL.md
Normal file
78
.claude/skills/gitnexus/gitnexus-exploring/SKILL.md
Normal file
@@ -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: "<what you want to understand>"}) → Find related execution flows
|
||||
4. context({name: "<symbol>"}) → 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
|
||||
```
|
||||
64
.claude/skills/gitnexus/gitnexus-guide/SKILL.md
Normal file
64
.claude/skills/gitnexus/gitnexus-guide/SKILL.md
Normal file
@@ -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
|
||||
```
|
||||
97
.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md
Normal file
97
.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md
Normal file
@@ -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
|
||||
```
|
||||
121
.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md
Normal file
121
.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md
Normal file
@@ -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
|
||||
```
|
||||
377
AGENTS.md
Normal file
377
AGENTS.md
Normal file
@@ -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<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {}
|
||||
|
||||
export function Button({ className, variant, ...props }: ButtonProps) {
|
||||
return (
|
||||
<button
|
||||
className={cn(buttonVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Context 提供者模式
|
||||
```tsx
|
||||
import React, { useMemo } from "react";
|
||||
import useFetchApi from "@utils/api";
|
||||
|
||||
const DataContext = React.createContext({} as DataType);
|
||||
|
||||
export default function DataProvider({ children }: { children: React.ReactNode }) {
|
||||
const { data, isLoading } = useFetchApi<Data[]>("/endpoint");
|
||||
|
||||
const value = useMemo(() => ({ data, isLoading }), [data, isLoading]);
|
||||
|
||||
return (
|
||||
<DataContext.Provider value={value}>
|
||||
{children}
|
||||
</DataContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useData = () => React.useContext(DataContext);
|
||||
```
|
||||
|
||||
### API 钩子模式
|
||||
```tsx
|
||||
import useFetchApi from "@utils/api";
|
||||
|
||||
// GET 请求使用 SWR
|
||||
const { data, isLoading, error } = useFetchApi<Data[]>("/endpoint");
|
||||
|
||||
// POST/PUT/DELETE 请求
|
||||
const { mutate } = useFetchApi("/endpoint", { method: "POST" });
|
||||
```
|
||||
|
||||
### 样式模式
|
||||
```tsx
|
||||
import { cn } from "@utils/helpers";
|
||||
|
||||
// 合并 Tailwind 类
|
||||
<div className={cn(
|
||||
"base-classes",
|
||||
condition && "conditional-classes",
|
||||
className
|
||||
)} />
|
||||
```
|
||||
|
||||
### 导入顺序
|
||||
由 `simple-import-sort` ESLint 插件强制执行:
|
||||
1. 副作用导入 (`import "polyfill"`)
|
||||
2. 外部包 (`import React from "react"`)
|
||||
3. 内部别名 (`import { Button } from "@/components"`)
|
||||
4. 相对导入 (`import { useData } from "./context"`)
|
||||
|
||||
### 文件命名
|
||||
- **组件:** PascalCase (`PeerTable.tsx`, `GroupSelector.tsx`)
|
||||
- **钩子:** camelCase 带 `use` 前缀 (`useLocalStorage.tsx`)
|
||||
- **工具函数:** camelCase (`helpers.ts`, `api.tsx`)
|
||||
- **接口:** PascalCase (`Peer.ts`, `Group.ts`)
|
||||
- **页面:** `page.tsx`(Next.js App Router 要求)
|
||||
- **布局:** `layout.tsx`(Next.js App Router 要求)
|
||||
|
||||
## 重要文件
|
||||
|
||||
### 入口点
|
||||
- `src/app/layout.tsx` - 根布局(提供者、字体、元数据)
|
||||
- `src/app/(dashboard)/layout.tsx` - 仪表板布局(导航、认证)
|
||||
- `src/app/page.tsx` - 首页重定向
|
||||
|
||||
### 配置文件
|
||||
- `next.config.js` - Next.js 配置
|
||||
- `tailwind.config.ts` - Tailwind CSS 配置
|
||||
- `components.json` - shadcn/ui 配置
|
||||
- `config.json` - 应用配置(API 端点、认证)
|
||||
- `.eslintrc.json` - ESLint 规则
|
||||
- `tsconfig.json` - TypeScript 配置
|
||||
|
||||
### 关键工具
|
||||
- `src/utils/api.tsx` - API 客户端(SWR + OIDC)
|
||||
- `src/utils/config.ts` - 配置加载器
|
||||
- `src/utils/helpers.ts` - 共享工具
|
||||
- `src/auth/OIDCProvider.tsx` - 认证提供者
|
||||
|
||||
## 运行时/工具偏好
|
||||
|
||||
### 必需环境
|
||||
- Node.js 18+(推荐 LTS)
|
||||
- npm(包管理器)
|
||||
|
||||
### 本地开发设置
|
||||
1. 克隆仓库
|
||||
2. 创建 `.local-config.json` 覆盖 `config.json` 中的值
|
||||
3. 运行 `npm install`
|
||||
4. 运行 `npm run copy`(复制 OIDC 服务工作者)
|
||||
5. 运行 `npm run dev`
|
||||
|
||||
### Docker 部署
|
||||
```bash
|
||||
docker run -d --name netbird-dashboard \
|
||||
-p 80:80 \
|
||||
-e AUTH0_DOMAIN=<domain> \
|
||||
-e AUTH0_CLIENT_ID=<client-id> \
|
||||
-e AUTH0_AUDIENCE=<audience> \
|
||||
-e NETBIRD_MGMT_API_ENDPOINT=<api-url> \
|
||||
netbirdio/dashboard:main
|
||||
```
|
||||
|
||||
### 配置
|
||||
- `config.json` - 默认配置
|
||||
- `.local-config.json` - 本地覆盖(已忽略)
|
||||
- Docker 部署的环境变量
|
||||
|
||||
## 测试与质量保证
|
||||
|
||||
### E2E 测试(Cypress)
|
||||
```bash
|
||||
# 打开 Cypress UI
|
||||
npm run cypress:open
|
||||
|
||||
# 无头运行测试
|
||||
npx cypress run
|
||||
```
|
||||
|
||||
**测试位置:** `cypress/e2e/`
|
||||
**支持文件:** `cypress/support/`
|
||||
**测试数据:** `cypress/fixtures/`
|
||||
|
||||
### 代码检查
|
||||
```bash
|
||||
npm run lint
|
||||
```
|
||||
|
||||
ESLint 配置:
|
||||
- `next/core-web-vitals` - Next.js 最佳实践
|
||||
- `prettier` - 代码格式化
|
||||
- `simple-import-sort` - 导入排序
|
||||
|
||||
### 类型检查
|
||||
TypeScript 严格模式已启用。运行 `npx tsc --noEmit` 检查类型。
|
||||
|
||||
## 模块上下文
|
||||
|
||||
参见 `docs/contexts/` 获取特定模块的详细文档:
|
||||
- `peers.md` - 对等节点管理模块
|
||||
- `networks.md` - 网络配置模块
|
||||
- `access-control.md` - ACL 策略模块
|
||||
- `dns.md` - DNS 管理模块
|
||||
- `api-client.md` - API 客户端模式
|
||||
- `authentication.md` - OIDC 认证流程
|
||||
|
||||
## 常见问题与注意事项
|
||||
|
||||
### OIDC 服务工作者
|
||||
安装后必须运行 `npm run copy` 将 OIDC 服务工作者复制到 `public/`。
|
||||
|
||||
### 本地配置
|
||||
创建 `.local-config.json` 覆盖 `config.json` 中的本地开发值。
|
||||
|
||||
### 静态导出
|
||||
应用在 Next.js 配置中使用 `output: "export"` - 运行时无服务器端渲染。
|
||||
|
||||
### 暗黑模式
|
||||
主题通过 `GlobalThemeProvider` 管理。使用 Tailwind 暗黑模式类。
|
||||
|
||||
### API 端点
|
||||
所有 API 调用通过 `src/utils/api.tsx`,它处理:
|
||||
- OIDC 令牌注入
|
||||
- 令牌刷新
|
||||
- 错误处理
|
||||
- SWR 缓存
|
||||
|
||||
## 快速参考
|
||||
|
||||
### 添加新页面
|
||||
1. 创建 `src/app/(dashboard)/new-page/page.tsx`
|
||||
2. 在 `src/layouts/Navigation.tsx` 中添加导航
|
||||
3. 如需要,在 `src/contexts/` 中创建上下文提供者
|
||||
4. 在 `src/interfaces/` 中添加类型
|
||||
|
||||
### 添加新组件
|
||||
1. 在 `src/components/`(共享)或 `src/modules/<feature>/`(功能特定)中创建
|
||||
2. 遵循 shadcn/ui 模式并实现变体
|
||||
3. 从组件文件导出
|
||||
|
||||
### 添加新 API 端点
|
||||
1. 在组件或上下文中使用 `useFetchApi` 钩子
|
||||
2. 在 `src/interfaces/` 中添加 TypeScript 接口
|
||||
3. 如果数据在组件间共享,创建上下文提供者
|
||||
|
||||
### 添加新模块
|
||||
1. 在 `src/modules/<feature>/` 中创建目录
|
||||
2. 为该功能添加组件
|
||||
3. 在 `src/contexts/` 中创建上下文提供者
|
||||
4. 在 `src/app/(dashboard)/<feature>/` 中添加页面
|
||||
5. 更新导航
|
||||
|
||||
---
|
||||
|
||||
*本文档由 AI 自动生成。随着代码库的发展而更新。*
|
||||
43
CLAUDE.md
Normal file
43
CLAUDE.md
Normal file
@@ -0,0 +1,43 @@
|
||||
<!-- gitnexus:start -->
|
||||
# GitNexus — Code Intelligence
|
||||
|
||||
This project is indexed by GitNexus as **dashboard** (4993 symbols, 15422 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
|
||||
|
||||
> Index stale? Run `node .gitnexus/run.cjs analyze` from the project root — it auto-selects an available runner. No `.gitnexus/run.cjs` yet? `npx gitnexus analyze` (npm 11 crash → `npm i -g gitnexus`; #1939).
|
||||
|
||||
## Always Do
|
||||
|
||||
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
|
||||
- **MUST run `detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows. For regression review, compare against the default branch: `detect_changes({scope: "compare", base_ref: "main"})`.
|
||||
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
|
||||
- When exploring unfamiliar code, use `query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
|
||||
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `context({name: "symbolName"})`.
|
||||
|
||||
## Never Do
|
||||
|
||||
- NEVER edit a function, class, or method without first running `impact` on it.
|
||||
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
|
||||
- NEVER rename symbols with find-and-replace — use `rename` which understands the call graph.
|
||||
- NEVER commit changes without running `detect_changes()` to check affected scope.
|
||||
|
||||
## Resources
|
||||
|
||||
| Resource | Use for |
|
||||
|----------|---------|
|
||||
| `gitnexus://repo/dashboard/context` | Codebase overview, check index freshness |
|
||||
| `gitnexus://repo/dashboard/clusters` | All functional areas |
|
||||
| `gitnexus://repo/dashboard/processes` | All execution flows |
|
||||
| `gitnexus://repo/dashboard/process/{name}` | Step-by-step execution trace |
|
||||
|
||||
## CLI
|
||||
|
||||
| Task | Read this skill file |
|
||||
|------|---------------------|
|
||||
| Understand architecture / "How does X work?" | `.claude/skills/gitnexus/gitnexus-exploring/SKILL.md` |
|
||||
| Blast radius / "What breaks if I change X?" | `.claude/skills/gitnexus/gitnexus-impact-analysis/SKILL.md` |
|
||||
| Trace bugs / "Why is X failing?" | `.claude/skills/gitnexus/gitnexus-debugging/SKILL.md` |
|
||||
| Rename / extract / split / refactor | `.claude/skills/gitnexus/gitnexus-refactoring/SKILL.md` |
|
||||
| Tools, resources, schema reference | `.claude/skills/gitnexus/gitnexus-guide/SKILL.md` |
|
||||
| Index, status, clean, wiki CLI commands | `.claude/skills/gitnexus/gitnexus-cli/SKILL.md` |
|
||||
|
||||
<!-- gitnexus:end -->
|
||||
59
docs/contexts/access-control.md
Normal file
59
docs/contexts/access-control.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# 访问控制模块
|
||||
|
||||
## 功能
|
||||
管理访问控制策略(ACL)- 定义哪些对等节点可以通信。
|
||||
|
||||
## API 接口
|
||||
- `GET /api/policies` - 列出策略
|
||||
- `GET /api/policies/:id` - 获取策略详情
|
||||
- `POST /api/policies` - 创建策略
|
||||
- `PUT /api/policies/:id` - 更新策略
|
||||
- `DELETE /api/policies/:id` - 删除策略
|
||||
|
||||
## 关键类型
|
||||
```typescript
|
||||
interface Policy {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
rules: PolicyRule[];
|
||||
// ... 更多字段
|
||||
}
|
||||
|
||||
interface PolicyRule {
|
||||
name: string;
|
||||
sources: Group[];
|
||||
destinations: Group[];
|
||||
// ... 更多字段
|
||||
}
|
||||
```
|
||||
|
||||
## 文件路径
|
||||
- `src/modules/access-control/` - UI 组件
|
||||
- `src/contexts/PoliciesProvider.tsx` - 数据提供者
|
||||
- `src/app/(dashboard)/access-control/page.tsx` - 页面组件
|
||||
|
||||
## 组件
|
||||
- 策略列表表格
|
||||
- 策略编辑器
|
||||
- 规则配置
|
||||
|
||||
## 使用方法
|
||||
```tsx
|
||||
import { usePolicies } from "@/contexts/PoliciesProvider";
|
||||
|
||||
function MyComponent() {
|
||||
const { policies, isLoading } = usePolicies();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 命令
|
||||
- 页面:`/access-control`
|
||||
|
||||
## 注意事项
|
||||
- 策略按顺序评估
|
||||
- 禁用的策略会被跳过
|
||||
- 规则引用分组,而不是单个对等节点
|
||||
- 可以向策略添加姿态检查
|
||||
97
docs/contexts/api-client.md
Normal file
97
docs/contexts/api-client.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# API Client
|
||||
|
||||
## Purpose
|
||||
Centralized API client with SWR integration and OIDC authentication.
|
||||
|
||||
## File Path
|
||||
- `src/utils/api.tsx`
|
||||
|
||||
## Key Exports
|
||||
```typescript
|
||||
// Main hook for API calls
|
||||
export default function useFetchApi<T>(
|
||||
url: string,
|
||||
options?: RequestOptions
|
||||
): SWRResponse<T, ErrorResponse>;
|
||||
|
||||
// Request options
|
||||
type RequestOptions = {
|
||||
key?: string;
|
||||
signal?: AbortSignal;
|
||||
origin?: string;
|
||||
globalParams?: Params;
|
||||
ignoreGlobalParams?: boolean;
|
||||
refreshInterval?: number;
|
||||
blob?: boolean;
|
||||
shouldRetryOnError?: boolean;
|
||||
};
|
||||
|
||||
// Error response type
|
||||
export type ErrorResponse = {
|
||||
code: number;
|
||||
message: string;
|
||||
};
|
||||
|
||||
// Query params type
|
||||
export type Params = Record<string, string | number | boolean>;
|
||||
```
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
### GET Request
|
||||
```tsx
|
||||
const { data, isLoading, error } = useFetchApi<Peer[]>("/peers");
|
||||
```
|
||||
|
||||
### GET with Refresh
|
||||
```tsx
|
||||
const { data } = useFetchApi<Peer[]>("/peers", {
|
||||
refreshInterval: 5000, // Poll every 5 seconds
|
||||
});
|
||||
```
|
||||
|
||||
### POST/PUT/DELETE
|
||||
```tsx
|
||||
const { mutate } = useFetchApi("/peers", { method: "POST" });
|
||||
// Or use direct apiRequest function
|
||||
```
|
||||
|
||||
### Custom Key
|
||||
```tsx
|
||||
const { data } = useFetchApi("/peers", {
|
||||
key: "my-custom-key",
|
||||
});
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Authentication
|
||||
- Uses OIDC tokens from `@axa-fr/react-oidc`
|
||||
- Automatically injects Authorization header
|
||||
- Handles token refresh
|
||||
|
||||
### Caching
|
||||
- Uses SWR for caching and revalidation
|
||||
- Global config in `ApplicationProvider`
|
||||
- Supports refresh intervals
|
||||
|
||||
### Error Handling
|
||||
- Returns structured `ErrorResponse`
|
||||
- Integrates with `ErrorBoundary`
|
||||
- Configurable retry behavior
|
||||
|
||||
### Configuration
|
||||
- Base URL from `config.json` (`apiOrigin`)
|
||||
- Can override with `origin` option
|
||||
- Global params merged from `ApplicationContext`
|
||||
|
||||
## Dependencies
|
||||
- `swr` - Data fetching and caching
|
||||
- `@axa-fr/react-oidc` - OIDC authentication
|
||||
- `react-jwt` - JWT token handling
|
||||
|
||||
## Gotchas
|
||||
- Requires OIDC provider to be initialized
|
||||
- Tokens stored in memory (not localStorage)
|
||||
- Global params applied to all requests unless `ignoreGlobalParams: true`
|
||||
- Use `shouldRetryOnError: false` to disable automatic retries
|
||||
93
docs/contexts/authentication.md
Normal file
93
docs/contexts/authentication.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Authentication
|
||||
|
||||
## Purpose
|
||||
OIDC-based authentication using Auth0 or compatible providers.
|
||||
|
||||
## File Paths
|
||||
- `src/auth/OIDCProvider.tsx` - Main OIDC provider
|
||||
- `src/auth/SecureProvider.tsx` - Protected route wrapper
|
||||
- `src/auth/OIDCError.tsx` - Error display
|
||||
- `src/auth/SessionLost.tsx` - Session lost handling
|
||||
|
||||
## Key Dependencies
|
||||
- `@axa-fr/react-oidc` - OIDC client library
|
||||
|
||||
## Configuration
|
||||
Configuration in `config.json`:
|
||||
```json
|
||||
{
|
||||
"auth0Domain": "your-tenant.auth0.com",
|
||||
"auth0ClientId": "your-client-id",
|
||||
"auth0Audience": "your-audience",
|
||||
"auth0Authority": "https://your-tenant.auth0.com",
|
||||
"auth0RedirectUri": "http://localhost:3000",
|
||||
"authScope": "openid profile email"
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variables (Docker)
|
||||
- `AUTH0_DOMAIN` - Auth0 tenant domain
|
||||
- `AUTH0_CLIENT_ID` - Auth0 application client ID
|
||||
- `AUTH0_AUDIENCE` - API audience identifier
|
||||
|
||||
## Usage
|
||||
|
||||
### Protected Routes
|
||||
```tsx
|
||||
import SecureProvider from "@/auth/SecureProvider";
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return <SecureProvider>{children}</SecureProvider>;
|
||||
}
|
||||
```
|
||||
|
||||
### Access User Info
|
||||
```tsx
|
||||
import { useOidc, useOidcAccessToken, useOidcIdToken } from "@axa-fr/react-oidc";
|
||||
|
||||
function MyComponent() {
|
||||
const { isAuthenticated, login, logout } = useOidc();
|
||||
const { oidcAccessToken } = useOidcAccessToken();
|
||||
const { oidcIdToken } = useOidcIdToken();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Flow
|
||||
1. User visits protected route
|
||||
2. `SecureProvider` checks authentication
|
||||
3. If not authenticated, redirects to Auth0 login
|
||||
4. After login, Auth0 redirects back with tokens
|
||||
5. Tokens stored in memory (not localStorage)
|
||||
6. API calls use access token for authorization
|
||||
|
||||
## Service Worker
|
||||
OIDC service worker must be copied to `public/`:
|
||||
```bash
|
||||
npm run copy
|
||||
npm run copytrusted
|
||||
```
|
||||
|
||||
This enables:
|
||||
- Token refresh without page reload
|
||||
- Secure token storage
|
||||
- Silent authentication
|
||||
|
||||
## Local Development
|
||||
Create `.local-config.json`:
|
||||
```json
|
||||
{
|
||||
"auth0Domain": "localhost",
|
||||
"auth0ClientId": "test-client-id",
|
||||
"auth0Audience": "test-audience",
|
||||
"auth0Authority": "http://localhost:9999",
|
||||
"auth0RedirectUri": "http://localhost:3000"
|
||||
}
|
||||
```
|
||||
|
||||
## Gotchas
|
||||
- Service worker file must be in `public/` directory
|
||||
- Tokens are in-memory only (cleared on page refresh)
|
||||
- CORS must be configured on Auth0 for local development
|
||||
- Redirect URI must match Auth0 configuration exactly
|
||||
- Silent authentication requires HTTPS in production
|
||||
46
docs/contexts/dns.md
Normal file
46
docs/contexts/dns.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# DNS 模块
|
||||
|
||||
## 功能
|
||||
管理网络的 DNS 名称服务器和 DNS 设置。
|
||||
|
||||
## API 接口
|
||||
- `GET /api/nameservers` - 列出名称服务器
|
||||
- `GET /api/nameservers/:id` - 获取名称服务器详情
|
||||
- `POST /api/nameservers` - 创建名称服务器
|
||||
- `PUT /api/nameservers/:id` - 更新名称服务器
|
||||
- `DELETE /api/nameservers/:id` - 删除名称服务器
|
||||
|
||||
## 关键类型
|
||||
```typescript
|
||||
interface Nameserver {
|
||||
id: string;
|
||||
name: string;
|
||||
ip: string;
|
||||
port: number;
|
||||
groups: Group[];
|
||||
// ... 更多字段
|
||||
}
|
||||
```
|
||||
|
||||
## 文件路径
|
||||
- `src/modules/dns/` - UI 组件
|
||||
- `src/interfaces/Nameserver.ts` - 类型定义
|
||||
- `src/app/(dashboard)/dns/page.tsx` - 页面组件
|
||||
|
||||
## 组件
|
||||
- 名称服务器列表表格
|
||||
- 名称服务器编辑器
|
||||
- 分组分配
|
||||
|
||||
## 使用方法
|
||||
```tsx
|
||||
// 通过自定义钩子或上下文访问
|
||||
```
|
||||
|
||||
## 命令
|
||||
- 页面:`/dns`
|
||||
|
||||
## 注意事项
|
||||
- 名称服务器分配给分组
|
||||
- 分组决定哪些对等节点使用哪些名称服务器
|
||||
- 可以配置默认名称服务器
|
||||
53
docs/contexts/networks.md
Normal file
53
docs/contexts/networks.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# 网络模块
|
||||
|
||||
## 功能
|
||||
配置和管理网络设置、路由和网络级策略。
|
||||
|
||||
## API 接口
|
||||
- `GET /api/networks` - 列出网络
|
||||
- `GET /api/networks/:id` - 获取网络详情
|
||||
- `POST /api/networks` - 创建网络
|
||||
- `PUT /api/networks/:id` - 更新网络
|
||||
- `DELETE /api/networks/:id` - 删除网络
|
||||
|
||||
## 关键类型
|
||||
```typescript
|
||||
interface Network {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
// ... 更多字段
|
||||
}
|
||||
```
|
||||
|
||||
## 文件路径
|
||||
- `src/modules/networks/` - UI 组件
|
||||
- `src/contexts/RoutesProvider.tsx` - 路由数据
|
||||
- `src/contexts/GroupRouteProvider.tsx` - 分组路由
|
||||
- `src/interfaces/Network.ts` - 类型定义
|
||||
- `src/interfaces/Route.ts` - 路由类型
|
||||
- `src/app/(dashboard)/networks/page.tsx` - 网络页面
|
||||
- `src/app/(dashboard)/network-routes/page.tsx` - 路由页面
|
||||
|
||||
## 组件
|
||||
- 网络列表组件在 `src/modules/networks/` 中
|
||||
- 路由管理在 `src/modules/routes/` 中
|
||||
|
||||
## 使用方法
|
||||
```tsx
|
||||
import { useRoutes } from "@/contexts/RoutesProvider";
|
||||
|
||||
function MyComponent() {
|
||||
const { routes, isLoading } = useRoutes();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 命令
|
||||
- 网络:`/networks`
|
||||
- 路由:`/network-routes`
|
||||
|
||||
## 注意事项
|
||||
- 网络按账户范围划分
|
||||
- 路由定义对等节点之间的流量流向
|
||||
- 分组路由允许将路由分配给对等节点分组
|
||||
61
docs/contexts/peers.md
Normal file
61
docs/contexts/peers.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# 对等节点模块
|
||||
|
||||
## 功能
|
||||
管理网络对等节点 - 查看、配置和监控已连接的设备。
|
||||
|
||||
## API 接口
|
||||
- `GET /api/peers` - 列出所有对等节点
|
||||
- `GET /api/peers/:id` - 获取对等节点详情
|
||||
- `PUT /api/peers/:id` - 更新对等节点
|
||||
- `DELETE /api/peers/:id` - 删除对等节点
|
||||
|
||||
## 关键类型
|
||||
```typescript
|
||||
interface Peer {
|
||||
id: string;
|
||||
name: string;
|
||||
ip: string;
|
||||
connected: boolean;
|
||||
last_seen: string;
|
||||
os: OperatingSystem;
|
||||
version: string;
|
||||
groups: Group[];
|
||||
// ... 更多字段
|
||||
}
|
||||
```
|
||||
|
||||
## 文件路径
|
||||
- `src/modules/peers/` - UI 组件
|
||||
- `src/contexts/PeersProvider.tsx` - 数据提供者
|
||||
- `src/interfaces/Peer.ts` - 类型定义
|
||||
- `src/app/(dashboard)/peers/page.tsx` - 页面组件
|
||||
- `src/app/(dashboard)/peer/[id]/page.tsx` - 详情页面
|
||||
|
||||
## 组件
|
||||
- `PeersTable.tsx` - 主要对等节点列表表格
|
||||
- `PeerNameCell.tsx` - 对等节点名称显示
|
||||
- `PeerAddressCell.tsx` - IP 地址显示
|
||||
- `PeerStatusCell.tsx` - 连接状态
|
||||
- `PeerOSCell.tsx` - 操作系统图标
|
||||
- `PeerGroupCell.tsx` - 分组成员资格
|
||||
- `PeerActionCell.tsx` - 操作按钮
|
||||
- `PeerMultiSelect.tsx` - 多对等节点选择器
|
||||
|
||||
## 使用方法
|
||||
```tsx
|
||||
import { usePeers } from "@/contexts/PeersProvider";
|
||||
|
||||
function MyComponent() {
|
||||
const { peers, isLoading } = usePeers();
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 命令
|
||||
- 页面:`/peers`
|
||||
- 详情:`/peer/:id`
|
||||
|
||||
## 注意事项
|
||||
- 对等节点列表可能很大 - 使用虚拟滚动
|
||||
- 状态更新通过轮询实现(SWR refreshInterval)
|
||||
- 操作系统图标位于 `src/assets/os-icons/`
|
||||
@@ -581,7 +581,10 @@ serviceUsersDescription: "Use service users to create API tokens and avoid losin
|
||||
serviceUsersEmptyDescription: "It looks like you don't have any service users. Get started by creating a service user.",
|
||||
blocked: "Blocked",
|
||||
accessTokens: "Access Tokens",
|
||||
accessTokensDescription: "Access tokens give access to NetBird API."
|
||||
accessTokensDescription: "Access tokens give access to NetBird API.",
|
||||
tokenName: "Name",
|
||||
tokenNameHelp: "Set an easily identifiable name for your token",
|
||||
tokenExpiresIn: "Expires in"
|
||||
},
|
||||
settings: {
|
||||
title: "Settings",
|
||||
@@ -948,7 +951,21 @@ serviceUsersDescription: "Use service users to create API tokens and avoid losin
|
||||
resourceTab: "Resource",
|
||||
optionalSettings: "Optional Settings",
|
||||
accessControlPolicies: "Access Control Policies",
|
||||
accessControlPoliciesHelp: "Define which source groups are allowed to access this resource. You can also restrict access to specific protocols and ports. Without policies access to this resource will not be possible."
|
||||
accessControlPoliciesHelp: "Define which source groups are allowed to access this resource. You can also restrict access to specific protocols and ports. Without policies access to this resource will not be possible.",
|
||||
routeType: "Route Type",
|
||||
routeTypeHelp: "Select your route type to add either a network range or a list of domains.",
|
||||
routeTypeNetworkRange: "Network Range",
|
||||
routeTypeDomains: "Domains",
|
||||
networkRange: "Network Range",
|
||||
networkRangeHelp: "Add a private IPv4 or IPv6 address or range",
|
||||
networkRangePlaceholder: "e.g., 172.16.0.1, 172.16.0.0/16, 2001:db8::1 or 2001:db8::/64",
|
||||
domains: "Domains",
|
||||
distributionGroups: "Distribution Groups",
|
||||
networkIdentifier: "Network Identifier",
|
||||
metric: "Metric",
|
||||
metricHelp: "Set a metric value to prioritize routes. Lower values take precedence.",
|
||||
metricPlaceholder: "Enter metric value (1-9999)",
|
||||
additionalSettings: "Additional Settings"
|
||||
},
|
||||
postureChecks: {
|
||||
title: "Posture Checks",
|
||||
@@ -1062,7 +1079,16 @@ serviceUsersDescription: "Use service users to create API tokens and avoid losin
|
||||
updated: "Setup key '{name}' successfully updated",
|
||||
deleted: "Setup key '{name}' successfully deleted",
|
||||
noSetupKeys: "No setup keys available",
|
||||
searchPlaceholder: "Search setup keys..."
|
||||
searchPlaceholder: "Search setup keys...",
|
||||
createTitle: "Create New Setup Key",
|
||||
createDescription: "Use this key to register new machines in your network",
|
||||
nameHelp: "Set an easily identifiable name for your key",
|
||||
usageLimitHelp: "For example, set to 30 if you want to enroll 30 peers",
|
||||
usageLimitSuffix: "Peer(s)",
|
||||
expiresIn: "Expires in",
|
||||
expiresInHelp: "Days until the key expires.",
|
||||
expiresInHelpEmpty: "Leave empty for no expiration.",
|
||||
expiresInSuffix: "Day(s)"
|
||||
},
|
||||
activity: {
|
||||
title: "Activity",
|
||||
|
||||
@@ -581,7 +581,10 @@ serviceUsersDescription: "使用服务用户创建 API 令牌,避免丢失自
|
||||
serviceUsersEmptyDescription: "看起来您还没有任何服务用户。开始使用,创建一个服务用户。",
|
||||
blocked: "已阻止",
|
||||
accessTokens: "访问令牌",
|
||||
accessTokensDescription: "访问令牌提供对 NetBird API 的访问权限。"
|
||||
accessTokensDescription: "访问令牌提供对 NetBird API 的访问权限。",
|
||||
tokenName: "名称",
|
||||
tokenNameHelp: "为令牌设置一个易于识别的名称",
|
||||
tokenExpiresIn: "过期时间"
|
||||
},
|
||||
settings: {
|
||||
title: "设置",
|
||||
@@ -948,7 +951,21 @@ disable2FA: "禁用两步验证",
|
||||
resourceTab: "资源",
|
||||
optionalSettings: "可选设置",
|
||||
accessControlPolicies: "访问控制策略",
|
||||
accessControlPoliciesHelp: "定义允许访问此资源的源组。您还可以限制对特定协议和端口的访问。如果没有策略,将无法访问此资源。"
|
||||
accessControlPoliciesHelp: "定义允许访问此资源的源组。您还可以限制对特定协议和端口的访问。如果没有策略,将无法访问此资源。",
|
||||
routeType: "路由类型",
|
||||
routeTypeHelp: "选择路由类型以添加网络范围或域名列表。",
|
||||
routeTypeNetworkRange: "网络范围",
|
||||
routeTypeDomains: "域名",
|
||||
networkRange: "网络范围",
|
||||
networkRangeHelp: "添加私有 IPv4 或 IPv6 地址或范围",
|
||||
networkRangePlaceholder: "例如:172.16.0.1, 172.16.0.0/16, 2001:db8::1 或 2001:db8::/64",
|
||||
domains: "域名",
|
||||
distributionGroups: "分发组",
|
||||
networkIdentifier: "网络标识符",
|
||||
metric: "度量值",
|
||||
metricHelp: "设置度量值以优先选择路由。数值越低的优先级越高。",
|
||||
metricPlaceholder: "输入度量值 (1-9999)",
|
||||
additionalSettings: "其他设置"
|
||||
},
|
||||
postureChecks: {
|
||||
title: "姿态检查",
|
||||
@@ -1062,7 +1079,16 @@ disable2FA: "禁用两步验证",
|
||||
updated: "安装密钥 '{name}' 更新成功",
|
||||
deleted: "安装密钥 '{name}' 已删除",
|
||||
noSetupKeys: "暂无安装密钥",
|
||||
searchPlaceholder: "搜索安装密钥..."
|
||||
searchPlaceholder: "搜索安装密钥...",
|
||||
createTitle: "创建新的安装密钥",
|
||||
createDescription: "使用此密钥在网络中注册新设备",
|
||||
nameHelp: "为密钥设置一个易于识别的名称",
|
||||
usageLimitHelp: "例如,设置为 30 表示允许注册 30 台设备",
|
||||
usageLimitSuffix: "台设备",
|
||||
expiresIn: "过期时间",
|
||||
expiresInHelp: "密钥过期前的天数。",
|
||||
expiresInHelpEmpty: "留空表示永不过期。",
|
||||
expiresInSuffix: "天"
|
||||
},
|
||||
activity: {
|
||||
title: "活动",
|
||||
|
||||
@@ -134,7 +134,8 @@ export function AccessTokenModalContent({
|
||||
onSuccess,
|
||||
user,
|
||||
}: Readonly<ModalProps>) {
|
||||
const t = useTranslations("common");
|
||||
const t = useTranslations("serviceUsers");
|
||||
const tCommon = useTranslations("common");
|
||||
const tokenRequest = useApiCall<AccessToken>(`/users/${user.id}/tokens`);
|
||||
const { mutate } = useSWRConfig();
|
||||
|
||||
@@ -177,8 +178,8 @@ export function AccessTokenModalContent({
|
||||
|
||||
<div className={"px-8 py-6 flex flex-col gap-8"}>
|
||||
<div>
|
||||
<Label>Name</Label>
|
||||
<HelpText>Set an easily identifiable name for your token</HelpText>
|
||||
<Label>{t("tokenName")}</Label>
|
||||
<HelpText>{t("tokenNameHelp")}</HelpText>
|
||||
<Input
|
||||
data-testid={"access-token-name"}
|
||||
placeholder={"e.g., Infra token"}
|
||||
@@ -189,7 +190,7 @@ export function AccessTokenModalContent({
|
||||
|
||||
<div className={"flex justify-between"}>
|
||||
<div>
|
||||
<Label>Expires in</Label>
|
||||
<Label>{t("tokenExpiresIn")}</Label>
|
||||
<HelpText>Should be between 1 and 365 days.</HelpText>
|
||||
</div>
|
||||
<Input
|
||||
@@ -224,7 +225,7 @@ export function AccessTokenModalContent({
|
||||
</div>
|
||||
<div className={"flex gap-3 w-full justify-end"}>
|
||||
<ModalClose asChild={true}>
|
||||
<Button variant={"secondary"}>{t("cancel")}</Button>
|
||||
<Button variant={"secondary"}>{tCommon("cancel")}</Button>
|
||||
</ModalClose>
|
||||
|
||||
<Button
|
||||
|
||||
@@ -171,7 +171,12 @@ export function EditGroupsModal({
|
||||
<Button variant={"secondary"}>{tCommon("cancel")}</Button>
|
||||
</ModalClose>
|
||||
|
||||
<Button variant={"primary"} onClick={handleSave} disabled={disabled} data-testid="save-groups">
|
||||
<Button
|
||||
variant={"primary"}
|
||||
onClick={handleSave}
|
||||
disabled={disabled}
|
||||
data-testid="save-groups"
|
||||
>
|
||||
{t("saveGroups")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -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")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value={"network"} className={"pb-8"}>
|
||||
<div className={"px-8 flex-col flex gap-4"}>
|
||||
<div className={cn(exitNode && "hidden")}>
|
||||
<Label>Route Type</Label>
|
||||
<Label>{t("routeType")}</Label>
|
||||
<HelpText>
|
||||
Select your route type to add either a network range or a list
|
||||
of domains.
|
||||
{t("routeTypeHelp")}
|
||||
</HelpText>
|
||||
<div className={"flex justify-between items-center w-full"}>
|
||||
<ButtonGroup className={"w-full"}>
|
||||
@@ -484,7 +484,7 @@ export function RouteModalContent({
|
||||
className={"w-full"}
|
||||
>
|
||||
<NetworkIcon size={16} />
|
||||
Network Range
|
||||
{t("routeTypeNetworkRange")}
|
||||
</ButtonGroup.Button>
|
||||
<ButtonGroup.Button
|
||||
variant={routeType == "domains" ? "tertiary" : "secondary"}
|
||||
@@ -493,7 +493,7 @@ export function RouteModalContent({
|
||||
data-testid="route-type-domains"
|
||||
>
|
||||
<GlobeIcon size={16} />
|
||||
Domains
|
||||
{t("routeTypeDomains")}
|
||||
</ButtonGroup.Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
@@ -504,12 +504,12 @@ export function RouteModalContent({
|
||||
routeType !== "ip-range" && "hidden",
|
||||
)}
|
||||
>
|
||||
<Label>Network Range</Label>
|
||||
<HelpText>Add a private IPv4 or IPv6 address or range</HelpText>
|
||||
<Label>{t("networkRange")}</Label>
|
||||
<HelpText>{t("networkRangeHelp")}</HelpText>
|
||||
<Input
|
||||
ref={networkRangeRef}
|
||||
customPrefix={<NetworkIcon size={16} />}
|
||||
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({
|
||||
<div
|
||||
className={cn("mt-5 mb-3", routeType !== "domains" && "hidden")}
|
||||
>
|
||||
<Label>Domains</Label>
|
||||
<Label>{t("domains")}</Label>
|
||||
<HelpText>
|
||||
Add domains that dynamically resolve to one or more IPv4
|
||||
addresses. <br /> A maximum of 32 domains can be added.
|
||||
@@ -670,7 +670,7 @@ export function RouteModalContent({
|
||||
<TabsContent value={"access-control"} className={"pb-8"}>
|
||||
<div className={"px-8 flex-col flex gap-6"}>
|
||||
<div>
|
||||
<Label>Distribution Groups</Label>
|
||||
<Label>{t("distributionGroups")}</Label>
|
||||
<HelpText>
|
||||
{exitNode
|
||||
? peer
|
||||
@@ -701,7 +701,7 @@ export function RouteModalContent({
|
||||
<TabsContent value={"general"} className={"px-8 pb-6"}>
|
||||
<div className={"flex flex-col gap-6"}>
|
||||
<div>
|
||||
<Label>Network Identifier</Label>
|
||||
<Label>{t("networkIdentifier")}</Label>
|
||||
<HelpText>
|
||||
Add a unique network identifier that is assigned to each device.
|
||||
</HelpText>
|
||||
@@ -774,7 +774,7 @@ export function RouteModalContent({
|
||||
|
||||
<div className={cn("flex justify-between")}>
|
||||
<div>
|
||||
<Label>Metric</Label>
|
||||
<Label>{t("metric")}</Label>
|
||||
<HelpText className={"max-w-[200px]"}>
|
||||
A lower metric indicates higher priority routes.
|
||||
</HelpText>
|
||||
@@ -822,7 +822,7 @@ export function RouteModalContent({
|
||||
<div className={"flex gap-3 w-full justify-end"}>
|
||||
{(tab == "network" || (tab == "access-control" && exitNode)) && (
|
||||
<ModalClose asChild={true}>
|
||||
<Button variant={"secondary"}>{t("cancel")}</Button>
|
||||
<Button variant={"secondary"}>{tCommon("cancel")}</Button>
|
||||
</ModalClose>
|
||||
)}
|
||||
|
||||
|
||||
@@ -58,7 +58,6 @@ export default function SetupKeyModal({
|
||||
showOnlyRoutingPeerOS,
|
||||
groups,
|
||||
}: Readonly<Props>) {
|
||||
const t = useTranslations("common");
|
||||
const [successModal, setSuccessModal] = useState(false);
|
||||
const [setupKey, setSetupKey] = useState<SetupKey>();
|
||||
const [installModal, setInstallModal] = useState(false);
|
||||
@@ -172,7 +171,8 @@ export function SetupKeyModalContent({
|
||||
predefinedName = "",
|
||||
groups,
|
||||
}: Readonly<ModalProps>) {
|
||||
const t = useTranslations("common");
|
||||
const t = useTranslations("setupKeys");
|
||||
const tCommon = useTranslations("common");
|
||||
const setupKeyRequest = useApiCall<SetupKey>("/setup-keys", true);
|
||||
const { mutate } = useSWRConfig();
|
||||
|
||||
@@ -230,8 +230,8 @@ export function SetupKeyModalContent({
|
||||
<ModalContent maxWidthClass={"max-w-xl"}>
|
||||
<ModalHeader
|
||||
icon={<SetupKeysIcon className={"fill-netbird"} />}
|
||||
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({
|
||||
<div className={"px-8 py-6 flex flex-col gap-8"}>
|
||||
{/* Name Field */}
|
||||
<div>
|
||||
<Label>Name</Label>
|
||||
<HelpText>Set an easily identifiable name for your key</HelpText>
|
||||
<Label>{t("name")}</Label>
|
||||
<HelpText>{t("nameHelp")}</HelpText>
|
||||
<Input
|
||||
placeholder={"e.g., AWS Servers"}
|
||||
placeholder={t("namePlaceholder")}
|
||||
value={name}
|
||||
data-testid={"setup-key-name"}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
@@ -268,9 +268,9 @@ export function SetupKeyModalContent({
|
||||
{/* Usage Limit */}
|
||||
<div className={cn("flex justify-between", !reusable && "opacity-50")}>
|
||||
<div>
|
||||
<Label>Usage limit</Label>
|
||||
<Label>{t("usageLimit")}</Label>
|
||||
<HelpText className={"max-w-[200px]"}>
|
||||
For example, set to 30 if you want to enroll 30 peers
|
||||
{t("usageLimitHelp")}
|
||||
</HelpText>
|
||||
</div>
|
||||
|
||||
@@ -286,18 +286,18 @@ export function SetupKeyModalContent({
|
||||
customPrefix={
|
||||
<MonitorSmartphoneIcon size={16} className={"text-nb-gray-300"} />
|
||||
}
|
||||
customSuffix={"Peer(s)"}
|
||||
customSuffix={t("usageLimitSuffix")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Expires in Days */}
|
||||
<div className={"flex justify-between"}>
|
||||
<div>
|
||||
<Label>Expires in</Label>
|
||||
<Label>{t("expiresIn")}</Label>
|
||||
<HelpText>
|
||||
Days until the key expires.
|
||||
{t("expiresInHelp")}
|
||||
<br />
|
||||
Leave empty for no expiration.
|
||||
{t("expiresInHelpEmpty")}
|
||||
</HelpText>
|
||||
</div>
|
||||
<Input
|
||||
@@ -312,7 +312,7 @@ export function SetupKeyModalContent({
|
||||
customPrefix={
|
||||
<AlarmClock size={16} className={"text-nb-gray-300"} />
|
||||
}
|
||||
customSuffix={"Day(s)"}
|
||||
customSuffix={t("expiresInSuffix")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -383,7 +383,7 @@ export function SetupKeyModalContent({
|
||||
</div>
|
||||
<div className={"flex gap-3 w-full justify-end"}>
|
||||
<ModalClose asChild={true}>
|
||||
<Button variant={"secondary"}>{t("cancel")}</Button>
|
||||
<Button variant={"secondary"}>{tCommon("cancel")}</Button>
|
||||
</ModalClose>
|
||||
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user