ddns/templates/index.templ

256 lines
6.1 KiB
Text

package templates
import "fmt"
import "strings"
type IndexProps struct {
Title string
IsConfigured bool
CurrentIP string
Config ConfigData
Records []DNSRecord
UpdateFreqs []UpdateFrequency
}
type ConfigData struct {
ZoneID string
Domain string
UpdatePeriod string
ApiToken string
}
type DNSRecord struct {
ID string
Type string
Name string
Content string
TTL int
Proxied bool
CreatedOn string
}
type UpdateFrequency struct {
Label string
Value string
}
templ Index(props IndexProps) {
@Layout(props.Title) {
<div class="container">
<div class="row justify-content-center">
<div class="col-md-10">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>{ props.Title }</h1>
<div class="current-ip d-flex align-items-center">
<span class="me-2">Current IP:</span>
<span id="current-ip" class="fw-bold">{ props.CurrentIP }</span>
<button
class="btn btn-sm btn-outline-secondary ms-2"
title="Refresh current IP"
hx-get="/refresh-ip"
hx-target="#current-ip"
hx-indicator="#refresh-spinner"
>
<i class="bi bi-arrow-clockwise"></i>
<span id="refresh-spinner" class="htmx-indicator spinner-border spinner-border-sm ms-1"></span>
</button>
</div>
</div>
if !props.IsConfigured {
@ConfigWarning()
} else {
@ConfigStatus(props.Config)
@DNSRecordsSection(props.Records, props.CurrentIP)
}
@ConfigModal(props.Config, props.UpdateFreqs)
@RecordModal(props.Config.Domain)
</div>
</div>
</div>
<div class="toast-container"></div>
}
}
templ ConfigWarning() {
<div class="alert alert-warning config-warning">
<h4>Configuration Required</h4>
<p>Please configure your Cloudflare API credentials to manage your DNS records.</p>
<button
class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#configModal"
>
Configure Now
</button>
</div>
}
templ ConfigStatus(config ConfigData) {
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Configuration</h5>
<button
class="btn btn-sm btn-outline-primary"
data-bs-toggle="modal"
data-bs-target="#configModal"
>
Edit
</button>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<strong>Domain:</strong> <span>{ config.Domain }</span>
</div>
<div class="col-md-4">
<strong>Zone ID:</strong> <span>{ config.ZoneID }</span>
</div>
<div class="col-md-4">
<strong>IP Update Schedule:</strong>
<span>{ formatUpdateSchedule(config.UpdatePeriod) }</span>
</div>
</div>
</div>
</div>
}
templ DNSRecordsSection(records []DNSRecord, currentIP string) {
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">DNS Records</h5>
<div>
<button
class="btn btn-sm btn-success me-2"
hx-post="/update-all-records"
hx-target="#dns-records-table"
hx-confirm="Are you sure you want to update all A records to your current IP?"
hx-indicator="#update-all-spinner"
>
<i class="bi bi-arrow-repeat"></i> Update All to Current IP
<span id="update-all-spinner" class="htmx-indicator spinner-border spinner-border-sm ms-1"></span>
</button>
<button
class="btn btn-sm btn-primary"
data-bs-toggle="modal"
data-bs-target="#recordModal"
onclick="resetRecordForm()"
>
<i class="bi bi-plus-lg"></i> Add Record
</button>
</div>
</div>
<div class="card-body p-0">
@DNSRecordsTable(records, currentIP)
</div>
</div>
}
templ DNSRecordsTable(records []DNSRecord, currentIP string) {
<div id="dns-records-table" class="table-responsive">
<table class="table table-striped table-hover mb-0">
<thead>
<tr>
<th>Type</th>
<th>Name</th>
<th>Content</th>
<th>TTL</th>
<th>Proxied</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
if len(records) == 0 {
<tr>
<td colspan="6" class="text-center">No DNS records found</td>
</tr>
} else {
for _, record := range records {
@DNSRecordRow(record, currentIP)
}
}
</tbody>
</table>
</div>
}
templ DNSRecordRow(record DNSRecord, currentIP string) {
<tr>
<td>{ record.Type }</td>
<td>{ record.Name }</td>
<td>
{ record.Content }
if record.Type == "A" {
if record.Content == currentIP {
<span class="badge bg-success update-badge">Current IP</span>
} else {
<span class="badge bg-warning update-badge">Outdated IP</span>
}
}
</td>
<td>
if record.TTL == 1 {
Auto
} else {
{ fmt.Sprintf("%ds", record.TTL) }
}
</td>
<td>
if record.Proxied {
<i class="bi bi-check-lg text-success"></i>
}
</td>
<td>
<button
class="btn btn-sm btn-outline-primary me-1"
hx-get={ fmt.Sprintf("/edit-record/%s", record.ID) }
hx-target="#record-modal-content"
data-bs-toggle="modal"
data-bs-target="#recordModal"
>
<i class="bi bi-pencil"></i>
</button>
<button
class="btn btn-sm btn-outline-danger"
hx-delete={ fmt.Sprintf("/records/%s", record.ID) }
hx-target="closest tr"
hx-swap="outerHTML"
hx-confirm={ fmt.Sprintf("Are you sure you want to delete the record for \"%s\"?", record.Name) }
>
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
}
// Helper function to format update schedule
func formatUpdateSchedule(period string) string {
switch period {
case "*/1 * * * *":
return "Every minute"
case "*/5 * * * *":
return "Every 5 minutes"
case "*/30 * * * *":
return "Every 30 minutes"
case "0 * * * *":
return "Hourly"
case "0 */6 * * *":
return "Every 6 hours"
case "0 0 * * *":
return "Daily"
case "":
return "Manual updates only"
default:
return period
}
}
// Helper function to extract subdomain name
func getRecordName(fullName, domain string) string {
if fullName == domain {
return "@"
}
if strings.HasSuffix(fullName, "."+domain) {
return fullName[:len(fullName)-len(domain)-1]
}
return fullName
}