feat(gateway): 网关(REST /api/v1 + WS Hub /ws/chat + admin /api/v1/admin/* + JWT/RBAC + 审计,单端口 :8080)
This commit is contained in:
1 parent
ff34b62079
commit
05e23ea590
7 files changed
+1535
No files matched your search
@@ -0,0 +1,113 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
apperr "git.yeij.top/AskaEth/Cyrene_v2/internal/errors"
|
||||
)
|
||||
|
||||
// cors sets the Access-Control-* headers for origins in the configured allow
|
||||
// list and short-circuits OPTIONS preflight requests. An empty origin (same-origin
|
||||
// or non-browser client) is passed through untouched.
|
||||
func (s *Server) cors(next http.Handler) http.Handler {
|
||||
allowed, allowAll := s.allowedOrigins()
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if origin := r.Header.Get("Origin"); origin != "" && (allowAll || allowed[origin]) {
|
||||
if allowAll {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
} else {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
}
|
||||
w.Header().Set("Vary", "Origin")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
|
||||
w.Header().Set("Access-Control-Max-Age", "600")
|
||||
}
|
||||
if r.Method == http.MethodOptions {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// allowedOrigins resolves the configured origin list into a lookup set plus an
|
||||
// allow-all flag for the "*" wildcard.
|
||||
func (s *Server) allowedOrigins() (map[string]bool, bool) {
|
||||
list := s.cfg.AllowedOrigins
|
||||
if len(list) == 0 {
|
||||
return map[string]bool{}, false
|
||||
}
|
||||
allowAll := false
|
||||
out := make(map[string]bool, len(list))
|
||||
for _, o := range list {
|
||||
if o == "*" {
|
||||
allowAll = true
|
||||
continue
|
||||
}
|
||||
out[o] = true
|
||||
}
|
||||
return out, allowAll
|
||||
}
|
||||
|
||||
// recover converts a panic in a downstream handler into a 500 {error:...}
|
||||
// response instead of crashing the process, and logs the panic.
|
||||
func (s *Server) recover(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
s.log.Error("panic recovered", "panic", rec, "method", r.Method, "path", r.URL.Path)
|
||||
writeError(w, apperr.ErrInternal)
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// logging records one structured line per request (method, path, status, dur).
|
||||
func (s *Server) logging(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
start := time.Now()
|
||||
rec := &statusRecorder{ResponseWriter: w, status: http.StatusOK}
|
||||
next.ServeHTTP(rec, r)
|
||||
s.log.Info("request",
|
||||
"method", r.Method,
|
||||
"path", r.URL.Path,
|
||||
"status", rec.status,
|
||||
"duration_ms", time.Since(start).Milliseconds(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// statusRecorder captures the written status so the logging middleware can report
|
||||
// it. It forwards the optional Hijacker/Flusher interfaces so the WebSocket
|
||||
// upgrader (which requires http.Hijacker) and streaming handlers still work.
|
||||
type statusRecorder struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
}
|
||||
|
||||
// WriteHeader records the status before delegating.
|
||||
func (r *statusRecorder) WriteHeader(code int) {
|
||||
r.status = code
|
||||
r.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
// Hijack forwards to the underlying writer when it supports connection hijacking.
|
||||
func (r *statusRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
if hj, ok := r.ResponseWriter.(http.Hijacker); ok {
|
||||
return hj.Hijack()
|
||||
}
|
||||
return nil, nil, fmt.Errorf("gateway: hijack unsupported by %T", r.ResponseWriter)
|
||||
}
|
||||
|
||||
// Flush forwards to the underlying writer when it supports flushing.
|
||||
func (r *statusRecorder) Flush() {
|
||||
if f, ok := r.ResponseWriter.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user