/*
* Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* 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)
}
}