Files
rimgo/main.go
orangix 02be603dcc refactor api package comments
* use encoding/json for comment parsing

* refactor by moving loop code to an UnmarshalJSON

* use a preallocated array and indices to maintain order while using
  goroutines again, this was removed a while ago

* use new struct in comment.hbs and contextComment.hbs

* rewriteUrl partial to reduce rimgo-specific code in api

* move RenderError into pages package to avoid import cycle between render and utils
2026-01-25 06:08:28 +01:00

152 lines
5.0 KiB
Go

package main
import (
"flag"
"fmt"
"io"
"net/http"
"os"
"strings"
"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/joho/godotenv"
)
// 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) {
defer func() {
if v := recover(); v != nil {
pages.RenderError(w, r, 500, fmt.Sprint(v))
}
}()
err := h(w, r)
if err != nil {
fmt.Println(err)
pages.RenderError(w, r, 500, err.Error())
}
})
}
func main() {
envPath := flag.String("c", ".env", "Path to env file")
godotenv.Load(*envPath)
utils.LoadConfig()
pages.InitializeApiClient()
views := views.GetFiles()
static := static.GetFiles()
render.Initialize(views)
app := http.NewServeMux()
app.Handle("GET /static/", http.StripPrefix("/static/", http.FileServerFS(static)))
app.Handle("GET /robots.txt", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
file, _ := static.Open("robots.txt")
defer file.Close()
io.Copy(w, file)
}))
app.Handle("GET /favicon.ico", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
file, _ := static.Open("favicon/favicon.ico")
defer file.Close()
io.Copy(w, file)
}))
if os.Getenv("ENV") == "dev" {
app.Handle("GET /errors/429", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
pages.RenderError(w, r, 429)
}))
app.Handle("GET /errors/429/img", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Location", "/static/img/error-429.png")
w.WriteHeader(302)
}))
app.Handle("GET /errors/404", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
pages.RenderError(w, r, 404)
}))
app.Handle("GET /errors/404/img", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Location", "/static/img/error-404.png")
w.WriteHeader(302)
}))
app.Handle("GET /errors/error", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
return fmt.Errorf("Test error")
}))
app.Handle("GET /errors/panic", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
panic("Test error")
}))
}
app.Handle("GET /{$}", wrapHandler(pages.HandleFrontpage))
app.Handle("GET /a/{postID}", wrapHandler(pages.HandlePost))
app.Handle("GET /a/{postID}/embed", wrapHandler(pages.HandleEmbed))
app.Handle("GET /t/{tag}", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
name, ext := utils.SplitNameExt(r.PathValue("tag"))
if ext != "" {
r.SetPathValue("tag", name[0:len(name)-1])
r.SetPathValue("type", ext)
return pages.HandleTagRSS(w, r)
}
return pages.HandleTag(w, r)
}))
app.Handle("GET /t/{tag}/{postID}", wrapHandler(pages.HandlePost))
app.Handle("GET /r/{sub}/{postID}", wrapHandler(pages.HandlePost))
app.Handle("GET /user/{userID}", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
name, ext := utils.SplitNameExt(r.PathValue("userID"))
if ext != "" {
r.SetPathValue("userID", name[0:len(name)-1])
r.SetPathValue("type", ext)
return pages.HandleUserRSS(w, r)
}
return pages.HandleUser(w, r)
}))
app.Handle("GET /user/{userID}/favorites", wrapHandler(pages.HandleUserFavorites))
app.Handle("GET /user/{userID}/comments", wrapHandler(pages.HandleUserComments))
app.Handle("GET /user/{userID}/cover", wrapHandler(pages.HandleUserCover))
app.Handle("GET /user/{userID}/avatar", wrapHandler(pages.HandleUserAvatar))
app.Handle("GET /gallery/{postID}", wrapHandler(pages.HandlePost))
app.Handle("GET /gallery/{postID}/embed", wrapHandler(pages.HandleEmbed))
app.Handle("GET /{component}", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
component := r.PathValue("component")
switch {
case component == "about":
return pages.HandleAbout(w, r)
case component == "privacy":
return pages.HandlePrivacy(w, r)
case component == "search":
return pages.HandleSearch(w, r)
case component == "trending":
return pages.HandleTrending(w, r)
case strings.HasPrefix(component, "trending."):
_, ext := utils.SplitNameExt(component)
r.SetPathValue("type", ext)
return pages.HandleTrendingRSS(w, r)
case strings.HasSuffix(component, ".gifv"):
r.SetPathValue("postID", component)
return pages.HandleGifv(w, r)
case strings.Contains(component, "."):
return pages.HandleMedia(w, r)
default:
r.SetPathValue("postID", component)
return pages.HandlePost(w, r)
}
}))
app.Handle("GET /stack/{component}", wrapHandler(pages.HandleMedia))
// matches anything with no more specific route
app.Handle("GET /", wrapHandler(func(w http.ResponseWriter, r *http.Request) error {
err := render.Render(w, "errors/404", nil)
return err
}))
addr := utils.Config.Addr + ":" + utils.Config.Port
fmt.Println("listening on " + addr)
err := http.ListenAndServe(addr, app)
if err != nil {
fmt.Println(err)
}
}