package main import ( "bytes" "fmt" "os" "slices" "sort" "strings" "git.akyoto.dev/go/markdown" "git.akyoto.dev/go/web" "git.akyoto.dev/go/web/send" ) type App struct { html string posts map[string]*Post } func New() *App { html := loadClean("public/app.html") css := loadClean("public/app.css") html = strings.Replace(html, "{head}", fmt.Sprintf("{head}", css), 1) posts := loadPosts("posts") return &App{ html: html, posts: posts, } } func (app *App) Run() { s := web.NewServer() s.Use(func(ctx web.Context) error { defer func() { err := recover() if err != nil { fmt.Println("Recovered panic:", err) } }() return ctx.Next() }) s.Use(func(ctx web.Context) error { path := ctx.Request().Path() if len(path) > 1 && strings.HasSuffix(path, "/") { return ctx.Redirect(301, strings.TrimSuffix(path, "/")) } return ctx.Next() }) render := func(ctx web.Context, head string, body string) error { html := app.html html = strings.Replace(html, "{head}", head, 1) html = strings.Replace(html, "{body}", body, 1) return send.HTML(ctx, html) } s.Get("/", func(ctx web.Context) error { html := bytes.Buffer{} html.WriteString(`

Blog

`) return render(ctx, "akyoto.dev", html.String()) }) s.Get("/:post", func(ctx web.Context) error { slug := ctx.Request().Param("post") post := app.posts[slug] head := fmt.Sprintf(`%s`, post.Title, strings.Join(post.Tags, ",")) content := "" if slices.Contains(post.Tags, "article") { content = fmt.Sprintf( `

%s

%s
`, post.Title, post.Created, post.Created[:len("YYYY-MM-DD")], markdown.Render(post.Content), ) } else { content = fmt.Sprintf( `

%s

%s`, post.Title, markdown.Render(post.Content), ) } return render(ctx, head, content) }) address := os.Getenv("LISTEN") if address == "" { address = ":8080" } s.Run(address) } func load(path string) string { dataBytes, err := os.ReadFile(path) if err != nil { panic(err) } return string(dataBytes) } func loadClean(path string) string { data := load(path) data = strings.ReplaceAll(data, "\t", "") data = strings.ReplaceAll(data, "\n", "") return data }