Compare commits
No commits in common. "main" and "main" have entirely different histories.
73 changed files with 139 additions and 61268 deletions
|
@ -8,7 +8,6 @@ App_HTTP_HOST=localhost
|
|||
App_HTTP_PORT=8080
|
||||
App_USE_HTTPS=false
|
||||
App_USE_LETSENCRYPT=false
|
||||
App_USE_CORESERVICES=false
|
||||
APP_LETSENCRYPT_EMAIL=mail@example.com
|
||||
App_HTTPS_HOSTS=example.com, www.example.com
|
||||
App_REDIRECT_HTTP_TO_HTTPS=false
|
||||
|
@ -19,7 +18,8 @@ App_KEY_FILE_PATH=tls/server.key
|
|||
###### TEMPLATES ######
|
||||
#######################################
|
||||
TEMPLATE_ENABLE=true
|
||||
COOKIE_SECRET=13d6b4dff8f84a10851021ec8608f814570d562c92fe6b5ec4c9f595bcb3234b
|
||||
TEMPLATE_PUBLIC=storage/public
|
||||
TEMPLATE_COMPONENTS=true
|
||||
|
||||
#######################################
|
||||
###### JWT ######
|
||||
|
@ -46,10 +46,10 @@ POSTGRES_PASSWORD=secret
|
|||
POSTGRES_DB_NAME=db_test
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_SSL_MODE=disable
|
||||
POSTGRES_TIMEZONE=America/Argentina/Buenos_Aires
|
||||
POSTGRES_TIMEZONE=Asia/Dubai
|
||||
|
||||
#_____ SQLITE _____#
|
||||
SQLITE_DB_PATH=storage/sqlite/sqlite.db
|
||||
SQLITE_DB_PATH=storage/sqlite.db
|
||||
|
||||
#######################################
|
||||
###### CACHE ######
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,10 +1,9 @@
|
|||
.air.toml
|
||||
.env
|
||||
.env-dev
|
||||
tmp/*
|
||||
logs/*
|
||||
!logs/.gitkeep
|
||||
tls/*
|
||||
!tls/.gitkeep
|
||||
.DS_Store
|
||||
storage/sqlite/*
|
||||
storage/public/*
|
||||
|
|
|
@ -8,10 +8,7 @@ package controllers
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -126,13 +123,6 @@ func Signin(c *core.Context) *core.Response {
|
|||
email := c.GetRequestParam("email")
|
||||
password := c.GetRequestParam("password")
|
||||
|
||||
// check if template engine is enable
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
data := map[string]interface{}{
|
||||
"email": email,
|
||||
"password": password,
|
||||
|
@ -145,62 +135,37 @@ func Signin(c *core.Context) *core.Response {
|
|||
|
||||
if v.Failed() {
|
||||
c.GetLogger().Error(v.GetErrorMessagesJson())
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
|
||||
}
|
||||
}
|
||||
|
||||
var user models.User
|
||||
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
c.GetLogger().Error(res.Error.Error())
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid email or password",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
ok, err := c.GetHashing().CheckPasswordHash(user.Password, c.CastToString(password))
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": err.Error(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid email or password",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
token, err := c.GetJWT().GenerateToken(map[string]interface{}{
|
||||
"userID": user.ID,
|
||||
|
@ -208,53 +173,25 @@ func Signin(c *core.Context) *core.Response {
|
|||
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
// TODO set error in session
|
||||
if TemplateEnable {
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
}
|
||||
// cache the token
|
||||
userAgent := c.GetUserAgent()
|
||||
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent)
|
||||
err = c.GetCache().Set(hashedCacheKey, token)
|
||||
|
||||
// delete data from old sessions
|
||||
sessionKey := fmt.Sprintf("sess_%v", userAgent)
|
||||
hashedSessionKey := utils.CreateAuthTokenHashedCacheKey(user.ID, sessionKey)
|
||||
_ = c.GetCache().Delete(hashedSessionKey)
|
||||
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if TemplateEnable {
|
||||
// create cookie
|
||||
err = core.SetCookie(c.Response.HttpResponseWriter, email.(string), token)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error write encrypted cookie: %v", err))
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// redirecto to app
|
||||
return c.Response.Redirect("/appsample")
|
||||
} else {
|
||||
return c.Response.Json(c.MapToJson(map[string]string{
|
||||
"token": token,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func ResetPasswordRequest(c *core.Context) *core.Response {
|
||||
email := c.GetRequestParam("email")
|
||||
|
@ -425,28 +362,8 @@ func SetNewPassword(c *core.Context) *core.Response {
|
|||
}
|
||||
|
||||
func Signout(c *core.Context) *core.Response {
|
||||
|
||||
// check if template engine is enable
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
token := ""
|
||||
|
||||
if TemplateEnable {
|
||||
// get cookie
|
||||
usercookie, err := c.GetCookie()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
token = usercookie.Token
|
||||
} else {
|
||||
tokenRaw := c.GetHeader("Authorization")
|
||||
token = strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
|
||||
}
|
||||
|
||||
token := strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
|
||||
if token == "" {
|
||||
return c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
|
|
|
@ -6,47 +6,15 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/core/template/components"
|
||||
)
|
||||
|
||||
// Show home page
|
||||
func WelcomeHome(c *core.Context) *core.Response {
|
||||
|
||||
// check if template engine is enable
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
if TemplateEnable {
|
||||
|
||||
// first, include all compoments
|
||||
type templateData struct {
|
||||
PageCard components.PageCard
|
||||
}
|
||||
|
||||
// now fill data of the components
|
||||
tmplData := templateData{
|
||||
PageCard: components.PageCard{
|
||||
CardTitle: "Golang Framework",
|
||||
CardBody: "Welcome to Goffee",
|
||||
},
|
||||
}
|
||||
|
||||
return c.Response.Template("welcome.html", tmplData)
|
||||
|
||||
} else {
|
||||
message := "{\"message\": \"Welcome to Goffee\"}"
|
||||
return c.Response.Json(message)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Show dashboard
|
||||
func WelcomeToDashboard(c *core.Context) *core.Response {
|
||||
message := "{\"message\": \"Welcome to Dashboard\"}"
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/core/template/components"
|
||||
"git.smarteching.com/goffee/cup/utils"
|
||||
)
|
||||
|
||||
// Show basic template
|
||||
func Sample(c *core.Context) *core.Response {
|
||||
|
||||
// first, include all compoments
|
||||
type templateData struct {
|
||||
PageCard components.PageCard
|
||||
}
|
||||
|
||||
// now fill data of the components
|
||||
tmplData := templateData{
|
||||
PageCard: components.PageCard{
|
||||
CardTitle: "Framework Goffee",
|
||||
CardBody: "Powered by Golang",
|
||||
},
|
||||
}
|
||||
|
||||
return c.Response.Template("basic.html", tmplData)
|
||||
|
||||
}
|
||||
|
||||
// Show basic app login
|
||||
func AppLogin(c *core.Context) *core.Response {
|
||||
|
||||
// first, include all compoments
|
||||
// first, include all compoments
|
||||
type templateData struct {
|
||||
PageCard components.PageCard
|
||||
}
|
||||
|
||||
// now fill data of the components
|
||||
tmplData := templateData{
|
||||
PageCard: components.PageCard{
|
||||
CardTitle: "Card title",
|
||||
CardBody: "Loerm ipsum at deim",
|
||||
},
|
||||
}
|
||||
return c.Response.Template("login.html", tmplData)
|
||||
}
|
||||
|
||||
// Show basic app login
|
||||
func AppSession(c *core.Context) *core.Response {
|
||||
|
||||
var session = new(utils.SessionUser)
|
||||
|
||||
// true if session is active
|
||||
hassession := session.Init(c)
|
||||
|
||||
//session.Set("numberdos", 66)
|
||||
|
||||
type templateData struct {
|
||||
PageCard components.PageCard
|
||||
}
|
||||
|
||||
// now fill data of the components
|
||||
tmplData := templateData{}
|
||||
|
||||
if hassession {
|
||||
|
||||
sesiondata := ""
|
||||
cardtitle := fmt.Sprintf("Session user id: %v", session.GetUserID())
|
||||
numberdos, ok := session.Get("numberdos")
|
||||
|
||||
if numberdos != nil {
|
||||
numberdos = numberdos.(float64) + 10
|
||||
} else {
|
||||
numberdos = 10
|
||||
}
|
||||
|
||||
session.Set("numberdos", numberdos)
|
||||
|
||||
if ok {
|
||||
sesiondata = fmt.Sprintf("OK, Session numberdos has %v", numberdos)
|
||||
} else {
|
||||
sesiondata = fmt.Sprintf("No ok, session numberdos has %v", numberdos)
|
||||
}
|
||||
|
||||
// delete single
|
||||
//session.Delete("numberdos")
|
||||
|
||||
// delete all data
|
||||
//session.Flush()
|
||||
|
||||
tmplData = templateData{
|
||||
PageCard: components.PageCard{
|
||||
CardTitle: cardtitle,
|
||||
CardBody: sesiondata,
|
||||
},
|
||||
}
|
||||
|
||||
return c.Response.Template("appsession.html", tmplData)
|
||||
|
||||
} else {
|
||||
|
||||
return c.Response.Template("login.html", tmplData)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Show basic app sample
|
||||
func AppSample(c *core.Context) *core.Response {
|
||||
|
||||
// first, include all compoments
|
||||
type templateData struct {
|
||||
PageCard components.PageCard
|
||||
ContentDropdown components.ContentDropdown
|
||||
}
|
||||
|
||||
// now fill data of the components
|
||||
tmplData := templateData{
|
||||
PageCard: components.PageCard{
|
||||
CardTitle: "Protected page",
|
||||
CardBody: "If you can see this page, your are loggedin",
|
||||
},
|
||||
ContentDropdown: components.ContentDropdown{
|
||||
Label: "dropdown",
|
||||
Items: []components.ContentDropdownItem{
|
||||
{
|
||||
Text: "Signout",
|
||||
Link: "#",
|
||||
ID: "signout",
|
||||
},
|
||||
{
|
||||
Text: "item disabled",
|
||||
Link: "#",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
//fmt.Printf("Outside cookie user is: %s", user.Email)
|
||||
|
||||
return c.Response.Template("app.html", tmplData)
|
||||
|
||||
}
|
|
@ -1,755 +0,0 @@
|
|||
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/core/template/components"
|
||||
)
|
||||
|
||||
// Show home page
|
||||
func Themedemo(c *core.Context) *core.Response {
|
||||
|
||||
// check if template engine is enabled
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
if TemplateEnable {
|
||||
|
||||
// first, include all compoments
|
||||
type templateData struct {
|
||||
PageCard components.PageCard
|
||||
}
|
||||
|
||||
// now fill data of the components
|
||||
tmplData := templateData{
|
||||
PageCard: components.PageCard{
|
||||
CardTitle: "Card title",
|
||||
CardBody: "Loerm ipsum at deim",
|
||||
},
|
||||
}
|
||||
|
||||
return c.Response.Template("custom_theme_base.html", tmplData)
|
||||
|
||||
} else {
|
||||
|
||||
message := "{\"message\": \"Error, template not enabled\"}"
|
||||
return c.Response.Json(message)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Show form element page
|
||||
func Themeform(c *core.Context) *core.Response {
|
||||
|
||||
// check if template engine is enabled
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
if TemplateEnable {
|
||||
|
||||
// first, include all compoments
|
||||
type templateData struct {
|
||||
FormText components.FormInput
|
||||
FormEmail components.FormInput
|
||||
FormButton components.FormButton
|
||||
FormSelectCity components.FormSelect
|
||||
FormTextarea components.FormTextarea
|
||||
FormRadio components.FormRadio
|
||||
FormCheckbox components.FormCheckbox
|
||||
}
|
||||
|
||||
// for select options
|
||||
var allOptions []components.FormSelectOption
|
||||
var option components.FormSelectOption
|
||||
option.Value = "ch"
|
||||
option.Caption = "China"
|
||||
allOptions = append(allOptions, option)
|
||||
option.Value = "ba"
|
||||
option.Caption = "Buenos Aires"
|
||||
allOptions = append(allOptions, option)
|
||||
option.Value = "fr"
|
||||
option.Caption = "France"
|
||||
selectedOption := option
|
||||
allOptions = append(allOptions, option)
|
||||
|
||||
// for radio options
|
||||
var allOptionsr []components.FormRadioItem
|
||||
var optionr components.FormRadioItem
|
||||
optionr.ID = "citysch"
|
||||
optionr.Name = "citys"
|
||||
optionr.Value = "china"
|
||||
optionr.Label = "China"
|
||||
allOptionsr = append(allOptionsr, optionr)
|
||||
optionr.ID = "citysba"
|
||||
optionr.Name = "citys"
|
||||
optionr.Value = "buenosaires"
|
||||
optionr.Label = "Buenos Aires"
|
||||
//optionr.IsDisabled = true
|
||||
allOptionsr = append(allOptionsr, optionr)
|
||||
|
||||
// for radio options
|
||||
var allOptionsc []components.FormCheckboxItem
|
||||
var optionc components.FormCheckboxItem
|
||||
optionc.ID = "citysch"
|
||||
optionc.Name = "citys"
|
||||
optionc.Value = "china"
|
||||
optionc.Label = "China"
|
||||
allOptionsc = append(allOptionsc, optionc)
|
||||
optionc.ID = "citysba"
|
||||
optionc.Name = "citys"
|
||||
optionc.Value = "buenosaires"
|
||||
optionc.Label = "Buenos Aires"
|
||||
allOptionsc = append(allOptionsc, optionc)
|
||||
optionc.ID = "london"
|
||||
optionc.Name = "london"
|
||||
optionc.Value = "london"
|
||||
optionc.Label = "London"
|
||||
//optionc.IsChecked = true
|
||||
allOptionsc = append(allOptionsc, optionc)
|
||||
|
||||
// now fill data of the components
|
||||
tmplData := templateData{
|
||||
FormText: components.FormInput{
|
||||
ID: "text",
|
||||
Label: "Name",
|
||||
Type: "text",
|
||||
Hint: "This is sample hint",
|
||||
Placeholder: "Enter your name",
|
||||
},
|
||||
FormEmail: components.FormInput{
|
||||
ID: "email",
|
||||
Label: "Email",
|
||||
Type: "email",
|
||||
IsRequired: true,
|
||||
Placeholder: "Enter your email address",
|
||||
},
|
||||
FormButton: components.FormButton{
|
||||
Text: "Login",
|
||||
IsSubmit: true,
|
||||
TypeClass: "primary",
|
||||
},
|
||||
FormSelectCity: components.FormSelect{
|
||||
ID: "city",
|
||||
Label: "Select city",
|
||||
AllOptions: allOptions,
|
||||
SelectedOption: selectedOption,
|
||||
},
|
||||
FormTextarea: components.FormTextarea{
|
||||
ID: "text",
|
||||
Label: "Example textarea",
|
||||
},
|
||||
FormRadio: components.FormRadio{
|
||||
Label: "Radio buttons",
|
||||
AllRadios: allOptionsr,
|
||||
},
|
||||
FormCheckbox: components.FormCheckbox{
|
||||
Label: "Checkbox options",
|
||||
AllCheckbox: allOptionsc,
|
||||
},
|
||||
}
|
||||
return c.Response.Template("custom_theme_formpage.html", tmplData)
|
||||
|
||||
} else {
|
||||
|
||||
message := "{\"message\": \"Error, template not enabled\"}"
|
||||
return c.Response.Json(message)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func ThemeElements(c *core.Context) *core.Response {
|
||||
// check if template engine is enabled
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
if TemplateEnable {
|
||||
type templateData struct {
|
||||
Buttons []components.FormButton
|
||||
Hrefs []components.ContentHref
|
||||
Badges []components.ContentBadge
|
||||
Dropdowns []components.ContentDropdown
|
||||
Lists []components.ContentList
|
||||
Menus []components.PageNav
|
||||
}
|
||||
buttons := []components.FormButton{
|
||||
{
|
||||
Text: "primary",
|
||||
TypeClass: "primary",
|
||||
},
|
||||
{
|
||||
Text: "secondary",
|
||||
TypeClass: "secondary",
|
||||
},
|
||||
{
|
||||
Text: "success",
|
||||
TypeClass: "success",
|
||||
},
|
||||
{
|
||||
Text: "danger",
|
||||
TypeClass: "danger",
|
||||
},
|
||||
{
|
||||
Text: "warning",
|
||||
TypeClass: "warning",
|
||||
},
|
||||
{
|
||||
Text: "info",
|
||||
TypeClass: "info",
|
||||
},
|
||||
{
|
||||
Text: "light",
|
||||
TypeClass: "light",
|
||||
},
|
||||
{
|
||||
Text: "dark",
|
||||
TypeClass: "dark",
|
||||
},
|
||||
{
|
||||
Text: "link",
|
||||
TypeClass: "link",
|
||||
},
|
||||
{
|
||||
Text: "disabled",
|
||||
TypeClass: "primary",
|
||||
IsDisabled: true,
|
||||
},
|
||||
{
|
||||
Text: "outline-primary",
|
||||
TypeClass: "outline-primary",
|
||||
},
|
||||
{
|
||||
Text: "outline-secondary",
|
||||
TypeClass: "outline-secondary",
|
||||
},
|
||||
{
|
||||
Text: "outline-success",
|
||||
TypeClass: "outline-success",
|
||||
},
|
||||
{
|
||||
Text: "outline-danger",
|
||||
TypeClass: "outline-danger",
|
||||
},
|
||||
{
|
||||
Text: "outline-warning",
|
||||
TypeClass: "outline-warning",
|
||||
},
|
||||
{
|
||||
Text: "outline-info",
|
||||
TypeClass: "outline-info",
|
||||
},
|
||||
{
|
||||
Text: "outline-light",
|
||||
TypeClass: "outline-light",
|
||||
},
|
||||
{
|
||||
Text: "outline-dark",
|
||||
TypeClass: "outline-dark",
|
||||
},
|
||||
}
|
||||
hrefs := []components.ContentHref{
|
||||
{
|
||||
Text: "href",
|
||||
Link: "#",
|
||||
IsButton: false,
|
||||
},
|
||||
{
|
||||
Text: "link",
|
||||
Link: "#",
|
||||
IsButton: false,
|
||||
TypeClass: "link",
|
||||
},
|
||||
{
|
||||
Text: "button",
|
||||
Link: "#",
|
||||
IsButton: true,
|
||||
TypeClass: "primary",
|
||||
},
|
||||
{
|
||||
Text: "href disabled",
|
||||
Link: "#",
|
||||
IsButton: false,
|
||||
IsDisabled: true,
|
||||
},
|
||||
{
|
||||
Text: "link disabled",
|
||||
Link: "#",
|
||||
TypeClass: "link",
|
||||
IsDisabled: true,
|
||||
},
|
||||
{
|
||||
Text: "button disabled",
|
||||
Link: "#",
|
||||
IsButton: true,
|
||||
TypeClass: "primary",
|
||||
IsDisabled: true,
|
||||
},
|
||||
}
|
||||
badges := []components.ContentBadge{
|
||||
{
|
||||
Text: "primary",
|
||||
TypeClass: "primary",
|
||||
},
|
||||
{
|
||||
Text: "secondary",
|
||||
TypeClass: "secondary",
|
||||
},
|
||||
{
|
||||
Text: "success",
|
||||
TypeClass: "success",
|
||||
},
|
||||
{
|
||||
Text: "danger",
|
||||
TypeClass: "danger",
|
||||
},
|
||||
{
|
||||
Text: "warning",
|
||||
TypeClass: "warning",
|
||||
},
|
||||
{
|
||||
Text: "info",
|
||||
TypeClass: "info",
|
||||
},
|
||||
{
|
||||
Text: "light",
|
||||
TypeClass: "light",
|
||||
},
|
||||
{
|
||||
Text: "dark",
|
||||
TypeClass: "dark",
|
||||
},
|
||||
{
|
||||
Text: "outline",
|
||||
TypeClass: "primary",
|
||||
IsOutline: true,
|
||||
},
|
||||
{
|
||||
Text: "outline",
|
||||
TypeClass: "success",
|
||||
IsOutline: true,
|
||||
},
|
||||
{
|
||||
Text: "outline",
|
||||
TypeClass: "danger",
|
||||
IsOutline: true,
|
||||
},
|
||||
{
|
||||
Text: "outline",
|
||||
TypeClass: "warning",
|
||||
IsOutline: true,
|
||||
},
|
||||
}
|
||||
dropdowns := []components.ContentDropdown{
|
||||
// dropdown
|
||||
{
|
||||
Label: "dropdown",
|
||||
Items: []components.ContentDropdownItem{
|
||||
{
|
||||
Text: "item ",
|
||||
Link: "#",
|
||||
},
|
||||
{
|
||||
Text: "item disabled",
|
||||
Link: "#",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// dropdown
|
||||
{
|
||||
Label: "primary",
|
||||
TypeClass: "primary",
|
||||
Items: []components.ContentDropdownItem{
|
||||
{
|
||||
Text: "item ",
|
||||
Link: "#",
|
||||
},
|
||||
{
|
||||
Text: "item ",
|
||||
Link: "#",
|
||||
IsActive: true,
|
||||
},
|
||||
{
|
||||
Text: "item disabled",
|
||||
Link: "#",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// dropdown
|
||||
{
|
||||
Label: "outline",
|
||||
TypeClass: "outline-primary",
|
||||
Items: []components.ContentDropdownItem{
|
||||
{
|
||||
Text: "item ",
|
||||
Link: "#",
|
||||
},
|
||||
},
|
||||
},
|
||||
// dropdown
|
||||
{
|
||||
Label: "disabled",
|
||||
TypeClass: "primary",
|
||||
IsDisabled: true,
|
||||
// items
|
||||
},
|
||||
}
|
||||
list := []components.ContentList{
|
||||
// basic list
|
||||
{
|
||||
Items: []components.ContentListItem{
|
||||
{
|
||||
Text: "item 1",
|
||||
},
|
||||
{
|
||||
Text: "item 2",
|
||||
EndElement: "end text",
|
||||
},
|
||||
{
|
||||
Text: "item disabled",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// description list
|
||||
{
|
||||
Items: []components.ContentListItem{
|
||||
{
|
||||
Text: "item 1",
|
||||
Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. ",
|
||||
},
|
||||
{
|
||||
Text: "item 2",
|
||||
Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||
},
|
||||
{
|
||||
Text: "item disabled",
|
||||
Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// list with class
|
||||
{
|
||||
Items: []components.ContentListItem{
|
||||
{
|
||||
Text: "class primary",
|
||||
TypeClass: "primary",
|
||||
},
|
||||
{
|
||||
Text: "class success",
|
||||
TypeClass: "success",
|
||||
},
|
||||
{
|
||||
Text: "class danger",
|
||||
TypeClass: "danger",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
menus := []components.PageNav{
|
||||
// nav
|
||||
{
|
||||
NavClass: "nav-pills",
|
||||
NavItems: []components.PageNavItem{
|
||||
{
|
||||
Text: "item active",
|
||||
Link: "#",
|
||||
IsActive: true,
|
||||
},
|
||||
{
|
||||
Text: "item",
|
||||
Link: "#",
|
||||
IsActive: false,
|
||||
},
|
||||
{
|
||||
Text: "item",
|
||||
Link: "#",
|
||||
IsActive: false,
|
||||
ChildItems: []components.PageNavItem{
|
||||
{
|
||||
Text: "item ",
|
||||
Link: "#",
|
||||
},
|
||||
{
|
||||
Text: "item disabled",
|
||||
Link: "#",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Text: "item disabled",
|
||||
Link: "#",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// nav
|
||||
{
|
||||
NavClass: "",
|
||||
NavItems: []components.PageNavItem{
|
||||
{
|
||||
Text: "item active",
|
||||
Link: "#",
|
||||
IsActive: true,
|
||||
},
|
||||
{
|
||||
Text: "item",
|
||||
Link: "#",
|
||||
IsActive: false,
|
||||
},
|
||||
{
|
||||
Text: "item",
|
||||
Link: "#",
|
||||
IsActive: false,
|
||||
},
|
||||
{
|
||||
Text: "item disabled",
|
||||
Link: "#",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// nav underline
|
||||
{
|
||||
NavClass: "nav-underline",
|
||||
NavItems: []components.PageNavItem{
|
||||
{
|
||||
Text: "item active",
|
||||
Link: "#",
|
||||
IsActive: true,
|
||||
},
|
||||
{
|
||||
Text: "item",
|
||||
Link: "#",
|
||||
IsActive: false,
|
||||
},
|
||||
{
|
||||
Text: "item",
|
||||
Link: "#",
|
||||
IsActive: false,
|
||||
},
|
||||
{
|
||||
Text: "item disabled",
|
||||
Link: "#",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// nav tabs
|
||||
{
|
||||
NavClass: "",
|
||||
IsTab: true,
|
||||
NavItems: []components.PageNavItem{
|
||||
{
|
||||
Text: "tab active",
|
||||
Link: "#",
|
||||
IsActive: true,
|
||||
},
|
||||
{
|
||||
Text: "tab",
|
||||
Link: "#",
|
||||
IsActive: false,
|
||||
},
|
||||
{
|
||||
Text: "tab",
|
||||
Link: "#",
|
||||
IsActive: false,
|
||||
},
|
||||
{
|
||||
Text: "tab disabled",
|
||||
Link: "#",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
// nav vertical
|
||||
{
|
||||
NavClass: "",
|
||||
IsVertical: true,
|
||||
NavItems: []components.PageNavItem{
|
||||
{
|
||||
Text: "item active",
|
||||
Link: "#",
|
||||
IsActive: true,
|
||||
},
|
||||
{
|
||||
Text: "item",
|
||||
Link: "#",
|
||||
IsActive: false,
|
||||
},
|
||||
{
|
||||
Text: "item",
|
||||
Link: "#",
|
||||
IsActive: false,
|
||||
},
|
||||
{
|
||||
Text: "item disabled",
|
||||
Link: "#",
|
||||
IsDisabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tmplData := templateData{
|
||||
Buttons: buttons,
|
||||
Hrefs: hrefs,
|
||||
Badges: badges,
|
||||
Dropdowns: dropdowns,
|
||||
Lists: list,
|
||||
Menus: menus,
|
||||
}
|
||||
return c.Response.Template("custom_theme_elements.html", tmplData)
|
||||
|
||||
} else {
|
||||
|
||||
message := "{\"message\": \"Error, template not enabled\"}"
|
||||
return c.Response.Json(message)
|
||||
}
|
||||
}
|
||||
|
||||
// Show form element page
|
||||
func Themecontent(c *core.Context) *core.Response {
|
||||
|
||||
// check if template engine is enabled
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
if TemplateEnable {
|
||||
|
||||
// first, include all compoments
|
||||
type templateData struct {
|
||||
ContentTable components.ContentTable
|
||||
ContentTabledetail components.ContentTabledetail
|
||||
ContentGraph components.ContentGraph
|
||||
}
|
||||
|
||||
// TABLES
|
||||
// for th head
|
||||
var allTh []components.ContentTableTH
|
||||
var th components.ContentTableTH
|
||||
th.Value = "Column heading 1"
|
||||
allTh = append(allTh, th)
|
||||
th.Value = "Column heading 2"
|
||||
allTh = append(allTh, th)
|
||||
th.ID = "ba"
|
||||
th.Value = "Column heading 3"
|
||||
allTh = append(allTh, th)
|
||||
th.Value = "Column badge"
|
||||
th.ValueType = "badge" // column type badge
|
||||
allTh = append(allTh, th)
|
||||
th.Value = "Column action"
|
||||
th.ValueType = "href" // column type href
|
||||
allTh = append(allTh, th)
|
||||
|
||||
// for td items
|
||||
var allTd [][]components.ContentTableTD
|
||||
//var vals []components.ContentTableTD
|
||||
// rows
|
||||
for i := 1; i <= 10; i++ {
|
||||
vals := make([]components.ContentTableTD, len(allTh))
|
||||
for b := 0; b < len(allTh)-2; b++ {
|
||||
vals[b].Value = fmt.Sprintf("%s%d%d", "TD data: ", i, b)
|
||||
vals[b].ID = fmt.Sprintf("%s%d%d", "idtd_", i, b)
|
||||
}
|
||||
// column badge
|
||||
vals[len(allTh)-2].Value = components.ContentBadge{
|
||||
Text: "success",
|
||||
TypeClass: "success",
|
||||
}
|
||||
// last column href
|
||||
vals[len(allTh)-1].Value = components.ContentHref{
|
||||
Text: "edit",
|
||||
Link: "#",
|
||||
}
|
||||
allTd = append(allTd, vals)
|
||||
}
|
||||
|
||||
// for td items in table detail
|
||||
var allTdetail []components.ContentTabledetailTD
|
||||
// table detail
|
||||
var thd components.ContentTabledetailTD
|
||||
thd.Caption = "Continent"
|
||||
thd.Value = "Asia"
|
||||
allTdetail = append(allTdetail, thd)
|
||||
thd.Caption = "Country"
|
||||
thd.Value = "South Korea"
|
||||
allTdetail = append(allTdetail, thd)
|
||||
thd.Caption = "Capital"
|
||||
thd.Value = "Seoul"
|
||||
allTdetail = append(allTdetail, thd)
|
||||
thd.Caption = "Details"
|
||||
thd.ValueType = "href" // column type href
|
||||
thd.Value = components.ContentHref{
|
||||
Text: "edit",
|
||||
Link: "#",
|
||||
}
|
||||
allTdetail = append(allTdetail, thd)
|
||||
thd.Caption = "Notifications"
|
||||
thd.ValueType = "badge" // column type href
|
||||
thd.Value = components.ContentBadge{
|
||||
Text: "success",
|
||||
TypeClass: "success",
|
||||
}
|
||||
allTdetail = append(allTdetail, thd)
|
||||
|
||||
// random values for pie
|
||||
one := rand.IntN(50)
|
||||
two := rand.IntN(50)
|
||||
three := rand.IntN(50)
|
||||
valuesgraph := fmt.Sprintf("%d|%d|%d", one, two, three)
|
||||
|
||||
// now fill data of the components
|
||||
tmplData := templateData{
|
||||
ContentTable: components.ContentTable{
|
||||
ID: "table_demo",
|
||||
AllTH: allTh,
|
||||
AllTD: allTd,
|
||||
},
|
||||
ContentTabledetail: components.ContentTabledetail{
|
||||
ID: "table_demodetail",
|
||||
Title: "Sample table detail",
|
||||
HeadClass: "table-warning",
|
||||
AllTD: allTdetail,
|
||||
},
|
||||
ContentGraph: components.ContentGraph{
|
||||
Graph: "pie",
|
||||
Labels: "Berlin|Paris|Venecia",
|
||||
Values: valuesgraph,
|
||||
},
|
||||
}
|
||||
|
||||
return c.Response.Template("custom_theme_contentpage.html", tmplData)
|
||||
|
||||
} else {
|
||||
|
||||
message := "{\"message\": \"Error, template not enabled\"}"
|
||||
return c.Response.Json(message)
|
||||
|
||||
}
|
||||
|
||||
}
|
48
go.mod
48
go.mod
|
@ -10,7 +10,7 @@ replace (
|
|||
go 1.23.1
|
||||
|
||||
require (
|
||||
git.smarteching.com/goffee/core v1.8.4
|
||||
git.smarteching.com/goffee/core v1.7.3
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
|
@ -18,41 +18,33 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
git.smarteching.com/zeni/go-chart/v2 v2.1.4 // indirect
|
||||
github.com/SparkPost/gosparkpost v0.2.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/go-chi/chi/v5 v5.1.0 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.8 // indirect
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect
|
||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
|
||||
github.com/harranali/mailing v1.2.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/pgx/v5 v5.7.1 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
github.com/jackc/pgx/v5 v5.3.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailgun/errors v0.4.0 // indirect
|
||||
github.com/mailgun/mailgun-go/v4 v4.17.3 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/json-iterator/go v1.1.10 // indirect
|
||||
github.com/mailgun/mailgun-go/v4 v4.10.0 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.7.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.0.5 // indirect
|
||||
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||
github.com/sendgrid/sendgrid-go v3.16.0+incompatible // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/image v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
gorm.io/driver/mysql v1.5.7 // indirect
|
||||
gorm.io/driver/postgres v1.5.9 // indirect
|
||||
gorm.io/driver/sqlite v1.5.6 // indirect
|
||||
github.com/sendgrid/sendgrid-go v3.12.0+incompatible // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gorm.io/driver/mysql v1.5.1 // indirect
|
||||
gorm.io/driver/postgres v1.5.2 // indirect
|
||||
gorm.io/driver/sqlite v1.5.2 // indirect
|
||||
)
|
||||
|
|
111
go.sum
111
go.sum
|
@ -1,23 +1,19 @@
|
|||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
git.smarteching.com/goffee/core v1.8.4 h1:XB+vpe7e8muiDChRVDaJ1TG7H+/FBxDQcMfWp4zloPs=
|
||||
git.smarteching.com/goffee/core v1.8.4/go.mod h1:JxXDvTQU2shKYY6c9aS3s6sFh7mEDzgmjzdc85HhBV8=
|
||||
git.smarteching.com/zeni/go-chart/v2 v2.1.4 h1:pF06+F6eqJLIG8uMiTVPR5TygPGMjM/FHMzTxmu5V/Q=
|
||||
git.smarteching.com/zeni/go-chart/v2 v2.1.4/go.mod h1:b3ueW9h3pGGXyhkormZAvilHaG4+mQti+bMNPdQBeOQ=
|
||||
git.smarteching.com/goffee/core v1.7.3 h1:GlZ7B/QwAQ6eSQcYBtqlglZoqA7tFYiXqvV2z27xuQY=
|
||||
git.smarteching.com/goffee/core v1.7.3/go.mod h1:QQNIHVN6qjJBtq42WCQMrLYN9oFE3wm26SLU8ZxNTec=
|
||||
github.com/SparkPost/gosparkpost v0.2.0 h1:yzhHQT7cE+rqzd5tANNC74j+2x3lrPznqPJrxC1yR8s=
|
||||
github.com/SparkPost/gosparkpost v0.2.0/go.mod h1:S9WKcGeou7cbPpx0kTIgo8Q69WZvUmVeVzbD+djalJ4=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/brianvoe/gofakeit/v6 v6.21.0 h1:tNkm9yxEbpuPK8Bx39tT4sSc5i9SUGiciLdNix+VDQY=
|
||||
github.com/brianvoe/gofakeit/v6 v6.21.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
|
||||
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
|
||||
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
|
||||
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buger/jsonparser v1.0.0/go.mod h1:tgcrVJ81GPSF0mz+0nu1Xaz0fazGPrmmJfJtxjbHhUQ=
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -29,19 +25,16 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojt
|
|||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
|
||||
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE=
|
||||
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
|
||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
@ -49,12 +42,10 @@ github.com/harranali/mailing v1.2.0 h1:ihIyJwB8hyRVcdk+v465wk1PHMrSrgJqo/kMd+gZC
|
|||
github.com/harranali/mailing v1.2.0/go.mod h1:4a5N3yG98pZKluMpmcYlTtll7bisvOfGQEMIng3VQk4=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
|
||||
github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.3.1 h1:Fcr8QJ1ZeLi5zsPZqQeUZhNhxfkkKBOgJuYkJHoBOtU=
|
||||
github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
|
||||
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||
github.com/jhillyerd/enmime v0.8.0/go.mod h1:MBHs3ugk03NGjMM6PuRynlKf+HA5eSillZ+TRCm73AE=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
|
@ -63,74 +54,62 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
|||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailgun/errors v0.4.0 h1:6LFBvod6VIW83CMIOT9sYNp28TCX0NejFPP4dSX++i8=
|
||||
github.com/mailgun/errors v0.4.0/go.mod h1:xGBaaKdEdQT0/FhwvoXv4oBaqqmVZz9P1XEnvD/onc0=
|
||||
github.com/mailgun/mailgun-go/v4 v4.17.3 h1:WoO48/VeXgAVSzjgzyeLvF08AoPzWU2EBz79INN8rEA=
|
||||
github.com/mailgun/mailgun-go/v4 v4.17.3/go.mod h1:0ood70bQR/SffQ9NxIsAY06H+HG0hrvMVApfUp9TihI=
|
||||
github.com/mailgun/mailgun-go/v4 v4.10.0 h1:e5LVsxpqjOYRyaOWifrJORoLQZTYDP+g4ljfmf9G2zE=
|
||||
github.com/mailgun/mailgun-go/v4 v4.10.0/go.mod h1:L9s941Lgk7iB3TgywTPz074pK2Ekkg4kgbnAaAyJ2z8=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o=
|
||||
github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
|
||||
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
|
||||
github.com/sendgrid/sendgrid-go v3.16.0+incompatible h1:i8eE6IMkiCy7vusSdacHHSBUpXyTcTXy/Rl9N9aZ/Qw=
|
||||
github.com/sendgrid/sendgrid-go v3.16.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sendgrid/sendgrid-go v3.12.0+incompatible h1:/N2vx18Fg1KmQOh6zESc5FJB8pYwt5QFBDflYPh1KVg=
|
||||
github.com/sendgrid/sendgrid-go v3.12.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
|
||||
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
|
||||
gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
|
||||
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
||||
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
|
||||
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
|
||||
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
|
||||
gorm.io/driver/postgres v1.5.2 h1:ytTDxxEv+MplXOfFe3Lzm7SjG09fcdb3Z/c056DTBx0=
|
||||
gorm.io/driver/postgres v1.5.2/go.mod h1:fmpX0m2I1PKuR7mKZiEluwrP3hbs+ps7JIGMUBpCgl8=
|
||||
gorm.io/driver/sqlite v1.5.2 h1:TpQ+/dqCY4uCigCFyrfnrJnrW9zjpelWVoEVNy5qJkc=
|
||||
gorm.io/driver/sqlite v1.5.2/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
|
|
|
@ -3,8 +3,6 @@ package hooks
|
|||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
|
@ -13,92 +11,20 @@ import (
|
|||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var CheckSessionCookie core.Hook = func(c *core.Context) {
|
||||
|
||||
pass := true
|
||||
token := ""
|
||||
usercookie, err := c.GetCookie()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
token = usercookie.Token
|
||||
if token == "" {
|
||||
pass = false
|
||||
} else {
|
||||
payload, err := c.GetJWT().DecodeToken(token)
|
||||
if err != nil {
|
||||
pass = false
|
||||
} else {
|
||||
|
||||
userAgent := c.GetUserAgent()
|
||||
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent)
|
||||
|
||||
cachedToken, err := c.GetCache().Get(hashedCacheKey)
|
||||
if err != nil {
|
||||
pass = false
|
||||
} else if cachedToken != token {
|
||||
pass = false
|
||||
} else {
|
||||
var user models.User
|
||||
res := c.GetGorm().Where("id = ?", payload["userID"]).First(&user)
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
pass = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if have session redirect protected page
|
||||
if pass {
|
||||
c.Response.Redirect("/appsample").ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
|
||||
}
|
||||
|
||||
var AuthCheck core.Hook = func(c *core.Context) {
|
||||
|
||||
// check if template engine is enable
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
token := ""
|
||||
|
||||
if TemplateEnable {
|
||||
usercookie, err := c.GetCookie()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
token = usercookie.Token
|
||||
if token == "" {
|
||||
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
tokenRaw := c.GetHeader("Authorization")
|
||||
token = strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
|
||||
token := strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
|
||||
if token == "" {
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
payload, err := c.GetJWT().DecodeToken(token)
|
||||
if err != nil {
|
||||
if TemplateEnable {
|
||||
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||
} else {
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
}
|
||||
return
|
||||
}
|
||||
userAgent := c.GetUserAgent()
|
||||
|
@ -107,24 +33,16 @@ var AuthCheck core.Hook = func(c *core.Context) {
|
|||
cachedToken, err := c.GetCache().Get(hashedCacheKey)
|
||||
if err != nil {
|
||||
// user signed out
|
||||
if TemplateEnable {
|
||||
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||
} else {
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
}
|
||||
return
|
||||
}
|
||||
if cachedToken != token {
|
||||
// using old token replaced with new one after recent signin
|
||||
if TemplateEnable {
|
||||
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||
} else {
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -133,13 +51,17 @@ var AuthCheck core.Hook = func(c *core.Context) {
|
|||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
// error with the database
|
||||
c.GetLogger().Error(res.Error.Error())
|
||||
if TemplateEnable {
|
||||
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||
} else {
|
||||
c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "internal error",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
// user record is not found (deleted)
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
15
main.go
15
main.go
|
@ -19,32 +19,21 @@ import (
|
|||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
//go:embed all:storage/templates
|
||||
//go:embed all:templates
|
||||
var resources embed.FS
|
||||
|
||||
// The main function
|
||||
func main() {
|
||||
app := core.New()
|
||||
basePath, err := os.Getwd()
|
||||
runMode := "dev"
|
||||
if len(os.Args) > 1 {
|
||||
if os.Args[1] == "prod" || os.Args[1] == "dev" {
|
||||
runMode = os.Args[1]
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal("error getting current working dir")
|
||||
}
|
||||
app.SetBasePath(basePath)
|
||||
app.SetRunMode(runMode)
|
||||
app.MakeDirs("logs", "storage", "storage/sqlite", "tls")
|
||||
// Handle the reading of the .env file
|
||||
if config.GetEnvFileConfig().UseDotEnvFile {
|
||||
envfile := ".env-dev"
|
||||
if runMode == "prod" {
|
||||
envfile = ".env"
|
||||
}
|
||||
envVars, err := godotenv.Read(envfile)
|
||||
envVars, err := godotenv.Read(".env")
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
|
|
30
routes.go
30
routes.go
|
@ -20,31 +20,11 @@ func registerRoutes() {
|
|||
|
||||
// Define your routes here...
|
||||
controller.Get("/", controllers.WelcomeHome)
|
||||
// Uncomment the lines below to enable theme demo
|
||||
controller.Get("/themebase", controllers.Themedemo)
|
||||
controller.Get("/themeform", controllers.Themeform)
|
||||
controller.Get("/themecontent", controllers.Themecontent)
|
||||
controller.Get("/themepanel", controllers.Themedemo)
|
||||
controller.Get("/themeelements", controllers.ThemeElements)
|
||||
|
||||
// Uncomment the lines below to enable authentication
|
||||
controller.Post("/signup", controllers.Signup)
|
||||
controller.Post("/signin", controllers.Signin)
|
||||
controller.Post("/signout", controllers.Signout)
|
||||
controller.Post("/reset-password", controllers.ResetPasswordRequest)
|
||||
controller.Post("/reset-password/code/:code", controllers.SetNewPassword)
|
||||
|
||||
// controller.Post("/signup", controllers.Signup)
|
||||
// controller.Post("/signin", controllers.Signin)
|
||||
// controller.Post("/signout", controllers.Signout)
|
||||
// controller.Post("/reset-password", controllers.ResetPasswordRequest)
|
||||
// controller.Post("/reset-password/code/:code", controllers.SetNewPassword)
|
||||
controller.Get("/dashboard", controllers.WelcomeToDashboard, hooks.AuthCheck)
|
||||
|
||||
// templates demos
|
||||
controller.Get("/signout", controllers.Signout)
|
||||
|
||||
controller.Get("/appsample", controllers.AppSample, hooks.AuthCheck)
|
||||
controller.Post("/appsample", controllers.AppSample, hooks.AuthCheck)
|
||||
|
||||
controller.Get("/applogin", controllers.AppLogin, hooks.CheckSessionCookie)
|
||||
controller.Post("/applogin", controllers.AppLogin, hooks.CheckSessionCookie)
|
||||
|
||||
controller.Get("/appsession", controllers.AppSession)
|
||||
controller.Post("/appsession", controllers.AppSession)
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
console.log("Start Goffee app");
|
||||
|
||||
let elem = document.querySelector('#signout');
|
||||
if (elem) {
|
||||
document.getElementById("signout").onclick = (_event) => {
|
||||
fetch('/signout').then(response => response.json())
|
||||
.then(data => {
|
||||
if (data['message'] == "signed out successfully") {
|
||||
// Refresh the page
|
||||
location.reload();
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
4085
storage/public/bootstrap/css/bootstrap-grid.css
vendored
4085
storage/public/bootstrap/css/bootstrap-grid.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4084
storage/public/bootstrap/css/bootstrap-grid.rtl.css
vendored
4084
storage/public/bootstrap/css/bootstrap-grid.rtl.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
597
storage/public/bootstrap/css/bootstrap-reboot.css
vendored
597
storage/public/bootstrap/css/bootstrap-reboot.css
vendored
|
@ -1,597 +0,0 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2024 The Bootstrap Authors
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
:root,
|
||||
[data-bs-theme=light] {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-primary-text-emphasis: #052c65;
|
||||
--bs-secondary-text-emphasis: #2b2f32;
|
||||
--bs-success-text-emphasis: #0a3622;
|
||||
--bs-info-text-emphasis: #055160;
|
||||
--bs-warning-text-emphasis: #664d03;
|
||||
--bs-danger-text-emphasis: #58151c;
|
||||
--bs-light-text-emphasis: #495057;
|
||||
--bs-dark-text-emphasis: #495057;
|
||||
--bs-primary-bg-subtle: #cfe2ff;
|
||||
--bs-secondary-bg-subtle: #e2e3e5;
|
||||
--bs-success-bg-subtle: #d1e7dd;
|
||||
--bs-info-bg-subtle: #cff4fc;
|
||||
--bs-warning-bg-subtle: #fff3cd;
|
||||
--bs-danger-bg-subtle: #f8d7da;
|
||||
--bs-light-bg-subtle: #fcfcfd;
|
||||
--bs-dark-bg-subtle: #ced4da;
|
||||
--bs-primary-border-subtle: #9ec5fe;
|
||||
--bs-secondary-border-subtle: #c4c8cb;
|
||||
--bs-success-border-subtle: #a3cfbb;
|
||||
--bs-info-border-subtle: #9eeaf9;
|
||||
--bs-warning-border-subtle: #ffe69c;
|
||||
--bs-danger-border-subtle: #f1aeb5;
|
||||
--bs-light-border-subtle: #e9ecef;
|
||||
--bs-dark-border-subtle: #adb5bd;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg: #fff;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-emphasis-color: #000;
|
||||
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||
--bs-secondary-color-rgb: 33, 37, 41;
|
||||
--bs-secondary-bg: #e9ecef;
|
||||
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||
--bs-tertiary-bg: #f8f9fa;
|
||||
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #0d6efd;
|
||||
--bs-link-color-rgb: 13, 110, 253;
|
||||
--bs-link-decoration: underline;
|
||||
--bs-link-hover-color: #0a58ca;
|
||||
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||
--bs-code-color: #d63384;
|
||||
--bs-highlight-color: #212529;
|
||||
--bs-highlight-bg: #fff3cd;
|
||||
--bs-border-width: 1px;
|
||||
--bs-border-style: solid;
|
||||
--bs-border-color: #dee2e6;
|
||||
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||
--bs-border-radius: 0.375rem;
|
||||
--bs-border-radius-sm: 0.25rem;
|
||||
--bs-border-radius-lg: 0.5rem;
|
||||
--bs-border-radius-xl: 1rem;
|
||||
--bs-border-radius-xxl: 2rem;
|
||||
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||
--bs-border-radius-pill: 50rem;
|
||||
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
--bs-focus-ring-width: 0.25rem;
|
||||
--bs-focus-ring-opacity: 0.25;
|
||||
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||
--bs-form-valid-color: #198754;
|
||||
--bs-form-valid-border-color: #198754;
|
||||
--bs-form-invalid-color: #dc3545;
|
||||
--bs-form-invalid-border-color: #dc3545;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-body-color: #dee2e6;
|
||||
--bs-body-color-rgb: 222, 226, 230;
|
||||
--bs-body-bg: #212529;
|
||||
--bs-body-bg-rgb: 33, 37, 41;
|
||||
--bs-emphasis-color: #fff;
|
||||
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||
--bs-secondary-color: rgba(222, 226, 230, 0.75);
|
||||
--bs-secondary-color-rgb: 222, 226, 230;
|
||||
--bs-secondary-bg: #343a40;
|
||||
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
|
||||
--bs-tertiary-color-rgb: 222, 226, 230;
|
||||
--bs-tertiary-bg: #2b3035;
|
||||
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||
--bs-primary-text-emphasis: #6ea8fe;
|
||||
--bs-secondary-text-emphasis: #a7acb1;
|
||||
--bs-success-text-emphasis: #75b798;
|
||||
--bs-info-text-emphasis: #6edff6;
|
||||
--bs-warning-text-emphasis: #ffda6a;
|
||||
--bs-danger-text-emphasis: #ea868f;
|
||||
--bs-light-text-emphasis: #f8f9fa;
|
||||
--bs-dark-text-emphasis: #dee2e6;
|
||||
--bs-primary-bg-subtle: #031633;
|
||||
--bs-secondary-bg-subtle: #161719;
|
||||
--bs-success-bg-subtle: #051b11;
|
||||
--bs-info-bg-subtle: #032830;
|
||||
--bs-warning-bg-subtle: #332701;
|
||||
--bs-danger-bg-subtle: #2c0b0e;
|
||||
--bs-light-bg-subtle: #343a40;
|
||||
--bs-dark-bg-subtle: #1a1d20;
|
||||
--bs-primary-border-subtle: #084298;
|
||||
--bs-secondary-border-subtle: #41464b;
|
||||
--bs-success-border-subtle: #0f5132;
|
||||
--bs-info-border-subtle: #087990;
|
||||
--bs-warning-border-subtle: #997404;
|
||||
--bs-danger-border-subtle: #842029;
|
||||
--bs-light-border-subtle: #495057;
|
||||
--bs-dark-border-subtle: #343a40;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #6ea8fe;
|
||||
--bs-link-hover-color: #8bb9fe;
|
||||
--bs-link-color-rgb: 110, 168, 254;
|
||||
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||
--bs-code-color: #e685b5;
|
||||
--bs-highlight-color: #dee2e6;
|
||||
--bs-highlight-bg: #664d03;
|
||||
--bs-border-color: #495057;
|
||||
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||
--bs-form-valid-color: #75b798;
|
||||
--bs-form-valid-border-color: #75b798;
|
||||
--bs-form-invalid-color: #ea868f;
|
||||
--bs-form-invalid-border-color: #ea868f;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
border: 0;
|
||||
border-top: var(--bs-border-width) solid;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: var(--bs-heading-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.1875em;
|
||||
color: var(--bs-highlight-color);
|
||||
background-color: var(--bs-highlight-bg);
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-code-color);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.1875rem 0.375rem;
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-body-bg);
|
||||
background-color: var(--bs-body-color);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: var(--bs-secondary-color);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* rtl:raw:
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
*/
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,594 +0,0 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2024 The Bootstrap Authors
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
:root,
|
||||
[data-bs-theme=light] {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-primary-text-emphasis: #052c65;
|
||||
--bs-secondary-text-emphasis: #2b2f32;
|
||||
--bs-success-text-emphasis: #0a3622;
|
||||
--bs-info-text-emphasis: #055160;
|
||||
--bs-warning-text-emphasis: #664d03;
|
||||
--bs-danger-text-emphasis: #58151c;
|
||||
--bs-light-text-emphasis: #495057;
|
||||
--bs-dark-text-emphasis: #495057;
|
||||
--bs-primary-bg-subtle: #cfe2ff;
|
||||
--bs-secondary-bg-subtle: #e2e3e5;
|
||||
--bs-success-bg-subtle: #d1e7dd;
|
||||
--bs-info-bg-subtle: #cff4fc;
|
||||
--bs-warning-bg-subtle: #fff3cd;
|
||||
--bs-danger-bg-subtle: #f8d7da;
|
||||
--bs-light-bg-subtle: #fcfcfd;
|
||||
--bs-dark-bg-subtle: #ced4da;
|
||||
--bs-primary-border-subtle: #9ec5fe;
|
||||
--bs-secondary-border-subtle: #c4c8cb;
|
||||
--bs-success-border-subtle: #a3cfbb;
|
||||
--bs-info-border-subtle: #9eeaf9;
|
||||
--bs-warning-border-subtle: #ffe69c;
|
||||
--bs-danger-border-subtle: #f1aeb5;
|
||||
--bs-light-border-subtle: #e9ecef;
|
||||
--bs-dark-border-subtle: #adb5bd;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg: #fff;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-emphasis-color: #000;
|
||||
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||
--bs-secondary-color-rgb: 33, 37, 41;
|
||||
--bs-secondary-bg: #e9ecef;
|
||||
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||
--bs-tertiary-bg: #f8f9fa;
|
||||
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #0d6efd;
|
||||
--bs-link-color-rgb: 13, 110, 253;
|
||||
--bs-link-decoration: underline;
|
||||
--bs-link-hover-color: #0a58ca;
|
||||
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||
--bs-code-color: #d63384;
|
||||
--bs-highlight-color: #212529;
|
||||
--bs-highlight-bg: #fff3cd;
|
||||
--bs-border-width: 1px;
|
||||
--bs-border-style: solid;
|
||||
--bs-border-color: #dee2e6;
|
||||
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||
--bs-border-radius: 0.375rem;
|
||||
--bs-border-radius-sm: 0.25rem;
|
||||
--bs-border-radius-lg: 0.5rem;
|
||||
--bs-border-radius-xl: 1rem;
|
||||
--bs-border-radius-xxl: 2rem;
|
||||
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||
--bs-border-radius-pill: 50rem;
|
||||
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
--bs-focus-ring-width: 0.25rem;
|
||||
--bs-focus-ring-opacity: 0.25;
|
||||
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||
--bs-form-valid-color: #198754;
|
||||
--bs-form-valid-border-color: #198754;
|
||||
--bs-form-invalid-color: #dc3545;
|
||||
--bs-form-invalid-border-color: #dc3545;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-body-color: #dee2e6;
|
||||
--bs-body-color-rgb: 222, 226, 230;
|
||||
--bs-body-bg: #212529;
|
||||
--bs-body-bg-rgb: 33, 37, 41;
|
||||
--bs-emphasis-color: #fff;
|
||||
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||
--bs-secondary-color: rgba(222, 226, 230, 0.75);
|
||||
--bs-secondary-color-rgb: 222, 226, 230;
|
||||
--bs-secondary-bg: #343a40;
|
||||
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
|
||||
--bs-tertiary-color-rgb: 222, 226, 230;
|
||||
--bs-tertiary-bg: #2b3035;
|
||||
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||
--bs-primary-text-emphasis: #6ea8fe;
|
||||
--bs-secondary-text-emphasis: #a7acb1;
|
||||
--bs-success-text-emphasis: #75b798;
|
||||
--bs-info-text-emphasis: #6edff6;
|
||||
--bs-warning-text-emphasis: #ffda6a;
|
||||
--bs-danger-text-emphasis: #ea868f;
|
||||
--bs-light-text-emphasis: #f8f9fa;
|
||||
--bs-dark-text-emphasis: #dee2e6;
|
||||
--bs-primary-bg-subtle: #031633;
|
||||
--bs-secondary-bg-subtle: #161719;
|
||||
--bs-success-bg-subtle: #051b11;
|
||||
--bs-info-bg-subtle: #032830;
|
||||
--bs-warning-bg-subtle: #332701;
|
||||
--bs-danger-bg-subtle: #2c0b0e;
|
||||
--bs-light-bg-subtle: #343a40;
|
||||
--bs-dark-bg-subtle: #1a1d20;
|
||||
--bs-primary-border-subtle: #084298;
|
||||
--bs-secondary-border-subtle: #41464b;
|
||||
--bs-success-border-subtle: #0f5132;
|
||||
--bs-info-border-subtle: #087990;
|
||||
--bs-warning-border-subtle: #997404;
|
||||
--bs-danger-border-subtle: #842029;
|
||||
--bs-light-border-subtle: #495057;
|
||||
--bs-dark-border-subtle: #343a40;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #6ea8fe;
|
||||
--bs-link-hover-color: #8bb9fe;
|
||||
--bs-link-color-rgb: 110, 168, 254;
|
||||
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||
--bs-code-color: #e685b5;
|
||||
--bs-highlight-color: #dee2e6;
|
||||
--bs-highlight-bg: #664d03;
|
||||
--bs-border-color: #495057;
|
||||
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||
--bs-form-valid-color: #75b798;
|
||||
--bs-form-valid-border-color: #75b798;
|
||||
--bs-form-invalid-color: #ea868f;
|
||||
--bs-form-invalid-border-color: #ea868f;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
border: 0;
|
||||
border-top: var(--bs-border-width) solid;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: var(--bs-heading-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.1875em;
|
||||
color: var(--bs-highlight-color);
|
||||
background-color: var(--bs-highlight-bg);
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-code-color);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.1875rem 0.375rem;
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-body-bg);
|
||||
background-color: var(--bs-body-color);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: var(--bs-secondary-color);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: right;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: right;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
5402
storage/public/bootstrap/css/bootstrap-utilities.css
vendored
5402
storage/public/bootstrap/css/bootstrap-utilities.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
5393
storage/public/bootstrap/css/bootstrap-utilities.rtl.css
vendored
5393
storage/public/bootstrap/css/bootstrap-utilities.rtl.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
12057
storage/public/bootstrap/css/bootstrap.css
vendored
12057
storage/public/bootstrap/css/bootstrap.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
12030
storage/public/bootstrap/css/bootstrap.rtl.css
vendored
12030
storage/public/bootstrap/css/bootstrap.rtl.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6314
storage/public/bootstrap/js/bootstrap.bundle.js
vendored
6314
storage/public/bootstrap/js/bootstrap.bundle.js
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4447
storage/public/bootstrap/js/bootstrap.esm.js
vendored
4447
storage/public/bootstrap/js/bootstrap.esm.js
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4494
storage/public/bootstrap/js/bootstrap.js
vendored
4494
storage/public/bootstrap/js/bootstrap.js
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
7
storage/public/bootstrap/js/bootstrap.min.js
vendored
7
storage/public/bootstrap/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6
storage/public/bootstrap/js/popper.min.js
vendored
6
storage/public/bootstrap/js/popper.min.js
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
## styles
|
||||
|
||||
body {
|
||||
background-color: #f2f2f2;
|
||||
font-family: "Helvetica Neue";
|
||||
}
|
||||
|
||||
.goffeelogo {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
BIN
storage/sqlite.db
Normal file
BIN
storage/sqlite.db
Normal file
Binary file not shown.
Binary file not shown.
|
@ -1,17 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Sample page"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{{template "content_dropdown" .ContentDropdown}}
|
||||
{{template "page_card" .PageCard}}
|
||||
{{ define "page_card_content" }}
|
||||
|
||||
<img class="goffeelogo"src="/public/img/goffee.png" alt="Goffee logo" />
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,12 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Sample page test session vars"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{{template "page_card" .PageCard}}
|
||||
</div>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,10 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Goffee"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
{{template "page_card" .PageCard}}
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,24 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Goffee"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<fieldset>
|
||||
<legend>Content demos</legend>
|
||||
<div class="row">
|
||||
{{template "content_table" .ContentTable}}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<h2>Pie chart</h2>
|
||||
{{template "content_graph" .ContentGraph}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
{{template "content_tabledetail" .ContentTabledetail}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,66 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Goffee"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos buttons</legend>
|
||||
<div class="container border rounded-3 p-2">
|
||||
{{range .Buttons}}
|
||||
{{template "form_button" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos href</legend>
|
||||
<div class="container d-flex justify-content-between border rounded-3 p-2">
|
||||
{{range .Hrefs}}
|
||||
{{template "content_href" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos Badges</legend>
|
||||
<div class="container d-flex justify-content-between border rounded-3 p-2">
|
||||
{{range .Badges}}
|
||||
{{template "content_badge" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos dropdown</legend>
|
||||
<div class="container d-flex justify-content-between border rounded-3 p-2">
|
||||
{{range .Dropdowns}}
|
||||
{{template "content_dropdown" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos List</legend>
|
||||
<div class="container d-flex justify-content-between border rounded-3 p-2">
|
||||
{{range .Lists}}
|
||||
{{template "content_list" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos nav</legend>
|
||||
<div class="container border rounded-3 p-2 ">
|
||||
{{range .Menus}}
|
||||
<div class="container border rounded-3 p-2 mb-2">
|
||||
{{template "page_nav" .}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,23 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Goffee"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>form demos</legend>
|
||||
<div class="row">
|
||||
{{template "form_input" .FormText}}
|
||||
{{template "form_input" .FormEmail}}
|
||||
{{template "form_select" .FormSelectCity}}
|
||||
{{template "form_textarea" .FormTextarea}}
|
||||
{{template "form_radio" .FormRadio}}
|
||||
{{template "form_checkbox" .FormCheckbox}}
|
||||
</div>
|
||||
</fieldset>
|
||||
{{template "form_button" .FormButton}}
|
||||
</form>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,24 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Goffee"}}
|
||||
<body>
|
||||
<main>
|
||||
|
||||
<form method="POST" action="/signin">
|
||||
<div>
|
||||
<label for="email">Email:</label>
|
||||
<input type="text" id="email" name="email" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" name="login">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</main>
|
||||
<script src="/public/app.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,15 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Sample page"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{{template "page_card" .PageCard}}
|
||||
{{ define "page_card_content" }}
|
||||
<img class="goffeelogo"src="/public/img/goffee.png" alt="Goffee logo" />
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
10
templates/sample.html
Normal file
10
templates/sample.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "head" "Sample"}}
|
||||
<body>
|
||||
{{template "title" .TheTitle}}
|
||||
<div class="section">
|
||||
Sample page!
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
170
utils/session.go
170
utils/session.go
|
@ -1,170 +0,0 @@
|
|||
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/cup/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SessionUser struct {
|
||||
mu sync.RWMutex
|
||||
context *core.Context
|
||||
userID uint
|
||||
hashedSessionKey string
|
||||
authenticated bool
|
||||
sessionStart time.Time
|
||||
values map[string]interface{}
|
||||
}
|
||||
|
||||
// start the struct
|
||||
func (s *SessionUser) Init(c *core.Context) bool {
|
||||
|
||||
// check session cookie
|
||||
pass := true
|
||||
token := ""
|
||||
s.context = c
|
||||
|
||||
payload := make(map[string]interface{})
|
||||
// get cookie
|
||||
usercookie, err := c.GetCookie()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
|
||||
token = usercookie.Token
|
||||
|
||||
if token == "" {
|
||||
|
||||
pass = false
|
||||
|
||||
} else {
|
||||
|
||||
payload, err = c.GetJWT().DecodeToken(token)
|
||||
|
||||
if err != nil {
|
||||
|
||||
pass = false
|
||||
|
||||
} else {
|
||||
|
||||
userID := uint(c.CastToInt(payload["userID"]))
|
||||
userAgent := c.GetUserAgent()
|
||||
|
||||
// get data from redis
|
||||
hashedCacheKey := CreateAuthTokenHashedCacheKey(userID, userAgent)
|
||||
cachedToken, err := c.GetCache().Get(hashedCacheKey)
|
||||
|
||||
if err != nil {
|
||||
pass = false
|
||||
} else if cachedToken != token {
|
||||
pass = false
|
||||
} else {
|
||||
var user models.User
|
||||
res := c.GetGorm().Where("id = ?", userID).First(&user)
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
pass = false
|
||||
}
|
||||
// if have session start the struct
|
||||
if pass {
|
||||
userAgent := c.GetUserAgent()
|
||||
sessionKey := fmt.Sprintf("sess_%v", userAgent)
|
||||
s.hashedSessionKey = CreateAuthTokenHashedCacheKey(userID, sessionKey)
|
||||
|
||||
s.values = make(map[string]interface{})
|
||||
s.authenticated = true
|
||||
s.userID = userID
|
||||
value, _ := c.GetCache().Get(s.hashedSessionKey)
|
||||
|
||||
if len(value) > 0 {
|
||||
_ = json.Unmarshal([]byte(value), &s.values)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
} else {
|
||||
|
||||
s.hashedSessionKey = ""
|
||||
s.authenticated = false
|
||||
s.userID = 0
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *SessionUser) Set(key string, value interface{}) error {
|
||||
s.mu.Lock()
|
||||
s.values[key] = value
|
||||
s.mu.Unlock()
|
||||
return s.Save()
|
||||
}
|
||||
|
||||
func (s *SessionUser) Get(key string) (interface{}, bool) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
val, ok := s.values[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
func (s *SessionUser) Delete(key string) interface{} {
|
||||
s.mu.RLock()
|
||||
v, ok := s.values[key]
|
||||
s.mu.RUnlock()
|
||||
if ok {
|
||||
s.mu.Lock()
|
||||
delete(s.values, key)
|
||||
s.mu.Unlock()
|
||||
}
|
||||
s.Save()
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *SessionUser) Flush() error {
|
||||
s.mu.Lock()
|
||||
s.context.GetCache().Delete(s.hashedSessionKey)
|
||||
s.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SessionUser) Save() error {
|
||||
|
||||
var value string
|
||||
|
||||
s.mu.RLock()
|
||||
|
||||
if len(s.values) > 0 {
|
||||
buf, err := json.Marshal(&s.values)
|
||||
if err != nil {
|
||||
s.mu.RUnlock()
|
||||
return err
|
||||
}
|
||||
value = string(buf)
|
||||
}
|
||||
|
||||
if len(value) > 0 {
|
||||
s.context.GetCache().Set(s.hashedSessionKey, value)
|
||||
} else {
|
||||
s.context.GetCache().Delete(s.hashedSessionKey)
|
||||
}
|
||||
s.mu.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SessionUser) GetUserID() uint {
|
||||
|
||||
return s.userID
|
||||
}
|
Loading…
Reference in a new issue