Compare commits
10 Commits
c87abb6956
...
258d2b560c
Author | SHA1 | Date |
---|---|---|
|
258d2b560c | |
|
59c3b1ce59 | |
|
4a64f2186f | |
|
769fe603c7 | |
|
f69cb661dc | |
|
ffd74c4222 | |
|
2676b87d5b | |
|
bb418101dd | |
|
a9fce1bcbf | |
|
e1346e2f96 |
|
@ -6,3 +6,5 @@ tmp/
|
|||
.go
|
||||
.direnv
|
||||
pb_data
|
||||
pages/static/public/out.css
|
||||
result
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
The MIT License (MIT) Copyright (c) 2022 - present, Efim Nefedov
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
##
|
||||
# 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
|
180
auth-notes.org
180
auth-notes.org
|
@ -4,8 +4,13 @@ https://pocketbase.io/docs/go-overview/
|
|||
* plan
|
||||
** DONE start pocketbase
|
||||
** DONE add middlewares for cookie session
|
||||
** TODO add index page, that will have either "current user" or 'login' link
|
||||
** TODO 'login' link should open dialog with oauth providers
|
||||
** 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/
|
||||
|
@ -14,13 +19,174 @@ https://pocketbase.io/docs/authentication/
|
|||
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
|
||||
** TODO i guess i would also like to send htmx event for reloading the page
|
||||
*** 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?
|
||||
** TODO add one more page that checks auth
|
||||
** TODO add tailwind styling
|
||||
** TODO package static into single binary
|
||||
** TODO write nix build
|
||||
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
|
||||
|
||||
** TODO 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?
|
||||
|
||||
** TODO add docker image from nix
|
||||
*** TODO add cli for port and host
|
||||
** TODO add readme and comments
|
||||
** TODO configure tls / ssl / https on franzk deployment
|
||||
can it be configured on render.com?
|
||||
** TODO maybe add middleware so that 401 would be a page, and not json
|
||||
** TODO get icons for the auth providers. surely they are accessible from the pocketbase itself?
|
||||
http://localhost:8090/_/images/oauth2/apple.svg
|
||||
yes.
|
||||
** 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
|
||||
|
|
34
flake.lock
34
flake.lock
|
@ -1,5 +1,23 @@
|
|||
{
|
||||
"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,
|
||||
|
@ -16,8 +34,24 @@
|
|||
},
|
||||
"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",
|
||||
|
|
115
flake.nix
115
flake.nix
|
@ -1,22 +1,103 @@
|
|||
{
|
||||
description = "going to look at the pocketbase apis";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs }: {
|
||||
devShell.x86_64-linux = let pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
in pkgs.mkShell {
|
||||
buildInputs = [
|
||||
pkgs.go
|
||||
pkgs.wgo # for restart of project
|
||||
pkgs.semgrep
|
||||
pkgs.gopls
|
||||
pkgs.nodePackages.tailwindcss
|
||||
pkgs.nodePackages.prettier
|
||||
];
|
||||
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
|
||||
'';
|
||||
};
|
||||
};
|
||||
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 = pkgs.lib;
|
||||
shortName = "pb-auth-example-group";
|
||||
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";
|
||||
};
|
||||
};
|
||||
config = lib.mkIf cfg.enable {
|
||||
users.groups."${shortName}-group" = { };
|
||||
users.users."${shortName}-user" = {
|
||||
isSystemUser = true;
|
||||
group = "${shortName}-group";
|
||||
};
|
||||
systemd.services.${shortName} = let
|
||||
protocol = if cfg.usePbTls then "https" else "http";
|
||||
serverHost = if cfg.useNginx then "127.0.0.1" else cfg.host;
|
||||
servedAddress = "${protocol}://${serverHost}:${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 ${servedAddress} --dir=/home/${
|
||||
config.users.users."${shortName}-user"
|
||||
}";
|
||||
Restart = "on-failure";
|
||||
User = "${shortName}-user";
|
||||
Group = "${shortName}-group";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ const AuthCookieName = "Auth"
|
|||
func AddCookieSessionMiddleware(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
e.Router.Use(loadAuthContextFromCookie(app))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -50,7 +49,7 @@ func AddCookieSessionMiddleware(app *pocketbase.PocketBase) {
|
|||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
app.OnBeforeServe().Add(getLogoutRoute(app))
|
||||
}
|
||||
|
||||
func loadAuthContextFromCookie(app core.App) echo.MiddlewareFunc {
|
||||
|
@ -109,3 +108,21 @@ func loadAuthContextFromCookie(app core.App) echo.MiddlewareFunc {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
})
|
||||
c.Response().Header().Add("HX-Trigger", "auth-change-event")
|
||||
return c.JSON(http.StatusOK, map[string]string{"message": "session cookie removed"})
|
||||
})
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
|
@ -5,6 +5,7 @@ import (
|
|||
"embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
|
@ -16,18 +17,33 @@ import (
|
|||
//go:embed templates
|
||||
var templatesFS embed.FS
|
||||
|
||||
//go:embed static
|
||||
var staticFilesFS embed.FS
|
||||
|
||||
func AddPageRoutes(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeServe().Add(getIndexPageRoute(app))
|
||||
app.OnBeforeServe().Add(getAuthPageRoute(app))
|
||||
app.OnBeforeServe().Add(somePageRoute)
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
// render and return index page
|
||||
func getIndexPageRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error {
|
||||
return func (e *core.ServeEvent) error {
|
||||
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
|
||||
info := apis.RequestInfo(c)
|
||||
admin := info.Admin // nil if not authenticated as admin
|
||||
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
|
||||
|
@ -52,19 +68,22 @@ func getIndexPageRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error
|
|||
fmt.Printf(">> enabled providers names %+v\n", oauthProviderNames)
|
||||
|
||||
indexPageData := struct {
|
||||
IsGuest, IsAdmin bool
|
||||
Username string
|
||||
IsGuest, IsAdmin bool
|
||||
Username string
|
||||
EnabledOauthProviders []string
|
||||
NavInfo navInfo
|
||||
}{
|
||||
IsGuest: isGuest,
|
||||
IsAdmin: admin != nil,
|
||||
Username: username,
|
||||
EnabledOauthProviders: oauthProviderNames,
|
||||
NavInfo: navInfo{
|
||||
IsGuest: isGuest,
|
||||
Username: username,
|
||||
EnabledOauthProviders: oauthProviderNames,
|
||||
},
|
||||
}
|
||||
|
||||
// then render template with it
|
||||
templateName := "templates/index.gohtml"
|
||||
tmpl := template.Must(template.ParseFS(templatesFS, templateName))
|
||||
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"})
|
||||
|
@ -76,21 +95,54 @@ func getIndexPageRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error
|
|||
}
|
||||
}
|
||||
|
||||
// render and return login page with configured oauth providers
|
||||
func getAuthPageRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error {
|
||||
return func (e *core.ServeEvent) error {
|
||||
e.Router.GET("/login", func(c echo.Context) error {
|
||||
const charset = "abcdefghijklmnopqrstuvwxyz" +
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
templateName := "templates/login.gohtml"
|
||||
tmpl := template.Must(template.ParseFS(templatesFS, templateName))
|
||||
var instantiatedTemplate bytes.Buffer
|
||||
if err := tmpl.Execute(&instantiatedTemplate, nil); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"message": "error parsing template"})
|
||||
}
|
||||
|
||||
return c.HTML(http.StatusOK, instantiatedTemplate.String())
|
||||
})
|
||||
return nil
|
||||
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 somePageRoute(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
|
||||
|
||||
info := apis.RequestInfo(c)
|
||||
admin := info.Admin // nil if not authenticated as admin
|
||||
record := info.AuthRecord // nil if not authenticated as regular auth record
|
||||
|
||||
username := ""
|
||||
switch {
|
||||
case admin != nil:
|
||||
username = admin.Email
|
||||
case record != nil:
|
||||
username = record.Username()
|
||||
}
|
||||
|
||||
somePageData := struct {
|
||||
RandomNumber int
|
||||
RandomString string
|
||||
NavInfo navInfo
|
||||
}{
|
||||
RandomNumber: rand.Int(),
|
||||
RandomString: stringWithCharset(25, charset),
|
||||
NavInfo: navInfo{
|
||||
Username: username,
|
||||
},
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,109 @@
|
|||
<!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">
|
||||
<!--[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 pb = new PocketBase("http://127.0.0.1:8090");
|
||||
|
||||
// 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,61 +1,19 @@
|
|||
<!doctype html>
|
||||
<html class="no-js" lang="">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
||||
<title>index page</title>
|
||||
<meta name="description" content="" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
{{ define "title" }}
|
||||
Index page
|
||||
{{ end }}
|
||||
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<!-- 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 type="text/javascript">
|
||||
async function callOauth(providerName) {
|
||||
const pb = new PocketBase("http://127.0.0.1:8090");
|
||||
|
||||
// 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();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welcome to index page</h1>
|
||||
<!--[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.
|
||||
</p<dialog open>
|
||||
<p>Greetings, one and all!</p>
|
||||
<form method="dialog">
|
||||
<button>OK</button>
|
||||
</form>
|
||||
</dialog>
|
||||
>
|
||||
<![endif]-->
|
||||
<p>{{ .Username }}</p>
|
||||
<dialog open>
|
||||
<p>Greetings, one and all!</p>
|
||||
<button onClick="callOauth('github')">OK!</button>
|
||||
</dialog>
|
||||
</body>
|
||||
</html>
|
||||
{{ 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>There will be some content only for authorized users</p>
|
||||
{{ end }}
|
||||
</main>
|
||||
{{ end }}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{{ 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 }}
|
|
@ -0,0 +1,9 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["pages/templates/**/*.gohtml"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
Loading…
Reference in New Issue