Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/aneiangsoft_aneiang.yarp.json
T

1 line
519 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"repo": "aneiangsoft/aneiang.yarp", "files": {"README.md": {"0b6ab4b8194091061ad932030fcaffa89e827917": {"t": 1785746399.1603527, "n": 24573, "h": "3171a48f49147a44", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · AI Assistant · 2FA · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, AI assistant, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com\n\n> **Live Demo**: https://yarp-test.aneiang.com/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n> **Online proxy address (site and proxy port are isolated)**: https://yarp-proxy.aneiang.com\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, WAF, AI assistant (Function Calling), 2FA, notifications, health check, circuit breakers, audit log | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities (8 repository interfaces) | ❌ |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite implementation with SQLCipher AES-256 encryption | via abstractions |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol (GatewayRegistry.proto) | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n ├── Aneiang.Yarp.Client\n ├── Aneiang.Yarp.Grpc\n └── Aneiang.Yarp.Storage.Abstractions\n └── Aneiang.Yarp.Storage.Sqlite\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters count, traffic QPS summary, cluster health quick preview, recent change timeline |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations, configure active/passive health checks, `HttpRequest` & `HttpClient` settings |\n| | Routes | Manage routes with transforms, metadata (`Waf:Enabled` per-route), drag-and-drop priority ordering, `CorsPolicy` config |\n| **Monitoring** | Statistics | Request volume trends, P50/P90/P99 latency percentiles, HTTP status distribution pie chart, top routes ranking |\n| | Logs | Real-time log stream (WebSocket) + history (TraceID-paired request/response merged view), filter by route/status/TraceID, sensitive data sanitization, sampling & errors-only mode |\n| | Circuits | Per-cluster/destination circuit breaker status: Closed (green) / Open (red) / HalfOpen (yellow), consecutive failures vs threshold, recovery countdown, one-click reset |\n| | Notifications | Manage webhook channels (DingTalk bot / generic HTTP), configure event rules (per-type toggle + cooldown), view notification history |\n| | Health Check | Cluster-level health overview table, drill-down per destination showing real-time health status, last check time & result |\n| **Security** | WAF Firewall | IP whitelist/blacklist (exact IP / CIDR / wildcard), SQL injection, XSS, path traversal detection, request size limits, security response headers |\n| | Policies | Traffic policies: create, edit, enable, disable, reorder — supports retry, timeout, rate limit, request transform |\n| | Plugins | View all registered `IGatewayPlugin` plugins, plugin metadata & load order |\n| **System** | Config History | Auto-snapshot before every change, view full content of any snapshot, one-click rollback to any version |\n| | Audit Log | Full audit trail: operation type, target, operator, before/after JSON diff, precise timestamp |\n| | Deployment | Current deployment mode (AllInOne/Split/ProxyOnly/DashboardOnly), listening endpoints (name/address/port/role/public status), health check endpoints & security warnings |\n| | Settings | Auth mode switching, JWT key config, AI config, log settings (sampling rate/sanitize list/body length limit), one-click database download, language switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF blocks are recorded in proxy logs, viewable in the Logs page with route and time filtering.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n`YarpRequestCaptureMiddleware` captures request data before forwarding and response data on return:\n\n- **Full capture**: HTTP method, full URL, request/response headers, request/response body, elapsed time, TraceId\n- **Real-time push**: WebSocket endpoint pushes logs to Dashboard in real time — no manual refresh needed\n- **History with TraceID pairing**: `ProxyRequest` and `ProxyResponse` are auto-paired by TraceID and displayed as a merged row, showing the complete request → forward → response data flow\n- **Sanitization**: `LogHeaderBlacklist` masks sensitive headers (`Authorization`, `Cookie`, etc.), `LogJsonFieldSanitizeList` recursively masks JSON fields (`password`, `token`, etc.)\n- **Sampling & filtering**: `LogSamplingRate` (0.0–1.0) for rate-based sampling; `LogErrorsOnly` for 4xx/5xx only; combined filtering by route name, status code, TraceID\n- **Structured storage**: `YarpEventFormatter` → `StructuredLogService` → SQLite with batch write optimization\n\n### AI Assistant\n\nDashboard-embedded AI chat assistant, powered by OpenAI-compatible LLMs, enabling natural language gateway management:\n\n**Multi-provider support**: Works with OpenAI, DeepSeek, Qwen, and any OpenAI-compatible API — switch via `Provider` + `BaseUrl` + `ApiKey`\n\n**40 Function Calling tools**:\n- **Read tools (21)**: auto-executed — query routes, clusters, circuit breaker status, proxy logs, traffic stats, WAF config, policies, audit log, etc.\n- **Write tools (19)**: require user confirmation — create/delete routes, create/update clusters, reset circuit breakers, update WAF settings, rollback config, etc.\n\n**Read/write separation security model**:\n- Read operations execute automatically and return results — no manual intervention needed\n- Write operations are proposed by AI, rendered as confirmation cards in the frontend, and only executed after the user clicks \"Confirm\"\n- All write operations must be explicitly authorized by the user, ensuring operational safety\n\n**Streaming & multi-turn conversation**:\n- Direct streaming output (typewriter effect) when no tool call is needed; two-phase output (tool execution → streaming summary) when tools are involved\n- Conversation history persisted to storage, supporting continuous context-aware dialogue\n- AI response language auto-follows Dashboard UI language (Chinese / English)\n\n**Floating chat on all pages**: a floating button at the bottom-right corner of every Dashboard page — one click to open the chat panel without leaving the current page\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n### Two-Factor Authentication (2FA)\n\nTOTP-based 2FA for enhanced login security:\n\n- **TOTP helper**: `TotpHelper` generates Base32 secrets + `otpauth://` URIs compatible with Google Authenticator, Microsoft Authenticator, etc.\n- **Setup flow**: Settings page → generate secret → scan QR code → enter 6-digit code to verify binding\n- **Login flow**: when 2FA enabled, login returns `202` → login page shows verification code input → submit code to complete login\n- **Runtime persistence**: 2FA state saved to `twofactor-state.json`, survives restarts\n- **Per-user control**: enable/disable 2FA anytime from Settings page\n\n### Enterprise UI\n\n- **Login page**: glassmorphism card, brand gradient panel, animated bubble background, version + license footer\n- **Sidebar**: collapsible grouped menu (5 groups), auto-expand active group, brand header with gradient icon, user card with online indicator\n- **Responsive**: mobile-friendly sidebar overlay, adaptive layouts\n- **i18n**: Chinese / English runtime switch, 180+ i18n keys\n- **Performance**: WOFF2 fonts, Brotli compression, per-page module loading, lazy Monaco editor, stripped unused language packs\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"],\n\n \"AI\": {\n \"Enabled\": true,\n \"Provider\": \"openai\",\n \"ApiKey\": \"sk-...\",\n \"BaseUrl\": \"https://api.openai.com/v1\",\n \"ChatModel\": \"gpt-4o-mini\"\n }\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n### AI Options\n\nConfiguration under `Gateway:Dashboard:AI`:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | AI module master switch |\n| `Provider` | `\"openai\"` | Provider: `openai` / `deepseek` / `qwen` / `custom` |\n| `ApiKey` | — | LLM provider API key |\n| `BaseUrl` | `\"https://api.openai.com/v1\"` | OpenAI-compatible API endpoint |\n| `ChatModel` | `\"gpt-4o-mini\"` | Model used for interactive chat |\n| `AnalysisModel` | `\"gpt-4o-mini\"` | Model used for background analysis (can use a cheaper model) |\n| `MaxTokens` | `4096` | Max tokens per response |\n| `Temperature` | `0.3` | Sampling temperature (0.0 = deterministic, 1.0 = creative) |\n| `MaxConversationHistory` | `20` | Recent conversation turns to retain |\n| `EnableBackgroundAnalysis` | `false` | Enable periodic background log analysis |\n| `AnalysisInterval` | `01:00:00` | Background analysis interval |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"],\n\n \"AI\": {\n \"Enabled\": true,\n \"Provider\": \"deepseek\",\n \"ApiKey\": \"sk-...\",\n \"BaseUrl\": \"https://api.deepseek.com/v1\",\n \"ChatModel\": \"deepseek-chat\",\n \"AnalysisModel\": \"deepseek-chat\"\n }\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Storage.Abstractions.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Abstractions) |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite storage with SQLCipher encryption | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Storage.Sqlite.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Sqlite) |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Grpc.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Grpc) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n<div align=\"center\" style=\"display: flex; justify-content: center; align-items: center; gap: 20px; \n background: #f6f8fa; padding: 20px 30px; border-radius: 12px; \n border: 1px solid #e1e4e8; box-shadow: 0 2px 8px rgba(0,0,0,0.05);\">\n <img src=\"docs/wechat_qrcode.jpg\" alt=\"Official Account QR Code\" \n style=\"width: 140px; height: 140px; border-radius: 8px; border: 2px solid #d0d7de;\" />\n <div style=\"text-align: left;\">\n <h3 style=\"margin: 0 0 4px 0; font-size: 22px; font-weight: 600; color: #24292e;\">\n 递归不爆炸\n </h3>\n <p style=\"margin: 0; font-size: 16px; color: #586069; max-width: 280px;\">\n Scan to follow and get more great content\n </p>\n <!-- Optional: add a WeChat badge -->\n <p style=\"margin-top: 8px;\">\n <img src=\"https://img.shields.io/badge/WeChat-递归不爆炸-brightgreen?style=flat-square&logo=wechat\" alt=\"WeChat\" />\n </p>\n </div>\n</div>\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "12acc257b4626c484c43735f773cccf816e9a5e6": {"t": 1785763348.5572429, "n": 24573, "h": "3171a48f49147a44", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · AI Assistant · 2FA · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, AI assistant, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com\n\n> **Live Demo**: https://yarp-test.aneiang.com/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n> **Online proxy address (site and proxy port are isolated)**: https://yarp-proxy.aneiang.com\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, WAF, AI assistant (Function Calling), 2FA, notifications, health check, circuit breakers, audit log | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities (8 repository interfaces) | ❌ |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite implementation with SQLCipher AES-256 encryption | via abstractions |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol (GatewayRegistry.proto) | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n ├── Aneiang.Yarp.Client\n ├── Aneiang.Yarp.Grpc\n └── Aneiang.Yarp.Storage.Abstractions\n └── Aneiang.Yarp.Storage.Sqlite\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters count, traffic QPS summary, cluster health quick preview, recent change timeline |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations, configure active/passive health checks, `HttpRequest` & `HttpClient` settings |\n| | Routes | Manage routes with transforms, metadata (`Waf:Enabled` per-route), drag-and-drop priority ordering, `CorsPolicy` config |\n| **Monitoring** | Statistics | Request volume trends, P50/P90/P99 latency percentiles, HTTP status distribution pie chart, top routes ranking |\n| | Logs | Real-time log stream (WebSocket) + history (TraceID-paired request/response merged view), filter by route/status/TraceID, sensitive data sanitization, sampling & errors-only mode |\n| | Circuits | Per-cluster/destination circuit breaker status: Closed (green) / Open (red) / HalfOpen (yellow), consecutive failures vs threshold, recovery countdown, one-click reset |\n| | Notifications | Manage webhook channels (DingTalk bot / generic HTTP), configure event rules (per-type toggle + cooldown), view notification history |\n| | Health Check | Cluster-level health overview table, drill-down per destination showing real-time health status, last check time & result |\n| **Security** | WAF Firewall | IP whitelist/blacklist (exact IP / CIDR / wildcard), SQL injection, XSS, path traversal detection, request size limits, security response headers |\n| | Policies | Traffic policies: create, edit, enable, disable, reorder — supports retry, timeout, rate limit, request transform |\n| | Plugins | View all registered `IGatewayPlugin` plugins, plugin metadata & load order |\n| **System** | Config History | Auto-snapshot before every change, view full content of any snapshot, one-click rollback to any version |\n| | Audit Log | Full audit trail: operation type, target, operator, before/after JSON diff, precise timestamp |\n| | Deployment | Current deployment mode (AllInOne/Split/ProxyOnly/DashboardOnly), listening endpoints (name/address/port/role/public status), health check endpoints & security warnings |\n| | Settings | Auth mode switching, JWT key config, AI config, log settings (sampling rate/sanitize list/body length limit), one-click database download, language switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF blocks are recorded in proxy logs, viewable in the Logs page with route and time filtering.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n`YarpRequestCaptureMiddleware` captures request data before forwarding and response data on return:\n\n- **Full capture**: HTTP method, full URL, request/response headers, request/response body, elapsed time, TraceId\n- **Real-time push**: WebSocket endpoint pushes logs to Dashboard in real time — no manual refresh needed\n- **History with TraceID pairing**: `ProxyRequest` and `ProxyResponse` are auto-paired by TraceID and displayed as a merged row, showing the complete request → forward → response data flow\n- **Sanitization**: `LogHeaderBlacklist` masks sensitive headers (`Authorization`, `Cookie`, etc.), `LogJsonFieldSanitizeList` recursively masks JSON fields (`password`, `token`, etc.)\n- **Sampling & filtering**: `LogSamplingRate` (0.0–1.0) for rate-based sampling; `LogErrorsOnly` for 4xx/5xx only; combined filtering by route name, status code, TraceID\n- **Structured storage**: `YarpEventFormatter` → `StructuredLogService` → SQLite with batch write optimization\n\n### AI Assistant\n\nDashboard-embedded AI chat assistant, powered by OpenAI-compatible LLMs, enabling natural language gateway management:\n\n**Multi-provider support**: Works with OpenAI, DeepSeek, Qwen, and any OpenAI-compatible API — switch via `Provider` + `BaseUrl` + `ApiKey`\n\n**40 Function Calling tools**:\n- **Read tools (21)**: auto-executed — query routes, clusters, circuit breaker status, proxy logs, traffic stats, WAF config, policies, audit log, etc.\n- **Write tools (19)**: require user confirmation — create/delete routes, create/update clusters, reset circuit breakers, update WAF settings, rollback config, etc.\n\n**Read/write separation security model**:\n- Read operations execute automatically and return results — no manual intervention needed\n- Write operations are proposed by AI, rendered as confirmation cards in the frontend, and only executed after the user clicks \"Confirm\"\n- All write operations must be explicitly authorized by the user, ensuring operational safety\n\n**Streaming & multi-turn conversation**:\n- Direct streaming output (typewriter effect) when no tool call is needed; two-phase output (tool execution → streaming summary) when tools are involved\n- Conversation history persisted to storage, supporting continuous context-aware dialogue\n- AI response language auto-follows Dashboard UI language (Chinese / English)\n\n**Floating chat on all pages**: a floating button at the bottom-right corner of every Dashboard page — one click to open the chat panel without leaving the current page\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n### Two-Factor Authentication (2FA)\n\nTOTP-based 2FA for enhanced login security:\n\n- **TOTP helper**: `TotpHelper` generates Base32 secrets + `otpauth://` URIs compatible with Google Authenticator, Microsoft Authenticator, etc.\n- **Setup flow**: Settings page → generate secret → scan QR code → enter 6-digit code to verify binding\n- **Login flow**: when 2FA enabled, login returns `202` → login page shows verification code input → submit code to complete login\n- **Runtime persistence**: 2FA state saved to `twofactor-state.json`, survives restarts\n- **Per-user control**: enable/disable 2FA anytime from Settings page\n\n### Enterprise UI\n\n- **Login page**: glassmorphism card, brand gradient panel, animated bubble background, version + license footer\n- **Sidebar**: collapsible grouped menu (5 groups), auto-expand active group, brand header with gradient icon, user card with online indicator\n- **Responsive**: mobile-friendly sidebar overlay, adaptive layouts\n- **i18n**: Chinese / English runtime switch, 180+ i18n keys\n- **Performance**: WOFF2 fonts, Brotli compression, per-page module loading, lazy Monaco editor, stripped unused language packs\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"],\n\n \"AI\": {\n \"Enabled\": true,\n \"Provider\": \"openai\",\n \"ApiKey\": \"sk-...\",\n \"BaseUrl\": \"https://api.openai.com/v1\",\n \"ChatModel\": \"gpt-4o-mini\"\n }\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n### AI Options\n\nConfiguration under `Gateway:Dashboard:AI`:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | AI module master switch |\n| `Provider` | `\"openai\"` | Provider: `openai` / `deepseek` / `qwen` / `custom` |\n| `ApiKey` | — | LLM provider API key |\n| `BaseUrl` | `\"https://api.openai.com/v1\"` | OpenAI-compatible API endpoint |\n| `ChatModel` | `\"gpt-4o-mini\"` | Model used for interactive chat |\n| `AnalysisModel` | `\"gpt-4o-mini\"` | Model used for background analysis (can use a cheaper model) |\n| `MaxTokens` | `4096` | Max tokens per response |\n| `Temperature` | `0.3` | Sampling temperature (0.0 = deterministic, 1.0 = creative) |\n| `MaxConversationHistory` | `20` | Recent conversation turns to retain |\n| `EnableBackgroundAnalysis` | `false` | Enable periodic background log analysis |\n| `AnalysisInterval` | `01:00:00` | Background analysis interval |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"],\n\n \"AI\": {\n \"Enabled\": true,\n \"Provider\": \"deepseek\",\n \"ApiKey\": \"sk-...\",\n \"BaseUrl\": \"https://api.deepseek.com/v1\",\n \"ChatModel\": \"deepseek-chat\",\n \"AnalysisModel\": \"deepseek-chat\"\n }\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Storage.Abstractions.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Abstractions) |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite storage with SQLCipher encryption | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Storage.Sqlite.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Sqlite) |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Grpc.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Grpc) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n<div align=\"center\" style=\"display: flex; justify-content: center; align-items: center; gap: 20px; \n background: #f6f8fa; padding: 20px 30px; border-radius: 12px; \n border: 1px solid #e1e4e8; box-shadow: 0 2px 8px rgba(0,0,0,0.05);\">\n <img src=\"docs/wechat_qrcode.jpg\" alt=\"Official Account QR Code\" \n style=\"width: 140px; height: 140px; border-radius: 8px; border: 2px solid #d0d7de;\" />\n <div style=\"text-align: left;\">\n <h3 style=\"margin: 0 0 4px 0; font-size: 22px; font-weight: 600; color: #24292e;\">\n 递归不爆炸\n </h3>\n <p style=\"margin: 0; font-size: 16px; color: #586069; max-width: 280px;\">\n Scan to follow and get more great content\n </p>\n <!-- Optional: add a WeChat badge -->\n <p style=\"margin-top: 8px;\">\n <img src=\"https://img.shields.io/badge/WeChat-递归不爆炸-brightgreen?style=flat-square&logo=wechat\" alt=\"WeChat\" />\n </p>\n </div>\n</div>\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "dd5d6df047613db54c0a81f44b267318d1527f9e": {"t": 1785763350.0375128, "n": 24478, "h": "c67d358c0768793b", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · AI Assistant · 2FA · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, AI assistant, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com\n\n> **Live Demo**: https://yarp-test.aneiang.com/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, WAF, AI assistant (Function Calling), 2FA, notifications, health check, circuit breakers, audit log | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities (8 repository interfaces) | ❌ |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite implementation with SQLCipher AES-256 encryption | via abstractions |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol (GatewayRegistry.proto) | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n ├── Aneiang.Yarp.Client\n ├── Aneiang.Yarp.Grpc\n └── Aneiang.Yarp.Storage.Abstractions\n └── Aneiang.Yarp.Storage.Sqlite\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters count, traffic QPS summary, cluster health quick preview, recent change timeline |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations, configure active/passive health checks, `HttpRequest` & `HttpClient` settings |\n| | Routes | Manage routes with transforms, metadata (`Waf:Enabled` per-route), drag-and-drop priority ordering, `CorsPolicy` config |\n| **Monitoring** | Statistics | Request volume trends, P50/P90/P99 latency percentiles, HTTP status distribution pie chart, top routes ranking |\n| | Logs | Real-time log stream (WebSocket) + history (TraceID-paired request/response merged view), filter by route/status/TraceID, sensitive data sanitization, sampling & errors-only mode |\n| | Circuits | Per-cluster/destination circuit breaker status: Closed (green) / Open (red) / HalfOpen (yellow), consecutive failures vs threshold, recovery countdown, one-click reset |\n| | Notifications | Manage webhook channels (DingTalk bot / generic HTTP), configure event rules (per-type toggle + cooldown), view notification history |\n| | Health Check | Cluster-level health overview table, drill-down per destination showing real-time health status, last check time & result |\n| **Security** | WAF Firewall | IP whitelist/blacklist (exact IP / CIDR / wildcard), SQL injection, XSS, path traversal detection, request size limits, security response headers |\n| | Policies | Traffic policies: create, edit, enable, disable, reorder — supports retry, timeout, rate limit, request transform |\n| | Plugins | View all registered `IGatewayPlugin` plugins, plugin metadata & load order |\n| **System** | Config History | Auto-snapshot before every change, view full content of any snapshot, one-click rollback to any version |\n| | Audit Log | Full audit trail: operation type, target, operator, before/after JSON diff, precise timestamp |\n| | Deployment | Current deployment mode (AllInOne/Split/ProxyOnly/DashboardOnly), listening endpoints (name/address/port/role/public status), health check endpoints & security warnings |\n| | Settings | Auth mode switching, JWT key config, AI config, log settings (sampling rate/sanitize list/body length limit), one-click database download, language switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF blocks are recorded in proxy logs, viewable in the Logs page with route and time filtering.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n`YarpRequestCaptureMiddleware` captures request data before forwarding and response data on return:\n\n- **Full capture**: HTTP method, full URL, request/response headers, request/response body, elapsed time, TraceId\n- **Real-time push**: WebSocket endpoint pushes logs to Dashboard in real time — no manual refresh needed\n- **History with TraceID pairing**: `ProxyRequest` and `ProxyResponse` are auto-paired by TraceID and displayed as a merged row, showing the complete request → forward → response data flow\n- **Sanitization**: `LogHeaderBlacklist` masks sensitive headers (`Authorization`, `Cookie`, etc.), `LogJsonFieldSanitizeList` recursively masks JSON fields (`password`, `token`, etc.)\n- **Sampling & filtering**: `LogSamplingRate` (0.0–1.0) for rate-based sampling; `LogErrorsOnly` for 4xx/5xx only; combined filtering by route name, status code, TraceID\n- **Structured storage**: `YarpEventFormatter` → `StructuredLogService` → SQLite with batch write optimization\n\n### AI Assistant\n\nDashboard-embedded AI chat assistant, powered by OpenAI-compatible LLMs, enabling natural language gateway management:\n\n**Multi-provider support**: Works with OpenAI, DeepSeek, Qwen, and any OpenAI-compatible API — switch via `Provider` + `BaseUrl` + `ApiKey`\n\n**40 Function Calling tools**:\n- **Read tools (21)**: auto-executed — query routes, clusters, circuit breaker status, proxy logs, traffic stats, WAF config, policies, audit log, etc.\n- **Write tools (19)**: require user confirmation — create/delete routes, create/update clusters, reset circuit breakers, update WAF settings, rollback config, etc.\n\n**Read/write separation security model**:\n- Read operations execute automatically and return results — no manual intervention needed\n- Write operations are proposed by AI, rendered as confirmation cards in the frontend, and only executed after the user clicks \"Confirm\"\n- All write operations must be explicitly authorized by the user, ensuring operational safety\n\n**Streaming & multi-turn conversation**:\n- Direct streaming output (typewriter effect) when no tool call is needed; two-phase output (tool execution → streaming summary) when tools are involved\n- Conversation history persisted to storage, supporting continuous context-aware dialogue\n- AI response language auto-follows Dashboard UI language (Chinese / English)\n\n**Floating chat on all pages**: a floating button at the bottom-right corner of every Dashboard page — one click to open the chat panel without leaving the current page\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n### Two-Factor Authentication (2FA)\n\nTOTP-based 2FA for enhanced login security:\n\n- **TOTP helper**: `TotpHelper` generates Base32 secrets + `otpauth://` URIs compatible with Google Authenticator, Microsoft Authenticator, etc.\n- **Setup flow**: Settings page → generate secret → scan QR code → enter 6-digit code to verify binding\n- **Login flow**: when 2FA enabled, login returns `202` → login page shows verification code input → submit code to complete login\n- **Runtime persistence**: 2FA state saved to `twofactor-state.json`, survives restarts\n- **Per-user control**: enable/disable 2FA anytime from Settings page\n\n### Enterprise UI\n\n- **Login page**: glassmorphism card, brand gradient panel, animated bubble background, version + license footer\n- **Sidebar**: collapsible grouped menu (5 groups), auto-expand active group, brand header with gradient icon, user card with online indicator\n- **Responsive**: mobile-friendly sidebar overlay, adaptive layouts\n- **i18n**: Chinese / English runtime switch, 180+ i18n keys\n- **Performance**: WOFF2 fonts, Brotli compression, per-page module loading, lazy Monaco editor, stripped unused language packs\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"],\n\n \"AI\": {\n \"Enabled\": true,\n \"Provider\": \"openai\",\n \"ApiKey\": \"sk-...\",\n \"BaseUrl\": \"https://api.openai.com/v1\",\n \"ChatModel\": \"gpt-4o-mini\"\n }\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n### AI Options\n\nConfiguration under `Gateway:Dashboard:AI`:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | AI module master switch |\n| `Provider` | `\"openai\"` | Provider: `openai` / `deepseek` / `qwen` / `custom` |\n| `ApiKey` | — | LLM provider API key |\n| `BaseUrl` | `\"https://api.openai.com/v1\"` | OpenAI-compatible API endpoint |\n| `ChatModel` | `\"gpt-4o-mini\"` | Model used for interactive chat |\n| `AnalysisModel` | `\"gpt-4o-mini\"` | Model used for background analysis (can use a cheaper model) |\n| `MaxTokens` | `4096` | Max tokens per response |\n| `Temperature` | `0.3` | Sampling temperature (0.0 = deterministic, 1.0 = creative) |\n| `MaxConversationHistory` | `20` | Recent conversation turns to retain |\n| `EnableBackgroundAnalysis` | `false` | Enable periodic background log analysis |\n| `AnalysisInterval` | `01:00:00` | Background analysis interval |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"],\n\n \"AI\": {\n \"Enabled\": true,\n \"Provider\": \"deepseek\",\n \"ApiKey\": \"sk-...\",\n \"BaseUrl\": \"https://api.deepseek.com/v1\",\n \"ChatModel\": \"deepseek-chat\",\n \"AnalysisModel\": \"deepseek-chat\"\n }\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Storage.Abstractions.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Abstractions) |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite storage with SQLCipher encryption | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Storage.Sqlite.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Sqlite) |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Grpc.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Grpc) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n<div align=\"center\" style=\"display: flex; justify-content: center; align-items: center; gap: 20px; \n background: #f6f8fa; padding: 20px 30px; border-radius: 12px; \n border: 1px solid #e1e4e8; box-shadow: 0 2px 8px rgba(0,0,0,0.05);\">\n <img src=\"docs/wechat_qrcode.jpg\" alt=\"Official Account QR Code\" \n style=\"width: 140px; height: 140px; border-radius: 8px; border: 2px solid #d0d7de;\" />\n <div style=\"text-align: left;\">\n <h3 style=\"margin: 0 0 4px 0; font-size: 22px; font-weight: 600; color: #24292e;\">\n 递归不爆炸\n </h3>\n <p style=\"margin: 0; font-size: 16px; color: #586069; max-width: 280px;\">\n Scan to follow and get more great content\n </p>\n <!-- Optional: add a WeChat badge -->\n <p style=\"margin-top: 8px;\">\n <img src=\"https://img.shields.io/badge/WeChat-递归不爆炸-brightgreen?style=flat-square&logo=wechat\" alt=\"WeChat\" />\n </p>\n </div>\n</div>\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "6dbd8b1a9eae52201b9bc2c71a4a4fc5104e6e36": {"t": 1785763354.6540983, "n": 19908, "h": "b93f16cc04899d54", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · 2FA · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com\n\n> **Live Demo**: https://yarp-test.aneiang.com/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, logs, WAF, 2FA, notifications, health check, auditing | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities (8 repository interfaces) | ❌ |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite implementation with SQLCipher AES-256 encryption | via abstractions |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol (GatewayRegistry.proto) | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n ├── Aneiang.Yarp.Client\n ├── Aneiang.Yarp.Grpc\n └── Aneiang.Yarp.Storage.Abstractions\n └── Aneiang.Yarp.Storage.Sqlite\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters, traffic summary, quick stats |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations and health checks |\n| | Routes | Manage routes with transforms, metadata, ordering |\n| **Monitoring** | Statistics | Request volume, latency percentiles, status code distribution |\n| | Logs | Real-time proxy logs with filtering by route, status, TraceID |\n| | Circuits | Circuit breaker status per cluster/destination, one-click reset |\n| | Notifications | Alert rules, webhook channels, notification history |\n| | Health Check | Cluster health overview, unhealthy destinations drill-down |\n| **Security** | Security Events | WAF attack logs, auto-refresh, attack type stats, top IPs |\n| | WAF Firewall | IP whitelist/blacklist, SQL injection, XSS, path traversal detection |\n| | Policies | Gateway traffic policies (retry, timeout, rate limit, etc.) |\n| | Plugins | Registered middleware plugins (WAF, etc.) |\n| **System** | Config History | Auto-snapshot before changes, one-click rollback to any version |\n| | Audit Log | Full audit trail: who changed what, before/after JSON diff |\n| | Settings | Auth config, logging config, database download, i18n switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF events are recorded with source IP, attack type, matched pattern, and timestamp — viewable in the Security Events dashboard.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n- Capture full request/response: method, URL, status, headers, body, elapsed time, TraceId\n- **Sanitization**: auto-mask sensitive headers (`Authorization`, `Cookie`, etc.), query params, and JSON fields\n- **Sampling**: rate-based sampling for production traffic\n- **Filtering**: by route, status code (4xx/5xx), TraceID\n- WebSocket real-time log push\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n### Two-Factor Authentication (2FA)\n\nTOTP-based 2FA for enhanced login security:\n\n- **TOTP helper**: `TotpHelper` generates Base32 secrets + `otpauth://` URIs compatible with Google Authenticator, Microsoft Authenticator, etc.\n- **Setup flow**: Settings page → generate secret → scan QR code → enter 6-digit code to verify binding\n- **Login flow**: when 2FA enabled, login returns `202` → login page shows verification code input → submit code to complete login\n- **Runtime persistence**: 2FA state saved to `twofactor-state.json`, survives restarts\n- **Per-user control**: enable/disable 2FA anytime from Settings page\n\n### Enterprise UI\n\n- **Login page**: glassmorphism card, brand gradient panel, animated bubble background, version + license footer\n- **Sidebar**: collapsible grouped menu (5 groups), auto-expand active group, brand header with gradient icon, user card with online indicator\n- **Responsive**: mobile-friendly sidebar overlay, adaptive layouts\n- **i18n**: Chinese / English runtime switch, 180+ i18n keys\n- **Performance**: WOFF2 fonts, Brotli compression, per-page module loading, lazy Monaco editor, stripped unused language packs\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Storage.Abstractions.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Abstractions) |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite storage with SQLCipher encryption | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Storage.Sqlite.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Sqlite) |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Grpc.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Grpc) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n<div align=\"center\" style=\"display: flex; justify-content: center; align-items: center; gap: 20px; \n background: #f6f8fa; padding: 20px 30px; border-radius: 12px; \n border: 1px solid #e1e4e8; box-shadow: 0 2px 8px rgba(0,0,0,0.05);\">\n <img src=\"docs/wechat_qrcode.jpg\" alt=\"Official Account QR Code\" \n style=\"width: 140px; height: 140px; border-radius: 8px; border: 2px solid #d0d7de;\" />\n <div style=\"text-align: left;\">\n <h3 style=\"margin: 0 0 4px 0; font-size: 22px; font-weight: 600; color: #24292e;\">\n 递归不爆炸\n </h3>\n <p style=\"margin: 0; font-size: 16px; color: #586069; max-width: 280px;\">\n Scan to follow and get more great content\n </p>\n <!-- Optional: add a WeChat badge -->\n <p style=\"margin-top: 8px;\">\n <img src=\"https://img.shields.io/badge/WeChat-递归不爆炸-brightgreen?style=flat-square&logo=wechat\" alt=\"WeChat\" />\n </p>\n </div>\n</div>\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "91de756fede71f5359e461670ad961094eccd00b": {"t": 1785763360.6598186, "n": 18996, "h": "2328a0654b7771f2", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · 2FA · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com\n\n> **Live Demo**: https://yarp-test.aneiang.com/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, logs, WAF, 2FA, notifications, health check, auditing | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities (8 repository interfaces) | ❌ |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite implementation with SQLCipher AES-256 encryption | via abstractions |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol (GatewayRegistry.proto) | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n ├── Aneiang.Yarp.Client\n ├── Aneiang.Yarp.Grpc\n └── Aneiang.Yarp.Storage.Abstractions\n └── Aneiang.Yarp.Storage.Sqlite\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters, traffic summary, quick stats |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations and health checks |\n| | Routes | Manage routes with transforms, metadata, ordering |\n| **Monitoring** | Statistics | Request volume, latency percentiles, status code distribution |\n| | Logs | Real-time proxy logs with filtering by route, status, TraceID |\n| | Circuits | Circuit breaker status per cluster/destination, one-click reset |\n| | Notifications | Alert rules, webhook channels, notification history |\n| | Health Check | Cluster health overview, unhealthy destinations drill-down |\n| **Security** | Security Events | WAF attack logs, auto-refresh, attack type stats, top IPs |\n| | WAF Firewall | IP whitelist/blacklist, SQL injection, XSS, path traversal detection |\n| | Policies | Gateway traffic policies (retry, timeout, rate limit, etc.) |\n| | Plugins | Registered middleware plugins (WAF, etc.) |\n| **System** | Config History | Auto-snapshot before changes, one-click rollback to any version |\n| | Audit Log | Full audit trail: who changed what, before/after JSON diff |\n| | Settings | Auth config, logging config, database download, i18n switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF events are recorded with source IP, attack type, matched pattern, and timestamp — viewable in the Security Events dashboard.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n- Capture full request/response: method, URL, status, headers, body, elapsed time, TraceId\n- **Sanitization**: auto-mask sensitive headers (`Authorization`, `Cookie`, etc.), query params, and JSON fields\n- **Sampling**: rate-based sampling for production traffic\n- **Filtering**: by route, status code (4xx/5xx), TraceID\n- WebSocket real-time log push\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n### Two-Factor Authentication (2FA)\n\nTOTP-based 2FA for enhanced login security:\n\n- **TOTP helper**: `TotpHelper` generates Base32 secrets + `otpauth://` URIs compatible with Google Authenticator, Microsoft Authenticator, etc.\n- **Setup flow**: Settings page → generate secret → scan QR code → enter 6-digit code to verify binding\n- **Login flow**: when 2FA enabled, login returns `202` → login page shows verification code input → submit code to complete login\n- **Runtime persistence**: 2FA state saved to `twofactor-state.json`, survives restarts\n- **Per-user control**: enable/disable 2FA anytime from Settings page\n\n### Enterprise UI\n\n- **Login page**: glassmorphism card, brand gradient panel, animated bubble background, version + license footer\n- **Sidebar**: collapsible grouped menu (5 groups), auto-expand active group, brand header with gradient icon, user card with online indicator\n- **Responsive**: mobile-friendly sidebar overlay, adaptive layouts\n- **i18n**: Chinese / English runtime switch, 180+ i18n keys\n- **Performance**: WOFF2 fonts, Brotli compression, per-page module loading, lazy Monaco editor, stripped unused language packs\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Storage.Abstractions.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Abstractions) |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite storage with SQLCipher encryption | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Storage.Sqlite.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Sqlite) |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Grpc.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Grpc) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "3e9ab49f4fd06a55c6bd6fcc403fa44162b528ac": {"t": 1785763363.1975992, "n": 17710, "h": "70b03cefd82abef6", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com\n\n> **Live Demo**: https://yarp-test.aneiang.com/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, logs, WAF, notifications, health check, auditing | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n └── Aneiang.Yarp.Client\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters, traffic summary, quick stats |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations and health checks |\n| | Routes | Manage routes with transforms, metadata, ordering |\n| **Monitoring** | Statistics | Request volume, latency percentiles, status code distribution |\n| | Logs | Real-time proxy logs with filtering by route, status, TraceID |\n| | Circuits | Circuit breaker status per cluster/destination, one-click reset |\n| | Notifications | Alert rules, webhook channels, notification history |\n| | Health Check | Cluster health overview, unhealthy destinations drill-down |\n| **Security** | Security Events | WAF attack logs, auto-refresh, attack type stats, top IPs |\n| | WAF Firewall | IP whitelist/blacklist, SQL injection, XSS, path traversal detection |\n| | Policies | Gateway traffic policies (retry, timeout, rate limit, etc.) |\n| | Plugins | Registered middleware plugins (WAF, etc.) |\n| **System** | Config History | Auto-snapshot before changes, one-click rollback to any version |\n| | Audit Log | Full audit trail: who changed what, before/after JSON diff |\n| | Settings | Auth config, logging config, database download, i18n switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF events are recorded with source IP, attack type, matched pattern, and timestamp — viewable in the Security Events dashboard.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n- Capture full request/response: method, URL, status, headers, body, elapsed time, TraceId\n- **Sanitization**: auto-mask sensitive headers (`Authorization`, `Cookie`, etc.), query params, and JSON fields\n- **Sampling**: rate-based sampling for production traffic\n- **Filtering**: by route, status code (4xx/5xx), TraceID\n- WebSocket real-time log push\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n<div align=\"center\" style=\"display: flex; justify-content: center; align-items: center; gap: 20px; \n background: #f6f8fa; padding: 20px 30px; border-radius: 12px; \n border: 1px solid #e1e4e8; box-shadow: 0 2px 8px rgba(0,0,0,0.05);\">\n <img src=\"docs/wechat_qrcode.jpg\" alt=\"Official Account QR Code\" \n style=\"width: 140px; height: 140px; border-radius: 8px; border: 2px solid #d0d7de;\" />\n <div style=\"text-align: left;\">\n <h3 style=\"margin: 0 0 4px 0; font-size: 22px; font-weight: 600; color: #24292e;\">\n 递归不爆炸\n </h3>\n <p style=\"margin: 0; font-size: 16px; color: #586069; max-width: 280px;\">\n Scan to follow and get more great content\n </p>\n <!-- Optional: add a WeChat badge -->\n <p style=\"margin-top: 8px;\">\n <img src=\"https://img.shields.io/badge/WeChat-递归不爆炸-brightgreen?style=flat-square&logo=wechat\" alt=\"WeChat\" />\n </p>\n </div>\n</div>\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "e7e3a15bcc97d7b2fc6c17e95e47b459a1132e88": {"t": 1785763368.7133596, "n": 17753, "h": "8319a13746e87045", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com\n\n> **Live Demo**: https://yarp-test.aneiang.com/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, logs, WAF, notifications, health check, auditing | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n └── Aneiang.Yarp.Client\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters, traffic summary, quick stats |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations and health checks |\n| | Routes | Manage routes with transforms, metadata, ordering |\n| **Monitoring** | Statistics | Request volume, latency percentiles, status code distribution |\n| | Logs | Real-time proxy logs with filtering by route, status, TraceID |\n| | Circuits | Circuit breaker status per cluster/destination, one-click reset |\n| | Notifications | Alert rules, webhook channels, notification history |\n| | Health Check | Cluster health overview, unhealthy destinations drill-down |\n| **Security** | Security Events | WAF attack logs, auto-refresh, attack type stats, top IPs |\n| | WAF Firewall | IP whitelist/blacklist, SQL injection, XSS, path traversal detection |\n| | Policies | Gateway traffic policies (retry, timeout, rate limit, etc.) |\n| | Plugins | Registered middleware plugins (WAF, etc.) |\n| **System** | Config History | Auto-snapshot before changes, one-click rollback to any version |\n| | Audit Log | Full audit trail: who changed what, before/after JSON diff |\n| | Settings | Auth config, logging config, database download, i18n switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF events are recorded with source IP, attack type, matched pattern, and timestamp — viewable in the Security Events dashboard.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n- Capture full request/response: method, URL, status, headers, body, elapsed time, TraceId\n- **Sanitization**: auto-mask sensitive headers (`Authorization`, `Cookie`, etc.), query params, and JSON fields\n- **Sampling**: rate-based sampling for production traffic\n- **Filtering**: by route, status code (4xx/5xx), TraceID\n- WebSocket real-time log push\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n<div align=\"center\" style=\"display: flex; justify-content: center; align-items: center; gap: 20px; \n background: #f6f8fa; padding: 20px 30px; border-radius: 12px; \n border: 1px solid #e1e4e8; box-shadow: 0 2px 8px rgba(0,0,0,0.05);\">\n <img src=\"https://your-image-link/qrcode.jpg\" alt=\"Official Account QR Code\" \n style=\"width: 140px; height: 140px; border-radius: 8px; border: 2px solid #d0d7de;\" />\n <div style=\"text-align: left;\">\n <h3 style=\"margin: 0 0 4px 0; font-size: 22px; font-weight: 600; color: #24292e;\">\n Your Official Account Name\n </h3>\n <p style=\"margin: 0; font-size: 16px; color: #586069; max-width: 280px;\">\n Scan to follow and get more great content\n </p>\n <!-- Optional: add a WeChat badge -->\n <p style=\"margin-top: 8px;\">\n <img src=\"https://img.shields.io/badge/WeChat-YourAccountName-brightgreen?style=flat-square&logo=wechat\" alt=\"WeChat\" />\n </p>\n </div>\n</div>\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "53f31b484fd867181d57d93d321caf7d9bbbbfff": {"t": 1785763371.8118143, "n": 16798, "h": "862e01e05966ad93", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com\n\n> **Live Demo**: https://yarp-test.aneiang.com/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, logs, WAF, notifications, health check, auditing | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n └── Aneiang.Yarp.Client\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters, traffic summary, quick stats |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations and health checks |\n| | Routes | Manage routes with transforms, metadata, ordering |\n| **Monitoring** | Statistics | Request volume, latency percentiles, status code distribution |\n| | Logs | Real-time proxy logs with filtering by route, status, TraceID |\n| | Circuits | Circuit breaker status per cluster/destination, one-click reset |\n| | Notifications | Alert rules, webhook channels, notification history |\n| | Health Check | Cluster health overview, unhealthy destinations drill-down |\n| **Security** | Security Events | WAF attack logs, auto-refresh, attack type stats, top IPs |\n| | WAF Firewall | IP whitelist/blacklist, SQL injection, XSS, path traversal detection |\n| | Policies | Gateway traffic policies (retry, timeout, rate limit, etc.) |\n| | Plugins | Registered middleware plugins (WAF, etc.) |\n| **System** | Config History | Auto-snapshot before changes, one-click rollback to any version |\n| | Audit Log | Full audit trail: who changed what, before/after JSON diff |\n| | Settings | Auth config, logging config, database download, i18n switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF events are recorded with source IP, attack type, matched pattern, and timestamp — viewable in the Security Events dashboard.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n- Capture full request/response: method, URL, status, headers, body, elapsed time, TraceId\n- **Sanitization**: auto-mask sensitive headers (`Authorization`, `Cookie`, etc.), query params, and JSON fields\n- **Sampling**: rate-based sampling for production traffic\n- **Filtering**: by route, status code (4xx/5xx), TraceID\n- WebSocket real-time log push\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "f9339155f244a349df1a206e47da55f27212e6c7": {"t": 1785763374.2129269, "n": 13403, "h": "ef0fdc7beb93461f", "c": "<div align=\"center\">\n\n**Give your YARP gateway a management dashboard — in just 3 lines of code**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nUsing YARP as your gateway, but still editing `appsettings.json` by hand to manage routes?\n\n**Aneiang.Yarp.Dashboard** lets you manage YARP right in the browser — visual CRUD, JSON editor, one-click rollback, real-time logs. Install and go.\n\n```csharp\n// Program.cs — that's it\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line\n```\n\nDemo:http://113.45.65.71:8930/apigateway \nadmin/demo123 \n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Get Started in 30 Seconds\n\n### 1. Install NuGet Packages\n\n```bash\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n### 2. Update Program.cs\n\n```csharp\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line, done\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← add this line, captures request logs\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 3. Open the Browser\n\nVisit `http://localhost:5000/apigateway` — the dashboard is already there.\n\n### Want a Login? (Optional)\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-password\"\n }\n }\n}\n```\n\nAdd the config, and the dashboard redirects to a login page. Default username: `admin`.\n\n---\n\n## What You Get\n\n| Feature | What It Does |\n|---------|-------------|\n| 📊 **Cluster Management** | Browse, create, edit, delete, rename clusters — or use the JSON editor |\n| 🛣️ **Route Management** | Full CRUD + JSON editor, supports standard YARP format |\n| 💾 **Config Import/Export** | One-click export of full YARP config, import with auto-validation |\n| ⏪ **Config Rollback** | Auto-snapshot on every change, one-click rollback if something breaks |\n| 📝 **Real-time Logs** | Full request/response capture, filter by route/status/TraceID |\n| 🔐 **Multi-mode Auth** | JWT login / API Key / custom delegate — works out of the box |\n| 🌐 **i18n** | Switch language at runtime, no restart needed |\n| 🛡️ **Log Sanitization** | Auto-mask Authorization, password, and other sensitive fields |\n| 📈 **Log Sampling** | Sample by rate in production to control log volume |\n\n**Design principle**: Zero intrusion — just 2 lines of code to enable, doesn't change your existing YARP config or proxy logic.\n\n---\n\n## Feature Details\n\n### 📊 Cluster & Route Management\n\nManage YARP clusters and routes right in the browser — no more hand-editing JSON config files:\n\n- **Form-based creation** — Fill in a few fields to create a route or cluster\n- **JSON editor** — Built-in code editor with syntax highlighting, live validation, auto-formatting\n- **Format compatibility** — Accepts both camelCase and PascalCase, paste official YARP config directly\n- **Smart association** — Creating a route whose target cluster doesn't exist? It auto-creates the cluster\n- **Safe deletion** — Optionally clean up orphaned clusters when deleting routes; deleting a cluster auto-updates references\n\n### 💾 Config Version Management\n\nBroke something? One-click rollback.\n\n- **Auto-snapshot** — Before every create/edit/delete, the system automatically saves a config snapshot\n- **Change history** — View timestamp, operator IP, and change content for each change\n- **One-click rollback** — Pick any historical version and roll back instantly\n- **Export** — Export in full YARP standard format, ready to paste into `appsettings.json`\n- **Import** — Import from JSON, auto-validate format, merge and persist\n\n### 📝 Real-time Request Logs\n\nSee what happens to every request passing through the gateway:\n\n- Request method, path, query parameters, headers\n- Response status code, headers\n- Request/response body (JSON auto-parsed)\n- Target cluster and route\n- Trace ID, request duration\n\n**Filtering**: by route ID / status code / Trace ID / time range\n\n**Security controls** (production-friendly):\n\n| Setting | One-liner |\n|---------|-----------|\n| `EnableProxyLogging` | Master switch — off stops the entire pipeline |\n| `EnableLogSampling` + `LogSamplingRate` | Sampling — 0.1 = only log 10% |\n| `LogErrorsOnly` | Only log 4xx/5xx |\n| `LogMaxBodyLength` | Truncate long bodies |\n| `LogRouteWhitelist` / `LogRouteBlacklist` | Filter by route |\n| `LogHeaderBlacklist` | `Authorization` → `***REDACTED***` |\n| `LogQueryBlacklist` | Mask sensitive query params |\n| `LogJsonFieldSanitizeList` | `password` in JSON → `***REDACTED***` |\n\n### 🔐 Authentication\n\nFour modes, pick what fits:\n\n| Mode | How It Works | Best For |\n|------|-------------|----------|\n| `None` | No protection | Local development |\n| `DefaultJwt` | Set a password, username is fixed as `admin` | Personal / small teams |\n| `CustomJwt` | Custom username + password | Enterprise |\n| `ApiKey` | Pass API Key via Header | API integrations |\n\nThere's also an `AuthorizeRequest` delegate you can use to plug in your own auth system (highest priority).\n\n---\n\n## Configuration Reference\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n---\n\n## API Endpoints\n\n### Dashboard — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n> Default prefix is `apigateway`, customizable via `RoutePrefix`.\n\n---\n\n## Advanced Usage\n\n<details>\n<summary><b>🔐 Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n // Return true = allow access\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>🛡️ Dashboard + Zero-Config Client Registration</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the password — zero config on the client side:\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard password\n\n// Client Program.cs — no auth config needed at all\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>🔧 Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>📋 Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## About Aneiang.Yarp\n\nThe dashboard depends on [Aneiang.Yarp](https://www.nuget.org/packages/Aneiang.Yarp) core library, which provides:\n\n| Capability | Description |\n|------------|-------------|\n| 🚀 **Dynamic Routing API** | `/api/gateway` for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration Client** | `AddAneiangYarpClient()` — one line, registers on startup, unregisters on shutdown |\n| 👥 **Instance Isolation** | Multi-developer debugging with automatic namespace isolation |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost → LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | `enableRegistration: false` removes registration endpoints entirely |\n\nThe core library works independently — no dashboard required.\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp.Dashboard** | Dashboard | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "ac198401790c61c9430730f8158383ebde5ebc35": {"t": 1785763394.4645746, "n": 16806, "h": "8fcdd6c41df355b6", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com/aneiang\n\n> **Live Demo**: https://yarp-test.aneiang.com/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, logs, WAF, notifications, health check, auditing | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n └── Aneiang.Yarp.Client\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters, traffic summary, quick stats |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations and health checks |\n| | Routes | Manage routes with transforms, metadata, ordering |\n| **Monitoring** | Statistics | Request volume, latency percentiles, status code distribution |\n| | Logs | Real-time proxy logs with filtering by route, status, TraceID |\n| | Circuits | Circuit breaker status per cluster/destination, one-click reset |\n| | Notifications | Alert rules, webhook channels, notification history |\n| | Health Check | Cluster health overview, unhealthy destinations drill-down |\n| **Security** | Security Events | WAF attack logs, auto-refresh, attack type stats, top IPs |\n| | WAF Firewall | IP whitelist/blacklist, SQL injection, XSS, path traversal detection |\n| | Policies | Gateway traffic policies (retry, timeout, rate limit, etc.) |\n| | Plugins | Registered middleware plugins (WAF, etc.) |\n| **System** | Config History | Auto-snapshot before changes, one-click rollback to any version |\n| | Audit Log | Full audit trail: who changed what, before/after JSON diff |\n| | Settings | Auth config, logging config, database download, i18n switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF events are recorded with source IP, attack type, matched pattern, and timestamp — viewable in the Security Events dashboard.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n- Capture full request/response: method, URL, status, headers, body, elapsed time, TraceId\n- **Sanitization**: auto-mask sensitive headers (`Authorization`, `Cookie`, etc.), query params, and JSON fields\n- **Sampling**: rate-based sampling for production traffic\n- **Filtering**: by route, status code (4xx/5xx), TraceID\n- WebSocket real-time log push\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "26d3c650d9548243c9381b76179a62810de768a7": {"t": 1785763395.9915924, "n": 16801, "h": "77bbe3168935d985", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com/aneiang\n\n> **Live Demo**: http://47.97.229.51:5200/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, logs, WAF, notifications, health check, auditing | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n └── Aneiang.Yarp.Client\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters, traffic summary, quick stats |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations and health checks |\n| | Routes | Manage routes with transforms, metadata, ordering |\n| **Monitoring** | Statistics | Request volume, latency percentiles, status code distribution |\n| | Logs | Real-time proxy logs with filtering by route, status, TraceID |\n| | Circuits | Circuit breaker status per cluster/destination, one-click reset |\n| | Notifications | Alert rules, webhook channels, notification history |\n| | Health Check | Cluster health overview, unhealthy destinations drill-down |\n| **Security** | Security Events | WAF attack logs, auto-refresh, attack type stats, top IPs |\n| | WAF Firewall | IP whitelist/blacklist, SQL injection, XSS, path traversal detection |\n| | Policies | Gateway traffic policies (retry, timeout, rate limit, etc.) |\n| | Plugins | Registered middleware plugins (WAF, etc.) |\n| **System** | Config History | Auto-snapshot before changes, one-click rollback to any version |\n| | Audit Log | Full audit trail: who changed what, before/after JSON diff |\n| | Settings | Auth config, logging config, database download, i18n switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF events are recorded with source IP, attack type, matched pattern, and timestamp — viewable in the Security Events dashboard.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n- Capture full request/response: method, URL, status, headers, body, elapsed time, TraceId\n- **Sanitization**: auto-mask sensitive headers (`Authorization`, `Cookie`, etc.), query params, and JSON fields\n- **Sampling**: rate-based sampling for production traffic\n- **Filtering**: by route, status code (4xx/5xx), TraceID\n- WebSocket real-time log push\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "688dfe37464cd43d31b2870b1a76dc7d7a089a60": {"t": 1785763423.3983498, "n": 17217, "h": "953936508c339b1d", "c": "<div align=\"center\">\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Docs**:https://yarp.aneiang.com/aneiang\n\n> **Live Demo**: http://47.97.229.51:5200/aneiang &nbsp;·&nbsp; `admin` / `demo123`\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, logs, WAF, notifications, health check, auditing | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n └── Aneiang.Yarp.Client\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<p align=\"center\">\n <img src=\"docs/overview.png\" alt=\"Dashboard Overview\" width=\"800\"/>\n</p>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters, traffic summary, quick stats |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations and health checks |\n| | Routes | Manage routes with transforms, metadata, ordering |\n| **Monitoring** | Statistics | Request volume, latency percentiles, status code distribution |\n| | Logs | Real-time proxy logs with filtering by route, status, TraceID |\n| | Circuits | Circuit breaker status per cluster/destination, one-click reset |\n| | Notifications | Alert rules, webhook channels, notification history |\n| | Health Check | Cluster health overview, unhealthy destinations drill-down |\n| **Security** | Security Events | WAF attack logs, auto-refresh, attack type stats, top IPs |\n| | WAF Firewall | IP whitelist/blacklist, SQL injection, XSS, path traversal detection |\n| | Policies | Gateway traffic policies (retry, timeout, rate limit, etc.) |\n| | Plugins | Registered middleware plugins (WAF, etc.) |\n| **System** | Config History | Auto-snapshot before changes, one-click rollback to any version |\n| | Audit Log | Full audit trail: who changed what, before/after JSON diff |\n| | Settings | Auth config, logging config, database download, i18n switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n```http\nPOST /api/gateway/register-route Register or update route + cluster\nDELETE /api/gateway/{routeName} Delete route (supports IP isolation)\nGET /api/gateway/routes Query all routes\nGET /api/gateway/dynamic-config Query config with metadata\nPOST /api/gateway/clusters Create cluster\nPUT /api/gateway/clusters/{id} Update cluster\nDELETE /api/gateway/clusters/{id} Delete cluster\nGET /api/gateway/ping Health check\n```\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF events are recorded with source IP, attack type, matched pattern, and timestamp — viewable in the Security Events dashboard.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n- Capture full request/response: method, URL, status, headers, body, elapsed time, TraceId\n- **Sanitization**: auto-mask sensitive headers (`Authorization`, `Cookie`, etc.), query params, and JSON fields\n- **Sampling**: rate-based sampling for production traffic\n- **Filtering**: by route, status code (4xx/5xx), TraceID\n- WebSocket real-time log push\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "e46d880996266bbf0209d3cb8551a2b84bddbc7f": {"t": 1785763429.002683, "n": 17648, "h": "ec518f00ff3d379e", "c": "<div align=\"center\">\n\n**Aneiang.Yarp — Full-featured API Gateway powered by YARP**\n\nDashboard · Dynamic Routing · WAF · Notifications · IP Isolation · Auto-Registration\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\n**Aneiang.Yarp** is an enhanced, production-ready API gateway built on [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It adds everything YARP leaves for you to build: a visual management dashboard, WAF firewall, notification alerts, health monitoring, circuit breaker views, client auto-registration, IP-based load balancing — all through three NuGet packages.\n\n> **Live Demo**: http://113.45.65.71:8930/apigateway &nbsp;·&nbsp; `admin` / `demo123`\n\n---\n\n## Packages\n\n| Package | Purpose | Depends on YARP |\n|:--------|:--------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based LB | ✅ |\n| **Aneiang.Yarp.Dashboard** | Web admin UI: full CRUD, logs, WAF, notifications, health check, auditing | via core |\n| **Aneiang.Yarp.Client** | Client SDK: one-liner auto-register / unregister on startup and shutdown | ❌ |\n\n```\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n └── Aneiang.Yarp.Client\n\nClient microservice → reference Aneiang.Yarp.Client only (zero YARP SDK)\nGateway project → reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\n---\n\n## Dashboard\n\nA full-featured web admin panel — **two lines of code** to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n<table>\n <tr>\n <td align=\"center\"><b>Clusters</b></td>\n <td align=\"center\"><b>Routes</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster list\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route list\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"JSON editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n### All 15 Dashboard Pages\n\n| Group | Page | Description |\n|:------|:-----|:------------|\n| **Overview** | Overview | Active routes & clusters, traffic summary, quick stats |\n| **Gateway** | Clusters | Create, edit, delete clusters with destinations and health checks |\n| | Routes | Manage routes with transforms, metadata, ordering |\n| **Monitoring** | Statistics | Request volume, latency percentiles, status code distribution |\n| | Logs | Real-time proxy logs with filtering by route, status, TraceID |\n| | Circuits | Circuit breaker status per cluster/destination, one-click reset |\n| | Notifications | Alert rules, webhook channels, notification history |\n| | Health Check | Cluster health overview, unhealthy destinations drill-down |\n| **Security** | Security Events | WAF attack logs, auto-refresh, attack type stats, top IPs |\n| | WAF Firewall | IP whitelist/blacklist, SQL injection, XSS, path traversal detection |\n| | Policies | Gateway traffic policies (retry, timeout, rate limit, etc.) |\n| | Plugins | Registered middleware plugins (WAF, etc.) |\n| **System** | Config History | Auto-snapshot before changes, one-click rollback to any version |\n| | Audit Log | Full audit trail: who changed what, before/after JSON diff |\n| | Settings | Auth config, logging config, database download, i18n switch |\n\n---\n\n## Core Features\n\n### Dynamic Routing\n\nYARP requires `appsettings.json` + restart for route changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual-source config**: static (`appsettings.json`) + dynamic (API / Dashboard), merged in YARP's `InMemoryConfigProvider`\n- **Auto-persistence**: dynamic changes auto-save to `gateway-dynamic.json`, survive restarts\n- **Source tracking**: every route & cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n- **Thread-safe**: all operations protected by `SemaphoreSlim(1,1)` — async-friendly concurrency\n\n```http\nPOST /api/gateway/register-route Register or update route + cluster\nDELETE /api/gateway/{routeName} Delete route (supports IP isolation)\nGET /api/gateway/routes Query all routes\nGET /api/gateway/dynamic-config Query config with metadata\nPOST /api/gateway/clusters Create cluster\nPUT /api/gateway/clusters/{id} Update cluster\nDELETE /api/gateway/clusters/{id} Delete cluster\nGET /api/gateway/ping Health check\n```\n\n### WAF Firewall\n\nProduction-grade web application firewall built into the gateway — no external service needed.\n\n| Protection | Details |\n|:-----------|:--------|\n| **IP Blacklist / Whitelist** | Exact IP, CIDR (`192.168.1.0/24`), wildcard. Whitelist takes priority. Regex-cached for performance. |\n| **SQL Injection** | Two precompiled regex patterns (keywords + injection values) with 5ms ReDoS timeout |\n| **XSS** | Detects `<script>`, `javascript:`, event handler injection |\n| **Path Traversal** | `../` variants, URL-encoded attacks, double-encoding |\n| **Request Limits** | Max body size (default 10MB), max header count, max header size, URI length (4096) |\n| **Security Headers** | Auto-inject `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, CSP |\n| **Per-Route Control** | Enable/disable WAF per route via YARP metadata `Waf:Enabled` |\n\nAll WAF events are recorded with source IP, attack type, matched pattern, and timestamp — viewable in the Security Events dashboard.\n\n### Notification & Alerting\n\nMulti-channel alerting for production incidents:\n\n- **Webhook channels**: configure endpoints, timeout, retry counts\n- **Event-based rules**: per-type toggle — circuit breaker open, retry exhausted, WAF block, proxy error, rate limit exceeded, config changes\n- **Alert cooldown**: prevent duplicate alerts within cooldown window\n- **Queue-based dispatch**: `ConfigChangeEventDispatcher` (BackgroundService) drains `ConcurrentQueue<PendingNotification>` at 200ms intervals\n- **Notification history**: persisted to database, filterable by event type\n\n### Health Check Monitoring\n\n- Active & passive health check configuration per cluster\n- Real-time health score and unhealthy destination drill-down\n- `DefaultHealthCheckService`: auto-applys passive health checks at startup\n\n### Circuit Breaker Panel\n\n- Live status per cluster/destination: **Closed · Open · HalfOpen**\n- Shows consecutive failures, threshold, recovery timeout countdown, opened-at time\n- One-click reset all circuit breakers\n\n### Client Auto-Registration\n\nMicroservices auto-register on startup, auto-unregister on shutdown — **truly one line**:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required:\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RouteName` | Entry assembly name | e.g. `MyAuthService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` → LAN IP |\n| `Order` | `50` | Route priority |\n\n**Protocols**: HTTP REST API + gRPC (auto port = HTTP port + 1, Kestrel HTTP/2 h2c)\n\n**Auth**: Bearer token, Basic auth, API key\n\n**Retry**: exponential backoff on startup registration failure (2s → 4s → 8s → 16s → 30s, up to 5 attempts)\n\n> `Aneiang.Yarp.Client` has **zero YARP dependency** — only `Microsoft.AspNetCore.App`. Clean dependency footprint for microservices.\n\n### IP Isolation Load Balancing\n\nUnique feature for **multi-developer debugging**. Each developer runs the same service locally — the gateway routes by client IP. The frontend is completely unaware.\n\n```\nDev A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDev B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther users → POST /api/user → Gateway → First available\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Configuration Management\n\n- **Import / Export**: one-click export full YARP config. Import with automatic validation, snapshot, and persistence\n- **Snapshot & Rollback**: auto-snapshot before every config change, rollback to any point in one click\n- **Audit Log**: full trail — operation type, target, operator, before/after JSON diff, timestamp\n- **SQLCipher Encryption**: database files encrypted at rest with AES-256 (`Data Source=gateway-store.db;Password=xxx`)\n- **Database Download**: one-click download SQLite file for local inspection with DB tools\n\n### Request Logging\n\n- Capture full request/response: method, URL, status, headers, body, elapsed time, TraceId\n- **Sanitization**: auto-mask sensitive headers (`Authorization`, `Cookie`, etc.), query params, and JSON fields\n- **Sampling**: rate-based sampling for production traffic\n- **Filtering**: by route, status code (4xx/5xx), TraceID\n- WebSocket real-time log push\n\n### Authentication\n\n| Mode | Description |\n|:-----|:------------|\n| `None` | No auth (default) |\n| `DefaultJwt` | Built-in JWT login (`admin` + password), secret auto-generated |\n| `CustomJwt` | Custom username / password / secret |\n| `ApiKey` | Via `X-Api-Key` header |\n| Custom delegate | `AuthorizeRequest` — plug into your own auth system |\n\nDashboard API auth for client auto-registration: auto-infers credentials from Dashboard JWT config. No duplicate setup.\n\n---\n\n## Quick Start\n\n### 1. Create the Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy\napp.MapControllers();\napp.Run();\n```\n\nDashboard available at `/apigateway`.\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarpClient(); // ← auto-registers on startup\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThe microservice registers itself on startup and unregisters on shutdown. No extra code.\n\n---\n\n## Configuration Reference\n\nAll settings under `Gateway:*` — **everything has a default**, zero-config startup works.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n \"EnableProxyLogging\": true,\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtSecret\": \"...\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-password\"\n }\n }\n }\n}\n```\n\n### Dashboard Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `RoutePrefix` | `\"apigateway\"` | URL prefix for dashboard pages |\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `Locale` | `\"en-US\"` | Default language (`en-US` / `zh-CN`), switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | — | JWT login password |\n| `JwtUsername` | — | Username (CustomJwt only; DefaultJwt fixed `admin`) |\n| `JwtSecret` | auto-gen | JWT signing key; regenerate on restart if not configured |\n| `ApiKey` | — | API Key value for `ApiKey` auth mode |\n| `EnableLogSampling` | `false` | Enable rate-based log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Log only 4xx/5xx responses |\n| `LogMaxBodyLength` | `8192` | Max captured body length in bytes |\n| `LogHeaderBlacklist` | — | Headers to sanitize in logs |\n| `LogJsonFieldSanitizeList` | — | JSON fields to sanitize in logs |\n\n### Storage Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `ConnectionString` | `Data Source=gateway-store.db` | SQLite connection string. Add `Password=xxx` for SQLCipher AES-256 encryption. |\n\n### WAF Options\n\n| Option | Default | Description |\n|:-------|:--------|:------------|\n| `Enabled` | `false` | WAF master switch |\n| `IpWhitelist` | — | IP whitelist (per-line: IP or CIDR) |\n| `IpBlacklist` | — | IP blacklist (per-line: IP or CIDR) |\n| `MaxRequestBodySize` | `10485760` | Max request body size in bytes |\n| `MaxHeaderCount` | `100` | Max number of request headers |\n| `MaxHeaderSize` | `8192` | Max single header size in bytes |\n| `EnableSqlInjectionDetection` | `true` | Detect SQL injection patterns |\n| `EnableXssDetection` | `true` | Detect XSS patterns |\n| `EnablePathTraversalDetection` | `true` | Detect path traversal attempts |\n| `EnableIpCheck` | `true` | Enable IP whitelist/blacklist enforcement |\n| `EnableRequestSizeValidation` | `true` | Enforce request body size limit |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard JWT auth configured, gateway management API auth auto-reads it:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and auto-skips dashboard requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n },\n \"Storage\": {\n \"Sqlite\": {\n \"ConnectionString\": \"Data Source=gateway-store.db;Password=your-production-password\"\n }\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start gateway (with Dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway` · Login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | NuGet |\n|:--------|:------------|:-----:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** &nbsp;·&nbsp; **YARP 2.3.0**\n\n---\n\n## License\n\n[MIT](LICENSE)\n\n---\n\n<div align=\"center\">\n\nIf this project helps you, [⭐ Star it](https://github.com/aneiang/Aneiang.Yarp) to support development\n\n</div>\n"}, "0b155b275ca1404194b12fc071bbc577217645be": {"t": 1785763433.6528687, "n": 14530, "h": "f5e19a64a8b44b9a", "c": "<div align=\"center\">\n\n**A full-featured YARP gateway enhancement — dashboard, dynamic routing, auto-registration, and IP isolation**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nAneiang.Yarp is an enhanced API gateway solution built on top of [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It provides three NuGet packages that work together to solve real-world gateway challenges:\n\n| Package | Purpose | YARP Dependency |\n|---------|---------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based load balancing | Yes |\n| **Aneiang.Yarp.Client** | Client auto-registration: one-liner to register/unregister with the gateway | No |\n| **Aneiang.Yarp.Dashboard** | Web management UI: cluster/route CRUD, config import/export, snapshot rollback, real-time logs | Via core |\n\n```\nDependencies:\n\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n └── Aneiang.Yarp.Client\n\nClient services → Reference Aneiang.Yarp.Client only (no YARP SDK pulled in)\nGateway services → Reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\nDemo: http://113.45.65.71:8930/apigateway &nbsp;&nbsp; admin/demo123\n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Features\n\n### Dynamic Route Management\n\nYARP relies on `appsettings.json` for route configuration, requiring restarts for changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual config sources**: Static config from `appsettings.json` + dynamic config from API calls, unified in YARP's `InMemoryConfigProvider`\n- **Config persistence**: All dynamic changes auto-persist to `gateway-dynamic.json`, surviving restarts\n- **Thread-safe**: All operations protected by `lock` for concurrent safety\n- **Source tracking**: Every route/cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n\nRESTful API at `api/gateway`:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| POST | `/api/gateway/register-route` | Register or update a route + cluster |\n| DELETE | `/api/gateway/{routeName}?clientIp=` | Delete a route (supports IP isolation) |\n| GET | `/api/gateway/routes` | Query all routes |\n| GET | `/api/gateway/dynamic-config` | Query dynamic config with metadata |\n| PUT | `/api/gateway/routes/{routeId}` | Update a route |\n| POST | `/api/gateway/clusters` | Create a cluster |\n| PUT | `/api/gateway/clusters/{clusterId}` | Update a cluster |\n| DELETE | `/api/gateway/clusters/{clusterId}` | Delete a cluster |\n| GET | `/api/gateway/ping` | Health check |\n\n### Client Auto-Registration\n\nMicroservices can auto-register with the gateway on startup and auto-unregister on shutdown — one line of code:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required. Everything else is auto-detected:\n\n| Config | Default | Description |\n|--------|---------|-------------|\n| `RouteName` | Entry assembly name | e.g. `MyService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` resolved to LAN IP |\n| `Order` | `50` | Route priority |\n\n> `Aneiang.Yarp.Client` has **no YARP SDK dependency** — it only depends on `Microsoft.AspNetCore.App`. Downstream services get a clean dependency footprint.\n\n### IP Isolation Load Balancing\n\nA unique feature for **multi-developer debugging**. Multiple developers run the same service locally, and the gateway routes requests by client IP — the frontend is completely unaware.\n\n```\nDeveloper A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDeveloper B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther requests → POST /api/user → Gateway → First available instance\n```\n\nEnable on the client side:\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: Custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Dashboard\n\nFull web management UI — 2 lines of code to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n| Feature | Description |\n|---------|-------------|\n| Cluster & Route CRUD | Form-based + JSON editor, supports YARP standard format |\n| Config Import/Export | One-click export/import in standard YARP format with validation |\n| Snapshot & Rollback | Auto-snapshot before every change, one-click rollback |\n| Real-time Logs | Request/response capture with route/status/TraceID filtering |\n| Log Sanitization | Auto-mask sensitive headers, query params, and JSON fields |\n| Log Sampling | Rate-based sampling for production traffic control |\n| i18n | Chinese / English, runtime switchable |\n| Multi-mode Auth | None / API Key / JWT (default & custom) / custom delegate |\n\n### API Authorization\n\nProtect the gateway management API (`GatewayConfigController`) with optional auth:\n\n```csharp\nbuilder.Services.AddGatewayApiAuth();\n```\n\n| Mode | Description |\n|------|-------------|\n| `BasicAuth` | HTTP Basic authentication |\n| `ApiKey` | Via `X-Api-Key` header |\n| `None` | No protection (default) |\n\n```json\n{\n \"Gateway\": {\n \"ApiAuth\": {\n \"Mode\": \"BasicAuth\",\n \"Username\": \"admin\",\n \"Password\": \"secure-password\"\n }\n }\n}\n```\n\nSmart inference: If `Gateway:ApiAuth` is not configured but `Gateway:Dashboard` has a JWT password, credentials are auto-derived (username `admin`, password = JWT password).\n\n---\n\n## Quick Start\n\n### 1. Create a Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // includes MapReverseProxy internally\napp.MapControllers();\napp.Run();\n```\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddAneiangYarpClient();\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThat's it. The microservice registers itself on startup and unregisters on shutdown.\n\n---\n\n## Dashboard Configuration\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime (`en-US` / `zh-CN`) |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors (4xx/5xx) |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n### Dashboard Endpoints\n\n#### Pages — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n#### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the JWT password as the API auth credential:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting (includes MapReverseProxy internally)\napp.MapControllers();\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips the dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dependency) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web management UI with auth, logs, and config management | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "4c5b45807e6e7cae288c5eb94121c299971a3b00": {"t": 1785763438.2619572, "n": 14600, "h": "acf7cbd44b9708d9", "c": "<div align=\"center\">\n<img src=\"Logo.png\" alt=\"LOGO\" width=\"240\" style=\"border-radius: 15px;\"/>\n\n**A full-featured YARP gateway enhancement — dashboard, dynamic routing, auto-registration, and IP isolation**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nAneiang.Yarp is an enhanced API gateway solution built on top of [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It provides three NuGet packages that work together to solve real-world gateway challenges:\n\n| Package | Purpose | YARP Dependency |\n|---------|---------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based load balancing | Yes |\n| **Aneiang.Yarp.Client** | Client auto-registration: one-liner to register/unregister with the gateway | No |\n| **Aneiang.Yarp.Dashboard** | Web management UI: cluster/route CRUD, config import/export, snapshot rollback, real-time logs | Via core |\n\n```\nDependencies:\n\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n └── Aneiang.Yarp.Client\n\nClient services → Reference Aneiang.Yarp.Client only (no YARP SDK pulled in)\nGateway services → Reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\nDemo: http://113.45.65.71:8930/apigateway &nbsp;&nbsp; admin/demo123\n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Features\n\n### Dynamic Route Management\n\nYARP relies on `appsettings.json` for route configuration, requiring restarts for changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual config sources**: Static config from `appsettings.json` + dynamic config from API calls, unified in YARP's `InMemoryConfigProvider`\n- **Config persistence**: All dynamic changes auto-persist to `gateway-dynamic.json`, surviving restarts\n- **Thread-safe**: All operations protected by `lock` for concurrent safety\n- **Source tracking**: Every route/cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n\nRESTful API at `api/gateway`:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| POST | `/api/gateway/register-route` | Register or update a route + cluster |\n| DELETE | `/api/gateway/{routeName}?clientIp=` | Delete a route (supports IP isolation) |\n| GET | `/api/gateway/routes` | Query all routes |\n| GET | `/api/gateway/dynamic-config` | Query dynamic config with metadata |\n| PUT | `/api/gateway/routes/{routeId}` | Update a route |\n| POST | `/api/gateway/clusters` | Create a cluster |\n| PUT | `/api/gateway/clusters/{clusterId}` | Update a cluster |\n| DELETE | `/api/gateway/clusters/{clusterId}` | Delete a cluster |\n| GET | `/api/gateway/ping` | Health check |\n\n### Client Auto-Registration\n\nMicroservices can auto-register with the gateway on startup and auto-unregister on shutdown — one line of code:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required. Everything else is auto-detected:\n\n| Config | Default | Description |\n|--------|---------|-------------|\n| `RouteName` | Entry assembly name | e.g. `MyService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` resolved to LAN IP |\n| `Order` | `50` | Route priority |\n\n> `Aneiang.Yarp.Client` has **no YARP SDK dependency** — it only depends on `Microsoft.AspNetCore.App`. Downstream services get a clean dependency footprint.\n\n### IP Isolation Load Balancing\n\nA unique feature for **multi-developer debugging**. Multiple developers run the same service locally, and the gateway routes requests by client IP — the frontend is completely unaware.\n\n```\nDeveloper A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDeveloper B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther requests → POST /api/user → Gateway → First available instance\n```\n\nEnable on the client side:\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: Custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Dashboard\n\nFull web management UI — 2 lines of code to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n| Feature | Description |\n|---------|-------------|\n| Cluster & Route CRUD | Form-based + JSON editor, supports YARP standard format |\n| Config Import/Export | One-click export/import in standard YARP format with validation |\n| Snapshot & Rollback | Auto-snapshot before every change, one-click rollback |\n| Real-time Logs | Request/response capture with route/status/TraceID filtering |\n| Log Sanitization | Auto-mask sensitive headers, query params, and JSON fields |\n| Log Sampling | Rate-based sampling for production traffic control |\n| i18n | Chinese / English, runtime switchable |\n| Multi-mode Auth | None / API Key / JWT (default & custom) / custom delegate |\n\n### API Authorization\n\nProtect the gateway management API (`GatewayConfigController`) with optional auth:\n\n```csharp\nbuilder.Services.AddGatewayApiAuth();\n```\n\n| Mode | Description |\n|------|-------------|\n| `BasicAuth` | HTTP Basic authentication |\n| `ApiKey` | Via `X-Api-Key` header |\n| `None` | No protection (default) |\n\n```json\n{\n \"Gateway\": {\n \"ApiAuth\": {\n \"Mode\": \"BasicAuth\",\n \"Username\": \"admin\",\n \"Password\": \"secure-password\"\n }\n }\n}\n```\n\nSmart inference: If `Gateway:ApiAuth` is not configured but `Gateway:Dashboard` has a JWT password, credentials are auto-derived (username `admin`, password = JWT password).\n\n---\n\n## Quick Start\n\n### 1. Create a Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard();\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddAneiangYarpClient();\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThat's it. The microservice registers itself on startup and unregisters on shutdown.\n\n---\n\n## Dashboard Configuration\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime (`en-US` / `zh-CN`) |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors (4xx/5xx) |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n### Dashboard Endpoints\n\n#### Pages — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n#### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the JWT password as the API auth credential:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips the dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dependency) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web management UI with auth, logs, and config management | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "e8d72cb1574c137292f783afd2a60ce6260ccdf3": {"t": 1785763440.8392282, "n": 14526, "h": "54f610ed665b0de9", "c": "<div align=\"center\">\n\n**A full-featured YARP gateway enhancement — dashboard, dynamic routing, auto-registration, and IP isolation**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nAneiang.Yarp is an enhanced API gateway solution built on top of [Microsoft YARP](https://microsoft.github.io/reverse-proxy/) 2.3.0. It provides three NuGet packages that work together to solve real-world gateway challenges:\n\n| Package | Purpose | YARP Dependency |\n|---------|---------|:---:|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, config persistence, API auth, IP-based load balancing | Yes |\n| **Aneiang.Yarp.Client** | Client auto-registration: one-liner to register/unregister with the gateway | No |\n| **Aneiang.Yarp.Dashboard** | Web management UI: cluster/route CRUD, config import/export, snapshot rollback, real-time logs | Via core |\n\n```\nDependencies:\n\nAneiang.Yarp.Dashboard\n └── Aneiang.Yarp\n └── Aneiang.Yarp.Client\n\nClient services → Reference Aneiang.Yarp.Client only (no YARP SDK pulled in)\nGateway services → Reference Aneiang.Yarp + Aneiang.Yarp.Dashboard\n```\n\nDemo: http://113.45.65.71:8930/apigateway &nbsp;&nbsp; admin/demo123\n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Features\n\n### Dynamic Route Management\n\nYARP relies on `appsettings.json` for route configuration, requiring restarts for changes. Aneiang.Yarp adds full runtime management:\n\n- **Dual config sources**: Static config from `appsettings.json` + dynamic config from API calls, unified in YARP's `InMemoryConfigProvider`\n- **Config persistence**: All dynamic changes auto-persist to `gateway-dynamic.json`, surviving restarts\n- **Thread-safe**: All operations protected by `lock` for concurrent safety\n- **Source tracking**: Every route/cluster records `CreatedAt`, `CreatedBy`, `Source` metadata\n\nRESTful API at `api/gateway`:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| POST | `/api/gateway/register-route` | Register or update a route + cluster |\n| DELETE | `/api/gateway/{routeName}?clientIp=` | Delete a route (supports IP isolation) |\n| GET | `/api/gateway/routes` | Query all routes |\n| GET | `/api/gateway/dynamic-config` | Query dynamic config with metadata |\n| PUT | `/api/gateway/routes/{routeId}` | Update a route |\n| POST | `/api/gateway/clusters` | Create a cluster |\n| PUT | `/api/gateway/clusters/{clusterId}` | Update a cluster |\n| DELETE | `/api/gateway/clusters/{clusterId}` | Delete a cluster |\n| GET | `/api/gateway/ping` | Health check |\n\n### Client Auto-Registration\n\nMicroservices can auto-register with the gateway on startup and auto-unregister on shutdown — one line of code:\n\n```csharp\nbuilder.Services.AddAneiangYarpClient();\n```\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n**Smart defaults** — only `GatewayUrl` is required. Everything else is auto-detected:\n\n| Config | Default | Description |\n|--------|---------|-------------|\n| `RouteName` | Entry assembly name | e.g. `MyService` |\n| `ClusterName` | Same as RouteName | |\n| `MatchPath` | `/{**catch-all}` | Match all paths |\n| `DestinationAddress` | Kestrel bind address | Auto-detected, `localhost` resolved to LAN IP |\n| `Order` | `50` | Route priority |\n\n> `Aneiang.Yarp.Client` has **no YARP SDK dependency** — it only depends on `Microsoft.AspNetCore.App`. Downstream services get a clean dependency footprint.\n\n### IP Isolation Load Balancing\n\nA unique feature for **multi-developer debugging**. Multiple developers run the same service locally, and the gateway routes requests by client IP — the frontend is completely unaware.\n\n```\nDeveloper A (192.168.1.10) → POST /api/user → Gateway → 192.168.1.10:5001\nDeveloper B (192.168.1.20) → POST /api/user → Gateway → 192.168.1.20:5001\nOther requests → POST /api/user → Gateway → First available instance\n```\n\nEnable on the client side:\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"RouteName\": \"my-service\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"UseIpIsolation\": true\n }\n }\n}\n```\n\nImplementation: Custom `IpBasedLoadBalancingPolicy` matches request source IP (via `X-Forwarded-For` or `RemoteIpAddress`) against destination metadata.\n\n### Dashboard\n\nFull web management UI — 2 lines of code to enable:\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard();\n// ...\napp.UseAneiangYarpDashboard();\n```\n\n| Feature | Description |\n|---------|-------------|\n| Cluster & Route CRUD | Form-based + JSON editor, supports YARP standard format |\n| Config Import/Export | One-click export/import in standard YARP format with validation |\n| Snapshot & Rollback | Auto-snapshot before every change, one-click rollback |\n| Real-time Logs | Request/response capture with route/status/TraceID filtering |\n| Log Sanitization | Auto-mask sensitive headers, query params, and JSON fields |\n| Log Sampling | Rate-based sampling for production traffic control |\n| i18n | Chinese / English, runtime switchable |\n| Multi-mode Auth | None / API Key / JWT (default & custom) / custom delegate |\n\n### API Authorization\n\nProtect the gateway management API (`GatewayConfigController`) with optional auth:\n\n```csharp\nbuilder.Services.AddGatewayApiAuth();\n```\n\n| Mode | Description |\n|------|-------------|\n| `BasicAuth` | HTTP Basic authentication |\n| `ApiKey` | Via `X-Api-Key` header |\n| `None` | No protection (default) |\n\n```json\n{\n \"Gateway\": {\n \"ApiAuth\": {\n \"Mode\": \"BasicAuth\",\n \"Username\": \"admin\",\n \"Password\": \"secure-password\"\n }\n }\n}\n```\n\nSmart inference: If `Gateway:ApiAuth` is not configured but `Gateway:Dashboard` has a JWT password, credentials are auto-derived (username `admin`, password = JWT password).\n\n---\n\n## Quick Start\n\n### 1. Create a Gateway\n\n```bash\ndotnet new web -n MyGateway\ncd MyGateway\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard();\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 2. Create a Microservice\n\n```bash\ndotnet new web -n MyService\ncd MyService\ndotnet add package Aneiang.Yarp.Client\n```\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddAneiangYarpClient();\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\"\n }\n }\n}\n```\n\nThat's it. The microservice registers itself on startup and unregisters on shutdown.\n\n---\n\n## Dashboard Configuration\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime (`en-US` / `zh-CN`) |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors (4xx/5xx) |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n### Dashboard Endpoints\n\n#### Pages — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n#### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n---\n\n## Advanced\n\n<details>\n<summary><b>Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>Gateway API Auth — Smart Credential Inference</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the JWT password as the API auth credential:\n\n```csharp\n// Gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard JWT password\n```\n\nNo extra config needed on the client side.\n\n</details>\n\n<details>\n<summary><b>Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips the dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"JwtSecret\": \"your-persisted-secret-key\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp** | Gateway core: dynamic routing, IP isolation, API auth | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dependency) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web management UI with auth, logs, and config management | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "a6371667ddfe0fae5aa7890c7d41bbb2522caf97": {"t": 1785763448.9565332, "n": 13786, "h": "e3662359c86ac6df", "c": "<div align=\"center\">\n\n**Give your YARP gateway a management dashboard — in just 3 lines of code**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nUsing YARP as your gateway, but still editing `appsettings.json` by hand to manage routes?\n\n**Aneiang.Yarp.Dashboard** lets you manage YARP right in the browser — visual CRUD, JSON editor, one-click rollback, real-time logs. Install and go.\n\n```csharp\n// Program.cs — that's it\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line\n```\n\nDemo:http://113.45.65.71:8930/apigateway \nadmin/demo123 \n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Get Started in 30 Seconds\n\n### 1. Install NuGet Packages\n\n```bash\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n### 2. Update Program.cs\n\n```csharp\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line, done\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← add this line, captures request logs\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 3. Open the Browser\n\nVisit `http://localhost:5000/apigateway` — the dashboard is already there.\n\n### Want a Login? (Optional)\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-password\"\n }\n }\n}\n```\n\nAdd the config, and the dashboard redirects to a login page. Default username: `admin`.\n\n---\n\n## What You Get\n\n| Feature | What It Does |\n|---------|-------------|\n| 📊 **Cluster Management** | Browse, create, edit, delete, rename clusters — or use the JSON editor |\n| 🛣️ **Route Management** | Full CRUD + JSON editor, supports standard YARP format |\n| 💾 **Config Import/Export** | One-click export of full YARP config, import with auto-validation |\n| ⏪ **Config Rollback** | Auto-snapshot on every change, one-click rollback if something breaks |\n| 📝 **Real-time Logs** | Full request/response capture, filter by route/status/TraceID |\n| 🔐 **Multi-mode Auth** | JWT login / API Key / custom delegate — works out of the box |\n| 🌐 **i18n** | Switch language at runtime, no restart needed |\n| 🛡️ **Log Sanitization** | Auto-mask Authorization, password, and other sensitive fields |\n| 📈 **Log Sampling** | Sample by rate in production to control log volume |\n\n**Design principle**: Zero intrusion — just 2 lines of code to enable, doesn't change your existing YARP config or proxy logic.\n\n---\n\n## Feature Details\n\n### 📊 Cluster & Route Management\n\nManage YARP clusters and routes right in the browser — no more hand-editing JSON config files:\n\n- **Form-based creation** — Fill in a few fields to create a route or cluster\n- **JSON editor** — Built-in code editor with syntax highlighting, live validation, auto-formatting\n- **Format compatibility** — Accepts both camelCase and PascalCase, paste official YARP config directly\n- **Smart association** — Creating a route whose target cluster doesn't exist? It auto-creates the cluster\n- **Safe deletion** — Optionally clean up orphaned clusters when deleting routes; deleting a cluster auto-updates references\n\n### 💾 Config Version Management\n\nBroke something? One-click rollback.\n\n- **Auto-snapshot** — Before every create/edit/delete, the system automatically saves a config snapshot\n- **Change history** — View timestamp, operator IP, and change content for each change\n- **One-click rollback** — Pick any historical version and roll back instantly\n- **Export** — Export in full YARP standard format, ready to paste into `appsettings.json`\n- **Import** — Import from JSON, auto-validate format, merge and persist\n\n### 📝 Real-time Request Logs\n\nSee what happens to every request passing through the gateway:\n\n- Request method, path, query parameters, headers\n- Response status code, headers\n- Request/response body (JSON auto-parsed)\n- Target cluster and route\n- Trace ID, request duration\n\n**Filtering**: by route ID / status code / Trace ID / time range\n\n**Security controls** (production-friendly):\n\n| Setting | One-liner |\n|---------|-----------|\n| `EnableProxyLogging` | Master switch — off stops the entire pipeline |\n| `EnableLogSampling` + `LogSamplingRate` | Sampling — 0.1 = only log 10% |\n| `LogErrorsOnly` | Only log 4xx/5xx |\n| `LogMaxBodyLength` | Truncate long bodies |\n| `LogRouteWhitelist` / `LogRouteBlacklist` | Filter by route |\n| `LogHeaderBlacklist` | `Authorization` → `***REDACTED***` |\n| `LogQueryBlacklist` | Mask sensitive query params |\n| `LogJsonFieldSanitizeList` | `password` in JSON → `***REDACTED***` |\n\n### 🔐 Authentication\n\nFour modes, pick what fits:\n\n| Mode | How It Works | Best For |\n|------|-------------|----------|\n| `None` | No protection | Local development |\n| `DefaultJwt` | Set a password, username is fixed as `admin` | Personal / small teams |\n| `CustomJwt` | Custom username + password | Enterprise |\n| `ApiKey` | Pass API Key via Header | API integrations |\n\nThere's also an `AuthorizeRequest` delegate you can use to plug in your own auth system (highest priority).\n\n---\n\n## Configuration Reference\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n---\n\n## API Endpoints\n\n### Dashboard — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n> Default prefix is `apigateway`, customizable via `RoutePrefix`.\n\n---\n\n## Advanced Usage\n\n<details>\n<summary><b>🔐 Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n // Return true = allow access\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>🛡️ Dashboard + Zero-Config Client Registration</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the password — zero config on the client side:\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard password\n\n// Client Program.cs — no auth config needed at all\n// Reference Aneiang.Yarp.Client package\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>🔧 Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>📋 Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## About Aneiang.Yarp\n\nThe dashboard depends on [Aneiang.Yarp](https://www.nuget.org/packages/Aneiang.Yarp) core library, which provides:\n\n| Capability | Description |\n|------------|-------------|\n| 🚀 **Dynamic Routing API** | `/api/gateway` for runtime route registration/update/unregistration |\n| 👥 **IP Isolation** | Multi-developer debugging with automatic IP-based routing isolation |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost → LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | `enableRegistration: false` removes registration endpoints entirely |\n\nThe core library works independently — no dashboard required.\n\nFor client services, the lightweight [Aneiang.Yarp.Client](https://www.nuget.org/packages/Aneiang.Yarp.Client) is recommended. Just `AddAneiangYarpClient()` to auto-register on startup and unregister on shutdown — **no YARP SDK dependency**.\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp.Dashboard** | Dashboard | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Gateway core library (standalone) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dependency) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Client.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "ef0e3ebeea47657e4a58a87b0edc04539841e70b": {"t": 1785763450.8588376, "n": 13404, "h": "d84b1a4c0822aacf", "c": "<div align=\"center\">\n\n**Give your YARP gateway a management dashboard — in just 3 lines of code**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nUsing YARP as your gateway, but still editing `appsettings.json` by hand to manage routes?\n\n**Aneiang.Yarp.Dashboard** lets you manage YARP right in the browser — visual CRUD, JSON editor, one-click rollback, real-time logs. Install and go.\n\n```csharp\n// Program.cs — that's it\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line\n```\n\nDemo:http://113.45.65.71:8930/apigateway \nadmin/demo123 \n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Get Started in 30 Seconds\n\n### 1. Install NuGet Packages\n\n```bash\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n### 2. Update Program.cs\n\n```csharp\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line, done\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← add this line, captures request logs\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 3. Open the Browser\n\nVisit `http://localhost:5000/apigateway` — the dashboard is already there.\n\n### Want a Login? (Optional)\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-password\"\n }\n }\n}\n```\n\nAdd the config, and the dashboard redirects to a login page. Default username: `admin`.\n\n---\n\n## What You Get\n\n| Feature | What It Does |\n|---------|-------------|\n| 📊 **Cluster Management** | Browse, create, edit, delete, rename clusters — or use the JSON editor |\n| 🛣️ **Route Management** | Full CRUD + JSON editor, supports standard YARP format |\n| 💾 **Config Import/Export** | One-click export of full YARP config, import with auto-validation |\n| ⏪ **Config Rollback** | Auto-snapshot on every change, one-click rollback if something breaks |\n| 📝 **Real-time Logs** | Full request/response capture, filter by route/status/TraceID |\n| 🔐 **Multi-mode Auth** | JWT login / API Key / custom delegate — works out of the box |\n| 🌐 **i18n** | Switch language at runtime, no restart needed |\n| 🛡️ **Log Sanitization** | Auto-mask Authorization, password, and other sensitive fields |\n| 📈 **Log Sampling** | Sample by rate in production to control log volume |\n\n**Design principle**: Zero intrusion — just 2 lines of code to enable, doesn't change your existing YARP config or proxy logic.\n\n---\n\n## Feature Details\n\n### 📊 Cluster & Route Management\n\nManage YARP clusters and routes right in the browser — no more hand-editing JSON config files:\n\n- **Form-based creation** — Fill in a few fields to create a route or cluster\n- **JSON editor** — Built-in code editor with syntax highlighting, live validation, auto-formatting\n- **Format compatibility** — Accepts both camelCase and PascalCase, paste official YARP config directly\n- **Smart association** — Creating a route whose target cluster doesn't exist? It auto-creates the cluster\n- **Safe deletion** — Optionally clean up orphaned clusters when deleting routes; deleting a cluster auto-updates references\n\n### 💾 Config Version Management\n\nBroke something? One-click rollback.\n\n- **Auto-snapshot** — Before every create/edit/delete, the system automatically saves a config snapshot\n- **Change history** — View timestamp, operator IP, and change content for each change\n- **One-click rollback** — Pick any historical version and roll back instantly\n- **Export** — Export in full YARP standard format, ready to paste into `appsettings.json`\n- **Import** — Import from JSON, auto-validate format, merge and persist\n\n### 📝 Real-time Request Logs\n\nSee what happens to every request passing through the gateway:\n\n- Request method, path, query parameters, headers\n- Response status code, headers\n- Request/response body (JSON auto-parsed)\n- Target cluster and route\n- Trace ID, request duration\n\n**Filtering**: by route ID / status code / Trace ID / time range\n\n**Security controls** (production-friendly):\n\n| Setting | One-liner |\n|---------|-----------|\n| `EnableProxyLogging` | Master switch — off stops the entire pipeline |\n| `EnableLogSampling` + `LogSamplingRate` | Sampling — 0.1 = only log 10% |\n| `LogErrorsOnly` | Only log 4xx/5xx |\n| `LogMaxBodyLength` | Truncate long bodies |\n| `LogRouteWhitelist` / `LogRouteBlacklist` | Filter by route |\n| `LogHeaderBlacklist` | `Authorization` → `***REDACTED***` |\n| `LogQueryBlacklist` | Mask sensitive query params |\n| `LogJsonFieldSanitizeList` | `password` in JSON → `***REDACTED***` |\n\n### 🔐 Authentication\n\nFour modes, pick what fits:\n\n| Mode | How It Works | Best For |\n|------|-------------|----------|\n| `None` | No protection | Local development |\n| `DefaultJwt` | Set a password, username is fixed as `admin` | Personal / small teams |\n| `CustomJwt` | Custom username + password | Enterprise |\n| `ApiKey` | Pass API Key via Header | API integrations |\n\nThere's also an `AuthorizeRequest` delegate you can use to plug in your own auth system (highest priority).\n\n---\n\n## Configuration Reference\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n---\n\n## API Endpoints\n\n### Dashboard — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n> Default prefix is `apigateway`, customizable via `RoutePrefix`.\n\n---\n\n## Advanced Usage\n\n<details>\n<summary><b>🔐 Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n // Return true = allow access\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>🛡️ Dashboard + Zero-Config Client Registration</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the password — zero config on the client side:\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard password\n\n// Client Program.cs — no auth config needed at all\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>🔧 Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>📋 Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## About Aneiang.Yarp\n\nThe dashboard depends on [Aneiang.Yarp](https://www.nuget.org/packages/Aneiang.Yarp) core library, which provides:\n\n| Capability | Description |\n|------------|-------------|\n| 🚀 **Dynamic Routing API** | `/api/gateway` for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration Client** | `AddAneiangYarpClient()` — one line, registers on startup, unregisters on shutdown |\n| 👥 **IP Isolation** | Multi-developer debugging with automatic IP-based routing isolation |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost → LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | `enableRegistration: false` removes registration endpoints entirely |\n\nThe core library works independently — no dashboard required.\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp.Dashboard** | Dashboard | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "cf978e4be33e1ab23a4678a81c2785d557f4061a": {"t": 1785763454.0946984, "n": 13430, "h": "d222e63ba955fba8", "c": "# Aneiang.Yarp.Dashboard\n\n<div align=\"center\">\n\n**Give your YARP gateway a management dashboard — in just 3 lines of code**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nUsing YARP as your gateway, but still editing `appsettings.json` by hand to manage routes?\n\n**Aneiang.Yarp.Dashboard** lets you manage YARP right in the browser — visual CRUD, JSON editor, one-click rollback, real-time logs. Install and go.\n\n```csharp\n// Program.cs — that's it\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line\n```\n\nDemo:http://113.45.65.71:8930/apigateway \nadmin/demo123 \n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Get Started in 30 Seconds\n\n### 1. Install NuGet Packages\n\n```bash\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n### 2. Update Program.cs\n\n```csharp\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line, done\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← add this line, captures request logs\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 3. Open the Browser\n\nVisit `http://localhost:5000/apigateway` — the dashboard is already there.\n\n### Want a Login? (Optional)\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-password\"\n }\n }\n}\n```\n\nAdd the config, and the dashboard redirects to a login page. Default username: `admin`.\n\n---\n\n## What You Get\n\n| Feature | What It Does |\n|---------|-------------|\n| 📊 **Cluster Management** | Browse, create, edit, delete, rename clusters — or use the JSON editor |\n| 🛣️ **Route Management** | Full CRUD + JSON editor, supports standard YARP format |\n| 💾 **Config Import/Export** | One-click export of full YARP config, import with auto-validation |\n| ⏪ **Config Rollback** | Auto-snapshot on every change, one-click rollback if something breaks |\n| 📝 **Real-time Logs** | Full request/response capture, filter by route/status/TraceID |\n| 🔐 **Multi-mode Auth** | JWT login / API Key / custom delegate — works out of the box |\n| 🌐 **i18n** | Switch language at runtime, no restart needed |\n| 🛡️ **Log Sanitization** | Auto-mask Authorization, password, and other sensitive fields |\n| 📈 **Log Sampling** | Sample by rate in production to control log volume |\n\n**Design principle**: Zero intrusion — just 2 lines of code to enable, doesn't change your existing YARP config or proxy logic.\n\n---\n\n## Feature Details\n\n### 📊 Cluster & Route Management\n\nManage YARP clusters and routes right in the browser — no more hand-editing JSON config files:\n\n- **Form-based creation** — Fill in a few fields to create a route or cluster\n- **JSON editor** — Built-in code editor with syntax highlighting, live validation, auto-formatting\n- **Format compatibility** — Accepts both camelCase and PascalCase, paste official YARP config directly\n- **Smart association** — Creating a route whose target cluster doesn't exist? It auto-creates the cluster\n- **Safe deletion** — Optionally clean up orphaned clusters when deleting routes; deleting a cluster auto-updates references\n\n### 💾 Config Version Management\n\nBroke something? One-click rollback.\n\n- **Auto-snapshot** — Before every create/edit/delete, the system automatically saves a config snapshot\n- **Change history** — View timestamp, operator IP, and change content for each change\n- **One-click rollback** — Pick any historical version and roll back instantly\n- **Export** — Export in full YARP standard format, ready to paste into `appsettings.json`\n- **Import** — Import from JSON, auto-validate format, merge and persist\n\n### 📝 Real-time Request Logs\n\nSee what happens to every request passing through the gateway:\n\n- Request method, path, query parameters, headers\n- Response status code, headers\n- Request/response body (JSON auto-parsed)\n- Target cluster and route\n- Trace ID, request duration\n\n**Filtering**: by route ID / status code / Trace ID / time range\n\n**Security controls** (production-friendly):\n\n| Setting | One-liner |\n|---------|-----------|\n| `EnableProxyLogging` | Master switch — off stops the entire pipeline |\n| `EnableLogSampling` + `LogSamplingRate` | Sampling — 0.1 = only log 10% |\n| `LogErrorsOnly` | Only log 4xx/5xx |\n| `LogMaxBodyLength` | Truncate long bodies |\n| `LogRouteWhitelist` / `LogRouteBlacklist` | Filter by route |\n| `LogHeaderBlacklist` | `Authorization` → `***REDACTED***` |\n| `LogQueryBlacklist` | Mask sensitive query params |\n| `LogJsonFieldSanitizeList` | `password` in JSON → `***REDACTED***` |\n\n### 🔐 Authentication\n\nFour modes, pick what fits:\n\n| Mode | How It Works | Best For |\n|------|-------------|----------|\n| `None` | No protection | Local development |\n| `DefaultJwt` | Set a password, username is fixed as `admin` | Personal / small teams |\n| `CustomJwt` | Custom username + password | Enterprise |\n| `ApiKey` | Pass API Key via Header | API integrations |\n\nThere's also an `AuthorizeRequest` delegate you can use to plug in your own auth system (highest priority).\n\n---\n\n## Configuration Reference\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n---\n\n## API Endpoints\n\n### Dashboard — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n> Default prefix is `apigateway`, customizable via `RoutePrefix`.\n\n---\n\n## Advanced Usage\n\n<details>\n<summary><b>🔐 Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n // Return true = allow access\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>🛡️ Dashboard + Zero-Config Client Registration</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the password — zero config on the client side:\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard password\n\n// Client Program.cs — no auth config needed at all\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>🔧 Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>📋 Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## About Aneiang.Yarp\n\nThe dashboard depends on [Aneiang.Yarp](https://www.nuget.org/packages/Aneiang.Yarp) core library, which provides:\n\n| Capability | Description |\n|------------|-------------|\n| 🚀 **Dynamic Routing API** | `/api/gateway` for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration Client** | `AddAneiangYarpClient()` — one line, registers on startup, unregisters on shutdown |\n| 👥 **IP Isolation** | Multi-developer debugging with automatic IP-based routing isolation |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost → LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | `enableRegistration: false` removes registration endpoints entirely |\n\nThe core library works independently — no dashboard required.\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp.Dashboard** | Dashboard | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "4d89186b8b353ddaea643c3948dffb1391108360": {"t": 1785763455.4550529, "n": 13403, "h": "ef0fdc7beb93461f", "c": "<div align=\"center\">\n\n**Give your YARP gateway a management dashboard — in just 3 lines of code**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nUsing YARP as your gateway, but still editing `appsettings.json` by hand to manage routes?\n\n**Aneiang.Yarp.Dashboard** lets you manage YARP right in the browser — visual CRUD, JSON editor, one-click rollback, real-time logs. Install and go.\n\n```csharp\n// Program.cs — that's it\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line\n```\n\nDemo:http://113.45.65.71:8930/apigateway \nadmin/demo123 \n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Get Started in 30 Seconds\n\n### 1. Install NuGet Packages\n\n```bash\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n### 2. Update Program.cs\n\n```csharp\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line, done\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← add this line, captures request logs\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 3. Open the Browser\n\nVisit `http://localhost:5000/apigateway` — the dashboard is already there.\n\n### Want a Login? (Optional)\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-password\"\n }\n }\n}\n```\n\nAdd the config, and the dashboard redirects to a login page. Default username: `admin`.\n\n---\n\n## What You Get\n\n| Feature | What It Does |\n|---------|-------------|\n| 📊 **Cluster Management** | Browse, create, edit, delete, rename clusters — or use the JSON editor |\n| 🛣️ **Route Management** | Full CRUD + JSON editor, supports standard YARP format |\n| 💾 **Config Import/Export** | One-click export of full YARP config, import with auto-validation |\n| ⏪ **Config Rollback** | Auto-snapshot on every change, one-click rollback if something breaks |\n| 📝 **Real-time Logs** | Full request/response capture, filter by route/status/TraceID |\n| 🔐 **Multi-mode Auth** | JWT login / API Key / custom delegate — works out of the box |\n| 🌐 **i18n** | Switch language at runtime, no restart needed |\n| 🛡️ **Log Sanitization** | Auto-mask Authorization, password, and other sensitive fields |\n| 📈 **Log Sampling** | Sample by rate in production to control log volume |\n\n**Design principle**: Zero intrusion — just 2 lines of code to enable, doesn't change your existing YARP config or proxy logic.\n\n---\n\n## Feature Details\n\n### 📊 Cluster & Route Management\n\nManage YARP clusters and routes right in the browser — no more hand-editing JSON config files:\n\n- **Form-based creation** — Fill in a few fields to create a route or cluster\n- **JSON editor** — Built-in code editor with syntax highlighting, live validation, auto-formatting\n- **Format compatibility** — Accepts both camelCase and PascalCase, paste official YARP config directly\n- **Smart association** — Creating a route whose target cluster doesn't exist? It auto-creates the cluster\n- **Safe deletion** — Optionally clean up orphaned clusters when deleting routes; deleting a cluster auto-updates references\n\n### 💾 Config Version Management\n\nBroke something? One-click rollback.\n\n- **Auto-snapshot** — Before every create/edit/delete, the system automatically saves a config snapshot\n- **Change history** — View timestamp, operator IP, and change content for each change\n- **One-click rollback** — Pick any historical version and roll back instantly\n- **Export** — Export in full YARP standard format, ready to paste into `appsettings.json`\n- **Import** — Import from JSON, auto-validate format, merge and persist\n\n### 📝 Real-time Request Logs\n\nSee what happens to every request passing through the gateway:\n\n- Request method, path, query parameters, headers\n- Response status code, headers\n- Request/response body (JSON auto-parsed)\n- Target cluster and route\n- Trace ID, request duration\n\n**Filtering**: by route ID / status code / Trace ID / time range\n\n**Security controls** (production-friendly):\n\n| Setting | One-liner |\n|---------|-----------|\n| `EnableProxyLogging` | Master switch — off stops the entire pipeline |\n| `EnableLogSampling` + `LogSamplingRate` | Sampling — 0.1 = only log 10% |\n| `LogErrorsOnly` | Only log 4xx/5xx |\n| `LogMaxBodyLength` | Truncate long bodies |\n| `LogRouteWhitelist` / `LogRouteBlacklist` | Filter by route |\n| `LogHeaderBlacklist` | `Authorization` → `***REDACTED***` |\n| `LogQueryBlacklist` | Mask sensitive query params |\n| `LogJsonFieldSanitizeList` | `password` in JSON → `***REDACTED***` |\n\n### 🔐 Authentication\n\nFour modes, pick what fits:\n\n| Mode | How It Works | Best For |\n|------|-------------|----------|\n| `None` | No protection | Local development |\n| `DefaultJwt` | Set a password, username is fixed as `admin` | Personal / small teams |\n| `CustomJwt` | Custom username + password | Enterprise |\n| `ApiKey` | Pass API Key via Header | API integrations |\n\nThere's also an `AuthorizeRequest` delegate you can use to plug in your own auth system (highest priority).\n\n---\n\n## Configuration Reference\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n---\n\n## API Endpoints\n\n### Dashboard — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n> Default prefix is `apigateway`, customizable via `RoutePrefix`.\n\n---\n\n## Advanced Usage\n\n<details>\n<summary><b>🔐 Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n // Return true = allow access\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>🛡️ Dashboard + Zero-Config Client Registration</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the password — zero config on the client side:\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard password\n\n// Client Program.cs — no auth config needed at all\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>🔧 Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>📋 Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## About Aneiang.Yarp\n\nThe dashboard depends on [Aneiang.Yarp](https://www.nuget.org/packages/Aneiang.Yarp) core library, which provides:\n\n| Capability | Description |\n|------------|-------------|\n| 🚀 **Dynamic Routing API** | `/api/gateway` for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration Client** | `AddAneiangYarpClient()` — one line, registers on startup, unregisters on shutdown |\n| 👥 **Instance Isolation** | Multi-developer debugging with automatic namespace isolation |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost → LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | `enableRegistration: false` removes registration endpoints entirely |\n\nThe core library works independently — no dashboard required.\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp.Dashboard** | Dashboard | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "18051025ab1def032602beb5ea01f2d659b0476f": {"t": 1785763458.7044728, "n": 13429, "h": "1e2ac462b4a5ab77", "c": "# Aneiang.Yarp.Dashboard\n\n<div align=\"center\">\n\n**Give your YARP gateway a management dashboard — in just 3 lines of code**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nUsing YARP as your gateway, but still editing `appsettings.json` by hand to manage routes?\n\n**Aneiang.Yarp.Dashboard** lets you manage YARP right in the browser — visual CRUD, JSON editor, one-click rollback, real-time logs. Install and go.\n\n```csharp\n// Program.cs — that's it\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line\n```\n\nDemo:http://113.45.65.71:8930/apigateway \nadmin/demo123 \n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Get Started in 30 Seconds\n\n### 1. Install NuGet Packages\n\n```bash\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n### 2. Update Program.cs\n\n```csharp\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line, done\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← add this line, captures request logs\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 3. Open the Browser\n\nVisit `http://localhost:5000/apigateway` — the dashboard is already there.\n\n### Want a Login? (Optional)\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-password\"\n }\n }\n}\n```\n\nAdd the config, and the dashboard redirects to a login page. Default username: `admin`.\n\n---\n\n## What You Get\n\n| Feature | What It Does |\n|---------|-------------|\n| 📊 **Cluster Management** | Browse, create, edit, delete, rename clusters — or use the JSON editor |\n| 🛣️ **Route Management** | Full CRUD + JSON editor, supports standard YARP format |\n| 💾 **Config Import/Export** | One-click export of full YARP config, import with auto-validation |\n| ⏪ **Config Rollback** | Auto-snapshot on every change, one-click rollback if something breaks |\n| 📝 **Real-time Logs** | Full request/response capture, filter by route/status/TraceID |\n| 🔐 **Multi-mode Auth** | JWT login / API Key / custom delegate — works out of the box |\n| 🌐 **i18n** | Switch language at runtime, no restart needed |\n| 🛡️ **Log Sanitization** | Auto-mask Authorization, password, and other sensitive fields |\n| 📈 **Log Sampling** | Sample by rate in production to control log volume |\n\n**Design principle**: Zero intrusion — just 2 lines of code to enable, doesn't change your existing YARP config or proxy logic.\n\n---\n\n## Feature Details\n\n### 📊 Cluster & Route Management\n\nManage YARP clusters and routes right in the browser — no more hand-editing JSON config files:\n\n- **Form-based creation** — Fill in a few fields to create a route or cluster\n- **JSON editor** — Built-in code editor with syntax highlighting, live validation, auto-formatting\n- **Format compatibility** — Accepts both camelCase and PascalCase, paste official YARP config directly\n- **Smart association** — Creating a route whose target cluster doesn't exist? It auto-creates the cluster\n- **Safe deletion** — Optionally clean up orphaned clusters when deleting routes; deleting a cluster auto-updates references\n\n### 💾 Config Version Management\n\nBroke something? One-click rollback.\n\n- **Auto-snapshot** — Before every create/edit/delete, the system automatically saves a config snapshot\n- **Change history** — View timestamp, operator IP, and change content for each change\n- **One-click rollback** — Pick any historical version and roll back instantly\n- **Export** — Export in full YARP standard format, ready to paste into `appsettings.json`\n- **Import** — Import from JSON, auto-validate format, merge and persist\n\n### 📝 Real-time Request Logs\n\nSee what happens to every request passing through the gateway:\n\n- Request method, path, query parameters, headers\n- Response status code, headers\n- Request/response body (JSON auto-parsed)\n- Target cluster and route\n- Trace ID, request duration\n\n**Filtering**: by route ID / status code / Trace ID / time range\n\n**Security controls** (production-friendly):\n\n| Setting | One-liner |\n|---------|-----------|\n| `EnableProxyLogging` | Master switch — off stops the entire pipeline |\n| `EnableLogSampling` + `LogSamplingRate` | Sampling — 0.1 = only log 10% |\n| `LogErrorsOnly` | Only log 4xx/5xx |\n| `LogMaxBodyLength` | Truncate long bodies |\n| `LogRouteWhitelist` / `LogRouteBlacklist` | Filter by route |\n| `LogHeaderBlacklist` | `Authorization` → `***REDACTED***` |\n| `LogQueryBlacklist` | Mask sensitive query params |\n| `LogJsonFieldSanitizeList` | `password` in JSON → `***REDACTED***` |\n\n### 🔐 Authentication\n\nFour modes, pick what fits:\n\n| Mode | How It Works | Best For |\n|------|-------------|----------|\n| `None` | No protection | Local development |\n| `DefaultJwt` | Set a password, username is fixed as `admin` | Personal / small teams |\n| `CustomJwt` | Custom username + password | Enterprise |\n| `ApiKey` | Pass API Key via Header | API integrations |\n\nThere's also an `AuthorizeRequest` delegate you can use to plug in your own auth system (highest priority).\n\n---\n\n## Configuration Reference\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n---\n\n## API Endpoints\n\n### Dashboard — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n> Default prefix is `apigateway`, customizable via `RoutePrefix`.\n\n---\n\n## Advanced Usage\n\n<details>\n<summary><b>🔐 Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n // Return true = allow access\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>🛡️ Dashboard + Zero-Config Client Registration</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the password — zero config on the client side:\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard password\n\n// Client Program.cs — no auth config needed at all\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>🔧 Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>📋 Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## About Aneiang.Yarp\n\nThe dashboard depends on [Aneiang.Yarp](https://www.nuget.org/packages/Aneiang.Yarp) core library, which provides:\n\n| Capability | Description |\n|------------|-------------|\n| 🚀 **Dynamic Routing API** | `/api/gateway` for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration Client** | `AddAneiangYarpClient()` — one line, registers on startup, unregisters on shutdown |\n| 👥 **Instance Isolation** | Multi-developer debugging with automatic namespace isolation |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost → LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | `enableRegistration: false` removes registration endpoints entirely |\n\nThe core library works independently — no dashboard required.\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp.Dashboard** | Dashboard | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "5ddbc244d240da9a84a0bd37998fd5dc476d7f60": {"t": 1785763460.6852436, "n": 13550, "h": "28bbc8d2b1fb92f6", "c": "# Aneiang.Yarp.Dashboard\n\n<div align=\"center\">\n\n**Give your YARP gateway a management dashboard — in just 3 lines of code**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nUsing YARP as your gateway, but still editing `appsettings.json` by hand to manage routes?\n\n**Aneiang.Yarp.Dashboard** lets you manage YARP right in the browser — visual CRUD, JSON editor, one-click rollback, real-time logs. Install and go.\n\n```csharp\n// Program.cs — that's it\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line\n```\n\nDemo:http://113.45.65.71:8930/apigateway \nadmin/demo123 \n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Get Started in 30 Seconds\n\n### 1. Install NuGet Packages\n\n```bash\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n### 2. Update Program.cs\n\n```csharp\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line, done\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← add this line, captures request logs\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 3. Open the Browser\n\nVisit `http://localhost:5000/apigateway` — the dashboard is already there.\n\n### Want a Login? (Optional)\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-password\"\n }\n }\n}\n```\n\nAdd the config, and the dashboard redirects to a login page. Default username: `admin`.\n\n---\n\n## What You Get\n\n| Feature | What It Does |\n|---------|-------------|\n| 📊 **Cluster Management** | Browse, create, edit, delete, rename clusters — or use the JSON editor |\n| 🛣️ **Route Management** | Full CRUD + JSON editor, supports standard YARP format |\n| 💾 **Config Import/Export** | One-click export of full YARP config, import with auto-validation |\n| ⏪ **Config Rollback** | Auto-snapshot on every change, one-click rollback if something breaks |\n| 📝 **Real-time Logs** | Full request/response capture, filter by route/status/TraceID |\n| 🔐 **Multi-mode Auth** | JWT login / API Key / custom delegate — works out of the box |\n| 🌐 **i18n** | Switch language at runtime, no restart needed |\n| 🛡️ **Log Sanitization** | Auto-mask Authorization, password, and other sensitive fields |\n| 📈 **Log Sampling** | Sample by rate in production to control log volume |\n\n**Design principle**: Zero intrusion — just 2 lines of code to enable, doesn't change your existing YARP config or proxy logic.\n\n---\n\n## Feature Details\n\n### 📊 Cluster & Route Management\n\nManage YARP clusters and routes right in the browser — no more hand-editing JSON config files:\n\n- **Form-based creation** — Fill in a few fields to create a route or cluster\n- **JSON editor** — Built-in code editor with syntax highlighting, live validation, auto-formatting\n- **Format compatibility** — Accepts both camelCase and PascalCase, paste official YARP config directly\n- **Smart association** — Creating a route whose target cluster doesn't exist? It auto-creates the cluster\n- **Safe deletion** — Optionally clean up orphaned clusters when deleting routes; deleting a cluster auto-updates references\n\n### 💾 Config Version Management\n\nBroke something? One-click rollback.\n\n- **Auto-snapshot** — Before every create/edit/delete, the system automatically saves a config snapshot\n- **Change history** — View timestamp, operator IP, and change content for each change\n- **One-click rollback** — Pick any historical version and roll back instantly\n- **Export** — Export in full YARP standard format, ready to paste into `appsettings.json`\n- **Import** — Import from JSON, auto-validate format, merge and persist\n\n### 📝 Real-time Request Logs\n\nSee what happens to every request passing through the gateway:\n\n- Request method, path, query parameters, headers\n- Response status code, headers\n- Request/response body (JSON auto-parsed)\n- Target cluster and route\n- Trace ID, request duration\n\n**Filtering**: by route ID / status code / Trace ID / time range\n\n**Security controls** (production-friendly):\n\n| Setting | One-liner |\n|---------|-----------|\n| `EnableProxyLogging` | Master switch — off stops the entire pipeline |\n| `EnableLogSampling` + `LogSamplingRate` | Sampling — 0.1 = only log 10% |\n| `LogErrorsOnly` | Only log 4xx/5xx |\n| `LogMaxBodyLength` | Truncate long bodies |\n| `LogRouteWhitelist` / `LogRouteBlacklist` | Filter by route |\n| `LogHeaderBlacklist` | `Authorization` → `***REDACTED***` |\n| `LogQueryBlacklist` | Mask sensitive query params |\n| `LogJsonFieldSanitizeList` | `password` in JSON → `***REDACTED***` |\n\n### 🔐 Authentication\n\nFour modes, pick what fits:\n\n| Mode | How It Works | Best For |\n|------|-------------|----------|\n| `None` | No protection | Local development |\n| `DefaultJwt` | Set a password, username is fixed as `admin` | Personal / small teams |\n| `CustomJwt` | Custom username + password | Enterprise |\n| `ApiKey` | Pass API Key via Header | API integrations |\n\nThere's also an `AuthorizeRequest` delegate you can use to plug in your own auth system (highest priority).\n\n---\n\n## Configuration Reference\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n---\n\n## API Endpoints\n\n### Dashboard — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n> Default prefix is `apigateway`, customizable via `RoutePrefix`.\n\n---\n\n## Advanced Usage\n\n<details>\n<summary><b>🔐 Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n // Return true = allow access\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>🛡️ Dashboard + Zero-Config Client Registration</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the password — zero config on the client side:\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard password\n\n// Client Program.cs — no auth config needed at all\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>🔧 Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseStaticFiles(); // ← place the static file middleware in front to avoid the inability to load front-end resources\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>📋 Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## About Aneiang.Yarp\n\nThe dashboard depends on [Aneiang.Yarp](https://www.nuget.org/packages/Aneiang.Yarp) core library, which provides:\n\n| Capability | Description |\n|------------|-------------|\n| 🚀 **Dynamic Routing API** | `/api/gateway` for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration Client** | `AddAneiangYarpClient()` — one line, registers on startup, unregisters on shutdown |\n| 👥 **Instance Isolation** | Multi-developer debugging with automatic namespace isolation |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost → LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | `enableRegistration: false` removes registration endpoints entirely |\n\nThe core library works independently — no dashboard required.\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp.Dashboard** | Dashboard | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "1fb232afbd6c0e0fb3280d41ad7282f432db00a0": {"t": 1785763462.1939297, "n": 13429, "h": "1e2ac462b4a5ab77", "c": "# Aneiang.Yarp.Dashboard\n\n<div align=\"center\">\n\n**Give your YARP gateway a management dashboard — in just 3 lines of code**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nUsing YARP as your gateway, but still editing `appsettings.json` by hand to manage routes?\n\n**Aneiang.Yarp.Dashboard** lets you manage YARP right in the browser — visual CRUD, JSON editor, one-click rollback, real-time logs. Install and go.\n\n```csharp\n// Program.cs — that's it\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line\n```\n\nDemo:http://113.45.65.71:8930/apigateway \nadmin/demo123 \n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Get Started in 30 Seconds\n\n### 1. Install NuGet Packages\n\n```bash\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n### 2. Update Program.cs\n\n```csharp\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line, done\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← add this line, captures request logs\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 3. Open the Browser\n\nVisit `http://localhost:5000/apigateway` — the dashboard is already there.\n\n### Want a Login? (Optional)\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-password\"\n }\n }\n}\n```\n\nAdd the config, and the dashboard redirects to a login page. Default username: `admin`.\n\n---\n\n## What You Get\n\n| Feature | What It Does |\n|---------|-------------|\n| 📊 **Cluster Management** | Browse, create, edit, delete, rename clusters — or use the JSON editor |\n| 🛣️ **Route Management** | Full CRUD + JSON editor, supports standard YARP format |\n| 💾 **Config Import/Export** | One-click export of full YARP config, import with auto-validation |\n| ⏪ **Config Rollback** | Auto-snapshot on every change, one-click rollback if something breaks |\n| 📝 **Real-time Logs** | Full request/response capture, filter by route/status/TraceID |\n| 🔐 **Multi-mode Auth** | JWT login / API Key / custom delegate — works out of the box |\n| 🌐 **i18n** | Switch language at runtime, no restart needed |\n| 🛡️ **Log Sanitization** | Auto-mask Authorization, password, and other sensitive fields |\n| 📈 **Log Sampling** | Sample by rate in production to control log volume |\n\n**Design principle**: Zero intrusion — just 2 lines of code to enable, doesn't change your existing YARP config or proxy logic.\n\n---\n\n## Feature Details\n\n### 📊 Cluster & Route Management\n\nManage YARP clusters and routes right in the browser — no more hand-editing JSON config files:\n\n- **Form-based creation** — Fill in a few fields to create a route or cluster\n- **JSON editor** — Built-in code editor with syntax highlighting, live validation, auto-formatting\n- **Format compatibility** — Accepts both camelCase and PascalCase, paste official YARP config directly\n- **Smart association** — Creating a route whose target cluster doesn't exist? It auto-creates the cluster\n- **Safe deletion** — Optionally clean up orphaned clusters when deleting routes; deleting a cluster auto-updates references\n\n### 💾 Config Version Management\n\nBroke something? One-click rollback.\n\n- **Auto-snapshot** — Before every create/edit/delete, the system automatically saves a config snapshot\n- **Change history** — View timestamp, operator IP, and change content for each change\n- **One-click rollback** — Pick any historical version and roll back instantly\n- **Export** — Export in full YARP standard format, ready to paste into `appsettings.json`\n- **Import** — Import from JSON, auto-validate format, merge and persist\n\n### 📝 Real-time Request Logs\n\nSee what happens to every request passing through the gateway:\n\n- Request method, path, query parameters, headers\n- Response status code, headers\n- Request/response body (JSON auto-parsed)\n- Target cluster and route\n- Trace ID, request duration\n\n**Filtering**: by route ID / status code / Trace ID / time range\n\n**Security controls** (production-friendly):\n\n| Setting | One-liner |\n|---------|-----------|\n| `EnableProxyLogging` | Master switch — off stops the entire pipeline |\n| `EnableLogSampling` + `LogSamplingRate` | Sampling — 0.1 = only log 10% |\n| `LogErrorsOnly` | Only log 4xx/5xx |\n| `LogMaxBodyLength` | Truncate long bodies |\n| `LogRouteWhitelist` / `LogRouteBlacklist` | Filter by route |\n| `LogHeaderBlacklist` | `Authorization` → `***REDACTED***` |\n| `LogQueryBlacklist` | Mask sensitive query params |\n| `LogJsonFieldSanitizeList` | `password` in JSON → `***REDACTED***` |\n\n### 🔐 Authentication\n\nFour modes, pick what fits:\n\n| Mode | How It Works | Best For |\n|------|-------------|----------|\n| `None` | No protection | Local development |\n| `DefaultJwt` | Set a password, username is fixed as `admin` | Personal / small teams |\n| `CustomJwt` | Custom username + password | Enterprise |\n| `ApiKey` | Pass API Key via Header | API integrations |\n\nThere's also an `AuthorizeRequest` delegate you can use to plug in your own auth system (highest priority).\n\n---\n\n## Configuration Reference\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n---\n\n## API Endpoints\n\n### Dashboard — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n> Default prefix is `apigateway`, customizable via `RoutePrefix`.\n\n---\n\n## Advanced Usage\n\n<details>\n<summary><b>🔐 Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n // Return true = allow access\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>🛡️ Dashboard + Zero-Config Client Registration</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the password — zero config on the client side:\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard password\n\n// Client Program.cs — no auth config needed at all\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>🔧 Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>📋 Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## About Aneiang.Yarp\n\nThe dashboard depends on [Aneiang.Yarp](https://www.nuget.org/packages/Aneiang.Yarp) core library, which provides:\n\n| Capability | Description |\n|------------|-------------|\n| 🚀 **Dynamic Routing API** | `/api/gateway` for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration Client** | `AddAneiangYarpClient()` — one line, registers on startup, unregisters on shutdown |\n| 👥 **Instance Isolation** | Multi-developer debugging with automatic namespace isolation |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost → LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | `enableRegistration: false` removes registration endpoints entirely |\n\nThe core library works independently — no dashboard required.\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp.Dashboard** | Dashboard | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "2a985a74c93fac7af8b481f950725c946b235db9": {"t": 1785763465.9962273, "n": 13368, "h": "3e2f9fdcd7936a78", "c": "# Aneiang.Yarp.Dashboard\n\n<div align=\"center\">\n\n**Give your YARP gateway a management dashboard — in just 3 lines of code**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[![YARP](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nUsing YARP as your gateway, but still editing `appsettings.json` by hand to manage routes?\n\n**Aneiang.Yarp.Dashboard** lets you manage YARP right in the browser — visual CRUD, JSON editor, one-click rollback, real-time logs. Install and go.\n\n```csharp\n// Program.cs — that's it\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line\n```\n\n---\n\n## Dashboard Preview\n\n<table>\n <tr>\n <td align=\"center\"><b>Cluster Management</b></td>\n <td align=\"center\"><b>Route Management</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-list.png\" alt=\"Cluster List\" width=\"480\"/></td>\n <td><img src=\"docs/route-list.png\" alt=\"Route List\" width=\"480\"/></td>\n </tr>\n <tr>\n <td align=\"center\"><b>JSON Editor</b></td>\n <td align=\"center\"><b>Request Logs</b></td>\n </tr>\n <tr>\n <td><img src=\"docs/cluster-create.png\" alt=\"Cluster Editor\" width=\"480\"/></td>\n <td><img src=\"docs/log-list.png\" alt=\"Request Logs\" width=\"480\"/></td>\n </tr>\n</table>\n\n<div align=\"center\">\n<img src=\"docs/overview.png\" alt=\"Overview\" width=\"720\"/>\n</div>\n\n---\n\n## Get Started in 30 Seconds\n\n### 1. Install NuGet Packages\n\n```bash\ndotnet add package Aneiang.Yarp\ndotnet add package Aneiang.Yarp.Dashboard\n```\n\n### 2. Update Program.cs\n\n```csharp\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard(); // ← add this line, done\n\nvar app = builder.Build();\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← add this line, captures request logs\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 3. Open the Browser\n\nVisit `http://localhost:5000/apigateway` — the dashboard is already there.\n\n### Want a Login? (Optional)\n\n```json\n// appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-password\"\n }\n }\n}\n```\n\nAdd the config, and the dashboard redirects to a login page. Default username: `admin`.\n\n---\n\n## What You Get\n\n| Feature | What It Does |\n|---------|-------------|\n| 📊 **Cluster Management** | Browse, create, edit, delete, rename clusters — or use the JSON editor |\n| 🛣️ **Route Management** | Full CRUD + JSON editor, supports standard YARP format |\n| 💾 **Config Import/Export** | One-click export of full YARP config, import with auto-validation |\n| ⏪ **Config Rollback** | Auto-snapshot on every change, one-click rollback if something breaks |\n| 📝 **Real-time Logs** | Full request/response capture, filter by route/status/TraceID |\n| 🔐 **Multi-mode Auth** | JWT login / API Key / custom delegate — works out of the box |\n| 🌐 **i18n** | Switch language at runtime, no restart needed |\n| 🛡️ **Log Sanitization** | Auto-mask Authorization, password, and other sensitive fields |\n| 📈 **Log Sampling** | Sample by rate in production to control log volume |\n\n**Design principle**: Zero intrusion — just 2 lines of code to enable, doesn't change your existing YARP config or proxy logic.\n\n---\n\n## Feature Details\n\n### 📊 Cluster & Route Management\n\nManage YARP clusters and routes right in the browser — no more hand-editing JSON config files:\n\n- **Form-based creation** — Fill in a few fields to create a route or cluster\n- **JSON editor** — Built-in code editor with syntax highlighting, live validation, auto-formatting\n- **Format compatibility** — Accepts both camelCase and PascalCase, paste official YARP config directly\n- **Smart association** — Creating a route whose target cluster doesn't exist? It auto-creates the cluster\n- **Safe deletion** — Optionally clean up orphaned clusters when deleting routes; deleting a cluster auto-updates references\n\n### 💾 Config Version Management\n\nBroke something? One-click rollback.\n\n- **Auto-snapshot** — Before every create/edit/delete, the system automatically saves a config snapshot\n- **Change history** — View timestamp, operator IP, and change content for each change\n- **One-click rollback** — Pick any historical version and roll back instantly\n- **Export** — Export in full YARP standard format, ready to paste into `appsettings.json`\n- **Import** — Import from JSON, auto-validate format, merge and persist\n\n### 📝 Real-time Request Logs\n\nSee what happens to every request passing through the gateway:\n\n- Request method, path, query parameters, headers\n- Response status code, headers\n- Request/response body (JSON auto-parsed)\n- Target cluster and route\n- Trace ID, request duration\n\n**Filtering**: by route ID / status code / Trace ID / time range\n\n**Security controls** (production-friendly):\n\n| Setting | One-liner |\n|---------|-----------|\n| `EnableProxyLogging` | Master switch — off stops the entire pipeline |\n| `EnableLogSampling` + `LogSamplingRate` | Sampling — 0.1 = only log 10% |\n| `LogErrorsOnly` | Only log 4xx/5xx |\n| `LogMaxBodyLength` | Truncate long bodies |\n| `LogRouteWhitelist` / `LogRouteBlacklist` | Filter by route |\n| `LogHeaderBlacklist` | `Authorization` → `***REDACTED***` |\n| `LogQueryBlacklist` | Mask sensitive query params |\n| `LogJsonFieldSanitizeList` | `password` in JSON → `***REDACTED***` |\n\n### 🔐 Authentication\n\nFour modes, pick what fits:\n\n| Mode | How It Works | Best For |\n|------|-------------|----------|\n| `None` | No protection | Local development |\n| `DefaultJwt` | Set a password, username is fixed as `admin` | Personal / small teams |\n| `CustomJwt` | Custom username + password | Enterprise |\n| `ApiKey` | Pass API Key via Header | API integrations |\n\nThere's also an `AuthorizeRequest` delegate you can use to plug in your own auth system (highest priority).\n\n---\n\n## Configuration Reference\n\nAll settings go under `Gateway:Dashboard`. Works out of the box without any config — every option has a default.\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\",\n\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\",\n \"JwtUsername\": \"admin\",\n \"JwtSecret\": \"...\",\n \"ApiKey\": \"your-api-key\",\n \"ApiKeyHeaderName\": \"X-Api-Key\",\n\n \"EnableLogSampling\": false,\n \"LogSamplingRate\": 1.0,\n \"LogErrorsOnly\": false,\n \"LogMaxBodyLength\": 8192,\n\n \"LogRouteWhitelist\": [],\n \"LogRouteBlacklist\": [],\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"],\n \"LogQueryBlacklist\": [],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"]\n }\n }\n}\n```\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `EnableProxyLogging` | `true` | Master switch for request logging |\n| `RoutePrefix` | `\"apigateway\"` | Dashboard URL prefix |\n| `Locale` | `\"en-US\"` | Default language, switchable at runtime |\n| `AuthMode` | `None` | `None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `JwtPassword` | null | JWT login password |\n| `JwtUsername` | null | CustomJwt username (DefaultJwt uses fixed `admin`) |\n| `JwtSecret` | null | JWT signing key — auto-generated if not set (resets on restart) |\n| `ApiKey` | null | API Key value |\n| `ApiKeyHeaderName` | `\"X-Api-Key\"` | Header name for API Key |\n| `EnableLogSampling` | `false` | Enable log sampling |\n| `LogSamplingRate` | `1.0` | Sampling rate 0.0–1.0 |\n| `LogErrorsOnly` | `false` | Only log errors |\n| `LogMaxBodyLength` | `8192` | Max body length in bytes |\n| `LogRouteWhitelist` | null | Route whitelist |\n| `LogRouteBlacklist` | null | Route blacklist |\n| `LogHeaderBlacklist` | null | Header sanitization list |\n| `LogQueryBlacklist` | null | Query param sanitization list |\n| `LogJsonFieldSanitizeList` | null | JSON field sanitization list |\n\n---\n\n## API Endpoints\n\n### Dashboard — `/{RoutePrefix}`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/{prefix}` | GET | Dashboard home |\n| `/{prefix}/login` | GET/POST | Login page / authenticate |\n| `/{prefix}/logout` | POST | Logout |\n| `/{prefix}/info` | GET | Gateway runtime info |\n| `/{prefix}/clusters` | GET | Cluster list |\n| `/{prefix}/routes` | GET | Route list |\n| `/{prefix}/logs` | GET/DELETE | Query logs / clear all |\n| `/{prefix}/auth/status` | GET | Auth status |\n\n### Config Management — `/{RoutePrefix}/api/config`\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/config/export` | GET | Export full YARP config |\n| `/api/config/import` | POST | Import config (validate + snapshot + persist) |\n| `/api/config/validate` | POST | Validate config format |\n| `/api/config/history` | GET | Change history |\n| `/api/config/rollback/{id}` | POST | Rollback to a specific version |\n| `/api/config/routes/{id}` | PUT/DELETE | Create/update / delete a route |\n| `/api/config/clusters/{id}` | PUT/DELETE | Create/update / delete a cluster |\n| `/api/config/clusters/{id}/rename` | PUT | Rename a cluster |\n\n> Default prefix is `apigateway`, customizable via `RoutePrefix`.\n\n---\n\n## Advanced Usage\n\n<details>\n<summary><b>🔐 Custom Authorization — Plug Into Your Own Auth System</b></summary>\n\n```csharp\nbuilder.Services.AddAneiangYarpDashboard(options =>\n{\n options.AuthorizeRequest = async (context) =>\n {\n // Return true = allow access\n return context.User.Identity?.IsAuthenticated == true\n && context.User.IsInRole(\"GatewayAdmin\");\n };\n});\n```\n\nPriority: `AuthorizeRequest` > `ApiKey` > `JWT` > `None`\n\n</details>\n\n<details>\n<summary><b>🛡️ Dashboard + Zero-Config Client Registration</b></summary>\n\nWhen the gateway has Dashboard auth configured, `AddGatewayApiAuth()` auto-reads the password — zero config on the client side:\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard password\n\n// Client Program.cs — no auth config needed at all\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>🔧 Middleware Order</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.UseAneiangYarpDashboard(); // ← after UseRouting\napp.MapControllers();\napp.MapReverseProxy(); // ← must be last\n```\n\nThe middleware captures YARP proxy request/response data and automatically skips dashboard's own requests.\n\n</details>\n\n<details>\n<summary><b>📋 Production Recommended Config</b></summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1,\n \"LogErrorsOnly\": true,\n \"LogMaxBodyLength\": 4096,\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n</details>\n\n---\n\n## About Aneiang.Yarp\n\nThe dashboard depends on [Aneiang.Yarp](https://www.nuget.org/packages/Aneiang.Yarp) core library, which provides:\n\n| Capability | Description |\n|------------|-------------|\n| 🚀 **Dynamic Routing API** | `/api/gateway` for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration Client** | `AddAneiangYarpClient()` — one line, registers on startup, unregisters on shutdown |\n| 👥 **Instance Isolation** | Multi-developer debugging with automatic namespace isolation |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost → LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | `enableRegistration: false` removes registration endpoints entirely |\n\nThe core library works independently — no dashboard required.\n\n---\n\n## Sample Projects\n\n```bash\n# Start the gateway (with dashboard)\ndotnet run --project samples/SampleGateway\n\n# Start the client (auto-registers to gateway)\ndotnet run --project samples/SampleLocalService\n\n# Test\ncurl http://localhost:5000/api/your-endpoint\n```\n\nDashboard: `/apigateway`, login: `admin` / `demo123`\n\n---\n\n## NuGet\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp.Dashboard** | Dashboard | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n\n**Supports** .NET 8.0 / .NET 9.0 · YARP 2.3.0\n\n---\n\n## License\n\n[MIT](LICENSE) — use it however you like.\n\n---\n\n<div align=\"center\">\n\nFind it useful? [⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) to help others find it\n\n</div>\n"}, "9f6cc855ab2d8941f2a7165ae434230cb6c134e8": {"t": 1785763469.3096018, "n": 19572, "h": "1019a7337859310c", "c": "# Aneiang.Yarp\n\n<div align=\"center\">\n\n**Dynamic Routing Gateway Management Library for .NET**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![License](https://img.shields.io/github/license/aneiang/Aneiang.Yarp.svg)](LICENSE)\n[![YARP Version](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nA powerful **dynamic routing gateway management library** built on [Microsoft YARP](https://github.com/microsoft/reverse-proxy), providing runtime route registration, automatic service discovery, real-time monitoring dashboard, while preserving all YARP reverse proxy capabilities.\n\n## 📦 Project Architecture: Two Independent NuGet Packages\n\nAneiang.Yarp uses a **modular design** with core functionality completely decoupled from the dashboard:\n\n```\n┌─────────────────────────────────────────────────┐\n│ Aneiang.Yarp.Dashboard │\n│ (Optional: Monitoring & Operations UI) │\n│ • Cluster/Route visualization │\n│ • Real-time request log capture │\n│ • JWT login authentication │\n└─────────────────────────────────────────────────┘\n ▲\n │ Optional dependency\n │\n┌─────────────────────────────────────────────────┐\n│ Aneiang.Yarp (Core Library) │\n│ (Independent: Dynamic Gateway Capabilities) │\n│ • Dynamic routing API │\n│ • Auto-registration client │\n│ • YARP reverse proxy enhancements │\n└─────────────────────────────────────────────────┘\n```\n\n**Core Design Principles:**\n- ✅ **Aneiang.Yarp works independently**: Runs fully without Dashboard\n- ✅ **Dashboard is an optional plugin**: Install and use, or skip it entirely\n- ✅ **Flexible combination**: Choose based on your needs\n\n---\n\n## 🎯 Core Library: Aneiang.Yarp\n\n**Aneiang.Yarp** is the core library providing complete dynamic gateway management capabilities. **It can run completely independently without Dashboard.**\n\n### Features\n\n| Feature | Description |\n|---------|-------------|\n| 🚀 **Dynamic Routing** | REST API for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration** | Services auto-register on startup & unregister on shutdown — **1 line of code** |\n| 👥 **Instance Isolation** | Automatic namespace isolation for multi-developer debugging |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost to LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | Enable/disable registration API via `enableRegistration` parameter |\n\n### Quick Start (Core Library Only)\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ One-line gateway setup (no Dashboard dependency)\nbuilder.Services.AddAneiangYarp();\n\nvar app = builder.Build();\napp.MapReverseProxy();\napp.Run();\n```\n\n---\n\n## 🌟 Optional Plugin: Aneiang.Yarp.Dashboard\n\n**Aneiang.Yarp.Dashboard** is the **recommended product** providing a comprehensive monitoring and operations UI. **It's optional — install it for enhanced visibility, or skip it for lightweight deployments.**\n\n### Features\n\n| Feature | Description |\n|---------|-------------|\n| 📊 **Cluster Status** | Real-time view of all service clusters and health checks |\n| 🛣️ **Route Management** | Visualize route rules with expandable configuration details |\n| 📝 **Real-time Logs** | Capture YARP forwarding logs and request/response details |\n| 🔐 **Multi-Mode Auth** | JWT login, API Key, or custom delegate authentication |\n| 🌐 **i18n Support** | Runtime language switching: English / Chinese |\n| 🔍 **Advanced Filtering** | Filter logs by route, status code, trace ID |\n| 🛡️ **Log Sanitization** | Automatic sensitive data masking (headers, JSON fields) |\n| 📈 **Log Sampling** | Configurable sampling rate for production environments |\n| 🎨 **Enhanced UI** | Search, filter, expand/collapse, copy-to-clipboard |\n\n### Enable Dashboard\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// Enable core gateway\nbuilder.Services.AddAneiangYarp();\n\n// Enable monitoring dashboard (optional)\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\nAccess dashboard at: `http://localhost:5000/apigateway`\n\n### Dashboard Configuration Reference\n\nAll Dashboard settings are configured under `Gateway:Dashboard` in `appsettings.json`:\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n // Basic Settings\n \"EnableProxyLogging\": true, // Enable request/response logging (default: true)\n \"RoutePrefix\": \"apigateway\", // Dashboard URL prefix (default: \"apigateway\")\n \"Locale\": \"zh-CN\", // UI language: \"zh-CN\" or \"en-US\" (default: \"zh-CN\")\n \n // Authentication (choose one mode)\n \"AuthMode\": \"DefaultJwt\", // None | ApiKey | CustomJwt | DefaultJwt\n \"JwtPassword\": \"your-strong-password\", // Required for JWT modes\n \"JwtUsername\": \"admin\", // Only for CustomJwt mode\n \"JwtSecret\": \"...\", // Auto-generated if not set (changes on restart)\n \"ApiKey\": \"your-api-key\", // Only for ApiKey mode\n \"ApiKeyHeaderName\": \"X-Api-Key\", // Header name for ApiKey mode (default: \"X-Api-Key\")\n \n // Log Sampling & Filtering (Production Safety)\n \"EnableLogSampling\": false, // Enable log sampling (default: false)\n \"LogSamplingRate\": 1.0, // Sampling rate 0.0-1.0 (default: 1.0 = all)\n \"LogErrorsOnly\": false, // Only log error requests (status >= 400)\n \"LogMaxBodyLength\": 8192, // Max body size to log in bytes (default: 8KB)\n \n // Route Filtering\n \"LogRouteWhitelist\": [], // Only log these route IDs (empty = all)\n \"LogRouteBlacklist\": [], // Exclude these route IDs from logging\n \n // Content & Header Filtering\n \"LogContentTypeWhitelist\": [], // Content types to log body for (empty = JSON only)\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\"], // Headers to exclude\n \"LogQueryBlacklist\": [], // Query params to exclude from logging\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"api-key\"] // Fields to sanitize\n }\n }\n}\n```\n\n### Production Security Recommendations\n\n**For production environments, consider these settings:**\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"very-strong-password-here\",\n \n // Reduce log volume in production\n \"EnableLogSampling\": true,\n \"LogSamplingRate\": 0.1, // Log only 10% of requests\n \"LogErrorsOnly\": true, // Only log errors\n \n // Protect sensitive data\n \"LogMaxBodyLength\": 4096, // Limit body logging to 4KB\n \"LogHeaderBlacklist\": [\"Authorization\", \"Cookie\", \"Set-Cookie\", \"X-Api-Key\"],\n \"LogJsonFieldSanitizeList\": [\"password\", \"token\", \"secret\", \"apikey\", \"creditCard\", \"ssn\"]\n }\n }\n}\n```\n\n---\n\n## 🚀 Client Service: Auto-Registration\n\n> **Note**: Auto-registration requires network connectivity between the client service and gateway. This feature is primarily designed for **development and debugging scenarios**.\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ One line: auto-register on startup → auto-unregister on shutdown\nbuilder.Services.AddAneiangYarpClient();\n\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n**`appsettings.json`** (minimum config):\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\nThat's it! Your service will automatically register to the gateway on startup and unregister on shutdown.\n\n---\n\n## 📦 NuGet Packages\n\n| Package | Description | Independent? | Link |\n|---------|-------------|--------------|------|\n| **Aneiang.Yarp** | Core library: dynamic routing + auto-registration client | ✅ Yes | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Dashboard** | 🌟 **Recommended**: Dashboard for monitoring & operations | ❌ Requires core | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**Requirements:**\n- Target Framework: `.NET 8.0` / `.NET 9.0`\n- YARP Version: `2.3.0`\n\n---\n\n## 📸 Dashboard Screenshots\n\n### Cluster Status\n\n![Cluster List](docs/cluster-list.png)\n\n### Route Configuration\n\n![Route List](docs/route-list.png)\n\n### Log Viewer\n\n![Log List](docs/log-list.png)\n\n---\n\n## ⚡ Advanced Usage\n\n<details>\n<summary><b>🔗 Multi-Level Gateway Chain</b></summary>\n\n```csharp\n// Intranet gateway also registers to external gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpClient(o =>\n{\n o.GatewayUrl = \"http://outer-gateway:5000\";\n});\n```\n\n</details>\n\n<details>\n<summary><b>🛡️ Gateway API Authorization (Optional)</b></summary>\n\n> **Important**: `AddGatewayApiAuth()` is **optional**. Without it, registration APIs are publicly accessible.\n\n**When to use:**\n- ✅ **Call it**: Production environment, public network, need access control\n- ❌ **Skip it**: Local development, isolated intranet (network-level protection)\n\n```csharp\n// Option 1: Auto-detect from Dashboard config (recommended)\nbuilder.Services.AddGatewayApiAuth();\n\n// Option 2: Explicit configuration\nbuilder.Services.AddGatewayApiAuth(o =>\n{\n o.Mode = GatewayApiAuthMode.BasicAuth;\n o.Username = \"admin\";\n o.Password = \"admin@2026\";\n});\n```\n\nConfig file:\n```json\n{\n \"Gateway\": {\n \"ApiAuth\": {\n \"Mode\": \"BasicAuth\",\n \"Username\": \"admin\",\n \"Password\": \"admin@2026\"\n }\n }\n}\n```\n\n**Configuration Priority** (later overrides earlier):\n1. `Gateway:ApiAuth` config section\n2. Auto-detect from `Gateway:Dashboard` (if Dashboard JWT password configured)\n3. `configureOptions` callback (highest precedence)\n\n**Auto-Detection Logic:**\nWhen `AddGatewayApiAuth()` is called without explicit configuration:\n- If `Gateway:Dashboard:JwtPassword` exists → automatically uses BasicAuth with username `admin` and password from `JwtPassword`\n- This enables zero-config client auto-registration when Dashboard auth is configured\n\n</details>\n\n<details>\n<summary><b>🔐 Auto-Registration Authorization (3 Scenarios)</b></summary>\n\nWhen clients auto-register to gateway, authentication can be configured in three ways:\n\n| Scenario | Gateway Config | Client Config | Description |\n|----------|---------------|---------------|-------------|\n| **1: Aneiang.Yarp only** | Explicit API auth | Manual credentials | Requires explicit config |\n| **2: Dashboard (auth enabled)** | Dashboard JWT/ApiKey | **Auto-read** from Dashboard config | **Zero-config** (recommended) |\n| **3: Dashboard (no auth)** | No auth configured | No config needed | Local dev only |\n\n**Scenario 2 Example (Recommended):**\n```json\n// Gateway appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\"\n }\n }\n}\n```\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard config\n\n// Client Program.cs - ZERO configuration needed!\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>👥 Instance Isolation (Multi-Developer)</b></summary>\n\n**Enabled by default** — automatically embeds machine identifier into routes:\n\n| Dimension | DevA (PC-JOHN) | DevB (PC-JANE) |\n|-----------|----------------|----------------|\n| routeName | `my-service-PC-JOHN` | `my-service-PC-JANE` |\n| matchPath | `/PC-JOHN/api/{**catch-all}` | `/PC-JANE/api/{**catch-all}` |\n\n**Special Handling:**\n- Automatically detects machine name as instance ID\n- Prevents route conflicts when multiple developers test against same gateway\n- Instance prefix is stripped before forwarding to downstream service\n\nCustom instance ID:\n```csharp\nbuilder.Services.AddAneiangYarpClient(options =>\n{\n options.InstanceId = \"dev-john\";\n options.InstancePrefixFormat = \"dev-{instanceId}\";\n});\n```\n\nDisable if not needed:\n```csharp\noptions.InstanceIsolation = false;\n```\n\n</details>\n\n<details>\n<summary><b>🌐 Automatic LAN IP Resolution</b></summary>\n\nWhen `DestinationAddress` contains `localhost` / `127.0.0.1` / `0.0.0.0`:\n\n```\nConfigured: http://localhost:5001\nResolved: http://192.168.1.101:5001 (LAN IP)\n```\n\n**Special Handling:**\n- Automatically detects local loopback addresses\n- Resolves to first available LAN IP for cross-machine accessibility\n- Critical for auto-registration: other machines can reach the service\n\nDisable: `AutoResolveIp = false`\n\n</details>\n\n<details>\n<summary><b>🚪 Conditional API Exposure (enableRegistration)</b></summary>\n\nControl whether the gateway exposes dynamic route registration APIs:\n\n```csharp\n// Enable registration API (default)\nbuilder.Services.AddAneiangYarp(enableRegistration: true);\n\n// Disable registration API (security hardening)\nbuilder.Services.AddAneiangYarp(enableRegistration: false);\n```\n\n**Special Handling:**\n- When `enableRegistration = false`, `GatewayConfigController` is completely removed from MVC application model\n- Returns **404 Not Found** (not 401/403) — no endpoint exists at all\n- Recommended for production gateways that should not accept external route changes\n- Does NOT affect YARP proxy functionality or Dashboard access\n\n</details>\n\n<details>\n<summary><b>🔧 Custom Middleware Pipeline</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRequestLogging();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.MapControllers();\napp.MapReverseProxy(); // Must be last\n```\n\n</details>\n\n---\n\n## 📖 Configuration\n\n<details>\n<summary><b>Gateway:Registration</b> — Full Configuration Options</summary>\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\",\n \"RouteName\": \"my-service\",\n \"ClusterName\": \"my-service-cluster\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"DestinationAddress\": \"http://localhost:5001\",\n \"Order\": 50,\n \"AutoResolveIp\": true,\n \"TimeoutSeconds\": 10,\n \"InstanceIsolation\": true,\n \"InstanceId\": \"john\",\n \"InstancePrefixFormat\": \"{instanceId}\",\n \"StripInstancePrefix\": true,\n \"DownstreamPathPrefix\": null,\n \"Transforms\": [],\n \"AuthToken\": null,\n \"ApiKey\": null,\n \"BasicAuthUsername\": null,\n \"BasicAuthPassword\": null\n }\n }\n}\n```\n\n**Configuration Priority:** Code `options => {}` > Environment Variables > `appsettings.json`\n\n</details>\n\n<details>\n<summary><b>Gateway:Dashboard</b> — Dashboard Configuration</summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"demo123\",\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\"\n }\n }\n}\n```\n\n**Auth Modes:** `None` | `ApiKey` | `CustomJwt` | `DefaultJwt`\n\n**Special Handling:**\n- JWT tokens expire in **8 hours** by default\n- Login via `POST /apigateway/login` to get token\n- When `AuthMode` is configured, `AddGatewayApiAuth()` can auto-detect credentials from `JwtPassword`\n- `RoutePrefix` allows customizing the dashboard URL path (default: `apigateway`)\n- `Locale` supports runtime language switching: `en-US` | `zh-CN`\n\n</details>\n\n<details>\n<summary><b>ReverseProxy</b> — Standard YARP Configuration</b></summary>\n\n```json\n{\n \"ReverseProxy\": {\n \"Routes\": {\n \"my-route\": {\n \"ClusterId\": \"my-cluster\",\n \"Match\": \"/api/test/{**catch-all}\"\n }\n },\n \"Clusters\": {\n \"my-cluster\": {\n \"Destinations\": {\n \"d1\": { \"Address\": \"http://localhost:5000/\" }\n },\n \"LoadBalancingPolicy\": \"PowerOfTwoChoices\"\n }\n }\n }\n}\n```\n\nSee [YARP docs](https://microsoft.github.io/reverse-proxy/articles/config-files.html) for details.\n\n</details>\n\n---\n\n## 🔌 API Endpoints\n\n<details>\n<summary><b>Gateway API</b> — <code>/api/gateway</code></summary>\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/gateway/register-route` | `POST` | Register/update route |\n| `/api/gateway/{routeName}` | `DELETE` | Unregister route |\n| `/api/gateway/routes` | `GET` | Query all routes |\n| `/api/gateway/ping` | `GET` | Health check |\n\n**Request Example:**\n```json\n{\n \"routeName\": \"my-service\",\n \"clusterName\": \"my-service-cluster\",\n \"matchPath\": \"/api/my-service/{**catch-all}\",\n \"destinationAddress\": \"http://192.168.1.101:5001\",\n \"order\": 50,\n \"transforms\": [\n { \"PathSet\": \"/api/backend/{**catch-all}\" }\n ]\n}\n```\n\n**Response Format:**\n```json\n{ \"code\": 200, \"message\": \"Route registered successfully\" }\n```\n\n</details>\n\n<details>\n<summary><b>Dashboard API</b> — <code>/apigateway</code></summary>\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /apigateway` | Dashboard home page |\n| `GET /apigateway/login` | Login page |\n| `POST /apigateway/login` | Login (returns JWT token) |\n| `GET /apigateway/info` | Gateway runtime info |\n| `GET /apigateway/clusters` | Cluster status |\n| `GET /apigateway/routes` | Route configuration |\n| `GET /apigateway/routes/{routeId}` | Route details |\n| `GET /apigateway/logs` | Recent YARP logs |\n| `DELETE /apigateway/logs` | Clear logs |\n\n</details>\n\n---\n\n## 📂 Project Structure\n\n```\nAneiang.Yarp/\n├── src/\n│ ├── Aneiang.Yarp/ # Core library\n│ │ ├── Controllers/ # Gateway REST API\n│ │ ├── Extensions/ # DI registration\n│ │ ├── Models/ # DTOs & Options\n│ │ └── Services/ # Core services\n│ │\n│ └── Aneiang.Yarp.Dashboard/ # Dashboard library\n│ ├── Controllers/ # Dashboard API\n│ ├── Views/ # Razor views\n│ ├── Services/ # Dashboard services\n│ ├── Models/ # Dashboard models\n│ └── Extensions/ # DI registration\n│\n└── samples/\n ├── SampleGateway/ # Gateway example\n └── SampleLocalService/ # Client example\n```\n\n---\n\n## 🧪 Sample Projects\n\n```bash\n# Terminal 1: Start gateway\ndotnet run --project samples/SampleGateway\n\n# Terminal 2: Start local service (auto-registers)\ndotnet run --project samples/SampleLocalService\n\n# Test the route\ncurl http://localhost:5000/api/your-endpoint\n```\n\n**Sample Gateway Features:**\n- Dashboard at `/apigateway` (login: `admin` / `demo123`)\n- Serilog logging\n- JWT authentication\n\n---\n\n## 📄 License\n\nThis project is licensed under the [MIT License](LICENSE).\n\n---\n\n<div align=\"center\">\n\n**Made with ❤️ for the .NET community**\n\n[⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) if you find it useful!\n\n</div>\n"}, "f38d027d4db801b5100b078463ce256158dc7184": {"t": 1785763471.0628946, "n": 16462, "h": "f37cba415878dfb2", "c": "# Aneiang.Yarp\n\n<div align=\"center\">\n\n**Dynamic Routing Gateway Management Library for .NET**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![License](https://img.shields.io/github/license/aneiang/Aneiang.Yarp.svg)](LICENSE)\n[![YARP Version](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nA powerful **dynamic routing gateway management library** built on [Microsoft YARP](https://github.com/microsoft/reverse-proxy), providing runtime route registration, automatic service discovery, real-time monitoring dashboard, while preserving all YARP reverse proxy capabilities.\n\n## 📦 Project Architecture: Two Independent NuGet Packages\n\nAneiang.Yarp uses a **modular design** with core functionality completely decoupled from the dashboard:\n\n```\n┌─────────────────────────────────────────────────┐\n│ Aneiang.Yarp.Dashboard │\n│ (Optional: Monitoring & Operations UI) │\n│ • Cluster/Route visualization │\n│ • Real-time request log capture │\n│ • JWT login authentication │\n└─────────────────────────────────────────────────┘\n ▲\n │ Optional dependency\n │\n┌─────────────────────────────────────────────────┐\n│ Aneiang.Yarp (Core Library) │\n│ (Independent: Dynamic Gateway Capabilities) │\n│ • Dynamic routing API │\n│ • Auto-registration client │\n│ • YARP reverse proxy enhancements │\n└─────────────────────────────────────────────────┘\n```\n\n**Core Design Principles:**\n- ✅ **Aneiang.Yarp works independently**: Runs fully without Dashboard\n- ✅ **Dashboard is an optional plugin**: Install and use, or skip it entirely\n- ✅ **Flexible combination**: Choose based on your needs\n\n---\n\n## 🎯 Core Library: Aneiang.Yarp\n\n**Aneiang.Yarp** is the core library providing complete dynamic gateway management capabilities. **It can run completely independently without Dashboard.**\n\n### Features\n\n| Feature | Description |\n|---------|-------------|\n| 🚀 **Dynamic Routing** | REST API for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration** | Services auto-register on startup & unregister on shutdown — **1 line of code** |\n| 👥 **Instance Isolation** | Automatic namespace isolation for multi-developer debugging |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost to LAN IP |\n| 🛡️ **API Authorization** | Optional BasicAuth/ApiKey protection for registration APIs |\n| 🚪 **Conditional API Exposure** | Enable/disable registration API via `enableRegistration` parameter |\n\n### Quick Start (Core Library Only)\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ One-line gateway setup (no Dashboard dependency)\nbuilder.Services.AddAneiangYarp();\n\nvar app = builder.Build();\napp.MapReverseProxy();\napp.Run();\n```\n\n---\n\n## 🌟 Optional Plugin: Aneiang.Yarp.Dashboard\n\n**Aneiang.Yarp.Dashboard** is the **recommended product** providing a comprehensive monitoring and operations UI. **It's optional — install it for enhanced visibility, or skip it for lightweight deployments.**\n\n### Features\n\n| Feature | Description |\n|---------|-------------|\n| 📊 **Cluster Status** | Real-time view of all service clusters and health checks |\n| 🛣️ **Route Management** | Visualize route rules with expandable configuration details |\n| 📝 **Real-time Logs** | Capture YARP forwarding logs and request/response details |\n| 🔐 **Multi-Mode Auth** | JWT login, API Key, or custom delegate authentication |\n| 🌐 **i18n Support** | Runtime language switching: English / Chinese |\n\n### Enable Dashboard\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// Enable core gateway\nbuilder.Services.AddAneiangYarp();\n\n// Enable monitoring dashboard (optional)\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\nAccess dashboard at: `http://localhost:5000/apigateway`\n---\n\n## 🚀 Client Service: Auto-Registration\n\n> **Note**: Auto-registration requires network connectivity between the client service and gateway. This feature is primarily designed for **development and debugging scenarios**.\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ One line: auto-register on startup → auto-unregister on shutdown\nbuilder.Services.AddAneiangYarpClient();\n\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n**`appsettings.json`** (minimum config):\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\nThat's it! Your service will automatically register to the gateway on startup and unregister on shutdown.\n\n---\n\n## 📦 NuGet Packages\n\n| Package | Description | Independent? | Link |\n|---------|-------------|--------------|------|\n| **Aneiang.Yarp** | Core library: dynamic routing + auto-registration client | ✅ Yes | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Dashboard** | 🌟 **Recommended**: Dashboard for monitoring & operations | ❌ Requires core | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**Requirements:**\n- Target Framework: `.NET 8.0` / `.NET 9.0`\n- YARP Version: `2.3.0`\n\n---\n\n## 📸 Dashboard Screenshots\n\n### Cluster Status\n\n![Cluster List](docs/cluster-list.png)\n\n### Route Configuration\n\n![Route List](docs/route-list.png)\n\n### Log Viewer\n\n![Log List](docs/log-list.png)\n\n---\n\n## ⚡ Advanced Usage\n\n<details>\n<summary><b>🔗 Multi-Level Gateway Chain</b></summary>\n\n```csharp\n// Intranet gateway also registers to external gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpClient(o =>\n{\n o.GatewayUrl = \"http://outer-gateway:5000\";\n});\n```\n\n</details>\n\n<details>\n<summary><b>🛡️ Gateway API Authorization (Optional)</b></summary>\n\n> **Important**: `AddGatewayApiAuth()` is **optional**. Without it, registration APIs are publicly accessible.\n\n**When to use:**\n- ✅ **Call it**: Production environment, public network, need access control\n- ❌ **Skip it**: Local development, isolated intranet (network-level protection)\n\n```csharp\n// Option 1: Auto-detect from Dashboard config (recommended)\nbuilder.Services.AddGatewayApiAuth();\n\n// Option 2: Explicit configuration\nbuilder.Services.AddGatewayApiAuth(o =>\n{\n o.Mode = GatewayApiAuthMode.BasicAuth;\n o.Username = \"admin\";\n o.Password = \"admin@2026\";\n});\n```\n\nConfig file:\n```json\n{\n \"Gateway\": {\n \"ApiAuth\": {\n \"Mode\": \"BasicAuth\",\n \"Username\": \"admin\",\n \"Password\": \"admin@2026\"\n }\n }\n}\n```\n\n**Configuration Priority** (later overrides earlier):\n1. `Gateway:ApiAuth` config section\n2. Auto-detect from `Gateway:Dashboard` (if Dashboard JWT password configured)\n3. `configureOptions` callback (highest precedence)\n\n**Auto-Detection Logic:**\nWhen `AddGatewayApiAuth()` is called without explicit configuration:\n- If `Gateway:Dashboard:JwtPassword` exists → automatically uses BasicAuth with username `admin` and password from `JwtPassword`\n- This enables zero-config client auto-registration when Dashboard auth is configured\n\n</details>\n\n<details>\n<summary><b>🔐 Auto-Registration Authorization (3 Scenarios)</b></summary>\n\nWhen clients auto-register to gateway, authentication can be configured in three ways:\n\n| Scenario | Gateway Config | Client Config | Description |\n|----------|---------------|---------------|-------------|\n| **1: Aneiang.Yarp only** | Explicit API auth | Manual credentials | Requires explicit config |\n| **2: Dashboard (auth enabled)** | Dashboard JWT/ApiKey | **Auto-read** from Dashboard config | **Zero-config** (recommended) |\n| **3: Dashboard (no auth)** | No auth configured | No config needed | Local dev only |\n\n**Scenario 2 Example (Recommended):**\n```json\n// Gateway appsettings.json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"your-strong-password\"\n }\n }\n}\n```\n\n```csharp\n// Gateway Program.cs\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // Auto-reads Dashboard config\n\n// Client Program.cs - ZERO configuration needed!\nbuilder.Services.AddAneiangYarpClient();\n```\n\n</details>\n\n<details>\n<summary><b>👥 Instance Isolation (Multi-Developer)</b></summary>\n\n**Enabled by default** — automatically embeds machine identifier into routes:\n\n| Dimension | DevA (PC-JOHN) | DevB (PC-JANE) |\n|-----------|----------------|----------------|\n| routeName | `my-service-PC-JOHN` | `my-service-PC-JANE` |\n| matchPath | `/PC-JOHN/api/{**catch-all}` | `/PC-JANE/api/{**catch-all}` |\n\n**Special Handling:**\n- Automatically detects machine name as instance ID\n- Prevents route conflicts when multiple developers test against same gateway\n- Instance prefix is stripped before forwarding to downstream service\n\nCustom instance ID:\n```csharp\nbuilder.Services.AddAneiangYarpClient(options =>\n{\n options.InstanceId = \"dev-john\";\n options.InstancePrefixFormat = \"dev-{instanceId}\";\n});\n```\n\nDisable if not needed:\n```csharp\noptions.InstanceIsolation = false;\n```\n\n</details>\n\n<details>\n<summary><b>🌐 Automatic LAN IP Resolution</b></summary>\n\nWhen `DestinationAddress` contains `localhost` / `127.0.0.1` / `0.0.0.0`:\n\n```\nConfigured: http://localhost:5001\nResolved: http://192.168.1.101:5001 (LAN IP)\n```\n\n**Special Handling:**\n- Automatically detects local loopback addresses\n- Resolves to first available LAN IP for cross-machine accessibility\n- Critical for auto-registration: other machines can reach the service\n\nDisable: `AutoResolveIp = false`\n\n</details>\n\n<details>\n<summary><b>🚪 Conditional API Exposure (enableRegistration)</b></summary>\n\nControl whether the gateway exposes dynamic route registration APIs:\n\n```csharp\n// Enable registration API (default)\nbuilder.Services.AddAneiangYarp(enableRegistration: true);\n\n// Disable registration API (security hardening)\nbuilder.Services.AddAneiangYarp(enableRegistration: false);\n```\n\n**Special Handling:**\n- When `enableRegistration = false`, `GatewayConfigController` is completely removed from MVC application model\n- Returns **404 Not Found** (not 401/403) — no endpoint exists at all\n- Recommended for production gateways that should not accept external route changes\n- Does NOT affect YARP proxy functionality or Dashboard access\n\n</details>\n\n<details>\n<summary><b>🔧 Custom Middleware Pipeline</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRequestLogging();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.MapControllers();\napp.MapReverseProxy(); // Must be last\n```\n\n</details>\n\n---\n\n## 📖 Configuration\n\n<details>\n<summary><b>Gateway:Registration</b> — Full Configuration Options</summary>\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\",\n \"RouteName\": \"my-service\",\n \"ClusterName\": \"my-service-cluster\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"DestinationAddress\": \"http://localhost:5001\",\n \"Order\": 50,\n \"AutoResolveIp\": true,\n \"TimeoutSeconds\": 10,\n \"InstanceIsolation\": true,\n \"InstanceId\": \"john\",\n \"InstancePrefixFormat\": \"{instanceId}\",\n \"StripInstancePrefix\": true,\n \"DownstreamPathPrefix\": null,\n \"Transforms\": [],\n \"AuthToken\": null,\n \"ApiKey\": null,\n \"BasicAuthUsername\": null,\n \"BasicAuthPassword\": null\n }\n }\n}\n```\n\n**Configuration Priority:** Code `options => {}` > Environment Variables > `appsettings.json`\n\n</details>\n\n<details>\n<summary><b>Gateway:Dashboard</b> — Dashboard Configuration</summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"demo123\",\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\"\n }\n }\n}\n```\n\n**Auth Modes:** `None` | `ApiKey` | `CustomJwt` | `DefaultJwt`\n\n**Special Handling:**\n- JWT tokens expire in **8 hours** by default\n- Login via `POST /apigateway/login` to get token\n- When `AuthMode` is configured, `AddGatewayApiAuth()` can auto-detect credentials from `JwtPassword`\n- `RoutePrefix` allows customizing the dashboard URL path (default: `apigateway`)\n- `Locale` supports runtime language switching: `en-US` | `zh-CN`\n\n</details>\n\n<details>\n<summary><b>ReverseProxy</b> — Standard YARP Configuration</b></summary>\n\n```json\n{\n \"ReverseProxy\": {\n \"Routes\": {\n \"my-route\": {\n \"ClusterId\": \"my-cluster\",\n \"Match\": \"/api/test/{**catch-all}\"\n }\n },\n \"Clusters\": {\n \"my-cluster\": {\n \"Destinations\": {\n \"d1\": { \"Address\": \"http://localhost:5000/\" }\n },\n \"LoadBalancingPolicy\": \"PowerOfTwoChoices\"\n }\n }\n }\n}\n```\n\nSee [YARP docs](https://microsoft.github.io/reverse-proxy/articles/config-files.html) for details.\n\n</details>\n\n---\n\n## 🔌 API Endpoints\n\n<details>\n<summary><b>Gateway API</b> — <code>/api/gateway</code></summary>\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/gateway/register-route` | `POST` | Register/update route |\n| `/api/gateway/{routeName}` | `DELETE` | Unregister route |\n| `/api/gateway/routes` | `GET` | Query all routes |\n| `/api/gateway/ping` | `GET` | Health check |\n\n**Request Example:**\n```json\n{\n \"routeName\": \"my-service\",\n \"clusterName\": \"my-service-cluster\",\n \"matchPath\": \"/api/my-service/{**catch-all}\",\n \"destinationAddress\": \"http://192.168.1.101:5001\",\n \"order\": 50,\n \"transforms\": [\n { \"PathSet\": \"/api/backend/{**catch-all}\" }\n ]\n}\n```\n\n**Response Format:**\n```json\n{ \"code\": 200, \"message\": \"Route registered successfully\" }\n```\n\n</details>\n\n<details>\n<summary><b>Dashboard API</b> — <code>/apigateway</code></summary>\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /apigateway` | Dashboard home page |\n| `GET /apigateway/login` | Login page |\n| `POST /apigateway/login` | Login (returns JWT token) |\n| `GET /apigateway/info` | Gateway runtime info |\n| `GET /apigateway/clusters` | Cluster status |\n| `GET /apigateway/routes` | Route configuration |\n| `GET /apigateway/routes/{routeId}` | Route details |\n| `GET /apigateway/logs` | Recent YARP logs |\n| `DELETE /apigateway/logs` | Clear logs |\n\n</details>\n\n---\n\n## 📂 Project Structure\n\n```\nAneiang.Yarp/\n├── src/\n│ ├── Aneiang.Yarp/ # Core library\n│ │ ├── Controllers/ # Gateway REST API\n│ │ ├── Extensions/ # DI registration\n│ │ ├── Models/ # DTOs & Options\n│ │ └── Services/ # Core services\n│ │\n│ └── Aneiang.Yarp.Dashboard/ # Dashboard library\n│ ├── Controllers/ # Dashboard API\n│ ├── Views/ # Razor views\n│ ├── Services/ # Dashboard services\n│ ├── Models/ # Dashboard models\n│ └── Extensions/ # DI registration\n│\n└── samples/\n ├── SampleGateway/ # Gateway example\n └── SampleLocalService/ # Client example\n```\n\n---\n\n## 🧪 Sample Projects\n\n```bash\n# Terminal 1: Start gateway\ndotnet run --project samples/SampleGateway\n\n# Terminal 2: Start local service (auto-registers)\ndotnet run --project samples/SampleLocalService\n\n# Test the route\ncurl http://localhost:5000/api/your-endpoint\n```\n\n**Sample Gateway Features:**\n- Dashboard at `/apigateway` (login: `admin` / `demo123`)\n- Serilog logging\n- JWT authentication\n\n---\n\n## 📄 License\n\nThis project is licensed under the [MIT License](LICENSE).\n\n---\n\n<div align=\"center\">\n\n**Made with ❤️ for the .NET community**\n\n[⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) if you find it useful!\n\n</div>\n"}, "e53286a4e4f9b8160222fb10989bf512bfd7a154": {"t": 1785763473.408634, "n": 10645, "h": "247c01bccc88ffbb", "c": "# Aneiang.Yarp\n\n<div align=\"center\">\n\n**Dynamic Routing Gateway Management Library for .NET**\n\n[![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp)\n[![License](https://img.shields.io/github/license/aneiang/Aneiang.Yarp.svg)](LICENSE)\n[![YARP Version](https://img.shields.io/badge/YARP-2.3.0-blue.svg)](https://github.com/microsoft/reverse-proxy)\n[![.NET](https://img.shields.io/badge/.NET-8.0%20%7C%209.0-purple.svg)](https://dotnet.microsoft.com/)\n\n[English](README.md) | [中文](README.zh-CN.md)\n\n</div>\n\n---\n\nA powerful **dynamic routing gateway management library** built on [Microsoft YARP](https://github.com/microsoft/reverse-proxy), providing runtime route registration, automatic service discovery, real-time monitoring dashboard, while preserving all YARP reverse proxy capabilities.\n\n## ✨ Features\n\n| Feature | Description |\n|---------|-------------|\n| 🚀 **Dynamic Routing** | REST API for runtime route registration/update/unregistration |\n| 🔄 **Auto-Registration** | Services auto-register on startup & unregister on shutdown — **1 line of code** *(requires network connectivity, ideal for debugging)* |\n| 🎯 **One-Line API** | `AddAneiangYarp()` / `AddAneiangYarpClient()` to setup gateway or client |\n| 👥 **Instance Isolation** | Automatic namespace isolation for multi-developer debugging |\n| ⚙️ **Highly Customizable** | Code > Env Vars > Config files priority; fine-grained component control |\n| 📊 **Dashboard (Recommended)** | Real-time clusters, routes, health status & YARP logs viewer |\n| 🧠 **Smart Defaults** | Auto-detect assembly name, Kestrel address, resolve localhost to LAN IP |\n\n## 📦 NuGet Packages\n\n| Package | Description | Link |\n|---------|-------------|------|\n| **Aneiang.Yarp** | Core library: dynamic routing + auto-registration client | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.svg)](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Dashboard** | 🌟 **Recommended**: Dashboard for monitoring & operations | [![NuGet](https://img.shields.io/nuget/v/Aneiang.Yarp.Dashboard.svg)](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**Requirements:**\n- Target Framework: `.NET 8.0` / `.NET 9.0`\n- YARP Version: `2.3.0`\n\n---\n\n## 🚀 Quick Start\n\n### 1️⃣ Setup Gateway\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ One-line gateway setup\nbuilder.Services.AddAneiangYarp();\n\n// Optional: Add dashboard\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.MapReverseProxy();\napp.Run();\n```\n\n### 2️⃣ Connect Client Service\n\n> **Note**: Auto-registration requires network connectivity between the client service and gateway. This feature is primarily designed for **development and debugging scenarios**.\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ One line: auto-register on startup → auto-unregister on shutdown\nbuilder.Services.AddAneiangYarpClient();\n\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n**`appsettings.json`** (minimum config):\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\nThat's it! Your service will automatically register to the gateway on startup and unregister on shutdown.\n\n---\n\n## 📸 Dashboard Screenshots\n\n### Cluster Status\n\n![Cluster List](docs/cluster-list.png)\n\n### Route Configuration\n\n![Route List](docs/route-list.png)\n\n### Log Viewer\n\n![Log List](docs/log-list.png)\n\n---\n\n## ⚡ Advanced Usage\n\n<details>\n<summary><b>🔗 Multi-Level Gateway Chain</b></summary>\n\n```csharp\n// Intranet gateway also registers to external gateway\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpClient(o =>\n{\n o.GatewayUrl = \"http://outer-gateway:5000\";\n});\n```\n\n</details>\n\n<details>\n<summary><b>🛡️ Gateway API Authorization</b></summary>\n\nProtect registration APIs with BasicAuth or ApiKey:\n\n```csharp\n// Auto-detect from Dashboard config\nbuilder.Services.AddGatewayApiAuth();\n\n// Or explicit configuration\nbuilder.Services.AddGatewayApiAuth(o =>\n{\n o.Mode = GatewayApiAuthMode.BasicAuth;\n o.Username = \"admin\";\n o.Password = \"admin@2026\";\n});\n```\n\nConfig file:\n```json\n{\n \"Gateway\": {\n \"ApiAuth\": {\n \"Mode\": \"BasicAuth\",\n \"Username\": \"admin\",\n \"Password\": \"admin@2026\"\n }\n }\n}\n```\n\n</details>\n\n<details>\n<summary><b>👥 Instance Isolation (Multi-Developer)</b></summary>\n\n**Enabled by default** — automatically embeds machine identifier into routes:\n\n| Dimension | DevA (PC-JOHN) | DevB (PC-JANE) |\n|-----------|----------------|----------------|\n| routeName | `my-service-PC-JOHN` | `my-service-PC-JANE` |\n| matchPath | `/PC-JOHN/api/{**catch-all}` | `/PC-JANE/api/{**catch-all}` |\n\nCustom instance ID:\n```csharp\nbuilder.Services.AddAneiangYarpClient(options =>\n{\n options.InstanceId = \"dev-john\";\n options.InstancePrefixFormat = \"dev-{instanceId}\";\n});\n```\n\nDisable if not needed:\n```csharp\noptions.InstanceIsolation = false;\n```\n\n</details>\n\n<details>\n<summary><b>🌐 Automatic LAN IP Resolution</b></summary>\n\nWhen `DestinationAddress` contains `localhost` / `127.0.0.1` / `0.0.0.0`:\n\n```\nConfigured: http://localhost:5001\nResolved: http://192.168.1.101:5001 (LAN IP)\n```\n\nDisable: `AutoResolveIp = false`\n\n</details>\n\n<details>\n<summary><b>🔧 Custom Middleware Pipeline</b></summary>\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication();\napp.UseAuthorization();\napp.UseRequestLogging();\napp.UseRateLimiter();\n\napp.UseRouting();\napp.MapControllers();\napp.MapReverseProxy(); // Must be last\n```\n\n</details>\n\n---\n\n## 📖 Configuration\n\n<details>\n<summary><b>Gateway:Registration</b> — Full Configuration Options</summary>\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\",\n \"RouteName\": \"my-service\",\n \"ClusterName\": \"my-service-cluster\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"DestinationAddress\": \"http://localhost:5001\",\n \"Order\": 50,\n \"AutoResolveIp\": true,\n \"TimeoutSeconds\": 10,\n \"InstanceIsolation\": true,\n \"InstanceId\": \"john\",\n \"InstancePrefixFormat\": \"{instanceId}\",\n \"StripInstancePrefix\": true,\n \"DownstreamPathPrefix\": null,\n \"Transforms\": [],\n \"AuthToken\": null,\n \"ApiKey\": null,\n \"BasicAuthUsername\": null,\n \"BasicAuthPassword\": null\n }\n }\n}\n```\n\n**Configuration Priority:** Code `options => {}` > Environment Variables > `appsettings.json`\n\n</details>\n\n<details>\n<summary><b>Gateway:Dashboard</b> — Dashboard Configuration</summary>\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"EnableProxyLogging\": true,\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"demo123\",\n \"RoutePrefix\": \"apigateway\",\n \"Locale\": \"en-US\"\n }\n }\n}\n```\n\n**Auth Modes:** `None` | `ApiKey` | `CustomJwt` | `DefaultJwt`\n\nJWT tokens expire in **8 hours**. Login via `POST /apigateway/login`.\n\n</details>\n\n<details>\n<summary><b>ReverseProxy</b> — Standard YARP Configuration</b></summary>\n\n```json\n{\n \"ReverseProxy\": {\n \"Routes\": {\n \"my-route\": {\n \"ClusterId\": \"my-cluster\",\n \"Match\": \"/api/test/{**catch-all}\"\n }\n },\n \"Clusters\": {\n \"my-cluster\": {\n \"Destinations\": {\n \"d1\": { \"Address\": \"http://localhost:5000/\" }\n },\n \"LoadBalancingPolicy\": \"PowerOfTwoChoices\"\n }\n }\n }\n}\n```\n\nSee [YARP docs](https://microsoft.github.io/reverse-proxy/articles/config-files.html) for details.\n\n</details>\n\n---\n\n## 🔌 API Endpoints\n\n<details>\n<summary><b>Gateway API</b> — <code>/api/gateway</code></summary>\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/api/gateway/register-route` | `POST` | Register/update route |\n| `/api/gateway/{routeName}` | `DELETE` | Unregister route |\n| `/api/gateway/routes` | `GET` | Query all routes |\n| `/api/gateway/ping` | `GET` | Health check |\n\n**Request Example:**\n```json\n{\n \"routeName\": \"my-service\",\n \"clusterName\": \"my-service-cluster\",\n \"matchPath\": \"/api/my-service/{**catch-all}\",\n \"destinationAddress\": \"http://192.168.1.101:5001\",\n \"order\": 50,\n \"transforms\": [\n { \"PathSet\": \"/api/backend/{**catch-all}\" }\n ]\n}\n```\n\n**Response Format:**\n```json\n{ \"code\": 200, \"message\": \"Route registered successfully\" }\n```\n\n</details>\n\n<details>\n<summary><b>Dashboard API</b> — <code>/apigateway</code></summary>\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /apigateway` | Dashboard home page |\n| `GET /apigateway/login` | Login page |\n| `POST /apigateway/login` | Login (returns JWT token) |\n| `GET /apigateway/info` | Gateway runtime info |\n| `GET /apigateway/clusters` | Cluster status |\n| `GET /apigateway/routes` | Route configuration |\n| `GET /apigateway/routes/{routeId}` | Route details |\n| `GET /apigateway/logs` | Recent YARP logs |\n| `DELETE /apigateway/logs` | Clear logs |\n\n</details>\n\n---\n\n## 📂 Project Structure\n\n```\nAneiang.Yarp/\n├── src/\n│ ├── Aneiang.Yarp/ # Core library\n│ │ ├── Controllers/ # Gateway REST API\n│ │ ├── Extensions/ # DI registration\n│ │ ├── Models/ # DTOs & Options\n│ │ └── Services/ # Core services\n│ │\n│ └── Aneiang.Yarp.Dashboard/ # Dashboard library\n│ ├── Controllers/ # Dashboard API\n│ ├── Views/ # Razor views\n│ ├── Services/ # Dashboard services\n│ ├── Models/ # Dashboard models\n│ └── Extensions/ # DI registration\n│\n└── samples/\n ├── SampleGateway/ # Gateway example\n └── SampleLocalService/ # Client example\n```\n\n---\n\n## 🧪 Sample Projects\n\n```bash\n# Terminal 1: Start gateway\ndotnet run --project samples/SampleGateway\n\n# Terminal 2: Start local service (auto-registers)\ndotnet run --project samples/SampleLocalService\n\n# Test the route\ncurl http://localhost:5000/api/your-endpoint\n```\n\n**Sample Gateway Features:**\n- Dashboard at `/apigateway` (login: `admin` / `demo123`)\n- Serilog logging\n- JWT authentication\n\n---\n\n## 📄 License\n\nThis project is licensed under the [MIT License](LICENSE).\n\n---\n\n<div align=\"center\">\n\n**Made with ❤️ for the .NET community**\n\n[⭐ Star this repo](https://github.com/aneiang/Aneiang.Yarp) if you find it useful!\n\n</div>\n"}, "e8f6b04f18afe89165c0849c56aa5c8f3095d2f2": {"t": 1785763480.8573484, "n": 14987, "h": "16052940fb42dd11", "c": "# Aneiang.Yarp\n\n基于 [YARP (Yet Another Reverse Proxy)](https://github.com/microsoft/reverse-proxy) 的**动态路由网关管理库**,提供动态路由注册、服务自动发现、实时监控仪表盘,同时完整保留 YARP 反向代理的全部能力(负载均衡、健康检查、路由转换、会话保持等)。\n\n### 核心能力\n\n| 能力 | 说明 |\n|------|------|\n| **动态路由注册** | Gateway 提供 REST API,允许客户端运行时注册/更新/注销反向代理路由 |\n| **自动注册客户端** | 本地服务启动时自动向网关注册路由,关闭时自动注销——**一行代码**搞定 |\n| **一键式 API** | `AddAneiangYarp()` / `AddAneiangYarpClient()` 一行搭建网关或客户端 |\n| **实例隔离** | 多人调试同一服务时自动隔离 routing 命名空间,互不干扰 |\n| **高度可定制** | 支持配置优先级:代码 > 环境变量 > 配置文件;组件级 API 精细控制 |\n| **仪表盘(可选)** | 实时查看集群、路由、健康状态、YARP 日志摘要 |\n| **智能默认值** | `routeName` 取程序集名、`matchPath` 默认全路径转发、`destinationAddress` 自动从 Kestrel 获取,`localhost` 自动解析为内网 IP |\n\n## 项目结构\n\n```\nAneiang.Yarp/\n├── src/\n│ ├── Aneiang.Yarp/ # 基础库:动态路由 + 自动注册客户端\n│ │ ├── Controllers/\n│ │ │ └── GatewayConfigController.cs # 网关侧 REST API\n│ │ ├── Extensions/\n│ │ │ └── YarpServiceCollectionExtensions.cs # DI 注册扩展\n│ │ ├── Models/\n│ │ │ ├── RegisterRouteRequest.cs # 注册请求 DTO\n│ │ │ └── GatewayRegistrationOptions.cs # 配置选项(智能默认值)\n│ │ └── Services/\n│ │ ├── DynamicYarpConfigService.cs # 动态路由管理\n│ │ ├── GatewayAutoRegistrationClient.cs # 客户端注册服务\n│ │ └── GatewayRegistrationHostedService.cs # 托管服务(自动注册/注销)\n│ │\n│ └── Aneiang.Yarp.Dashboard/ # 仪表盘库(可选)\n│ ├── Controllers/\n│ │ └── DashboardController.cs # 仪表盘 API\n│ ├── Views/\n│ │ ├── Dashboard/Index.cshtml # 仪表盘 UI\n│ │ ├── Dashboard/Login.cshtml # 登录页面\n│ │ └── Shared/_DashboardLayout.cshtml # 布局页\n│ ├── Services/\n│ │ ├── ProxyLogStore.cs # 日志环形缓冲区\n│ │ └── ProxyLogProvider.cs # YARP 日志捕获\n│ ├── Models/\n│ │ ├── DashboardOptions.cs # 仪表盘配置选项\n│ │ └── LogEntry.cs # 日志条目模型\n│ └── Extensions/\n│ └── YarpServiceCollectionExtensions.cs # DI 注册扩展\n│\n└── samples/\n ├── SampleGateway/ # 网关项目示例\n └── SampleLocalService/ # 本地服务(客户端)示例\n```\n\n### NuGet 包\n\n| 包名 | 说明 | 必需 |\n|------|------|------|\n| `Aneiang.Yarp` | 基础库:动态路由 + 自动注册客户端 | ✅ |\n| `Aneiang.Yarp.Dashboard` | 仪表盘:监控运维 UI | 可选 |\n\n- **目标框架**:`net8.0` / `net9.0`\n- **YARP 版本**:`2.3.0`\n\n---\n\n## 快速开始\n\n### ① 搭建内网网关(Gateway 角色)\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\nusing Aneiang.Yarp.Dashboard.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ 一行搭建网关(自动加载 ReverseProxy 静态路由 + 动态路由管理)\nbuilder.Services.AddAneiangYarp();\n\n// 可选:添加仪表盘\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.MapControllers(); // GatewayConfigController API\napp.MapReverseProxy(); // YARP 反向代理\n\napp.Run();\n```\n\n**`appsettings.json`**(静态路由可选):\n\n```json\n{\n \"ReverseProxy\": {\n \"Routes\": {\n \"my-api\": {\n \"ClusterId\": \"my-api-cluster\",\n \"Match\": { \"Path\": \"/api/stable/{**catch-all}\" }\n }\n },\n \"Clusters\": {\n \"my-api-cluster\": {\n \"Destinations\": {\n \"d1\": { \"Address\": \"http://internal-server:8080/\" }\n }\n }\n }\n }\n}\n```\n\n### ② 本地服务接入(Client 角色)\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ 一行搞定:启动自动注册 → 关闭自动注销\nbuilder.Services.AddAneiangYarpClient();\n\n// 你的业务代码\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n**`appsettings.json`**(最少只需一行):\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n> **智能默认值一览:**\n> \n> | 字段 | 默认值 | 来源 |\n> |------|--------|------|\n> | `RouteName` | 入口程序集名称 | `Assembly.GetEntryAssembly()` |\n> | `ClusterName` | 同 `RouteName` | — |\n> | `MatchPath` | `/{**catch-all}` | — |\n> | `DestinationAddress` | Kestrel 绑定地址 | `Urls` 配置 |\n> | `Order` | `50` | — |\n> | `AutoResolveIp` | `true` | `localhost` → 内网 IP |\n> | `TimeoutSeconds` | `5` | — |\n> | `Enabled` | 有 `GatewayUrl` 即启用 | 自动判断 |\n> | `InstanceIsolation` | `true` | 开启 |\n> | `InstanceId` | `Environment.MachineName` | 本机主机名 |\n\n---\n\n## API 设计原则:简洁分层,按需组合\n\n```\n 一站式网关 客户端自动注册 可选增强\n (1 行) (1 行) (1 行)\n ──────── ────────── ────────\n AddAneiangYarp() AddAneiangYarpClient() AddAneiangYarpDashboard()\n AddGatewayApiAuth()\n```\n\n### ⭐ 一站式 API\n\n| 方法 | 用途 | 自动注册的服务 |\n|------|------|---------------|\n| `AddAneiangYarp()` | 搭建网关 | `AddReverseProxy` + `InMemoryConfigProvider` + `DynamicYarpConfigService` + `AddControllersWithViews` + `AddHttpClient` + `GatewayAutoRegistrationClient` |\n| `AddAneiangYarpClient()` | 客户端接入 | `AddHttpClient` + `GatewayAutoRegistrationClient` + `GatewayRegistrationHostedService`(自动注册/注销) |\n\n### 🔧 可定制 API\n\n```csharp\n// 客户端:自定义注册选项(优先级:代码 > 环境变量 > 配置文件)\nbuilder.Services.AddAneiangYarpClient(options =>\n{\n options.GatewayUrl = \"http://192.168.1.100:5000\";\n options.MatchPath = \"/api/users/{**catch-all}\";\n options.Order = 10;\n options.AutoResolveIp = true;\n options.TimeoutSeconds = 10;\n});\n\n// 网关:自定义 YARP 管道(transforms, service discovery 等)\nbuilder.Services.AddAneiangYarp(proxy =>\n{\n proxy.AddTransforms<MyCustomTransform>();\n});\n\n// 网关 + 客户端:网关也注册到上级网关\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpClient(o => o.GatewayUrl = \"http://upstream-gateway:5000\");\n```\n\n### 🛠 精细化 API\n\n```csharp\n// 完全手动控制\nbuilder.Services.AddReverseProxy();\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpDashboard();\nbuilder.Services.AddGatewayApiAuth(); // 保护 GatewayConfigController API\n```\n\n---\n\n## 配置参考\n\n### `Gateway:Registration` 节\n\n所有字段均为可选(除 `GatewayUrl` 外),缺失时使用智能默认值。\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\",\n \"RouteName\": \"my-service\",\n \"ClusterName\": \"my-service-cluster\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"DestinationAddress\": \"http://localhost:5001\",\n \"Order\": 50,\n \"AutoResolveIp\": true,\n \"TimeoutSeconds\": 10,\n \"InstanceIsolation\": true,\n \"InstanceId\": \"john\"\n }\n }\n}\n```\n\n| 字段 | 类型 | 必填 | 默认值 | 说明 |\n|------|------|------|--------|------|\n| `GatewayUrl` | `string` | ✅ | — | 网关服务地址 |\n| `RouteName` | `string` | — | 程序集名 | 路由唯一标识 |\n| `ClusterName` | `string` | — | 同 RouteName | 集群名称 |\n| `MatchPath` | `string` | — | `/{**catch-all}` | 路径匹配模板 |\n| `DestinationAddress` | `string` | — | Kestrel 绑定地址 | 本机目标地址 |\n| `Order` | `int` | — | `50` | 路由优先级(越小越优先) |\n| `AutoResolveIp` | `bool` | — | `true` | 自动 localhost → 内网 IP |\n| `TimeoutSeconds` | `int` | — | `5` | HTTP 请求超时(秒) |\n| `Enabled` | `bool` | — | 有 GatewayUrl 即 true | 是否启用 |\n| `InstanceIsolation` | `bool` | — | `true` | 开启实例隔离 |\n| `InstanceId` | `string` | — | `Environment.MachineName` | 自定义实例标识 |\n| `InstancePrefixFormat` | `string` | — | `{instanceId}` | 路径前缀格式模板 |\n\n**配置优先级:代码 `options => {}` > 环境变量 `Gateway__Registration__GatewayUrl` > 配置文件 `appsettings.json`**\n\n### `Gateway:Dashboard` 节\n\n仪表盘配置,绑定配置节 `\"Gateway\": { \"Dashboard\": { ... } }`。\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"demo123\",\n \"RoutePrefix\": \"apigateway\"\n }\n }\n}\n```\n\n| 字段 | 类型 | 必填 | 默认值 | 说明 |\n|------|------|------|--------|------|\n| `AuthMode` | `string` | — | `None` | 鉴权模式:`None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `ApiKey` | `string` | — | — | API Key 值(仅 `ApiKey` 模式) |\n| `ApiKeyHeaderName` | `string` | — | `X-Api-Key` | API Key 请求头名称 |\n| `JwtSecret` | `string` | — | 随机生成 | JWT 签名密钥(不设置则重启后令牌失效) |\n| `JwtUsername` | `string` | — | — | 登录用户名(仅 `CustomJwt` 模式) |\n| `JwtPassword` | `string` | — | — | 登录密码(`CustomJwt` / `DefaultJwt` 均需设置) |\n| `RoutePrefix` | `string` | — | `apigateway` | 仪表盘路由前缀 |\n\n**鉴权模式说明:**\n\n| 模式 | 验证方式 | 适用场景 |\n|------|----------|----------|\n| `None` | 不验证 | 内网调试、开发环境 |\n| `ApiKey` | 请求头 `X-Api-Key` 或查询参数 `api-key` | 服务间调用、自动化工具 |\n| `CustomJwt` | 自定义用户名+密码 → JWT token | 生产环境、多用户管理 |\n| `DefaultJwt` | 固定用户名 `admin` + 配置文件密码 → JWT token | 快速启用安全访问 |\n\nJWT 令牌默认有效期 **8 小时**,通过 `POST /apigateway/login` 获取,支持 `Authorization: Bearer` 请求头或 `dashboard_token` Cookie。\n\n### `ReverseProxy` 节\n\n标准 YARP 配置,详见 [YARP 官方文档](https://microsoft.github.io/reverse-proxy/articles/config-files.html)。支持两种 `Match` 写法:\n\n```json\n{\n \"ReverseProxy\": {\n \"Routes\": {\n \"my-route\": {\n \"ClusterId\": \"my-cluster\",\n \"Match\": \"/api/test/{**catch-all}\" // 简化写法\n // 或标准写法:\n // \"Match\": { \"Path\": \"/api/test/{**catch-all}\", \"Methods\": [\"GET\"] }\n }\n },\n \"Clusters\": {\n \"my-cluster\": {\n \"Destinations\": {\n \"d1\": { \"Address\": \"http://localhost:5000/\" }\n },\n \"LoadBalancingPolicy\": \"PowerOfTwoChoices\" // 可选\n }\n }\n }\n}\n```\n\n---\n\n## API 参考\n\n### 网关侧 API(`/api/gateway`)\n\n这些端点由 `GatewayConfigController` 提供,运行在网关上,供客户端远程调用。\n\n| 端点 | 方法 | 说明 |\n|------|------|------|\n| `/api/gateway/register-route` | `POST` | 注册/更新路由(含数据验证,支持 Transforms) |\n| `/api/gateway/{routeName}` | `DELETE` | 注销路由(无关联路由时自动清理集群) |\n| `/api/gateway/routes` | `GET` | 查询所有已注册路由 |\n| `/api/gateway/ping` | `GET` | 健康检查 |\n\n**注册路由请求体:**\n\n```json\n{\n \"routeName\": \"my-service\",\n \"clusterName\": \"my-service-cluster\",\n \"matchPath\": \"/api/my-service/{**catch-all}\",\n \"destinationAddress\": \"http://192.168.1.101:5001\",\n \"order\": 50,\n \"transforms\": [\n { \"PathSet\": \"/api/backend/{**catch-all}\" },\n { \"RequestHeader\": \"X-Forwarded-By\", \"Set\": \"gateway\" }\n ]\n}\n```\n\n| 字段 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `routeName` | `string` | ✅ | 路由唯一标识(≤200 字符) |\n| `clusterName` | `string` | ✅ | 集群名称(≤200 字符) |\n| `matchPath` | `string` | ✅ | 路径匹配模板 |\n| `destinationAddress` | `string` | ✅ | 目标 URL |\n| `order` | `int` | — | 优先级(默认 50) |\n| `transforms` | `array` | — | 路由转换规则(见下方示例) |\n\n**响应格式(所有端点统一):**\n\n```json\n// 成功\n{ \"code\": 200, \"info\": \"路由 'my-service' 注册成功\" }\n\n// 带数据\n{ \"code\": 200, \"data\": [{ \"routeId\": \"...\", \"clusterId\": \"...\", \"path\": \"...\", \"order\": 50 }] }\n\n// 失败\n{ \"code\": 400, \"info\": \"路由名称不能为空; 目标地址必须是有效的 URL\" }\n```\n\n### 仪表盘 API(`/apigateway`)\n\n| 端点 | 说明 |\n|------|------|\n| `GET /apigateway` | 仪表盘首页(HTML) |\n| `GET /apigateway/login` | 登录页面(HTML) |\n| `POST /apigateway/login` | 登录接口(返回 JWT token) |\n| `GET /apigateway/info` | 网关运行信息(版本/环境/运行时间/内存) |\n| `GET /apigateway/clusters` | 集群状态(目标列表/健康/不健康/未知) |\n| `GET /apigateway/routes` | 路由配置列表(路径/方法/优先级/Transforms) |\n| `GET /apigateway/logs` | 最近 YARP 代理日志 |\n| `DELETE /apigateway/logs` | 清空日志缓冲区 |\n\n### 扩展方法\n\n| 命名空间 | 方法 | 说明 |\n|----------|------|------|\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarp()` | ⭐ 一站式搭建网关(含 YARP 反向代理、静态/动态路由管理、Controllers、HttpClient) |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarp(Action<IReverseProxyBuilder>)` | 网关 + YARP 管道定制 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpClient()` | ⭐ 客户端自动注册/注销 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpClient(Action<GatewayRegistrationOptions>)` | 客户端 + 自定义注册选项 |\n| `Aneiang.Yarp.Extensions` | `AddGatewayApiAuth()` | 网关配置 API 授权保护 |\n| `Aneiang.Yarp.Dashboard.Extensions` | `AddAneiangYarpDashboard()` | 仪表盘(默认无授权) |\n| `Aneiang.Yarp.Dashboard.Extensions` | `AddAneiangYarpDashboard(Action<DashboardOptions>)` | 仪表盘 + 自定义选项 |\n\n---\n\n## 高级用法\n\n### 1. 多级网关链\n\n```\n外网 Gateway → 内网 Gateway → 本机调试服务\n```\n\n```csharp\n// 内网网关同时注册到外网网关\nbuilder.Services.AddAneiangYarp();\nbuilder.Services.AddAneiangYarpClient(o =>\n{\n o.GatewayUrl = \"http://outer-gateway:5000\";\n});\n```\n\n### 2. 自定义中间件管道\n\n网关项目中可以自由插入中间件,YARP 转发前会经过完整管道:\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication(); // 认证\napp.UseAuthorization(); // 授权\napp.UseRequestLogging(); // 日志\napp.UseRateLimiter(); // 限流\n\napp.UseRouting();\napp.MapControllers();\napp.MapReverseProxy(); // YARP 必须放最后\n```\n\n### 3. 根据环境切换配置\n\n```json\n// appsettings.Development.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"MatchPath\": \"/api/dev/{**catch-all}\"\n }\n }\n}\n\n// appsettings.Production.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://gateway.internal:5000\"\n }\n }\n}\n```\n\n### 4. 手动 API 调用(绕过自动注册)\n\n```bash\n# 注册\ncurl -X POST http://gateway:5000/api/gateway/register-route \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"routeName\": \"manual-route\",\n \"clusterName\": \"manual-cluster\",\n \"matchPath\": \"/api/manual/{**catch-all}\",\n \"destinationAddress\": \"http://192.168.1.101:5001\",\n \"order\": 50\n }'\n\n# 查询\ncurl http://gateway:5000/api/gateway/routes\n\n# 注销\ncurl -X DELETE http://gateway:5000/api/gateway/manual-route\n```\n\n### 5. 内网 IP 自动解析机制\n\n当 `DestinationAddress` 包含 `localhost` / `127.0.0.1` / `0.0.0.0` 时,会自动解析为本机内网 IPv4 地址:\n\n```\n配置值: http://localhost:5001\n解析后: http://192.168.1.101:5001 (本机内网 IP)\n```\n\n这样网关才能从内网访问到本机。可通过 `AutoResolveIp = false` 关闭。\n\n### 6. 多人协作:实例隔离模式\n\n**实例隔离默认已开启**,自动将本机标识嵌入路由名称和 URL 路径中,实现完全隔离:\n\n**隔离效果(假设 DevA 在 `PC-JOHN`,DevB 在 `PC-JANE`):**\n\n| 维度 | 隔离后(互不影响) |\n|------|-------------------|\n| routeName | `my-service-PC-JOHN` / `my-service-PC-JANE` |\n| clusterName | `my-service-cluster-PC-JOHN` / `my-service-cluster-PC-JANE` |\n| matchPath | `/PC-JOHN/api/{**catch-all}` / `/PC-JANE/api/{**catch-all}` |\n\n**测试时各自使用隔离后的路径:**\n\n```bash\n# DevA 测试(请求经 PC-JOHN → 本机 localhost:5001)\ncurl http://gateway:5000/PC-JOHN/api/users\n\n# DevB 测试(请求经 PC-JANE → 本机 localhost:5001)\ncurl http://gateway:5000/PC-JANE/api/users\n```\n\n> 如果一人调试、不需要实例隔离,可以显式关闭:\n> ```csharp\n> builder.Services.AddAneiangYarpClient(options => { options.InstanceIsolation = false; });\n> ```\n\n#### 自定义实例标识\n\n```csharp\n// 方式 1:代码内指定\nbuilder.Services.AddAneiangYarpClient(options =>\n{\n options.InstanceId = \"dev-john\"; // 简短的别名\n options.InstancePrefixFormat = \"dev-{instanceId}\"; // 路径前缀格式\n});\n\n// 方式 2:配置文件(适合同项目不同人,各自改 appsettings)\n// { \"Gateway\": { \"Registration\": { \"InstanceId\": \"dev-john\" } } }\n```\n\n#### 路径前缀格式化\n\n`InstancePrefixFormat` 支持占位符替换,定制路径前缀样式:\n\n| 占位符 | 示例值 | 说明 |\n|--------|--------|------|\n| `{instanceId}` | `john` | 实例 ID |\n| `{machineName}` | `PC-JOHN` | 本机主机名 |\n| `{userName}` | `john.doe` | 当前用户名 |\n\n配置示例:\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"InstanceId\": \"john\",\n \"InstancePrefixFormat\": \"dev-{userName}\"\n }\n }\n}\n```\n\n→ routeName: `my-service-dev-john.doe`\n→ matchPath: `/dev-john.doe/{**catch-all}`\n\n---\n\n## 示例项目\n\n| 项目 | 说明 | 启动命令 |\n|------|------|----------|\n| `samples/SampleGateway` | 内网网关示例(含仪表盘) | `dotnet run --project samples/SampleGateway` |\n| `samples/SampleLocalService` | 本地服务示例(自动注册到网关) | `dotnet run --project samples/SampleLocalService` |\n\n```bash\n# 终端 1:启动网关(localhost:5000)\ncd samples/SampleGateway && dotnet run\n\n# 终端 2:启动本地服务(localhost:5001,自动注册到网关)\ncd samples/SampleLocalService && dotnet run\n\n# 验证:请求流经网关 → 转发到本地服务\ncurl http://localhost:5000/api/your-endpoint\n```\n\n---\n\n## 依赖\n\n- **.NET** ≥ 8.0\n- **[YARP](https://github.com/microsoft/reverse-proxy)** `2.3.0`\n- `Aneiang.Yarp.Dashboard` 依赖 `Aneiang.Yarp`\n\n---\n\n## 许可证\n\n[MIT](LICENSE)\n"}, "d1de91134da831781780f8d7027167f1b35b4bbe": {"t": 1785763485.5661044, "n": 15360, "h": "78e3b8c3866757ae", "c": "# Aneiang.Yarp\n\n基于 [YARP (Yet Another Reverse Proxy)](https://github.com/microsoft/reverse-proxy) 的**动态路由网关管理库**,提供动态路由注册、服务自动发现、实时监控仪表盘,同时完整保留 YARP 反向代理的全部能力(负载均衡、健康检查、路由转换、会话保持等)。\n\n### 核心能力\n\n| 能力 | 说明 |\n|------|------|\n| **动态路由注册** | Gateway 提供 REST API,允许客户端运行时注册/更新/注销反向代理路由 |\n| **自动注册客户端** | 本地服务启动时自动向网关注册路由,关闭时自动注销——**一行代码**搞定 |\n| **一键式 API** | `AddAneiangYarpGateway()` / `AddAneiangYarpClient()` 一行搭建网关或客户端 |\n| **实例隔离** | 多人调试同一服务时自动隔离 routing 命名空间,互不干扰 |\n| **高度可定制** | 支持配置优先级:代码 > 环境变量 > 配置文件;组件级 API 精细控制 |\n| **仪表盘(可选)** | 实时查看集群、路由、健康状态、YARP 日志摘要 |\n| **智能默认值** | `routeName` 取程序集名、`matchPath` 默认全路径转发、`destinationAddress` 自动从 Kestrel 获取,`localhost` 自动解析为内网 IP |\n\n## 项目结构\n\n```\nAneiang.Yarp/\n├── src/\n│ ├── Aneiang.Yarp/ # 基础库:动态路由 + 自动注册客户端\n│ │ ├── Controllers/\n│ │ │ └── GatewayConfigController.cs # 网关侧 REST API\n│ │ ├── Extensions/\n│ │ │ ├── YarpServiceCollectionExtensions.cs # DI 注册扩展\n│ │ │ └── YarpApplicationExtensions.cs # 中间件扩展\n│ │ ├── Models/\n│ │ │ ├── RegisterRouteRequest.cs # 注册请求 DTO\n│ │ │ └── GatewayRegistrationOptions.cs # 配置选项(智能默认值)\n│ │ └── Services/\n│ │ ├── DynamicYarpConfigService.cs # 动态路由管理\n│ │ ├── GatewayAutoRegistrationClient.cs # 客户端注册服务\n│ │ └── GatewayRegistrationHostedService.cs # 托管服务(自动注册/注销)\n│ │\n│ └── Aneiang.Yarp.Dashboard/ # 仪表盘库(可选)\n│ ├── Controllers/\n│ │ └── DashboardController.cs # 仪表盘 API\n│ ├── Views/\n│ │ ├── Dashboard/Index.cshtml # 仪表盘 UI\n│ │ ├── Dashboard/Login.cshtml # 登录页面\n│ │ └── Shared/_DashboardLayout.cshtml # 布局页\n│ ├── Services/\n│ │ ├── ProxyLogStore.cs # 日志环形缓冲区\n│ │ └── ProxyLogProvider.cs # YARP 日志捕获\n│ ├── Models/\n│ │ ├── DashboardOptions.cs # 仪表盘配置选项\n│ │ └── LogEntry.cs # 日志条目模型\n│ └── Extensions/\n│ └── YarpServiceCollectionExtensions.cs # DI 注册扩展\n│\n└── samples/\n ├── SampleGateway/ # 网关项目示例\n └── SampleLocalService/ # 本地服务(客户端)示例\n```\n\n### NuGet 包\n\n| 包名 | 说明 | 必需 |\n|------|------|------|\n| `Aneiang.Yarp` | 基础库:动态路由 + 自动注册客户端 | ✅ |\n| `Aneiang.Yarp.Dashboard` | 仪表盘:监控运维 UI | 可选 |\n\n- **目标框架**:`net6.0` / `net7.0` / `net8.0` / `net9.0`\n- **YARP 版本**:net6.0 → 2.1.0 / net7.0 → 2.2.0 / net8.0+ → 2.3.0\n\n---\n\n## 快速开始\n\n### ① 搭建内网网关(Gateway 角色)\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ 一行搭建网关\nbuilder.Services.AddAneiangYarpGateway();\n\n// 可选:添加仪表盘\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.MapControllers(); // GatewayConfigController API\napp.MapReverseProxy(); // YARP 反向代理\n\napp.Run();\n```\n\n**`appsettings.json`**(静态路由可选):\n\n```json\n{\n \"ReverseProxy\": {\n \"Routes\": {\n \"my-api\": {\n \"ClusterId\": \"my-api-cluster\",\n \"Match\": { \"Path\": \"/api/stable/{**catch-all}\" }\n }\n },\n \"Clusters\": {\n \"my-api-cluster\": {\n \"Destinations\": {\n \"d1\": { \"Address\": \"http://internal-server:8080/\" }\n }\n }\n }\n }\n}\n```\n\n### ② 本地服务接入(Client 角色)\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ 一行搞定:启动自动注册 → 关闭自动注销\nbuilder.Services.AddAneiangYarpClient();\n\n// 你的业务代码\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n**`appsettings.json`**(最少只需一行):\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n }\n}\n```\n\n> **智能默认值一览:**\n> \n> | 字段 | 默认值 | 来源 |\n> |------|--------|------|\n> | `RouteName` | 入口程序集名称 | `Assembly.GetEntryAssembly()` |\n> | `ClusterName` | 同 `RouteName` | — |\n> | `MatchPath` | `/{**catch-all}` | — |\n> | `DestinationAddress` | Kestrel 绑定地址 | `Urls` 配置 |\n> | `Order` | `50` | — |\n> | `AutoResolveIp` | `true` | `localhost` → 内网 IP |\n> | `TimeoutSeconds` | `5` | — |\n> | `Enabled` | 有 `GatewayUrl` 即启用 | 自动判断 |\n> | `InstanceIsolation` | `true` | 开启 |\n> | `InstanceId` | `Environment.MachineName` | 本机主机名 |\n\n---\n\n## API 设计原则:三种级别,按需选择\n\n```\n 一键式 可定制 精细化\n (1 行) (1 行 + λ) (完全控制)\n ────── ───────── ────────\n Gateway: Gateway: AddReverseProxy()\n AddAneiangYarp AddAneiangYarp AddAneiangYarp()\n Gateway() Gateway(o => {}) AddControllers()\n AddHttpClient()\n Client: Client:\n AddAneiangYarp AddAneiangYarp AddAneiangYarp\n Client() Client(o => {}) GatewayClient()\n```\n\n### ⭐ 一键式 API\n\n| 方法 | 用途 | 自动注册的服务 |\n|------|------|---------------|\n| `AddAneiangYarpGateway()` | 搭建网关 | `AddReverseProxy` + `AddAneiangYarp` + `AddControllersWithViews` + `AddHttpClient` |\n| `AddAneiangYarpClient()` | 客户端接入 | `AddHttpClient` + `GatewayAutoRegistrationClient` + `GatewayRegistrationHostedService`(自动注册/注销) |\n\n### 🔧 可定制 API\n\n```csharp\n// 自定义网关注册选项(优先级高于配置文件)\nbuilder.Services.AddAneiangYarpClient(options =>\n{\n options.GatewayUrl = \"http://192.168.1.100:5000\";\n options.MatchPath = \"/api/users/{**catch-all}\";\n options.Order = 10; // 高优先级\n options.AutoResolveIp = true; // localhost → 内网 IP\n options.TimeoutSeconds = 10;\n});\n\n// 网关也可以配置向上级网关注册\nbuilder.Services.AddAneiangYarpGateway(options =>\n{\n options.GatewayUrl = \"http://upstream-gateway:5000\";\n});\n```\n\n### 🛠 精细化 API\n\n```csharp\n// 完全手动控制 DI 注册顺序\nbuilder.Services.AddReverseProxy();\nbuilder.Services.AddAneiangYarp(); // 核心服务(不含 AddHttpClient)\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddHttpClient();\n\n// 客户端组件级注册(不含自动注册/注销)\nbuilder.Services.AddAneiangYarpGatewayClient();\n// 需要手动调用注册/注销:\napp.UseAneiangYarpGateway();\n```\n\n---\n\n## 配置参考\n\n### `Gateway:Registration` 节\n\n所有字段均为可选(除 `GatewayUrl` 外),缺失时使用智能默认值。\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\",\n \"RouteName\": \"my-service\",\n \"ClusterName\": \"my-service-cluster\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"DestinationAddress\": \"http://localhost:5001\",\n \"Order\": 50,\n \"AutoResolveIp\": true,\n \"TimeoutSeconds\": 10,\n \"InstanceIsolation\": true,\n \"InstanceId\": \"john\"\n }\n }\n}\n```\n\n| 字段 | 类型 | 必填 | 默认值 | 说明 |\n|------|------|------|--------|------|\n| `GatewayUrl` | `string` | ✅ | — | 网关服务地址 |\n| `RouteName` | `string` | — | 程序集名 | 路由唯一标识 |\n| `ClusterName` | `string` | — | 同 RouteName | 集群名称 |\n| `MatchPath` | `string` | — | `/{**catch-all}` | 路径匹配模板 |\n| `DestinationAddress` | `string` | — | Kestrel 绑定地址 | 本机目标地址 |\n| `Order` | `int` | — | `50` | 路由优先级(越小越优先) |\n| `AutoResolveIp` | `bool` | — | `true` | 自动 localhost → 内网 IP |\n| `TimeoutSeconds` | `int` | — | `5` | HTTP 请求超时(秒) |\n| `Enabled` | `bool` | — | 有 GatewayUrl 即 true | 是否启用 |\n| `InstanceIsolation` | `bool` | — | `true` | 开启实例隔离 |\n| `InstanceId` | `string` | — | `Environment.MachineName` | 自定义实例标识 |\n| `InstancePrefixFormat` | `string` | — | `{instanceId}` | 路径前缀格式模板 |\n\n**配置优先级:代码 `options => {}` > 环境变量 `Gateway__Registration__GatewayUrl` > 配置文件 `appsettings.json`**\n\n### `Gateway:Dashboard` 节\n\n仪表盘配置,绑定配置节 `\"Gateway\": { \"Dashboard\": { ... } }`。\n\n```json\n{\n \"Gateway\": {\n \"Dashboard\": {\n \"AuthMode\": \"DefaultJwt\",\n \"JwtPassword\": \"demo123\",\n \"RoutePrefix\": \"apigateway\"\n }\n }\n}\n```\n\n| 字段 | 类型 | 必填 | 默认值 | 说明 |\n|------|------|------|--------|------|\n| `AuthMode` | `string` | — | `None` | 鉴权模式:`None` / `ApiKey` / `CustomJwt` / `DefaultJwt` |\n| `ApiKey` | `string` | — | — | API Key 值(仅 `ApiKey` 模式) |\n| `ApiKeyHeaderName` | `string` | — | `X-Api-Key` | API Key 请求头名称 |\n| `JwtSecret` | `string` | — | 随机生成 | JWT 签名密钥(不设置则重启后令牌失效) |\n| `JwtUsername` | `string` | — | — | 登录用户名(仅 `CustomJwt` 模式) |\n| `JwtPassword` | `string` | — | — | 登录密码(`CustomJwt` / `DefaultJwt` 均需设置) |\n| `RoutePrefix` | `string` | — | `apigateway` | 仪表盘路由前缀 |\n| `AllowedRoles` | `string[]` | — | — | ASP.NET Core 角色白名单 |\n\n**鉴权模式说明:**\n\n| 模式 | 验证方式 | 适用场景 |\n|------|----------|----------|\n| `None` | 不验证 | 内网调试、开发环境 |\n| `ApiKey` | 请求头 `X-Api-Key` 或查询参数 `api-key` | 服务间调用、自动化工具 |\n| `CustomJwt` | 自定义用户名+密码 → JWT token | 生产环境、多用户管理 |\n| `DefaultJwt` | 固定用户名 `admin` + 配置文件密码 → JWT token | 快速启用安全访问 |\n\nJWT 令牌默认有效期 **8 小时**,通过 `POST /apigateway/login` 获取,支持 `Authorization: Bearer` 请求头或 `dashboard_token` Cookie。\n\n### `ReverseProxy` 节\n\n标准 YARP 配置,详见 [YARP 官方文档](https://microsoft.github.io/reverse-proxy/articles/config-files.html)。支持两种 `Match` 写法:\n\n```json\n{\n \"ReverseProxy\": {\n \"Routes\": {\n \"my-route\": {\n \"ClusterId\": \"my-cluster\",\n \"Match\": \"/api/test/{**catch-all}\" // 简化写法\n // 或标准写法:\n // \"Match\": { \"Path\": \"/api/test/{**catch-all}\", \"Methods\": [\"GET\"] }\n }\n },\n \"Clusters\": {\n \"my-cluster\": {\n \"Destinations\": {\n \"d1\": { \"Address\": \"http://localhost:5000/\" }\n },\n \"LoadBalancingPolicy\": \"PowerOfTwoChoices\" // 可选\n }\n }\n }\n}\n```\n\n---\n\n## API 参考\n\n### 网关侧 API(`/api/gateway`)\n\n这些端点由 `GatewayConfigController` 提供,运行在网关上,供客户端远程调用。\n\n| 端点 | 方法 | 说明 |\n|------|------|------|\n| `/api/gateway/register-route` | `POST` | 注册/更新路由(含数据验证,支持 Transforms) |\n| `/api/gateway/{routeName}` | `DELETE` | 注销路由(无关联路由时自动清理集群) |\n| `/api/gateway/routes` | `GET` | 查询所有已注册路由 |\n| `/api/gateway/ping` | `GET` | 健康检查 |\n\n**注册路由请求体:**\n\n```json\n{\n \"routeName\": \"my-service\",\n \"clusterName\": \"my-service-cluster\",\n \"matchPath\": \"/api/my-service/{**catch-all}\",\n \"destinationAddress\": \"http://192.168.1.101:5001\",\n \"order\": 50,\n \"transforms\": [\n { \"PathSet\": \"/api/backend/{**catch-all}\" },\n { \"RequestHeader\": \"X-Forwarded-By\", \"Set\": \"gateway\" }\n ]\n}\n```\n\n| 字段 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `routeName` | `string` | ✅ | 路由唯一标识(≤200 字符) |\n| `clusterName` | `string` | ✅ | 集群名称(≤200 字符) |\n| `matchPath` | `string` | ✅ | 路径匹配模板 |\n| `destinationAddress` | `string` | ✅ | 目标 URL |\n| `order` | `int` | — | 优先级(默认 50) |\n| `transforms` | `array` | — | 路由转换规则(见下方示例) |\n\n**响应格式(所有端点统一):**\n\n```json\n// 成功\n{ \"code\": 200, \"info\": \"路由 'my-service' 注册成功\" }\n\n// 带数据\n{ \"code\": 200, \"data\": [{ \"routeId\": \"...\", \"clusterId\": \"...\", \"path\": \"...\", \"order\": 50 }] }\n\n// 失败\n{ \"code\": 400, \"info\": \"路由名称不能为空; 目标地址必须是有效的 URL\" }\n```\n\n### 仪表盘 API(`/apigateway`)\n\n| 端点 | 说明 |\n|------|------|\n| `GET /apigateway` | 仪表盘首页(HTML) |\n| `GET /apigateway/login` | 登录页面(HTML) |\n| `POST /apigateway/login` | 登录接口(返回 JWT token) |\n| `GET /apigateway/info` | 网关运行信息(版本/环境/运行时间/内存) |\n| `GET /apigateway/clusters` | 集群状态(目标列表/健康/不健康/未知) |\n| `GET /apigateway/routes` | 路由配置列表(路径/方法/优先级/Transforms) |\n| `GET /apigateway/logs` | 最近 YARP 代理日志 |\n| `DELETE /apigateway/logs` | 清空日志缓冲区 |\n\n### 扩展方法\n\n| 命名空间 | 方法 | 说明 |\n|----------|------|------|\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpGateway()` | ⭐ 一键搭建网关 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpGateway(Action<GatewayRegistrationOptions>)` | 一键 + 自定义 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpClient()` | ⭐ 一键客户端接入 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpClient(Action<GatewayRegistrationOptions>)` | 一键 + 自定义 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarp()` | 🔧 核心网关组件 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpGatewayClient()` | 🔧 客户端组件 |\n| `Aneiang.Yarp.Extensions` | `UseAneiangYarpGateway()` | 手动控制注册/注销时机 |\n| `Aneiang.Yarp.Dashboard.Extensions` | `AddAneiangYarpDashboard()` | 仪表盘 |\n| `Aneiang.Yarp.Dashboard.Extensions` | `AddAneiangYarpDashboard(Action<DashboardOptions>)` | 仪表盘 + 自定义选项 |\n\n---\n\n## 高级用法\n\n### 1. 多级网关链\n\n```\n外网 Gateway → 内网 Gateway → 本机调试服务\n```\n\n```csharp\n// 内网网关(同时作为上级网关的客户端)\nbuilder.Services.AddAneiangYarpGateway(options =>\n{\n options.GatewayUrl = \"http://outer-gateway:5000\"; // 连到外网网关\n});\n```\n\n### 2. 自定义中间件管道\n\n网关项目中可以自由插入中间件,YARP 转发前会经过完整管道:\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication(); // 认证\napp.UseAuthorization(); // 授权\napp.UseRequestLogging(); // 日志\napp.UseRateLimiter(); // 限流\n\napp.UseRouting();\napp.MapControllers();\napp.MapReverseProxy(); // YARP 必须放最后\n```\n\n### 3. 根据环境切换配置\n\n```json\n// appsettings.Development.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"MatchPath\": \"/api/dev/{**catch-all}\"\n }\n }\n}\n\n// appsettings.Production.json\n{\n \"Gateway\": {\n \"Registration\": {\n \"GatewayUrl\": \"http://gateway.internal:5000\"\n }\n }\n}\n```\n\n### 4. 手动 API 调用(绕过自动注册)\n\n```bash\n# 注册\ncurl -X POST http://gateway:5000/api/gateway/register-route \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"routeName\": \"manual-route\",\n \"clusterName\": \"manual-cluster\",\n \"matchPath\": \"/api/manual/{**catch-all}\",\n \"destinationAddress\": \"http://192.168.1.101:5001\",\n \"order\": 50\n }'\n\n# 查询\ncurl http://gateway:5000/api/gateway/routes\n\n# 注销\ncurl -X DELETE http://gateway:5000/api/gateway/manual-route\n```\n\n### 5. 内网 IP 自动解析机制\n\n当 `DestinationAddress` 包含 `localhost` / `127.0.0.1` / `0.0.0.0` 时,会自动解析为本机内网 IPv4 地址:\n\n```\n配置值: http://localhost:5001\n解析后: http://192.168.1.101:5001 (本机内网 IP)\n```\n\n这样网关才能从内网访问到本机。可通过 `AutoResolveIp = false` 关闭。\n\n### 6. 多人协作:实例隔离模式\n\n**实例隔离默认已开启**,自动将本机标识嵌入路由名称和 URL 路径中,实现完全隔离:\n\n**隔离效果(假设 DevA 在 `PC-JOHN`,DevB 在 `PC-JANE`):**\n\n| 维度 | 隔离后(互不影响) |\n|------|-------------------|\n| routeName | `my-service-PC-JOHN` / `my-service-PC-JANE` |\n| clusterName | `my-service-cluster-PC-JOHN` / `my-service-cluster-PC-JANE` |\n| matchPath | `/PC-JOHN/api/{**catch-all}` / `/PC-JANE/api/{**catch-all}` |\n\n**测试时各自使用隔离后的路径:**\n\n```bash\n# DevA 测试(请求经 PC-JOHN → 本机 localhost:5001)\ncurl http://gateway:5000/PC-JOHN/api/users\n\n# DevB 测试(请求经 PC-JANE → 本机 localhost:5001)\ncurl http://gateway:5000/PC-JANE/api/users\n```\n\n> 如果一人调试、不需要实例隔离,可以显式关闭:\n> ```csharp\n> builder.Services.AddAneiangYarpClient(options => { options.InstanceIsolation = false; });\n> ```\n\n#### 自定义实例标识\n\n```csharp\n// 方式 1:代码内指定\nbuilder.Services.AddAneiangYarpClient(options =>\n{\n options.InstanceId = \"dev-john\"; // 简短的别名\n options.InstancePrefixFormat = \"dev-{instanceId}\"; // 路径前缀格式\n});\n\n// 方式 2:配置文件(适合同项目不同人,各自改 appsettings)\n// { \"Gateway\": { \"Registration\": { \"InstanceId\": \"dev-john\" } } }\n```\n\n#### 路径前缀格式化\n\n`InstancePrefixFormat` 支持占位符替换,定制路径前缀样式:\n\n| 占位符 | 示例值 | 说明 |\n|--------|--------|------|\n| `{instanceId}` | `john` | 实例 ID |\n| `{machineName}` | `PC-JOHN` | 本机主机名 |\n| `{userName}` | `john.doe` | 当前用户名 |\n\n配置示例:\n\n```json\n{\n \"Gateway\": {\n \"Registration\": {\n \"InstanceId\": \"john\",\n \"InstancePrefixFormat\": \"dev-{userName}\"\n }\n }\n}\n```\n\n→ routeName: `my-service-dev-john.doe`\n→ matchPath: `/dev-john.doe/{**catch-all}`\n\n---\n\n## 示例项目\n\n| 项目 | 说明 | 启动命令 |\n|------|------|----------|\n| `samples/SampleGateway` | 内网网关示例(含仪表盘) | `dotnet run --project samples/SampleGateway` |\n| `samples/SampleLocalService` | 本地服务示例(自动注册到网关) | `dotnet run --project samples/SampleLocalService` |\n\n```bash\n# 终端 1:启动网关(localhost:5000)\ncd samples/SampleGateway && dotnet run\n\n# 终端 2:启动本地服务(localhost:5001,自动注册到网关)\ncd samples/SampleLocalService && dotnet run\n\n# 验证:请求流经网关 → 转发到本地服务\ncurl http://localhost:5000/api/your-endpoint\n```\n\n---\n\n## 依赖\n\n- **.NET** ≥ 6.0\n- **[YARP](https://github.com/microsoft/reverse-proxy)**(按目标框架自动匹配版本)\n- `Aneiang.Yarp.Dashboard` 依赖 `Aneiang.Yarp`\n\n---\n\n## 许可证\n\n[MIT](LICENSE)\n"}, "7bcbf1042e0e172247ce2ae88a2bb26d262f1ae7": {"t": 1785763488.0070746, "n": 13657, "h": "56248c7bec956bf8", "c": "# Aneiang.Yarp\n\n基于 [YARP (Yet Another Reverse Proxy)](https://github.com/microsoft/reverse-proxy) 的**动态路由网关库**,专注于解决内网微服务调试场景——让本机单体服务通过内网网关的中间件管道后转发到本机,实现\"调试即内网互通\"。\n\n### 核心能力\n\n| 能力 | 说明 |\n|------|------|\n| **动态路由注册** | Gateway 提供 REST API,允许客户端运行时注册/更新/注销反向代理路由 |\n| **自动注册客户端** | 本地服务启动时自动向网关注册路由,关闭时自动注销——**一行代码**搞定 |\n| **一键式 API** | `AddAneiangYarpGateway()` / `AddAneiangYarpClient()` 一行搭建网关或客户端 |\n| **实例隔离** | 多人调试同一服务时自动隔离 routing 命名空间,互不干扰 |\n| **高度可定制** | 支持配置优先级:代码 > 环境变量 > 配置文件;组件级 API 精细控制 |\n| **仪表盘(可选)** | 实时查看集群、路由、健康状态、Seq 日志摘要 |\n| **智能默认值** | `routeName` 取程序集名、`matchPath` 默认全路径转发、`destinationAddress` 自动从 Kestrel 获取,`localhost` 自动解析为内网 IP |\n\n### 场景示意\n\n```\n外网 client ──→ 内网网关 [ 中间件(Auth/Logging/限流) → YARP 转发 ]\n ▲ │\n │ POST /register-route │ 转发\n │ ▼\n ┌─ 本机调试服务 ─────────────────┐\n │ AddAneiangYarpClient() │\n │ → 启动注册 / 关闭注销 │\n └────────────────────────────────┘\n```\n\n---\n\n## 项目结构\n\n```\nAneiang.Yarp/\n├── src/\n│ ├── Aneiang.Yarp/ # 基础库:动态路由 + 自动注册客户端\n│ │ ├── Controllers/\n│ │ │ └── GatewayConfigController.cs # 网关侧 REST API\n│ │ ├── Extensions/\n│ │ │ ├── YarpServiceCollectionExtensions.cs # DI 注册扩展\n│ │ │ └── YarpApplicationExtensions.cs # 中间件扩展\n│ │ ├── Models/\n│ │ │ ├── RegisterRouteRequest.cs # 注册请求 DTO\n│ │ │ └── GatewayRegistrationOptions.cs # 配置选项(智能默认值)\n│ │ └── Services/\n│ │ ├── DynamicYarpConfigService.cs # 动态路由管理\n│ │ ├── GatewayAutoRegistrationClient.cs # 客户端注册服务\n│ │ └── GatewayRegistrationHostedService.cs # 托管服务(自动注册/注销)\n│ │\n│ └── Aneiang.Yarp.Dashboard/ # 仪表盘库(可选)\n│ ├── Controllers/\n│ │ └── DashboardController.cs # 仪表盘 API\n│ ├── Views/\n│ │ └── Dashboard/Index.cshtml # 仪表盘 UI\n│ └── Extensions/\n│ └── YarpServiceCollectionExtensions.cs\n│\n└── samples/\n ├── SampleGateway/ # 网关项目示例\n └── SampleLocalService/ # 本地服务(客户端)示例\n```\n\n### NuGet 包\n\n| 包名 | 说明 | 必需 |\n|------|------|------|\n| `Aneiang.Yarp` | 基础库:动态路由 + 自动注册客户端 | ✅ |\n| `Aneiang.Yarp.Dashboard` | 仪表盘:监控运维 UI | 可选 |\n\n- **目标框架**:`net6.0` / `net7.0` / `net8.0` / `net9.0`\n- **YARP 版本**:net6.0 → 2.1.0 / net7.0 → 2.2.0 / net8.0+ → 2.3.0\n\n---\n\n## 快速开始\n\n### ① 搭建内网网关(Gateway 角色)\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ 一行搭建网关\nbuilder.Services.AddAneiangYarpGateway();\n\n// 可选:添加仪表盘\nbuilder.Services.AddAneiangYarpDashboard();\n\nvar app = builder.Build();\n\napp.UseRouting();\napp.MapControllers(); // GatewayConfigController API\napp.MapReverseProxy(); // YARP 反向代理\n\napp.Run();\n```\n\n**`appsettings.json`**(静态路由可选):\n\n```json\n{\n \"ReverseProxy\": {\n \"Routes\": {\n \"my-api\": {\n \"ClusterId\": \"my-api-cluster\",\n \"Match\": { \"Path\": \"/api/stable/{**catch-all}\" }\n }\n },\n \"Clusters\": {\n \"my-api-cluster\": {\n \"Destinations\": {\n \"d1\": { \"Address\": \"http://internal-server:8080/\" }\n }\n }\n }\n }\n}\n```\n\n### ② 本地服务接入(Client 角色)\n\n```csharp\n// Program.cs\nusing Aneiang.Yarp.Extensions;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n// ⭐ 一行搞定:启动自动注册 → 关闭自动注销\nbuilder.Services.AddAneiangYarpClient();\n\n// 你的业务代码\nbuilder.Services.AddControllers();\n\nvar app = builder.Build();\napp.UseRouting();\napp.MapControllers();\napp.Run();\n```\n\n**`appsettings.json`**(最少只需一行):\n\n```json\n{\n \"GatewayRegistration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\"\n }\n}\n```\n\n> **智能默认值一览:**\n> \n> | 字段 | 默认值 | 来源 |\n> |------|--------|------|\n> | `RouteName` | 入口程序集名称 | `Assembly.GetEntryAssembly()` |\n> | `ClusterName` | 同 `RouteName` | — |\n> | `MatchPath` | `/{**catch-all}` | — |\n> | `DestinationAddress` | Kestrel 绑定地址 | `Urls` 配置 |\n> | `Order` | `50` | — |\n> | `AutoResolveIp` | `true` | `localhost` → 内网 IP |\n> | `TimeoutSeconds` | `5` | — |\n> | `Enabled` | 有 `GatewayUrl` 即启用 | 自动判断 |\n> | `InstanceIsolation` | `true` | 开启 |\n> | `InstanceId` | `Environment.MachineName` | 本机主机名 |\n\n---\n\n## API 设计原则:三种级别,按需选择\n\n```\n 一键式 可定制 精细化\n (1 行) (1 行 + λ) (完全控制)\n ────── ───────── ────────\n Gateway: Gateway: AddReverseProxy()\n AddAneiangYarp AddAneiangYarp AddAneiangYarp()\n Gateway() Gateway(o => {}) AddControllers()\n AddHttpClient()\n Client: Client:\n AddAneiangYarp AddAneiangYarp AddAneiangYarp\n Client() Client(o => {}) GatewayClient()\n```\n\n### ⭐ 一键式 API\n\n| 方法 | 用途 | 自动注册的服务 |\n|------|------|---------------|\n| `AddAneiangYarpGateway()` | 搭建网关 | `AddReverseProxy` + `AddAneiangYarp` + `AddControllersWithViews` + `AddHttpClient` |\n| `AddAneiangYarpClient()` | 客户端接入 | `AddHttpClient` + `GatewayAutoRegistrationClient` + `GatewayRegistrationHostedService`(自动注册/注销) |\n\n### 🔧 可定制 API\n\n```csharp\n// 自定义网关注册选项(优先级高于配置文件)\nbuilder.Services.AddAneiangYarpClient(options =>\n{\n options.GatewayUrl = \"http://192.168.1.100:5000\";\n options.MatchPath = \"/api/users/{**catch-all}\";\n options.Order = 10; // 高优先级\n options.AutoResolveIp = true; // localhost → 内网 IP\n options.TimeoutSeconds = 10;\n});\n\n// 网关也可以配置向上级网关注册\nbuilder.Services.AddAneiangYarpGateway(options =>\n{\n options.GatewayUrl = \"http://upstream-gateway:5000\";\n});\n```\n\n### 🛠 精细化 API\n\n```csharp\n// 完全手动控制 DI 注册顺序\nbuilder.Services.AddReverseProxy();\nbuilder.Services.AddAneiangYarp(); // 核心服务(不含 AddHttpClient)\nbuilder.Services.AddControllersWithViews();\nbuilder.Services.AddHttpClient();\n\n// 客户端组件级注册(不含自动注册/注销)\nbuilder.Services.AddAneiangYarpGatewayClient();\n// 需要手动调用注册/注销:\napp.UseAneiangYarpGateway();\n```\n\n---\n\n## 配置参考\n\n### `GatewayRegistration` 节\n\n所有字段均为可选(除 `GatewayUrl` 外),缺失时使用智能默认值。\n\n```json\n{\n \"GatewayRegistration\": {\n \"GatewayUrl\": \"http://192.168.1.100:5000\",\n \"RouteName\": \"my-service\",\n \"ClusterName\": \"my-service-cluster\",\n \"MatchPath\": \"/api/my-service/{**catch-all}\",\n \"DestinationAddress\": \"http://localhost:5001\",\n \"Order\": 50,\n \"AutoResolveIp\": true,\n \"TimeoutSeconds\": 10,\n \"InstanceIsolation\": true,\n \"InstanceId\": \"john\"\n }\n}\n```\n\n| 字段 | 类型 | 必填 | 默认值 | 说明 |\n|------|------|------|--------|------|\n| `GatewayUrl` | `string` | ✅ | — | 网关服务地址 |\n| `RouteName` | `string` | — | 程序集名 | 路由唯一标识 |\n| `ClusterName` | `string` | — | 同 RouteName | 集群名称 |\n| `MatchPath` | `string` | — | `/{**catch-all}` | 路径匹配模板 |\n| `DestinationAddress` | `string` | — | Kestrel 绑定地址 | 本机目标地址 |\n| `Order` | `int` | — | `50` | 路由优先级(越小越优先) |\n| `AutoResolveIp` | `bool` | — | `true` | 自动 localhost → 内网 IP |\n| `TimeoutSeconds` | `int` | — | `5` | HTTP 请求超时(秒) |\n| `Enabled` | `bool` | — | 有 GatewayUrl 即 true | 是否启用 |\n| `InstanceIsolation` | `bool` | — | `true` | 开启实例隔离 |\n| `InstanceId` | `string` | — | `Environment.MachineName` | 自定义实例标识 |\n| `InstancePrefixFormat` | `string` | — | `{instanceId}` | 路径前缀格式模板 |\n\n**配置优先级:代码 `options => {}` > 环境变量 `GatewayRegistration__GatewayUrl` > 配置文件 `appsettings.json`**\n\n### `ReverseProxy` 节\n\n标准 YARP 配置,详见 [YARP 官方文档](https://microsoft.github.io/reverse-proxy/articles/config-files.html)。支持两种 `Match` 写法:\n\n```json\n{\n \"ReverseProxy\": {\n \"Routes\": {\n \"my-route\": {\n \"ClusterId\": \"my-cluster\",\n \"Match\": \"/api/test/{**catch-all}\" // 简化写法\n // 或标准写法:\n // \"Match\": { \"Path\": \"/api/test/{**catch-all}\", \"Methods\": [\"GET\"] }\n }\n },\n \"Clusters\": {\n \"my-cluster\": {\n \"Destinations\": {\n \"d1\": { \"Address\": \"http://localhost:5000/\" }\n },\n \"LoadBalancingPolicy\": \"PowerOfTwoChoices\" // 可选\n }\n }\n }\n}\n```\n\n---\n\n## API 参考\n\n### 网关侧 API(`/api/gateway`)\n\n这些端点由 `GatewayConfigController` 提供,运行在网关上,供客户端远程调用。\n\n| 端点 | 方法 | 说明 |\n|------|------|------|\n| `/api/gateway/register-route` | `POST` | 注册/更新路由(含数据验证) |\n| `/api/gateway/{routeName}` | `DELETE` | 注销路由(无关联路由时自动清理集群) |\n| `/api/gateway/routes` | `GET` | 查询所有已注册路由 |\n| `/api/gateway/ping` | `GET` | 健康检查 |\n\n**注册路由请求体:**\n\n```json\n{\n \"routeName\": \"my-service\",\n \"clusterName\": \"my-service-cluster\",\n \"matchPath\": \"/api/my-service/{**catch-all}\",\n \"destinationAddress\": \"http://192.168.1.101:5001\",\n \"order\": 50\n}\n```\n\n| 字段 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `routeName` | `string` | ✅ | 路由唯一标识(≤200 字符) |\n| `clusterName` | `string` | ✅ | 集群名称(≤200 字符) |\n| `matchPath` | `string` | ✅ | 路径匹配模板 |\n| `destinationAddress` | `string` | ✅ | 目标 URL |\n| `order` | `int` | — | 优先级(默认 50) |\n\n**响应格式(所有端点统一):**\n\n```json\n// 成功\n{ \"code\": 200, \"info\": \"路由 'my-service' 注册成功\" }\n\n// 带数据\n{ \"code\": 200, \"data\": [{ \"routeId\": \"...\", \"clusterId\": \"...\", \"path\": \"...\", \"order\": 50 }] }\n\n// 失败\n{ \"code\": 400, \"info\": \"路由名称不能为空; 目标地址必须是有效的 URL\" }\n```\n\n### 仪表盘 API(`/apigateway`)\n\n| 端点 | 说明 |\n|------|------|\n| `GET /apigateway` | 仪表盘首页(HTML) |\n| `GET /apigateway/info` | 网关运行信息(版本/环境/运行时间/内存) |\n| `GET /apigateway/clusters` | 集群状态(目标列表/健康/不健康/未知) |\n| `GET /apigateway/routes` | 路由配置列表(路径/方法/优先级) |\n\n### 扩展方法\n\n| 命名空间 | 方法 | 说明 |\n|----------|------|------|\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpGateway()` | ⭐ 一键搭建网关 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpGateway(Action<GatewayRegistrationOptions>)` | 一键 + 自定义 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpClient()` | ⭐ 一键客户端接入 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpClient(Action<GatewayRegistrationOptions>)` | 一键 + 自定义 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarp()` | 🔧 核心网关组件 |\n| `Aneiang.Yarp.Extensions` | `AddAneiangYarpGatewayClient()` | 🔧 客户端组件 |\n| `Aneiang.Yarp.Extensions` | `UseAneiangYarpGateway()` | 手动控制注册/注销时机 |\n| `Aneiang.Yarp.Dashboard.Extensions` | `AddAneiangYarpDashboard()` | 仪表盘 |\n\n---\n\n## 高级用法\n\n### 1. 多级网关链\n\n```\n外网 Gateway → 内网 Gateway → 本机调试服务\n```\n\n```csharp\n// 内网网关(同时作为上级网关的客户端)\nbuilder.Services.AddAneiangYarpGateway(options =>\n{\n options.GatewayUrl = \"http://outer-gateway:5000\"; // 连到外网网关\n});\n```\n\n### 2. 自定义中间件管道\n\n网关项目中可以自由插入中间件,YARP 转发前会经过完整管道:\n\n```csharp\nvar app = builder.Build();\n\napp.UseAuthentication(); // 认证\napp.UseAuthorization(); // 授权\napp.UseRequestLogging(); // 日志\napp.UseRateLimiter(); // 限流\n\napp.UseRouting();\napp.MapControllers();\napp.MapReverseProxy(); // YARP 必须放最后\n```\n\n### 3. 根据环境切换配置\n\n```json\n// appsettings.Development.json\n{\n \"GatewayRegistration\": {\n \"GatewayUrl\": \"http://localhost:5000\",\n \"MatchPath\": \"/api/dev/{**catch-all}\"\n }\n}\n\n// appsettings.Production.json\n{\n \"GatewayRegistration\": {\n \"GatewayUrl\": \"http://gateway.internal:5000\"\n }\n}\n```\n\n### 4. 手动 API 调用(绕过自动注册)\n\n```bash\n# 注册\ncurl -X POST http://gateway:5000/api/gateway/register-route \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"routeName\": \"manual-route\",\n \"clusterName\": \"manual-cluster\",\n \"matchPath\": \"/api/manual/{**catch-all}\",\n \"destinationAddress\": \"http://192.168.1.101:5001\",\n \"order\": 50\n }'\n\n# 查询\ncurl http://gateway:5000/api/gateway/routes\n\n# 注销\ncurl -X DELETE http://gateway:5000/api/gateway/manual-route\n```\n\n### 5. 内网 IP 自动解析机制\n\n当 `DestinationAddress` 包含 `localhost` / `127.0.0.1` / `0.0.0.0` 时,会自动解析为本机内网 IPv4 地址:\n\n```\n配置值: http://localhost:5001\n解析后: http://192.168.1.101:5001 (本机内网 IP)\n```\n\n这样网关才能从内网访问到本机。可通过 `AutoResolveIp = false` 关闭。\n\n### 6. 多人协作:实例隔离模式\n\n**实例隔离默认已开启**,自动将本机标识嵌入路由名称和 URL 路径中,实现完全隔离:\n\n**隔离效果(假设 DevA 在 `PC-JOHN`,DevB 在 `PC-JANE`):**\n\n| 维度 | 隔离后(互不影响) |\n|------|-------------------|\n| routeName | `my-service-PC-JOHN` / `my-service-PC-JANE` |\n| clusterName | `my-service-cluster-PC-JOHN` / `my-service-cluster-PC-JANE` |\n| matchPath | `/PC-JOHN/api/{**catch-all}` / `/PC-JANE/api/{**catch-all}` |\n\n**测试时各自使用隔离后的路径:**\n\n```bash\n# DevA 测试(请求经 PC-JOHN → 本机 localhost:5001)\ncurl http://gateway:5000/PC-JOHN/api/users\n\n# DevB 测试(请求经 PC-JANE → 本机 localhost:5001)\ncurl http://gateway:5000/PC-JANE/api/users\n```\n\n> 如果一人调试、不需要实例隔离,可以显式关闭:\n> ```csharp\n> builder.Services.AddAneiangYarpClient(options => { options.InstanceIsolation = false; });\n> ```\n\n#### 自定义实例标识\n\n```csharp\n// 方式 1:代码内指定\nbuilder.Services.AddAneiangYarpClient(options =>\n{\n options.InstanceId = \"dev-john\"; // 简短的别名\n options.InstancePrefixFormat = \"dev-{instanceId}\"; // 路径前缀格式\n});\n\n// 方式 2:配置文件(适合同项目不同人,各自改 appsettings)\n// { \"GatewayRegistration\": { \"InstanceId\": \"dev-john\" } }\n```\n\n#### 路径前缀格式化\n\n`InstancePrefixFormat` 支持占位符替换,定制路径前缀样式:\n\n| 占位符 | 示例值 | 说明 |\n|--------|--------|------|\n| `{instanceId}` | `john` | 实例 ID |\n| `{machineName}` | `PC-JOHN` | 本机主机名 |\n| `{userName}` | `john.doe` | 当前用户名 |\n\n配置示例:\n\n```json\n{\n \"GatewayRegistration\": {\n \"InstanceId\": \"john\",\n \"InstancePrefixFormat\": \"dev-{userName}\"\n }\n}\n```\n\n→ routeName: `my-service-dev-john.doe`\n→ matchPath: `/dev-john.doe/{**catch-all}`\n\n---\n\n## 示例项目\n\n| 项目 | 说明 | 启动命令 |\n|------|------|----------|\n| `samples/SampleGateway` | 内网网关示例(含仪表盘) | `dotnet run --project samples/SampleGateway` |\n| `samples/SampleLocalService` | 本地服务示例(自动注册到网关) | `dotnet run --project samples/SampleLocalService` |\n\n```bash\n# 终端 1:启动网关(localhost:5000)\ncd samples/SampleGateway && dotnet run\n\n# 终端 2:启动本地服务(localhost:5001,自动注册到网关)\ncd samples/SampleLocalService && dotnet run\n\n# 验证:请求流经网关 → 转发到本地服务\ncurl http://localhost:5000/api/your-endpoint\n```\n\n---\n\n## 依赖\n\n- **.NET** ≥ 6.0\n- **[YARP](https://github.com/microsoft/reverse-proxy)**(按目标框架自动匹配版本)\n- `Aneiang.Yarp.Dashboard` 依赖 `Aneiang.Yarp`\n\n---\n\n## 许可证\n\n[MIT](LICENSE)\n"}}}}