Files
ipv64-tui/widgets.py
T
paase d15c927f55 feat: initial commit for ipv64 DNS TUI client
- Implement modular Textual TUI (main, app, widgets, config, api_client)
- Integrate IPv64.net REST API client using httpx
- Support record listing, creation, in-place edit, and deletion
- Add flexible token resolution (.env, env export, interactive prompt)
- Add bilingual README (DE/EN) and project configuration
2026-09-24 13:51:42 +02:00

108 lines
4.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""IPv64 DNS TUI Client
Author: Pascal Bouquet <pascal@ptbos.de>
License: GNU General Public License v3.0 (GPL-3.0)
Year: 2026
"""
from rich.markup import escape
from textual.app import ComposeResult
from textual.containers import Horizontal, Vertical
from textual.widgets import Button, Input, Label, Select, Static
class RecordForm(Static):
"""Eingabe- und Bearbeitungsformular für DNS-Records."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.edit_record_id: str | None = None
def compose(self) -> ComposeResult:
with Vertical(id="form-pane"):
yield Label("Record hinzufügen:", id="form-title")
with Horizontal(classes="form-row"):
yield Label("Subdomain:", classes="form-label")
yield Input(placeholder="z. B. www oder mx01", id="in-subdomain")
yield Label("Typ:", classes="form-label")
yield Select(
[
("A", "A"),
("AAAA", "AAAA"),
("CNAME", "CNAME"),
("TXT", "TXT"),
("MX", "MX"),
("NS", "NS"),
("SRV", "SRV"),
],
value="A",
id="sel-type",
)
with Horizontal(classes="form-row"):
yield Label("Content/IP:", classes="form-label")
yield Input(placeholder="192.0.2.1 oder Ziel", id="in-content")
yield Label("Prio:", classes="form-label")
yield Input(placeholder="10 (nur MX/SRV)", id="in-prio")
yield Button("Erstellen", id="btn-submit", variant="success")
yield Button(
"Abbrechen",
id="btn-cancel",
variant="default",
classes="hidden",
)
def set_edit_mode(
self, record_id: str, subdomain: str, typ: str, content: str, prio: str
) -> None:
"""Schaltet das Formular in den Bearbeitungsmodus."""
self.edit_record_id = record_id
# Runde Klammern und escape() verhindern den Rich-Markup-Error
clean_id = escape(str(record_id))
self.query_one("#form-title", Label).update(
f"Record bearbeiten (ID: {clean_id}):"
)
# Subdomain bereinigen: @ oder leer wird zu leerem Input
self.query_one("#in-subdomain", Input).value = "" if subdomain in ("@", "") else subdomain
# Select-Wert für Typ setzen, falls in Optionen vorhanden
sel_type = self.query_one("#sel-type", Select)
if typ in [opt[1] for opt in sel_type._options]:
sel_type.value = typ
self.query_one("#in-content", Input).value = "" if content == "-" else content
self.query_one("#in-prio", Input).value = "" if prio in ("-", "None") else prio
btn_submit = self.query_one("#btn-submit", Button)
btn_submit.label = "Speichern"
btn_submit.variant = "warning"
self.query_one("#btn-cancel", Button).remove_class("hidden")
def reset_mode(self) -> None:
"""Setzt das Formular wieder auf 'Neuer Record' zurück."""
self.edit_record_id = None
self.query_one("#form-title", Label).update("Record hinzufügen:")
self.clear()
btn_submit = self.query_one("#btn-submit", Button)
btn_submit.label = "Erstellen"
btn_submit.variant = "success"
self.query_one("#btn-cancel", Button).add_class("hidden")
def get_data(self) -> dict:
return {
"record_id": self.edit_record_id,
"subdomain": self.query_one("#in-subdomain", Input).value.strip(),
"type": str(self.query_one("#sel-type", Select).value),
"content": self.query_one("#in-content", Input).value.strip(),
"prio": self.query_one("#in-prio", Input).value.strip(),
}
def clear(self) -> None:
self.query_one("#in-subdomain", Input).value = ""
self.query_one("#in-content", Input).value = ""
self.query_one("#in-prio", Input).value = ""