Compare commits
2 Commits
a367ed9a34
...
8842235372
Author | SHA1 | Date |
---|---|---|
|
8842235372 | |
|
ec47c9d610 |
|
@ -242,7 +242,12 @@ https://stackoverflow.com/questions/62307431/firefox-sends-secure-cookies-to-loc
|
|||
see: except on localhost : https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
|
||||
|
||||
|
||||
** TODO maybe add middleware so that 401 would be a page, and not json
|
||||
** 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.
|
||||
|
@ -253,3 +258,6 @@ 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
|
||||
|
|
1
main.go
1
main.go
|
@ -11,6 +11,7 @@ func main() {
|
|||
app := pocketbase.New()
|
||||
|
||||
middleware.AddCookieSessionMiddleware(app)
|
||||
middleware.AddErrorsMiddleware(app)
|
||||
pages.AddPageRoutes(app)
|
||||
|
||||
if err := app.Start(); err != nil {
|
||||
|
|
|
@ -101,7 +101,7 @@ func getLogoutRoute(app *pocketbase.PocketBase) func(*core.ServeEvent) error {
|
|||
HttpOnly: true,
|
||||
})
|
||||
c.Response().Header().Add("HX-Trigger", "auth-change-event")
|
||||
return c.JSON(http.StatusOK, map[string]string{"message": "session cookie removed"})
|
||||
return c.NoContent(http.StatusOK)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
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())
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
"html/template"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
|
@ -21,7 +22,8 @@ var staticFilesFS embed.FS
|
|||
|
||||
func AddPageRoutes(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeServe().Add(getIndexPageRoute(app))
|
||||
app.OnBeforeServe().Add(somePageRoute)
|
||||
app.OnBeforeServe().Add(getSomePageRoute(app))
|
||||
app.OnBeforeServe().Add(getErrorPageRoute(app))
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
e.Router.StaticFS("/static", staticFilesFS)
|
||||
|
@ -41,40 +43,13 @@ 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
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
navInfoData := initNavInfoData(app, c)
|
||||
indexPageData := struct {
|
||||
IsGuest, IsAdmin bool
|
||||
Username string
|
||||
EnabledOauthProviders []string
|
||||
BackendMessage string
|
||||
NavInfo navInfo
|
||||
}{
|
||||
IsAdmin: admin != nil,
|
||||
NavInfo: navInfo{
|
||||
IsGuest: isGuest,
|
||||
Username: username,
|
||||
EnabledOauthProviders: oauthProviderNames,
|
||||
},
|
||||
BackendMessage: "Hello from the backend!",
|
||||
NavInfo: navInfoData,
|
||||
}
|
||||
|
||||
// then render template with it
|
||||
|
@ -101,22 +76,12 @@ func stringWithCharset(length int, charset string) string {
|
|||
}
|
||||
return string(b)
|
||||
}
|
||||
func somePageRoute(e *core.ServeEvent) error {
|
||||
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
|
||||
|
||||
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()
|
||||
}
|
||||
navInfoData := initNavInfoData(app, c)
|
||||
|
||||
somePageData := struct {
|
||||
RandomNumber int
|
||||
|
@ -125,9 +90,7 @@ func somePageRoute(e *core.ServeEvent) error {
|
|||
}{
|
||||
RandomNumber: rand.Int(),
|
||||
RandomString: stringWithCharset(25, charset),
|
||||
NavInfo: navInfo{
|
||||
Username: username,
|
||||
},
|
||||
NavInfo: navInfoData,
|
||||
}
|
||||
|
||||
// then render template with it
|
||||
|
@ -142,3 +105,83 @@ func somePageRoute(e *core.ServeEvent) error {
|
|||
}, 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
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
></script>
|
||||
<script defer src="/static/static/public/htmx.min.js"></script>
|
||||
</head>
|
||||
<body hx-get="/" hx-trigger="auth-change-event">
|
||||
<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
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{{ 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 }}
|
|
@ -0,0 +1,9 @@
|
|||
{{ 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 }}
|
|
@ -0,0 +1,10 @@
|
|||
{{ 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 }}
|
|
@ -13,6 +13,7 @@
|
|||
</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>
|
||||
|
|
Loading…
Reference in New Issue