/* * Copyright (C) 2023 Jonni Liljamo * * This file is licensed under AGPL-3.0-or-later, see NOTICE and LICENSE for * more information. */ package main import ( "fmt" "log" "net/http" "strconv" "time" "git.src.quest/~skye/felu-ddns/components" "git.src.quest/~skye/felu-ddns/config" "git.src.quest/~skye/felu-ddns/db" "git.src.quest/~skye/felu-ddns/dns" "git.src.quest/~skye/felu-ddns/handlers" "git.src.quest/~skye/felu-ddns/middlewares" "github.com/alexedwards/scs/v2" "github.com/gin-gonic/gin" "golang.org/x/sync/errgroup" ) var version = "notset-builtin" var ( g errgroup.Group sessionManager *scs.SessionManager ) func ginLogFormat(param gin.LogFormatterParams, router string) string { var statusColor, methodColor, resetColor string if param.IsOutputColor() { statusColor = param.StatusCodeColor() methodColor = param.MethodColor() resetColor = param.ResetColor() } if param.Latency > time.Minute { param.Latency = param.Latency.Truncate(time.Second) } return fmt.Sprintf("[GIN] [felu/%-8s] | %v |%s %3d %s| %13v | %15s |%s %-7s %s %#v\n%s", router, param.TimeStamp.Format("2006/01/02 - 15:04:05"), statusColor, param.StatusCode, resetColor, param.Latency, param.ClientIP, methodColor, param.Method, resetColor, param.Path, param.ErrorMessage, ) } func setupFrontendRouter() *gin.Engine { r := gin.New() r.Use(gin.Recovery()) r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { return ginLogFormat(param, "frontend") })) r.Static("/static", "./static") r.HTMLRender = &TemplRender{} r.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "", components.Index()) }) r.GET("/login", middlewares.SessionExists(sessionManager), func(c *gin.Context) { c.HTML(http.StatusOK, "", components.Login()) }) auth := r.Group("/auth") { auth.POST("/login", func(c *gin.Context) { handlers.AuthLogin(sessionManager, c) }) } // routes for the actual application frontend manage := r.Group("/manage").Use(middlewares.SessionExists(sessionManager)) { manage.GET("/", func(c *gin.Context) { c.HTML(http.StatusOK, "", components.Manage()) }) manage.GET("/settings", func(c *gin.Context) { c.HTML(http.StatusOK, "", components.ManageSettings()) }) } manageAdmin := r.Group("/manage/admin").Use( middlewares.SessionExists(sessionManager), middlewares.AdminOnly(), ) { manageAdmin.GET("/", handlers.ManageAdmin()) manageAdmin.GET("/users", handlers.ManageAdminUsers()) manageAdmin.GET("/domains", handlers.ManageAdminDomains()) manageAdmin.GET("/partials/users_list", handlers.AdminPartialsUsersList()) } return r } func setupBackendRouter() *gin.Engine { r := gin.New() r.Use(gin.Recovery()) r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { return ginLogFormat(param, "backend") })) r.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "status": "success", }) }) r.GET("/version", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "version": version, }) }) return r } func main() { log.Print("[felu] Starting up...") config.InitConfig() if err := db.InitDB(); err != nil { log.Fatalf("[felu] Failed to initialize database: '%s'", err) } defer db.DBConn.Close() if err := db.InitAdminUser(); err != nil { log.Fatalf("[felu] Failed to initialize admin user: '%s'", err) } sessionManager = scs.New() sessionManager.Lifetime = 6 * time.Hour frontend := &http.Server{ Addr: config.FeluConfig.FrontendBindAddr, Handler: sessionManager.LoadAndSave(setupFrontendRouter()), ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } backend := &http.Server{ Addr: config.FeluConfig.BackendBindAddr, Handler: setupBackendRouter(), ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } log.Printf("[felu] Serving frontend at '%s'", config.FeluConfig.FrontendBindAddr) g.Go(func() error { return frontend.ListenAndServe() }) log.Printf("[felu] Serving backend at '%s'", config.FeluConfig.BackendBindAddr) g.Go(func() error { return backend.ListenAndServe() }) dnsIP := config.FeluConfig.DNSBindIP dnsPort := strconv.Itoa(int(config.FeluConfig.DNSBindPort)) dnsAddr := dnsIP + ":" + dnsPort log.Printf("[felu] Serving DNS at '%s'", dnsAddr) g.Go(func() error { return dns.Run(dnsAddr) }) if err := g.Wait(); err != nil { log.Fatalf("[felu] Error while running: %s", err) } }