mirror of
https://github.com/AuxXxilium/docker-ddns-server.git
synced 2024-11-23 14:50:59 +07:00
- 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:
parent
fcb7f88507
commit
d84c3352a9
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -1,18 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/benjaminbear/docker-ddns-server/dyndns/handler"
|
||||
"github.com/foolin/goview"
|
||||
"github.com/foolin/goview/supports/echoview-v4"
|
||||
"github.com/go-playground/validator/v10"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/labstack/gommon/log"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Set new instance
|
||||
e := echo.New()
|
||||
|
||||
e.Logger.SetLevel(log.ERROR)
|
||||
@ -20,7 +23,17 @@ func main() {
|
||||
e.Use(middleware.Logger())
|
||||
|
||||
// Set Renderer
|
||||
e.Renderer = echoview.Default()
|
||||
e.Renderer = echoview.New(goview.Config{
|
||||
Root: "views",
|
||||
Master: "layouts/master",
|
||||
Extension: ".html",
|
||||
Funcs: template.FuncMap{
|
||||
"year": func() string {
|
||||
return time.Now().Format("2006")
|
||||
},
|
||||
},
|
||||
DisableCache: true,
|
||||
})
|
||||
|
||||
// Set Validator
|
||||
e.Validator = &handler.CustomValidator{Validator: validator.New()}
|
||||
@ -37,38 +50,60 @@ func main() {
|
||||
}
|
||||
defer h.DB.Close()
|
||||
|
||||
if err := h.ParseEnvs(); err != nil {
|
||||
authAdmin, err := h.ParseEnvs()
|
||||
if err != nil {
|
||||
e.Logger.Fatal(err)
|
||||
}
|
||||
|
||||
e.Use(middleware.BasicAuth(h.Authenticate))
|
||||
|
||||
// UI Routes
|
||||
e.GET("/", func(c echo.Context) error {
|
||||
//render with master
|
||||
return c.Render(http.StatusOK, "listhosts", nil)
|
||||
groupPublic := e.Group("/")
|
||||
groupPublic.GET("*", func(c echo.Context) error {
|
||||
//redirect to admin
|
||||
return c.Redirect(301, "./admin/")
|
||||
})
|
||||
groupAdmin := e.Group("/admin")
|
||||
if authAdmin {
|
||||
groupAdmin.Use(middleware.BasicAuth(h.AuthenticateAdmin))
|
||||
}
|
||||
|
||||
e.GET("/hosts/add", h.AddHost)
|
||||
e.GET("/hosts/edit/:id", h.EditHost)
|
||||
e.GET("/hosts", h.ListHosts)
|
||||
e.GET("/cnames/add", h.AddCName)
|
||||
e.GET("/cnames", h.ListCNames)
|
||||
e.GET("/logs", h.ShowLogs)
|
||||
e.GET("/logs/host/:id", h.ShowHostLogs)
|
||||
groupAdmin.GET("/", h.ListHosts)
|
||||
groupAdmin.GET("/hosts/add", h.AddHost)
|
||||
groupAdmin.GET("/hosts/edit/:id", h.EditHost)
|
||||
groupAdmin.GET("/hosts", h.ListHosts)
|
||||
groupAdmin.GET("/cnames/add", h.AddCName)
|
||||
groupAdmin.GET("/cnames", h.ListCNames)
|
||||
groupAdmin.GET("/logs", h.ShowLogs)
|
||||
groupAdmin.GET("/logs/host/:id", h.ShowHostLogs)
|
||||
|
||||
// Rest Routes
|
||||
e.POST("/hosts/add", h.CreateHost)
|
||||
e.POST("/hosts/edit/:id", h.UpdateHost)
|
||||
e.GET("/hosts/delete/:id", h.DeleteHost)
|
||||
e.POST("/cnames/add", h.CreateCName)
|
||||
e.GET("/cnames/delete/:id", h.DeleteCName)
|
||||
groupAdmin.POST("/hosts/add", h.CreateHost)
|
||||
groupAdmin.POST("/hosts/edit/:id", h.UpdateHost)
|
||||
groupAdmin.GET("/hosts/delete/:id", h.DeleteHost)
|
||||
groupAdmin.POST("/cnames/add", h.CreateCName)
|
||||
groupAdmin.GET("/cnames/delete/:id", h.DeleteCName)
|
||||
|
||||
// dyndns compatible api
|
||||
e.GET("/update", h.UpdateIP)
|
||||
e.GET("/nic/update", h.UpdateIP)
|
||||
e.GET("/v2/update", h.UpdateIP)
|
||||
e.GET("/v3/update", h.UpdateIP)
|
||||
// (avoid breaking changes and create groups for each update endpoint)
|
||||
updateRoute := e.Group("/update")
|
||||
updateRoute.Use(middleware.BasicAuth(h.AuthenticateUpdate))
|
||||
updateRoute.GET("", h.UpdateIP)
|
||||
nicRoute := e.Group("/nic")
|
||||
nicRoute.Use(middleware.BasicAuth(h.AuthenticateUpdate))
|
||||
updateRoute.GET("/update", h.UpdateIP)
|
||||
v2Route := e.Group("/v2")
|
||||
v2Route.Use(middleware.BasicAuth(h.AuthenticateUpdate))
|
||||
v2Route.GET("/update", h.UpdateIP)
|
||||
v3Route := e.Group("/v3")
|
||||
v3Route.Use(middleware.BasicAuth(h.AuthenticateUpdate))
|
||||
v3Route.GET("/update", h.UpdateIP)
|
||||
|
||||
// health-check
|
||||
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"))
|
||||
|
@ -3,7 +3,7 @@ package nswrapper
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/labstack/gommon/log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
@ -25,7 +25,7 @@ func GetIPType(ipAddr string) string {
|
||||
// GetCallerIP searches for the "real" IP senders has actually.
|
||||
// If its a private address we won't use it.
|
||||
func GetCallerIP(r *http.Request) (string, error) {
|
||||
fmt.Println("request", r.Header)
|
||||
log.Info("request", r.Header)
|
||||
for _, h := range []string{"X-Real-Ip", "X-Forwarded-For"} {
|
||||
addresses := strings.Split(r.Header.Get(h), ",")
|
||||
// march from right to left until we get a public address
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/labstack/gommon/log"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -11,7 +12,7 @@ import (
|
||||
|
||||
// UpdateRecord builds a nsupdate file and updates a record by executing it with nsupdate.
|
||||
func UpdateRecord(hostname string, target string, addrType string, zone string, ttl int) error {
|
||||
fmt.Printf("%s record update request: %s -> %s\n", addrType, hostname, target)
|
||||
log.Info(fmt.Sprintf("%s record update request: %s -> %s", addrType, hostname, target))
|
||||
|
||||
f, err := ioutil.TempFile(os.TempDir(), "dyndns")
|
||||
if err != nil {
|
||||
|
7
dyndns/static/css/bootstrap.min.css
vendored
Normal file
7
dyndns/static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dyndns/static/css/bootstrap.min.css.map
Normal file
1
dyndns/static/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
7
dyndns/static/css/jquery-ui.min.css
vendored
Normal file
7
dyndns/static/css/jquery-ui.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
dyndns/static/icons/favicon.ico
Normal file
BIN
dyndns/static/icons/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -1,18 +1,18 @@
|
||||
$("button.addHost").click(function () {
|
||||
location.href='/hosts/add';
|
||||
location.href='/admin/hosts/add';
|
||||
});
|
||||
|
||||
$("button.editHost").click(function () {
|
||||
location.href='/hosts/edit/' + $(this).attr('id');
|
||||
location.href='/admin/hosts/edit/' + $(this).attr('id');
|
||||
});
|
||||
|
||||
$("button.deleteHost").click(function () {
|
||||
$.ajax({
|
||||
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
type: 'GET',
|
||||
url: "/hosts/delete/" + $(this).attr('id')
|
||||
url: "/admin/hosts/delete/" + $(this).attr('id')
|
||||
}).done(function(data, textStatus, jqXHR) {
|
||||
location.href="/hosts";
|
||||
location.href="/admin/hosts";
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
alert("Error: " + $.parseJSON(jqXHR.responseText).message);
|
||||
location.reload()
|
||||
@ -20,7 +20,7 @@ $("button.deleteHost").click(function () {
|
||||
});
|
||||
|
||||
$("button.showHostLog").click(function () {
|
||||
location.href='/logs/host/' + $(this).attr('id');
|
||||
location.href='/admin/logs/host/' + $(this).attr('id');
|
||||
});
|
||||
|
||||
$("button.add, button.edit").click(function () {
|
||||
@ -53,9 +53,9 @@ $("button.add, button.edit").click(function () {
|
||||
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
data: $('#editHostForm').serialize(),
|
||||
type: 'POST',
|
||||
url: '/'+type+'/'+action+id,
|
||||
url: '/admin/'+type+'/'+action+id,
|
||||
}).done(function(data, textStatus, jqXHR) {
|
||||
location.href="/"+type;
|
||||
location.href="/admin/"+type;
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
alert("Error: " + $.parseJSON(jqXHR.responseText).message);
|
||||
});
|
||||
@ -89,16 +89,16 @@ $("#logout").click(function (){
|
||||
});
|
||||
|
||||
$("button.addCName").click(function () {
|
||||
location.href='/cnames/add';
|
||||
location.href='/admin/cnames/add';
|
||||
});
|
||||
|
||||
$("button.deleteCName").click(function () {
|
||||
$.ajax({
|
||||
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
type: 'GET',
|
||||
url: "/cnames/delete/" + $(this).attr('id')
|
||||
url: "/admin/cnames/delete/" + $(this).attr('id')
|
||||
}).done(function(data, textStatus, jqXHR) {
|
||||
location.href="/cnames";
|
||||
location.href="/admin/cnames";
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
alert("Error: " + $.parseJSON(jqXHR.responseText).message);
|
||||
location.reload()
|
||||
@ -124,6 +124,16 @@ $("button.copyToClipboard").click(function () {
|
||||
copyText.setSelectionRange(0, 99999);
|
||||
document.execCommand("copy");
|
||||
});
|
||||
$("button.copyUrlToClipboard").click(function () {
|
||||
let id = $(this).attr('id');
|
||||
let hostname = document.getElementById('host-hostname_'+id).innerHTML
|
||||
let domain = document.getElementById('host-domain_'+id).innerHTML
|
||||
let username = document.getElementById('host-username_'+id).innerHTML
|
||||
let password = document.getElementById('host-password_'+id).innerHTML
|
||||
let out = location.protocol + '//' +username.trim()+':'+password.trim()+'@'+ domain
|
||||
out +='/update?hostname='+hostname
|
||||
navigator.clipboard.writeText(out.trim());
|
||||
});
|
||||
|
||||
function randomHash() {
|
||||
let chars = "abcdefghijklmnopqrstuvwxyz!@#$%^&*()-+<>ABCDEFGHIJKLMNOP1234567890";
|
||||
@ -155,7 +165,7 @@ $(document).ready(function(){
|
||||
}
|
||||
});
|
||||
|
||||
urlPath = new URL(window.location.href).pathname.split("/")[1];
|
||||
urlPath = new URL(window.location.href).pathname.split("/")[2];
|
||||
if (urlPath === "") {
|
||||
urlPath = "hosts"
|
||||
}
|
||||
|
7
dyndns/static/js/bootstrap.min.js
vendored
Normal file
7
dyndns/static/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dyndns/static/js/bootstrap.min.js.map
Normal file
1
dyndns/static/js/bootstrap.min.js.map
Normal file
File diff suppressed because one or more lines are too long
2
dyndns/static/js/jquery-3.4.1.min.js
vendored
Normal file
2
dyndns/static/js/jquery-3.4.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
13
dyndns/static/js/jquery-ui.min.js
vendored
Normal file
13
dyndns/static/js/jquery-ui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
dyndns/static/js/popper.min.js
vendored
Normal file
5
dyndns/static/js/popper.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dyndns/static/js/popper.min.js.map
Normal file
1
dyndns/static/js/popper.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -7,13 +7,13 @@
|
||||
<meta name="author" content="">
|
||||
<link rel="icon" href="/static/icons/favicon.ico">
|
||||
|
||||
<title>TheBBCloud DynDNS</title>
|
||||
<title>{{.title}}</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
|
||||
|
||||
<!-- JQueryUI base CSS -->
|
||||
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.min.css" integrity="sha256-sEGfrwMkIjbgTBwGLVK38BG/XwIiNC/EAG9Rzsfda6A=" crossorigin="anonymous" />
|
||||
<link rel="stylesheet" href="/static/css/jquery-ui.min.css"/>
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="/static/css/narrow-jumbotron.css" rel="stylesheet">
|
||||
@ -27,27 +27,27 @@
|
||||
<nav>
|
||||
<ul class="nav nav-pills float-right">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link nav-hosts" href="/hosts">Hosts</a>
|
||||
<a class="nav-link nav-hosts" href="/admin/hosts">Hosts</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link nav-cnames" href="/cnames">CNames</a>
|
||||
<a class="nav-link nav-cnames" href="/admin/cnames">CNames</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link nav-logs" href="/logs">Logs</a>
|
||||
<a class="nav-link nav-logs" href="/admin/logs">Logs</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link nav-logout" href="" id="logout">Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<h3 class="text-muted">TheBBCloud DynDNS</h3>
|
||||
<h3 class="text-muted">{{.title}}</h3>
|
||||
</div>
|
||||
|
||||
<!-- Page Content -->
|
||||
{{template "content" .}}
|
||||
|
||||
<footer class="footer">
|
||||
<p>© TheBBCloud 2021</p>
|
||||
<p>© {{.title}} {{year}}</p>
|
||||
</footer>
|
||||
|
||||
</div> <!-- /container -->
|
||||
@ -56,10 +56,11 @@
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
|
||||
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>
|
||||
<script src="/static/js/jquery-3.4.1.min.js"></script>
|
||||
<!-- popper.js@1.16.0 -->
|
||||
<script src="/static/js/popper.min.js"></script>
|
||||
<script src="/static/js/bootstrap.min.js"></script>
|
||||
<script src="/static/js/jquery-ui.min.js"></script>
|
||||
<script src="/static/js/actions-1.0.0.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,27 +1,54 @@
|
||||
{{define "content"}}
|
||||
<div class="container marketing">
|
||||
<h3 class="text-center mb-4">DNS Host Entries</h3>
|
||||
<table class="table table-striped text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hostname</th>
|
||||
<th>IP</th>
|
||||
<th>TTL</th>
|
||||
<th>LastUpdate</th>
|
||||
<th><button class="addHost btn btn-primary">Add Host Entry</button></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .hosts}}
|
||||
<tr>
|
||||
<td>{{.Hostname}}.{{.Domain}}</td>
|
||||
<td>{{.Ip}}</td>
|
||||
<td>{{.Ttl}}</td>
|
||||
<td>{{.LastUpdate.Format "01/02/2006 15:04 MEZ"}}</td>
|
||||
<td><button id="{{.ID}}" class="editHost btn btn-outline-secondary btn-sm"><img src="/static/icons/pencil.svg" alt="" width="16" height="16" title="Edit"></button> <button id="{{.ID}}" class="deleteHost btn btn-outline-secondary btn-sm"><img src="/static/icons/trash.svg" alt="" width="16" height="16" title="Delete"></button> <button id="{{.ID}}" class="showHostLog btn btn-outline-secondary btn-sm"><img src="/static/icons/table.svg" alt="" width="16" height="16" title="Logs"></button></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="container marketing">
|
||||
<h3 class="text-center mb-4">DNS Host Entries</h3>
|
||||
<table class="table table-striped text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Hostname</th>
|
||||
<th>IP</th>
|
||||
<th>TTL</th>
|
||||
<th>LastUpdate</th>
|
||||
<th>
|
||||
<button class="addHost btn btn-primary">Add Host Entry</button>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .hosts}}
|
||||
<tr id="host_{{.ID}}">
|
||||
<td id="host-domain_{{.ID}}">{{.Domain}}</td>
|
||||
<td id="host-hostname_{{.ID}}">{{.Hostname}}.{{.Domain}}</td>
|
||||
<td>{{.Ip}}</td>
|
||||
<td>{{.Ttl}}</td>
|
||||
<td>{{.LastUpdate.Format "01/02/2006 15:04 MEZ"}}</td>
|
||||
<td>
|
||||
<div style="display:none">
|
||||
<div id="host-username_{{.ID}}">
|
||||
{{.UserName}}
|
||||
</div>
|
||||
<div id="host-password_{{.ID}}" >
|
||||
{{.Password}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group" id="host_{{.ID}}" >
|
||||
<button id="{{.ID}}" class="editHost btn btn-outline-secondary btn-sm"><img
|
||||
src="/static/icons/pencil.svg" alt="" width="16" height="16" title="Edit"></button>
|
||||
<button
|
||||
id="{{.ID}}" class="deleteHost btn btn-outline-secondary btn-sm"><img
|
||||
src="/static/icons/trash.svg"
|
||||
alt="" width="16" height="16"
|
||||
title="Delete"></button>
|
||||
<button id="{{.ID}}" class="showHostLog btn btn-outline-secondary btn-sm"><img
|
||||
src="/static/icons/table.svg" alt="" width="16" height="16" title="Logs"></button>
|
||||
<button id="{{.ID}}" class="copyUrlToClipboard btn btn-outline-secondary btn-sm"><img
|
||||
src="/static/icons/clipboard.svg" alt="" width="16" height="16" title="Copy URL to clipboard"></button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
Loading…
Reference in New Issue
Block a user