Refactor templates to use go:embed
This commit is contained in:
parent
266af06be3
commit
55ee291390
|
@ -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"]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
8
main.go
8
main.go
|
@ -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)),
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Elon Eats My Tweets</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{block "content" .}}{{end}}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -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}}
|
Reference in New Issue