Start moving to static

Signed-off-by: jolheiser <john.olheiser@gmail.com>
jolheiser 2022-07-07 00:05:18 -05:00
parent 447c88febe
commit fd06b70fc9
Signed by: jolheiser
GPG Key ID: B853ADA5DA7BBF7A
11 changed files with 175 additions and 155 deletions

View File

@ -1,17 +0,0 @@
---
kind: pipeline
name: compliance
trigger:
event:
- pull_request
steps:
- name: test
pull: always
image: golang:1.17
commands:
- go test -race ./...
- name: vet
pull: always
image: golang:1.17
commands:
- go vet -race ./...

23
.goreleaser.yaml 100644
View File

@ -0,0 +1,23 @@
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
archives:
- replacements:
386: i386
amd64: x86_64
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
release:
gitea:
owner: jolheiser
name: blog
gitea_urls:
api: https://git.jojodev.com/api/v1/
download: https://git.jojodev.com

View File

@ -0,0 +1,39 @@
clone:
git:
image: woodpeckerci/plugin-git
settings:
tags: true
pipeline:
compliance:
image: golang:1.18
commands:
- go test -race ./...
- go vet ./...
- go run github.com/rs/zerolog/cmd/lint@latest go.jolheiser.com/blog
when:
event: pull_request
build:
image: goreleaser/goreleaser
commands:
- goreleaser build --snapshot
when:
event: pull_request
release:
image: goreleaser/goreleaser
commands:
- goreleaser release
secrets: [ gitea_token ]
when:
event: tag
prune:
image: jolheiser/drone-gitea-prune
settings:
base: https://git.jojodev.com
token:
from_secret: gitea_token
when:
event: tag

View File

@ -1,75 +0,0 @@
package main
import (
"fmt"
"github.com/goyek/goyek"
)
const zerologLintVer = "1.24.0"
func main() {
flow().Main()
}
func flow() *goyek.Flow {
flow := &goyek.Flow{}
fmt := flow.Register(taskFmt)
test := flow.Register(taskTest)
vet := flow.Register(taskVet)
lint := flow.Register(taskLint)
flow.DefaultTask = flow.Register(goyek.Task{
Name: "all",
Usage: "Run all flows",
Deps: []goyek.RegisteredTask{
fmt,
lint,
test,
vet,
},
})
return flow
}
var taskFmt = goyek.Task{
Name: "fmt",
Usage: "go fmt",
Action: func(tf *goyek.TF) {
if err := tf.Cmd("go", "fmt", "./...").Run(); err != nil {
tf.Fatal(err)
}
},
}
var taskTest = goyek.Task{
Name: "test",
Usage: "go test",
Action: func(tf *goyek.TF) {
if err := tf.Cmd("go", "test", "-race", "./...").Run(); err != nil {
tf.Fatal(err)
}
},
}
var taskVet = goyek.Task{
Name: "vet",
Usage: "go vet",
Action: func(tf *goyek.TF) {
if err := tf.Cmd("go", "vet", "-race", "./...").Run(); err != nil {
tf.Fatal(err)
}
},
}
var taskLint = goyek.Task{
Name: "lint",
Usage: "Run linter(s)",
Action: func(tf *goyek.TF) {
if err := tf.Cmd("go", "run", fmt.Sprintf("github.com/rs/zerolog/cmd/lint@v%s", zerologLintVer), ".").Run(); err != nil {
tf.Fatal(err)
}
},
}

5
go.mod
View File

@ -5,7 +5,6 @@ go 1.17
require ( require (
github.com/alecthomas/chroma v0.7.2-0.20200305040604-4f3623dce67a github.com/alecthomas/chroma v0.7.2-0.20200305040604-4f3623dce67a
github.com/go-chi/chi/v5 v5.0.4 github.com/go-chi/chi/v5 v5.0.4
github.com/goyek/goyek v0.6.0
github.com/pelletier/go-toml/v2 v2.0.0-beta.3 github.com/pelletier/go-toml/v2 v2.0.0-beta.3
github.com/peterbourgon/ff/v3 v3.1.0 github.com/peterbourgon/ff/v3 v3.1.0
github.com/rs/zerolog v1.24.0 github.com/rs/zerolog v1.24.0
@ -16,6 +15,8 @@ require (
) )
require ( require (
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae // indirect
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
github.com/dlclark/regexp2 v1.2.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
) )

11
go.sum
View File

@ -6,8 +6,9 @@ github.com/alecthomas/chroma v0.7.2-0.20200305040604-4f3623dce67a/go.mod h1:fv5S
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI= github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
@ -15,13 +16,12 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/go-chi/chi/v5 v5.0.4 h1:5e494iHzsYBiyXQAHHuI4tyJS9M3V84OuX3ufIIGHFo= github.com/go-chi/chi/v5 v5.0.4 h1:5e494iHzsYBiyXQAHHuI4tyJS9M3V84OuX3ufIIGHFo=
github.com/go-chi/chi/v5 v5.0.4/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.0.4/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/goyek/goyek v0.6.0 h1:2YQ4V3X7q+zFF98IBWMc1WRwfzs0TQ8jrwOKY3XRQRk=
github.com/goyek/goyek v0.6.0/go.mod h1:UGjZz3juJL2l2eMqRbxQYjG8ieyKb7WMYPv0KB0KVxA=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@ -72,8 +72,9 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

View File

@ -41,6 +41,7 @@ func main() {
r := router.New(b) r := router.New(b)
go func() { go func() {
log.Info().Msgf("Listening at http://localhost:%d", *port)
if err := http.ListenAndServe(fmt.Sprintf(":%d", *port), r); err != nil { if err := http.ListenAndServe(fmt.Sprintf(":%d", *port), r); err != nil {
log.Error().Err(err).Msg("could not open server") log.Error().Err(err).Msg("could not open server")
} }

View File

@ -1,29 +1,32 @@
package post package post
import ( import (
"html/template"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
"sync"
"time" "time"
"go.jolheiser.com/blog/markdown" "go.jolheiser.com/blog/markdown"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"go.jolheiser.com/emdbed"
) )
func NewBlog(basePath string) (*Blog, error) { func NewBlog(basePath string) (*Blog, error) {
posts, err := Scan(basePath) posts, err := scan(basePath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Blog{ return &Blog{
Path: basePath, Path: basePath,
Posts: posts, posts: posts,
}, nil }, nil
} }
func Scan(basePath string) (map[string]*Post, error) { func scan(basePath string) (map[string]*Post, error) {
posts := make(map[string]*Post) posts := make(map[string]*Post)
ents, err := os.ReadDir(basePath) ents, err := os.ReadDir(basePath)
if err != nil { if err != nil {
@ -55,22 +58,40 @@ func Scan(basePath string) (map[string]*Post, error) {
continue continue
} }
posts[post.Slug] = post posts[post.Slug] = post
if err := fi.Close(); err != nil { if err := fi.Close(); err != nil {
log.Error().Err(err).Msg("could not close file") log.Error().Err(err).Msg("could not close file")
continue continue
} }
if err := post.Load(); err != nil {
log.Err(err).Msg("could not load post")
continue
}
} }
return posts, nil return posts, nil
} }
type Blog struct { type Blog struct {
Path string Path string
Posts map[string]*Post posts map[string]*Post
mx sync.RWMutex
}
func (b *Blog) Scan() error {
posts, err := scan(b.Path)
if err != nil {
return err
}
b.mx.Lock()
defer b.mx.Unlock()
b.posts = posts
return nil
} }
func (b *Blog) SortedPosts() []*Post { func (b *Blog) SortedPosts() []*Post {
posts := make([]*Post, 0, len(b.Posts)) posts := make([]*Post, 0, len(b.posts))
for _, post := range b.Posts { for _, post := range b.posts {
posts = append(posts, post) posts = append(posts, post)
} }
sort.Slice(posts, func(i, j int) bool { sort.Slice(posts, func(i, j int) bool {
@ -79,11 +100,47 @@ func (b *Blog) SortedPosts() []*Post {
return posts return posts
} }
func (b *Blog) Post(name string) (*Post, bool) {
b.mx.RLock()
defer b.mx.RUnlock()
if post, ok := b.posts[name]; ok {
return post, true
}
return nil, false
}
type Post struct { type Post struct {
Path string `toml:"-"` Path string `toml:"-"`
Slug string `toml:"-"` Slug string `toml:"-"`
Content template.HTML `toml:"-"`
Title string `toml:"title"` Title string `toml:"title"`
Author string `toml:"author"` Author string `toml:"author"`
Date time.Time `toml:"date"` Date time.Time `toml:"date"`
Tags []string `toml:"tags"` Tags []string `toml:"tags"`
} }
func (p *Post) Load() error {
fi, err := os.Open(p.Path)
if err != nil {
return err
}
defer fi.Close()
mdContent, err := markdown.Content(fi)
if err != nil {
return err
}
emdbedContent, err := emdbed.Convert(filepath.Dir(p.Path), strings.NewReader(mdContent))
if err != nil {
return err
}
md, err := markdown.Convert(strings.NewReader(emdbedContent))
if err != nil {
return err
}
p.Content = template.HTML(md)
return nil
}

View File

@ -1,15 +1,9 @@
package router package router
import ( import (
"html/template" "net"
"net/http" "net/http"
"os"
"path/filepath"
"strings"
"go.jolheiser.com/emdbed"
"go.jolheiser.com/blog/markdown"
"go.jolheiser.com/blog/post" "go.jolheiser.com/blog/post"
"go.jolheiser.com/blog/static" "go.jolheiser.com/blog/static"
@ -26,7 +20,10 @@ func New(blog *post.Blog) *chi.Mux {
m.Get("/", indexHandler(blog)) m.Get("/", indexHandler(blog))
m.Get("/{post}", fileHandler(blog)) m.Get("/{post}", fileHandler(blog))
m.Get("/sakura.css", static.SakuraCSS) m.Route("/_", func(r chi.Router) {
r.Get("/reload", reloadHandler(blog))
r.Get("/sakura.css", static.SakuraCSS)
})
return m return m
} }
@ -44,45 +41,38 @@ func indexHandler(blog *post.Blog) http.HandlerFunc {
func fileHandler(blog *post.Blog) http.HandlerFunc { func fileHandler(blog *post.Blog) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
postName := chi.URLParam(r, "post") postName := chi.URLParam(r, "post")
p, ok := blog.Posts[postName] p, ok := blog.Post(postName)
if !ok { if !ok {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
return return
} }
fi, err := os.Open(p.Path)
if err != nil {
log.Error().Err(err).Msg("could not open post")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer fi.Close()
mdContent, err := markdown.Content(fi)
if err != nil {
log.Error().Err(err).Msg("could not get content")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
emdbedContent, err := emdbed.Convert(filepath.Dir(p.Path), strings.NewReader(mdContent))
if err != nil {
log.Error().Err(err).Msg("could not emdbed")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
md, err := markdown.Convert(strings.NewReader(emdbedContent))
if err != nil {
log.Error().Err(err).Msg("could not convert")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if err := static.PostTemplate.Execute(w, map[string]interface{}{ if err := static.PostTemplate.Execute(w, map[string]interface{}{
"Post": p, "Post": p,
"Content": template.HTML(md),
}); err != nil { }); err != nil {
log.Error().Err(err).Msg("could not execute template") log.Error().Err(err).Msg("could not execute template")
} }
} }
} }
func reloadHandler(blog *post.Blog) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
ip := net.ParseIP(host)
if ip == nil || !ip.IsLoopback() {
w.WriteHeader(http.StatusUnauthorized)
return
}
log.Info().Msg("reloading posts")
if err := blog.Scan(); err != nil {
http.Error(w, "could not re-scan", http.StatusInternalServerError)
log.Error().Err(err).Msg("could not re-scan")
}
}
}

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Blog</title> <title>Blog</title>
<link rel="icon" href="data:,"> <link rel="icon" href="data:,">
<link rel="stylesheet" href="sakura.css"/> <link rel="stylesheet" href="_/sakura.css"/>
<style> <style>
.tag { .tag {
font-size: .7em; font-size: .7em;

View File

@ -4,11 +4,11 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{{.Post.Title}}</title> <title>{{.Post.Title}}</title>
<link rel="icon" href="data:,"> <link rel="icon" href="data:,">
<link rel="stylesheet" href="sakura.css"/> <link rel="stylesheet" href="_/sakura.css"/>
</head> </head>
<body> <body>
<a style="position: absolute; left: 1em;" href="../">Index</a> <a style="position: absolute; left: 1em;" href="../../">Index</a>
{{.Content}} {{.Post.Content}}
</body> </body>
<script> <script>
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {