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