1 line
519 KiB
JSON
1 line
519 KiB
JSON
{"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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities | [](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Abstractions) |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite storage with SQLCipher encryption | [](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Sqlite) |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol | [](https://www.nuget.org/packages/Aneiang.Yarp.Grpc) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities | [](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Abstractions) |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite storage with SQLCipher encryption | [](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Sqlite) |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol | [](https://www.nuget.org/packages/Aneiang.Yarp.Grpc) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities | [](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Abstractions) |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite storage with SQLCipher encryption | [](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Sqlite) |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol | [](https://www.nuget.org/packages/Aneiang.Yarp.Grpc) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities | [](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Abstractions) |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite storage with SQLCipher encryption | [](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Sqlite) |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol | [](https://www.nuget.org/packages/Aneiang.Yarp.Grpc) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp.Storage.Abstractions** | Storage interfaces & entities | [](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Abstractions) |\n| **Aneiang.Yarp.Storage.Sqlite** | SQLite storage with SQLCipher encryption | [](https://www.nuget.org/packages/Aneiang.Yarp.Storage.Sqlite) |\n| **Aneiang.Yarp.Grpc** | gRPC registration protocol | [](https://www.nuget.org/packages/Aneiang.Yarp.Grpc) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [](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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](https://dotnet.microsoft.com/)\n[](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 · `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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dep) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web admin UI with full management capabilities | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n\n**.NET 8.0 / 9.0** · **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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dependency) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web management UI with auth, logs, and config management | [](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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dependency) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web management UI with auth, logs, and config management | [](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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Client)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dependency) | [](https://www.nuget.org/packages/Aneiang.Yarp.Client) |\n| **Aneiang.Yarp.Dashboard** | Web management UI with auth, logs, and config management | [](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[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Gateway core library (standalone) | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Client** | Client auto-registration (lightweight, no YARP dependency) | [](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[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [](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[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [](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[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [](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[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [](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[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [](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[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [](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[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp.Dashboard) |\n| **Aneiang.Yarp** | Core library (standalone) | [](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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](LICENSE)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Dashboard** | 🌟 **Recommended**: Dashboard for monitoring & operations | ❌ Requires core | [](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\n\n### Route Configuration\n\n\n\n### Log Viewer\n\n\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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](LICENSE)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Dashboard** | 🌟 **Recommended**: Dashboard for monitoring & operations | ❌ Requires core | [](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\n\n### Route Configuration\n\n\n\n### Log Viewer\n\n\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[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](https://www.nuget.org/packages/Aneiang.Yarp)\n[](LICENSE)\n[](https://github.com/microsoft/reverse-proxy)\n[](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 | [](https://www.nuget.org/packages/Aneiang.Yarp) |\n| **Aneiang.Yarp.Dashboard** | 🌟 **Recommended**: Dashboard for monitoring & operations | [](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\n\n### Route Configuration\n\n\n\n### Log Viewer\n\n\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"}}}} |