Files
mrlee 302248a907
CI / verify (push) Canceled after 0s
chore(release): 发布 v2.0.0
- 内置 SSH 终端(/terminal):复用已保存的 SSH 密码/私钥与独立 known_hosts 校验
- 终端二次认证、会话审计(/terminal/audits)、录像审计与常用命令
- DDNS 服务商名称与自定义 IP,新增迁移 037/038/039
- CHANGELOG 与 VERSION 更新至 2.0.0
2026-10-16 20:18:04 +08:00

73 lines
1.8 KiB
Go

package api
import (
"encoding/json"
"log/slog"
"net/http"
"reflect"
)
type ErrorBody struct {
Error string `json:"error"`
// Code 是给前端分支用的稳定标识(例如 terminal_locked),文案可能改,code 不改
Code string `json:"code,omitempty"`
}
func writeJSON(w http.ResponseWriter, status int, payload any) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
if payload == nil {
_, _ = w.Write([]byte("null"))
return
}
// Go nil slices/maps encode as JSON null; clients expect [] / {}.
v := reflect.ValueOf(payload)
switch v.Kind() {
case reflect.Slice:
if v.IsNil() {
_, _ = w.Write([]byte("[]"))
return
}
case reflect.Map:
if v.IsNil() {
_, _ = w.Write([]byte("{}"))
return
}
}
_ = json.NewEncoder(w).Encode(payload)
}
func writeError(r *http.Request, w http.ResponseWriter, status int, message string, cause ...error) {
logAPIError(r, status, message, cause)
writeJSON(w, status, ErrorBody{Error: message})
}
// writeErrorCode 与 writeError 相同,但多带一个稳定错误码,供前端区分分支。
func writeErrorCode(r *http.Request, w http.ResponseWriter, status int, code, message string, cause ...error) {
logAPIError(r, status, message, cause)
writeJSON(w, status, ErrorBody{Error: message, Code: code})
}
func logAPIError(r *http.Request, status int, message string, cause []error) {
if status < 400 {
return
}
attrs := []any{
"module", "API",
"status", status,
"message", message,
}
if r != nil {
attrs = append(attrs, "method", r.Method, "path", r.URL.Path)
}
if len(cause) > 0 && cause[0] != nil {
attrs = append(attrs, "error", cause[0].Error())
}
switch {
case status >= 500:
slog.Error("request failed", attrs...)
default:
slog.Warn("request rejected", attrs...)
}
}