func postHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	pathStrings := strings.Split(r.URL.Path, "/")
	postIDString := pathStrings[2]
	postID, err := strconv.ParseInt(postIDString, 10, 64)
	if err != nil {
		err = errors.Wrap(err, "Cannot parse post id")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	var post *tumblr.Post
	for _, p := range d.board.Posts {
		if p.ID == postID {
			post = &p
			break
		}
	}
	if post == nil {
		err = errors.New("Cannot find post")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	headers := []metaHeader{
		metaHeader{"og:title", post.Title},
		metaHeader{"og:image", post.Image},
	}
	indexHandlerWithHeaders(w, r, d, headers)
}
postHandler is an http handler that validates the correctness of a post url
and returns the index page html to render it correct
package server
import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"time"
	"github.com/albertyw/reaction-pics/tumblr"
	"github.com/ikeikeikeike/go-sitemap-generator/v2/stm"
	"github.com/pkg/errors"
	"github.com/rollbar/rollbar-go"
	"go.uber.org/zap"
)
const (
	maxResults = 20
)
type metaHeader struct {
	Property string
	Content  string
}
func relToAbsPath(path string) string {
	_, filename, _, ok := runtime.Caller(0)
	if !ok {
		filename = "."
	}
	absPath := filepath.Join(filepath.Dir(filename), path)
	return absPath
}
func indexHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	if r.URL.Path != "/" && !strings.HasPrefix(r.URL.Path, "/post/") {
		err := fmt.Errorf("file not found: %s", r.URL.Path)
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	indexHandlerWithHeaders(w, r, d, []metaHeader{})
}
func indexHandlerWithHeaders(w http.ResponseWriter, r *http.Request, d handlerDeps, headers []metaHeader) {
	path := relToAbsPath("static/index.htm")
	t, err := template.ParseFiles(path)
	if err != nil {
		err = errors.Wrap(err, "Cannot read post template")
		d.logger.Error(err)
		rollbar.RequestError(rollbar.ERR, r, err)
		http.Error(w, err.Error(), 500)
		return
	}
	templateData := struct {
		CacheString string
		MetaHeaders []metaHeader
	}{
		CacheString: d.appCacheString,
		MetaHeaders: headers,
	}
	err = t.Execute(w, templateData)
	if err != nil {
		err = errors.Wrap(err, "Cannot execute template")
		d.logger.Error(err)
		rollbar.RequestError(rollbar.ERR, r, err)
		http.Error(w, err.Error(), 500)
		return
	}
}
func searchHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	query := r.URL.Query().Get("query")
	query = strings.ToLower(query)
	queriedBoard := d.board.FilterBoard(query)
	if query == "" {
		queriedBoard.RandomizePosts()
	}
	offsetString := r.URL.Query().Get("offset")
	offset, err := strconv.Atoi(offsetString)
	if err != nil {
		offset = 0
	}
	data := map[string]interface{}{
		"offset":       offset,
		"totalResults": len(queriedBoard.Posts),
	}
	queriedBoard.LimitBoard(offset, maxResults)
	queriedBoard.SortPostsByLikes()
	data["data"] = queriedBoard.PostsToJSON()
	dataBytes, _ := json.Marshal(data)
	fmt.Fprint(w, string(dataBytes))
}
func postDataHandler(w http.ResponseWriter, r *http.Request, d handlerDeps) {
	pathStrings := strings.Split(r.URL.Path, "/")
	postIDString := pathStrings[2]
	postID, err := strconv.ParseInt(postIDString, 10, 64)
	if err != nil {
		err = errors.Wrap(err, "Cannot parse post id")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	post := d.board.GetPostByID(postID)
	if post == nil {
		err = errors.New("Cannot find post")
		d.logger.Warn(err)
		rollbar.RequestError(rollbar.WARN, r, err)
		http.NotFound(w, r)
		return
	}
	data := map[string]interface{}{
		"offset":       0,
		"totalResults": 1,
		"data":         []tumblr.PostJSON{post.ToJSONStruct()},
	}
	marshalledPost, _ := json.Marshal(data)
	fmt.Fprint(w, string(marshalledPost))
}