--
This commit is contained in:
parent
3858816837
commit
36612dd31b
41
app/app.go
41
app/app.go
@ -3,7 +3,9 @@ package app
|
||||
import (
|
||||
"bitbucket.org/skeris/heruvym/dal"
|
||||
"bitbucket.org/skeris/heruvym/middleware"
|
||||
"bitbucket.org/skeris/heruvym/router"
|
||||
"bitbucket.org/skeris/heruvym/service"
|
||||
"bitbucket.org/skeris/heruvym/tools"
|
||||
"bitbucket.org/skeris/heruvym/version"
|
||||
rAL "bitbucket.org/skeris/profile/dal"
|
||||
"context"
|
||||
@ -11,7 +13,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/BlackBroker/trashlog/wrappers/zaptg"
|
||||
"github.com/skeris/appInit"
|
||||
"github.com/skeris/authService/router"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -116,8 +117,6 @@ func New(ctx context.Context, opts interface{}) (appInit.CommonApp, error) {
|
||||
logger := hlog.New(zapLogger)
|
||||
logger.Emit(InfoSvcStarted{})
|
||||
|
||||
apiMux := router.NewRouter(http.NewServeMux())
|
||||
|
||||
database, err := dal.New(
|
||||
ctx,
|
||||
options.MongoURI,
|
||||
@ -140,11 +139,39 @@ func New(ctx context.Context, opts interface{}) (appInit.CommonApp, error) {
|
||||
interrupt := make(chan os.Signal, 1)
|
||||
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
|
||||
mux := router.NewRouter(map[string]http.HandlerFunc{
|
||||
"/support/create": heruvym.CreateTicket,
|
||||
"/support/subscribe": tools.SseWrapper(heruvym.GetList),
|
||||
"/support/ticket": tools.SseWrapper(heruvym.Subscribe),
|
||||
"/support/send": tools.HandlerWrapper(heruvym.PutMessage),
|
||||
"/support/getTickets": tools.HandlerWrapper(heruvym.GetTickets),
|
||||
"/support/getMessages": tools.HandlerWrapper(heruvym.GetMessages),
|
||||
"/support/pick": tools.HandlerWrapper(heruvym.Pick),
|
||||
"/support/delegate": tools.HandlerWrapper(heruvym.Delegate),
|
||||
"/support/vote": tools.HandlerWrapper(heruvym.Vote),
|
||||
"/support/close": tools.HandlerWrapper(heruvym.CloseTicket),
|
||||
})
|
||||
|
||||
mw := middleware.NewMiddleware(
|
||||
logger,
|
||||
nil,
|
||||
"*",
|
||||
nil,
|
||||
)
|
||||
|
||||
mux.Use(
|
||||
mw.MiddlewareLogger,
|
||||
mw.MiddlewareOriginAccess,
|
||||
mw.MiddlewareRecovery,
|
||||
mw.MiddlewareJwt,
|
||||
mw.MiddlewareGetJwt,
|
||||
//mw.MiddlewareJwtPlug,
|
||||
//mw.MiddlewareRoleAccess,
|
||||
)
|
||||
|
||||
server := &http.Server{
|
||||
Handler: http.StripPrefix(
|
||||
"/support",
|
||||
http_middleware.Wrap(heruvym.Register(apiMux), zapLogger),
|
||||
),
|
||||
Handler: mux,
|
||||
Addr: fmt.Sprintf(":%s", options.NumberPortLocal),
|
||||
}
|
||||
|
||||
|
1
go.mod
1
go.mod
@ -6,6 +6,7 @@ require (
|
||||
bitbucket.org/skeris/profile v0.0.0
|
||||
github.com/BlackBroker/trashlog v0.0.0-20210406151703-e2c4874359bf
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rs/xid v1.3.0
|
||||
github.com/skeris/appInit v0.1.12
|
||||
github.com/skeris/authService v1.1.1
|
||||
|
@ -1,66 +0,0 @@
|
||||
package hijack
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func New(w http.ResponseWriter, commit func(http.ResponseWriter)) (http.ResponseWriter, func()) {
|
||||
responseWriter := &responseWriter{
|
||||
commit: commit,
|
||||
w: w,
|
||||
}
|
||||
|
||||
if _, ok := w.(http.Hijacker); ok {
|
||||
return &responseWriterHijacker{
|
||||
responseWriter: responseWriter,
|
||||
}, responseWriter.ensureCommitted
|
||||
} else {
|
||||
return responseWriter, responseWriter.ensureCommitted
|
||||
}
|
||||
}
|
||||
|
||||
var _ http.ResponseWriter = new(responseWriter)
|
||||
var _ http.ResponseWriter = new(responseWriterHijacker)
|
||||
var _ http.Hijacker = new(responseWriterHijacker)
|
||||
|
||||
type responseWriter struct {
|
||||
commit func(http.ResponseWriter)
|
||||
w http.ResponseWriter
|
||||
hijacked bool
|
||||
writeOnce sync.Once
|
||||
}
|
||||
|
||||
func (rw *responseWriter) ensureCommitted() {
|
||||
rw.writeOnce.Do(func() {
|
||||
if rw.hijacked {
|
||||
return
|
||||
}
|
||||
rw.commit(rw.w)
|
||||
})
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Header() http.Header {
|
||||
return rw.w.Header()
|
||||
}
|
||||
|
||||
func (rw *responseWriter) Write(data []byte) (int, error) {
|
||||
rw.ensureCommitted()
|
||||
return rw.w.Write(data)
|
||||
}
|
||||
|
||||
func (rw *responseWriter) WriteHeader(status int) {
|
||||
rw.ensureCommitted()
|
||||
rw.w.WriteHeader(status)
|
||||
}
|
||||
|
||||
type responseWriterHijacker struct {
|
||||
*responseWriter
|
||||
}
|
||||
|
||||
func (rw *responseWriterHijacker) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
rw.hijacked = true
|
||||
return rw.w.(http.Hijacker).Hijack()
|
||||
}
|
36
middleware/hlogger.go
Normal file
36
middleware/hlogger.go
Normal file
@ -0,0 +1,36 @@
|
||||
package middleware
|
||||
|
||||
type ErrorHandlerRecovered struct {
|
||||
Recovered interface{}
|
||||
}
|
||||
|
||||
type ErrorWritingPanicResponse struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
type ErrorPanicInHttpHandler struct {
|
||||
Code int
|
||||
Message string
|
||||
Recovered interface{}
|
||||
}
|
||||
|
||||
type DebugHttpRequest struct {
|
||||
Url string
|
||||
}
|
||||
|
||||
type ErrorOriginAccess struct {
|
||||
Origin string
|
||||
Url string
|
||||
}
|
||||
|
||||
type ErrorJwtAccess struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
type ErrorJwtEncode struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
type ErrorRoleAccess struct {
|
||||
Err error
|
||||
}
|
@ -1,242 +1,205 @@
|
||||
package http_middleware
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bitbucket.org/skeris/heruvym/jwt_adapter"
|
||||
"bitbucket.org/skeris/heruvym/middleware/hijack"
|
||||
"bitbucket.org/skeris/profile/dal"
|
||||
"bitbucket.org/skeris/profile/errors"
|
||||
"bitbucket.org/skeris/profile/jwt_adapter"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/skeris/authService/errors"
|
||||
errors2 "github.com/pkg/errors"
|
||||
"github.com/themakers/hlog"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type MiddlewareFunc func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
|
||||
// My MiddleWare with gorilla/mux
|
||||
|
||||
type AfterFunc func(ctx context.Context, r *http.Request) (context.Context, error)
|
||||
|
||||
type SetCookieValueFunc func(w http.ResponseWriter, value string)
|
||||
|
||||
func DefaultChain(
|
||||
log *zap.Logger,
|
||||
recFn RecoverFunc,
|
||||
afn AfterFunc,
|
||||
setCookieValue SetCookieValueFunc,
|
||||
mws ...MiddlewareFunc,
|
||||
) http.HandlerFunc {
|
||||
return Chain(
|
||||
append(
|
||||
[]MiddlewareFunc{
|
||||
MiddlewareRecovery(log, recFn),
|
||||
MiddlewareLogger(log),
|
||||
DefaultCookieAndRecoveryMiddleware(
|
||||
log,
|
||||
recFn, afn, setCookieValue,
|
||||
),
|
||||
},
|
||||
mws...,
|
||||
)...,
|
||||
)
|
||||
type Middleware struct {
|
||||
logger hlog.Logger
|
||||
mongo dal.LayerMongoDb
|
||||
allowedOrigins string
|
||||
allowedRoles map[string]string // key - path, value - roles
|
||||
}
|
||||
|
||||
func DefaultCookieAndRecoveryMiddleware(
|
||||
log *zap.Logger,
|
||||
recFn RecoverFunc,
|
||||
afn AfterFunc,
|
||||
setCookieValue SetCookieValueFunc,
|
||||
) MiddlewareFunc {
|
||||
func NewMiddleware(
|
||||
logger hlog.Logger,
|
||||
mongo dal.LayerMongoDb,
|
||||
allowedOrigins string,
|
||||
allowedRoles map[string]string,
|
||||
) *Middleware {
|
||||
return &Middleware{
|
||||
logger: logger,
|
||||
mongo: mongo,
|
||||
allowedOrigins: allowedOrigins,
|
||||
allowedRoles: allowedRoles,
|
||||
}
|
||||
}
|
||||
|
||||
const headerKey = jwt_adapter.DefaultHeaderKey
|
||||
func (mw *Middleware) MiddlewareLogger(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mw.logger.Emit(DebugHttpRequest{Url: r.URL.String()})
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
var (
|
||||
err error
|
||||
tokenHeader string
|
||||
)
|
||||
cookie := &jwt_adapter.JwtAdapter{}
|
||||
func (mw *Middleware) MiddlewareOriginAccess(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if len(r.Header["Origin"]) > 0 {
|
||||
if mw.allowedOrigins != "*" && !strings.Contains(mw.allowedOrigins, r.Header["Origin"][0]) {
|
||||
mw.logger.Emit(ErrorOriginAccess{Origin: r.Header["Origin"][0], Url: r.URL.String()})
|
||||
return
|
||||
}
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
if r.Method == http.MethodGet {
|
||||
tokenHeader = fmt.Sprintf("Bearer %s", r.Form.Get(headerKey))
|
||||
func recFn(rec interface{}) (int, string) {
|
||||
var (
|
||||
code int
|
||||
message string
|
||||
)
|
||||
|
||||
if err, ok := rec.(error); ok {
|
||||
if v, ok := errors.IsForbidden(err); ok {
|
||||
code = http.StatusForbidden
|
||||
message = v.Error()
|
||||
} else if v, ok := errors.IsUnauthenticated(err); ok {
|
||||
code = http.StatusUnauthorized
|
||||
message = v.Error()
|
||||
} else {
|
||||
tokenHeader = r.Header.Get(headerKey)
|
||||
code = http.StatusInternalServerError
|
||||
message = err.Error()
|
||||
}
|
||||
} else {
|
||||
code = http.StatusInternalServerError
|
||||
message = fmt.Sprintf("%v", rec)
|
||||
}
|
||||
|
||||
return code, message
|
||||
}
|
||||
|
||||
func (mw *Middleware) MiddlewareRecovery(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
code, message := recFn(rec)
|
||||
w.WriteHeader(code)
|
||||
if _, err := fmt.Fprint(w, message); err != nil {
|
||||
mw.logger.Emit(ErrorWritingPanicResponse{Err: err})
|
||||
}
|
||||
mw.logger.Emit(ErrorPanicInHttpHandler{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Recovered: rec,
|
||||
})
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func (mw *Middleware) MiddlewareJwt(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Escape GET requests
|
||||
if r.Method == http.MethodGet {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if tokenHeader == "" {
|
||||
fmt.Println("ERROR NO authHEader")
|
||||
cookie.Init()
|
||||
} else {
|
||||
if len(r.Header[jwt_adapter.DefaultHeaderKey]) <= 0 {
|
||||
mw.logger.Emit(ErrorJwtAccess{Err: errors2.New(jwt_adapter.DefaultHeaderKey + "header missing")})
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
splitted := strings.Split(tokenHeader, " ")
|
||||
if len(splitted) != 2 {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
token := r.Header[jwt_adapter.DefaultHeaderKey][0]
|
||||
token = strings.Replace(token, "Bearer ", "", -1)
|
||||
|
||||
adapter, err := jwt_adapter.Decode(token)
|
||||
|
||||
if err != nil {
|
||||
mw.logger.Emit(ErrorJwtAccess{Err: err})
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
err = setJwtHeader(adapter, w, mw.logger)
|
||||
if err != nil {
|
||||
mw.logger.Emit(ErrorJwtAccess{Err: err})
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), "JWT", adapter)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func getJwtUserId(r *http.Request) (string, error) {
|
||||
if jwtAdapter, ok := r.Context().Value("JWT").(*jwt_adapter.JwtAdapter); ok {
|
||||
return jwtAdapter.User, nil
|
||||
}
|
||||
|
||||
return "", errors2.New("no token in context")
|
||||
}
|
||||
|
||||
func (mw *Middleware) MiddlewareRoleAccess(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Если доступ по роли задан
|
||||
if allowedRoles, ok := mw.allowedRoles[r.URL.Path]; ok {
|
||||
|
||||
// Если роли не указаны
|
||||
if allowedRoles == "" {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
tokenPart := splitted[1]
|
||||
id, err := getJwtUserId(r)
|
||||
|
||||
cookie, err = jwt_adapter.Decode(tokenPart)
|
||||
if err != nil {
|
||||
cookie.Init()
|
||||
mw.logger.Emit(ErrorRoleAccess{Err: err})
|
||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
recovery := struct {
|
||||
val interface{}
|
||||
trace string
|
||||
}{}
|
||||
role, err := mw.mongo.GetProfileRole(r.Context(), id)
|
||||
|
||||
ctx := context.WithValue(r.Context(), headerKey, cookie)
|
||||
|
||||
if afn != nil {
|
||||
c, err := afn(ctx, r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
mw.logger.Emit(ErrorRoleAccess{Err: err})
|
||||
http.Error(w, "internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Если у пользователя не задана роль - блокируем доступ
|
||||
if role == "" {
|
||||
err = errors.UserHaveNoRole("User have no role")
|
||||
mw.logger.Emit(ErrorRoleAccess{err})
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// Если указан астериск - доступ имеет любая роль
|
||||
if !(allowedRoles == "*" || strings.Contains(allowedRoles, role)) {
|
||||
err = errors.UserHaveNoRole("User role not allowed")
|
||||
mw.logger.Emit(ErrorRoleAccess{err})
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
ctx = c
|
||||
}
|
||||
|
||||
w, commit := hijack.New(w, func(w http.ResponseWriter) {
|
||||
cookie.LastSeen = jwt_adapter.Timestamp()
|
||||
if val, err := cookie.Encode(); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
setCookieValue(w, val)
|
||||
}
|
||||
|
||||
if recovery.val != nil {
|
||||
log.Error("handler recovered", zap.Any("recovered", recovery.val))
|
||||
code, message := recFn(recovery.val, ctx)
|
||||
w.WriteHeader(code)
|
||||
if _, err := fmt.Fprint(w, message); err != nil {
|
||||
log.Error("error writing panic response", zap.Error(err))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
recovery.val = rec
|
||||
recovery.trace = string(debug.Stack())
|
||||
}
|
||||
commit()
|
||||
}()
|
||||
|
||||
next(w, r.WithContext(ctx))
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func Chain(mws ...MiddlewareFunc) http.HandlerFunc {
|
||||
if len(mws) == 0 {
|
||||
return func(w http.ResponseWriter, r *http.Request) {}
|
||||
}
|
||||
h := link(mws[len(mws)-1], nil)
|
||||
for i := len(mws) - 2; i >= 0; i-- {
|
||||
mw := mws[i]
|
||||
// MiddlewareJwtPlug jwt заглушка для отладки кода, удалить в релизе
|
||||
func (mw *Middleware) MiddlewareJwtPlug(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
adapter := jwt_adapter.JwtAdapter{ID: "604b79aced1d431b9e911f56"}
|
||||
adapter.Init()
|
||||
adapter.SetUserID("604b79aced1d431b9e911f56")
|
||||
ctx := context.WithValue(r.Context(), "JWT", &adapter)
|
||||
|
||||
h = link(mw, h)
|
||||
}
|
||||
return h
|
||||
}
|
||||
func link(mw MiddlewareFunc, next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
mw(w, r, next)
|
||||
}
|
||||
}
|
||||
|
||||
type RecoverFunc func(rec interface{}, ctx context.Context) (code int, message string)
|
||||
|
||||
func MiddlewareRecovery(log *zap.Logger, recFn RecoverFunc) MiddlewareFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
defer func() {
|
||||
if rec := recover(); rec != nil {
|
||||
code, message := recFn(rec, r.Context())
|
||||
w.WriteHeader(code)
|
||||
if _, err := fmt.Fprint(w, message); err != nil {
|
||||
log.Error("error writing panic response", zap.Error(err))
|
||||
}
|
||||
log.Error("panic in http handler", zap.Int("code", code), zap.String("message", message), zap.Any("recovered", rec))
|
||||
}
|
||||
}()
|
||||
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
func MiddlewareLogger(log *zap.Logger) MiddlewareFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
log.Debug("http request", zap.String("url", r.URL.String()))
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func Handler(h http.HandlerFunc) MiddlewareFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
h.ServeHTTP(w, r)
|
||||
if next != nil {
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Wrap(mux *http.ServeMux, logger *zap.Logger) http.HandlerFunc {
|
||||
return DefaultChain(
|
||||
logger,
|
||||
func(rec interface{}, ctx context.Context) (int, string) {
|
||||
var (
|
||||
code int
|
||||
message string
|
||||
)
|
||||
|
||||
if err, ok := rec.(error); ok {
|
||||
if v, ok := errors.IsForbidden(err); ok {
|
||||
code = http.StatusForbidden
|
||||
message = v.Error()
|
||||
} else if v, ok := errors.IsUnauthenticated(err); ok {
|
||||
code = http.StatusUnauthorized
|
||||
message = v.Error()
|
||||
} else {
|
||||
code = http.StatusInternalServerError
|
||||
message = err.Error()
|
||||
}
|
||||
} else {
|
||||
code = http.StatusInternalServerError
|
||||
message = fmt.Sprintf("%v", rec)
|
||||
}
|
||||
|
||||
return code, message
|
||||
},
|
||||
nil,
|
||||
func(w http.ResponseWriter, val string) {
|
||||
var setFn func(key, value string)
|
||||
if w.Header().Get(jwt_adapter.DefaultHeaderKey) == "" {
|
||||
setFn = w.Header().Add
|
||||
} else {
|
||||
setFn = w.Header().Set
|
||||
}
|
||||
|
||||
setFn(jwt_adapter.DefaultHeaderKey, fmt.Sprintf("Bearer %s", val))
|
||||
},
|
||||
Handler((func() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
//todo specify origins
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "*")
|
||||
w.Header().Set("Access-Control-Expose-Headers", "*")
|
||||
|
||||
if r.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
w.Header().Set("Expires", "0")
|
||||
|
||||
mux.ServeHTTP(w, r)
|
||||
|
||||
}
|
||||
})()))
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
20
middleware/middleware.go
Normal file
20
middleware/middleware.go
Normal file
@ -0,0 +1,20 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bitbucket.org/skeris/profile/jwt_adapter"
|
||||
"github.com/themakers/hlog"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func setJwtHeader(adapter *jwt_adapter.JwtAdapter, w http.ResponseWriter, logger hlog.Logger) error {
|
||||
adapter.LastSeen = jwt_adapter.Timestamp()
|
||||
|
||||
token, err := adapter.Encode()
|
||||
if err != nil {
|
||||
logger.Emit(ErrorJwtEncode{Err: err})
|
||||
return err
|
||||
}
|
||||
|
||||
w.Header().Set(jwt_adapter.DefaultHeaderKey, token)
|
||||
return nil
|
||||
}
|
29
middleware/sse_middleware.go
Normal file
29
middleware/sse_middleware.go
Normal file
@ -0,0 +1,29 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bitbucket.org/skeris/profile/jwt_adapter"
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (mw *Middleware) MiddlewareGetJwt(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Escape non-GET requests
|
||||
if r.Method != http.MethodGet {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
bearer := r.URL.Query().Get(jwt_adapter.DefaultHeaderKey)
|
||||
|
||||
adapter, err := jwt_adapter.Decode(bearer)
|
||||
if err != nil {
|
||||
mw.logger.Emit(ErrorJwtAccess{Err: err})
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), "JWT", adapter)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
18
router/router.go
Normal file
18
router/router.go
Normal file
@ -0,0 +1,18 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewRouter(additional... map[string]http.HandlerFunc) *mux.Router {
|
||||
apiMux := mux.NewRouter()
|
||||
|
||||
for _, handler := range additional {
|
||||
for endpoint, handler := range handler {
|
||||
apiMux.HandleFunc(endpoint, handler)
|
||||
}
|
||||
}
|
||||
|
||||
return apiMux
|
||||
}
|
@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -15,7 +14,13 @@ type DataEmitter func(ctx context.Context) chan interface{}
|
||||
const ContextURLKey = "url"
|
||||
|
||||
func SseWrapper(emitter DataEmitter) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request,) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Отправляем header event-stream
|
||||
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
http.Error(
|
||||
@ -29,8 +34,8 @@ func SseWrapper(emitter DataEmitter) http.HandlerFunc {
|
||||
ctx := context.WithValue(
|
||||
r.Context(),
|
||||
ContextURLKey,
|
||||
strings.Split(r.URL.Path, "/"),
|
||||
)
|
||||
r.URL.Query().Get("ticket"),
|
||||
)
|
||||
|
||||
dE := emitter(ctx)
|
||||
|
||||
@ -110,4 +115,4 @@ func HandlerWrapper(f interface{}) http.HandlerFunc {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user