2024-07-25 05:13:23 +00:00
|
|
|
package web
|
2024-05-05 22:10:32 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"embed"
|
2024-07-25 06:01:44 +00:00
|
|
|
"errors"
|
2024-07-24 07:53:20 +00:00
|
|
|
"fmt"
|
2024-05-05 22:10:32 +00:00
|
|
|
"html/template"
|
|
|
|
"io"
|
|
|
|
"io/fs"
|
|
|
|
|
2024-07-24 07:53:20 +00:00
|
|
|
"git.janky.solutions/finn/go-project-template/config"
|
2024-07-25 06:01:44 +00:00
|
|
|
"git.janky.solutions/finn/go-project-template/db"
|
|
|
|
"github.com/gorilla/sessions"
|
|
|
|
pgx "github.com/jackc/pgx/v5"
|
2024-05-05 22:10:32 +00:00
|
|
|
echo "github.com/labstack/echo/v4"
|
2024-07-25 06:01:44 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2024-05-05 22:10:32 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
//go:embed templates
|
|
|
|
templatesFS embed.FS
|
|
|
|
templatesSubFS fs.FS
|
|
|
|
allTemplates *template.Template
|
|
|
|
funcs = template.FuncMap{
|
2024-07-24 07:53:20 +00:00
|
|
|
"version": func() string {
|
2024-07-24 21:42:51 +00:00
|
|
|
return fmt.Sprintf("%s %s", config.BuildInfo.Main.Path, config.Version)
|
2024-07-24 07:53:20 +00:00
|
|
|
},
|
2024-05-05 22:10:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//go:embed static
|
|
|
|
static embed.FS
|
|
|
|
Static fs.FS
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
t := template.New("").Funcs(funcs)
|
|
|
|
|
|
|
|
var err error
|
|
|
|
templatesSubFS, err = fs.Sub(templatesFS, "templates")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
allTemplates, err = t.ParseFS(templatesSubFS, "*")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
Static, err = fs.Sub(static, "static")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Template struct {
|
|
|
|
}
|
|
|
|
|
2024-07-25 06:01:44 +00:00
|
|
|
type templateData struct {
|
|
|
|
Authenticated bool
|
|
|
|
User db.User
|
|
|
|
Version string
|
|
|
|
Data any
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *templateData) dataFromContext(c echo.Context) error {
|
|
|
|
sessionInterface := c.Get(contextKeySession)
|
|
|
|
if sessionInterface == nil {
|
|
|
|
return nil // no session, no data to populate
|
|
|
|
}
|
|
|
|
|
|
|
|
session := sessionInterface.(*sessions.Session)
|
|
|
|
|
|
|
|
userIDInterface, ok := session.Values[sessionValueAuthUser]
|
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
userID, ok := userIDInterface.(int32)
|
|
|
|
if !ok {
|
|
|
|
logrus.WithField("userID", userIDInterface).Warn("unexpected session error: user ID is not an int32")
|
|
|
|
return errors.New("user ID is not an in32")
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := c.Request().Context()
|
|
|
|
queries, conn, err := db.Get(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer conn.Close(ctx)
|
|
|
|
|
|
|
|
user, err := queries.GetUser(ctx, userID)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Authenticated = true
|
|
|
|
t.User = user
|
|
|
|
t.Version = fmt.Sprintf("%s %s", config.BuildInfo.Main.Path, config.Version)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Template) Render(w io.Writer, name string, data any, c echo.Context) error {
|
|
|
|
td := templateData{Data: data}
|
|
|
|
td.dataFromContext(c)
|
|
|
|
|
2024-05-05 22:10:32 +00:00
|
|
|
// Why does it work like this? because go's templating system doesn't handle multiple templates extending from a common base well
|
|
|
|
// just doing it normally causes every template to render the same (the import form, at time of writing, probably the last template alphabetically)
|
|
|
|
// https://stackoverflow.com/a/69244593/21894038
|
|
|
|
tmpl := template.Must(allTemplates.Clone())
|
|
|
|
tmpl = template.Must(tmpl.ParseFS(templatesSubFS, name))
|
2024-07-25 06:01:44 +00:00
|
|
|
return tmpl.ExecuteTemplate(w, name, td)
|
2024-05-05 22:10:32 +00:00
|
|
|
}
|