├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── app ├── main.go ├── profile.go ├── profile.templ ├── todos.go └── todos.templ ├── auth ├── login.go ├── login.templ ├── register.go └── register.templ ├── go.mod ├── go.sum ├── lib ├── auth.go ├── base.templ ├── htmx.go └── render.go ├── main.go ├── middleware └── auth.go ├── model ├── crud.go └── todo.go ├── pocketbase.json └── public ├── index.js └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | pb_data/ 2 | *_templ.go -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Etienne Gobeli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | dev: 2 | npx nodemon --signal SIGTERM -e "templ go" -x "templ generate && go run main.go serve" -i "**/*_templ.go" 3 | 4 | generate: 5 | templ generate 6 | 7 | build: generate 8 | go build 9 | 10 | run: generate 11 | go run main.go serve -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pocketbase x HTMX 2 | 3 | This is a small demo on how to use [pocketbase](https://pocketbase.io/) as a framework, with [templ](https://templ.guide/) and [htmx](https://htmx.org/). 4 | 5 | ## Prerequisites 6 | - templ installed 7 | - node/npx installed (only for dev) 8 | 9 | ## Setup 10 | If you have node installed you can start the dev server with `make dev`, else run `make run` 11 | 12 | After setting up pocketbase import the pocketbase.json file and add a user in the user collection. 13 | 14 | Then you can checkout the small demo up under http://localhost:8090/auth/login and login with your newly created user. 15 | -------------------------------------------------------------------------------- /app/main.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/gobeli/pocketbase-htmx/middleware" 5 | "github.com/labstack/echo/v5" 6 | "github.com/pocketbase/pocketbase" 7 | "github.com/pocketbase/pocketbase/core" 8 | ) 9 | 10 | func InitAppRoutes(e *core.ServeEvent, pb *pocketbase.PocketBase) { 11 | appGroup := e.Router.Group("/app", middleware.LoadAuthContextFromCookie(pb), middleware.AuthGuard) 12 | 13 | appGroup.GET("", func(c echo.Context) error { 14 | return c.Redirect(303, "profile") 15 | }) 16 | appGroup.GET("/profile", ProfileGet) 17 | appGroup.GET("/todos", TodosGet(e)) 18 | appGroup.GET("/todos/add", TodoAddGet) 19 | appGroup.POST("/todos/add", TodoAddPost(e)) 20 | appGroup.POST("/todos/:id/delete", TodoDelete(e)) 21 | } 22 | -------------------------------------------------------------------------------- /app/profile.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/gobeli/pocketbase-htmx/lib" 5 | "github.com/labstack/echo/v5" 6 | "github.com/pocketbase/pocketbase/apis" 7 | "github.com/pocketbase/pocketbase/models" 8 | ) 9 | 10 | func ProfileGet(c echo.Context) error { 11 | var record *models.Record = c.Get(apis.ContextAuthRecordKey).(*models.Record) 12 | return lib.Render(c, 200, Profile(record)) 13 | } 14 | -------------------------------------------------------------------------------- /app/profile.templ: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/pocketbase/pocketbase/models" 5 | "github.com/gobeli/pocketbase-htmx/lib" 6 | ) 7 | 8 | templ Profile(user *models.Record) { 9 | @lib.BaseLayout() { 10 |

{user.Username()}

11 |

Id: {user.GetId()}

12 |
13 | 14 |
15 | Todos 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/todos.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/a-h/templ" 5 | validation "github.com/go-ozzo/ozzo-validation/v4" 6 | "github.com/gobeli/pocketbase-htmx/lib" 7 | "github.com/gobeli/pocketbase-htmx/model" 8 | "github.com/labstack/echo/v5" 9 | "github.com/pocketbase/pocketbase/apis" 10 | "github.com/pocketbase/pocketbase/core" 11 | "github.com/pocketbase/pocketbase/models" 12 | ) 13 | 14 | func TodosGet(e *core.ServeEvent) func(echo.Context) error { 15 | return func(c echo.Context) error { 16 | var record *models.Record = c.Get(apis.ContextAuthRecordKey).(*models.Record) 17 | todos, err := (&model.Todo{}).FindAll(e.App.Dao(), record) 18 | 19 | if err != nil { 20 | return err 21 | } 22 | 23 | return lib.Render(c, 200, TodosList(todos)) 24 | } 25 | } 26 | 27 | func TodoDelete(e *core.ServeEvent) func(echo.Context) error { 28 | return func(c echo.Context) error { 29 | var authRecord *models.Record = c.Get(apis.ContextAuthRecordKey).(*models.Record) 30 | 31 | id := c.PathParam("id") 32 | todo := model.Todo{} 33 | err := (&todo).FindById(e.App.Dao(), authRecord, id) 34 | 35 | if err == nil { 36 | err = todo.Delete(e.App.Dao()) 37 | } 38 | 39 | if err != nil { 40 | c.NoContent(400) 41 | return nil 42 | } 43 | 44 | return lib.HtmxRedirect(c, "/app/todos") 45 | } 46 | } 47 | 48 | type AddTodoFormValue struct { 49 | name string 50 | } 51 | 52 | func (atfv AddTodoFormValue) Validate() error { 53 | return validation.ValidateStruct(&atfv, 54 | validation.Field(&atfv.name, validation.Required, validation.Length(1, 50)), 55 | ) 56 | } 57 | 58 | func TodoAddGet(c echo.Context) error { 59 | return lib.Render(c, 200, TodoAdd(nil, nil)) 60 | } 61 | 62 | func TodoAddPost(e *core.ServeEvent) func(echo.Context) error { 63 | return func(c echo.Context) error { 64 | var authRecord *models.Record = c.Get(apis.ContextAuthRecordKey).(*models.Record) 65 | todo := model.Todo{Name: c.FormValue("name"), User: authRecord.Id} 66 | err := todo.Save(e.App.Dao()) 67 | 68 | if err == nil { 69 | return lib.HtmxRedirect(c, "/app/todos") 70 | } 71 | 72 | component := lib.HtmxRender( 73 | c, 74 | func() templ.Component { return TodoAddForm(&todo, err) }, 75 | func() templ.Component { return TodoAdd(&todo, err) }, 76 | ) 77 | 78 | return lib.Render(c, 200, component) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/todos.templ: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/gobeli/pocketbase-htmx/lib" 5 | "github.com/gobeli/pocketbase-htmx/model" 6 | ) 7 | 8 | templ TodosList(todos []*model.Todo) { 9 | @lib.BaseLayout() { 10 |

Todos

11 | 12 | 22 | 23 | Add todo 24 | } 25 | } 26 | 27 | templ TodoAdd(form *model.Todo, err error) { 28 | @lib.BaseLayout() { 29 | @TodoAddForm(form, err) 30 | } 31 | } 32 | 33 | templ TodoAddForm(form *model.Todo, err error) { 34 |
35 |
36 | 37 | 42 |
43 | if err != nil { 44 |

{err.Error()}

45 | } 46 | 47 |
48 | } 49 | -------------------------------------------------------------------------------- /auth/login.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/a-h/templ" 7 | validation "github.com/go-ozzo/ozzo-validation/v4" 8 | "github.com/gobeli/pocketbase-htmx/lib" 9 | "github.com/gobeli/pocketbase-htmx/middleware" 10 | 11 | "github.com/labstack/echo/v5" 12 | "github.com/pocketbase/pocketbase/apis" 13 | "github.com/pocketbase/pocketbase/core" 14 | ) 15 | 16 | type LoginFormValue struct { 17 | username string 18 | password string 19 | } 20 | 21 | func (lfv LoginFormValue) Validate() error { 22 | return validation.ValidateStruct(&lfv, 23 | validation.Field(&lfv.username, validation.Required, validation.Length(3, 50)), 24 | validation.Field(&lfv.password, validation.Required), 25 | ) 26 | } 27 | 28 | func getLoginFormValue(c echo.Context) LoginFormValue { 29 | return LoginFormValue{ 30 | username: c.FormValue("username"), 31 | password: c.FormValue("password"), 32 | } 33 | } 34 | 35 | func RegisterLoginRoutes(e *core.ServeEvent, group echo.Group) { 36 | group.GET("/login", func(c echo.Context) error { 37 | if c.Get(apis.ContextAuthRecordKey) != nil { 38 | return c.Redirect(302, "/app/profile") 39 | } 40 | 41 | return lib.Render(c, 200, Login(LoginFormValue{}, nil)) 42 | }) 43 | 44 | group.POST("/login", func(c echo.Context) error { 45 | form := getLoginFormValue(c) 46 | err := form.Validate() 47 | 48 | if err == nil { 49 | err = lib.Login(e, c, form.username, form.password) 50 | } 51 | 52 | if err != nil { 53 | component := lib.HtmxRender( 54 | c, 55 | func() templ.Component { return LoginForm(form, err) }, 56 | func() templ.Component { return Login(form, err) }, 57 | ) 58 | return lib.Render(c, 200, component) 59 | } 60 | 61 | return lib.HtmxRedirect(c, "/app/profile") 62 | }) 63 | 64 | group.POST("/logout", func(c echo.Context) error { 65 | c.SetCookie(&http.Cookie{ 66 | Name: middleware.AuthCookieName, 67 | Value: "", 68 | Path: "/", 69 | Secure: true, 70 | HttpOnly: true, 71 | MaxAge: -1, 72 | }) 73 | 74 | return lib.HtmxRedirect(c, "/auth/login") 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /auth/login.templ: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "github.com/gobeli/pocketbase-htmx/lib" 4 | 5 | templ Login(form LoginFormValue, err error) { 6 | @lib.BaseLayout() { 7 | @LoginForm(form, err) 8 | } 9 | } 10 | 11 | templ LoginForm(form LoginFormValue, err error) { 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 | if err != nil { 22 |

{err.Error()}

23 | } 24 | 25 |
26 | } -------------------------------------------------------------------------------- /auth/register.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "github.com/a-h/templ" 5 | validation "github.com/go-ozzo/ozzo-validation/v4" 6 | "github.com/gobeli/pocketbase-htmx/lib" 7 | 8 | "github.com/labstack/echo/v5" 9 | "github.com/pocketbase/pocketbase/apis" 10 | "github.com/pocketbase/pocketbase/core" 11 | ) 12 | 13 | type RegisterFormValue struct { 14 | username string 15 | password string 16 | passwordRepeat string 17 | } 18 | 19 | func (lfv RegisterFormValue) Validate() error { 20 | return validation.ValidateStruct(&lfv, 21 | validation.Field(&lfv.username, validation.Required, validation.Length(3, 50)), 22 | validation.Field(&lfv.password, validation.Required), 23 | ) 24 | } 25 | 26 | func getRegisterFormValue(c echo.Context) RegisterFormValue { 27 | return RegisterFormValue{ 28 | username: c.FormValue("username"), 29 | password: c.FormValue("password"), 30 | passwordRepeat: c.FormValue("passwordRepeat"), 31 | } 32 | } 33 | 34 | func RegisterRegisterRoutes(e *core.ServeEvent, group echo.Group) { 35 | group.GET("/register", func(c echo.Context) error { 36 | if c.Get(apis.ContextAuthRecordKey) != nil { 37 | return c.Redirect(302, "/app/profile") 38 | } 39 | 40 | return lib.Render(c, 200, Register(RegisterFormValue{}, nil)) 41 | }) 42 | 43 | group.POST("/register", func(c echo.Context) error { 44 | form := getRegisterFormValue(c) 45 | err := form.Validate() 46 | 47 | if err == nil { 48 | err = lib.Register(e, c, form.username, form.password, form.passwordRepeat) 49 | } 50 | 51 | if err != nil { 52 | component := lib.HtmxRender( 53 | c, 54 | func() templ.Component { return RegisterForm(form, err) }, 55 | func() templ.Component { return Register(form, err) }, 56 | ) 57 | return lib.Render(c, 200, component) 58 | } 59 | 60 | return lib.HtmxRedirect(c, "/app/profile") 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /auth/register.templ: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import "github.com/gobeli/pocketbase-htmx/lib" 4 | 5 | templ Register(form RegisterFormValue, err error) { 6 | @lib.BaseLayout() { 7 | @RegisterForm(form, err) 8 | } 9 | } 10 | 11 | templ RegisterForm(form RegisterFormValue, err error) { 12 |
13 |
14 | 15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 | 24 |
25 | if err != nil { 26 |

{err.Error()}

27 | } 28 | 29 |
30 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gobeli/pocketbase-htmx 2 | 3 | go 1.21.4 4 | 5 | require ( 6 | github.com/a-h/templ v0.2.543 7 | github.com/go-ozzo/ozzo-validation/v4 v4.3.0 8 | github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 9 | github.com/pocketbase/dbx v1.10.1 10 | github.com/pocketbase/pocketbase v0.21.1 11 | ) 12 | 13 | require ( 14 | github.com/AlecAivazis/survey/v2 v2.3.7 // indirect 15 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 16 | github.com/aws/aws-sdk-go v1.50.2 // indirect 17 | github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect 18 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect 19 | github.com/aws/aws-sdk-go-v2/config v1.26.6 // indirect 20 | github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect 21 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect 22 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.14 // indirect 23 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect 24 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect 25 | github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // indirect 26 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 // indirect 27 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect 28 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 // indirect 29 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect 30 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 // indirect 31 | github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 // indirect 32 | github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect 33 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect 34 | github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect 35 | github.com/aws/smithy-go v1.19.0 // indirect 36 | github.com/disintegration/imaging v1.6.2 // indirect 37 | github.com/domodwyer/mailyak/v3 v3.6.2 // indirect 38 | github.com/dustin/go-humanize v1.0.1 // indirect 39 | github.com/fatih/color v1.16.0 // indirect 40 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 41 | github.com/ganigeorgiev/fexpr v0.4.0 // indirect 42 | github.com/goccy/go-json v0.10.2 // indirect 43 | github.com/golang-jwt/jwt/v4 v4.5.0 // indirect 44 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 45 | github.com/golang/protobuf v1.5.3 // indirect 46 | github.com/google/uuid v1.6.0 // indirect 47 | github.com/google/wire v0.5.0 // indirect 48 | github.com/googleapis/gax-go/v2 v2.12.0 // indirect 49 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 50 | github.com/jmespath/go-jmespath v0.4.0 // indirect 51 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 52 | github.com/mattn/go-colorable v0.1.13 // indirect 53 | github.com/mattn/go-isatty v0.0.20 // indirect 54 | github.com/mattn/go-sqlite3 v1.14.19 // indirect 55 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect 56 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect 57 | github.com/spf13/cast v1.6.0 // indirect 58 | github.com/spf13/cobra v1.8.0 // indirect 59 | github.com/spf13/pflag v1.0.5 // indirect 60 | github.com/valyala/bytebufferpool v1.0.0 // indirect 61 | github.com/valyala/fasttemplate v1.2.2 // indirect 62 | go.opencensus.io v0.24.0 // indirect 63 | gocloud.dev v0.36.0 // indirect 64 | golang.org/x/crypto v0.18.0 // indirect 65 | golang.org/x/image v0.15.0 // indirect 66 | golang.org/x/mod v0.14.0 // indirect 67 | golang.org/x/net v0.20.0 // indirect 68 | golang.org/x/oauth2 v0.16.0 // indirect 69 | golang.org/x/sync v0.6.0 // indirect 70 | golang.org/x/sys v0.16.0 // indirect 71 | golang.org/x/term v0.16.0 // indirect 72 | golang.org/x/text v0.14.0 // indirect 73 | golang.org/x/time v0.5.0 // indirect 74 | golang.org/x/tools v0.17.0 // indirect 75 | golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect 76 | google.golang.org/api v0.157.0 // indirect 77 | google.golang.org/appengine v1.6.8 // indirect 78 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect 79 | google.golang.org/grpc v1.61.0 // indirect 80 | google.golang.org/protobuf v1.32.0 // indirect 81 | lukechampine.com/uint128 v1.3.0 // indirect 82 | modernc.org/cc/v3 v3.41.0 // indirect 83 | modernc.org/ccgo/v3 v3.16.15 // indirect 84 | modernc.org/libc v1.40.7 // indirect 85 | modernc.org/mathutil v1.6.0 // indirect 86 | modernc.org/memory v1.7.2 // indirect 87 | modernc.org/opt v0.1.3 // indirect 88 | modernc.org/sqlite v1.28.0 // indirect 89 | modernc.org/strutil v1.2.0 // indirect 90 | modernc.org/token v1.1.0 // indirect 91 | ) 92 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= 3 | cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= 4 | cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= 5 | cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= 6 | cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= 7 | cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 8 | cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= 9 | cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= 10 | cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= 11 | cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= 12 | github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= 13 | github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= 14 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 15 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= 16 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= 17 | github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk= 18 | github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA= 19 | github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= 20 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= 21 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 22 | github.com/aws/aws-sdk-go v1.50.2 h1:/vS+Uhv2FPcqcTxBmgT3tvvN5q6pMAKu6QXltgXlGgo= 23 | github.com/aws/aws-sdk-go v1.50.2/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= 24 | github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= 25 | github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= 26 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs= 27 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo= 28 | github.com/aws/aws-sdk-go-v2/config v1.26.6 h1:Z/7w9bUqlRI0FFQpetVuFYEsjzE3h7fpU6HuGmfPL/o= 29 | github.com/aws/aws-sdk-go-v2/config v1.26.6/go.mod h1:uKU6cnDmYCvJ+pxO9S4cWDb2yWWIH5hra+32hVh1MI4= 30 | github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= 31 | github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= 32 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= 33 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= 34 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.14 h1:ogP1WgyvN/qxPJkgtFMD7G2eKb5p/61Jomx+nIHXUQ4= 35 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.14/go.mod h1:nYd/WmIrXlBHW/5QwrZP81/Gz08wKi87nV6EI1kmqx4= 36 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= 37 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= 38 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= 39 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= 40 | github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 h1:n3GDfwqF2tzEkXlv5cuy4iy7LpKDtqDMcNLfZDu9rls= 41 | github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= 42 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10 h1:5oE2WzJE56/mVveuDZPJESKlg/00AaS2pY2QZcnxg4M= 43 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.10/go.mod h1:FHbKWQtRBYUz4vO5WBWjzMD2by126ny5y/1EoaWoLfI= 44 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= 45 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= 46 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 h1:L0ai8WICYHozIKK+OtPzVJBugL7culcuM4E4JOpIEm8= 47 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10/go.mod h1:byqfyxJBshFk0fF9YmK0M0ugIO8OWjzH2T3bPG4eGuA= 48 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= 49 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= 50 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 h1:KOxnQeWy5sXyS37fdKEvAsGHOr9fa/qvwxfJurR/BzE= 51 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10/go.mod h1:jMx5INQFYFYB3lQD9W0D8Ohgq6Wnl7NYOJ2TQndbulI= 52 | github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 h1:PJTdBMsyvra6FtED7JZtDpQrIAflYDHFoZAu/sKYkwU= 53 | github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0/go.mod h1:4qXHrG1Ne3VGIMZPCB8OjH/pLFO94sKABIusjh0KWPU= 54 | github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= 55 | github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= 56 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA= 57 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= 58 | github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= 59 | github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= 60 | github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= 61 | github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= 62 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 63 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 64 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 65 | github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 66 | github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= 67 | github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 68 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 69 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 70 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 71 | github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= 72 | github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= 73 | github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8= 74 | github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c= 75 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 76 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 77 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 78 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 79 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 80 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 81 | github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 82 | github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 83 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 84 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 85 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 86 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 87 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= 88 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= 89 | github.com/ganigeorgiev/fexpr v0.4.0 h1:ojitI+VMNZX/odeNL1x3RzTTE8qAIVvnSSYPNAnQFDI= 90 | github.com/ganigeorgiev/fexpr v0.4.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE= 91 | github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= 92 | github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 93 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 94 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 95 | github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= 96 | github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= 97 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 98 | github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= 99 | github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 100 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 101 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 102 | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 103 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 104 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 105 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 106 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 107 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 108 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 109 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 110 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 111 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 112 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 113 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 114 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 115 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 116 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 117 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 118 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 119 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 120 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 121 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 122 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 123 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 124 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 125 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 126 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 127 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 128 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 129 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 130 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 131 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 132 | github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE= 133 | github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk= 134 | github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= 135 | github.com/google/go-replayers/httpreplay v1.2.0/go.mod h1:WahEFFZZ7a1P4VM1qEeHy+tME4bwyqPcwWbNlUI1Mcg= 136 | github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= 137 | github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 138 | github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= 139 | github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= 140 | github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= 141 | github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= 142 | github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 143 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 144 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 145 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 146 | github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= 147 | github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= 148 | github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= 149 | github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= 150 | github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= 151 | github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= 152 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= 153 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= 154 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 155 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 156 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 157 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 158 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 159 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 160 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 161 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 162 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 163 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 164 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 165 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 166 | github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 h1:FwuzbVh87iLiUQj1+uQUsuw9x5t9m5n5g7rG7o4svW4= 167 | github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8= 168 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 169 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 170 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 171 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 172 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 173 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 174 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 175 | github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= 176 | github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= 177 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 178 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= 179 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 180 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 181 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 182 | github.com/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA= 183 | github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs= 184 | github.com/pocketbase/pocketbase v0.21.1 h1:yLDEdGod6ykf9pnt+a2lg21YYS9WxuyaC7ggxxwzJCw= 185 | github.com/pocketbase/pocketbase v0.21.1/go.mod h1:V3/y5RJFddyN0I+/cmMboKq5Tv3UHpTuM+R8/Qo39OU= 186 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 187 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 188 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 189 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 190 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 191 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 192 | github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= 193 | github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 194 | github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= 195 | github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= 196 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 197 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 198 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 199 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 200 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 201 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 202 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 203 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 204 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 205 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 206 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= 207 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 208 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 209 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 210 | github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 211 | github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 212 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 213 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 214 | go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 215 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= 216 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= 217 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= 218 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= 219 | go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= 220 | go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= 221 | go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= 222 | go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= 223 | go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= 224 | go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= 225 | gocloud.dev v0.36.0 h1:q5zoXux4xkOZP473e1EZbG8Gq9f0vlg1VNH5Du/ybus= 226 | gocloud.dev v0.36.0/go.mod h1:bLxah6JQVKBaIxzsr5BQLYB4IYdWHkMZdzCXlo6F0gg= 227 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 228 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 229 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 230 | golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= 231 | golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= 232 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 233 | golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 234 | golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= 235 | golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= 236 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 237 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 238 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 239 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 240 | golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= 241 | golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 242 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 243 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 244 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 245 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 246 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 247 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 248 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 249 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 250 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 251 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 252 | golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= 253 | golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= 254 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 255 | golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= 256 | golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= 257 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 258 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 259 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 260 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 261 | golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= 262 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 263 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 264 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 265 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 266 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 267 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 268 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 269 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 270 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 271 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 272 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 273 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 274 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 275 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 276 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 277 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 278 | golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= 279 | golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= 280 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 281 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 282 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 283 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 284 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 285 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 286 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 287 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 288 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 289 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 290 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 291 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 292 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 293 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 294 | golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 295 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 296 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 297 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 298 | golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= 299 | golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= 300 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 301 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 302 | golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 303 | golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 304 | google.golang.org/api v0.157.0 h1:ORAeqmbrrozeyw5NjnMxh7peHO0UzV4wWYSwZeCUb20= 305 | google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= 306 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 307 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 308 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 309 | google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= 310 | google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= 311 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 312 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 313 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 314 | google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg= 315 | google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= 316 | google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= 317 | google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= 318 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= 319 | google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= 320 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 321 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 322 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 323 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 324 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 325 | google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= 326 | google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= 327 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 328 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 329 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 330 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 331 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 332 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 333 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 334 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 335 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 336 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 337 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 338 | google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= 339 | google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 340 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 341 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 342 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 343 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 344 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 345 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 346 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 347 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 348 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 349 | lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= 350 | lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= 351 | modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= 352 | modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= 353 | modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= 354 | modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= 355 | modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= 356 | modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= 357 | modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= 358 | modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= 359 | modernc.org/libc v1.40.7 h1:oeLS0G067ZqUu+v143Dqad0btMfKmNS7SuOsnkq0Ysg= 360 | modernc.org/libc v1.40.7/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= 361 | modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= 362 | modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= 363 | modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= 364 | modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= 365 | modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= 366 | modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= 367 | modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= 368 | modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= 369 | modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= 370 | modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= 371 | modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= 372 | modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= 373 | modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= 374 | modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= 375 | modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= 376 | modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= 377 | -------------------------------------------------------------------------------- /lib/auth.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/gobeli/pocketbase-htmx/middleware" 8 | "github.com/labstack/echo/v5" 9 | "github.com/pocketbase/pocketbase/core" 10 | "github.com/pocketbase/pocketbase/models" 11 | "github.com/pocketbase/pocketbase/tokens" 12 | ) 13 | 14 | type Users struct { 15 | models.Record 16 | } 17 | 18 | func (*Users) TableName() string { 19 | return "users" 20 | } 21 | 22 | func Login(e *core.ServeEvent, c echo.Context, username string, password string) error { 23 | user, err := e.App.Dao().FindAuthRecordByUsername("users", username) 24 | if err != nil { 25 | return fmt.Errorf("Login failed") 26 | } 27 | 28 | valid := user.ValidatePassword(password) 29 | if !valid { 30 | return fmt.Errorf("Login failed") 31 | } 32 | 33 | return setAuthToken(e.App, c, user) 34 | } 35 | 36 | func Register(e *core.ServeEvent, c echo.Context, username string, password string, passwordRepeat string) error { 37 | user, _ := e.App.Dao().FindAuthRecordByUsername("users", username) 38 | if user != nil { 39 | return fmt.Errorf("username already taken") 40 | } 41 | 42 | if password != passwordRepeat { 43 | return fmt.Errorf("passwords don't match") 44 | } 45 | 46 | collection, err := e.App.Dao().FindCollectionByNameOrId("users") 47 | if err != nil { 48 | return err 49 | } 50 | 51 | newUser := models.NewRecord(collection) 52 | newUser.SetPassword(password) 53 | newUser.SetUsername(username) 54 | 55 | if err = e.App.Dao().SaveRecord(newUser); err != nil { 56 | return err 57 | } 58 | 59 | return setAuthToken(e.App, c, newUser) 60 | } 61 | 62 | func setAuthToken(app core.App, c echo.Context, user *models.Record) error { 63 | s, tokenErr := tokens.NewRecordAuthToken(app, user) 64 | if tokenErr != nil { 65 | return fmt.Errorf("Login failed") 66 | } 67 | 68 | c.SetCookie(&http.Cookie{ 69 | Name: middleware.AuthCookieName, 70 | Value: s, 71 | Path: "/", 72 | Secure: true, 73 | HttpOnly: true, 74 | }) 75 | 76 | return nil 77 | } 78 | -------------------------------------------------------------------------------- /lib/base.templ: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | templ BaseLayout() { 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | ECHO 13 | 14 | 15 |
16 | { children... } 17 |
18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lib/htmx.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "github.com/a-h/templ" 5 | "github.com/labstack/echo/v5" 6 | ) 7 | 8 | func IsHtmxRequest(c echo.Context) bool { 9 | return c.Request().Header.Get("HX-Request") == "true" 10 | } 11 | 12 | type TemplRenderFunc func() templ.Component 13 | 14 | func HtmxRender(c echo.Context, htmxTemplate TemplRenderFunc, nonHtmxTemplate TemplRenderFunc) templ.Component { 15 | if IsHtmxRequest(c) { 16 | return htmxTemplate() 17 | } else { 18 | return nonHtmxTemplate() 19 | } 20 | } 21 | 22 | func HtmxRedirect(c echo.Context, path string) error { 23 | if IsHtmxRequest(c) { 24 | c.Response().Header().Set("HX-Location", path) 25 | return c.NoContent(204) 26 | } 27 | 28 | return c.Redirect(302, path) 29 | } 30 | -------------------------------------------------------------------------------- /lib/render.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/a-h/templ" 8 | "github.com/labstack/echo/v5" 9 | ) 10 | 11 | func Render(ctx echo.Context, status int, t templ.Component) error { 12 | ctx.Response().Writer.WriteHeader(status) 13 | 14 | err := t.Render(context.Background(), ctx.Response().Writer) 15 | if err != nil { 16 | return ctx.String(http.StatusInternalServerError, "failed to render response template") 17 | } 18 | 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/gobeli/pocketbase-htmx/app" 7 | "github.com/gobeli/pocketbase-htmx/auth" 8 | "github.com/gobeli/pocketbase-htmx/middleware" 9 | 10 | "github.com/pocketbase/pocketbase" 11 | "github.com/pocketbase/pocketbase/core" 12 | ) 13 | 14 | const AuthCookieName = "Auth" 15 | 16 | func main() { 17 | pb := pocketbase.New() 18 | 19 | // serves static files from the provided public dir (if exists) 20 | pb.OnBeforeServe().Add(func(e *core.ServeEvent) error { 21 | e.Router.Static("/public", "public") 22 | 23 | authGroup := e.Router.Group("/auth", middleware.LoadAuthContextFromCookie(pb)) 24 | auth.RegisterLoginRoutes(e, *authGroup) 25 | auth.RegisterRegisterRoutes(e, *authGroup) 26 | 27 | app.InitAppRoutes(e, pb) 28 | return nil 29 | }) 30 | 31 | if err := pb.Start(); err != nil { 32 | log.Fatal(err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /middleware/auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/labstack/echo/v5" 5 | "github.com/pocketbase/pocketbase/apis" 6 | "github.com/pocketbase/pocketbase/core" 7 | ) 8 | 9 | const AuthCookieName = "Auth" 10 | 11 | func LoadAuthContextFromCookie(app core.App) echo.MiddlewareFunc { 12 | return func(next echo.HandlerFunc) echo.HandlerFunc { 13 | return func(c echo.Context) error { 14 | tokenCookie, err := c.Request().Cookie(AuthCookieName) 15 | if err != nil { 16 | return next(c) 17 | } 18 | 19 | token := tokenCookie.Value 20 | record, err := app.Dao().FindAuthRecordByToken( 21 | token, 22 | app.Settings().RecordAuthToken.Secret, 23 | ) 24 | 25 | if err != nil { 26 | return next(c) 27 | } 28 | 29 | c.Set(apis.ContextAuthRecordKey, record) 30 | return next(c) 31 | } 32 | } 33 | } 34 | 35 | func AuthGuard(next echo.HandlerFunc) echo.HandlerFunc { 36 | return func(c echo.Context) error { 37 | record := c.Get(apis.ContextAuthRecordKey) 38 | 39 | if record == nil { 40 | return c.Redirect(302, "/auth/login") 41 | } 42 | 43 | return next(c) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /model/crud.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/pocketbase/dbx" 7 | "github.com/pocketbase/pocketbase/daos" 8 | "github.com/pocketbase/pocketbase/models" 9 | ) 10 | 11 | type Crud interface { 12 | models.Model 13 | GetUser() string 14 | Validate() error 15 | } 16 | 17 | func FindAll[C Crud](model Crud, dao *daos.Dao, authRecord *models.Record) ([]C, error) { 18 | items := []C{} 19 | error := dao.ModelQuery(model).Where(dbx.NewExp("user = {:id}", dbx.Params{"id": authRecord.Id})).All(&items) 20 | 21 | return items, error 22 | } 23 | 24 | func FindById(model Crud, dao *daos.Dao, authRecord *models.Record, id string) error { 25 | err := dao.FindById(model, id) 26 | 27 | if err != nil || model.GetUser() != authRecord.Id { 28 | return fmt.Errorf("record not found") 29 | } 30 | 31 | return nil 32 | } 33 | 34 | func Save(model Crud, dao *daos.Dao) error { 35 | err := model.Validate() 36 | if err == nil { 37 | return dao.Save(model) 38 | } 39 | 40 | return err 41 | } 42 | -------------------------------------------------------------------------------- /model/todo.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | validation "github.com/go-ozzo/ozzo-validation/v4" 5 | "github.com/pocketbase/pocketbase/daos" 6 | "github.com/pocketbase/pocketbase/models" 7 | ) 8 | 9 | type Todo struct { 10 | models.BaseModel 11 | User string `db:"user" json:"user"` 12 | Name string `db:"name" json:"name"` 13 | } 14 | 15 | func (*Todo) TableName() string { 16 | return "todos" 17 | } 18 | 19 | func (todo *Todo) GetUser() string { 20 | return todo.User 21 | } 22 | 23 | func (todo *Todo) Validate() error { 24 | return validation.ValidateStruct(todo, 25 | validation.Field(&todo.Name, validation.Required, validation.Length(1, 50)), 26 | ) 27 | } 28 | 29 | func (todo *Todo) FindById(dao *daos.Dao, authRecord *models.Record, id string) error { 30 | return FindById(todo, dao, authRecord, id) 31 | } 32 | 33 | func (todo *Todo) FindAll(dao *daos.Dao, authRecord *models.Record) ([]*Todo, error) { 34 | return FindAll[*Todo](todo, dao, authRecord) 35 | } 36 | 37 | func (todo *Todo) Save(dao *daos.Dao) error { 38 | return Save(todo, dao) 39 | } 40 | 41 | func (todo *Todo) Delete(dao *daos.Dao) error { 42 | return dao.Delete(todo) 43 | } 44 | -------------------------------------------------------------------------------- /pocketbase.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "_pb_users_auth_", 4 | "name": "users", 5 | "type": "auth", 6 | "system": false, 7 | "schema": [ 8 | { 9 | "system": false, 10 | "id": "users_name", 11 | "name": "name", 12 | "type": "text", 13 | "required": false, 14 | "presentable": false, 15 | "unique": false, 16 | "options": { 17 | "min": null, 18 | "max": null, 19 | "pattern": "" 20 | } 21 | }, 22 | { 23 | "system": false, 24 | "id": "users_avatar", 25 | "name": "avatar", 26 | "type": "file", 27 | "required": false, 28 | "presentable": false, 29 | "unique": false, 30 | "options": { 31 | "mimeTypes": [ 32 | "image/jpeg", 33 | "image/png", 34 | "image/svg+xml", 35 | "image/gif", 36 | "image/webp" 37 | ], 38 | "thumbs": null, 39 | "maxSelect": 1, 40 | "maxSize": 5242880, 41 | "protected": false 42 | } 43 | } 44 | ], 45 | "indexes": [], 46 | "listRule": "id = @request.auth.id", 47 | "viewRule": "id = @request.auth.id", 48 | "createRule": "", 49 | "updateRule": "id = @request.auth.id", 50 | "deleteRule": "id = @request.auth.id", 51 | "options": { 52 | "allowEmailAuth": true, 53 | "allowOAuth2Auth": true, 54 | "allowUsernameAuth": true, 55 | "exceptEmailDomains": null, 56 | "manageRule": null, 57 | "minPasswordLength": 8, 58 | "onlyEmailDomains": null, 59 | "onlyVerified": false, 60 | "requireEmail": false 61 | } 62 | }, 63 | { 64 | "id": "s2ip6162d8weamd", 65 | "name": "todos", 66 | "type": "base", 67 | "system": false, 68 | "schema": [ 69 | { 70 | "system": false, 71 | "id": "7uszpfqr", 72 | "name": "name", 73 | "type": "text", 74 | "required": false, 75 | "presentable": false, 76 | "unique": false, 77 | "options": { 78 | "min": null, 79 | "max": null, 80 | "pattern": "" 81 | } 82 | }, 83 | { 84 | "system": false, 85 | "id": "zsetthmw", 86 | "name": "done", 87 | "type": "bool", 88 | "required": false, 89 | "presentable": false, 90 | "unique": false, 91 | "options": {} 92 | }, 93 | { 94 | "system": false, 95 | "id": "qycc2wvg", 96 | "name": "user", 97 | "type": "relation", 98 | "required": false, 99 | "presentable": false, 100 | "unique": false, 101 | "options": { 102 | "collectionId": "_pb_users_auth_", 103 | "cascadeDelete": false, 104 | "minSelect": null, 105 | "maxSelect": 1, 106 | "displayFields": null 107 | } 108 | } 109 | ], 110 | "indexes": [], 111 | "listRule": "user.id = @request.auth.id", 112 | "viewRule": "user.id = @request.auth.id", 113 | "createRule": "", 114 | "updateRule": "user.id = @request.auth.id", 115 | "deleteRule": "user.id = @request.auth.id", 116 | "options": {} 117 | } 118 | ] -------------------------------------------------------------------------------- /public/index.js: -------------------------------------------------------------------------------- 1 | htmx.config.globalViewTransitions = true 2 | document.body.addEventListener('htmx:beforeTransition', (e) => { 3 | console.log(e); 4 | if (e.target !== document.body) { 5 | e.preventDefault(); 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /public/style.css: -------------------------------------------------------------------------------- 1 | @keyframes fade-in { 2 | from { 3 | opacity: 0; 4 | } 5 | } 6 | 7 | @keyframes fade-out { 8 | to { 9 | opacity: 0; 10 | } 11 | } 12 | 13 | @keyframes slide-from-right { 14 | from { 15 | transform: translateX(90px); 16 | } 17 | } 18 | 19 | @keyframes slide-to-left { 20 | to { 21 | transform: translateX(-90px); 22 | } 23 | } 24 | 25 | /* define animations for the old and new content */ 26 | ::view-transition-old(slide-it) { 27 | animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out, 28 | 600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left; 29 | } 30 | ::view-transition-new(slide-it) { 31 | animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, 32 | 600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right; 33 | } 34 | 35 | /* tie the view transition to a given CSS class */ 36 | .sample-transition { 37 | view-transition-name: slide-it; 38 | } 39 | --------------------------------------------------------------------------------