package server import ( "context" "html/template" "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/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 } 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, } } func (s *Server) Run() error { s.SetupMiddleware() s.RegisterRoutes() s.RegisterViews() s.PowChallenger.Init() return s.Router.Listen(s.ListenAddr) } 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{"https://kycnot.me*", "https://www.kycnot.me*"}, // 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) // POST s.Router.Post("/new/service", s.handlePostRequestServiceForm) // API routes api_v1 := s.Router.Party("/api/v1") api_v1.Get("/pow/verify/{id}/{nonce:int}", s.handleVerifyPow) api_v1.Get("/picture/{id}", s.handleApiPicture) } func (s *Server) RegisterViews() { // Use blocks as the templating engine blocks := iris.Blocks("./frontend/templates", ".gohtml") 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) }, "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) }