~evanj/evanjon.es

420d2707f2d423977b12534af72c1c90c8dddbe2 — Evan M Jones 1 year, 5 months ago 1945c2a
Feat(prev/next): Adding previous and next post links.
M evanjon.es.go => evanjon.es.go +35 -5
@@ 35,6 35,12 @@ func New(out io.Writer) *App {
	l := log.New(out, "[evanjon.es] ", 0)
	e := c.New(log.New(out, "[evanjon.es:baseRouter] ", 0))

	typePage, err1 := strconv.Atoi(os.Getenv("CMS_TYPE_PAGE"))
	typePost, err2 := strconv.Atoi(os.Getenv("CMS_TYPE_POST"))
	if err1 != nil || err2 != nil {
		l.Fatal("contenttype not set correctly")
	}

	space, err := strconv.Atoi(os.Getenv("CMS_SPACE"))
	if err != nil {
		l.Fatal("invalid space id")


@@ 54,11 60,33 @@ func New(out io.Writer) *App {
	)

	return &App{
		log:          l,
		baseRouter:   e,
		homeRouter:   home.New(e, log.New(out, "[evanjon.es:home] ", 0), cms),
		searchRouter: search.New(e, log.New(out, "[evanjon.es:search] ", 0), cms),
		listRouter:   list.New(e, log.New(out, "[evanjon.es:list] ", 0), cms),
		log:        l,
		baseRouter: e,

		homeRouter: home.New(
			e,
			log.New(out, "[evanjon.es:home] ", 0),
			cms,
			typePage,
			typePost,
		),

		searchRouter: search.New(
			e,
			log.New(out, "[evanjon.es:search] ", 0),
			cms,
			typePage,
			typePost,
		),

		listRouter: list.New(
			e,
			log.New(out, "[evanjon.es:list] ", 0),
			cms,
			typePage,
			typePost,
		),

		// staticRouter: http.FileServer(http.Dir(".")),
		cacheRouter: cms,
		rssRouter: rss.New(


@@ 72,6 100,8 @@ func New(out io.Writer) *App {
				"Evan Jones",
				"me@evanjon.es",
			),
			typePage,
			typePost,
		),
	}
}

M internal/c/home/home.go => internal/c/home/home.go +6 -54
@@ 17,8 17,9 @@ var (

type HomeEndpoint struct {
	*c.Endpoint
	log *log.Logger
	cms cmsProvider
	log                *log.Logger
	cms                cmsProvider
	typePage, typePost int
}

type cmsProvider interface {


@@ 26,68 27,19 @@ type cmsProvider interface {
	List(ctx context.Context, typeID int) ([]content.Content, error)
}

func New(base *c.Endpoint, log *log.Logger, cms cmsProvider) *HomeEndpoint {
func New(base *c.Endpoint, log *log.Logger, cms cmsProvider, typePage, typePost int) *HomeEndpoint {
	return &HomeEndpoint{
		base,
		log,
		cms,
		typePage, typePost,
	}
}

func (e *HomeEndpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// ctx := e.ContextTimeout(r)
	// e.log.Println("finding home content")

	// var eg errgroup.Group
	// cCh := make(chan content.Content, 1)
	// lCh := make(chan []content.Content, 1)

	// defer close(cCh)
	// defer close(lCh)

	// eg.Go(func() error {
	// 	c, err := e.cms.Find(ctx, content.TypePage, "slug", "ahoj")
	// 	if err != nil {
	// 		return err
	// 	}
	// 	cCh <- c
	// 	return nil
	// })

	// eg.Go(func() error {
	// 	l, err := e.cms.List(ctx, content.TypePost)
	// 	if err != nil {
	// 		return err
	// 	}
	// 	lCh <- l
	// 	return nil
	// })

	// if err := eg.Wait(); err != nil {
	// 	e.log.Println("failed to find home content", err)
	// 	w.WriteHeader(http.StatusNotFound)
	// 	w.Write([]byte("failed find home content"))
	// 	return
	// }

	// c := <-cCh
	// l := <-lCh

	// max := 5 // TODO: Implement this at CMS level (current pagination is hardcoded).
	// if len(l) > max-1 {
	// 	l = l[:max]
	// }

	// e.log.Println("found home content")
	// e.HTML(w, r, ItemHTML, map[string]interface{}{
	// 	"Item":     c,
	// 	"List":     l,
	// 	"ShowDate": c.Type() != content.TypePage,
	// })

	ctx := e.ContextTimeout(r)

	c, err := e.cms.Find(ctx, content.TypePage, "slug", "next-ahoj")
	c, err := e.cms.Find(ctx, e.typePage, "slug", "home")
	if err != nil {
		e.log.Println("failed to find home content", err)
		w.WriteHeader(http.StatusNotFound)

M internal/c/list/list.go => internal/c/list/list.go +7 -5
@@ 16,28 16,30 @@ var (

type ListEndpoint struct {
	*c.Endpoint
	log *log.Logger
	cms cmsProvider
	log                *log.Logger
	cms                cmsProvider
	typePage, typePost int
}

type cmsProvider interface {
	List(ctx context.Context, typeID int) ([]content.Content, error)
}

func New(base *c.Endpoint, log *log.Logger, cms cmsProvider) *ListEndpoint {
func New(base *c.Endpoint, log *log.Logger, cms cmsProvider, typePage, typePost int) *ListEndpoint {
	return &ListEndpoint{
		base,
		log,
		cms,
		typePage, typePost,
	}
}

func (e *ListEndpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := e.ContextTimeout(r)

	e.log.Println("searching for list with type of:", content.TypePost)
	e.log.Println("searching for list with type of:", e.typePost)

	l, err := e.cms.List(ctx, content.TypePost)
	l, err := e.cms.List(ctx, e.typePost)
	if err != nil {
		e.log.Println("failed to find list", err)
		w.WriteHeader(http.StatusNotFound)

M internal/c/rss/rss.go => internal/c/rss/rss.go +7 -5
@@ 11,9 11,10 @@ import (

type ListEndpoint struct {
	*c.Endpoint
	log *log.Logger
	cms cmsProvider
	rss rssProvider
	log                *log.Logger
	cms                cmsProvider
	rss                rssProvider
	typePage, typePost int
}

type cmsProvider interface {


@@ 24,19 25,20 @@ type rssProvider interface {
	List(items []content.Content) (string, error)
}

func New(base *c.Endpoint, log *log.Logger, cms cmsProvider, rss rssProvider) *ListEndpoint {
func New(base *c.Endpoint, log *log.Logger, cms cmsProvider, rss rssProvider, typePage, typePost int) *ListEndpoint {
	return &ListEndpoint{
		base,
		log,
		cms,
		rss,
		typePage, typePost,
	}
}

func (e *ListEndpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ctx := e.ContextTimeout(r)

	l, err := e.cms.List(ctx, content.TypePost)
	l, err := e.cms.List(ctx, e.typePost)
	if err != nil {
		e.log.Println(err)
		w.WriteHeader(http.StatusNotFound)

M internal/c/search/search.go => internal/c/search/search.go +8 -6
@@ 16,19 16,21 @@ var (

type SearchEndpoint struct {
	*c.Endpoint
	log *log.Logger
	cms cmsProvider
	log                *log.Logger
	cms                cmsProvider
	typePage, typePost int
}

type cmsProvider interface {
	Find(ctx context.Context, typeID int, field, query string) (content.Content, error)
}

func New(base *c.Endpoint, log *log.Logger, cms cmsProvider) *SearchEndpoint {
func New(base *c.Endpoint, log *log.Logger, cms cmsProvider, typePage, typePost int) *SearchEndpoint {
	return &SearchEndpoint{
		base,
		log,
		cms,
		typePage, typePost,
	}
}



@@ 46,8 48,8 @@ func (e *SearchEndpoint) search(ctx context.Context, slug string) (content.Conte
	contentCh := make(chan content.Content)
	errCh := make(chan error)

	go do(ctx, content.TypePage, slug, contentCh, errCh)
	go do(ctx, content.TypePost, slug, contentCh, errCh)
	go do(ctx, e.typePost, slug, contentCh, errCh)
	go do(ctx, e.typePost, slug, contentCh, errCh)

	errcount := 0



@@ 88,6 90,6 @@ func (e *SearchEndpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	e.HTML(w, r, ItemHTML, map[string]interface{}{
		"Item":     c,
		"ShowDate": c.Type() != content.TypePage,
		"ShowDate": c.Type() != e.typePage,
	})
}

M internal/m/content/content.go => internal/m/content/content.go +0 -5
@@ 5,11 5,6 @@ import (
	"time"
)

const (
	TypePost = 14
	TypePage = 17
)

type Content interface {
	Name() string
	Slug() string

M internal/s/cms/cms.go => internal/s/cms/cms.go +20 -0
@@ 34,6 34,7 @@ type Content struct {
	hasImage                          bool
	imageURL                          string
	typ                               int
	prev, next                        *Content
}

type provider interface {


@@ 112,6 113,17 @@ func (cms *CMS) transform(in *orig.Content) (*Content, error) {
		return t, nil
	}

	getref := func(key string) *Content {
		for _, item := range in.ContentValues {
			if item.FieldName == key {
				// We don't care about errors here. Content can be bogus.
				ref, _ := cms.transform(&item.FieldReference)
				return ref
			}
		}
		return nil
	}

	name, err := getstr("name")
	if err != nil {
		return nil, err


@@ 154,6 166,8 @@ func (cms *CMS) transform(in *orig.Content) (*Content, error) {
		desc:      desc,
		published: publishDate,
		typ:       typ,
		prev:      getref("prev"),
		next:      getref("next"),
		// hasImage:  imageURL != "",
		// imageURL:  imageURL,
		// // TODO: Reference, ReferenceList


@@ 295,3 309,9 @@ func (c *Content) Desc() template.HTML {
func (c *Content) Type() int {
	return c.typ
}

func (c *Content) HasPrev() bool  { return c.prev != nil }
func (c *Content) Prev() *Content { return c.prev }

func (c *Content) HasNext() bool  { return c.next != nil }
func (c *Content) Next() *Content { return c.next }

M internal/s/tmpl/html/item.html => internal/s/tmpl/html/item.html +5 -0
@@ 22,6 22,11 @@
    </header>
    <article>{{ .Item.Desc }}</article>
    <hr>
    <div>
      {{ if .Item.HasPrev }}<div><a href='/{{.Item.Prev.Slug}}'>⬅️ {{.Item.Prev.Name}}</div></a>{{ end }}
      {{ if .Item.HasNext }}<div><a href='/{{.Item.Next.Slug}}'>{{.Item.Next.Name}} ➡️</div></a>{{ end }}
    </div>
    <hr>
    <nav>
      <a href='/'>Home</a>,
      <a href='/blog'>Blog</a>

M internal/s/tmpl/tmpls_embed.go => internal/s/tmpl/tmpls_embed.go +1 -1
@@ 20,7 20,7 @@ func init() {

	tmpls["html/index.html"] = tostring("PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ZW4+CjxoZWFkPgogIHt7IHRlbXBsYXRlICJodG1sL19oZWFkLmh0bWwiIH19CiAgPHRpdGxlPkV2YW4ncyBTaXRlPC90aXRsZT4KICA8bWV0YSBuYW1lPWRlc2NyaXB0aW9uIGNvbnRlbnQ9Int7IC5JdGVtLlNob3J0IH19Ij4KPC9oZWFkPgo8Ym9keT4KICA8c3R5bGU+e3sgdGVtcGxhdGUgImNzcy9tYWluLmNzcyIgfX08L3N0eWxlPgogIDxtYWluPgogICAgPGhlYWRlcj4KICAgICAgPHRhYmxlPiAKICAgICAgICA8dHI+CiAgICAgICAgICA8dGQgdmFsaWduPWJvdHRvbSB3aWR0aD05NCBoZWlnaHQ9Nzk+CiAgICAgICAgICAgIDxpbWcgYWx0PSJUaGUgJ2JyYW5kJyBpbWFnZSBvZiB0aGUgd2Vic2l0ZTsgaXQncyBhIGNsb3NlIG9mIHVwIENoYXJsZXMnIGZhY2UgZnJvbSBFdXJla2EgNyIgc3JjPSIvL2UzLmV2YW5qb24uZXMvYjg4OWJkM2UtNDQwNC00OWI3LTZhMjUtNmNlMjgzODg2OGY2LmpwZyIvPgogICAgICAgICAgPC90ZD4KICAgICAgICAgIDx0ZCB2YWxpZ249Ym90dG9tPgogICAgICAgICAgICA8aDE+PGEgaHJlZj0nLyc+e3sgLkl0ZW0uTmFtZSB9fTwvYT48L2gxPgogICAgICAgICAgPC90ZD4KICAgICAgICA8L3RyPgogICAgICA8L3RhYmxlPgogICAgPC9oZWFkZXI+CiAgICA8YXJ0aWNsZT57eyAuSXRlbS5EZXNjIH19PC9hcnRpY2xlPgogICAgPGhyPgogICAgPGEgaHJlZj0naHR0cHM6Ly9naXQuc3IuaHQvfmV2YW5qL2V2YW5qb24uZXMvJz5Tb3VyY2UgQ29kZTwvYT4sIAogICAgPGEgaHJlZj0nbWFpbHRvOm1lQGV2YW5qb24uZXMnPkVtYWlsPC9hPiwgCiAgICA8YSBocmVmPScvcnNzJz5SU1MgRmVlZDwvYT4sIAogICAgPGEgaHJlZj0naHR0cHM6Ly9lMy5ldmFuam9uLmVzLzI1ZGNlNjAxLTdkYWItNDY0Ny02ZjU0LTRkMjcxMGVmMThmYS5ncGcnPkdQRzwvYT4KICAgIDxocj4KICAgIDxmb290ZXI+TGFzdCBlZGl0IHt7IC5JdGVtLlByZXR0eURhdGUgfX08L2Zvb3Rlcj4KICA8L21haW4+CjwvYm9keT4KCjwvaHRtbD4K")

	tmpls["html/item.html"] = tostring("PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ZW4+CjxoZWFkPgogIHt7IHRlbXBsYXRlICJodG1sL19oZWFkLmh0bWwiIH19CiAgPHRpdGxlPkV2YW4ncyBTaXRlPC90aXRsZT4KICA8bWV0YSBuYW1lPWRlc2NyaXB0aW9uIGNvbnRlbnQ9Int7IC5JdGVtLlNob3J0IH19Ij4KPC9oZWFkPgo8Ym9keT4KICA8c3R5bGU+e3sgdGVtcGxhdGUgImNzcy9tYWluLmNzcyIgfX08L3N0eWxlPgogIDxtYWluPgogICAgPGhlYWRlcj4KICAgICAgPHRhYmxlPiAKICAgICAgICA8dHI+CiAgICAgICAgICA8dGQgdmFsaWduPWJvdHRvbSB3aWR0aD05NCBoZWlnaHQ9Nzk+CiAgICAgICAgICAgIDxpbWcgYWx0PSJUaGUgJ2JyYW5kJyBpbWFnZSBvZiB0aGUgd2Vic2l0ZTsgaXQncyBhIGNsb3NlIG9mIHVwIENoYXJsZXMnIGZhY2UgZnJvbSBFdXJla2EgNyIgc3JjPSIvL2UzLmV2YW5qb24uZXMvYjg4OWJkM2UtNDQwNC00OWI3LTZhMjUtNmNlMjgzODg2OGY2LmpwZyIvPgogICAgICAgICAgPC90ZD4KICAgICAgICAgIDx0ZCB2YWxpZ249Ym90dG9tPgogICAgICAgICAgICA8aDE+PGEgaHJlZj0nL3t7IC5JdGVtLlNsdWcgfX0nPnt7IC5JdGVtLk5hbWUgfX08L2E+PC9oMT4KICAgICAgICAgIDwvdGQ+CiAgICAgICAgPC90cj4KICAgICAgPC90YWJsZT4KICAgIDwvaGVhZGVyPgogICAgPGFydGljbGU+e3sgLkl0ZW0uRGVzYyB9fTwvYXJ0aWNsZT4KICAgIDxocj4KICAgIDxuYXY+CiAgICAgIDxhIGhyZWY9Jy8nPkhvbWU8L2E+LAogICAgICA8YSBocmVmPScvYmxvZyc+QmxvZzwvYT4KICAgIDwvbmF2PgogICAgPGhyPgogICAgPGZvb3Rlcj5MYXN0IGVkaXQge3sgLkl0ZW0uUHJldHR5RGF0ZSB9fTwvZm9vdGVyPgogIDwvbWFpbj4KPC9ib2R5Pgo8L2h0bWw+Cg==")
	tmpls["html/item.html"] = tostring("PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ZW4+CjxoZWFkPgogIHt7IHRlbXBsYXRlICJodG1sL19oZWFkLmh0bWwiIH19CiAgPHRpdGxlPkV2YW4ncyBTaXRlPC90aXRsZT4KICA8bWV0YSBuYW1lPWRlc2NyaXB0aW9uIGNvbnRlbnQ9Int7IC5JdGVtLlNob3J0IH19Ij4KPC9oZWFkPgo8Ym9keT4KICA8c3R5bGU+e3sgdGVtcGxhdGUgImNzcy9tYWluLmNzcyIgfX08L3N0eWxlPgogIDxtYWluPgogICAgPGhlYWRlcj4KICAgICAgPHRhYmxlPiAKICAgICAgICA8dHI+CiAgICAgICAgICA8dGQgdmFsaWduPWJvdHRvbSB3aWR0aD05NCBoZWlnaHQ9Nzk+CiAgICAgICAgICAgIDxpbWcgYWx0PSJUaGUgJ2JyYW5kJyBpbWFnZSBvZiB0aGUgd2Vic2l0ZTsgaXQncyBhIGNsb3NlIG9mIHVwIENoYXJsZXMnIGZhY2UgZnJvbSBFdXJla2EgNyIgc3JjPSIvL2UzLmV2YW5qb24uZXMvYjg4OWJkM2UtNDQwNC00OWI3LTZhMjUtNmNlMjgzODg2OGY2LmpwZyIvPgogICAgICAgICAgPC90ZD4KICAgICAgICAgIDx0ZCB2YWxpZ249Ym90dG9tPgogICAgICAgICAgICA8aDE+PGEgaHJlZj0nL3t7IC5JdGVtLlNsdWcgfX0nPnt7IC5JdGVtLk5hbWUgfX08L2E+PC9oMT4KICAgICAgICAgIDwvdGQ+CiAgICAgICAgPC90cj4KICAgICAgPC90YWJsZT4KICAgIDwvaGVhZGVyPgogICAgPGFydGljbGU+e3sgLkl0ZW0uRGVzYyB9fTwvYXJ0aWNsZT4KICAgIDxocj4KICAgIDxkaXY+CiAgICAgIHt7IGlmIC5JdGVtLkhhc1ByZXYgfX08ZGl2PjxhIGhyZWY9Jy97ey5JdGVtLlByZXYuU2x1Z319Jz7irIXvuI8ge3suSXRlbS5QcmV2Lk5hbWV9fTwvZGl2PjwvYT57eyBlbmQgfX0KICAgICAge3sgaWYgLkl0ZW0uSGFzTmV4dCB9fTxkaXY+PGEgaHJlZj0nL3t7Lkl0ZW0uTmV4dC5TbHVnfX0nPnt7Lkl0ZW0uTmV4dC5OYW1lfX0g4p6h77iPPC9kaXY+PC9hPnt7IGVuZCB9fQogICAgPC9kaXY+CiAgICA8aHI+CiAgICA8bmF2PgogICAgICA8YSBocmVmPScvJz5Ib21lPC9hPiwKICAgICAgPGEgaHJlZj0nL2Jsb2cnPkJsb2c8L2E+CiAgICA8L25hdj4KICAgIDxocj4KICAgIDxmb290ZXI+TGFzdCBlZGl0IHt7IC5JdGVtLlByZXR0eURhdGUgfX08L2Zvb3Rlcj4KICA8L21haW4+CjwvYm9keT4KPC9odG1sPgo=")

	tmpls["html/list.html"] = tostring("PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ZW4+CjxoZWFkPgogIHt7IHRlbXBsYXRlICJodG1sL19oZWFkLmh0bWwiIH19CiAgPHRpdGxlPkV2YW4ncyBTaXRlPC90aXRsZT4KICA8bWV0YSBuYW1lPWRlc2NyaXB0aW9uIGNvbnRlbnQ9Int7IC5JdGVtLlNob3J0IH19Ij4KPC9oZWFkPgo8Ym9keSBjbGFzcz1saXN0PgogIDxzdHlsZT57eyB0ZW1wbGF0ZSAiY3NzL21haW4uY3NzIiB9fTwvc3R5bGU+CiAgPG1haW4+CiAgICB7eyByYW5nZSAuTGlzdCB9fQogICAgPGEgaHJlZj0nL3t7IC5TbHVnIH19Jz4KICAgICAgPHNlY3Rpb24+CiAgICAgICAgPGgyPnt7IC5OYW1lIH19PC9oMj4KICAgICAgICA8ZGl2Pnt7IC5QcmV0dHlEYXRlIH19PC9kaXY+CiAgICAgICAgPGRpdj57eyAuU2hvcnQgfX08L2Rpdj4KICAgICAgPC9zZWN0aW9uPgogICAgPC9hPgogICAgPGJyLz4gCiAgICB7eyBlbmQgfX0KICA8L21haW4+CjwvYm9keT4KCjwvaHRtbD4K")


M makefile => makefile +7 -3
@@ 1,14 1,17 @@
BIN=evanjon.es
ENV=`cat .env`
SRCS=`find . \( -name '*.go' -not -name '*_embed.go' -o -name '*.html' -o -name '*.css' -o -name '*.js' \)`

all: vendor gen build
all: setup vendor gen build

setup:
	@go get git.sr.ht/~evanj/embed

vendor: go.mod go.sum
	@go mod tidy
	@go mod vendor

build:
	@clear
	@go build -ldflags='-s -w' -o $(BIN)

gen: 


@@ 18,7 21,8 @@ test:
	@go test ./...

run: gen build
	@clear
	@env $(ENV) ./$(BIN)

dev:
	@ls $(SRCS) | entr -cr make run
	@find * -not -name '*_embed.go' | grep -E '*.(go|js|css|html)' | entr -r make run

M pkg/cms/cms.go => pkg/cms/cms.go +5 -4
@@ 29,13 29,14 @@ type Content struct {
type ContentList struct {
	ContentList []Content
	ContentMore bool
	// ContentLast Content
}

type ContentValue struct {
	FieldType  string
	FieldName  string
	FieldValue string
	FieldType          string
	FieldName          string
	FieldValue         string
	FieldReference     Content
	FieldReferenceList []Content
}

func New(user, pass, baseURL string, space int) *CMS {