Refactor templates to use go:embed

This commit is contained in:
Rob Watson 2022-05-21 09:27:51 +02:00
parent 266af06be3
commit 55ee291390
8 changed files with 35 additions and 27 deletions

View File

@ -12,8 +12,5 @@ RUN go build -o ./elon .
FROM alpine:3.14 FROM alpine:3.14
COPY --from=go-builder /app/elon /app/elon COPY --from=go-builder /app/elon /app/elon
COPY --from=go-builder /app/public /app/public
ENV ELON_PUBLIC_PATH "/app/public"
ENTRYPOINT ["/app/elon"] ENTRYPOINT ["/app/elon"]

View File

@ -9,7 +9,6 @@ type TwitterConfig struct {
ClientID, ClientSecret, CallbackURL, AuthorizeURL, TokenURL string ClientID, ClientSecret, CallbackURL, AuthorizeURL, TokenURL string
} }
type Config struct { type Config struct {
PublicPath string
DatabaseURL string DatabaseURL string
SessionKey string SessionKey string
ListenAddr string ListenAddr string
@ -26,7 +25,6 @@ func NewFromEnv() (Config, error) {
return Config{}, errors.New("missing ELON_SESSION_KEY") return Config{}, errors.New("missing ELON_SESSION_KEY")
} }
return Config{ return Config{
PublicPath: os.Getenv("ELON_PUBLIC_PATH"),
DatabaseURL: os.Getenv("ELON_DATABASE_URL"), DatabaseURL: os.Getenv("ELON_DATABASE_URL"),
SessionKey: sessionKey, SessionKey: sessionKey,
ListenAddr: listenAddr, ListenAddr: listenAddr,

View File

@ -5,12 +5,12 @@ package httpserver
import ( import (
"context" "context"
"html/template"
"net/http" "net/http"
"time" "time"
"git.netflux.io/rob/elon-eats-my-tweets/config" "git.netflux.io/rob/elon-eats-my-tweets/config"
"git.netflux.io/rob/elon-eats-my-tweets/generated/store" "git.netflux.io/rob/elon-eats-my-tweets/generated/store"
"git.netflux.io/rob/elon-eats-my-tweets/templates"
"git.netflux.io/rob/elon-eats-my-tweets/twitter" "git.netflux.io/rob/elon-eats-my-tweets/twitter"
"github.com/go-chi/chi" "github.com/go-chi/chi"
"github.com/go-chi/chi/middleware" "github.com/go-chi/chi/middleware"
@ -32,7 +32,6 @@ type TwitterAPIClient interface {
} }
type handler struct { type handler struct {
templates *template.Template
store twitter.Store store twitter.Store
twitterAPIClientFunc func(c *http.Client) TwitterAPIClient twitterAPIClientFunc func(c *http.Client) TwitterAPIClient
oauth2Config *oauth2.Config oauth2Config *oauth2.Config
@ -41,7 +40,7 @@ type handler struct {
logger *zap.SugaredLogger logger *zap.SugaredLogger
} }
func NewHandler(cfg config.Config, templates *template.Template, store twitter.Store, twitterAPIClientFunc func(c *http.Client) TwitterAPIClient, sessionStore sessions.Store, tokenGenerator TokenGenerator, logger *zap.Logger) http.Handler { func NewHandler(cfg config.Config, store twitter.Store, twitterAPIClientFunc func(c *http.Client) TwitterAPIClient, sessionStore sessions.Store, tokenGenerator TokenGenerator, logger *zap.Logger) http.Handler {
r := chi.NewRouter() r := chi.NewRouter()
r.Use(middleware.RequestID) r.Use(middleware.RequestID)
r.Use(middleware.RealIP) r.Use(middleware.RealIP)
@ -49,7 +48,6 @@ func NewHandler(cfg config.Config, templates *template.Template, store twitter.S
r.Use(middleware.Recoverer) r.Use(middleware.Recoverer)
h := handler{ h := handler{
templates: templates,
store: store, store: store,
twitterAPIClientFunc: twitterAPIClientFunc, twitterAPIClientFunc: twitterAPIClientFunc,
oauth2Config: &oauth2.Config{ oauth2Config: &oauth2.Config{
@ -76,7 +74,7 @@ func NewHandler(cfg config.Config, templates *template.Template, store twitter.S
} }
func (h *handler) getIndex(w http.ResponseWriter, r *http.Request) { func (h *handler) getIndex(w http.ResponseWriter, r *http.Request) {
if err := h.templates.ExecuteTemplate(w, "index", nil); err != nil { if err := templates.Execute(w, "index.html", nil); err != nil {
h.logger.With("err", err).Error("error rendering template") h.logger.With("err", err).Error("error rendering template")
http.Error(w, "error rendering template", http.StatusInternalServerError) http.Error(w, "error rendering template", http.StatusInternalServerError)
return return

View File

@ -2,11 +2,9 @@ package httpserver_test
import ( import (
"errors" "errors"
"html/template"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"path/filepath"
"testing" "testing"
"git.netflux.io/rob/elon-eats-my-tweets/config" "git.netflux.io/rob/elon-eats-my-tweets/config"
@ -21,8 +19,6 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
var templates = template.Must(template.ParseGlob(filepath.Join("..", "public", "views", "*.html")))
// mockTokenGenerator implements httpserver.TokenGenerator. // mockTokenGenerator implements httpserver.TokenGenerator.
type mockTokenGenerator struct { type mockTokenGenerator struct {
i int i int
@ -44,7 +40,6 @@ func TestGetIndex(t *testing.T) {
handler := httpserver.NewHandler( handler := httpserver.NewHandler(
config.Config{}, config.Config{},
templates,
&mocks.Store{}, &mocks.Store{},
func(*http.Client) httpserver.TwitterAPIClient { return &mocks.TwitterAPIClient{} }, func(*http.Client) httpserver.TwitterAPIClient { return &mocks.TwitterAPIClient{} },
&mocks.SessionStore{}, &mocks.SessionStore{},
@ -100,7 +95,6 @@ func TestLogin(t *testing.T) {
TokenURL: "https://www.example.com/oauth/token", TokenURL: "https://www.example.com/oauth/token",
}, },
}, },
templates,
&mocks.Store{}, &mocks.Store{},
func(*http.Client) httpserver.TwitterAPIClient { return &mocks.TwitterAPIClient{} }, func(*http.Client) httpserver.TwitterAPIClient { return &mocks.TwitterAPIClient{} },
&mockSessionStore, &mockSessionStore,
@ -266,7 +260,6 @@ func TestCallback(t *testing.T) {
TokenURL: srv.URL + "/oauth/token", TokenURL: srv.URL + "/oauth/token",
}, },
}, },
templates,
&mockStore, &mockStore,
func(*http.Client) httpserver.TwitterAPIClient { return &mockTwitterClient }, func(*http.Client) httpserver.TwitterAPIClient { return &mockTwitterClient },
&mockSessionStore, &mockSessionStore,

View File

@ -4,10 +4,8 @@ package main
import ( import (
"context" "context"
"html/template"
"log" "log"
"net/http" "net/http"
"path/filepath"
"git.netflux.io/rob/elon-eats-my-tweets/config" "git.netflux.io/rob/elon-eats-my-tweets/config"
"git.netflux.io/rob/elon-eats-my-tweets/generated/store" "git.netflux.io/rob/elon-eats-my-tweets/generated/store"
@ -29,11 +27,6 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
templates, err := template.ParseGlob(filepath.Join(cfg.PublicPath, "views", "*.html"))
if err != nil {
log.Fatalf("error loading templates: %s", err)
}
ctx := context.Background() ctx := context.Background()
dbconn, err := pgxpool.Connect(ctx, cfg.DatabaseURL) dbconn, err := pgxpool.Connect(ctx, cfg.DatabaseURL)
if err != nil { if err != nil {
@ -44,7 +37,6 @@ func main() {
handler := httpserver.NewHandler( handler := httpserver.NewHandler(
cfg, cfg,
templates,
store, store,
func(c *http.Client) httpserver.TwitterAPIClient { return twitter.NewAPIClient(c) }, func(c *http.Client) httpserver.TwitterAPIClient { return twitter.NewAPIClient(c) },
sessions.NewCookieStore([]byte(cfg.SessionKey)), sessions.NewCookieStore([]byte(cfg.SessionKey)),

20
templates/templates.go Normal file
View File

@ -0,0 +1,20 @@
package templates
import (
"embed"
"html/template"
"io"
)
//go:embed views/*.html
var tmplFS embed.FS
var templatesMap *template.Template
func init() {
templatesMap = template.Must(template.ParseFS(tmplFS, "views/*.html"))
}
func Execute(w io.Writer, name string, data any) error {
return templatesMap.ExecuteTemplate(w, name, data)
}

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Elon Eats My Tweets</title>
</head>
<body>
{{block "content" .}}{{end}}
</body>
</html>

View File

@ -1,8 +1,9 @@
{{define "index"}} {{template "base.html" .}}
{{define "content"}}
<h1>Elon eats my tweets<h1> <h1>Elon eats my tweets<h1>
<p>Sick of Elon? Tired of Twitter? Have Elon Musk delete your tweets for you.</p> <p>Sick of Elon? Tired of Twitter? Have Elon Musk delete your tweets for you.</p>
<a href="/login">Sign in with Twitter</a> <a href="/login">Sign in with Twitter</a>
{{end}} {{end}}