diff --git a/context.go b/context.go index ad1321d..30c750b 100644 --- a/context.go +++ b/context.go @@ -68,49 +68,10 @@ func (c *Context) RequestParamExists(key string) bool { return c.Request.httpRequest.Form.Has(key) } -func (c *Context) GetRequesForm() interface{} { - return c.Request.httpRequest.Form -} - -func (c *Context) GetRequesBodyMap() map[string]interface{} { - var dat map[string]any - body := c.Request.httpRequest.Body - if body != nil { - if content, err := io.ReadAll(body); err == nil { - json.Unmarshal(content, &dat) - } - } - return dat -} - -// get json body and bind to dest interface -func (c *Context) GetRequesBodyStruct(dest interface{}) error { - body := c.Request.httpRequest.Body - if body != nil { - value := reflect.ValueOf(dest) - if value.Kind() != reflect.Ptr { - fmt.Println("dest is not a pointer") - return errors.New("dest is not a pointer") - } - err := json.NewDecoder(body).Decode(dest) - return err - } - return nil -} - func (c *Context) GetHeader(key string) string { return c.Request.httpRequest.Header.Get(key) } -func (c *Context) GetCookie() (UserCookie, error) { - - user, err := GetCookie(c.Request.httpRequest) - if err != nil { - return user, err - } - return user, nil -} - func (c *Context) GetUploadedFile(name string) *UploadedFileInfo { file, fileHeader, err := c.Request.httpRequest.FormFile(name) if err != nil { diff --git a/cookies.go b/cookies.go deleted file mode 100644 index 8c03c10..0000000 --- a/cookies.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (c) 2024 Zeni Kim -// Use of this source code is governed by MIT-style -// license that can be found in the LICENSE file. - -package core - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/base64" - "encoding/gob" - "encoding/hex" - "errors" - "fmt" - "io" - "net/http" - "os" - "strconv" - "strings" -) - -var ( - ErrValueTooLong = errors.New("cookie value too long") - ErrInvalidValue = errors.New("invalid cookie value") -) - -// Declare the User type. -type UserCookie struct { - Email string - Token string -} - -var secretcookie []byte - -func GetCookie(r *http.Request) (UserCookie, error) { - - var err error - // Create a new instance of a User type. - var user UserCookie - - // check if template engine is enable - TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") - if TemplateEnableStr == "" { - TemplateEnableStr = "false" - } - TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) - // if enabled, - if TemplateEnable { - cookie_secret := os.Getenv("COOKIE_SECRET") - if cookie_secret == "" { - panic("cookie secret key is not set") - } - secretcookie, err = hex.DecodeString(cookie_secret) - if err != nil { - return user, err - } - } else { - panic("Templates are disabled") - } - - gobEncodedValue, err := CookieReadEncrypted(r, "goffee", secretcookie) - if err != nil { - return user, err - } - - // Create an strings.Reader containing the gob-encoded value. - reader := strings.NewReader(gobEncodedValue) - - // Decode it into the User type. Notice that we need to pass a *pointer* to - // the Decode() target here? - if err := gob.NewDecoder(reader).Decode(&user); err != nil { - return user, err - } - - return user, nil - -} - -func SetCookie(w http.ResponseWriter, email string, token string) error { - // Initialize a User struct containing the data that we want to store in the - // cookie. - var err error - - // check if template engine is enable - TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE") - if TemplateEnableStr == "" { - TemplateEnableStr = "false" - } - TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) - // if enabled, - if TemplateEnable { - cookie_secret := os.Getenv("COOKIE_SECRET") - if cookie_secret == "" { - panic("cookie secret key is not set") - } - secretcookie, err = hex.DecodeString(cookie_secret) - if err != nil { - return err - } - } else { - panic("Templates are disabled") - } - - if err != nil { - return err - } - - user := UserCookie{Email: email, Token: token} - - // Initialize a buffer to hold the gob-encoded data. - var buf bytes.Buffer - - // Gob-encode the user data, storing the encoded output in the buffer. - err = gob.NewEncoder(&buf).Encode(&user) - if err != nil { - return err - } - - // Call buf.String() to get the gob-encoded value as a string and set it as - // the cookie value. - cookie := http.Cookie{ - Name: "goffee", - Value: buf.String(), - Path: "/", - MaxAge: 3600, - HttpOnly: true, - Secure: true, - SameSite: http.SameSiteLaxMode, - } - - // Write an encrypted cookie containing the gob-encoded data as normal. - err = CookieWriteEncrypted(w, cookie, secretcookie) - if err != nil { - return err - } - - return nil -} - -func CookieWrite(w http.ResponseWriter, cookie http.Cookie) error { - // Encode the cookie value using base64. - cookie.Value = base64.URLEncoding.EncodeToString([]byte(cookie.Value)) - - // Check the total length of the cookie contents. Return the ErrValueTooLong - // error if it's more than 4096 bytes. - if len(cookie.String()) > 4096 { - return ErrValueTooLong - } - - // Write the cookie as normal. - http.SetCookie(w, &cookie) - - return nil -} - -func CookieRead(r *http.Request, name string) (string, error) { - // Read the cookie as normal. - cookie, err := r.Cookie(name) - if err != nil { - return "", err - } - - // Decode the base64-encoded cookie value. If the cookie didn't contain a - // valid base64-encoded value, this operation will fail and we return an - // ErrInvalidValue error. - value, err := base64.URLEncoding.DecodeString(cookie.Value) - if err != nil { - return "", ErrInvalidValue - } - - // Return the decoded cookie value. - return string(value), nil -} - -func CookieWriteEncrypted(w http.ResponseWriter, cookie http.Cookie, secretKey []byte) error { - // Create a new AES cipher block from the secret key. - block, err := aes.NewCipher(secretKey) - if err != nil { - return err - } - - // Wrap the cipher block in Galois Counter Mode. - aesGCM, err := cipher.NewGCM(block) - if err != nil { - return err - } - - // Create a unique nonce containing 12 random bytes. - nonce := make([]byte, aesGCM.NonceSize()) - _, err = io.ReadFull(rand.Reader, nonce) - if err != nil { - return err - } - - // Prepare the plaintext input for encryption. Because we want to - // authenticate the cookie name as well as the value, we make this plaintext - // in the format "{cookie name}:{cookie value}". We use the : character as a - // separator because it is an invalid character for cookie names and - // therefore shouldn't appear in them. - plaintext := fmt.Sprintf("%s:%s", cookie.Name, cookie.Value) - - // Encrypt the data using aesGCM.Seal(). By passing the nonce as the first - // parameter, the encrypted data will be appended to the nonce — meaning - // that the returned encryptedValue variable will be in the format - // "{nonce}{encrypted plaintext data}". - encryptedValue := aesGCM.Seal(nonce, nonce, []byte(plaintext), nil) - - // Set the cookie value to the encryptedValue. - cookie.Value = string(encryptedValue) - - // Write the cookie as normal. - return CookieWrite(w, cookie) -} - -func CookieReadEncrypted(r *http.Request, name string, secretKey []byte) (string, error) { - // Read the encrypted value from the cookie as normal. - encryptedValue, err := CookieRead(r, name) - if err != nil { - return "", err - } - - // Create a new AES cipher block from the secret key. - block, err := aes.NewCipher(secretKey) - if err != nil { - return "", err - } - - // Wrap the cipher block in Galois Counter Mode. - aesGCM, err := cipher.NewGCM(block) - if err != nil { - return "", err - } - - // Get the nonce size. - nonceSize := aesGCM.NonceSize() - - // To avoid a potential 'index out of range' panic in the next step, we - // check that the length of the encrypted value is at least the nonce - // size. - if len(encryptedValue) < nonceSize { - return "", ErrInvalidValue - } - - // Split apart the nonce from the actual encrypted data. - nonce := encryptedValue[:nonceSize] - ciphertext := encryptedValue[nonceSize:] - - // Use aesGCM.Open() to decrypt and authenticate the data. If this fails, - // return a ErrInvalidValue error. - plaintext, err := aesGCM.Open(nil, []byte(nonce), []byte(ciphertext), nil) - if err != nil { - return "", ErrInvalidValue - } - - // The plaintext value is in the format "{cookie name}:{cookie value}". We - // use strings.Cut() to split it on the first ":" character. - expectedName, value, ok := strings.Cut(string(plaintext), ":") - if !ok { - return "", ErrInvalidValue - } - - // Check that the cookie name is the expected one and hasn't been changed. - if expectedName != name { - return "", ErrInvalidValue - } - - // Return the plaintext cookie value. - return value, nil -} diff --git a/core.go b/core.go index 59691c6..ba208d3 100644 --- a/core.go +++ b/core.go @@ -98,7 +98,9 @@ func (app *App) Run(router *httprouter.Router) { TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr) // if enabled, if TemplateEnable { - router.ServeFiles("/public/*filepath", http.Dir("storage/public")) + // add public path + publicPath := os.Getenv("TEMPLATE_PUBLIC") + router.ServeFiles("/public/*filepath", http.Dir(publicPath)) } useHttpsStr := os.Getenv("App_USE_HTTPS") @@ -181,7 +183,6 @@ func (app *App) RegisterRoutes(routes []Route, router *httprouter.Router) *httpr func (app *App) makeHTTPRouterHandlerFunc(h Controller, ms []Hook) httprouter.Handle { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - ctx := &Context{ Request: &Request{ httpRequest: r, @@ -205,13 +206,11 @@ func (app *App) makeHTTPRouterHandlerFunc(h Controller, ms []Hook) httprouter.Ha GetEventsManager: resolveEventsManager(), GetLogger: resolveLogger(), } - ctx.prepare(ctx) rhs := app.combHandlers(h, ms) app.prepareChain(rhs) app.t = 0 app.chain.execute(ctx) - for _, header := range ctx.Response.headers { w.Header().Add(header.key, header.val) } @@ -224,7 +223,6 @@ func (app *App) makeHTTPRouterHandlerFunc(h Controller, ms []Hook) httprouter.Ha } else { ct = CONTENT_TYPE_HTML } - w.Header().Add(CONTENT_TYPE, ct) if ctx.Response.statusCode != 0 { w.WriteHeader(ctx.Response.statusCode) diff --git a/template/components/button.go b/template/components/button.go new file mode 100644 index 0000000..0c52a38 --- /dev/null +++ b/template/components/button.go @@ -0,0 +1,10 @@ +package components + +type Button struct { + Text string + Link string + Icon string + IsSubmit bool + IsPrimary bool + IsDisabled bool +} diff --git a/template/components/button.html b/template/components/button.html new file mode 100644 index 0000000..6590905 --- /dev/null +++ b/template/components/button.html @@ -0,0 +1,22 @@ +{{define "button"}} + +{{end}} \ No newline at end of file diff --git a/template/components/content_badge.go b/template/components/content_badge.go deleted file mode 100644 index 7da7147..0000000 --- a/template/components/content_badge.go +++ /dev/null @@ -1,7 +0,0 @@ -package components - -type ContentBadge struct { - Text string - TypeClass string // type primary, secondary, success, danger, warning, info, light, dark, (default secondary) - IsOutline bool -} diff --git a/template/components/content_badge.html b/template/components/content_badge.html deleted file mode 100644 index 6f72cf7..0000000 --- a/template/components/content_badge.html +++ /dev/null @@ -1,7 +0,0 @@ -{{define "content_badge"}} - {{.Text}} -{{end}} \ No newline at end of file diff --git a/template/components/content_dropdown.go b/template/components/content_dropdown.go deleted file mode 100644 index 1040060..0000000 --- a/template/components/content_dropdown.go +++ /dev/null @@ -1,15 +0,0 @@ -package components - -type ContentDropdown struct { - Label string - TypeClass string // type primary, secondary, success, danger, warning, info, light, dark, link, outline-primary - IsDisabled bool - Items []ContentDropdownItem -} - -type ContentDropdownItem struct { - Text string - Link string - IsDisabled bool - IsActive bool -} diff --git a/template/components/content_dropdown.html b/template/components/content_dropdown.html deleted file mode 100644 index ce83a26..0000000 --- a/template/components/content_dropdown.html +++ /dev/null @@ -1,16 +0,0 @@ -{{define "content_dropdown_item"}} -
  • {{.Text}}
  • -{{end}} - -{{define "content_dropdown"}} - -{{end}} \ No newline at end of file diff --git a/template/components/content_href.go b/template/components/content_href.go deleted file mode 100644 index 143ca80..0000000 --- a/template/components/content_href.go +++ /dev/null @@ -1,10 +0,0 @@ -package components - -type ContentHref struct { - Text string - Link string - Icon string - IsButton bool - TypeClass string // type primary, secondary, success, danger, warning, info, light, dark, link - IsDisabled bool -} diff --git a/template/components/content_href.html b/template/components/content_href.html deleted file mode 100644 index f4980ac..0000000 --- a/template/components/content_href.html +++ /dev/null @@ -1,6 +0,0 @@ -{{define "content_href"}} - - {{.Text}} - -{{end}} \ No newline at end of file diff --git a/template/components/content_list.go b/template/components/content_list.go deleted file mode 100644 index 34bfb49..0000000 --- a/template/components/content_list.go +++ /dev/null @@ -1,19 +0,0 @@ -package components - -type ContentList struct { - Items []ContentListItem -} - -type ContentListItem struct { - Text string - Description string - EndElement string - //Link string - TypeClass string // primary, secondary, success, danger, warning, info, light, dark - IsDisabled bool - //IsActive bool -} - -//link -//border -// badge diff --git a/template/components/content_list.html b/template/components/content_list.html deleted file mode 100644 index 31af60b..0000000 --- a/template/components/content_list.html +++ /dev/null @@ -1,14 +0,0 @@ -{{define "content_list"}} - - - - -{{end}} \ No newline at end of file diff --git a/template/components/content_table.go b/template/components/content_table.go deleted file mode 100644 index 1cb7024..0000000 --- a/template/components/content_table.go +++ /dev/null @@ -1,20 +0,0 @@ -package components - -type ContentTable struct { - ID string - TableClass string // table-primary, table-secondary,.. table-striped table-bordered - HeadClass string // table-dark table-light - AllTH []ContentTableTH - AllTD [][]ContentTableTD -} - -type ContentTableTH struct { - ID string - ValueType string // -> default string, href, badge - Value string -} - -type ContentTableTD struct { - ID string - Value interface{} // string or component struct according ValueType -} diff --git a/template/components/content_table.html b/template/components/content_table.html deleted file mode 100644 index 2bf559d..0000000 --- a/template/components/content_table.html +++ /dev/null @@ -1,25 +0,0 @@ -{{define "content_table"}} - - - - {{range $index, $col := .AllTH}}{{end}} - - - - {{- range .AllTD}} - {{range $index, $item := .}} - {{end}} - {{- end}} - -
    {{ $col.Value }}
    - {{ with $x := index $.AllTH $index }} - {{ if eq $x.ValueType "href"}} - {{template "content_href" $item.Value}} - {{ else if eq $x.ValueType "badge"}} - {{template "content_badge" $item.Value}} - {{ else }} - {{ $item.Value }} - {{end}} - {{end}} -
    -{{end}} diff --git a/template/components/form_button.go b/template/components/form_button.go deleted file mode 100644 index 658306e..0000000 --- a/template/components/form_button.go +++ /dev/null @@ -1,9 +0,0 @@ -package components - -type FormButton struct { - Text string - Icon string - IsSubmit bool - TypeClass string // type primary, secondary, success, danger, warning, info, light, dark, link, outline-primary - IsDisabled bool -} diff --git a/template/components/form_button.html b/template/components/form_button.html deleted file mode 100644 index 7594613..0000000 --- a/template/components/form_button.html +++ /dev/null @@ -1,20 +0,0 @@ -{{define "form_button"}} - - - {{if eq .Icon "gear"}} - - - - - {{else if eq .Icon "arrow-right"}} - - - - {{else if eq .Icon "plus"}} - - - - {{end}} -{{end}} \ No newline at end of file diff --git a/template/components/form_checkbox.go b/template/components/form_checkbox.go deleted file mode 100644 index 819da82..0000000 --- a/template/components/form_checkbox.go +++ /dev/null @@ -1,14 +0,0 @@ -package components - -type FormCheckbox struct { - Label string - AllCheckbox []FormCheckboxItem -} - -type FormCheckboxItem struct { - ID string - Name string - Value string - Label string - IsChecked bool -} diff --git a/template/components/form_checkbox.html b/template/components/form_checkbox.html deleted file mode 100644 index 9a8d845..0000000 --- a/template/components/form_checkbox.html +++ /dev/null @@ -1,11 +0,0 @@ -{{define "form_checkbox"}} -
    - - {{range $options := .AllCheckbox}} -
    - - -
    - {{end}} -
    -{{end}} \ No newline at end of file diff --git a/template/components/form_input.go b/template/components/form_input.go deleted file mode 100644 index e27910f..0000000 --- a/template/components/form_input.go +++ /dev/null @@ -1,13 +0,0 @@ -package components - -type FormInput struct { - ID string - Label string - Type string - Placeholder string - Value string - Hint string - Error string - IsDisabled bool - IsRequired bool -} diff --git a/template/components/form_input.html b/template/components/form_input.html deleted file mode 100644 index 8159896..0000000 --- a/template/components/form_input.html +++ /dev/null @@ -1,18 +0,0 @@ -{{define "form_input"}} -
    - - - {{if ne .Hint ""}}{{.Hint}}{{end}} - {{if ne .Error ""}}
    {{.Error}}
    {{end}} -
    -{{end}} \ No newline at end of file diff --git a/template/components/form_radio.go b/template/components/form_radio.go deleted file mode 100644 index e8e923c..0000000 --- a/template/components/form_radio.go +++ /dev/null @@ -1,15 +0,0 @@ -package components - -type FormRadio struct { - Label string - AllRadios []FormRadioItem -} - -type FormRadioItem struct { - ID string - Name string - Value string - Label string - IsDisabled bool - IsChecked bool -} diff --git a/template/components/form_radio.html b/template/components/form_radio.html deleted file mode 100644 index a0c26cd..0000000 --- a/template/components/form_radio.html +++ /dev/null @@ -1,11 +0,0 @@ -{{define "form_radio"}} -
    - - {{range $options := .AllRadios}} -
    - - -
    - {{end}} -
    -{{end}} \ No newline at end of file diff --git a/template/components/form_select.go b/template/components/form_select.go deleted file mode 100644 index db94fe3..0000000 --- a/template/components/form_select.go +++ /dev/null @@ -1,13 +0,0 @@ -package components - -type FormSelect struct { - ID string - SelectedOption FormSelectOption - Label string - AllOptions []FormSelectOption -} - -type FormSelectOption struct { - Value string - Caption string -} diff --git a/template/components/form_select.html b/template/components/form_select.html deleted file mode 100644 index 6317e6b..0000000 --- a/template/components/form_select.html +++ /dev/null @@ -1,10 +0,0 @@ -{{define "form_select"}} -
    - - -
    -{{end}} \ No newline at end of file diff --git a/template/components/form_textarea.go b/template/components/form_textarea.go deleted file mode 100644 index 818ae02..0000000 --- a/template/components/form_textarea.go +++ /dev/null @@ -1,7 +0,0 @@ -package components - -type FormTextarea struct { - ID string - Label string - Value string -} diff --git a/template/components/form_textarea.html b/template/components/form_textarea.html deleted file mode 100644 index 23559ab..0000000 --- a/template/components/form_textarea.html +++ /dev/null @@ -1,6 +0,0 @@ -{{define "form_textarea"}} -
    - - -
    -{{end}} \ No newline at end of file diff --git a/template/components/head.html b/template/components/head.html new file mode 100644 index 0000000..cecc5ed --- /dev/null +++ b/template/components/head.html @@ -0,0 +1,8 @@ +{{define "head"}} + + + + {{.}} | Goffee + + +{{end}} \ No newline at end of file diff --git a/template/components/page_card.go b/template/components/page_card.go deleted file mode 100644 index 7f9b504..0000000 --- a/template/components/page_card.go +++ /dev/null @@ -1,8 +0,0 @@ -package components - -type PageCard struct { - CardTitle string - CardSubTitle string - CardBody string - CardLink string -} diff --git a/template/components/page_card.html b/template/components/page_card.html deleted file mode 100644 index 9ef8821..0000000 --- a/template/components/page_card.html +++ /dev/null @@ -1,11 +0,0 @@ -{{define "page_card"}} -
    -
    - {{if .CardTitle}}
    {{.CardTitle}}
    {{end}} - {{if .CardSubTitle}}
    {{.CardSubTitle}}
    {{end}} - {{if .CardBody}}

    {{.CardBody}}

    {{end}} - {{block "page_card_content" .}}{{end}} - {{if .CardLink}}Card link{{end}} -
    -
    -{{end}} \ No newline at end of file diff --git a/template/components/page_footer.html b/template/components/page_footer.html deleted file mode 100644 index 6421f3e..0000000 --- a/template/components/page_footer.html +++ /dev/null @@ -1,7 +0,0 @@ -{{define "page_footer"}} - - - -{{end}} \ No newline at end of file diff --git a/template/components/page_head.html b/template/components/page_head.html deleted file mode 100644 index e589e84..0000000 --- a/template/components/page_head.html +++ /dev/null @@ -1,11 +0,0 @@ -{{define "page_head"}} - - - - - {{.}} | Goffee - - - - -{{end}} \ No newline at end of file diff --git a/template/components/page_nav.go b/template/components/page_nav.go deleted file mode 100644 index 862d1c8..0000000 --- a/template/components/page_nav.go +++ /dev/null @@ -1,16 +0,0 @@ -package components - -type PageNav struct { - NavClass string // nav-pills, nav-underline - NavItems []PageNavItem - IsVertical bool - IsTab bool -} - -type PageNavItem struct { - Text string - Link string - IsDisabled bool - IsActive bool - ChildItems []PageNavItem -} diff --git a/template/components/page_nav.html b/template/components/page_nav.html deleted file mode 100644 index 08faaf4..0000000 --- a/template/components/page_nav.html +++ /dev/null @@ -1,23 +0,0 @@ -{{define "page_nav"}} - -{{end}} diff --git a/template/components/page_page.html b/template/components/page_page.html deleted file mode 100644 index 2c83582..0000000 --- a/template/components/page_page.html +++ /dev/null @@ -1,14 +0,0 @@ - - - {{template "page_head" "Goffee"}} - -
    - {{block "page_content" .}} -
    -

    Use this file as base inside cup application

    -
    - {{end}} - {{template "page_footer"}} -
    - - \ No newline at end of file diff --git a/template/components/title.go b/template/components/title.go new file mode 100644 index 0000000..0e68b6a --- /dev/null +++ b/template/components/title.go @@ -0,0 +1,5 @@ +package components + +type Title struct { + Label string +} diff --git a/template/components/title.html b/template/components/title.html new file mode 100644 index 0000000..eef5bf3 --- /dev/null +++ b/template/components/title.html @@ -0,0 +1,5 @@ +{{define "title"}} +
    +

    {{.Label}}

    +
    +{{end}} \ No newline at end of file diff --git a/validator.go b/validator.go index ba4d8a2..7d8eac8 100644 --- a/validator.go +++ b/validator.go @@ -33,14 +33,18 @@ func (v *Validator) Validate(data map[string]interface{}, rules map[string]inter vr = validationResult{} vr.hasFailed = false res := map[string]string{} - for rule_key, rule_val := range rules { - rls, err := parseRules(rule_val) + for key, val := range data { + _, ok := rules[key] + if !ok { + continue + } + rls, err := parseRules(rules[key]) if err != nil { panic(err.Error()) } - err = validation.Validate(data[rule_key], rls...) + err = validation.Validate(val, rls...) if err != nil { - res[rule_key] = fmt.Sprintf("%v: %v", rule_key, err.Error()) + res[key] = fmt.Sprintf("%v: %v", key, err.Error()) } }