package main
import (
"flag"
"html/template"
"log"
"log/slog"
"net/http"
"os"
"strings"
"sync"
"time"
"tailscale.com/tsweb"
"xeiaso.net/v4"
"xeiaso.net/v4/config"
"xeiaso.net/v4/internal"
"xeiaso.net/v4/internal/embedded"
)
var (
addr = flag.String("addr", ":8080", "address to listen on")
)
var baseTmpl *template.Template
var templateMap = map[string]*template.Template{}
var templateLock sync.RWMutex
func templateFor(name string) *template.Template {
templateLock.RLock()
tmpl, ok := templateMap[name]
templateLock.RUnlock()
if ok {
return tmpl
}
templateLock.Lock()
defer templateLock.Unlock()
base := template.Must(baseTmpl.Clone())
tmpl = template.Must(base.ParseFS(xeiaso.Templates, "tmpl/"+name))
tmpl = tmpl.Lookup(name)
templateMap[name] = tmpl
return tmpl
}
type Site struct {
Config *config.Config
Blog []*internal.Post
Talks []*internal.Post
Gallery []*internal.Post
}
func (s *Site) Index(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
w.WriteHeader(http.StatusNotFound)
if err := templateFor("404.html").Execute(w, map[string]string{
"Page": r.URL.Path,
}); err != nil {
slog.Error("can't render template", "err", err)
}
return
}
if err := templateFor("index.html").Execute(w, nil); err != nil {
slog.Error("can't render template", "err", err)
}
}
func (s *Site) BlogIndex(w http.ResponseWriter, r *http.Request) {
if err := templateFor("blogindex.html").Execute(w, s.Blog); err != nil {
slog.Error("can't render template", "err", err)
}
}
func main() {
flag.Parse()
internal.Slog()
mux := http.NewServeMux()
slog.Debug("loading config")
config, err := config.Parse("./config/config.ts")
if err != nil {
log.Fatal(err)
}
posts := embedded.Posts
var blog []*internal.Post
var talks []*internal.Post
var gallery []*internal.Post
postsIndex := map[string]int{}
for i, post := range posts {
switch strings.Split(post.Link, "/")[0] {
case "blog":
blog = append(blog, post)
case "talks":
talks = append(talks, post)
case "gallery":
gallery = append(gallery, post)
}
postsIndex[post.Link] = i
}
mux.Handle("/static/", http.FileServer(http.FS(xeiaso.Static)))
site := &Site{
Config: config,
Blog: blog,
Talks: talks,
Gallery: gallery,
}
baseTmpl = template.Must(
template.New("xesite/v4").Funcs(template.FuncMap{
"getYear": func() string {
return time.Now().Format("2006")
},
"argv0": func() string {
return os.Args[0]
},
"config": func() any {
return config
},
"recentPosts": func() []*internal.Post {
return posts[:5]
},
}).
ParseFS(xeiaso.Templates, "tmpl/base.html"),
)
for k, v := range config.Redirects {
mux.HandleFunc(k, func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, v, http.StatusMovedPermanently)
})
}
mux.HandleFunc("/", site.Index)
mux.HandleFunc("/blog", site.BlogIndex)
mux.HandleFunc("/metrics", tsweb.VarzHandler)
slog.Info("listening", "addr", *addr)
log.Fatal(http.ListenAndServe(*addr, mux))
}