package support import ( "encoding/json" "net/http" "os" "runtime" "strings" "git.fsdpf.net/go/contracts" "github.com/gorilla/websocket" "github.com/samber/lo" ) type JsonResponse struct { raw []byte } type MsgResponse struct { code int msg string } type ErrResponse struct { *MsgResponse stack []byte } type FileResponse struct { disposition string name string } var wsUpgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, } func (this JsonResponse) Get(path ...string) contracts.GlobalParams { return lo.Ternary(len(path) == 0, NewGlobalParam(string(this.raw), nil), NewGlobalParam(string(this.raw), nil).Get(strings.Join(path, "."))) } func (this JsonResponse) Send(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Upgrade") != "" && strings.ToLower(r.Header.Get("Upgrade")) == "websocket" { c, _ := wsUpgrader.Upgrade(w, r, nil) c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, string(this.raw))) c.Close() } else { w.Header().Set("Content-Type", "application/json") w.Write(this.raw) } } func (this MsgResponse) Get(path ...string) contracts.GlobalParams { if resp, err := json.Marshal(map[string]any{ "code": this.code, "msg": this.msg, }); err == nil { return lo.Ternary(len(path) == 0, NewGlobalParam(string(resp), nil), NewGlobalParam(string(resp), nil).Get(strings.Join(path, "."))) } return nil } func (this MsgResponse) Send(w http.ResponseWriter, r *http.Request) { if resp, err := json.Marshal(this.Get().Value()); err == nil { (&JsonResponse{raw: resp}).Send(w, r) } else { HttpResponse(err).Send(w, r) } } func (this ErrResponse) Get(path ...string) contracts.GlobalParams { if resp, err := json.Marshal(map[string]any{ "code": this.code, "msg": this.msg, "error": string(this.stack), }); err == nil { return lo.Ternary(len(path) == 0, NewGlobalParam(string(resp), nil), NewGlobalParam(string(resp), nil).Get(strings.Join(path, "."))) } return nil } func (this ErrResponse) Send(w http.ResponseWriter, r *http.Request) { if resp, err := json.Marshal(this.Get().Value()); err == nil { (&JsonResponse{raw: resp}).Send(w, r) } else { HttpResponse(err).Send(w, r) } } func (this FileResponse) Get(path ...string) contracts.GlobalParams { if resp, err := json.Marshal(map[string]any{ "name": this.name, "disposition": this.disposition, }); err == nil { return lo.Ternary(len(path) == 0, NewGlobalParam(string(resp), nil), NewGlobalParam(string(resp), nil).Get(strings.Join(path, "."))) } return nil } func (this FileResponse) Send(w http.ResponseWriter, r *http.Request) { if this.Get("disposition").String() != "" { w.Header().Set("Content-Disposition", `attachment; filename="`+this.Get("disposition").String()+`"`) } if _, err := os.Stat(this.Get("name").String()); err != nil && os.IsNotExist(err) { w.WriteHeader(http.StatusNotFound) } else { http.ServeFile(w, r, this.Get("name").String()) } } func NewJsonResponse(b []byte) contracts.HttpResponse { return &JsonResponse{raw: b} } func NewFileResponse(name string, disposition string) contracts.HttpResponse { return &FileResponse{name: name, disposition: disposition} } func NewMsgResponse(msg string, code int) contracts.HttpResponse { return &MsgResponse{msg: msg, code: code} } func NewErrResponse(p ...any) contracts.HttpResponse { code := contracts.InternalServerError.Code msg := contracts.InternalServerError.Error() data := any(nil) for i := 0; i < len(p); i++ { switch v := p[i].(type) { case error: msg = v.Error() case string: msg = v case int: code = v case contracts.Errno: msg = v.Error() code = v.Code case *contracts.Errno: msg = v.Error() code = v.Code default: data = v } } stackBuf := make([]byte, 1024) n := runtime.Stack(stackBuf[:], false) b, _ := json.Marshal(struct { Code int `json:"code"` Msg string `json:"msg"` Data any `json:"data,omitempty"` Stack string `json:"stack"` }{ Code: code, Msg: msg, Data: data, Stack: string(stackBuf[:n]), }) return NewJsonResponse(b) } func HttpResponse(data any) contracts.HttpResponse { var err error = contracts.InternalServerError switch v := data.(type) { case contracts.HttpResponse: return v case contracts.Errno: return NewMsgResponse(v.Error(), v.Code) case *contracts.Errno: return NewMsgResponse(v.Error(), v.Code) case error: err = v case []byte: if b, e := json.Marshal(map[string]any{ "code": contracts.OK.Code, "msg": contracts.OK.Msg, "data": json.RawMessage(v), }); e == nil { return &JsonResponse{raw: b} } else { err = e } default: if b, e := json.Marshal(map[string]any{ "code": contracts.OK.Code, "msg": contracts.OK.Msg, "data": v, }); e == nil { return &JsonResponse{raw: b} } else { err = e } } return NewMsgResponse(err.Error(), contracts.InternalServerError.Code) }