桌面 GUI 外链修复 + 主题顺序调整(含结构扁平化进行中的工作区基线)
- 集成 tauri-plugin-opener:Cargo/lib.rs/capabilities/npm 四处接线,新增 src/openExternal.ts(桌面走 opener,浏览器回退 window.open,相对路径绝对化) - 关于页 4 个链接、活动栏官网菜单、代理错误横幅 2 处健康检查链接全部改走 openExternal,桌面端可正常跳转 - 设置→外观 主题顺序调整为 深色/浅色/跟随系统 - release 二进制重编并验证(浏览器实测关于页链接可开文档页) - 附带工作区中未提交的扁平化重构现状(另一会话进行中) - homepage 落地页与文档终检通过(8787 / 与 /docs 双 200)
This commit is contained in:
1 parent
6452026fd6
commit
2bb4ef7a95
299 files changed
+6059
-3322
No files matched your search
@@ -0,0 +1,154 @@
|
||||
// Package echo 提供本地 HTTP 回显,供自动化回归套件使用(不依赖外网 httpbin)。
|
||||
package echo
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Mount registers /v1/echo/* routes.
|
||||
func Mount(r *gin.Engine) {
|
||||
e := r.Group("/v1/echo")
|
||||
e.GET("/get", getEcho)
|
||||
e.POST("/json", jsonEcho)
|
||||
e.POST("/form", formEcho)
|
||||
e.POST("/multipart", multipartEcho)
|
||||
e.GET("/headers", headersEcho)
|
||||
e.GET("/cookies", cookiesEcho)
|
||||
e.GET("/bearer", bearerEcho)
|
||||
e.GET("/basic", basicEcho)
|
||||
}
|
||||
|
||||
func baseMeta(c *gin.Context) gin.H {
|
||||
hdrs := gin.H{}
|
||||
for k, v := range c.Request.Header {
|
||||
if len(v) == 1 {
|
||||
hdrs[k] = v[0]
|
||||
} else {
|
||||
hdrs[k] = v
|
||||
}
|
||||
}
|
||||
q := gin.H{}
|
||||
for k, v := range c.Request.URL.Query() {
|
||||
if len(v) == 1 {
|
||||
q[k] = v[0]
|
||||
} else {
|
||||
q[k] = v
|
||||
}
|
||||
}
|
||||
return gin.H{
|
||||
"ok": true,
|
||||
"service": "opsxapi-echo",
|
||||
"method": c.Request.Method,
|
||||
"path": c.Request.URL.Path,
|
||||
"query": q,
|
||||
"headers": hdrs,
|
||||
}
|
||||
}
|
||||
|
||||
func getEcho(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, baseMeta(c))
|
||||
}
|
||||
|
||||
func jsonEcho(c *gin.Context) {
|
||||
body, _ := io.ReadAll(c.Request.Body)
|
||||
out := baseMeta(c)
|
||||
out["bodyRaw"] = string(body)
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func formEcho(c *gin.Context) {
|
||||
_ = c.Request.ParseForm()
|
||||
form := gin.H{}
|
||||
for k, v := range c.Request.PostForm {
|
||||
if len(v) == 1 {
|
||||
form[k] = v[0]
|
||||
} else {
|
||||
form[k] = v
|
||||
}
|
||||
}
|
||||
out := baseMeta(c)
|
||||
out["form"] = form
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func multipartEcho(c *gin.Context) {
|
||||
_ = c.Request.ParseMultipartForm(8 << 20)
|
||||
form := gin.H{}
|
||||
if c.Request.MultipartForm != nil {
|
||||
for k, v := range c.Request.MultipartForm.Value {
|
||||
if len(v) == 1 {
|
||||
form[k] = v[0]
|
||||
} else {
|
||||
form[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
out := baseMeta(c)
|
||||
out["form"] = form
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func headersEcho(c *gin.Context) {
|
||||
out := baseMeta(c)
|
||||
out["xOpsxProbe"] = c.GetHeader("X-Opsx-Probe")
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func cookiesEcho(c *gin.Context) {
|
||||
out := baseMeta(c)
|
||||
cookies := gin.H{}
|
||||
for _, ck := range c.Request.Cookies() {
|
||||
cookies[ck.Name] = ck.Value
|
||||
}
|
||||
// 浏览器禁止脚本设置 Cookie 头;回归用例改走 X-Opsx-Cookie
|
||||
if raw := c.GetHeader("X-Opsx-Cookie"); raw != "" {
|
||||
for _, part := range strings.Split(raw, ";") {
|
||||
part = strings.TrimSpace(part)
|
||||
if part == "" {
|
||||
continue
|
||||
}
|
||||
kv := strings.SplitN(part, "=", 2)
|
||||
if len(kv) == 2 {
|
||||
cookies[strings.TrimSpace(kv[0])] = strings.TrimSpace(kv[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
out["cookies"] = cookies
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func bearerEcho(c *gin.Context) {
|
||||
auth := c.GetHeader("Authorization")
|
||||
if !strings.HasPrefix(auth, "Bearer ") || strings.TrimSpace(strings.TrimPrefix(auth, "Bearer ")) == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"ok": false, "error": "missing bearer"})
|
||||
return
|
||||
}
|
||||
out := baseMeta(c)
|
||||
out["authenticated"] = true
|
||||
out["token"] = strings.TrimSpace(strings.TrimPrefix(auth, "Bearer "))
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
|
||||
func basicEcho(c *gin.Context) {
|
||||
auth := c.GetHeader("Authorization")
|
||||
if !strings.HasPrefix(auth, "Basic ") {
|
||||
c.Header("WWW-Authenticate", `Basic realm="opsxapi"`)
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"ok": false, "error": "missing basic"})
|
||||
return
|
||||
}
|
||||
raw, err := base64.StdEncoding.DecodeString(strings.TrimSpace(strings.TrimPrefix(auth, "Basic ")))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"ok": false, "error": "bad basic"})
|
||||
return
|
||||
}
|
||||
parts := strings.SplitN(string(raw), ":", 2)
|
||||
out := baseMeta(c)
|
||||
out["authenticated"] = true
|
||||
out["user"] = parts[0]
|
||||
c.JSON(http.StatusOK, out)
|
||||
}
|
||||
Reference in new issue
Block a user