diff --git a/main.go b/main.go index 7877ca3..daa074a 100644 --- a/main.go +++ b/main.go @@ -4,22 +4,27 @@ import ( "flag" "fmt" "net/http" - "os" - "time" "codeberg.org/rimgo/rimgo/pages" + "codeberg.org/rimgo/rimgo/render" "codeberg.org/rimgo/rimgo/static" "codeberg.org/rimgo/rimgo/utils" "codeberg.org/rimgo/rimgo/views" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/cache" - "github.com/gofiber/fiber/v2/middleware/filesystem" - "github.com/gofiber/fiber/v2/middleware/recover" - "github.com/gofiber/template/handlebars/v2" "github.com/joho/godotenv" - "github.com/mailgun/raymond/v2" ) +// a handler that returns error if it can't respond +type handler func(w http.ResponseWriter, r *http.Request) error + +func wrapHandler(h handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + err := h(w, r) + if err != nil { + http.Error(w, "oop", 500) + } + }) +} + func main() { envPath := flag.String("c", ".env", "Path to env file") godotenv.Load(*envPath) @@ -27,120 +32,21 @@ func main() { pages.InitializeApiClient() - views := http.FS(views.GetFiles()) - if os.Getenv("ENV") == "dev" { - views = http.Dir("./views") - } - engine := handlebars.NewFileSystem(views, ".hbs") + views := views.GetFiles() + static := static.GetFiles() + render.Initialize(views) - engine.AddFunc("noteq", func(a interface{}, b interface{}, options *raymond.Options) interface{} { - if raymond.Str(a) != raymond.Str(b) { - return options.Fn() - } - return "" - }) - - app := fiber.New(fiber.Config{ - Views: engine, - Prefork: utils.Config.FiberPrefork, - UnescapePath: true, - StreamRequestBody: true, - ErrorHandler: func(ctx *fiber.Ctx, err error) error { - code := fiber.StatusInternalServerError - - if e, ok := err.(*fiber.Error); ok { - code = e.Code - } - - return utils.RenderError(ctx, code) - }, - }) - - app.Use(recover.New(recover.Config{ - EnableStackTrace: true, - StackTraceHandler: func(c *fiber.Ctx, e interface{}) { - fmt.Println(e) - }, + app := http.NewServeMux() + app.Handle("/static/", http.FileServerFS(static)) + app.Handle("GET /test-render", wrapHandler(func(w http.ResponseWriter, r *http.Request) error { + err := render.Render(w, "errors/404", nil) + fmt.Println(err) + return err })) - if os.Getenv("ENV") == "dev" { - app.Use("/static", filesystem.New(filesystem.Config{ - Root: http.Dir("./static"), - })) - app.Get("/errors/429", func(c *fiber.Ctx) error { - return c.Render("errors/429", nil) - }) - app.Get("/errors/429/img", func(c *fiber.Ctx) error { - return c.Redirect("/static/img/error-429.png") - }) - app.Get("/errors/404", func(c *fiber.Ctx) error { - return c.Render("errors/404", nil) - }) - app.Get("/errors/404/img", func(c *fiber.Ctx) error { - return c.Redirect("/static/img/error-404.png") - }) - app.Get("/errors/error", func(c *fiber.Ctx) error { - return c.Render("errors/error", fiber.Map{ - "err": "Test error", - }) - }) - app.Get("/errors/error/img", func(c *fiber.Ctx) error { - return c.Redirect("/static/img/error-generic.png") - }) - } else { - app.Use("/static", filesystem.New(filesystem.Config{ - MaxAge: 2592000, - Root: http.FS(static.GetFiles()), - })) - app.Use(cache.New(cache.Config{ - Expiration: 30 * time.Minute, - MaxBytes: 25000000, - KeyGenerator: func(c *fiber.Ctx) string { - return utils.GetInstanceUrl(c) + c.OriginalURL() - }, - CacheControl: true, - StoreResponseHeaders: true, - })) - } - - app.Get("/robots.txt", func(c *fiber.Ctx) error { - file, _ := static.GetFiles().ReadFile("robots.txt") - _, err := c.Write(file) - return err - }) - app.Get("/favicon.ico", func(c *fiber.Ctx) error { - file, _ := static.GetFiles().ReadFile("favicon/favicon.ico") - _, err := c.Write(file) - return err - }) - - app.Get("/", pages.HandleFrontpage) - app.Get("/about", pages.HandleAbout) - app.Get("/privacy", pages.HandlePrivacy) - app.Get("/search", pages.HandleSearch) - app.Get("/trending.:type", pages.HandleTrendingRSS) - app.Get("/trending", pages.HandleTrending) - app.Get("/a/:postID", pages.HandlePost) - app.Get("/a/:postID/embed", pages.HandleEmbed) - app.Get("/t/:tag.:type", pages.HandleTagRSS) - app.Get("/t/:tag", pages.HandleTag) - app.Get("/t/:tag/:postID", pages.HandlePost) - app.Get("/r/:sub/:postID", pages.HandlePost) - app.Get("/user/:userID.:type", pages.HandleUserRSS) - app.Get("/user/:userID", pages.HandleUser) - app.Get("/user/:userID/favorites", pages.HandleUserFavorites) - app.Get("/user/:userID/comments", pages.HandleUserComments) - app.Get("/user/:userID/cover", pages.HandleUserCover) - app.Get("/user/:userID/avatar", pages.HandleUserAvatar) - app.Get("/gallery/:postID", pages.HandlePost) - app.Get("/gallery/:postID/embed", pages.HandleEmbed) - app.Get("/:postID.gifv", pages.HandleGifv) - app.Get("/:baseName.:extension", pages.HandleMedia) - app.Get("/stack/:baseName.:extension", pages.HandleMedia) - app.Get("/:postID", pages.HandlePost) - app.Get("/:postID/embed", pages.HandleEmbed) - - err := app.Listen(utils.Config.Addr + ":" + utils.Config.Port) + addr := utils.Config.Addr + ":" + utils.Config.Port + fmt.Println("listening on " + addr) + err := http.ListenAndServe(addr, app) if err != nil { fmt.Println(err) } diff --git a/render/render.go b/render/render.go new file mode 100644 index 0000000..d62e32a --- /dev/null +++ b/render/render.go @@ -0,0 +1,74 @@ +// stolen from gofiber/template but simpler +package render + +import ( + "fmt" + "io" + "io/fs" + "path/filepath" + "strings" + + "codeberg.org/rimgo/rimgo/utils" + "github.com/mailgun/raymond/v2" +) + +var Renderer *renderer + +func Render(out io.Writer, name string, bind map[string]any) error { + return Renderer.Render(out, name, bind) +} + +type renderer struct { + templates map[string]*raymond.Template + funcmap map[string]any +} + +const ext = ".hbs" + +func Initialize(views fs.FS) { + r := new(renderer) + r.templates = make(map[string]*raymond.Template) + raymond.RegisterHelpers(r.funcmap) + fs.WalkDir(views, "/", func(path string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() { + return err + } + path, _ = filepath.Rel("/", path) + name, hasExt := strings.CutSuffix(path, ext) + if !hasExt { + return nil + } + path = filepath.ToSlash(path) + buf, err := utils.ReadFile(path, views) + if err != nil { + return err + } + tmpl, err := raymond.Parse(string(buf)) + if err != nil { + return err + } + r.templates[name] = tmpl + return nil + }) + for j := range r.templates { + for n, template := range r.templates { + r.templates[j].RegisterPartialTemplate(n, template) + } + } + Renderer = r +} + +func (r *renderer) Render(out io.Writer, name string, bind map[string]any) error { + tmpl := r.templates[name] + if tmpl == nil { + return fmt.Errorf("render: template %s does not exist", name) + } + parsed, err := tmpl.Exec(bind) + if err != nil { + return fmt.Errorf("render: %w", err) + } + if _, err = out.Write([]byte(parsed)); err != nil { + return fmt.Errorf("render: %w", err) + } + return err +} diff --git a/utils/error.go b/utils/error.go index e6bc8dd..7d4ac7d 100644 --- a/utils/error.go +++ b/utils/error.go @@ -14,7 +14,7 @@ func RenderError(c *fiber.Ctx, code int) error { if code != 0 { codeStr = strconv.Itoa(code) } - img, _ := static.GetFiles().ReadFile("img/error-" + codeStr + ".png") + img, _ := ReadFile("img/error-"+codeStr+".png", static.GetFiles()) c.Set("Content-Type", "image/png") return c.Status(code).Send(img) } else { diff --git a/utils/readFile.go b/utils/readFile.go new file mode 100644 index 0000000..6b1593c --- /dev/null +++ b/utils/readFile.go @@ -0,0 +1,15 @@ +package utils + +import ( + "io" + "io/fs" +) + +func ReadFile(path string, fs fs.FS) ([]byte, error) { + file, err := fs.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + return io.ReadAll(file) +}