feat: OpsxAPI v1.0 全协议接口工作台

- 六协议调试:HTTP/GraphQL/WebSocket/SSE/Socket.IO/MQTT
- 集合树/多标签/多环境变量/历史记录,localStorage 持久化
- 导入:OpenAPI 2/3/3.1、Postman、Apifox、Apipost、cURL、HAR、
  Insomnia、Hoppscotch、Markdown(自动识别,21 个单测覆盖)
- 导出:OpenAPI 3.0 JSON/YAML、Postman v2.1、Markdown 文档、
  全量备份;单请求生成 cURL/fetch/Python/Go 代码
- Gin 服务:HTTP 代理、NDJSON 流式代理(SSE 透传)、
  WebSocket 隧道(浏览器自定义握手 Header)
- 内置产品官网 /home 与用户文档站 /docs(SSR,随二进制交付)
- lucide 图标体系 + 品牌标识(Logo/favicon),明暗双主题
- 环境变量:彩色徽标切换器、管理弹窗、{{ 自动补全
This commit is contained in:
logan committed 2026-09-18 00:16:59 +08:00
commit dec66db5aa
182 files changed
+23377

No files matched your search

+63
View File
@@ -0,0 +1,63 @@
package site
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gin-gonic/gin"
)
func newTestRouter(t *testing.T) *gin.Engine {
t.Helper()
gin.SetMode(gin.TestMode)
r := gin.New()
New().Register(r)
return r
}
func TestSitePageRender(t *testing.T) {
r := newTestRouter(t)
for _, path := range []string{"/", "/home", "/index.html"} {
w := httptest.NewRecorder()
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, path, nil))
if w.Code != http.StatusOK {
t.Fatalf("%s: expect 200, got %d", path, w.Code)
}
body := w.Body.String()
for _, want := range []string{
"<title>OpsxAPI", // 标题
`id="features"`, // 功能区
`id="pricing"`, // 定价区
"¥99", // 入门价
"¥8,000", // 企业档标价
"¥30,000", // 买断标价
"¥5,000–8,000", // 成交区间
"永久买断", // 买断 FAQ
"早鸟创始价", // 让利策略
"Socket.IO", // 协议覆盖
"hello@opsxapi.dev", // 联系方式
`href="/docs"`, // 文档互链
`href="/site/assets/site.css"`, // 样式资源
} {
if !strings.Contains(body, want) {
t.Fatalf("%s: missing %q", path, want)
}
}
}
}
func TestSiteAsset(t *testing.T) {
r := newTestRouter(t)
w := httptest.NewRecorder()
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/site/assets/site.css", nil))
if w.Code != http.StatusOK || !strings.Contains(w.Header().Get("Content-Type"), "text/css") {
t.Fatalf("css: %d %s", w.Code, w.Header().Get("Content-Type"))
}
w = httptest.NewRecorder()
r.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/site/assets/../template.html", nil))
if w.Code != http.StatusBadRequest && w.Code != http.StatusNotFound {
t.Fatalf("path traversal should be rejected, got %d", w.Code)
}
}