feat(cnnl-service): 添加根路径 API 文档页面

- 新增 GET / 路由,返回 HTML 格式的 API 文档页面
- 页面展示所有 5 个 API 端点说明
- 深色主题,符合 NAC 技术风格
- 包含 curl 快速示例和服务信息卡片
- 修复浏览器访问根路径返回 404 的问题
This commit is contained in:
NAC Core Team 2026-02-28 09:05:26 +08:00
parent 9b8a21b321
commit c57b5216f7
1 changed files with 224 additions and 0 deletions

View File

@ -394,6 +394,229 @@ async fn handle_version(_state: web::Data<AppState>) -> impl Responder {
HttpResponse::Ok().json(ApiResponse::ok(version, 0))
}
// ============================================================
// 根路径 - API 文档页面
// ============================================================
async fn handle_root() -> impl Responder {
let html = r#"<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NAC CNNL </title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
background: #0a0e1a;
color: #c9d1d9;
min-height: 100vh;
padding: 40px 20px;
}
.container { max-width: 900px; margin: 0 auto; }
.header {
text-align: center;
padding: 60px 0 40px;
border-bottom: 1px solid #21262d;
margin-bottom: 40px;
}
.logo {
font-size: 48px;
font-weight: 700;
background: linear-gradient(135deg, #58a6ff, #3fb950, #f78166);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
letter-spacing: -1px;
}
.subtitle {
color: #8b949e;
font-size: 16px;
margin-top: 12px;
}
.badge {
display: inline-block;
background: #1f6feb;
color: #fff;
font-size: 12px;
padding: 3px 10px;
border-radius: 20px;
margin-top: 10px;
font-weight: 500;
}
.section { margin-bottom: 40px; }
h2 {
font-size: 20px;
color: #e6edf3;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #21262d;
}
.endpoint-grid { display: grid; gap: 12px; }
.endpoint {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 16px 20px;
display: flex;
align-items: flex-start;
gap: 16px;
}
.method {
font-size: 12px;
font-weight: 700;
padding: 4px 10px;
border-radius: 4px;
min-width: 52px;
text-align: center;
flex-shrink: 0;
margin-top: 2px;
}
.method.get { background: #1a7f37; color: #3fb950; border: 1px solid #238636; }
.method.post { background: #1a4a7f; color: #58a6ff; border: 1px solid #1f6feb; }
.endpoint-info { flex: 1; }
.path {
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 14px;
color: #e6edf3;
font-weight: 600;
}
.desc { color: #8b949e; font-size: 13px; margin-top: 4px; }
.code-block {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 20px;
font-family: "SFMono-Regular", Consolas, monospace;
font-size: 13px;
overflow-x: auto;
line-height: 1.6;
}
.code-block .key { color: #79c0ff; }
.code-block .str { color: #a5d6ff; }
.code-block .num { color: #f2cc60; }
.code-block .bool { color: #ff7b72; }
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
}
.info-card {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 16px;
}
.info-label { color: #8b949e; font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
.info-value { color: #e6edf3; font-size: 15px; font-weight: 600; margin-top: 4px; }
.footer {
text-align: center;
color: #484f58;
font-size: 13px;
padding-top: 40px;
border-top: 1px solid #21262d;
margin-top: 40px;
}
a { color: #58a6ff; text-decoration: none; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="logo">NAC CNNL</div>
<div class="subtitle"></div>
<div class="badge">v0.1.0 · </div>
</div>
<div class="section">
<h2>API </h2>
<div class="endpoint-grid">
<div class="endpoint">
<span class="method post">POST</span>
<div class="endpoint-info">
<div class="path">/api/v1/compile</div>
<div class="desc"> CNNL NVM </div>
</div>
</div>
<div class="endpoint">
<span class="method post">POST</span>
<div class="endpoint-info">
<div class="path">/api/v1/parse</div>
<div class="desc"> CNNL AST</div>
</div>
</div>
<div class="endpoint">
<span class="method post">POST</span>
<div class="endpoint-info">
<div class="path">/api/v1/validate</div>
<div class="desc"> CNNL </div>
</div>
</div>
<div class="endpoint">
<span class="method get">GET</span>
<div class="endpoint-info">
<div class="path">/api/v1/health</div>
<div class="desc"></div>
</div>
</div>
<div class="endpoint">
<span class="method get">GET</span>
<div class="endpoint-info">
<div class="path">/api/v1/version</div>
<div class="desc"></div>
</div>
</div>
</div>
</div>
<div class="section">
<h2></h2>
<div class="code-block">
<span style="color:#8b949e"># CNNL </span>
curl -X POST https://cnnl.newassetchain.io/api/v1/compile \
-H <span class="str">"Content-Type: application/json"</span> \
-d <span class="str">'{
"source": "clause XTZH_GOLD_COVERAGE {\n level: eternal\n title: \"黄金储备覆盖率底线\"\n parameter XTZH_GOLD_COVERAGE_MIN: f64 = 1.25\n}",
"generate_state": true
}'</span>
</div>
</div>
<div class="section">
<h2></h2>
<div class="info-grid">
<div class="info-card">
<div class="info-label"></div>
<div class="info-value">nac-cnnl-service</div>
</div>
<div class="info-card">
<div class="info-label"></div>
<div class="info-value">CNNL v0.1.0</div>
</div>
<div class="info-card">
<div class="info-label"></div>
<div class="info-value">NVM (NAC VM)</div>
</div>
<div class="info-card">
<div class="info-label"></div>
<div class="info-value">HTTPS / TLS 1.3</div>
</div>
</div>
</div>
<div class="footer">
<p>NAC NewAssetChain · CNNL · <a href="https://git.newassetchain.io/nacadmin/NAC_Blockchain"></a></p>
</div>
</div>
</body>
</html>"#;
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(html)
}
// ============================================================
// 主函数
// ============================================================
@ -420,6 +643,7 @@ async fn main() -> std::io::Result<()> {
.app_data(app_state.clone())
.app_data(web::JsonConfig::default().limit(1024 * 1024)) // 1MB 请求体限制
// API 路由
.route("/", web::get().to(handle_root))
.route("/api/v1/compile", web::post().to(handle_compile))
.route("/api/v1/parse", web::post().to(handle_parse))
.route("/api/v1/validate", web::post().to(handle_validate))