dev: automated commit - 2025-06-17 16:15:21
This commit is contained in:
parent
73e91db78b
commit
27775e6b29
16 changed files with 1860 additions and 1240 deletions
|
@ -3,253 +3,314 @@ package templates
|
|||
import "fmt"
|
||||
|
||||
templ ConfigModal(config ConfigData, frequencies []UpdateFrequency) {
|
||||
<div
|
||||
class="modal fade"
|
||||
id="configModal"
|
||||
tabindex="-1"
|
||||
aria-labelledby="configModalLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="configModalLabel">Configuration</h5>
|
||||
<div id="contact" class="modal-container">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Configuration Settings</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
@click="$el.closest('dialog').close()"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
<form
|
||||
x-target="config-status"
|
||||
method="post"
|
||||
action="/config"
|
||||
@ajax:success="$el.closest('dialog').close()"
|
||||
x-data="{ saving: false }"
|
||||
@ajax:before="saving = true"
|
||||
@ajax:after="saving = false"
|
||||
@ajax:error="saving = false"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="api-token" class="form-label fw-semibold">
|
||||
<i class="bi bi-key-fill text-primary me-2"></i>
|
||||
Cloudflare API Token
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="api-token"
|
||||
name="api_token"
|
||||
value={ config.ApiToken }
|
||||
placeholder="Enter your Cloudflare API token"
|
||||
required
|
||||
/>
|
||||
<div class="form-text">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
Create a token with <code>Zone.DNS:Edit</code> permissions in
|
||||
the Cloudflare dashboard.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="zone-id-input" class="form-label fw-semibold">
|
||||
<i class="bi bi-globe text-info me-2"></i>
|
||||
Zone ID
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="zone-id-input"
|
||||
name="zone_id"
|
||||
value={ config.ZoneID }
|
||||
placeholder="e.g., 1234567890abcdef1234567890abcdef"
|
||||
required
|
||||
/>
|
||||
<div class="form-text">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
Found in the Cloudflare dashboard under your domain's overview page.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="domain-input" class="form-label fw-semibold">
|
||||
<i class="bi bi-link-45deg text-success me-2"></i>
|
||||
Domain
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="domain-input"
|
||||
name="domain"
|
||||
value={ config.Domain }
|
||||
placeholder="e.g., example.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="update-period" class="form-label fw-semibold">
|
||||
<i class="bi bi-clock text-warning me-2"></i>
|
||||
Update Frequency
|
||||
</label>
|
||||
<select class="form-select" id="update-period" name="update_period">
|
||||
for _, freq := range frequencies {
|
||||
<option value={ freq.Value } selected?={ freq.Value == config.UpdatePeriod }>
|
||||
{ freq.Label }
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
class="btn btn-secondary"
|
||||
@click="$el.closest('dialog').close()"
|
||||
>
|
||||
<i class="bi bi-x-lg me-1"></i>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
:disabled="saving"
|
||||
>
|
||||
<template x-if="!saving">
|
||||
<span class="d-flex align-items-center">
|
||||
<i class="bi bi-check-lg me-2"></i>
|
||||
Save Configuration
|
||||
</span>
|
||||
</template>
|
||||
<template x-if="saving">
|
||||
<span class="d-flex align-items-center">
|
||||
<span class="spinner-border spinner-border-sm me-2"></span>
|
||||
Saving...
|
||||
</span>
|
||||
</template>
|
||||
</button>
|
||||
</div>
|
||||
<form hx-post="/config" hx-target="body" hx-swap="outerHTML">
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="api-token" class="form-label">Cloudflare API Token</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="api-token"
|
||||
name="api_token"
|
||||
value={ config.ApiToken }
|
||||
required
|
||||
/>
|
||||
<div class="form-text">
|
||||
Create a token with <code>Zone.DNS:Edit</code> permissions in
|
||||
the Cloudflare dashboard.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="zone-id-input" class="form-label">Zone ID</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="zone-id-input"
|
||||
name="zone_id"
|
||||
value={ config.ZoneID }
|
||||
required
|
||||
/>
|
||||
<div class="form-text">
|
||||
Found in the Cloudflare dashboard under your domain's overview page.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="domain-input" class="form-label">Domain</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="domain-input"
|
||||
name="domain"
|
||||
value={ config.Domain }
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="update-period" class="form-label">Update Frequency</label>
|
||||
<select class="form-select" id="update-period" name="update_period">
|
||||
for _, freq := range frequencies {
|
||||
<option value={ freq.Value } selected?={ freq.Value == config.UpdatePeriod }>
|
||||
{ freq.Label }
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Save
|
||||
<span class="htmx-indicator spinner-border spinner-border-sm ms-1"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ RecordModal(domain string) {
|
||||
<div
|
||||
class="modal fade"
|
||||
id="recordModal"
|
||||
tabindex="-1"
|
||||
aria-labelledby="recordModalLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div id="record-modal-content" class="modal-content">
|
||||
@RecordForm("Add DNS Record", "", domain, DNSRecord{Type: "A", TTL: 1})
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ RecordForm(title, recordID, domain string, record DNSRecord) {
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{ title }</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
<form
|
||||
if recordID != "" {
|
||||
hx-put={ fmt.Sprintf("/records/%s", recordID) }
|
||||
} else {
|
||||
hx-post="/records"
|
||||
}
|
||||
hx-target="#dns-records-table"
|
||||
hx-on::after-request="if(event.detail.successful) bootstrap.Modal.getInstance(document.getElementById('recordModal')).hide()"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="record-name" class="form-label">Name</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="record-name"
|
||||
name="name"
|
||||
placeholder="subdomain"
|
||||
value={ getRecordName(record.Name, domain) }
|
||||
required
|
||||
/>
|
||||
<span class="input-group-text">.{ domain }</span>
|
||||
</div>
|
||||
<div class="form-text">Use @ for the root domain</div>
|
||||
<div id="contact" class="modal-container">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
<i class="bi bi-dns text-primary me-2"></i>
|
||||
{ title }
|
||||
</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
@click="$el.closest('dialog').close()"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="record-type" class="form-label">Type</label>
|
||||
<select
|
||||
class="form-select"
|
||||
id="record-type"
|
||||
name="type"
|
||||
onchange="toggleMyIPOption()"
|
||||
>
|
||||
<option value="A" selected?={ record.Type == "A" }>A</option>
|
||||
<option value="AAAA" selected?={ record.Type == "AAAA" }>AAAA</option>
|
||||
<option value="CNAME" selected?={ record.Type == "CNAME" }>CNAME</option>
|
||||
<option value="TXT" selected?={ record.Type == "TXT" }>TXT</option>
|
||||
<option value="MX" selected?={ record.Type == "MX" }>MX</option>
|
||||
</select>
|
||||
<div id="error-message" class="alert alert-danger d-none mx-3 mt-3" role="alert">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<span class="error-text"></span>
|
||||
</div>
|
||||
<div class="mb-3" id="content-group">
|
||||
<label for="record-content" class="form-label">Content</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="record-content"
|
||||
name="content"
|
||||
value={ record.Content }
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-3 form-check" id="use-my-ip-group" style="display: none;">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
id="use-my-ip"
|
||||
name="use_my_ip"
|
||||
onchange="toggleContentField()"
|
||||
/>
|
||||
<label class="form-check-label" for="use-my-ip">Use my current IP address</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="record-ttl" class="form-label">TTL</label>
|
||||
<select class="form-select" id="record-ttl" name="ttl">
|
||||
<option value="1" selected?={ record.TTL == 1 }>Auto</option>
|
||||
<option value="120" selected?={ record.TTL == 120 }>2 minutes</option>
|
||||
<option value="300" selected?={ record.TTL == 300 }>5 minutes</option>
|
||||
<option value="600" selected?={ record.TTL == 600 }>10 minutes</option>
|
||||
<option value="1800" selected?={ record.TTL == 1800 }>30 minutes</option>
|
||||
<option value="3600" selected?={ record.TTL == 3600 }>1 hour</option>
|
||||
<option value="7200" selected?={ record.TTL == 7200 }>2 hours</option>
|
||||
<option value="18000" selected?={ record.TTL == 18000 }>5 hours</option>
|
||||
<option value="43200" selected?={ record.TTL == 43200 }>12 hours</option>
|
||||
<option value="86400" selected?={ record.TTL == 86400 }>1 day</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
id="record-proxied"
|
||||
name="proxied"
|
||||
checked?={ record.Proxied }
|
||||
/>
|
||||
<label class="form-check-label" for="record-proxied">Proxied through Cloudflare</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
<form
|
||||
if recordID != "" {
|
||||
method="put"
|
||||
action={ templ.URL(fmt.Sprintf("/records/%s", recordID)) }
|
||||
} else {
|
||||
method="post"
|
||||
action="/records"
|
||||
}
|
||||
x-target="dns-records-table"
|
||||
x-target.error="none"
|
||||
@ajax:success="$el.closest('dialog').close()"
|
||||
x-data="{ saving: false }"
|
||||
@ajax:before="saving = true"
|
||||
@ajax:after="saving = false"
|
||||
@ajax:error="saving = false"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Save
|
||||
<span class="htmx-indicator spinner-border spinner-border-sm ms-1"></span>
|
||||
</button>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-md-8 mb-3">
|
||||
<label for="record-name" class="form-label fw-semibold">
|
||||
<i class="bi bi-tag text-info me-2"></i>
|
||||
Record Name
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="record-name"
|
||||
name="name"
|
||||
placeholder="subdomain"
|
||||
value={ getRecordName(record.Name, domain) }
|
||||
required
|
||||
/>
|
||||
<span class="input-group-text bg-light text-muted">.{ domain }</span>
|
||||
</div>
|
||||
<div class="form-text">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
Use <code>{ "@" }</code> { "for" } the root domain
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="record-type" class="form-label fw-semibold">
|
||||
<i class="bi bi-diagram-3 text-success me-2"></i>
|
||||
Type
|
||||
</label>
|
||||
<select
|
||||
class="form-select"
|
||||
id="record-type"
|
||||
name="type"
|
||||
onchange="toggleMyIPOption()"
|
||||
>
|
||||
<option value="A" selected?={ record.Type == "A" }>A (IPv4)</option>
|
||||
<option value="AAAA" selected?={ record.Type == "AAAA" }>AAAA (IPv6)</option>
|
||||
<option value="CNAME" selected?={ record.Type == "CNAME" }>CNAME</option>
|
||||
<option value="TXT" selected?={ record.Type == "TXT" }>TXT</option>
|
||||
<option value="MX" selected?={ record.Type == "MX" }>MX</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3" id="content-group">
|
||||
<label for="record-content" class="form-label fw-semibold">
|
||||
<i class="bi bi-file-text text-warning me-2"></i>
|
||||
Content
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="record-content"
|
||||
name="content"
|
||||
value={ record.Content }
|
||||
placeholder="Enter the record value"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-3 form-check bg-light p-3 rounded" id="use-my-ip-group" style="display: none;">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
id="use-my-ip"
|
||||
name="use_my_ip"
|
||||
onchange="toggleContentField()"
|
||||
/>
|
||||
<label class="form-check-label fw-semibold" for="use-my-ip">
|
||||
<i class="bi bi-geo-alt text-primary me-2"></i>
|
||||
Use my current IP address
|
||||
</label>
|
||||
<div class="form-text">
|
||||
Automatically use your current public IP address
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="record-ttl" class="form-label fw-semibold">
|
||||
<i class="bi bi-clock-history text-secondary me-2"></i>
|
||||
TTL (Time to Live)
|
||||
</label>
|
||||
<select class="form-select" id="record-ttl" name="ttl">
|
||||
<option value="1" selected?={ record.TTL == 1 }>Auto</option>
|
||||
<option value="120" selected?={ record.TTL == 120 }>2 minutes</option>
|
||||
<option value="300" selected?={ record.TTL == 300 }>5 minutes</option>
|
||||
<option value="600" selected?={ record.TTL == 600 }>10 minutes</option>
|
||||
<option value="1800" selected?={ record.TTL == 1800 }>30 minutes</option>
|
||||
<option value="3600" selected?={ record.TTL == 3600 }>1 hour</option>
|
||||
<option value="7200" selected?={ record.TTL == 7200 }>2 hours</option>
|
||||
<option value="18000" selected?={ record.TTL == 18000 }>5 hours</option>
|
||||
<option value="43200" selected?={ record.TTL == 43200 }>12 hours</option>
|
||||
<option value="86400" selected?={ record.TTL == 86400 }>1 day</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3 d-flex align-items-end">
|
||||
<div class="form-check bg-light p-3 rounded w-100">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
id="record-proxied"
|
||||
name="proxied"
|
||||
checked?={ record.Proxied }
|
||||
/>
|
||||
<label class="form-check-label fw-semibold" for="record-proxied">
|
||||
<i class="bi bi-shield-check text-success me-2"></i>
|
||||
Proxied through Cloudflare
|
||||
</label>
|
||||
<div class="form-text">
|
||||
Enable Cloudflare's proxy and security features
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
@click="$el.closest('dialog').close()"
|
||||
>
|
||||
<i class="bi bi-x-lg me-1"></i>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
:disabled="saving"
|
||||
>
|
||||
<template x-if="!saving">
|
||||
<span class="d-flex align-items-center">
|
||||
<i class="bi bi-check-lg me-2"></i>
|
||||
if recordID != "" {
|
||||
Update Record
|
||||
} else {
|
||||
Create Record
|
||||
}
|
||||
</span>
|
||||
</template>
|
||||
<template x-if="saving">
|
||||
<span class="d-flex align-items-center">
|
||||
<span class="spinner-border spinner-border-sm me-2"></span>
|
||||
if recordID != "" {
|
||||
Updating...
|
||||
} else {
|
||||
Creating...
|
||||
}
|
||||
</span>
|
||||
</template>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
function toggleMyIPOption() {
|
||||
const recordType = document.getElementById('record-type').value;
|
||||
const useMyIPGroup = document.getElementById('use-my-ip-group');
|
||||
const contentGroup = document.getElementById('content-group');
|
||||
|
||||
if (recordType === 'A') {
|
||||
useMyIPGroup.style.display = 'block';
|
||||
} else {
|
||||
useMyIPGroup.style.display = 'none';
|
||||
contentGroup.style.display = 'block';
|
||||
document.getElementById('use-my-ip').checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleContentField() {
|
||||
const useMyIP = document.getElementById('use-my-ip').checked;
|
||||
const contentGroup = document.getElementById('content-group');
|
||||
contentGroup.style.display = useMyIP ? 'none' : 'block';
|
||||
}
|
||||
|
||||
function resetRecordForm() {
|
||||
setTimeout(() => {
|
||||
document.getElementById('record-type').value = 'A';
|
||||
toggleMyIPOption();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Initialize the form state
|
||||
toggleMyIPOption();
|
||||
</script>
|
||||
</div>
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue