github.com/kotovmak/go-admin@v1.1.1/adapter/chi/chi.go (about)

     1  // Copyright 2019 GoAdmin Core Team. All rights reserved.
     2  // Use of this source code is governed by a Apache-2.0 style
     3  // license that can be found in the LICENSE file.
     4  
     5  package chi
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"net/http"
    11  	"net/url"
    12  	"regexp"
    13  	"strings"
    14  
    15  	"github.com/go-chi/chi/v5"
    16  	"github.com/kotovmak/go-admin/adapter"
    17  	"github.com/kotovmak/go-admin/context"
    18  	"github.com/kotovmak/go-admin/engine"
    19  	cfg "github.com/kotovmak/go-admin/modules/config"
    20  	"github.com/kotovmak/go-admin/plugins"
    21  	"github.com/kotovmak/go-admin/plugins/admin/models"
    22  	"github.com/kotovmak/go-admin/plugins/admin/modules/constant"
    23  	"github.com/kotovmak/go-admin/template/types"
    24  )
    25  
    26  // Chi structure value is a Chi GoAdmin adapter.
    27  type Chi struct {
    28  	adapter.BaseAdapter
    29  	ctx Context
    30  	app *chi.Mux
    31  }
    32  
    33  func init() {
    34  	engine.Register(new(Chi))
    35  }
    36  
    37  // User implements the method Adapter.User.
    38  func (ch *Chi) User(ctx interface{}) (models.UserModel, bool) {
    39  	return ch.GetUser(ctx, ch)
    40  }
    41  
    42  // Use implements the method Adapter.Use.
    43  func (ch *Chi) Use(app interface{}, plugs []plugins.Plugin) error {
    44  	return ch.GetUse(app, plugs, ch)
    45  }
    46  
    47  // Content implements the method Adapter.Content.
    48  func (ch *Chi) Content(ctx interface{}, getPanelFn types.GetPanelFn, fn context.NodeProcessor, btns ...types.Button) {
    49  	ch.GetContent(ctx, getPanelFn, ch, btns, fn)
    50  }
    51  
    52  type HandlerFunc func(ctx Context) (types.Panel, error)
    53  
    54  func Content(handler HandlerFunc) http.HandlerFunc {
    55  	return func(writer http.ResponseWriter, request *http.Request) {
    56  		ctx := Context{
    57  			Request:  request,
    58  			Response: writer,
    59  		}
    60  		engine.Content(ctx, func(ctx interface{}) (types.Panel, error) {
    61  			return handler(ctx.(Context))
    62  		})
    63  	}
    64  }
    65  
    66  // SetApp implements the method Adapter.SetApp.
    67  func (ch *Chi) SetApp(app interface{}) error {
    68  	var (
    69  		eng *chi.Mux
    70  		ok  bool
    71  	)
    72  	if eng, ok = app.(*chi.Mux); !ok {
    73  		return errors.New("chi adapter SetApp: wrong parameter")
    74  	}
    75  	ch.app = eng
    76  	return nil
    77  }
    78  
    79  // AddHandler implements the method Adapter.AddHandler.
    80  func (ch *Chi) AddHandler(method, path string, handlers context.Handlers) {
    81  	url := path
    82  	reg1 := regexp.MustCompile(":(.*?)/")
    83  	reg2 := regexp.MustCompile(":(.*?)$")
    84  	url = reg1.ReplaceAllString(url, "{$1}/")
    85  	url = reg2.ReplaceAllString(url, "{$1}")
    86  
    87  	if len(url) > 1 && url[0] == '/' && url[1] == '/' {
    88  		url = url[1:]
    89  	}
    90  
    91  	getHandleFunc(ch.app, strings.ToUpper(method))(url, func(w http.ResponseWriter, r *http.Request) {
    92  
    93  		if r.URL.Path[len(r.URL.Path)-1] == '/' {
    94  			r.URL.Path = r.URL.Path[:len(r.URL.Path)-1]
    95  		}
    96  
    97  		ctx := context.NewContext(r)
    98  
    99  		params := chi.RouteContext(r.Context()).URLParams
   100  
   101  		for i := 0; i < len(params.Values); i++ {
   102  			if r.URL.RawQuery == "" {
   103  				r.URL.RawQuery += strings.ReplaceAll(params.Keys[i], ":", "") + "=" + params.Values[i]
   104  			} else {
   105  				r.URL.RawQuery += "&" + strings.ReplaceAll(params.Keys[i], ":", "") + "=" + params.Values[i]
   106  			}
   107  		}
   108  
   109  		ctx.SetHandlers(handlers).Next()
   110  		for key, head := range ctx.Response.Header {
   111  			w.Header().Set(key, head[0])
   112  		}
   113  		if ctx.Response.Body != nil {
   114  			buf := new(bytes.Buffer)
   115  			_, _ = buf.ReadFrom(ctx.Response.Body)
   116  			w.WriteHeader(ctx.Response.StatusCode)
   117  			_, _ = w.Write(buf.Bytes())
   118  		} else {
   119  			w.WriteHeader(ctx.Response.StatusCode)
   120  		}
   121  	})
   122  }
   123  
   124  // HandleFun is type of route methods of chi.
   125  type HandleFun func(pattern string, handlerFn http.HandlerFunc)
   126  
   127  func getHandleFunc(eng *chi.Mux, method string) HandleFun {
   128  	switch method {
   129  	case "GET":
   130  		return eng.Get
   131  	case "POST":
   132  		return eng.Post
   133  	case "PUT":
   134  		return eng.Put
   135  	case "DELETE":
   136  		return eng.Delete
   137  	case "HEAD":
   138  		return eng.Head
   139  	case "OPTIONS":
   140  		return eng.Options
   141  	case "PATCH":
   142  		return eng.Patch
   143  	default:
   144  		panic("wrong method")
   145  	}
   146  }
   147  
   148  // Context wraps the Request and Response object of Chi.
   149  type Context struct {
   150  	Request  *http.Request
   151  	Response http.ResponseWriter
   152  }
   153  
   154  // SetContext implements the method Adapter.SetContext.
   155  func (*Chi) SetContext(contextInterface interface{}) adapter.WebFrameWork {
   156  	var (
   157  		ctx Context
   158  		ok  bool
   159  	)
   160  	if ctx, ok = contextInterface.(Context); !ok {
   161  		panic("chi adapter SetContext: wrong parameter")
   162  	}
   163  	return &Chi{ctx: ctx}
   164  }
   165  
   166  // Name implements the method Adapter.Name.
   167  func (*Chi) Name() string {
   168  	return "chi"
   169  }
   170  
   171  // Redirect implements the method Adapter.Redirect.
   172  func (ch *Chi) Redirect() {
   173  	http.Redirect(ch.ctx.Response, ch.ctx.Request, cfg.Url(cfg.GetLoginUrl()), http.StatusFound)
   174  }
   175  
   176  // SetContentType implements the method Adapter.SetContentType.
   177  func (ch *Chi) SetContentType() {
   178  	ch.ctx.Response.Header().Set("Content-Type", ch.HTMLContentType())
   179  }
   180  
   181  // Write implements the method Adapter.Write.
   182  func (ch *Chi) Write(body []byte) {
   183  	ch.ctx.Response.WriteHeader(http.StatusOK)
   184  	_, _ = ch.ctx.Response.Write(body)
   185  }
   186  
   187  // GetCookie implements the method Adapter.GetCookie.
   188  func (ch *Chi) GetCookie() (string, error) {
   189  	cookie, err := ch.ctx.Request.Cookie(ch.CookieKey())
   190  	if err != nil {
   191  		return "", err
   192  	}
   193  	return cookie.Value, err
   194  }
   195  
   196  // Lang implements the method Adapter.Lang.
   197  func (ch *Chi) Lang() string {
   198  	return ch.ctx.Request.URL.Query().Get("__ga_lang")
   199  }
   200  
   201  // Path implements the method Adapter.Path.
   202  func (ch *Chi) Path() string {
   203  	return ch.ctx.Request.URL.Path
   204  }
   205  
   206  // Method implements the method Adapter.Method.
   207  func (ch *Chi) Method() string {
   208  	return ch.ctx.Request.Method
   209  }
   210  
   211  // FormParam implements the method Adapter.FormParam.
   212  func (ch *Chi) FormParam() url.Values {
   213  	_ = ch.ctx.Request.ParseMultipartForm(32 << 20)
   214  	return ch.ctx.Request.PostForm
   215  }
   216  
   217  // IsPjax implements the method Adapter.IsPjax.
   218  func (ch *Chi) IsPjax() bool {
   219  	return ch.ctx.Request.Header.Get(constant.PjaxHeader) == "true"
   220  }
   221  
   222  // Query implements the method Adapter.Query.
   223  func (ch *Chi) Query() url.Values {
   224  	return ch.ctx.Request.URL.Query()
   225  }