package main
import (
"html/template"
"net/http"
"strings"
"time"
"github.com/w3K-one/docker-ddns-server/dyndns/handler"
"github.com/foolin/goview"
"github.com/foolin/goview/supports/echoview-v4"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/labstack/gommon/log"
)
func main() {
// Set new instance
e := echo.New()
e.Logger.SetLevel(log.ERROR)
e.Use(middleware.Logger())
// Set Renderer with custom template functions
e.Renderer = echoview.New(goview.Config{
Root: "views",
Master: "layouts/master",
Extension: ".html",
Funcs: template.FuncMap{
"year": func() string {
return time.Now().Format("2006")
},
"hasPrefix": func(s, prefix string) bool {
return strings.HasPrefix(s, prefix)
},
"slice": func(s string, start, end int) string {
if start < 0 {
start = 0
}
if end > len(s) {
end = len(s)
}
if start > end {
return ""
}
return s[start:end]
},
"mod": func(i, j int) int {
return i % j
},
},
DisableCache: true,
})
// Set Validator
e.Validator = &handler.CustomValidator{Validator: validator.New()}
// Set Statics
e.Static("/static", "static")
// Initialize handler
h := &handler.Handler{}
// Database connection
if err := h.InitDB(); err != nil {
e.Logger.Fatal(err)
}
// Parse environment variables and initialize session store
authAdmin, err := h.ParseEnvs()
if err != nil {
e.Logger.Fatal(err)
}
// Apply IP blocker middleware globally
e.Use(h.IPBlockerMiddleware())
// Apply cleanup middleware
e.Use(h.CleanupMiddleware())
// Public redirect (root redirects to admin)
e.GET("/", func(c echo.Context) error {
return c.Redirect(http.StatusMovedPermanently, "/@/")
})
// Admin routes with session-based authentication and HTTPS redirect
groupAdmin := e.Group("/@")
// Apply HTTPS redirect middleware (only for admin routes)
groupAdmin.Use(h.HTTPSRedirectMiddleware())
// Login routes (no auth required)
groupAdmin.GET("/login", h.ShowLoginPage)
groupAdmin.POST("/login", h.HandleLogin)
// Logout route (no auth required - handles its own session check)
groupAdmin.GET("/logout", h.HandleLogout)
// Protected admin routes (require authentication)
if authAdmin {
groupAdmin.Use(h.SessionAuthMiddleware())
}
// Main admin pages
groupAdmin.GET("/", h.ListHosts)
groupAdmin.GET("/hosts", h.ListHosts)
groupAdmin.GET("/hosts/add", h.AddHost)
groupAdmin.GET("/hosts/edit/:id", h.EditHost)
groupAdmin.POST("/hosts/add", h.CreateHost)
groupAdmin.POST("/hosts/edit/:id", h.UpdateHost)
groupAdmin.GET("/hosts/delete/:id", h.DeleteHost)
// CName routes
groupAdmin.GET("/cnames", h.ListCNames)
groupAdmin.GET("/cnames/add", h.AddCName)
groupAdmin.POST("/cnames/add", h.CreateCName)
groupAdmin.GET("/cnames/delete/:id", h.DeleteCName)
// Log routes
groupAdmin.GET("/logs", h.ShowLogs)
groupAdmin.GET("/logs/host/:id", h.ShowHostLogs)
// Security management routes
if authAdmin {
groupAdmin.GET("/security", h.ShowSecurityDashboard)
groupAdmin.GET("/security/blocked-ips", h.ShowBlockedIPs)
groupAdmin.GET("/security/failed-auths", h.ShowFailedAuths)
groupAdmin.POST("/security/unblock/:ip", h.UnblockIPHandler)
}
// DynDNS API endpoints (HTTP allowed, BasicAuth required)
// These endpoints are used by routers/NVRs and need BasicAuth
updateRoute := e.Group("/update")
updateRoute.Use(h.UpdateAuthMiddleware())
updateRoute.GET("", h.UpdateIP)
nicRoute := e.Group("/nic")
nicRoute.Use(h.UpdateAuthMiddleware())
nicRoute.GET("/update", h.UpdateIP)
v2Route := e.Group("/v2")
v2Route.Use(h.UpdateAuthMiddleware())
v2Route.GET("/update", h.UpdateIP)
v3Route := e.Group("/v3")
v3Route.Use(h.UpdateAuthMiddleware())
v3Route.GET("/update", h.UpdateIP)
// Health-check endpoint (no auth)
e.GET("/ping", func(c echo.Context) error {
u := &handler.Error{
Message: "OK",
}
return c.JSON(http.StatusOK, u)
})
// Start server
e.Logger.Fatal(e.Start(":8080"))
}