diff --git a/hbd/server/templates/settings.html b/hbd/server/templates/settings.html index adf7104..f1c4344 100644 --- a/hbd/server/templates/settings.html +++ b/hbd/server/templates/settings.html @@ -265,6 +265,93 @@ .mini-table .crit { color: #b71c1c; font-weight: 600; } .mini-table .dim { color: #aaa; } .mini-table .metric-path { font-family: monospace; font-size: 0.88em; } + + /* ---- Editable inputs ---- */ + .field-input { + width: 100%; + max-width: 360px; + border: 1px solid #ccc; + border-radius: 4px; + padding: 4px 8px; + font-size: 0.88em; + box-sizing: border-box; + font-family: inherit; + } + .field-input:focus { border-color: #0066cc; outline: none; box-shadow: 0 0 0 2px rgba(0,102,204,.15); } + + /* ---- Section footer (Stage Changes button) ---- */ + .section-footer { + padding: 10px 20px; + border-top: 1px solid #f0f0f0; + display: flex; + justify-content: flex-end; + } + + /* ---- Pending changes banner ---- */ + .pending-banner { + position: sticky; + top: 8px; + z-index: 100; + background: #fffbe6; + border: 1px solid #e8c840; + border-radius: 6px; + padding: 10px 16px; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.87em; + margin-bottom: 16px; + box-shadow: 0 2px 8px rgba(0,0,0,.08); + } + .pending-banner .pending-msg { color: #7a6000; } + .pending-banner .pending-actions { display: flex; gap: 8px; } + + /* ---- YAML editor ---- */ + .yaml-editor { + width: 100%; + font-family: monospace; + font-size: 0.83em; + border: 1px solid #ccc; + border-radius: 4px; + padding: 8px; + box-sizing: border-box; + background: #fafafa; + resize: vertical; + min-height: 140px; + } + .yaml-editor:focus { border-color: #0066cc; outline: none; } + + /* ---- Button styles ---- */ + .btn { border: none; border-radius: 4px; padding: 5px 12px; font-size: 0.85em; cursor: pointer; } + .btn-primary { background: #0066cc; color: #fff; } + .btn-primary:hover { background: #0055aa; } + .btn-success { background: #2a7a2a; color: #fff; } + .btn-success:hover { background: #226622; } + .btn-secondary { background: #888; color: #fff; } + .btn-secondary:hover { background: #666; } + .btn-danger { background: transparent; color: #c62828; border: 1px solid #e0e0e0; border-radius: 4px; padding: 2px 7px; font-size: 0.82em; cursor: pointer; } + .btn-danger:hover { background: #fce4ec; } + + /* ---- CRUD table for users / oauth ---- */ + .crud-table { width: 100%; border-collapse: collapse; font-size: 0.83em; } + .crud-table th { background: #f5f5f5; padding: 6px 10px; text-align: left; font-weight: 600; color: #555; font-size: .78em; text-transform: uppercase; letter-spacing: .03em; border-bottom: 1px solid #e0e0e0; } + .crud-table td { padding: 6px 10px; border-bottom: 1px solid #f0f0f0; vertical-align: top; } + .crud-table tbody tr:last-child td { border-bottom: none; } + .crud-table .field-input { max-width: none; } + + /* ---- Rollback modal ---- */ + .modal-overlay { + position: fixed; inset: 0; background: rgba(0,0,0,.4); + display: flex; align-items: center; justify-content: center; z-index: 1000; + } + .modal-box { + background: #fff; border-radius: 8px; padding: 24px; + min-width: 340px; max-width: 520px; width: 90%; + box-shadow: 0 8px 32px rgba(0,0,0,.18); + } + .modal-box h3 { margin: 0 0 12px; font-size: 1em; } + .backup-row { display: flex; align-items: center; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #f0f0f0; font-size: .87em; } + .backup-row:last-child { border-bottom: none; } @@ -272,7 +359,27 @@

Settings

-

Current server configuration — read from the config file at startup.

+

Edit server configuration — changes are staged until you publish them to .hb.yaml.

+ + + + + +
@@ -283,6 +390,8 @@ {% for section in sections %} {{ section.title }} {% endfor %} +
+ View backups / rollback
@@ -295,212 +404,423 @@ {% if section.description %}

{{ section.description }}

{% endif %}
- {# ---- Standard field rows ---- #} + {# ---- Users CRUD ---- #} + {% if section.id == 'users' %} +
+ {% for f in section.fields %} + {% if f.editable %} +
+
{{ f.label }}
+
+ + {% if f.description %}

{{ f.description }}

{% endif %} +
+
+ {% endif %} + {% endfor %} +
+
+ + + + + + + {% for u in section.users %} + + + + + + + + + + {% endfor %} + +
UsernameDisplay nameAvatar URLAdminChannelsNew password
{{ u.username | e }} + {% for ch in all_channel_names %} + + {% endfor %} +
+
+ + + {# ---- OAuth CRUD ---- #} + {% elif section.id == 'oauth' %} +
+ + + + + + + {% for p in section.providers %} + + + + + + + + + + + {% endfor %} + +
Name (slug)TypeURLClient IDClient SecretLabelLogo URL
{{ p.name | e }} + +
+
+ + + {# ---- YAML editor section ---- #} + {% elif section.section_mode == 'yaml' %} +
+ +
+ + +
+
+ + {# ---- Form section (generic fields) ---- #} + {% else %} {% for f in section.fields %}
{{ f.label }}
- {% if f.sensitive %} + {% if f.editable and section.api_section %} + {% if f.type == 'boolean' %} + + {% elif f.type in ('number', 'port', 'size') %} + + {% else %} + + {% endif %} + {% if f.description %}

{{ f.description }}

{% endif %} + {% elif f.sensitive %}
••••••••
- {% elif f.type == "boolean" %} + {% elif f.type == 'boolean' %}
- - {{ 'Enabled' if f.value else 'Disabled' }} - + {{ 'Enabled' if f.value else 'Disabled' }}
- {% elif f.type == "list" %} + {% elif f.type == 'list' %}
- {% if f.value %} - - {% for item in f.value %}{{ item }}{% endfor %} - - {% else %} - None - {% endif %} + {% if f.value %}{% for item in f.value %}{{ item }}{% endfor %} + {% else %}None{% endif %}
- {% elif f.value is none or f.value == "" %} -
Not set
{% else %} -
{{ f.value }}
- {% endif %} - {% if f.description %} -
{{ f.description }}
+
{{ f.value if f.value is not none else '' }}
{% endif %} + {% if f.description and not f.editable %}

{{ f.description }}

{% endif %}
{% endfor %} - - {# ---- Users section ---- #} - {% if section.id == "users" and section.users %} -
- - - - - - - - - - - - {% for u in section.users %} - - - - - - - - {% endfor %} - -
UsernameFull NameRoleAvatarChannels
{{ u.username }}{{ u.full_name or '—' }} - {% if u.admin %} - Admin - {% else %} - User - {% endif %} - - {% if u.avatar %}{{ u.avatar }}{% else %}—{% endif %} - - {% if u.notification_channels %} - - {% for ch in u.notification_channels %} - {{ ch }} - {% endfor %} - - {% else %}—{% endif %} -
+ {% if section.api_section %} + {% endif %} - - {# ---- Notification channels section ---- #} - {% if section.id == "channels" %} - {% for ch in section.channels %} -
-
- {{ ch.name }} - {{ ch.type_label }} -
-
- {% for cf in ch.fields %} -
- {{ cf.label }} - - {% if cf.sensitive %} - •••••••• - {% elif cf.value is iterable and cf.value is not string %} - {{ cf.value | join(', ') }} - {% else %} - {{ cf.value }} - {% endif %} - -
- {% endfor %} -
-
- {% endfor %} - {% if not section.channels %} -
No notification channels configured.
- {% endif %} {% endif %} - {# ---- Threshold configurations section ---- #} - {% if section.id == "thresholds" %} - {% if section.threshold_configs %} - {% for tc in section.threshold_configs %} -
-
{{ tc.name }}
- {% if tc.metrics %} -
- - - - - - - - - - - - - {% for m in tc.metrics %} - - - - - - - - - {% endfor %} - -
MetricOpWarningCriticalHysteresisCount
{{ m.metric }}{{ m.operator or '>' }}{{ m.warning if m.warning is not none else '—' }}{{ m.critical if m.critical is not none else '—' }}{{ '%.0f%%' % (m.hysteresis * 100) if m.hysteresis else '—' }}{{ m.count }}
-
- {% else %} - No thresholds defined. - {% endif %} -
- {% endfor %} - {% else %} -
No threshold configurations defined.
- {% endif %} - {% endif %} - - {# ---- Hosts section ---- #} - {% if section.id == "hosts" %} - {% if section.hosts %} -
- - - - - - - - - - - - - {% for h in section.hosts %} - - - - - - - - - {% endfor %} - -
HostWatchDynDNSOwnerThreshold configChannels
{{ h.name }} - - - - {{ h.owner or '—' }}{{ h.threshold_config or '—' }} - {% if h.notification_channels %} - - {% for ch in h.notification_channels %} - {{ ch }} - {% endfor %} - - {% else %}—{% endif %} -
-
- {% else %} -
No hosts defined in config.
- {% endif %} - {% endif %} - -
{# /section #} + {% endfor %} {# /settings-main #} {# /settings-layout #} {# /container #} -