- switched all admin routes to /admin/...
- auto redirect from ./ to ./admin - enabled two auth flows (admin and update) - disabled auth for admin by skipping env variable DDNS_ADMIN_LOGIN - introduced optional env variable DDNS_TITLE for dynamic UI title (default TheBBCloudDynDNS) - set copyright date in footer dynamic on startup - moved all remote js/css packages into static in order to avoid external dependencies - added "copy to clipboard button" on host overview page - replaced all fmt.Println to log... - introduced new optional env variable DDNS_CLEAR_LOG_INTERVAL to clear logs after n days (int). (check runs daily once if update request received) - newest logs are shown from top to button on logs page
This commit is contained in:
@@ -24,6 +24,7 @@ func (h *Handler) ListCNames(c echo.Context) (err error) {
|
||||
|
||||
return c.Render(http.StatusOK, "listcnames", echo.Map{
|
||||
"cnames": cnames,
|
||||
"title": h.Title,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,6 +43,7 @@ func (h *Handler) AddCName(c echo.Context) (err error) {
|
||||
return c.Render(http.StatusOK, "addcname", echo.Map{
|
||||
"config": h.Config,
|
||||
"hosts": hosts,
|
||||
"title": h.Title,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+69
-21
@@ -2,8 +2,12 @@ package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/labstack/gommon/log"
|
||||
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/model"
|
||||
"github.com/go-playground/validator/v10"
|
||||
@@ -13,10 +17,14 @@ import (
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
DB *gorm.DB
|
||||
AuthHost *model.Host
|
||||
AuthAdmin bool
|
||||
Config Envs
|
||||
DB *gorm.DB
|
||||
AuthHost *model.Host
|
||||
AuthAdmin bool
|
||||
Config Envs
|
||||
Title string
|
||||
DisableAdminAuth bool
|
||||
LastClearedLogs time.Time
|
||||
ClearInterval uint64
|
||||
}
|
||||
|
||||
type Envs struct {
|
||||
@@ -39,13 +47,25 @@ type Error struct {
|
||||
|
||||
// Authenticate is the method the website admin user and the host update user have to authenticate against.
|
||||
// To gather admin rights the username password combination must match with the credentials given by the env var.
|
||||
func (h *Handler) Authenticate(username, password string, c echo.Context) (bool, error) {
|
||||
func (h *Handler) AuthenticateUpdate(username, password string, c echo.Context) (bool, error) {
|
||||
h.CheckClearInterval()
|
||||
h.AuthHost = nil
|
||||
h.AuthAdmin = false
|
||||
|
||||
host := &model.Host{}
|
||||
if err := h.DB.Where(&model.Host{UserName: username, Password: password}).First(host).Error; err != nil {
|
||||
log.Error("Error:", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
h.AuthHost = host
|
||||
|
||||
return true, nil
|
||||
}
|
||||
func (h *Handler) AuthenticateAdmin(username, password string, c echo.Context) (bool, error) {
|
||||
h.AuthAdmin = false
|
||||
ok, err := h.authByEnv(username, password)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
log.Error("Error:", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -54,17 +74,8 @@ func (h *Handler) Authenticate(username, password string, c echo.Context) (bool,
|
||||
return true, nil
|
||||
}
|
||||
|
||||
host := &model.Host{}
|
||||
if err := h.DB.Where(&model.Host{UserName: username, Password: password}).First(host).Error; err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
h.AuthHost = host
|
||||
|
||||
return true, nil
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (h *Handler) authByEnv(username, password string) (bool, error) {
|
||||
hashReader := strings.NewReader(h.Config.AdminLogin)
|
||||
|
||||
@@ -83,19 +94,40 @@ func (h *Handler) authByEnv(username, password string) (bool, error) {
|
||||
// ParseEnvs parses all needed environment variables:
|
||||
// DDNS_ADMIN_LOGIN: The basic auth login string in htpasswd style.
|
||||
// DDNS_DOMAINS: All domains that will be handled by the dyndns server.
|
||||
func (h *Handler) ParseEnvs() error {
|
||||
func (h *Handler) ParseEnvs() (adminAuth bool, err error) {
|
||||
log.Info("Read environment variables")
|
||||
h.Config = Envs{}
|
||||
adminAuth = true
|
||||
h.Config.AdminLogin = os.Getenv("DDNS_ADMIN_LOGIN")
|
||||
if h.Config.AdminLogin == "" {
|
||||
return fmt.Errorf("environment variable DDNS_ADMIN_LOGIN has to be set")
|
||||
log.Info("No Auth! DDNS_ADMIN_LOGIN should be set")
|
||||
adminAuth = false
|
||||
h.AuthAdmin = true
|
||||
h.DisableAdminAuth = true
|
||||
}
|
||||
h.Title = os.Getenv("DDNS_TITLE")
|
||||
if h.Title == "" {
|
||||
h.Title = "TheBBCloud DynDNS"
|
||||
}
|
||||
|
||||
clearEnv := os.Getenv("DDNS_CLEAR_LOG_INTERVAL")
|
||||
clearInterval, err := strconv.ParseUint(clearEnv, 10, 32)
|
||||
if err != nil {
|
||||
log.Info("No log clear interval found")
|
||||
} else {
|
||||
log.Info("log clear interval found:", clearInterval, "days")
|
||||
h.ClearInterval = clearInterval
|
||||
if clearInterval > 0 {
|
||||
h.LastClearedLogs = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
h.Config.Domains = strings.Split(os.Getenv("DDNS_DOMAINS"), ",")
|
||||
if len(h.Config.Domains) < 1 {
|
||||
return fmt.Errorf("environment variable DDNS_DOMAINS has to be set")
|
||||
return adminAuth, fmt.Errorf("environment variable DDNS_DOMAINS has to be set")
|
||||
}
|
||||
|
||||
return nil
|
||||
return adminAuth, nil
|
||||
}
|
||||
|
||||
// InitDB creates an empty database and creates all tables if there isn't already one, or opens the existing one.
|
||||
@@ -126,3 +158,19 @@ func (h *Handler) InitDB() (err error) {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if a log cleaning is needed
|
||||
func (h *Handler) CheckClearInterval() {
|
||||
if !h.LastClearedLogs.IsZero() {
|
||||
if !DateEqual(time.Now(), h.LastClearedLogs) {
|
||||
go h.ClearLogs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compare two dates
|
||||
func DateEqual(date1, date2 time.Time) bool {
|
||||
y1, m1, d1 := date1.Date()
|
||||
y2, m2, d2 := date2.Date()
|
||||
return y1 == y2 && m1 == m2 && d1 == d2
|
||||
}
|
||||
|
||||
+11
-7
@@ -2,6 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
l "github.com/labstack/gommon/log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -51,6 +52,7 @@ func (h *Handler) ListHosts(c echo.Context) (err error) {
|
||||
|
||||
return c.Render(http.StatusOK, "listhosts", echo.Map{
|
||||
"hosts": hosts,
|
||||
"title": h.Title,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,6 +65,7 @@ func (h *Handler) AddHost(c echo.Context) (err error) {
|
||||
return c.Render(http.StatusOK, "edithost", echo.Map{
|
||||
"addEdit": "add",
|
||||
"config": h.Config,
|
||||
"title": h.Title,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -86,6 +89,7 @@ func (h *Handler) EditHost(c echo.Context) (err error) {
|
||||
"host": host,
|
||||
"addEdit": "edit",
|
||||
"config": h.Config,
|
||||
"title": h.Title,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -109,7 +113,7 @@ func (h *Handler) CreateHost(c echo.Context) (err error) {
|
||||
if err = h.checkUniqueHostname(host.Hostname, host.Domain); err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
host.LastUpdate = time.Now()
|
||||
if err = h.DB.Create(host).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
@@ -237,7 +241,7 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
|
||||
if err != nil {
|
||||
log.Message = "Bad Request: Unable to get caller IP"
|
||||
if err = h.CreateLogEntry(log); err != nil {
|
||||
fmt.Println(err)
|
||||
l.Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusBadRequest, "badrequest\n")
|
||||
@@ -249,7 +253,7 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
|
||||
if hostname == "" || hostname != h.AuthHost.Hostname+"."+h.AuthHost.Domain {
|
||||
log.Message = "Hostname or combination of authenticated user and hostname is invalid"
|
||||
if err = h.CreateLogEntry(log); err != nil {
|
||||
fmt.Println(err)
|
||||
l.Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusBadRequest, "notfqdn\n")
|
||||
@@ -263,7 +267,7 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
|
||||
if ipType == "" {
|
||||
log.Message = "Bad Request: Sent IP is invalid"
|
||||
if err = h.CreateLogEntry(log); err != nil {
|
||||
fmt.Println(err)
|
||||
l.Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusBadRequest, "badrequest\n")
|
||||
@@ -273,10 +277,10 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
|
||||
// add/update DNS record
|
||||
if err = nswrapper.UpdateRecord(log.Host.Hostname, log.SentIP, ipType, log.Host.Domain, log.Host.Ttl); err != nil {
|
||||
log.Message = fmt.Sprintf("DNS error: %v", err)
|
||||
l.Error(log.Message)
|
||||
if err = h.CreateLogEntry(log); err != nil {
|
||||
fmt.Println(err)
|
||||
l.Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusBadRequest, "dnserr\n")
|
||||
}
|
||||
|
||||
@@ -285,7 +289,7 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
|
||||
log.Status = true
|
||||
log.Message = "No errors occurred"
|
||||
if err = h.CreateLogEntry(log); err != nil {
|
||||
fmt.Println(err)
|
||||
l.Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "good\n")
|
||||
|
||||
+15
-4
@@ -1,8 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/model"
|
||||
"github.com/labstack/echo/v4"
|
||||
@@ -24,12 +26,13 @@ func (h *Handler) ShowLogs(c echo.Context) (err error) {
|
||||
}
|
||||
|
||||
logs := new([]model.Log)
|
||||
if err = h.DB.Preload("Host").Limit(30).Find(logs).Error; err != nil {
|
||||
if err = h.DB.Preload("Host").Limit(30).Order("created_at desc").Find(logs).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "listlogs", echo.Map{
|
||||
"logs": logs,
|
||||
"logs": logs,
|
||||
"title": h.Title,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -45,11 +48,19 @@ func (h *Handler) ShowHostLogs(c echo.Context) (err error) {
|
||||
}
|
||||
|
||||
logs := new([]model.Log)
|
||||
if err = h.DB.Preload("Host").Where(&model.Log{HostID: uint(id)}).Limit(30).Find(logs).Error; err != nil {
|
||||
if err = h.DB.Preload("Host").Where(&model.Log{HostID: uint(id)}).Order("created_at desc").Limit(30).Find(logs).Error; err != nil {
|
||||
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "listlogs", echo.Map{
|
||||
"logs": logs,
|
||||
"logs": logs,
|
||||
"title": h.Title,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) ClearLogs() {
|
||||
var clearInterval = strconv.FormatUint(h.ClearInterval, 10) + " days"
|
||||
h.DB.Exec("DELETE FROM LOGS WHERE created_at < datetime('now', '" + clearInterval + "');REINDEX LOGS;")
|
||||
h.LastClearedLogs = time.Now()
|
||||
log.Print("logs cleared")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user