Upgraded to the latest go-web-kit.
This commit is contained in:
parent
051474bdc9
commit
41f31a621f
14 changed files with 124 additions and 142 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -31,3 +31,5 @@ config.toml
|
|||
public
|
||||
*.idx
|
||||
*.sqlite3
|
||||
static/videos
|
||||
media
|
||||
|
|
|
|||
13
.ssgod.json
Normal file
13
.ssgod.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"views": "pages",
|
||||
"layout": "views/layouts/main.html",
|
||||
"target": "public",
|
||||
"watch_delay": "500ms",
|
||||
"sync_dir": "static",
|
||||
"exclude": [
|
||||
"pages/layouts/.*"
|
||||
],
|
||||
"meta_files": [
|
||||
"pages/sitemap.html"
|
||||
]
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func SearchTable(search string, table string, the_type reflect.Type, limit uint64, page uint64) ([]any, error) {
|
||||
|
||||
var results []any
|
||||
like := fmt.Sprint("%", search, "%")
|
||||
|
||||
|
|
@ -132,6 +133,7 @@ func Update(table string, value reflect.Value) error {
|
|||
builder = builder.Where(sq.Eq{"id": value.FieldByName("Id").Interface()})
|
||||
sql_query, args, err := builder.ToSql()
|
||||
|
||||
fmt.Println("UPDATE QUERY", sql_query, args)
|
||||
if err != nil { return err}
|
||||
|
||||
_, err = data.DB.Exec(sql_query, args...)
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ import (
|
|||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"MY/webapp/data"
|
||||
"MY/webapp/api"
|
||||
. "MY/webapp/common"
|
||||
"MY/webapp/auth"
|
||||
)
|
||||
|
||||
func GetApiTableIndex(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
var tables []string
|
||||
|
|
@ -24,7 +24,7 @@ func GetApiTableIndex(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
func GetApiSelectAll(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
table := c.Params("table")
|
||||
|
|
@ -50,14 +50,14 @@ func GetApiSelectAll(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
func GetPageSelectAll(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
return c.Render("admin/table/contents", fiber.Map{"Table": c.Params("table")})
|
||||
}
|
||||
|
||||
func GetApiSelectOne(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
table := c.Params("table")
|
||||
|
|
@ -73,7 +73,7 @@ func GetApiSelectOne(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
func GetPageSelectOne(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
table := c.Params("table")
|
||||
|
|
@ -87,7 +87,7 @@ func GetPageSelectOne(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
func PostApiUpdate(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
table := c.Params("table")
|
||||
|
|
@ -102,7 +102,7 @@ func PostApiUpdate(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
func GetPageInsert(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
table := c.Params("table")
|
||||
|
|
@ -110,7 +110,7 @@ func GetPageInsert(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
func GetApiInsert(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
table := c.Params("table")
|
||||
|
|
@ -120,7 +120,7 @@ func GetApiInsert(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
func PostApiInsert(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
table := c.Params("table")
|
||||
|
|
@ -136,7 +136,7 @@ func PostApiInsert(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
func DeleteApi(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
table := c.Params("table")
|
||||
|
|
@ -151,7 +151,7 @@ func DeleteApi(c *fiber.Ctx) error {
|
|||
}
|
||||
|
||||
func GetPageAdminIndex(c *fiber.Ctx) error {
|
||||
_, err := api.CheckAuthed(c, true)
|
||||
_, err := auth.Check(c, true)
|
||||
if err != nil { return c.Redirect("/") }
|
||||
|
||||
return c.Render("admin/table/index", fiber.Map{})
|
||||
|
|
|
|||
|
|
@ -11,11 +11,9 @@ import (
|
|||
|
||||
"MY/webapp/data"
|
||||
. "MY/webapp/common"
|
||||
"MY/webapp/auth"
|
||||
)
|
||||
|
||||
var STORE *session.Store
|
||||
|
||||
|
||||
func GetApiStream(c *fiber.Ctx) error {
|
||||
sql, args, err := sq.Select("*").From("stream").ToSql()
|
||||
err = data.SelectJson[data.Stream](c, err, sql, args...)
|
||||
|
|
@ -45,7 +43,7 @@ func PostApiLink(c *fiber.Ctx) error {
|
|||
var sql string
|
||||
var args []interface{}
|
||||
|
||||
_, err := CheckAuthed(c, false)
|
||||
_, err := auth.Check(c, false)
|
||||
if err != nil { return c.Redirect("/login/") }
|
||||
|
||||
link, err := ReceivePost[data.Link](c)
|
||||
|
|
@ -95,12 +93,6 @@ func Setup(app *fiber.App) {
|
|||
app.Get("/api/stream/:id", GetApiStreamId)
|
||||
app.Get("/api/stream/:id/links", GetApiStreamIdLinks)
|
||||
app.Post("/api/link", PostApiLink)
|
||||
|
||||
// api/auth.go
|
||||
app.Get("/api/authcheck", GetApiAuthCheck)
|
||||
app.Get("/api/logout", GetApiLogout)
|
||||
app.Post("/api/register", PostApiRegister)
|
||||
app.Post("/api/login", PostApiLogin)
|
||||
}
|
||||
|
||||
func Shutdown() {
|
||||
|
|
|
|||
73
auth/handlers.go
Normal file
73
auth/handlers.go
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
|
||||
"MY/webapp/data"
|
||||
. "MY/webapp/common"
|
||||
)
|
||||
|
||||
func GetApiAuthCheck(c *fiber.Ctx) error {
|
||||
_, err := Check(c, false)
|
||||
// auth failure or not authed is determined by err, with nil meaning YES AUTHED
|
||||
return c.JSON(fiber.Map{"is_authed": err == nil})
|
||||
}
|
||||
|
||||
func GetApiLogout(c *fiber.Ctx) error {
|
||||
err := LogoutUser(c)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
return c.Redirect("/")
|
||||
}
|
||||
|
||||
func PostApiRegister(c *fiber.Ctx) error {
|
||||
user, err := ReceivePost[data.User](c)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
err = SetUserPassword(user)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
sql, args, err := sq.Insert("user").
|
||||
Columns("username", "email", "password").
|
||||
Values(user.Username, user.Email, user.Password).ToSql()
|
||||
|
||||
_, err = data.Exec(err, sql, args...)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
return c.Redirect("/login/")
|
||||
}
|
||||
|
||||
func PostApiLogin(c *fiber.Ctx) error {
|
||||
var user data.User
|
||||
|
||||
login, err := ReceivePost[data.Login](c)
|
||||
if(err != nil) { return IfErrNil(err, c) }
|
||||
|
||||
pass_good, err := LoginUser(&user, login)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
if pass_good {
|
||||
sess, err := STORE.Get(c)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
sess.Set("user_id", user.Id)
|
||||
sess.Set("authenticated", true)
|
||||
sess.Set("admin", IsAdmin(&user))
|
||||
err = sess.Save()
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
return c.Redirect("/")
|
||||
} else {
|
||||
return c.Redirect("/login/")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func Setup(app *fiber.App) {
|
||||
app.Get("/api/authcheck", GetApiAuthCheck)
|
||||
app.Get("/api/logout", GetApiLogout)
|
||||
app.Post("/api/register", PostApiRegister)
|
||||
app.Post("/api/login", PostApiLogin)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package api
|
||||
package auth
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
|
@ -12,79 +12,21 @@ import (
|
|||
|
||||
"MY/webapp/data"
|
||||
"MY/webapp/config"
|
||||
. "MY/webapp/common"
|
||||
"MY/webapp/common"
|
||||
)
|
||||
|
||||
func GetApiAuthCheck(c *fiber.Ctx) error {
|
||||
_, err := CheckAuthed(c, false)
|
||||
// auth failure or not authed is determined by err, with nil meaning YES AUTHED
|
||||
return c.JSON(fiber.Map{"is_authed": err == nil})
|
||||
}
|
||||
|
||||
func GetApiLogout(c *fiber.Ctx) error {
|
||||
err := LogoutUser(c)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
return c.Redirect("/")
|
||||
}
|
||||
|
||||
func PostApiRegister(c *fiber.Ctx) error {
|
||||
user, err := ReceivePost[data.User](c)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
err = SetUserPassword(user)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
sql, args, err := sq.Insert("user").
|
||||
Columns("username", "email", "password").
|
||||
Values(user.Username, user.Email, user.Password).ToSql()
|
||||
|
||||
_, err = data.Exec(err, sql, args...)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
return c.Redirect("/login/")
|
||||
}
|
||||
|
||||
func PostApiLogin(c *fiber.Ctx) error {
|
||||
var user data.User
|
||||
|
||||
login, err := ReceivePost[data.Login](c)
|
||||
if(err != nil) { return IfErrNil(err, c) }
|
||||
|
||||
pass_good, err := LoginUser(&user, login)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
if pass_good {
|
||||
sess, err := STORE.Get(c)
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
sess.Set("user_id", user.Id)
|
||||
sess.Set("authenticated", true)
|
||||
sess.Set("admin", IsAdmin(&user))
|
||||
err = sess.Save()
|
||||
if err != nil { return IfErrNil(err, c) }
|
||||
|
||||
return c.Redirect("/")
|
||||
} else {
|
||||
return c.Redirect("/login/")
|
||||
}
|
||||
}
|
||||
|
||||
func IsAdmin(user *data.User) bool {
|
||||
return user.Username == config.Settings.Admin
|
||||
}
|
||||
|
||||
func CheckAuthed(c *fiber.Ctx, needs_admin bool) (*session.Session, error) {
|
||||
sess, err := STORE.Get(c)
|
||||
func Check(c *fiber.Ctx, needs_admin bool) (*session.Session, error) {
|
||||
sess, err := common.STORE.Get(c)
|
||||
if err != nil { return sess, err }
|
||||
|
||||
// BUG: this has to come from the databse, just temporary
|
||||
admin := sess.Get("admin") == true
|
||||
authed := sess.Get("authenticated") == true
|
||||
|
||||
log.Printf("session admin=%v, session authed=%v, needs_admin = %v",
|
||||
sess.Get("admin"), sess.Get("authenticated"), needs_admin)
|
||||
|
||||
if needs_admin {
|
||||
authed = admin && authed
|
||||
log.Printf("after needs_admin block: authed=%v", authed)
|
||||
|
|
@ -100,7 +42,7 @@ func CheckAuthed(c *fiber.Ctx, needs_admin bool) (*session.Session, error) {
|
|||
}
|
||||
|
||||
func LogoutUser(c *fiber.Ctx) error {
|
||||
sess, err := STORE.Get(c)
|
||||
sess, err := common.STORE.Get(c)
|
||||
if err != nil { return err }
|
||||
|
||||
err = sess.Destroy()
|
||||
|
|
@ -3,8 +3,11 @@ package common
|
|||
import (
|
||||
"strings"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/session"
|
||||
)
|
||||
|
||||
var STORE *session.Store
|
||||
|
||||
func Page(path string) (func(c *fiber.Ctx) error) {
|
||||
page_id := strings.ReplaceAll(path, "/", "-") + "-page"
|
||||
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -55,7 +55,7 @@ require (
|
|||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lcthw.dev/go/ozai v0.1.0 // indirect
|
||||
lcthw.dev/go/ssgod v0.1.0 // indirect
|
||||
lcthw.dev/go/ssgod v0.2.2 // indirect
|
||||
)
|
||||
|
||||
tool (
|
||||
|
|
|
|||
8
go.sum
8
go.sum
|
|
@ -123,11 +123,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
|||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lcthw.dev/go/ozai v0.0.0-20250914152405-53a1959fe44e h1:EAbBnwzBlusAOOs/Cq6QXbhhmMxaO7bsilKk6mU8oIc=
|
||||
lcthw.dev/go/ozai v0.0.0-20250914152405-53a1959fe44e/go.mod h1:AwxRrUAb/KtUzSWgKmhv/eBg6ZjJ4tVZ9YA/DlKqtWs=
|
||||
lcthw.dev/go/ozai v0.1.0 h1:LNuLMZV4m+ciHRjf1atcunR/63Jguvz3puEzD5vmFec=
|
||||
lcthw.dev/go/ozai v0.1.0/go.mod h1:AwxRrUAb/KtUzSWgKmhv/eBg6ZjJ4tVZ9YA/DlKqtWs=
|
||||
lcthw.dev/go/ssgod v0.0.0-20250910143122-4457e9bad2af h1:PN/XR1qhfxTn32AGOdQgyBPJs0qqnWVLcPxE5IUENgM=
|
||||
lcthw.dev/go/ssgod v0.0.0-20250910143122-4457e9bad2af/go.mod h1:c0cAznS0qaDUOIGU8nKODfuFLwemEi/KGDCf8guTleA=
|
||||
lcthw.dev/go/ssgod v0.1.0 h1:b1YkIc7hEgUen8zF85ZRG5YCyCeFAvx1KtmNFp1OuMg=
|
||||
lcthw.dev/go/ssgod v0.1.0/go.mod h1:c0cAznS0qaDUOIGU8nKODfuFLwemEi/KGDCf8guTleA=
|
||||
lcthw.dev/go/ssgod v0.2.2 h1:lvzhxOXDka9iejIQj7Db7ePjruBqYvqe7A5zzxsdank=
|
||||
lcthw.dev/go/ssgod v0.2.2/go.mod h1:fCe4J/00a3T4pURfpUp6LZlkUeh5eHDpjApvy6HHiPk=
|
||||
|
|
|
|||
9
pages/sitemap.html
Normal file
9
pages/sitemap.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<h1>Site Map</h1>
|
||||
|
||||
<ul>
|
||||
{{range $index, $page := .Pages}}
|
||||
<li><a href="{{$page}}">{{$page}}</a></li>
|
||||
{{ else }}
|
||||
Nothing Here
|
||||
{{end}}
|
||||
</ul>
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
views = "pages"
|
||||
layout = "views/layouts/main.html"
|
||||
target = "public"
|
||||
watch_delay = "500ms"
|
||||
sync_dir = "static"
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
views = "pages/copy"
|
||||
layout = "pages/layouts/copy.html"
|
||||
target = "public"
|
||||
watch_delay = "500ms"
|
||||
|
|
@ -247,9 +247,6 @@
|
|||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -314,21 +311,12 @@
|
|||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
.flex-shrink {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.shrink-0 {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.border-collapse {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.transform {
|
||||
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
|
||||
}
|
||||
.resize {
|
||||
resize: both;
|
||||
}
|
||||
.two-panel {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
grid-template-rows: repeat(2, minmax(0, 1fr));
|
||||
|
|
@ -366,9 +354,6 @@
|
|||
.justify-evenly {
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
.gap-0 {
|
||||
gap: calc(var(--spacing) * 0);
|
||||
}
|
||||
.gap-0\! {
|
||||
gap: calc(var(--spacing) * 0) !important;
|
||||
}
|
||||
|
|
@ -432,10 +417,6 @@
|
|||
border-width: 1px !important;
|
||||
border-color: var(--color-red-900) !important;
|
||||
}
|
||||
.border {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 1px;
|
||||
}
|
||||
.border-1 {
|
||||
border-style: var(--tw-border-style);
|
||||
border-width: 1px;
|
||||
|
|
@ -506,9 +487,6 @@
|
|||
.bg-gray-800 {
|
||||
background-color: var(--color-gray-800);
|
||||
}
|
||||
.bg-green-400 {
|
||||
background-color: var(--color-green-400);
|
||||
}
|
||||
.bg-green-400\! {
|
||||
background-color: var(--color-green-400) !important;
|
||||
}
|
||||
|
|
@ -521,9 +499,6 @@
|
|||
.\!p-4 {
|
||||
padding: calc(var(--spacing) * 4) !important;
|
||||
}
|
||||
.p-0 {
|
||||
padding: calc(var(--spacing) * 0);
|
||||
}
|
||||
.p-0\! {
|
||||
padding: calc(var(--spacing) * 0) !important;
|
||||
}
|
||||
|
|
@ -551,9 +526,6 @@
|
|||
.\!pb-20 {
|
||||
padding-bottom: calc(var(--spacing) * 20) !important;
|
||||
}
|
||||
.pb-0 {
|
||||
padding-bottom: calc(var(--spacing) * 0);
|
||||
}
|
||||
.pb-0\! {
|
||||
padding-bottom: calc(var(--spacing) * 0) !important;
|
||||
}
|
||||
|
|
@ -572,9 +544,6 @@
|
|||
.pb-8 {
|
||||
padding-bottom: calc(var(--spacing) * 8);
|
||||
}
|
||||
.pb-10 {
|
||||
padding-bottom: calc(var(--spacing) * 10);
|
||||
}
|
||||
.pb-10\! {
|
||||
padding-bottom: calc(var(--spacing) * 10) !important;
|
||||
}
|
||||
|
|
@ -623,9 +592,6 @@
|
|||
--tw-font-weight: var(--font-weight-light);
|
||||
font-weight: var(--font-weight-light);
|
||||
}
|
||||
.text-wrap {
|
||||
text-wrap: wrap;
|
||||
}
|
||||
.text-gray-50 {
|
||||
color: var(--color-gray-50);
|
||||
}
|
||||
|
|
@ -644,17 +610,10 @@
|
|||
.text-red-500 {
|
||||
color: var(--color-red-500);
|
||||
}
|
||||
.underline {
|
||||
text-decoration-line: underline;
|
||||
}
|
||||
.shadow-lg {
|
||||
--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
}
|
||||
.outline {
|
||||
outline-style: var(--tw-outline-style);
|
||||
outline-width: 1px;
|
||||
}
|
||||
.transition {
|
||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, visibility, content-visibility, overlay, pointer-events;
|
||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue