feat(ui): ui improvements and fixes (#687)
- UI theme improved - Dark/Light themes depending on the user's browser settings - Mobile UI (vertical table) - Fix serving static files (favicon did not work before) - Add readme UI gif and mobile screenshot
@@ -78,9 +78,13 @@ Program to keep DNS A and/or AAAA records updated for multiple DNS providers
|
||||
- Variomedia.de
|
||||
- Zoneedit
|
||||
- **Want more?** [Create an issue for it](https://github.com/qdm12/ddns-updater/issues/new/choose)!
|
||||
- Web User interface
|
||||
- Web user interface (Desktop)
|
||||
|
||||

|
||||

|
||||
|
||||
- Web user interface (Mobile)
|
||||
|
||||

|
||||
|
||||
- Send notifications with [**Shoutrrr**](https://containrrr.dev/shoutrrr/v0.8/services/overview/) using `SHOUTRRR_ADDRESSES`
|
||||
- Container (Docker/K8s) specific features:
|
||||
|
||||
@@ -53,15 +53,15 @@ func (r *Record) HTML(now time.Time) models.HTMLRow {
|
||||
func convertStatus(status models.Status) string {
|
||||
switch status {
|
||||
case constants.SUCCESS:
|
||||
return `<font color="green"><b>Success</b></font>`
|
||||
return `<span class="success">Success</span>`
|
||||
case constants.FAIL:
|
||||
return `<font color="red"><b>Failure</b></font>`
|
||||
return `<span class="error">Failure</span>`
|
||||
case constants.UPTODATE:
|
||||
return `<font color="#00CC66"><b>Up to date</b></font>`
|
||||
return `<span class="uptodate">Up to date</span>`
|
||||
case constants.UPDATING:
|
||||
return `<font color="orange"><b>Updating</b></font>`
|
||||
return `<span class="updating">Updating</span>`
|
||||
case constants.UNSET:
|
||||
return `<font color="purple"><b>Unset</b></font>`
|
||||
return `<span class="unset">Unset</span>`
|
||||
default:
|
||||
return "Unknown status"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
"text/template"
|
||||
@@ -29,6 +30,11 @@ func newHandler(ctx context.Context, rootURL string,
|
||||
db Database, runner UpdateForcer) http.Handler {
|
||||
indexTemplate := template.Must(template.ParseFS(uiFS, "ui/index.html"))
|
||||
|
||||
staticFolder, err := fs.Sub(uiFS, "ui/static")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
handlers := &handlers{
|
||||
ctx: ctx,
|
||||
db: db,
|
||||
@@ -50,5 +56,7 @@ func newHandler(ctx context.Context, rootURL string,
|
||||
|
||||
router.Get(rootURL+"/update", handlers.update)
|
||||
|
||||
router.Handle(rootURL+"/static/*", http.StripPrefix(rootURL+"/static/", http.FileServerFS(staticFolder)))
|
||||
|
||||
return router
|
||||
}
|
||||
|
||||
@@ -1,82 +1,56 @@
|
||||
<html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>DDNS Updater</title>
|
||||
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||
<style>
|
||||
table {
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 1vw;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
border: 2px solid #9a9fa1;
|
||||
text-align: center;
|
||||
padding: 1%;
|
||||
max-width: 35%;
|
||||
transition: all 0.7s;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #d8daf7;
|
||||
}
|
||||
|
||||
tr:nth-child(odd) {
|
||||
background-color: #e6f7ea;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: #f3ebe3;
|
||||
}
|
||||
|
||||
tr {
|
||||
transition: all 0.7s;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: #c1e2f0;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="static/favicon.svg" sizes="any" type="image/svg+xml">
|
||||
<link rel="icon" href="static/favicon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="static/styles.css" type="text/css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Host</th>
|
||||
<th>Provider</th>
|
||||
<th>IP version</th>
|
||||
<th>Update status</th>
|
||||
<th>Set IP</th>
|
||||
<th>Previous IPs (reverse chronological order)</th>
|
||||
</tr>
|
||||
{{range .Rows}}
|
||||
<tr>
|
||||
<td>{{.Domain}}</td>
|
||||
<td>{{.Host}}</td>
|
||||
<td>{{.Provider}}</td>
|
||||
<td>{{.IPVersion}}</td>
|
||||
<td>{{.Status}}</td>
|
||||
<td>{{.CurrentIP}}</td>
|
||||
<td>{{.PreviousIPs}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
<table role="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Host</th>
|
||||
<th>Provider</th>
|
||||
<th>IP Version</th>
|
||||
<th>Update Status</th>
|
||||
<th>Current IP</th>
|
||||
<th>Previous IPs<small> (reverse chronological order)</small></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .Rows}}
|
||||
<tr>
|
||||
<td data-label="Domain">{{.Domain}}</td>
|
||||
<td data-label="Host">{{.Host}}</td>
|
||||
<td data-label="Provider">{{.Provider}}</td>
|
||||
<td data-label="IP Version">{{.IPVersion}}</td>
|
||||
<td data-label="Update Status">{{.Status}}</td>
|
||||
<td data-label="Current IP">{{.CurrentIP}}</td>
|
||||
<td data-label="Previous IPs">{{.PreviousIPs}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
Made by <a href="https://qqq.ninja">Quentin McGaw</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://github.com/qdm12/ddns-updater">github.com/qdm12/ddns-updater</a>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<div>
|
||||
<a href="https://github.com/qdm12/ddns-updater" class="text-big">
|
||||
<svg class="github-icon" height="1em" aria-hidden="true" viewBox="0 0 16 16" version="1.1"
|
||||
data-view-component="true">
|
||||
<path
|
||||
d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div>by <a href="https://github.com/qdm12">Quentin McGaw</a> / UI reworked by <a
|
||||
href="https://github.com/fuse314">Gottfried Mayer</a></div>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
181
internal/server/ui/static/styles.css
Normal file
@@ -0,0 +1,181 @@
|
||||
:root {
|
||||
--page-background: #fff;
|
||||
--table-background: #eee;
|
||||
--background-zebra: #ddd;
|
||||
--background-hover: #e9e9f9;
|
||||
--text-color: #000;
|
||||
--border-color: #666;
|
||||
--link-color: #039;
|
||||
--link-color-hover: #36c;
|
||||
--success-color: #181;
|
||||
--warn-color: #951;
|
||||
--error-color: #811;
|
||||
--progress-color: #818;
|
||||
--footer-background: #eee;
|
||||
--footer-link-color: #666;
|
||||
--footer-link-color-hover: #777;
|
||||
}
|
||||
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--page-background: #000;
|
||||
--table-background: #111;
|
||||
--background-zebra: #222;
|
||||
--background-hover: #191929;
|
||||
--text-color: #ddd;
|
||||
--border-color: #aaa;
|
||||
--link-color: #9cf;
|
||||
--link-color-hover: #69c;
|
||||
--success-color: #9f9;
|
||||
--warn-color: #fb8;
|
||||
--error-color: #f88;
|
||||
--progress-color: #c7c;
|
||||
--footer-background: #111;
|
||||
--footer-link-color: #999;
|
||||
--footer-link-color-hover: #888;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||
font-size: 16px;
|
||||
background-color: var(--page-background);
|
||||
color: var(--text-color);
|
||||
margin: 0;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-radius: 0.3rem;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
max-width: 1900px;
|
||||
background: var(--table-background);
|
||||
margin: 0 auto;
|
||||
position: center;
|
||||
}
|
||||
|
||||
td,
|
||||
th {
|
||||
text-align: center;
|
||||
padding: 0.7rem;
|
||||
}
|
||||
|
||||
th {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
tbody tr:nth-child(odd) {
|
||||
background: var(--background-zebra);
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background: var(--background-hover);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--link-color-hover);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: var(--footer-background);
|
||||
font-size: 0.9em;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
footer a {
|
||||
color: var(--footer-link-color);
|
||||
}
|
||||
footer a:hover {
|
||||
color: var(--footer-link-color-hover);
|
||||
}
|
||||
|
||||
.text-big {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.success, .error, .uptodate, .updating, .unset {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.uptodate {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.updating {
|
||||
color: var(--progress-color);
|
||||
}
|
||||
|
||||
.unset {
|
||||
color: var(--warn-color);
|
||||
}
|
||||
|
||||
.github-icon {
|
||||
vertical-align: text-bottom;
|
||||
fill: currentColor;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
/* responsive table. source: https://css-tricks.com/responsive-data-tables/ */
|
||||
@media only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: 1024px) {
|
||||
table,
|
||||
thead,
|
||||
tbody,
|
||||
th,
|
||||
td,
|
||||
tr {
|
||||
display: block;
|
||||
}
|
||||
|
||||
thead tr {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
tr {
|
||||
margin: 0 0 1rem 0;
|
||||
}
|
||||
|
||||
td {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
position: relative;
|
||||
padding-left: 50%;
|
||||
}
|
||||
|
||||
td:before {
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
width: 45%;
|
||||
padding-right: 10px;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
content: attr(data-label);
|
||||
}
|
||||
}
|
||||
BIN
readme/webui-desktop.gif
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
readme/webui-mobile.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
readme/webui.png
|
Before Width: | Height: | Size: 137 KiB |