Compare commits

...

10 Commits

Author SHA1 Message Date
efim 258d2b560c feat: adding a license for pushing to repo 2023-10-08 13:41:45 +00:00
efim 59c3b1ce59 feat: initial nixos module impl 2023-10-08 13:36:31 +00:00
efim 4a64f2186f feat: nix build enabled
nix build
will put binary into nix store, and symlink to ./result
running with
./result/bin/auth-pocketbase-attempt serve --dir=./pb_data

if i understood migrations correctly, when using as a framework
migrations will be generated as go files, and will be packaged into the binary
2023-10-08 07:20:48 +00:00
efim 769fe603c7 feat: Makefile for building and running
yay, tailwindcss is only executed when templates or config has changed
i can remove out.css from the repository!
2023-10-07 03:20:24 +00:00
efim f69cb661dc feat: enable tailwind, committing out.css for now 2023-10-06 14:19:27 +00:00
efim ffd74c4222 refactor: adding base template for header & nav 2023-10-06 13:42:20 +00:00
efim 2676b87d5b feat: page reload on auth event 2023-10-06 12:38:09 +00:00
efim bb418101dd feat: serving static files
for example htmx library
in the future tailwindcss out.css file as well
2023-10-06 10:31:50 +00:00
efim a9fce1bcbf feat: moving dialog script to head
this way htmx page switch would keep it.
and calling on event DOM loaded means elements should be found
2023-10-06 09:40:28 +00:00
efim e1346e2f96 feat: logout route and login dialog 2023-10-06 09:20:00 +00:00
14 changed files with 606 additions and 112 deletions

2
.gitignore vendored
View File

@ -6,3 +6,5 @@ tmp/
.go
.direnv
pb_data
pages/static/public/out.css
result

8
License.md Normal file
View File

@ -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.

38
Makefile Normal file
View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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";
};
};
};
};
});
}

View File

@ -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
}
}

3
pages/input.css Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -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
}

1
pages/static/public/htmx.min.js vendored Normal file

File diff suppressed because one or more lines are too long

109
pages/templates/base.gohtml Normal file
View File

@ -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>

View File

@ -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 }}

View File

@ -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 }}

9
tailwind.config.js Normal file
View File

@ -0,0 +1,9 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["pages/templates/**/*.gohtml"],
theme: {
extend: {},
},
plugins: [],
}