kycnot.me/src/server/server.go
2024-02-25 18:28:59 +01:00

195 lines
4.6 KiB
Go

package server
import (
"context"
"html/template"
"math/rand"
"net/http"
"os"
"path"
"strings"
"time"
"github.com/allegro/bigcache/v3"
"github.com/dustin/go-humanize"
"github.com/iris-contrib/middleware/cors"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/rate"
"github.com/rs/zerolog/log"
"pluja.dev/kycnot.me/config"
"pluja.dev/kycnot.me/utils/pow"
)
type Server struct {
ListenAddr string
Router *iris.Application
PowChallenger *pow.PowChallenger
Cache *bigcache.BigCache
HttpClient *http.Client
}
func NewServer(listenAddr string) *Server {
app := iris.New()
cache, err := bigcache.New(context.Background(), bigcache.Config{
Shards: 1024,
LifeWindow: 30 * (24 * time.Hour),
MaxEntriesInWindow: 1000 * 10 * 60,
MaxEntrySize: 500,
Verbose: false,
HardMaxCacheSize: 0,
})
if err != nil {
log.Fatal().Err(err).Msg("Could not initialize cache")
}
return &Server{
ListenAddr: listenAddr,
Router: app,
PowChallenger: &pow.PowChallenger{},
Cache: cache,
HttpClient: &http.Client{},
}
}
func (s *Server) Run() error {
s.SetupMiddleware()
s.RegisterRoutes()
s.RegisterViews()
s.PowChallenger.Init()
return s.Router.Listen(s.ListenAddr, iris.WithSitemap("https://kycnot.me"))
}
func (s *Server) SetupMiddleware() {
s.Router.Use(iris.Compression)
if config.Conf.Dev {
log.Debug().Msg("CORS is enabled")
crs := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, // allows everything, use that to change the hosts.
AllowCredentials: true,
})
s.Router.Use(crs)
} else {
crs := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, // allows everything, use that to change the hosts.
AllowCredentials: true,
})
s.Router.Use(crs)
}
if config.Conf.Dev {
s.Router.Logger().SetLevel("debug")
s.Router.Use(iris.Cache304(10 * time.Second))
} else {
s.Router.Logger().SetLevel("warn")
s.Router.Use(iris.Cache304(24 * 60 * 60))
}
}
func (s *Server) RegisterRoutes() {
// Static routes
s.Router.Favicon(path.Join(os.Getenv("ROOT_DIR"), "/frontend/static", "/assets/favicon.webp"))
s.Router.HandleDir("/static", iris.Dir(path.Join(os.Getenv("ROOT_DIR"), "/frontend/static")))
// GET
s.Router.Get("/", s.handleIndex)
s.Router.Get("/about", s.handleAbout)
s.Router.Get("/pending", s.handlePending)
s.Router.Get("/service/{name:string}", s.handleService)
s.Router.Get("/exchange/{name:string}", s.handleService)
s.Router.Get("/point/{id:string}", s.handleAttribute)
s.Router.Get("/attr/{id:string}", s.handleAttribute)
s.Router.Get("/request/service", s.handleRequestServiceForm)
// Miscellaneous Routes
s.Router.Get("/pgp", s.handlePgp)
s.Router.Get("/nostr", s.handleNostr)
// POST
s.Router.Post("/new/service", s.handlePostRequestServiceForm)
// API routes
genericApi := s.Router.Party("/api/g")
{
genericApi.Get("/pow/verify/{id}/{nonce:int}", s.handleVerifyPow)
genericApi.Get("/picture/{id}", s.handleApiPicture)
}
v1Api := s.Router.Party("/api/v1")
{
limitV1 := rate.Limit(rate.Every(1*time.Minute), 5, rate.PurgeEvery(time.Minute, 5*time.Minute))
v1Api.Use(limitV1)
v1Api.Get("/service/{name:string}", s.handleApiService)
v1Api.Get("/service/{name:string}/summary", s.handleScoreSummary)
}
}
func (s *Server) RegisterViews() {
// Use blocks as the templating engine
blocks := iris.Blocks("./frontend/templates", ".html")
if config.Conf.Dev {
blocks.Reload(true)
}
blocks.Engine.Funcs(template.FuncMap{
"attr": func(s string) template.HTMLAttr {
return template.HTMLAttr(s)
},
"safe": func(s string) template.HTML {
return template.HTML(s)
},
"randomElem": func(vs []string) string {
if len(vs) == 0 {
return ""
}
return vs[rand.Intn(len(vs))]
},
"shortText": func(s string) string {
if len(s) > 50 {
return strings.TrimSpace(s[:50]) + "..."
}
return s
},
"humanizePbTimeString": func(t string) string {
if t == "" {
return "Unknown"
}
layout := "2006-01-02 15:04:05.000Z"
tm, err := time.Parse(layout, t)
if err != nil {
return t
}
return humanize.Time(tm)
},
"dateString": func(t string) string {
if t == "" {
return "Unknown"
}
layout := "2006-01-02 15:04:05.000Z"
tm, err := time.Parse(layout, t)
if err != nil {
return t
}
return tm.Format("2006-01-02")
},
"isNew": func(t string) bool {
if t == "" {
return false
}
layout := "2006-01-02 15:04:05.000Z"
tm, err := time.Parse(layout, t)
if err != nil {
return false
}
return time.Since(tm) < 7*(24*time.Hour)
},
})
s.Router.RegisterView(blocks)
}