├── .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 |
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 |
13 | for _, v := range todos {
14 |
15 |
19 |
20 | }
21 |
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 |
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 |
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 |
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 |
11 |
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 |
--------------------------------------------------------------------------------