forked from goffee/cup
Merge pull request 'develop' (#4) from develop into main
Reviewed-on: goffee/cup#4
This commit is contained in:
commit
0e2803df9e
67 changed files with 60792 additions and 70 deletions
|
@ -18,8 +18,7 @@ App_KEY_FILE_PATH=tls/server.key
|
||||||
###### TEMPLATES ######
|
###### TEMPLATES ######
|
||||||
#######################################
|
#######################################
|
||||||
TEMPLATE_ENABLE=true
|
TEMPLATE_ENABLE=true
|
||||||
TEMPLATE_PUBLIC=storage/public
|
COOKIE_SECRET=13d6b4dff8f84a10851021ec8608f814570d562c92fe6b5ec4c9f595bcb3234b
|
||||||
TEMPLATE_COMPONENTS=true
|
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
###### JWT ######
|
###### JWT ######
|
||||||
|
@ -46,7 +45,7 @@ POSTGRES_PASSWORD=secret
|
||||||
POSTGRES_DB_NAME=db_test
|
POSTGRES_DB_NAME=db_test
|
||||||
POSTGRES_PORT=5432
|
POSTGRES_PORT=5432
|
||||||
POSTGRES_SSL_MODE=disable
|
POSTGRES_SSL_MODE=disable
|
||||||
POSTGRES_TIMEZONE=Asia/Dubai
|
POSTGRES_TIMEZONE=America/Argentina/Buenos_Aires
|
||||||
|
|
||||||
#_____ SQLITE _____#
|
#_____ SQLITE _____#
|
||||||
SQLITE_DB_PATH=storage/sqlite.db
|
SQLITE_DB_PATH=storage/sqlite.db
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,4 +6,3 @@ logs/*
|
||||||
tls/*
|
tls/*
|
||||||
!tls/.gitkeep
|
!tls/.gitkeep
|
||||||
.DS_Store
|
.DS_Store
|
||||||
storage/public/*
|
|
||||||
|
|
|
@ -8,7 +8,10 @@ package controllers
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -19,6 +22,8 @@ import (
|
||||||
"git.smarteching.com/goffee/cup/utils"
|
"git.smarteching.com/goffee/cup/utils"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"git.smarteching.com/goffee/core/template/components"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Signup(c *core.Context) *core.Response {
|
func Signup(c *core.Context) *core.Response {
|
||||||
|
@ -123,6 +128,13 @@ func Signin(c *core.Context) *core.Response {
|
||||||
email := c.GetRequestParam("email")
|
email := c.GetRequestParam("email")
|
||||||
password := c.GetRequestParam("password")
|
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{}{
|
data := map[string]interface{}{
|
||||||
"email": email,
|
"email": email,
|
||||||
"password": password,
|
"password": password,
|
||||||
|
@ -135,37 +147,62 @@ func Signin(c *core.Context) *core.Response {
|
||||||
|
|
||||||
if v.Failed() {
|
if v.Failed() {
|
||||||
c.GetLogger().Error(v.GetErrorMessagesJson())
|
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())
|
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var user models.User
|
var user models.User
|
||||||
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
|
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
|
||||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
c.GetLogger().Error(res.Error.Error())
|
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{
|
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||||
"message": "internal server error",
|
"message": "internal server error",
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
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{
|
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||||
"message": "invalid email or password",
|
"message": "invalid email or password",
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ok, err := c.GetHashing().CheckPasswordHash(user.Password, c.CastToString(password))
|
ok, err := c.GetHashing().CheckPasswordHash(user.Password, c.CastToString(password))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.GetLogger().Error(err.Error())
|
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{
|
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !ok {
|
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{
|
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||||
"message": "invalid email or password",
|
"message": "invalid email or password",
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
token, err := c.GetJWT().GenerateToken(map[string]interface{}{
|
token, err := c.GetJWT().GenerateToken(map[string]interface{}{
|
||||||
"userID": user.ID,
|
"userID": user.ID,
|
||||||
|
@ -173,24 +210,46 @@ func Signin(c *core.Context) *core.Response {
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.GetLogger().Error(err.Error())
|
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{
|
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||||
"message": "internal server error",
|
"message": "internal server error",
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// cache the token
|
// cache the token
|
||||||
userAgent := c.GetUserAgent()
|
userAgent := c.GetUserAgent()
|
||||||
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent)
|
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent)
|
||||||
err = c.GetCache().Set(hashedCacheKey, token)
|
err = c.GetCache().Set(hashedCacheKey, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.GetLogger().Error(err.Error())
|
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{}{
|
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
|
||||||
"message": "internal server error",
|
"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{
|
return c.Response.Json(c.MapToJson(map[string]string{
|
||||||
"token": token,
|
"token": token,
|
||||||
}))
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ResetPasswordRequest(c *core.Context) *core.Response {
|
func ResetPasswordRequest(c *core.Context) *core.Response {
|
||||||
|
@ -389,3 +448,43 @@ func Signout(c *core.Context) *core.Response {
|
||||||
"message": "signed out successfully",
|
"message": "signed out successfully",
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 sample
|
||||||
|
func AppSample(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: "Protected page",
|
||||||
|
CardBody: "If you can see this page, your are loggedin",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
//fmt.Printf("Outside cookie user is: %s", user.Email)
|
||||||
|
|
||||||
|
return c.Response.Template("app.html", tmplData)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -6,13 +6,45 @@
|
||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"git.smarteching.com/goffee/core"
|
"git.smarteching.com/goffee/core"
|
||||||
|
"git.smarteching.com/goffee/core/template/components"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Show home page
|
// Show home page
|
||||||
func WelcomeHome(c *core.Context) *core.Response {
|
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\"}"
|
message := "{\"message\": \"Welcome to Goffee\"}"
|
||||||
return c.Response.Json(message)
|
return c.Response.Json(message)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show dashboard
|
// Show dashboard
|
||||||
|
|
30
controllers/sample.go
Normal file
30
controllers/sample.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// 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 (
|
||||||
|
"git.smarteching.com/goffee/core"
|
||||||
|
"git.smarteching.com/goffee/core/template/components"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
}
|
706
controllers/themedemo.go
Normal file
706
controllers/themedemo.go
Normal file
|
@ -0,0 +1,706 @@
|
||||||
|
// 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"
|
||||||
|
"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 = "sogas"
|
||||||
|
optionc.Name = "sogas"
|
||||||
|
optionc.Value = "Sogamoso"
|
||||||
|
optionc.Label = "Sogamoso"
|
||||||
|
//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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// now fill data of the components
|
||||||
|
tmplData := templateData{
|
||||||
|
ContentTable: components.ContentTable{
|
||||||
|
ID: "table_demo",
|
||||||
|
AllTH: allTh,
|
||||||
|
AllTD: allTd,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return c.Response.Template("custom_theme_contentpage.html", tmplData)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
message := "{\"message\": \"Error, template not enabled\"}"
|
||||||
|
return c.Response.Json(message)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ package hooks
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.smarteching.com/goffee/core"
|
"git.smarteching.com/goffee/core"
|
||||||
|
@ -11,20 +13,92 @@ import (
|
||||||
"gorm.io/gorm"
|
"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) {
|
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")
|
tokenRaw := c.GetHeader("Authorization")
|
||||||
token := strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
|
token = strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
|
||||||
if token == "" {
|
if token == "" {
|
||||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||||
"message": "unauthorized",
|
"message": "unauthorized",
|
||||||
})).ForceSendResponse()
|
})).ForceSendResponse()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
payload, err := c.GetJWT().DecodeToken(token)
|
payload, err := c.GetJWT().DecodeToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if TemplateEnable {
|
||||||
|
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||||
|
} else {
|
||||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||||
"message": "unauthorized",
|
"message": "unauthorized",
|
||||||
})).ForceSendResponse()
|
})).ForceSendResponse()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userAgent := c.GetUserAgent()
|
userAgent := c.GetUserAgent()
|
||||||
|
@ -33,16 +107,24 @@ var AuthCheck core.Hook = func(c *core.Context) {
|
||||||
cachedToken, err := c.GetCache().Get(hashedCacheKey)
|
cachedToken, err := c.GetCache().Get(hashedCacheKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// user signed out
|
// user signed out
|
||||||
|
if TemplateEnable {
|
||||||
|
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||||
|
} else {
|
||||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||||
"message": "unauthorized",
|
"message": "unauthorized",
|
||||||
})).ForceSendResponse()
|
})).ForceSendResponse()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cachedToken != token {
|
if cachedToken != token {
|
||||||
// using old token replaced with new one after recent signin
|
// 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{}{
|
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||||
"message": "unauthorized",
|
"message": "unauthorized",
|
||||||
})).ForceSendResponse()
|
})).ForceSendResponse()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,17 +133,13 @@ var AuthCheck core.Hook = func(c *core.Context) {
|
||||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||||
// error with the database
|
// error with the database
|
||||||
c.GetLogger().Error(res.Error.Error())
|
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{}{
|
c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
|
||||||
"message": "internal error",
|
"message": "internal error",
|
||||||
})).ForceSendResponse()
|
})).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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
main.go
2
main.go
|
@ -19,7 +19,7 @@ import (
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed all:templates
|
//go:embed all:storage/templates
|
||||||
var resources embed.FS
|
var resources embed.FS
|
||||||
|
|
||||||
// The main function
|
// The main function
|
||||||
|
|
24
routes.go
24
routes.go
|
@ -20,11 +20,25 @@ func registerRoutes() {
|
||||||
|
|
||||||
// Define your routes here...
|
// Define your routes here...
|
||||||
controller.Get("/", controllers.WelcomeHome)
|
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
|
// Uncomment the lines below to enable authentication
|
||||||
// controller.Post("/signup", controllers.Signup)
|
controller.Post("/signup", controllers.Signup)
|
||||||
// controller.Post("/signin", controllers.Signin)
|
controller.Post("/signin", controllers.Signin)
|
||||||
// controller.Post("/signout", controllers.Signout)
|
controller.Post("/signout", controllers.Signout)
|
||||||
// controller.Post("/reset-password", controllers.ResetPasswordRequest)
|
controller.Post("/reset-password", controllers.ResetPasswordRequest)
|
||||||
// controller.Post("/reset-password/code/:code", controllers.SetNewPassword)
|
controller.Post("/reset-password/code/:code", controllers.SetNewPassword)
|
||||||
|
|
||||||
controller.Get("/dashboard", controllers.WelcomeToDashboard, hooks.AuthCheck)
|
controller.Get("/dashboard", controllers.WelcomeToDashboard, hooks.AuthCheck)
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
1
storage/public/app.js
Normal file
1
storage/public/app.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Start Goffee app");
|
4085
storage/public/bootstrap/css/bootstrap-grid.css
vendored
Normal file
4085
storage/public/bootstrap/css/bootstrap-grid.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
storage/public/bootstrap/css/bootstrap-grid.css.map
Normal file
1
storage/public/bootstrap/css/bootstrap-grid.css.map
Normal file
File diff suppressed because one or more lines are too long
6
storage/public/bootstrap/css/bootstrap-grid.min.css
vendored
Normal file
6
storage/public/bootstrap/css/bootstrap-grid.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
storage/public/bootstrap/css/bootstrap-grid.min.css.map
Normal file
1
storage/public/bootstrap/css/bootstrap-grid.min.css.map
Normal file
File diff suppressed because one or more lines are too long
4084
storage/public/bootstrap/css/bootstrap-grid.rtl.css
vendored
Normal file
4084
storage/public/bootstrap/css/bootstrap-grid.rtl.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
storage/public/bootstrap/css/bootstrap-grid.rtl.css.map
Normal file
1
storage/public/bootstrap/css/bootstrap-grid.rtl.css.map
Normal file
File diff suppressed because one or more lines are too long
6
storage/public/bootstrap/css/bootstrap-grid.rtl.min.css
vendored
Normal file
6
storage/public/bootstrap/css/bootstrap-grid.rtl.min.css
vendored
Normal file
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
Normal file
597
storage/public/bootstrap/css/bootstrap-reboot.css
vendored
Normal file
|
@ -0,0 +1,597 @@
|
||||||
|
/*!
|
||||||
|
* 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 */
|
1
storage/public/bootstrap/css/bootstrap-reboot.css.map
Normal file
1
storage/public/bootstrap/css/bootstrap-reboot.css.map
Normal file
File diff suppressed because one or more lines are too long
6
storage/public/bootstrap/css/bootstrap-reboot.min.css
vendored
Normal file
6
storage/public/bootstrap/css/bootstrap-reboot.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
594
storage/public/bootstrap/css/bootstrap-reboot.rtl.css
vendored
Normal file
594
storage/public/bootstrap/css/bootstrap-reboot.rtl.css
vendored
Normal file
|
@ -0,0 +1,594 @@
|
||||||
|
/*!
|
||||||
|
* 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
6
storage/public/bootstrap/css/bootstrap-reboot.rtl.min.css
vendored
Normal file
6
storage/public/bootstrap/css/bootstrap-reboot.rtl.min.css
vendored
Normal file
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
Normal file
5402
storage/public/bootstrap/css/bootstrap-utilities.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
storage/public/bootstrap/css/bootstrap-utilities.css.map
Normal file
1
storage/public/bootstrap/css/bootstrap-utilities.css.map
Normal file
File diff suppressed because one or more lines are too long
6
storage/public/bootstrap/css/bootstrap-utilities.min.css
vendored
Normal file
6
storage/public/bootstrap/css/bootstrap-utilities.min.css
vendored
Normal file
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
Normal file
5393
storage/public/bootstrap/css/bootstrap-utilities.rtl.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
6
storage/public/bootstrap/css/bootstrap-utilities.rtl.min.css
vendored
Normal file
6
storage/public/bootstrap/css/bootstrap-utilities.rtl.min.css
vendored
Normal file
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
Normal file
12057
storage/public/bootstrap/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
storage/public/bootstrap/css/bootstrap.css.map
Normal file
1
storage/public/bootstrap/css/bootstrap.css.map
Normal file
File diff suppressed because one or more lines are too long
6
storage/public/bootstrap/css/bootstrap.min.css
vendored
Normal file
6
storage/public/bootstrap/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
storage/public/bootstrap/css/bootstrap.min.css.map
Normal file
1
storage/public/bootstrap/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
12030
storage/public/bootstrap/css/bootstrap.rtl.css
vendored
Normal file
12030
storage/public/bootstrap/css/bootstrap.rtl.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
storage/public/bootstrap/css/bootstrap.rtl.css.map
Normal file
1
storage/public/bootstrap/css/bootstrap.rtl.css.map
Normal file
File diff suppressed because one or more lines are too long
6
storage/public/bootstrap/css/bootstrap.rtl.min.css
vendored
Normal file
6
storage/public/bootstrap/css/bootstrap.rtl.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
storage/public/bootstrap/css/bootstrap.rtl.min.css.map
Normal file
1
storage/public/bootstrap/css/bootstrap.rtl.min.css.map
Normal file
File diff suppressed because one or more lines are too long
6314
storage/public/bootstrap/js/bootstrap.bundle.js
vendored
Normal file
6314
storage/public/bootstrap/js/bootstrap.bundle.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
storage/public/bootstrap/js/bootstrap.bundle.js.map
Normal file
1
storage/public/bootstrap/js/bootstrap.bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
7
storage/public/bootstrap/js/bootstrap.bundle.min.js
vendored
Normal file
7
storage/public/bootstrap/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
storage/public/bootstrap/js/bootstrap.bundle.min.js.map
Normal file
1
storage/public/bootstrap/js/bootstrap.bundle.min.js.map
Normal file
File diff suppressed because one or more lines are too long
4447
storage/public/bootstrap/js/bootstrap.esm.js
vendored
Normal file
4447
storage/public/bootstrap/js/bootstrap.esm.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
storage/public/bootstrap/js/bootstrap.esm.js.map
Normal file
1
storage/public/bootstrap/js/bootstrap.esm.js.map
Normal file
File diff suppressed because one or more lines are too long
7
storage/public/bootstrap/js/bootstrap.esm.min.js
vendored
Normal file
7
storage/public/bootstrap/js/bootstrap.esm.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
storage/public/bootstrap/js/bootstrap.esm.min.js.map
Normal file
1
storage/public/bootstrap/js/bootstrap.esm.min.js.map
Normal file
File diff suppressed because one or more lines are too long
4494
storage/public/bootstrap/js/bootstrap.js
vendored
Normal file
4494
storage/public/bootstrap/js/bootstrap.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
storage/public/bootstrap/js/bootstrap.js.map
Normal file
1
storage/public/bootstrap/js/bootstrap.js.map
Normal file
File diff suppressed because one or more lines are too long
7
storage/public/bootstrap/js/bootstrap.min.js
vendored
Normal file
7
storage/public/bootstrap/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
storage/public/bootstrap/js/bootstrap.min.js.map
Normal file
1
storage/public/bootstrap/js/bootstrap.min.js.map
Normal file
File diff suppressed because one or more lines are too long
6
storage/public/bootstrap/js/popper.min.js
vendored
Normal file
6
storage/public/bootstrap/js/popper.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
storage/public/img/favicon.ico
Normal file
BIN
storage/public/img/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
storage/public/img/goffee.png
Normal file
BIN
storage/public/img/goffee.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
1
storage/public/index.html
Normal file
1
storage/public/index.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
11
storage/public/style.css
Normal file
11
storage/public/style.css
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
## styles
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
font-family: "Helvetica Neue";
|
||||||
|
}
|
||||||
|
|
||||||
|
.goffeelogo {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
15
storage/templates/app.html
Normal file
15
storage/templates/app.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<!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
storage/templates/custom_theme_base.html
Normal file
10
storage/templates/custom_theme_base.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
{{template "page_head" "Goffee"}}
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
{{template "page_card" .PageCard}}
|
||||||
|
</div>
|
||||||
|
{{template "page_footer"}}
|
||||||
|
</body>
|
||||||
|
</html>
|
15
storage/templates/custom_theme_contentpage.html
Normal file
15
storage/templates/custom_theme_contentpage.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<!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>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
{{template "page_footer"}}
|
||||||
|
</body>
|
||||||
|
</html>
|
66
storage/templates/custom_theme_elements.html
Normal file
66
storage/templates/custom_theme_elements.html
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<!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>
|
23
storage/templates/custom_theme_formpage.html
Normal file
23
storage/templates/custom_theme_formpage.html
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<!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>
|
24
storage/templates/login.html
Normal file
24
storage/templates/login.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<!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>
|
15
storage/templates/welcome.html
Normal file
15
storage/templates/welcome.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<!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>
|
|
@ -1,10 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
{{template "head" "Sample"}}
|
|
||||||
<body>
|
|
||||||
{{template "title" .TheTitle}}
|
|
||||||
<div class="section">
|
|
||||||
Sample page!
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in a new issue