Compare commits
8 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cfe5d555a4 | |||
| 69c962f506 | |||
| 1cc668a0e1 | |||
| 3d4b3651f2 | |||
| 9d83eed4f3 | |||
| 892ab64064 | |||
| dbe2f72344 | |||
| 8b9728c3d2 |
8 changed files with 101 additions and 21 deletions
42
context.go
42
context.go
|
|
@ -56,8 +56,8 @@ func (c *Context) Next() {
|
||||||
func (c *Context) prepare(ctx *Context) {
|
func (c *Context) prepare(ctx *Context) {
|
||||||
// Only parse multipart form if it hasn't been parsed already
|
// Only parse multipart form if it hasn't been parsed already
|
||||||
// This prevents race conditions when multiple goroutines might access the same request
|
// This prevents race conditions when multiple goroutines might access the same request
|
||||||
if ctx.Request.httpRequest.MultipartForm == nil {
|
if ctx.Request.HttpRequest.MultipartForm == nil {
|
||||||
ctx.Request.httpRequest.ParseMultipartForm(int64(app.Config.Request.MaxUploadFileSize))
|
ctx.Request.HttpRequest.ParseMultipartForm(int64(app.Config.Request.MaxUploadFileSize))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,32 +66,50 @@ func (c *Context) GetPathParam(key string) interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) GetRequestParam(key string) interface{} {
|
func (c *Context) GetRequestParam(key string) interface{} {
|
||||||
return c.Request.httpRequest.FormValue(key)
|
return c.Request.HttpRequest.FormValue(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) RequestParamExists(key string) bool {
|
func (c *Context) RequestParamExists(key string) bool {
|
||||||
return c.Request.httpRequest.Form.Has(key)
|
return c.Request.HttpRequest.Form.Has(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) GetRequesForm(key string) interface{} {
|
func (c *Context) GetRequesForm(key string) interface{} {
|
||||||
c.Request.httpRequest.ParseForm()
|
c.Request.HttpRequest.ParseForm()
|
||||||
return c.Request.httpRequest.Form[key]
|
return c.Request.HttpRequest.Form[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) GetRequesBodyMap() map[string]interface{} {
|
func (c *Context) GetRequesBodyMap() map[string]interface{} {
|
||||||
var dat map[string]any
|
var dat map[string]any
|
||||||
body := c.Request.httpRequest.Body
|
body := c.Request.HttpRequest.Body
|
||||||
|
|
||||||
if body != nil {
|
if body != nil {
|
||||||
if content, err := io.ReadAll(body); err == nil {
|
if content, err := io.ReadAll(body); err == nil {
|
||||||
json.Unmarshal(content, &dat)
|
json.Unmarshal(content, &dat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
defer body.Close()
|
||||||
return dat
|
return dat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get raw data for file binary
|
||||||
|
func (c *Context) GetRawBody() ([]byte, error) {
|
||||||
|
if c.Request.HttpRequest.Body == nil {
|
||||||
|
return nil, errors.New("empty body")
|
||||||
|
}
|
||||||
|
|
||||||
|
defer c.Request.HttpRequest.Body.Close()
|
||||||
|
|
||||||
|
data, err := io.ReadAll(c.Request.HttpRequest.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
// get json body and bind to dest interface
|
// get json body and bind to dest interface
|
||||||
func (c *Context) GetRequesBodyStruct(dest interface{}) error {
|
func (c *Context) GetRequesBodyStruct(dest interface{}) error {
|
||||||
body := c.Request.httpRequest.Body
|
body := c.Request.HttpRequest.Body
|
||||||
if body != nil {
|
if body != nil {
|
||||||
value := reflect.ValueOf(dest)
|
value := reflect.ValueOf(dest)
|
||||||
if value.Kind() != reflect.Ptr {
|
if value.Kind() != reflect.Ptr {
|
||||||
|
|
@ -105,12 +123,12 @@ func (c *Context) GetRequesBodyStruct(dest interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) GetHeader(key string) string {
|
func (c *Context) GetHeader(key string) string {
|
||||||
return c.Request.httpRequest.Header.Get(key)
|
return c.Request.HttpRequest.Header.Get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) GetCookie() (UserCookie, error) {
|
func (c *Context) GetCookie() (UserCookie, error) {
|
||||||
|
|
||||||
user, err := GetCookie(c.Request.httpRequest)
|
user, err := GetCookie(c.Request.HttpRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +144,7 @@ func (c *Context) GetQueueClient() *asynq.Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) GetUploadedFile(name string) (*UploadedFileInfo, error) {
|
func (c *Context) GetUploadedFile(name string) (*UploadedFileInfo, error) {
|
||||||
file, fileHeader, err := c.Request.httpRequest.FormFile(name)
|
file, fileHeader, err := c.Request.HttpRequest.FormFile(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error retrieving file: %v", err)
|
return nil, fmt.Errorf("error retrieving file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -290,7 +308,7 @@ func (c *Context) CastToString(value interface{}) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Context) GetUserAgent() string {
|
func (c Context) GetUserAgent() string {
|
||||||
return c.Request.httpRequest.UserAgent()
|
return c.Request.HttpRequest.UserAgent()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) CastToInt(value interface{}) int {
|
func (c *Context) CastToInt(value interface{}) int {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ func TestDebugAny(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c := &Context{
|
c := &Context{
|
||||||
Request: &Request{
|
Request: &Request{
|
||||||
httpRequest: r,
|
HttpRequest: r,
|
||||||
httpPathParams: nil,
|
httpPathParams: nil,
|
||||||
},
|
},
|
||||||
Response: &Response{
|
Response: &Response{
|
||||||
|
|
@ -538,7 +538,7 @@ func makeCTXLogTestCTX(t *testing.T, w http.ResponseWriter, r *http.Request, tmp
|
||||||
t.Helper()
|
t.Helper()
|
||||||
return &Context{
|
return &Context{
|
||||||
Request: &Request{
|
Request: &Request{
|
||||||
httpRequest: r,
|
HttpRequest: r,
|
||||||
httpPathParams: nil,
|
httpPathParams: nil,
|
||||||
},
|
},
|
||||||
Response: &Response{
|
Response: &Response{
|
||||||
|
|
|
||||||
10
core.go
10
core.go
|
|
@ -242,7 +242,7 @@ func (app *App) makeHTTPRouterHandlerFunc(h Controller, ms []Hook) httprouter.Ha
|
||||||
reqCtx := &requestContext{
|
reqCtx := &requestContext{
|
||||||
chainIndex: 0,
|
chainIndex: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the chain nodes for this request
|
// Prepare the chain nodes for this request
|
||||||
mw := app.hooks.GetHooks()
|
mw := app.hooks.GetHooks()
|
||||||
for _, v := range mw {
|
for _, v := range mw {
|
||||||
|
|
@ -252,11 +252,11 @@ func (app *App) makeHTTPRouterHandlerFunc(h Controller, ms []Hook) httprouter.Ha
|
||||||
for _, v := range rhs {
|
for _, v := range rhs {
|
||||||
reqCtx.chainNodes = append(reqCtx.chainNodes, v)
|
reqCtx.chainNodes = append(reqCtx.chainNodes, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store chain state in request context
|
// Store chain state in request context
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
Request: &Request{
|
Request: &Request{
|
||||||
httpRequest: r.WithContext(context.WithValue(r.Context(), "goffeeChain", reqCtx)),
|
HttpRequest: r.WithContext(context.WithValue(r.Context(), "goffeeChain", reqCtx)),
|
||||||
httpPathParams: ps,
|
httpPathParams: ps,
|
||||||
},
|
},
|
||||||
Response: &Response{
|
Response: &Response{
|
||||||
|
|
@ -279,7 +279,7 @@ func (app *App) makeHTTPRouterHandlerFunc(h Controller, ms []Hook) httprouter.Ha
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.prepare(ctx)
|
ctx.prepare(ctx)
|
||||||
|
|
||||||
// Execute the first handler in the chain
|
// Execute the first handler in the chain
|
||||||
if len(reqCtx.chainNodes) > 0 {
|
if len(reqCtx.chainNodes) > 0 {
|
||||||
n := reqCtx.chainNodes[0]
|
n := reqCtx.chainNodes[0]
|
||||||
|
|
@ -392,7 +392,7 @@ func UseHook(mw Hook) {
|
||||||
// Next advances to the next middleware or controller in the chain and invokes it with the given context if available.
|
// Next advances to the next middleware or controller in the chain and invokes it with the given context if available.
|
||||||
func (app *App) Next(c *Context) {
|
func (app *App) Next(c *Context) {
|
||||||
// Get request-specific chain state from context
|
// Get request-specific chain state from context
|
||||||
if reqCtx, ok := c.Request.httpRequest.Context().Value("goffeeChain").(*requestContext); ok {
|
if reqCtx, ok := c.Request.HttpRequest.Context().Value("goffeeChain").(*requestContext); ok {
|
||||||
reqCtx.chainIndex++
|
reqCtx.chainIndex++
|
||||||
if reqCtx.chainIndex < len(reqCtx.chainNodes) {
|
if reqCtx.chainIndex < len(reqCtx.chainNodes) {
|
||||||
n := reqCtx.chainNodes[reqCtx.chainIndex]
|
n := reqCtx.chainNodes[reqCtx.chainIndex]
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ func makeCTX(t *testing.T) *Context {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
return &Context{
|
return &Context{
|
||||||
Request: &Request{
|
Request: &Request{
|
||||||
httpRequest: httptest.NewRequest(GET, LOCALHOST, nil),
|
HttpRequest: httptest.NewRequest(GET, LOCALHOST, nil),
|
||||||
httpPathParams: nil,
|
httpPathParams: nil,
|
||||||
},
|
},
|
||||||
Response: &Response{
|
Response: &Response{
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
httpRequest *http.Request
|
HttpRequest *http.Request
|
||||||
httpPathParams httprouter.Params
|
httpPathParams httprouter.Params
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
response.go
20
response.go
|
|
@ -9,10 +9,13 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Response represents an HTTP response to be sent to the client, including headers, body, status code, and metadata.
|
// Response represents an HTTP response to be sent to the client, including headers, body, status code, and metadata.
|
||||||
type Response struct {
|
type Response struct {
|
||||||
|
FlashMessage string
|
||||||
|
MsgType string
|
||||||
headers []header
|
headers []header
|
||||||
body []byte
|
body []byte
|
||||||
statusCode int
|
statusCode int
|
||||||
|
|
@ -144,6 +147,23 @@ func (rs *Response) Redirect(url string) *Response {
|
||||||
}, map[string]interface{}{
|
}, map[string]interface{}{
|
||||||
"url": "url",
|
"url": "url",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if rs.FlashMessage != "" || rs.MsgType != "" {
|
||||||
|
if !strings.Contains(url, "?") {
|
||||||
|
url += "?"
|
||||||
|
} else {
|
||||||
|
url += "&"
|
||||||
|
}
|
||||||
|
if rs.FlashMessage != "" {
|
||||||
|
url += "flash_message=" + rs.FlashMessage
|
||||||
|
}
|
||||||
|
if rs.MsgType != "" {
|
||||||
|
if rs.FlashMessage != "" {
|
||||||
|
url += "&"
|
||||||
|
}
|
||||||
|
url += "msg_type=" + rs.MsgType
|
||||||
|
}
|
||||||
|
}
|
||||||
if v.Failed() {
|
if v.Failed() {
|
||||||
if url[0:1] != "/" {
|
if url[0:1] != "/" {
|
||||||
rs.redirectTo = "/" + url
|
rs.redirectTo = "/" + url
|
||||||
|
|
|
||||||
14
template/components/form_color_opacity.go
Normal file
14
template/components/form_color_opacity.go
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
package components
|
||||||
|
|
||||||
|
type FormColorOpacity struct {
|
||||||
|
ColorID string
|
||||||
|
OpacityID string
|
||||||
|
Label string
|
||||||
|
ColorHexValue string
|
||||||
|
OpacityValue string
|
||||||
|
Hint string
|
||||||
|
Error string
|
||||||
|
IsDisabled bool
|
||||||
|
IsRequired bool
|
||||||
|
HasOpacity bool
|
||||||
|
}
|
||||||
28
template/components/form_color_opacity.html
Normal file
28
template/components/form_color_opacity.html
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{{ define "form_color_opacity"}}
|
||||||
|
<div class="input-container">
|
||||||
|
<script src="/public/jscolor.js"></script>
|
||||||
|
<label class="form-label" for="{{.ColorID}}">{{.Label}}</label><br>
|
||||||
|
<input id="{{ .ColorID }}" data-jscolor="{alphaElement:'#{{ .OpacityID }}'}" value="{{ .ColorHexValue }}" name="{{ .ColorID }}"
|
||||||
|
{{if eq .IsDisabled true}}
|
||||||
|
disabled
|
||||||
|
{{end}}
|
||||||
|
{{if eq .IsRequired true}}
|
||||||
|
required
|
||||||
|
{{end}}
|
||||||
|
>
|
||||||
|
{{if eq .HasOpacity true}}
|
||||||
|
opacity: <input id="{{ .OpacityID }}" value="{{ .OpacityValue }}" name="{{ .OpacityID }}" size="5"
|
||||||
|
{{if eq .IsDisabled true}}
|
||||||
|
disabled
|
||||||
|
{{end}}
|
||||||
|
{{if eq .IsRequired true}}
|
||||||
|
required
|
||||||
|
{{end}}
|
||||||
|
>
|
||||||
|
{{if ne .Hint ""}}<small id="{{.ID}}Help" class="form-text text-muted">{{.Hint}}</small>{{end}}
|
||||||
|
{{if ne .Error ""}}<div class="error">{{.Error}}</div>{{end}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
<input type="text" id="name" name="name" placeholder class="form-control" required autocomplete="off" value="Plaza estrella" aria-describedby="nameHelp">
|
||||||
Loading…
Add table
Add a link
Reference in a new issue