Compare commits
No commits in common. "0e2803df9e0f5ad6a92e49db9cd4ead449bde226" and "2f35c6e792e7e5a4c26b556e91ff3e2402a71b1f" have entirely different histories.
0e2803df9e
...
2f35c6e792
67 changed files with 70 additions and 60792 deletions
|
@ -18,7 +18,8 @@ App_KEY_FILE_PATH=tls/server.key
|
|||
###### TEMPLATES ######
|
||||
#######################################
|
||||
TEMPLATE_ENABLE=true
|
||||
COOKIE_SECRET=13d6b4dff8f84a10851021ec8608f814570d562c92fe6b5ec4c9f595bcb3234b
|
||||
TEMPLATE_PUBLIC=storage/public
|
||||
TEMPLATE_COMPONENTS=true
|
||||
|
||||
#######################################
|
||||
###### JWT ######
|
||||
|
@ -45,7 +46,7 @@ POSTGRES_PASSWORD=secret
|
|||
POSTGRES_DB_NAME=db_test
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_SSL_MODE=disable
|
||||
POSTGRES_TIMEZONE=America/Argentina/Buenos_Aires
|
||||
POSTGRES_TIMEZONE=Asia/Dubai
|
||||
|
||||
#_____ SQLITE _____#
|
||||
SQLITE_DB_PATH=storage/sqlite.db
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ logs/*
|
|||
tls/*
|
||||
!tls/.gitkeep
|
||||
.DS_Store
|
||||
storage/public/*
|
||||
|
|
|
@ -8,10 +8,7 @@ package controllers
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -22,8 +19,6 @@ import (
|
|||
"git.smarteching.com/goffee/cup/utils"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"git.smarteching.com/goffee/core/template/components"
|
||||
)
|
||||
|
||||
func Signup(c *core.Context) *core.Response {
|
||||
|
@ -128,13 +123,6 @@ func Signin(c *core.Context) *core.Response {
|
|||
email := c.GetRequestParam("email")
|
||||
password := c.GetRequestParam("password")
|
||||
|
||||
// check if template engine is enable
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
data := map[string]interface{}{
|
||||
"email": email,
|
||||
"password": password,
|
||||
|
@ -147,62 +135,37 @@ func Signin(c *core.Context) *core.Response {
|
|||
|
||||
if v.Failed() {
|
||||
c.GetLogger().Error(v.GetErrorMessagesJson())
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(v.GetErrorMessagesJson())
|
||||
}
|
||||
}
|
||||
|
||||
var user models.User
|
||||
res := c.GetGorm().Where("email = ?", c.CastToString(email)).First(&user)
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
c.GetLogger().Error(res.Error.Error())
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid email or password",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
ok, err := c.GetHashing().CheckPasswordHash(user.Password, c.CastToString(password))
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": err.Error(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusUnprocessableEntity).Json(c.MapToJson(map[string]string{
|
||||
"message": "invalid email or password",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
token, err := c.GetJWT().GenerateToken(map[string]interface{}{
|
||||
"userID": user.ID,
|
||||
|
@ -210,46 +173,24 @@ func Signin(c *core.Context) *core.Response {
|
|||
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
// TODO set error in session
|
||||
if TemplateEnable {
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]string{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
}
|
||||
// cache the token
|
||||
userAgent := c.GetUserAgent()
|
||||
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(user.ID, userAgent)
|
||||
err = c.GetCache().Set(hashedCacheKey, token)
|
||||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
if TemplateEnable {
|
||||
// TODO set error in session
|
||||
return c.Response.Redirect("/applogin")
|
||||
} else {
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "internal server error",
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if TemplateEnable {
|
||||
// create cookie
|
||||
err = core.SetCookie(c.Response.HttpResponseWriter, email.(string), token)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error write encrypted cookie: %v", err))
|
||||
return c.Response.SetStatusCode(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// redirecto to app
|
||||
return c.Response.Redirect("/appsample")
|
||||
} else {
|
||||
return c.Response.Json(c.MapToJson(map[string]string{
|
||||
"token": token,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func ResetPasswordRequest(c *core.Context) *core.Response {
|
||||
|
@ -448,43 +389,3 @@ func Signout(c *core.Context) *core.Response {
|
|||
"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,45 +6,13 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/core/template/components"
|
||||
)
|
||||
|
||||
// Show home page
|
||||
func WelcomeHome(c *core.Context) *core.Response {
|
||||
|
||||
// check if template engine is enable
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
if TemplateEnable {
|
||||
|
||||
// first, include all compoments
|
||||
type templateData struct {
|
||||
PageCard components.PageCard
|
||||
}
|
||||
|
||||
// now fill data of the components
|
||||
tmplData := templateData{
|
||||
PageCard: components.PageCard{
|
||||
CardTitle: "Golang Framework",
|
||||
CardBody: "Welcome to Goffee",
|
||||
},
|
||||
}
|
||||
|
||||
return c.Response.Template("welcome.html", tmplData)
|
||||
|
||||
} else {
|
||||
message := "{\"message\": \"Welcome to Goffee\"}"
|
||||
return c.Response.Json(message)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Show dashboard
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"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)
|
||||
|
||||
}
|
|
@ -1,706 +0,0 @@
|
|||
// Copyright (c) 2024 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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,8 +3,6 @@ package hooks
|
|||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
|
@ -13,92 +11,20 @@ import (
|
|||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var CheckSessionCookie core.Hook = func(c *core.Context) {
|
||||
|
||||
pass := true
|
||||
token := ""
|
||||
usercookie, err := c.GetCookie()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
token = usercookie.Token
|
||||
if token == "" {
|
||||
pass = false
|
||||
} else {
|
||||
payload, err := c.GetJWT().DecodeToken(token)
|
||||
if err != nil {
|
||||
pass = false
|
||||
} else {
|
||||
|
||||
userAgent := c.GetUserAgent()
|
||||
hashedCacheKey := utils.CreateAuthTokenHashedCacheKey(uint(c.CastToInt(payload["userID"])), userAgent)
|
||||
|
||||
cachedToken, err := c.GetCache().Get(hashedCacheKey)
|
||||
if err != nil {
|
||||
pass = false
|
||||
} else if cachedToken != token {
|
||||
pass = false
|
||||
} else {
|
||||
var user models.User
|
||||
res := c.GetGorm().Where("id = ?", payload["userID"]).First(&user)
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
pass = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if have session redirect protected page
|
||||
if pass {
|
||||
c.Response.Redirect("/appsample").ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
|
||||
}
|
||||
|
||||
var AuthCheck core.Hook = func(c *core.Context) {
|
||||
|
||||
// check if template engine is enable
|
||||
TemplateEnableStr := os.Getenv("TEMPLATE_ENABLE")
|
||||
if TemplateEnableStr == "" {
|
||||
TemplateEnableStr = "false"
|
||||
}
|
||||
TemplateEnable, _ := strconv.ParseBool(TemplateEnableStr)
|
||||
|
||||
token := ""
|
||||
|
||||
if TemplateEnable {
|
||||
usercookie, err := c.GetCookie()
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
token = usercookie.Token
|
||||
if token == "" {
|
||||
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
tokenRaw := c.GetHeader("Authorization")
|
||||
token = strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
|
||||
token := strings.TrimSpace(strings.Replace(tokenRaw, "Bearer", "", 1))
|
||||
if token == "" {
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
payload, err := c.GetJWT().DecodeToken(token)
|
||||
if err != nil {
|
||||
if TemplateEnable {
|
||||
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||
} else {
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
}
|
||||
return
|
||||
}
|
||||
userAgent := c.GetUserAgent()
|
||||
|
@ -107,24 +33,16 @@ var AuthCheck core.Hook = func(c *core.Context) {
|
|||
cachedToken, err := c.GetCache().Get(hashedCacheKey)
|
||||
if err != nil {
|
||||
// user signed out
|
||||
if TemplateEnable {
|
||||
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||
} else {
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
}
|
||||
return
|
||||
}
|
||||
if cachedToken != token {
|
||||
// using old token replaced with new one after recent signin
|
||||
if TemplateEnable {
|
||||
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||
} else {
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -133,13 +51,17 @@ var AuthCheck core.Hook = func(c *core.Context) {
|
|||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
// error with the database
|
||||
c.GetLogger().Error(res.Error.Error())
|
||||
if TemplateEnable {
|
||||
c.Response.Redirect("/applogin").ForceSendResponse()
|
||||
} else {
|
||||
c.Response.SetStatusCode(http.StatusInternalServerError).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "internal error",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
// user record is not found (deleted)
|
||||
c.Response.SetStatusCode(http.StatusUnauthorized).Json(c.MapToJson(map[string]interface{}{
|
||||
"message": "unauthorized",
|
||||
})).ForceSendResponse()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
2
main.go
2
main.go
|
@ -19,7 +19,7 @@ import (
|
|||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
//go:embed all:storage/templates
|
||||
//go:embed all:templates
|
||||
var resources embed.FS
|
||||
|
||||
// The main function
|
||||
|
|
24
routes.go
24
routes.go
|
@ -20,25 +20,11 @@ func registerRoutes() {
|
|||
|
||||
// Define your routes here...
|
||||
controller.Get("/", controllers.WelcomeHome)
|
||||
// Uncomment the lines below to enable theme demo
|
||||
controller.Get("/themebase", controllers.Themedemo)
|
||||
controller.Get("/themeform", controllers.Themeform)
|
||||
controller.Get("/themecontent", controllers.Themecontent)
|
||||
controller.Get("/themepanel", controllers.Themedemo)
|
||||
controller.Get("/themeelements", controllers.ThemeElements)
|
||||
|
||||
// Uncomment the lines below to enable authentication
|
||||
controller.Post("/signup", controllers.Signup)
|
||||
controller.Post("/signin", controllers.Signin)
|
||||
controller.Post("/signout", controllers.Signout)
|
||||
controller.Post("/reset-password", controllers.ResetPasswordRequest)
|
||||
controller.Post("/reset-password/code/:code", controllers.SetNewPassword)
|
||||
|
||||
// controller.Post("/signup", controllers.Signup)
|
||||
// controller.Post("/signin", controllers.Signin)
|
||||
// controller.Post("/signout", controllers.Signout)
|
||||
// controller.Post("/reset-password", controllers.ResetPasswordRequest)
|
||||
// controller.Post("/reset-password/code/:code", controllers.SetNewPassword)
|
||||
controller.Get("/dashboard", controllers.WelcomeToDashboard, hooks.AuthCheck)
|
||||
|
||||
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 +0,0 @@
|
|||
console.log("Start Goffee app");
|
4085
storage/public/bootstrap/css/bootstrap-grid.css
vendored
4085
storage/public/bootstrap/css/bootstrap-grid.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4084
storage/public/bootstrap/css/bootstrap-grid.rtl.css
vendored
4084
storage/public/bootstrap/css/bootstrap-grid.rtl.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
597
storage/public/bootstrap/css/bootstrap-reboot.css
vendored
597
storage/public/bootstrap/css/bootstrap-reboot.css
vendored
|
@ -1,597 +0,0 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2024 The Bootstrap Authors
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
:root,
|
||||
[data-bs-theme=light] {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-primary-text-emphasis: #052c65;
|
||||
--bs-secondary-text-emphasis: #2b2f32;
|
||||
--bs-success-text-emphasis: #0a3622;
|
||||
--bs-info-text-emphasis: #055160;
|
||||
--bs-warning-text-emphasis: #664d03;
|
||||
--bs-danger-text-emphasis: #58151c;
|
||||
--bs-light-text-emphasis: #495057;
|
||||
--bs-dark-text-emphasis: #495057;
|
||||
--bs-primary-bg-subtle: #cfe2ff;
|
||||
--bs-secondary-bg-subtle: #e2e3e5;
|
||||
--bs-success-bg-subtle: #d1e7dd;
|
||||
--bs-info-bg-subtle: #cff4fc;
|
||||
--bs-warning-bg-subtle: #fff3cd;
|
||||
--bs-danger-bg-subtle: #f8d7da;
|
||||
--bs-light-bg-subtle: #fcfcfd;
|
||||
--bs-dark-bg-subtle: #ced4da;
|
||||
--bs-primary-border-subtle: #9ec5fe;
|
||||
--bs-secondary-border-subtle: #c4c8cb;
|
||||
--bs-success-border-subtle: #a3cfbb;
|
||||
--bs-info-border-subtle: #9eeaf9;
|
||||
--bs-warning-border-subtle: #ffe69c;
|
||||
--bs-danger-border-subtle: #f1aeb5;
|
||||
--bs-light-border-subtle: #e9ecef;
|
||||
--bs-dark-border-subtle: #adb5bd;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg: #fff;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-emphasis-color: #000;
|
||||
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||
--bs-secondary-color-rgb: 33, 37, 41;
|
||||
--bs-secondary-bg: #e9ecef;
|
||||
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||
--bs-tertiary-bg: #f8f9fa;
|
||||
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #0d6efd;
|
||||
--bs-link-color-rgb: 13, 110, 253;
|
||||
--bs-link-decoration: underline;
|
||||
--bs-link-hover-color: #0a58ca;
|
||||
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||
--bs-code-color: #d63384;
|
||||
--bs-highlight-color: #212529;
|
||||
--bs-highlight-bg: #fff3cd;
|
||||
--bs-border-width: 1px;
|
||||
--bs-border-style: solid;
|
||||
--bs-border-color: #dee2e6;
|
||||
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||
--bs-border-radius: 0.375rem;
|
||||
--bs-border-radius-sm: 0.25rem;
|
||||
--bs-border-radius-lg: 0.5rem;
|
||||
--bs-border-radius-xl: 1rem;
|
||||
--bs-border-radius-xxl: 2rem;
|
||||
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||
--bs-border-radius-pill: 50rem;
|
||||
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
--bs-focus-ring-width: 0.25rem;
|
||||
--bs-focus-ring-opacity: 0.25;
|
||||
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||
--bs-form-valid-color: #198754;
|
||||
--bs-form-valid-border-color: #198754;
|
||||
--bs-form-invalid-color: #dc3545;
|
||||
--bs-form-invalid-border-color: #dc3545;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-body-color: #dee2e6;
|
||||
--bs-body-color-rgb: 222, 226, 230;
|
||||
--bs-body-bg: #212529;
|
||||
--bs-body-bg-rgb: 33, 37, 41;
|
||||
--bs-emphasis-color: #fff;
|
||||
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||
--bs-secondary-color: rgba(222, 226, 230, 0.75);
|
||||
--bs-secondary-color-rgb: 222, 226, 230;
|
||||
--bs-secondary-bg: #343a40;
|
||||
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
|
||||
--bs-tertiary-color-rgb: 222, 226, 230;
|
||||
--bs-tertiary-bg: #2b3035;
|
||||
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||
--bs-primary-text-emphasis: #6ea8fe;
|
||||
--bs-secondary-text-emphasis: #a7acb1;
|
||||
--bs-success-text-emphasis: #75b798;
|
||||
--bs-info-text-emphasis: #6edff6;
|
||||
--bs-warning-text-emphasis: #ffda6a;
|
||||
--bs-danger-text-emphasis: #ea868f;
|
||||
--bs-light-text-emphasis: #f8f9fa;
|
||||
--bs-dark-text-emphasis: #dee2e6;
|
||||
--bs-primary-bg-subtle: #031633;
|
||||
--bs-secondary-bg-subtle: #161719;
|
||||
--bs-success-bg-subtle: #051b11;
|
||||
--bs-info-bg-subtle: #032830;
|
||||
--bs-warning-bg-subtle: #332701;
|
||||
--bs-danger-bg-subtle: #2c0b0e;
|
||||
--bs-light-bg-subtle: #343a40;
|
||||
--bs-dark-bg-subtle: #1a1d20;
|
||||
--bs-primary-border-subtle: #084298;
|
||||
--bs-secondary-border-subtle: #41464b;
|
||||
--bs-success-border-subtle: #0f5132;
|
||||
--bs-info-border-subtle: #087990;
|
||||
--bs-warning-border-subtle: #997404;
|
||||
--bs-danger-border-subtle: #842029;
|
||||
--bs-light-border-subtle: #495057;
|
||||
--bs-dark-border-subtle: #343a40;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #6ea8fe;
|
||||
--bs-link-hover-color: #8bb9fe;
|
||||
--bs-link-color-rgb: 110, 168, 254;
|
||||
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||
--bs-code-color: #e685b5;
|
||||
--bs-highlight-color: #dee2e6;
|
||||
--bs-highlight-bg: #664d03;
|
||||
--bs-border-color: #495057;
|
||||
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||
--bs-form-valid-color: #75b798;
|
||||
--bs-form-valid-border-color: #75b798;
|
||||
--bs-form-invalid-color: #ea868f;
|
||||
--bs-form-invalid-border-color: #ea868f;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
border: 0;
|
||||
border-top: var(--bs-border-width) solid;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: var(--bs-heading-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.1875em;
|
||||
color: var(--bs-highlight-color);
|
||||
background-color: var(--bs-highlight-bg);
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-code-color);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.1875rem 0.375rem;
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-body-bg);
|
||||
background-color: var(--bs-body-color);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: var(--bs-secondary-color);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: left;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* rtl:raw:
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
*/
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,594 +0,0 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2024 The Bootstrap Authors
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
:root,
|
||||
[data-bs-theme=light] {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
--bs-gray-dark: #343a40;
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #0d6efd;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #198754;
|
||||
--bs-info: #0dcaf0;
|
||||
--bs-warning: #ffc107;
|
||||
--bs-danger: #dc3545;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 13, 110, 253;
|
||||
--bs-secondary-rgb: 108, 117, 125;
|
||||
--bs-success-rgb: 25, 135, 84;
|
||||
--bs-info-rgb: 13, 202, 240;
|
||||
--bs-warning-rgb: 255, 193, 7;
|
||||
--bs-danger-rgb: 220, 53, 69;
|
||||
--bs-light-rgb: 248, 249, 250;
|
||||
--bs-dark-rgb: 33, 37, 41;
|
||||
--bs-primary-text-emphasis: #052c65;
|
||||
--bs-secondary-text-emphasis: #2b2f32;
|
||||
--bs-success-text-emphasis: #0a3622;
|
||||
--bs-info-text-emphasis: #055160;
|
||||
--bs-warning-text-emphasis: #664d03;
|
||||
--bs-danger-text-emphasis: #58151c;
|
||||
--bs-light-text-emphasis: #495057;
|
||||
--bs-dark-text-emphasis: #495057;
|
||||
--bs-primary-bg-subtle: #cfe2ff;
|
||||
--bs-secondary-bg-subtle: #e2e3e5;
|
||||
--bs-success-bg-subtle: #d1e7dd;
|
||||
--bs-info-bg-subtle: #cff4fc;
|
||||
--bs-warning-bg-subtle: #fff3cd;
|
||||
--bs-danger-bg-subtle: #f8d7da;
|
||||
--bs-light-bg-subtle: #fcfcfd;
|
||||
--bs-dark-bg-subtle: #ced4da;
|
||||
--bs-primary-border-subtle: #9ec5fe;
|
||||
--bs-secondary-border-subtle: #c4c8cb;
|
||||
--bs-success-border-subtle: #a3cfbb;
|
||||
--bs-info-border-subtle: #9eeaf9;
|
||||
--bs-warning-border-subtle: #ffe69c;
|
||||
--bs-danger-border-subtle: #f1aeb5;
|
||||
--bs-light-border-subtle: #e9ecef;
|
||||
--bs-dark-border-subtle: #adb5bd;
|
||||
--bs-white-rgb: 255, 255, 255;
|
||||
--bs-black-rgb: 0, 0, 0;
|
||||
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #212529;
|
||||
--bs-body-color-rgb: 33, 37, 41;
|
||||
--bs-body-bg: #fff;
|
||||
--bs-body-bg-rgb: 255, 255, 255;
|
||||
--bs-emphasis-color: #000;
|
||||
--bs-emphasis-color-rgb: 0, 0, 0;
|
||||
--bs-secondary-color: rgba(33, 37, 41, 0.75);
|
||||
--bs-secondary-color-rgb: 33, 37, 41;
|
||||
--bs-secondary-bg: #e9ecef;
|
||||
--bs-secondary-bg-rgb: 233, 236, 239;
|
||||
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
|
||||
--bs-tertiary-color-rgb: 33, 37, 41;
|
||||
--bs-tertiary-bg: #f8f9fa;
|
||||
--bs-tertiary-bg-rgb: 248, 249, 250;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #0d6efd;
|
||||
--bs-link-color-rgb: 13, 110, 253;
|
||||
--bs-link-decoration: underline;
|
||||
--bs-link-hover-color: #0a58ca;
|
||||
--bs-link-hover-color-rgb: 10, 88, 202;
|
||||
--bs-code-color: #d63384;
|
||||
--bs-highlight-color: #212529;
|
||||
--bs-highlight-bg: #fff3cd;
|
||||
--bs-border-width: 1px;
|
||||
--bs-border-style: solid;
|
||||
--bs-border-color: #dee2e6;
|
||||
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
|
||||
--bs-border-radius: 0.375rem;
|
||||
--bs-border-radius-sm: 0.25rem;
|
||||
--bs-border-radius-lg: 0.5rem;
|
||||
--bs-border-radius-xl: 1rem;
|
||||
--bs-border-radius-xxl: 2rem;
|
||||
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||
--bs-border-radius-pill: 50rem;
|
||||
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
|
||||
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
--bs-focus-ring-width: 0.25rem;
|
||||
--bs-focus-ring-opacity: 0.25;
|
||||
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
|
||||
--bs-form-valid-color: #198754;
|
||||
--bs-form-valid-border-color: #198754;
|
||||
--bs-form-invalid-color: #dc3545;
|
||||
--bs-form-invalid-border-color: #dc3545;
|
||||
}
|
||||
|
||||
[data-bs-theme=dark] {
|
||||
color-scheme: dark;
|
||||
--bs-body-color: #dee2e6;
|
||||
--bs-body-color-rgb: 222, 226, 230;
|
||||
--bs-body-bg: #212529;
|
||||
--bs-body-bg-rgb: 33, 37, 41;
|
||||
--bs-emphasis-color: #fff;
|
||||
--bs-emphasis-color-rgb: 255, 255, 255;
|
||||
--bs-secondary-color: rgba(222, 226, 230, 0.75);
|
||||
--bs-secondary-color-rgb: 222, 226, 230;
|
||||
--bs-secondary-bg: #343a40;
|
||||
--bs-secondary-bg-rgb: 52, 58, 64;
|
||||
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
|
||||
--bs-tertiary-color-rgb: 222, 226, 230;
|
||||
--bs-tertiary-bg: #2b3035;
|
||||
--bs-tertiary-bg-rgb: 43, 48, 53;
|
||||
--bs-primary-text-emphasis: #6ea8fe;
|
||||
--bs-secondary-text-emphasis: #a7acb1;
|
||||
--bs-success-text-emphasis: #75b798;
|
||||
--bs-info-text-emphasis: #6edff6;
|
||||
--bs-warning-text-emphasis: #ffda6a;
|
||||
--bs-danger-text-emphasis: #ea868f;
|
||||
--bs-light-text-emphasis: #f8f9fa;
|
||||
--bs-dark-text-emphasis: #dee2e6;
|
||||
--bs-primary-bg-subtle: #031633;
|
||||
--bs-secondary-bg-subtle: #161719;
|
||||
--bs-success-bg-subtle: #051b11;
|
||||
--bs-info-bg-subtle: #032830;
|
||||
--bs-warning-bg-subtle: #332701;
|
||||
--bs-danger-bg-subtle: #2c0b0e;
|
||||
--bs-light-bg-subtle: #343a40;
|
||||
--bs-dark-bg-subtle: #1a1d20;
|
||||
--bs-primary-border-subtle: #084298;
|
||||
--bs-secondary-border-subtle: #41464b;
|
||||
--bs-success-border-subtle: #0f5132;
|
||||
--bs-info-border-subtle: #087990;
|
||||
--bs-warning-border-subtle: #997404;
|
||||
--bs-danger-border-subtle: #842029;
|
||||
--bs-light-border-subtle: #495057;
|
||||
--bs-dark-border-subtle: #343a40;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #6ea8fe;
|
||||
--bs-link-hover-color: #8bb9fe;
|
||||
--bs-link-color-rgb: 110, 168, 254;
|
||||
--bs-link-hover-color-rgb: 139, 185, 254;
|
||||
--bs-code-color: #e685b5;
|
||||
--bs-highlight-color: #dee2e6;
|
||||
--bs-highlight-bg: #664d03;
|
||||
--bs-border-color: #495057;
|
||||
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
|
||||
--bs-form-valid-color: #75b798;
|
||||
--bs-form-valid-border-color: #75b798;
|
||||
--bs-form-invalid-color: #ea868f;
|
||||
--bs-form-invalid-border-color: #ea868f;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:root {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--bs-body-font-family);
|
||||
font-size: var(--bs-body-font-size);
|
||||
font-weight: var(--bs-body-font-weight);
|
||||
line-height: var(--bs-body-line-height);
|
||||
color: var(--bs-body-color);
|
||||
text-align: var(--bs-body-text-align);
|
||||
background-color: var(--bs-body-bg);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem 0;
|
||||
color: inherit;
|
||||
border: 0;
|
||||
border-top: var(--bs-border-width) solid;
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
h6, h5, h4, h3, h2, h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
color: var(--bs-heading-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: calc(1.375rem + 1.5vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: calc(1.325rem + 0.9vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: calc(1.3rem + 0.6vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h3 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title] {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: 0.5rem;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0.1875em;
|
||||
color: var(--bs-highlight-color);
|
||||
background-color: var(--bs-highlight-bg);
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 0.75em;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
|
||||
text-decoration: underline;
|
||||
}
|
||||
a:hover {
|
||||
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
|
||||
}
|
||||
|
||||
a:not([href]):not([class]), a:not([href]):not([class]):hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
pre code {
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-code-color);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
a > code {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.1875rem 0.375rem;
|
||||
font-size: 0.875em;
|
||||
color: var(--bs-body-bg);
|
||||
background-color: var(--bs-body-color);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
kbd kbd {
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img,
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
caption-side: bottom;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
color: var(--bs-secondary-color);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus:not(:focus-visible) {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
select:disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
button,
|
||||
[type=button],
|
||||
[type=reset],
|
||||
[type=submit] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
button:not(:disabled),
|
||||
[type=button]:not(:disabled),
|
||||
[type=reset]:not(:disabled),
|
||||
[type=submit]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
float: right;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: calc(1.275rem + 0.3vw);
|
||||
line-height: inherit;
|
||||
}
|
||||
@media (min-width: 1200px) {
|
||||
legend {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
legend + * {
|
||||
clear: right;
|
||||
}
|
||||
|
||||
::-webkit-datetime-edit-fields-wrapper,
|
||||
::-webkit-datetime-edit-text,
|
||||
::-webkit-datetime-edit-minute,
|
||||
::-webkit-datetime-edit-hour-field,
|
||||
::-webkit-datetime-edit-day-field,
|
||||
::-webkit-datetime-edit-month-field,
|
||||
::-webkit-datetime-edit-year-field {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type=search] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
[type="tel"],
|
||||
[type="url"],
|
||||
[type="email"],
|
||||
[type="number"] {
|
||||
direction: ltr;
|
||||
}
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
::file-selector-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
5402
storage/public/bootstrap/css/bootstrap-utilities.css
vendored
5402
storage/public/bootstrap/css/bootstrap-utilities.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
5393
storage/public/bootstrap/css/bootstrap-utilities.rtl.css
vendored
5393
storage/public/bootstrap/css/bootstrap-utilities.rtl.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
12057
storage/public/bootstrap/css/bootstrap.css
vendored
12057
storage/public/bootstrap/css/bootstrap.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
12030
storage/public/bootstrap/css/bootstrap.rtl.css
vendored
12030
storage/public/bootstrap/css/bootstrap.rtl.css
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6314
storage/public/bootstrap/js/bootstrap.bundle.js
vendored
6314
storage/public/bootstrap/js/bootstrap.bundle.js
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4447
storage/public/bootstrap/js/bootstrap.esm.js
vendored
4447
storage/public/bootstrap/js/bootstrap.esm.js
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4494
storage/public/bootstrap/js/bootstrap.js
vendored
4494
storage/public/bootstrap/js/bootstrap.js
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
7
storage/public/bootstrap/js/bootstrap.min.js
vendored
7
storage/public/bootstrap/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6
storage/public/bootstrap/js/popper.min.js
vendored
6
storage/public/bootstrap/js/popper.min.js
vendored
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
## styles
|
||||
|
||||
body {
|
||||
background-color: #f2f2f2;
|
||||
font-family: "Helvetica Neue";
|
||||
}
|
||||
|
||||
.goffeelogo {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Sample page"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{{template "page_card" .PageCard}}
|
||||
{{ define "page_card_content" }}
|
||||
<img class="goffeelogo"src="/public/img/goffee.png" alt="Goffee logo" />
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,10 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Goffee"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
{{template "page_card" .PageCard}}
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,15 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Goffee"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<fieldset>
|
||||
<legend>Content demos</legend>
|
||||
<div class="row">
|
||||
{{template "content_table" .ContentTable}}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,66 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Goffee"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos buttons</legend>
|
||||
<div class="container border rounded-3 p-2">
|
||||
{{range .Buttons}}
|
||||
{{template "form_button" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos href</legend>
|
||||
<div class="container d-flex justify-content-between border rounded-3 p-2">
|
||||
{{range .Hrefs}}
|
||||
{{template "content_href" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos Badges</legend>
|
||||
<div class="container d-flex justify-content-between border rounded-3 p-2">
|
||||
{{range .Badges}}
|
||||
{{template "content_badge" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos dropdown</legend>
|
||||
<div class="container d-flex justify-content-between border rounded-3 p-2">
|
||||
{{range .Dropdowns}}
|
||||
{{template "content_dropdown" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos List</legend>
|
||||
<div class="container d-flex justify-content-between border rounded-3 p-2">
|
||||
{{range .Lists}}
|
||||
{{template "content_list" .}}
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="mb-3">
|
||||
<legend>Demos nav</legend>
|
||||
<div class="container border rounded-3 p-2 ">
|
||||
{{range .Menus}}
|
||||
<div class="container border rounded-3 p-2 mb-2">
|
||||
{{template "page_nav" .}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,23 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Goffee"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<form>
|
||||
<fieldset>
|
||||
<legend>form demos</legend>
|
||||
<div class="row">
|
||||
{{template "form_input" .FormText}}
|
||||
{{template "form_input" .FormEmail}}
|
||||
{{template "form_select" .FormSelectCity}}
|
||||
{{template "form_textarea" .FormTextarea}}
|
||||
{{template "form_radio" .FormRadio}}
|
||||
{{template "form_checkbox" .FormCheckbox}}
|
||||
</div>
|
||||
</fieldset>
|
||||
{{template "form_button" .FormButton}}
|
||||
</form>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
|
@ -1,24 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Goffee"}}
|
||||
<body>
|
||||
<main>
|
||||
|
||||
<form method="POST" action="/signin">
|
||||
<div>
|
||||
<label for="email">Email:</label>
|
||||
<input type="text" id="email" name="email" required>
|
||||
</div>
|
||||
<div>
|
||||
<label for="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" name="login">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</main>
|
||||
<script src="/public/app.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,15 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "Sample page"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{{template "page_card" .PageCard}}
|
||||
{{ define "page_card_content" }}
|
||||
<img class="goffeelogo"src="/public/img/goffee.png" alt="Goffee logo" />
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
10
templates/sample.html
Normal file
10
templates/sample.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "head" "Sample"}}
|
||||
<body>
|
||||
{{template "title" .TheTitle}}
|
||||
<div class="section">
|
||||
Sample page!
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue