initial post 01.01.1970


how this thing is built

templates

<!-- base.html -->
{{define "header"}}
<!DOCTYPE html>
<html lang="en">
    <head>
    ...
    </head>
    <body>
    ...
{{end}}

{{define "footer"}}
</main>
<footer>
...
</footer>
</body>
</html>
{{end}}
<!-- post.html -->
{{define "post"}}
{{template "header"}}

<h1>{{.Title}} {{.Timestamp.Format "02.01.2006"}}</h1>
<hr>

{{.Content}}

{{template "footer"}}
{{end}}
//go:embed templates/*.html
var templatesFS embed.FS

var templates = template.Must(template.ParseFS(must(fs.Sub(templatesFS, "templates")), "*.html"))

mux.HandleFunc("/404", func(w http.ResponseWriter, r *http.Request) {
    _ = templates.ExecuteTemplate(w, "404", nil)
})

posts

const (
	dateFormat = "02.01.2006"
)

//go:embed *.md
var postsFS embed.FS

var (
	postNameRE = regexp.MustCompile(`^(?P<date>\d{2}\.\d{2}\.\d{4})-(?P<title>[^.]+)\.md$`)
)

var Posts = map[string]Post{}

func init() {
	converter := goldmark.New(
		goldmark.WithExtensions(
			extension.GFM,
			highlighting.NewHighlighting(
				highlighting.WithStyle("monokai"),
			),
		),
		goldmark.WithParserOptions(
			parser.WithAutoHeadingID(),
		),
		goldmark.WithRendererOptions(
			html.WithHardWraps(),
			html.WithXHTML(),
		),
	)

	if err := fs.WalkDir(postsFS, ".", func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}

		if d.IsDir() {
			return nil
		}

		content, err := postsFS.ReadFile(path)
		if err != nil {
			return err
		}

		var buf bytes.Buffer

		if err := converter.Convert(content, &buf); err != nil {
			return err
		}

		tokens := postNameRE.FindStringSubmatch(path)

		rawDate := tokens[postNameRE.SubexpIndex("date")]
		rawTitle := tokens[postNameRE.SubexpIndex("title")]

		date, err := time.Parse(dateFormat, rawDate)
		if err != nil {
			return err
		}

		id := rawDate + rawTitle

		Posts[id] = Post{
			ID:        id,
			Timestamp: date,
			Title:     strings.ReplaceAll(rawTitle, "_", " "),
			Content:   template.HTML(buf.String()),
		}

		return nil
	}); err != nil {
		panic(err)
	}
}

type Post struct {
	ID        string
	Timestamp time.Time
	Title     string
	Content   template.HTML
}

deploy

GOOS=linux GOARCH=amd64 go build
ssh larynjahor@home "sudo systemctl stop blog"
scp blogo home:/opt/blog
ssh larynjahor@home "sudo systemctl start blog"