Compare commits
1
Commits
main
..
2f577ce18a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f577ce18a |
-10
@@ -1,10 +0,0 @@
|
||||
.DS_Store
|
||||
.idea
|
||||
*.log
|
||||
tmp/
|
||||
|
||||
.go
|
||||
.direnv
|
||||
pb_data
|
||||
pages/static/public/out.css
|
||||
result
|
||||
@@ -1,8 +1,9 @@
|
||||
The MIT License (MIT) Copyright (c) 2023 - present, Efim Nefedov
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 efim
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
##
|
||||
# auth-pocketbase-attempt
|
||||
#
|
||||
# @file
|
||||
# @version 0.1
|
||||
TAILWIND_SRC = pages/input.css
|
||||
TEMPLATES = $(wildcard pages/templates/*.gohtml pages/templates/**/*.gohtml)
|
||||
TAILWIND_CONFIG = tailwind.config.js
|
||||
TAILWIND_OUT = pages/static/public/out.css
|
||||
BINARY_NAME = auth-pocketbase-attempt
|
||||
|
||||
.PHONY: build
|
||||
build: tailwindcss
|
||||
go build -o=./${BINARY_NAME} .
|
||||
|
||||
.PHONY: run
|
||||
run: tailwindcss
|
||||
go run . serve
|
||||
|
||||
# this will restart the server on source change
|
||||
# and will sometimes also recompile tailwind out.css which is needed for bundling
|
||||
.PHONY: run/live
|
||||
run/live:
|
||||
wgo -verbose -file=.go -file=.gohtml -file=tailwind.config.js make run
|
||||
|
||||
# this is a phony job
|
||||
# it gets executed every time it's called directly or as a dependency
|
||||
# but, if out.css is fresh enough no compilation is called
|
||||
.PHONY: tailwindcss
|
||||
tailwindcss: $(TAILWIND_OUT)
|
||||
|
||||
# this is a job for producing out.css
|
||||
# it's dependencies are files that should trigger compilation
|
||||
# if resulting file is fresher than all of these - no build necessary
|
||||
$(TAILWIND_OUT): $(TAILWIND_SRC) $(TEMPLATES) $(TAILWIND_CONFIG)
|
||||
tailwindcss -i $(TAILWIND_SRC) -o $(TAILWIND_OUT)
|
||||
|
||||
# end
|
||||
-266
@@ -1,266 +0,0 @@
|
||||
#+title: Readme
|
||||
* Go Server Side Rendering attempt with Pocketbase
|
||||
** Description
|
||||
Code in this repo is an attempt to implement a Server Side Rendered (go, templates, htmx) website that would allow signup / authentication via social oauth2 providers.
|
||||
|
||||
Using [[https://pocketbase.io/][pocketbase]] importing it as a framework, to have out-of-the box administration portal with user management, data api, logs.
|
||||
With everything being compiled into a single binary which is [[https://pocketbase.io/docs/going-to-production/][very easy to deploy]] thanks to pocketbase architecture.
|
||||
|
||||
Most common usage of pocketbase - to develop only front end and utilize api's, either json web api, or JS SDK \ Dark SDK.
|
||||
But ability to extend the pocketbase by importing it into go project allows to use it also as a pre-built backend for a server that serves SSR html pages.
|
||||
[[https://htmx.org/essays/][And it can feel really nice, especially if we can also make website feel responsive]].
|
||||
|
||||
* Building and deploying
|
||||
** Without nix
|
||||
1. Have required dependencies on PATH:
|
||||
- gnumake
|
||||
- go
|
||||
- tailwindcss
|
||||
2. use Makefile:
|
||||
#+begin_src bash
|
||||
make build
|
||||
#+end_src
|
||||
|
||||
It will build tailwindcss style, and then build a binary with all necessary files bundled.
|
||||
3. Use deployment guide for pocketbase
|
||||
https://pocketbase.io/docs/going-to-production/
|
||||
- either copy the binary to the production server
|
||||
- or write up a Dockerfile
|
||||
4. To run locally:
|
||||
#+begin_src bash
|
||||
auth-pocketbase-attempt serve
|
||||
#+end_src
|
||||
will start service on 127.0.0.1:8090
|
||||
can start on any port with argument `--http=127.0.0.1:9999`
|
||||
** With [[https://nixos.org/][nix]]
|
||||
*** nix build
|
||||
Will build default application, the server.
|
||||
Also packages all static resources directly into binary.
|
||||
To run:
|
||||
#+begin_src bash
|
||||
./result/bin/auth-pocketbase-attempt serve --dir=./pb_data
|
||||
#+end_src
|
||||
|
||||
Specifying pb_data from the root of the dev project, otherwise will try to create database and other things inside of the /nix/store
|
||||
*** deploy on [[https://www.tweag.io/blog/2020-05-25-flakes/][NixOS with flakes]]:
|
||||
Flake contains a NixOS module, for deployment on server with NixOS
|
||||
Module includes
|
||||
- systemd job for starting server on machine reboot, automatically restarting on errors, logging.
|
||||
- option to choose reverse proxy with nginx, or running pocketbase directly bound to port 443 and serving the domain name
|
||||
Which should make pocketbase handle it's own certificates (see step 3 [[https://pocketbase.io/docs/going-to-production][here]])
|
||||
|
||||
|
||||
Deployment process:
|
||||
1. taking flake as input
|
||||
#+begin_src nix
|
||||
inputs.go-ssr-oauth-attempt.url = "git+http://git.sunshine.industries/efim/go-ssr-pocketbase-oauth-attempt.git";
|
||||
#+end_src
|
||||
2. importing in the server config:
|
||||
#+begin_src nix
|
||||
imports = [
|
||||
inputs.go-ssr-oauth-attempt.nixosModules.x86_64-linux.auth-pocketbase-attempt
|
||||
]
|
||||
#+end_src
|
||||
3. setting options
|
||||
#+begin_src nix
|
||||
services.auth-pocketbase-attempt = {
|
||||
enable = true;
|
||||
host = "go-ssr-oauth-attempt.sunshine.industries";
|
||||
port = 45001;
|
||||
useHostTls = true;
|
||||
};
|
||||
#+end_src
|
||||
4. enabling TLS with lets encrypt for the server and opening https port:
|
||||
#+begin_src nix
|
||||
security.acme.acceptTerms = true;
|
||||
security.acme.defaults.email = "your@email.net";
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
#+end_src
|
||||
see https://nixos.org/manual/nixos/stable/#module-security-acme-nginx
|
||||
( and also same here https://nixos.wiki/wiki/Nginx )
|
||||
5. Apply config to your server.
|
||||
(i use [[https://github.com/serokell/deploy-rs][deploy-rs]], but simple nixos-rebuild switch via ssh is good)
|
||||
* Running during development
|
||||
** With nix
|
||||
Flake contains dev shell with all required dependencies,
|
||||
If you have
|
||||
#+begin_src nix
|
||||
direnv = {
|
||||
enable = true;
|
||||
nix-direnv.enable = true;
|
||||
};
|
||||
#+end_src
|
||||
on your machine, you can just `direnv allow` to have build dependencies automatically put on PATH when you enter the project directory.
|
||||
|
||||
Otherwise `nix develop` will put you into shell with all dependencies.
|
||||
|
||||
Then running:
|
||||
#+begin_src bash
|
||||
make run/live
|
||||
#+end_src
|
||||
will build and start the server,
|
||||
and will trigger rebuild and restart when files change,
|
||||
only rebuilding tailwindcss when templates or css input changes
|
||||
** Without nix
|
||||
You'll need to have all required dependencies:
|
||||
- gnumake
|
||||
to run Makefile that composes several build steps into single commands
|
||||
- go
|
||||
compiler
|
||||
- wgo
|
||||
for server recompilation and restart
|
||||
- gopls
|
||||
lsp server
|
||||
- semgrep
|
||||
some other lsp server which emacs asked me to install for go
|
||||
- tailwindcss
|
||||
to build output.css
|
||||
- prettier
|
||||
to format the .gohtml files
|
||||
|
||||
Then running:
|
||||
#+begin_src bash
|
||||
make run/live
|
||||
#+end_src
|
||||
will build and start the server,
|
||||
and will trigger rebuild and restart when files change,
|
||||
only rebuilding tailwindcss when templates or css input changes
|
||||
* Main parts:
|
||||
** Authentication middleware:
|
||||
Registering hooks:
|
||||
1) after successful auth to set the token into secure cookie:
|
||||
#+begin_src go
|
||||
// fires for every auth collection
|
||||
app.OnRecordAuthRequest().Add(func(e *core.RecordAuthEvent) error {
|
||||
e.HttpContext.SetCookie(&http.Cookie{
|
||||
Name: AuthCookieName,
|
||||
Value: e.Token,
|
||||
Path: "/",
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
#+end_src
|
||||
2) on call to any pocketbase endpoint to populate request context with parsed auth info:
|
||||
#+begin_src go
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
e.Router.Use(loadAuthContextFromCookie(app))
|
||||
return nil
|
||||
})
|
||||
|
||||
func loadAuthContextFromCookie(app core.App) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
tokenCookie, err := c.Request().Cookie(AuthCookieName)
|
||||
if err != nil || tokenCookie.Value == "" {
|
||||
return next(c) // no token cookie
|
||||
}
|
||||
|
||||
token := tokenCookie.Value
|
||||
|
||||
claims, _ := security.ParseUnverifiedJWT(token)
|
||||
tokenType := cast.ToString(claims["type"])
|
||||
|
||||
switch tokenType {
|
||||
case tokens.TypeAdmin:
|
||||
admin, err := app.Dao().FindAdminByToken(
|
||||
token,
|
||||
app.Settings().AdminAuthToken.Secret,
|
||||
)
|
||||
if err == nil && admin != nil {
|
||||
// "authenticate" the admin
|
||||
c.Set(apis.ContextAdminKey, admin)
|
||||
}
|
||||
|
||||
case tokens.TypeAuthRecord:
|
||||
record, err := app.Dao().FindAuthRecordByToken(
|
||||
token,
|
||||
app.Settings().RecordAuthToken.Secret,
|
||||
)
|
||||
if err == nil && record != nil {
|
||||
// "authenticate" the app user
|
||||
c.Set(apis.ContextAuthRecordKey, record)
|
||||
}
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
#+end_src
|
||||
|
||||
as taken from discussion: https://github.com/pocketbase/pocketbase/discussions/989#discussioncomment-4109411
|
||||
|
||||
Now when pages are served from routes registered in pocketbase - they will be able to access the auth info from context.
|
||||
And also any other json api request will be automatically authenticated with correct user.
|
||||
** Front end side of authentication
|
||||
Since on go side we hook into 'post successful auth' we can use "All in one (recommended)" [[https://pocketbase.io/docs/authentication/][way for oauth2 in the guide]].
|
||||
|
||||
In the js script we initialize pocketbase SDK,
|
||||
and for each existing oauth provider rendering a button that calls a method:
|
||||
#+begin_src js
|
||||
const authData = await pb.collection('users').authWithOAuth2({ provider: 'google' });
|
||||
#+end_src
|
||||
*** Authentication with passwords can also be coded
|
||||
With form making POST directly to existing json api (closer to HATEOAS)
|
||||
or js calls through SDK.
|
||||
And any successful auth should also call our middleware and set the cookie.
|
||||
|
||||
** Pages package
|
||||
With templates and static files (out.css and htmx.min.js) bundled into resulting binary with `embed.FS`
|
||||
|
||||
Having
|
||||
#+begin_src go
|
||||
app.OnBeforeServe().Add(getIndexPageRoute(app))
|
||||
#+end_src
|
||||
adds route that renders html and returns on some path of our site.
|
||||
Passing in `app` gives access to things like `app.DAO` for data querying.
|
||||
|
||||
** Changing error responses to html
|
||||
Registering `OnBeforeApiError` to change json response into html.
|
||||
This way errors are displayed to end users in a more friendly manner, good idea because usual usage of pocketbase is to have front-end application that would translate error jsons into human readable view.
|
||||
* Useful parts of documentation:
|
||||
** Things available in backend through `app`
|
||||
- [[https://pocketbase.io/docs/go-routing/][Routing]]
|
||||
registering new routes, reading path / query parameters
|
||||
- [[https://pocketbase.io/docs/go-event-hooks/][Database]]
|
||||
querying data
|
||||
- [[https://pocketbase.io/docs/go-migrations/][Migrations]]
|
||||
Current project doesn't include migrations,
|
||||
I didn't understand it all, but it seems that for the project that uses pocketbase as a framework migrations are generated in form of .go files.
|
||||
If you change tables in admin portal, the changes to them will be encoded as migration path.
|
||||
Which need to be imported somewhere in 'main' package, included into binary during compilation and automatically applied to production database after updated binary first runs.
|
||||
** [[https://pocketbase.io/docs/authentication/][Overview of authentication from the front end side]]
|
||||
** Tips on [[Things required for produ][going to production]]
|
||||
* Things which are not good right now
|
||||
** I'd like to figure out a better way to load js scripts
|
||||
Having them in 'base.gohtml' is ok, but it seems to much, maybe another template or something.
|
||||
Same with <nav> which has 2 scripts and seem big and unpleasant.
|
||||
Maybe hypersript would achieve same with couple lines of code, maybe there's some other fine art of adding js into htmx projects on go.
|
||||
** Building with both Makefile and nix derivation
|
||||
Having a Makefile is awesome for run/live
|
||||
which only triggers tailwind step if tailwind inputs have changed.
|
||||
|
||||
My previous attempt was to run 'wgo' directly:
|
||||
#+begin_src bash
|
||||
wgo -verbose -file=.go -file=.gohtml -file=tailwind.config.js tailwindcss -i ./pages/input.css -o pages/static/public/out.css :: go run . serve
|
||||
#+end_src
|
||||
|
||||
But this triggered tailwind on change of .go files with business logic, which took up time before service is available.
|
||||
|
||||
Unfortunately building go with dependencies is easy in nix (with 'buildGoModule'), but not trivial, because dependencies has to be pre-downloaded and set up as 'fixed output derivation'.
|
||||
|
||||
So I don't know of a way to just reuse Makefile for nix derivation.
|
||||
Thus build is described in two places independently.
|
||||
|
||||
And if any new build step is added, so that Makefile has to change - then i have to not to forget that nix derivation should also be changed.
|
||||
** Error pages
|
||||
Currently 'before error' makes error return a page that redirects to error page
|
||||
and error pages are in 'pages' module
|
||||
|
||||
But putting middleware into module 'pages/errors' and make it directly render error pages will be better.
|
||||
** Currently have all pages in one file
|
||||
and don't have separate rotues to return only <main> part of template for HX reqeusts that do 'hx-boost' switches.
|
||||
** Would be nice to somehow set up JS SDK dependency locally and serve it from static files
|
||||
This would reduce dependency on cdn
|
||||
-280
@@ -1,280 +0,0 @@
|
||||
#+title: Auth Notes
|
||||
* starting the pocketbase as framework
|
||||
https://pocketbase.io/docs/go-overview/
|
||||
* plan
|
||||
** DONE start pocketbase
|
||||
** DONE add middlewares for cookie session
|
||||
** DONE add index page, that will have either "current user" or 'login' link
|
||||
*** DONE let's add some content that only opens up when person is authed
|
||||
*** DONE also, how do i logout?
|
||||
separate route that deleted the cookie i guess.
|
||||
since auth is a jwt which would expire on its own
|
||||
and htmx get thingy, and reload i guess?
|
||||
** DONE 'login' link should open dialog with oauth providers
|
||||
so, i want a window with available oauth providers,
|
||||
to trigger the js code from example
|
||||
https://pocketbase.io/docs/authentication/
|
||||
( all in one, recommended )
|
||||
|
||||
let's get configured providers in the go code,
|
||||
add as slice of strings, and in template create buttons for each of those
|
||||
with js code from the example
|
||||
*** DONE in template range over enabled providers to create buttons for each
|
||||
*** DONE make dialog show on click of some element
|
||||
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog
|
||||
** DONE i guess i would also like to send htmx event for reloading the page
|
||||
on successful auth?
|
||||
now, why would logout work, and login not work?
|
||||
|
||||
eh, let's go back on body doing the hx-get on event?
|
||||
|
||||
maybe this is because of open dialog
|
||||
*** wait, maybe then returning from other auth middlewares will work
|
||||
no.
|
||||
for some reason
|
||||
#+begin_src go
|
||||
e.HttpContext.Response().Header().Add("HX-Trigger", "auth-change-event")
|
||||
#+end_src
|
||||
|
||||
this header when returned with response to request triggered by js, doesn't result in event being triggered,
|
||||
ok, i guess
|
||||
*** so yeah, uglier that i wanted
|
||||
wanted to have hx-get="/" hx-trigger="auth-change-event"
|
||||
and send these events from all auth middleware methods
|
||||
|
||||
https://htmx.org/docs/#response-headers
|
||||
|
||||
but on auth success, even though header is present in the response, no event is triggered
|
||||
( checked with event listener in console )
|
||||
so, yup. coupling between js code of oauth, middlewares and body tag. this seems like too much.
|
||||
|
||||
but it somewhat works
|
||||
|
||||
** DONE add one more page that checks auth
|
||||
and let's use existing middleware from framework documentation
|
||||
|
||||
with hx-boost things are well,
|
||||
but i also need header as fragment, so that opening in new tab would work.
|
||||
and all js imports and libraries that are required by all pages, should be in all templates
|
||||
|
||||
** DONE i suppose there has to be a base template then
|
||||
and now all since base template has Nav,
|
||||
i need to provide attibutes which are used there, huh
|
||||
well. hmmmmm. yeah, i guess
|
||||
** DONE add tailwind styling
|
||||
and wgo command should move from
|
||||
wgo -file=.gohtml -file=.go go run . serve
|
||||
|
||||
to
|
||||
wgo -verbose -file=.go -file=.gohtml -file=tailwind.config.js tailwindcss -i ./pages/input.css -o pages/static/public/out.css :: go run . serve
|
||||
*** DONE style pages
|
||||
*** DONE style dialog
|
||||
|
||||
** DONE i guess i'll want a makefile?
|
||||
then wgo could be build with makefile and run
|
||||
and nix packaging could be more straightforward, and not too prohibitive to those who don't use nix
|
||||
*** it seems that with MakeFile i could have go code depend on tailwind output
|
||||
and not have other way around,
|
||||
it should speed up the restart of the service in cases where only go code has changed.
|
||||
|
||||
also - i think i can have different build and run for go code, so yeah
|
||||
*** allright, it looks like people also do that
|
||||
https://www.alexedwards.net/blog/a-time-saving-makefile-for-your-go-projects
|
||||
*** some helpful things:
|
||||
https://makefiletutorial.com/
|
||||
|
||||
example of things for go
|
||||
https://earthly.dev/blog/golang-makefile/
|
||||
https://www.alexedwards.net/blog/a-time-saving-makefile-for-your-go-projects
|
||||
and i guess i could also search online for tailwindcss Makefile examples and tips
|
||||
|
||||
** DONE package static into single binary
|
||||
i guess already done?
|
||||
** DONE write nix build
|
||||
and it should be even easier with a Makefile?
|
||||
|
||||
https://ryantm.github.io/nixpkgs/stdenv/stdenv/
|
||||
|
||||
simple stdenv.mkDerivation calls generic builder which uses Makefile
|
||||
|
||||
now i have a problem with using go build in a homeless-shelter
|
||||
> failed to initialize build cache at /homeless-shelter/.cache/go-build: mkdir /homeless-shelter: permission denied
|
||||
*** well, especially with go.mod dependencies i'd need to use buildGoModule
|
||||
but
|
||||
[efim@chunky:~/Documents/personal/go-learning/auth-pocketbase-attempt]$ ./result/bin/auth-pocketbase-attempt serve
|
||||
2023/10/07 04:05:56 mkdir result/bin/pb_data: read-only file system
|
||||
|
||||
so, i need to pass some place in tmp? this is probably pocketbase settings, hopefully as command line argument
|
||||
|
||||
https://nixos.org/manual/nixpkgs/stable/#sec-language-go
|
||||
https://nixos.wiki/wiki/Go
|
||||
|
||||
so, if i call executable from somewhere, it looks for pb_data in current directory
|
||||
|
||||
but then for some reason
|
||||
[efim@chunky:~/Documents/personal/go-learning/auth-pocketbase-attempt]$ ./result/bin/auth-pocketbase-attempt serve
|
||||
2023/10/08 06:37:19 mkdir result/bin/pb_data: read-only file system
|
||||
|
||||
here it tries to init pb_data near the binary
|
||||
|
||||
this works:
|
||||
[efim@chunky:~/Documents/personal/go-learning/auth-pocketbase-attempt]$ ./result/bin/auth-pocketbase-attempt serve --dir=./pb_data
|
||||
|
||||
*** oh, i don't need to specify location of migrations.
|
||||
because they are static. and should be just present in the nix store
|
||||
|
||||
and --dir is already built in. nice
|
||||
|
||||
well, i don't see any pb_migrations in my project directory even though,
|
||||
i'm creating and updating the table
|
||||
maybe it's all in pb_data now?
|
||||
|
||||
if now - i'll need to add something like
|
||||
#+begin_src nix
|
||||
postBuild = ''
|
||||
cp pb_migration $out/bin/pb_migration
|
||||
'';
|
||||
#+end_src
|
||||
|
||||
*** so, if using as framework migrations are not automatically enabled?
|
||||
https://github.com/pocketbase/pocketbase/discussions/2218
|
||||
|
||||
https://pocketbase.io/docs/go-migrations/#enable-go-migrations
|
||||
The prebuilt executable enables the migrate command by default, but when you are extending PocketBase with Go you have to enable it manually
|
||||
*** now `nix build` produces the binary capable to run the site
|
||||
and
|
||||
#+begin_src bash
|
||||
./result/bin/auth-pocketbase-attempt serve --dir=./pb_data
|
||||
#+end_src
|
||||
is what i need for it to pick up pb_data from work directory, cool
|
||||
|
||||
** DONE write nixos module
|
||||
need to pass data and migration location as params
|
||||
and address on which to serve, cool
|
||||
i suppose
|
||||
but also nginx settins at the same time
|
||||
*** this is behavior of specifying the host and port:
|
||||
[efim@chunky:~/Documents/personal/go-learning/auth-pocketbase-attempt]$ sudo ./result/bin/auth-pocketbase-attempt serve --https=127.0.0.1:8090 --dir=./pb_data
|
||||
2023/10/08 12:58:04 Server started at https://127.0.0.1:8090
|
||||
├─ REST API: https://127.0.0.1:8090/api/
|
||||
└─ Admin UI: https://127.0.0.1:8090/_/
|
||||
^C
|
||||
[efim@chunky:~/Documents/personal/go-learning/auth-pocketbase-attempt]$ sudo ./result/bin/auth-pocketbase-attempt serve 127.0.0.1:8090 --dir=./pb_data
|
||||
2023/10/08 12:58:15 Server started at https://127.0.0.1:8090
|
||||
├─ REST API: https://127.0.0.1:8090/api/
|
||||
└─ Admin UI: https://127.0.0.1:8090/_/
|
||||
^C
|
||||
[efim@chunky:~/Documents/personal/go-learning/auth-pocketbase-attempt]$ sudo ./result/bin/auth-pocketbase-attempt serve --http=127.0.0.1:8090 --dir=./pb_data
|
||||
2023/10/08 12:58:20 Server started at http://127.0.0.1:8090
|
||||
├─ REST API: http://127.0.0.1:8090/api/
|
||||
└─ Admin UI: http://127.0.0.1:8090/_/
|
||||
*** by default - if host is present, serving on https.
|
||||
cool
|
||||
|
||||
oh, but if i'm using nginx i'll need my own certificate, that makes sence
|
||||
*** maybe things are ok?
|
||||
let's try to plaintext deploy?
|
||||
*** quoting of the '' in multiline string
|
||||
https://nixos.org/manual/nix/stable/language/values.html
|
||||
*** not accessible still
|
||||
sudo journalctl -u nginx --since "1 day ago"
|
||||
*** oh, i forgot to add subname in gandi ui
|
||||
now works
|
||||
*** now i need a way to pass in the hostname
|
||||
because front-end is setting up js 'new PocketBase' with 127.0.0.1 connection
|
||||
*** adding a custom flag:
|
||||
https://github.com/pocketbase/pocketbase/discussions/1900
|
||||
** DONE change some additional config to option :
|
||||
${optionalString config.proxyWebsockets ''
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
''}
|
||||
( also in planning poker repo )
|
||||
|
||||
https://github.com/NixOS/nixpkgs/blob/nixos-23.05/nixos/modules/services/web-servers/nginx/default.nix#L428
|
||||
|
||||
|
||||
** TODO add docker image from nix
|
||||
*** CANCELLED add cli for port and host
|
||||
** DONE add readme and comments
|
||||
*** DONE pupose of the code
|
||||
*** DONE how to build, install
|
||||
with and without nix
|
||||
*** DONE development things 'make run/live'
|
||||
*** DONE main parts:
|
||||
**** DONE auth middleware
|
||||
**** DONE using js auth
|
||||
**** DONE pages
|
||||
**** DONE error pages
|
||||
*** DONE links to main documentation:
|
||||
- [X] adding new auth providers
|
||||
- [X] adding middlewares and working with collections
|
||||
*** DONE things which aren't good here:
|
||||
- [X] error pages, i guess module in pages, but exposing before error hook themselves
|
||||
- [X] rendering full pages, not doing 'just main' for hx requests
|
||||
- [X] maybe serving js pocketbase from own static files?
|
||||
*** DONE comments on all main modules
|
||||
** DONE configure tls / ssl / https on franzk deployment
|
||||
https://nixos.org/manual/nixos/stable/#module-security-acme-nginx
|
||||
( and also same here https://nixos.wiki/wiki/Nginx )
|
||||
|
||||
can it be configured on render.com?
|
||||
omg
|
||||
line 112 & 113 in project config:
|
||||
http://git.sunshine.industries/efim/go-ssr-pocketbase-oauth-attempt/commit/875de35177462f21732e3ba108a94d77a543da05
|
||||
|
||||
and this in my server config:
|
||||
https://github.com/efim/dotfiles/commit/b3695148082d8c9850a781aaa7a88920bdb1fa7f
|
||||
|
||||
this is all that's needed to enable tls
|
||||
mind blown
|
||||
** DONE somehow set cookie to httpOnly & secure
|
||||
with ability to disable for development session
|
||||
*** a complication
|
||||
since i'm under the nginx, i can't just match on the serving address :
|
||||
#+begin_src
|
||||
[efim@franzk:~]$ systemctl status pb-auth-example-app.service
|
||||
● pb-auth-example-app.service - Exercise app auth-pocketbase-attempt
|
||||
Loaded: loaded (/etc/systemd/system/pb-auth-example-app.service; enabled; preset: enabled)
|
||||
Active: active (running) since Mon 2023-10-09 04:29:20 UTC; 1min 17s ago
|
||||
Main PID: 411857 (auth-pocketbase)
|
||||
Tasks: 13 (limit: 629145)
|
||||
Memory: 28.3M
|
||||
CPU: 148ms
|
||||
CGroup: /system.slice/pb-auth-example-app.service
|
||||
└─411857 /nix/store/czq95bjhwszasncp8f04d9yn4m0xf4kw-auth-pocketbase-attempt-0.0.1/bin/auth-pocketbase-attempt serve --http 127.0.0.1:45001 --dir=/home/pb-auth-example-app-user
|
||||
|
||||
Oct 09 04:29:20 franzk systemd[1]: Started Exercise app auth-pocketbase-attempt.
|
||||
Oct 09 04:29:20 franzk auth-pocketbase-attempt[411857]: 2023/10/09 04:29:20 Warning: starting server with cookie Secure = false!
|
||||
Oct 09 04:29:20 franzk auth-pocketbase-attempt[411857]: 2023/10/09 04:29:20 Server started at http://127.0.0.1:45001
|
||||
Oct 09 04:29:20 franzk auth-pocketbase-attempt[411857]: ├─ REST API: http://127.0.0.1:45001/api/
|
||||
Oct 09 04:29:20 franzk auth-pocketbase-attempt[411857]: └─ Admin UI: http://127.0.0.1:45001/_/
|
||||
#+end_src
|
||||
*** so, custom arg is required, hello
|
||||
https://github.com/pocketbase/pocketbase/discussions/1900
|
||||
*** holy cow, Firefox and later Chrome will accept Secure cookie on localhost
|
||||
https://stackoverflow.com/questions/62307431/firefox-sends-secure-cookies-to-localhost
|
||||
|
||||
see: except on localhost : https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
||||
|
||||
|
||||
** DONE prettying up server responses for "we show html" land
|
||||
let's do this also, yes
|
||||
*** DONE logout should push root url in htmx
|
||||
*** DONE lets make 404 page and return it
|
||||
*** DONE lets make 401 page and return it
|
||||
*** DONE and let's make NavInfo init common for reuse
|
||||
** TODO get icons for the auth providers. surely they are accessible from the pocketbase itself?
|
||||
http://localhost:8090/_/images/oauth2/apple.svg
|
||||
yes.
|
||||
** TODO read and add ok logging
|
||||
** TODO figure out and enbale migrations
|
||||
https://pocketbase.io/docs/go-migrations/#enable-go-migrations
|
||||
|
||||
if i understood correctly, when i enable migration generation
|
||||
i would be able to modify locally run instance via admin interface,
|
||||
go files with migration would be generated, i'll have to import them somewhere in my main module, and then after building/packaging when i run `serve` on production the migrations would run on the production data
|
||||
** adding google oauth
|
||||
support article : https://developers.google.com/identity/sign-in/web/sign-in
|
||||
settings are in : https://console.cloud.google.com/apis/credentials
|
||||
Generated
-59
@@ -1,59 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694529238,
|
||||
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1695806987,
|
||||
"narHash": "sha256-fX5kGs66NZIxCMcpAGIpxuftajHL8Hil1vjHmjjl118=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f3dab3509afca932f3f4fd0908957709bb1c1f57",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
{
|
||||
description = "going to look at the pocketbase apis";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
pname = "auth-pocketbase-attempt";
|
||||
version = "0.0.1";
|
||||
in rec {
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = [
|
||||
pkgs.go
|
||||
pkgs.wgo # for restart of project
|
||||
pkgs.semgrep
|
||||
pkgs.gopls
|
||||
pkgs.nodePackages.tailwindcss
|
||||
pkgs.nodePackages.prettier
|
||||
pkgs.gnumake
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
export GOPATH=$PWD/.go
|
||||
export PATH=$GOPATH/bin:$PATH
|
||||
'';
|
||||
};
|
||||
packages = rec {
|
||||
auth-pocketbase-attempt = pkgs.buildGoModule {
|
||||
inherit pname version;
|
||||
src = pkgs.nix-gitignore.gitignoreSource [ ] ./.;
|
||||
vendorHash =
|
||||
"sha256-7B5EkrLpL+P5wipQG5a12hrvXQn/UpYAjrz/DuHmSUQ="; # set to "" when get dependencies in go.mod
|
||||
|
||||
# Adding the Tailwind build step to preBuild
|
||||
preBuild = ''
|
||||
${pkgs.nodePackages.tailwindcss}/bin/tailwindcss -i pages/input.css -o pages/static/public/out.css
|
||||
'';
|
||||
};
|
||||
default = auth-pocketbase-attempt;
|
||||
};
|
||||
nixosModules.auth-pocketbase-attempt = { config, pkgs, ... }:
|
||||
let
|
||||
cfg = config.services.${pname};
|
||||
lib = nixpkgs.lib;
|
||||
shortName = "pb-auth-example-app";
|
||||
in {
|
||||
options.services.${pname} = {
|
||||
enable = lib.mkEnableOption
|
||||
"Enable simple ssr oauth example build on pocketbase";
|
||||
port = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 8090;
|
||||
description =
|
||||
"Port to listen on. Use 443 for tls when no nginx, usual plaintext is 8090.";
|
||||
};
|
||||
host = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "127.0.0.1";
|
||||
description = "Host to bind to.";
|
||||
};
|
||||
useNginx = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Whether to use Nginx to proxy requests.";
|
||||
};
|
||||
usePbTls = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description =
|
||||
"Whether pocketbase should serve on https and issue own certs. Main case for true - when not under nginx";
|
||||
};
|
||||
useHostTls = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description =
|
||||
"Whether virtual host should enable NixOS ACME certs";
|
||||
};
|
||||
};
|
||||
config = let
|
||||
username = "${shortName}-user";
|
||||
groupname = "${shortName}-group";
|
||||
in lib.mkIf cfg.enable {
|
||||
users.groups."${groupname}" = { };
|
||||
users.users."${username}" = {
|
||||
isNormalUser = true; # needed to allow for home dir
|
||||
group = "${groupname}";
|
||||
};
|
||||
systemd.services.${shortName} = let
|
||||
protocol = if cfg.usePbTls then "https" else "http";
|
||||
serverHost = if cfg.useNginx then "127.0.0.1" else cfg.host;
|
||||
serveCliArg =
|
||||
"--${protocol} ${serverHost}:${toString cfg.port}";
|
||||
in {
|
||||
description = "Exercise app ${pname}";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
startLimitIntervalSec = 30;
|
||||
startLimitBurst = 10;
|
||||
serviceConfig = {
|
||||
ExecStart =
|
||||
"${packages.auth-pocketbase-attempt}/bin/${pname} serve ${serveCliArg} --dir=/home/${
|
||||
"${username}"
|
||||
}";
|
||||
Restart = "on-failure";
|
||||
User = "${username}";
|
||||
Group = "${groupname}";
|
||||
};
|
||||
};
|
||||
services.nginx = lib.mkIf cfg.useNginx {
|
||||
virtualHosts.${cfg.host} = {
|
||||
forceSSL = cfg.useHostTls;
|
||||
enableACME = cfg.useHostTls;
|
||||
locations."/" = {
|
||||
proxyPass = "http://127.0.0.1:${toString cfg.port}";
|
||||
# taken from https://pocketbase.io/docs/going-to-production/
|
||||
proxyWebsockets = true;
|
||||
extraConfig = ''
|
||||
# check http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive
|
||||
proxy_read_timeout 360s;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
module sunshine.industries/auth-pocketbase-attempt
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
||||
github.com/pocketbase/pocketbase v0.18.9
|
||||
github.com/spf13/cast v1.5.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aws/aws-sdk-go v1.45.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.42 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.40 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.86 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.39.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.14.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 // indirect
|
||||
github.com/aws/smithy-go v1.14.2 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/ganigeorgiev/fexpr v0.3.0 // indirect
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
github.com/google/wire v0.5.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/pocketbase/dbx v1.10.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/spf13/cobra v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
gocloud.dev v0.34.0 // indirect
|
||||
golang.org/x/crypto v0.13.0 // indirect
|
||||
golang.org/x/image v0.12.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
golang.org/x/oauth2 v0.12.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/term v0.12.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.142.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
|
||||
google.golang.org/grpc v1.58.2 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
lukechampine.com/uint128 v1.3.0 // indirect
|
||||
modernc.org/cc/v3 v3.41.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.15 // indirect
|
||||
modernc.org/libc v1.24.1 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.1 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/sqlite v1.25.0 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
||||
@@ -1,344 +0,0 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o=
|
||||
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
|
||||
cloud.google.com/go/storage v1.31.0 h1:+S3LjjEN2zZ+L5hOwj4+1OkGCsLVe0NzpXKQ1pSdTCI=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/aws/aws-sdk-go v1.45.16 h1:spca2z7UJgoQ5V2fX6XiHDCj2E65kOJAfbUPozSkE24=
|
||||
github.com/aws/aws-sdk-go v1.45.16/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.42 h1:28jHROB27xZwU0CB88giDSjz7M1Sba3olb5JBGwina8=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.42/go.mod h1:4AZM3nMMxwlG+eZlxvBKqwVbkDLlnN2a4UGTL6HjaZI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.40 h1:s8yOkDh+5b1jUDhMBtngF6zKWLDs84chUk2Vk0c38Og=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.40/go.mod h1:VtEHVAAqDWASwdOqj/1huyT6uHbs5s8FUHfDQdky/Rs=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.86 h1:tnn/U5bz5flqoTCFSgRMEdg93ULR9Q6+tL5LkwjJ0DM=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.86/go.mod h1:TJGNZIhz3fsaQ6PU9roZacAEMMnG89X2UzaDblNoeNw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43 h1:g+qlObJH4Kn4n21g69DjspU0hKTjWtq7naZ9OLCv0ew=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.43/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4/go.mod h1:1PrKYwxTM+zjpw9Y41KFtoJCQrJ34Z47Y4VgVbfndjo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14/go.mod h1:dDilntgHy9WnHXsh7dDtUPgHKEfTJIBUTHM8OWm0f/0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36/go.mod h1:lGnOkH9NJATw0XEPcAknFBj3zzNTEGRHtSw+CwC1YTg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4/go.mod h1:LhTyt8J04LL+9cIt7pYJ5lbS/U98ZmXovLOR/4LUsk8=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.39.0 h1:VZ2WMkKLio5tVjYfThcy5+pb6YHGd6B6egq75FfM6hU=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.39.0/go.mod h1:rDGMZA7f4pbmTtPOk5v5UM2lmX6UAbRnMDJeDvnH7AM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.14.1 h1:YkNzx1RLS0F5qdf9v1Q8Cuv9NXCL2TkosOxhzlUPV64=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.14.1/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1 h1:8lKOidPkmSmfUtiTgtdXWgaKItCZ/g75/jEk6Ql6GsA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.1/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.22.0 h1:s4bioTgjSFRwOoyEFzAVCmFmoowBgjTR8gkrF/sQ4wk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.22.0/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU=
|
||||
github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ=
|
||||
github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/ganigeorgiev/fexpr v0.3.0 h1:RwSyJBME+g/XdzlUW0paH/4VXhLHPg+rErtLeC7K8Ew=
|
||||
github.com/ganigeorgiev/fexpr v0.3.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE=
|
||||
github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk=
|
||||
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
|
||||
github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 h1:gpptm606MZYGaMHMsB4Srmb6EbW/IVHnt04rcMXnkBQ=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
|
||||
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
|
||||
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 h1:FwuzbVh87iLiUQj1+uQUsuw9x5t9m5n5g7rG7o4svW4=
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA=
|
||||
github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||
github.com/pocketbase/pocketbase v0.18.9 h1:1i0Lg/vlXJe7DGgHqwIRGBxkpGgZ+0z9m3HNVumb/FM=
|
||||
github.com/pocketbase/pocketbase v0.18.9/go.mod h1:uXjdxN7w0gpVISqbFvX9xXhg9YhRoQ4hYlKEe27yTJ8=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
gocloud.dev v0.34.0 h1:LzlQY+4l2cMtuNfwT2ht4+fiXwWf/NmPTnXUlLmGif4=
|
||||
gocloud.dev v0.34.0/go.mod h1:psKOachbnvY3DAOPbsFVmLIErwsbWPUG2H5i65D38vE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ=
|
||||
golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
|
||||
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.142.0 h1:mf+7EJ94fi5ZcnpPy+m0Yv2dkz8bKm+UL0snTCuwXlY=
|
||||
google.golang.org/api v0.142.0/go.mod h1:zJAN5o6HRqR7O+9qJUFOWrZkYE66RH+efPBdTLA4xBA=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
|
||||
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo=
|
||||
lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q=
|
||||
modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
|
||||
modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0=
|
||||
modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
|
||||
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.1 h1:9J+2/GKTlV503mk3yv8QJ6oEpRCUrRy0ad8TXEPoV8M=
|
||||
modernc.org/memory v1.7.1/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
|
||||
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
||||
@@ -1,22 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"sunshine.industries/auth-pocketbase-attempt/middleware"
|
||||
"sunshine.industries/auth-pocketbase-attempt/pages"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := pocketbase.New()
|
||||
|
||||
middleware.AddCookieSessionMiddleware(app)
|
||||
middleware.AddErrorsMiddleware(app)
|
||||
pages.AddPageRoutes(app)
|
||||
|
||||
// starts the pocketbase backend
|
||||
// parses cli arguments for hostname and data dir
|
||||
if err := app.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tokens"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
const AuthCookieName = "Auth"
|
||||
// front end side of authentication:
|
||||
// in base.gohtml template, in <nav> bar
|
||||
// js code uses SDK for pocketbase to handle oauth calls to backend.
|
||||
// Also custom event
|
||||
// in oauth js code
|
||||
// document.body.dispatchEvent(new Event("auth-change-event"));
|
||||
// and in logout route
|
||||
// c.Response().Header().Add("HX-Trigger", "auth-change-event")
|
||||
// trigger hx-get on <body>
|
||||
// so that on successful auth and logout the page would refresh
|
||||
// This is suboptimal in that 3 places:
|
||||
// <body> with hx-get, js code with `dispatchEvent` and logout route with
|
||||
// HX-Trigger share responsibility for this piece of logic. For some reason
|
||||
// returning HX-Trigger from auth routes via middleware doesn't trigger event on
|
||||
// htmx side, maybe because these reqeusts are done through js and not directly
|
||||
// by user in browser. Or maybe this would be considered a bug on htmx side and
|
||||
// system could be simplified to just use HX-Trigger response header. Or some
|
||||
// other way to simplify
|
||||
|
||||
|
||||
// registeres on pocketbase middleware that
|
||||
// Sets and Reads session data into a secure cookie
|
||||
func AddCookieSessionMiddleware(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
e.Router.Use(loadAuthContextFromCookie(app))
|
||||
return nil
|
||||
})
|
||||
|
||||
// fires for every auth collection
|
||||
app.OnRecordAuthRequest().Add(func(e *core.RecordAuthEvent) error {
|
||||
e.HttpContext.SetCookie(&http.Cookie{
|
||||
Name: AuthCookieName,
|
||||
Value: e.Token,
|
||||
Path: "/",
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
})
|
||||
e.HttpContext.SetCookie(&http.Cookie{
|
||||
Name: "username",
|
||||
Value: e.Record.Username(),
|
||||
})
|
||||
return nil
|
||||
})
|
||||
// fires for admin authentication
|
||||
app.OnAdminAuthRequest().Add(func(e *core.AdminAuthEvent) error {
|
||||
e.HttpContext.SetCookie(&http.Cookie{
|
||||
Name: AuthCookieName,
|
||||
Value: e.Token,
|
||||
Path: "/",
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
app.OnBeforeServe().Add(getLogoutRoute(app))
|
||||
}
|
||||
|
||||
func loadAuthContextFromCookie(app core.App) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
tokenCookie, err := c.Request().Cookie(AuthCookieName)
|
||||
if err != nil || tokenCookie.Value == "" {
|
||||
return next(c) // no token cookie
|
||||
}
|
||||
|
||||
token := tokenCookie.Value
|
||||
|
||||
claims, _ := security.ParseUnverifiedJWT(token)
|
||||
tokenType := cast.ToString(claims["type"])
|
||||
|
||||
switch tokenType {
|
||||
case tokens.TypeAdmin:
|
||||
admin, err := app.Dao().FindAdminByToken(
|
||||
token,
|
||||
app.Settings().AdminAuthToken.Secret,
|
||||
)
|
||||
if err == nil && admin != nil {
|
||||
// "authenticate" the admin
|
||||
c.Set(apis.ContextAdminKey, admin)
|
||||
}
|
||||
|
||||
case tokens.TypeAuthRecord:
|
||||
record, err := app.Dao().FindAuthRecordByToken(
|
||||
token,
|
||||
app.Settings().RecordAuthToken.Secret,
|
||||
)
|
||||
if err == nil && record != nil {
|
||||
// "authenticate" the app user
|
||||
c.Set(apis.ContextAuthRecordKey, record)
|
||||
}
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// render and return login page with configured oauth providers
|
||||
func getLogoutRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error {
|
||||
return func (e *core.ServeEvent) error {
|
||||
e.Router.GET("/logout", func(c echo.Context) error {
|
||||
c.SetCookie(&http.Cookie{
|
||||
Name: AuthCookieName,
|
||||
Value: "",
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
})
|
||||
c.Response().Header().Add("HX-Trigger", "auth-change-event")
|
||||
return c.NoContent(http.StatusOK)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"log"
|
||||
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
func AddErrorsMiddleware(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeApiError().Add(func(e *core.ApiErrorEvent) error {
|
||||
log.Printf("in before api error with %+v with response %v and error %+v", e, e.HttpContext.Response(), e.Error)
|
||||
// oh, i guess i could do redirect?
|
||||
return renderErrorPage(e)
|
||||
})
|
||||
}
|
||||
|
||||
var redirectTemplate = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; url='/error/{{ . }}'" />
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting to error page</p>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
var tmpl = template.Must( template.New("redirect-to-error").Parse(redirectTemplate) )
|
||||
|
||||
func renderErrorPage(e *core.ApiErrorEvent) error {
|
||||
errorMessage := e.Error.Error()
|
||||
log.Printf("in error to html middleware for %s with status %+v", errorMessage, e)
|
||||
|
||||
errorCode := 500
|
||||
switch errorMessage {
|
||||
case "Not Found.":
|
||||
// not authorized
|
||||
errorCode = 404
|
||||
case "The request requires admin or record authorization token to be set.":
|
||||
// not found
|
||||
errorCode = 401
|
||||
}
|
||||
|
||||
var instantiatedTemplate bytes.Buffer
|
||||
if err := tmpl.Execute(&instantiatedTemplate, errorCode); err != nil {
|
||||
// couldn't execute the template
|
||||
return e.HttpContext.HTML(200, "Error 500")
|
||||
}
|
||||
|
||||
return e.HttpContext.HTML(200, instantiatedTemplate.String())
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,202 +0,0 @@
|
||||
package pages
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"embed"
|
||||
"html/template"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
// template files are bundled with binary
|
||||
// for worry free deployment that needs to copy a single file
|
||||
|
||||
//go:embed templates
|
||||
var templatesFS embed.FS
|
||||
|
||||
// static files are bundled into separate FS
|
||||
// because full content of that embed.FS is available
|
||||
// under http://127.0.0.1:8090/static/static/public/
|
||||
|
||||
//go:embed static
|
||||
var staticFilesFS embed.FS
|
||||
|
||||
// registers site pages, to be served by pocketbase
|
||||
// passes `app` to allow access to `DAO` and other apis
|
||||
// each page will get auth data in request context
|
||||
// and will be able to create all necessary info for page render:
|
||||
// user data, external api calls, calculations
|
||||
func AddPageRoutes(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeServe().Add(getIndexPageRoute(app))
|
||||
app.OnBeforeServe().Add(getSomePageRoute(app))
|
||||
app.OnBeforeServe().Add(getErrorPageRoute(app))
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
e.Router.StaticFS("/static", staticFilesFS)
|
||||
// this path works : http://127.0.0.1:8090/static/static/public/htmx.min.js
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type navInfo struct {
|
||||
Username string
|
||||
IsGuest bool
|
||||
EnabledOauthProviders []string
|
||||
}
|
||||
|
||||
// render and return some page
|
||||
func getIndexPageRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error {
|
||||
return func(e *core.ServeEvent) error {
|
||||
e.Router.GET("/", func(c echo.Context) error {
|
||||
// first collect data
|
||||
navInfoData := initNavInfoData(app, c)
|
||||
indexPageData := struct {
|
||||
BackendMessage string
|
||||
NavInfo navInfo
|
||||
}{
|
||||
BackendMessage: "Hello from the backend!",
|
||||
NavInfo: navInfoData,
|
||||
}
|
||||
|
||||
// then render template with it
|
||||
templateName := "templates/index.gohtml"
|
||||
tmpl := template.Must(template.ParseFS(templatesFS, "templates/base.gohtml", templateName))
|
||||
var instantiatedTemplate bytes.Buffer
|
||||
if err := tmpl.Execute(&instantiatedTemplate, indexPageData); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "error parsing template"})
|
||||
}
|
||||
|
||||
return c.HTML(http.StatusOK, instantiatedTemplate.String())
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const charset = "abcdefghijklmnopqrstuvwxyz" +
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
func stringWithCharset(length int, charset string) string {
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[rand.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
func getSomePageRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error {
|
||||
return func(e *core.ServeEvent) error {
|
||||
e.Router.GET("/somepage", func(c echo.Context) error {
|
||||
// get data
|
||||
// and since i'm using 'base.gohtml' with Nav, i'll need Nav info
|
||||
navInfoData := initNavInfoData(app, c)
|
||||
|
||||
somePageData := struct {
|
||||
RandomNumber int
|
||||
RandomString string
|
||||
NavInfo navInfo
|
||||
}{
|
||||
RandomNumber: rand.Int(),
|
||||
RandomString: stringWithCharset(25, charset),
|
||||
NavInfo: navInfoData,
|
||||
}
|
||||
|
||||
// then render template with it
|
||||
templateName := "templates/somepage.gohtml"
|
||||
tmpl := template.Must(template.ParseFS(templatesFS, "templates/base.gohtml", templateName))
|
||||
var instantiatedTemplate bytes.Buffer
|
||||
if err := tmpl.Execute(&instantiatedTemplate, somePageData); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "error parsing template"})
|
||||
}
|
||||
|
||||
return c.HTML(http.StatusOK, instantiatedTemplate.String())
|
||||
}, apis.RequireAdminOrRecordAuth())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func getErrorPageRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error {
|
||||
return func(e *core.ServeEvent) error {
|
||||
e.Router.GET("/error/:code", func(c echo.Context) error {
|
||||
// get data
|
||||
code := c.PathParam("code")
|
||||
codeNum, err := strconv.ParseInt(code, 10, 64)
|
||||
if err != nil {
|
||||
codeNum = 500
|
||||
}
|
||||
errorText := http.StatusText(int(codeNum))
|
||||
if errorText == "" {
|
||||
codeNum = 500
|
||||
errorText = http.StatusText(500)
|
||||
}
|
||||
|
||||
// and since i'm using 'base.gohtml' with Nav, i'll need Nav info
|
||||
navInfoData := initNavInfoData(app, c)
|
||||
|
||||
somePageData := struct {
|
||||
NavInfo navInfo
|
||||
ErrorCode int64
|
||||
ErrorText string
|
||||
}{
|
||||
NavInfo: navInfoData,
|
||||
ErrorCode: codeNum,
|
||||
ErrorText: errorText,
|
||||
}
|
||||
|
||||
// then render template with it
|
||||
templateName := "templates/errors/error.gohtml"
|
||||
switch codeNum {
|
||||
case 404:
|
||||
templateName = "templates/errors/404.gohtml"
|
||||
case 401:
|
||||
templateName = "templates/errors/401.gohtml"
|
||||
}
|
||||
tmpl := template.Must(template.ParseFS(templatesFS, "templates/base.gohtml", templateName))
|
||||
var instantiatedTemplate bytes.Buffer
|
||||
if err := tmpl.Execute(&instantiatedTemplate, somePageData); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "error parsing template"})
|
||||
}
|
||||
|
||||
return c.HTML(int(codeNum), instantiatedTemplate.String())
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// initializing data which is used by any page that has <nav> bar
|
||||
// - whether user is already authenticated
|
||||
// - which authentication methods are available
|
||||
// this is used in templates/base.gohtml
|
||||
func initNavInfoData(app *pocketbase.PocketBase, c echo.Context) navInfo {
|
||||
// first collect data
|
||||
info := apis.RequestInfo(c)
|
||||
admin := info.Admin // nil if not authenticated as admin
|
||||
record := info.AuthRecord // nil if not authenticated as regular auth record
|
||||
|
||||
isGuest := admin == nil && record == nil
|
||||
|
||||
username := ""
|
||||
switch {
|
||||
case admin != nil:
|
||||
username = admin.Email
|
||||
case record != nil:
|
||||
username = record.Username()
|
||||
}
|
||||
|
||||
oauthProviders := app.Settings().NamedAuthProviderConfigs()
|
||||
oauthProviderNames := make([]string, 0, len(oauthProviders))
|
||||
for name, config := range oauthProviders {
|
||||
if config.Enabled {
|
||||
oauthProviderNames = append(oauthProviderNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
return navInfo{
|
||||
IsGuest: isGuest,
|
||||
Username: username,
|
||||
EnabledOauthProviders: oauthProviderNames,
|
||||
}
|
||||
}
|
||||
Vendored
-1
File diff suppressed because one or more lines are too long
@@ -1,110 +0,0 @@
|
||||
<!doctype html>
|
||||
<html class="no-js" lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>{{ template "title" . }}</title>
|
||||
<meta name="description" content="" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<link href="/static/static/public/out.css" rel="stylesheet">
|
||||
<!-- Place favicon.ico in the root directory -->
|
||||
<script
|
||||
defer
|
||||
src="https://cdn.jsdelivr.net/gh/pocketbase/js-sdk@master/dist/pocketbase.umd.js"
|
||||
></script>
|
||||
<script defer src="/static/static/public/htmx.min.js"></script>
|
||||
</head>
|
||||
<body hx-get="/" hx-trigger="auth-change-event" hx-push-url="true">
|
||||
<!--[if lt IE 8]>
|
||||
<p class="browserupgrade">
|
||||
You are using an <strong>outdated</strong> browser. Please
|
||||
<a href="http://browsehappy.com/">upgrade your browser</a> to improve
|
||||
your experience.
|
||||
<![endif]-->
|
||||
<nav class="bg-blue-300 flex flex-row p-4 gap-x-4">
|
||||
<h2 class="text-lg font-bold flex-1">Using SSR and oauth with pocketbase as Go framework</h2>
|
||||
{{ if .NavInfo.IsGuest }}
|
||||
<button id="openAuth">Authenticate</button>
|
||||
<dialog id="authDialog"
|
||||
class="open:border open:border-4 open:flex open:flex-col open:p-6 open:gap-y-4 backdrop:bg-gradient-to-tl backdrop:to-cyan-700/25 backdrop:from-blue-900/50"
|
||||
>
|
||||
<div class="flex flex-row gap-x-7">
|
||||
<p class="flex-1">Greetings, one and all!</p>
|
||||
<button id="closeAuth">[X]</button>
|
||||
</div>
|
||||
<div class="flex flex-row gap-x-4">
|
||||
{{ range .NavInfo.EnabledOauthProviders }}
|
||||
<button
|
||||
class="underline"
|
||||
onClick="callOauth('{{ . }}')">Login with {{ . }}</button>
|
||||
{{ else }}
|
||||
<p>Please configure at least one oauth provider</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
</dialog>
|
||||
<script defer type="text/javascript">
|
||||
async function callOauth(providerName) {
|
||||
const baseUrl = window.location.protocol + "//" + window.location.host;
|
||||
const pb = new PocketBase(baseUrl);
|
||||
|
||||
// This method initializes a one-off realtime subscription and will
|
||||
// open a popup window with the OAuth2 vendor page to authenticate.
|
||||
//
|
||||
// Once the external OAuth2 sign-in/sign-up flow is completed, the popup
|
||||
// window will be automatically closed and the OAuth2 data sent back
|
||||
// to the user through the previously established realtime connection.
|
||||
const authData = await pb
|
||||
.collection("users")
|
||||
.authWithOAuth2({ provider: providerName });
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
document.body.dispatchEvent(new Event("auth-change-event"));
|
||||
}
|
||||
</script>
|
||||
<script defer type="text/javascript">
|
||||
function initAuthDialog() {
|
||||
const dialog = document.querySelector("#authDialog");
|
||||
const showButton = document.querySelector("#openAuth");
|
||||
// const closeButton = document.querySelector("#closeAuth");
|
||||
// Select all buttons that are direct children of the dialog
|
||||
var buttons = authDialog.querySelectorAll("button");
|
||||
|
||||
if (!dialog || !showButton) {
|
||||
console.log("some auth elements are not present");
|
||||
return;
|
||||
}
|
||||
console.log("setting up script for buttons");
|
||||
|
||||
// "Show the dialog" button opens the dialog modally
|
||||
showButton.addEventListener("click", () => {
|
||||
dialog.showModal();
|
||||
});
|
||||
|
||||
buttons.forEach(function (button) {
|
||||
button.addEventListener("click", function () {
|
||||
authDialog.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
initAuthDialog();
|
||||
// "DOMContentLoaded" doesn't work with htmx replacing body
|
||||
// maybe i could use htmx related event, but ok to just do asap, i guess
|
||||
// also - i bet hyperscript would work here
|
||||
// document.addEventListener("DOMContentLoaded", initAuthDialog);
|
||||
</script>
|
||||
{{ else }}
|
||||
<p>Logged in as: {{ .NavInfo.Username }}</p>
|
||||
<button hx-get="/logout">Logout</button>
|
||||
{{ end }}
|
||||
</nav>
|
||||
<main hx-boost="true">{{ template "main" . }}</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,10 +0,0 @@
|
||||
{{ define "title" }}
|
||||
Not Authorized
|
||||
{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<main hx-boost="true" class="px-10 pt-10 flex flex-col gap-y-10">
|
||||
The page you are trying to access requires authorization.
|
||||
Please log in.
|
||||
</main>
|
||||
{{ end }}
|
||||
@@ -1,9 +0,0 @@
|
||||
{{ define "title" }}
|
||||
Page Not Found
|
||||
{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<main hx-boost="true" class="px-10 pt-10 flex flex-col gap-y-10">
|
||||
Error 404 means the page was not found
|
||||
</main>
|
||||
{{ end }}
|
||||
@@ -1,10 +0,0 @@
|
||||
{{ define "title" }}
|
||||
Error occurred
|
||||
{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<main hx-boost="true" class="px-10 pt-10 flex flex-col gap-y-10">
|
||||
<p> Error {{ .ErrorCode }} occurred! </p>
|
||||
<p> {{ .ErrorText }} </p>
|
||||
</main>
|
||||
{{ end }}
|
||||
@@ -1,20 +0,0 @@
|
||||
{{ define "title" }}
|
||||
Index page
|
||||
{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<main hx-boost="true" class="px-10 pt-10 flex flex-col gap-y-10">
|
||||
<h1 class="text-2xl font-bold">Welcome to index page</h1>
|
||||
{{ if not .NavInfo.IsGuest }}
|
||||
<p>This is content only for authenticated users! Congratulations!</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/somepage" class="text-blue-500 visited:text-purple-500 underline">Link to page only for logged in users</a>
|
||||
</li>
|
||||
</ul>
|
||||
{{ else }}
|
||||
<p>Rendering this on the backend, passing values from the code: {{ .BackendMessage }}</p>
|
||||
<p>There will be some content only for authorized users</p>
|
||||
{{ end }}
|
||||
</main>
|
||||
{{ end }}
|
||||
@@ -1,16 +0,0 @@
|
||||
{{ define "title" }}
|
||||
Some page with content
|
||||
{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<main class="flex flex-col gap-y-4 p-10 ">
|
||||
<h1 class="text-2xl">This is another page</h1>
|
||||
<p>Will be rendered on server</p>
|
||||
<p>and locked under apis.RequireAdminOrRecordAuth default middleware</p>
|
||||
<p>here are some random numbers</p>
|
||||
<ul>
|
||||
<li>{{ .RandomNumber }}</li>
|
||||
<li>{{ .RandomString }}</li>
|
||||
</ul>
|
||||
</main>
|
||||
{{ end }}
|
||||
@@ -1,9 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["pages/templates/**/*.gohtml"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user