|
|
@@ -56,9 +56,11 @@ type StatusResponse struct {
|
|
|
|
|
|
type NetworkStatus struct {
|
|
|
Eth0IP string `json:"eth0_ip,omitempty"`
|
|
|
+ Eth0MAC string `json:"eth0_mac,omitempty"`
|
|
|
Eth0RX int64 `json:"eth0_rx,omitempty"`
|
|
|
Eth0TX int64 `json:"eth0_tx,omitempty"`
|
|
|
Wlan0IP string `json:"wlan0_ip,omitempty"`
|
|
|
+ Wlan0MAC string `json:"wlan0_mac,omitempty"`
|
|
|
Wlan0SSID string `json:"wlan0_ssid,omitempty"`
|
|
|
Wlan0Signal int `json:"wlan0_signal,omitempty"`
|
|
|
Wlan0Channel int `json:"wlan0_channel,omitempty"`
|
|
|
@@ -164,6 +166,7 @@ func (s *APIServer) Start(addr string) error {
|
|
|
mux.HandleFunc("/api/settings", s.handleSettings)
|
|
|
mux.HandleFunc("/api/unlock", s.handleUnlock)
|
|
|
mux.HandleFunc("/api/logs", s.handleLogs)
|
|
|
+ mux.HandleFunc("/api/kernel-logs", s.handleKernelLogs)
|
|
|
mux.HandleFunc("/api/ws", s.handleWebSocket)
|
|
|
|
|
|
// Serve static files for dashboard with SPA fallback
|
|
|
@@ -261,9 +264,11 @@ func (s *APIServer) handleStatus(w http.ResponseWriter, r *http.Request) {
|
|
|
Uptime: getUptime(),
|
|
|
Network: NetworkStatus{
|
|
|
Eth0IP: getInterfaceIP("eth0"),
|
|
|
+ Eth0MAC: getMacAddress("eth0"),
|
|
|
Eth0RX: eth0rx,
|
|
|
Eth0TX: eth0tx,
|
|
|
Wlan0IP: getInterfaceIP("wlan0"),
|
|
|
+ Wlan0MAC: getMacAddress("wlan0"),
|
|
|
Wlan0SSID: wlanInfo.ssid,
|
|
|
Wlan0Signal: wlanInfo.signal,
|
|
|
Wlan0Channel: wlanInfo.channel,
|
|
|
@@ -344,6 +349,38 @@ func (s *APIServer) handleLogs(w http.ResponseWriter, r *http.Request) {
|
|
|
s.jsonResponse(w, result)
|
|
|
}
|
|
|
|
|
|
+// handleKernelLogs returns recent kernel log lines from dmesg
|
|
|
+func (s *APIServer) handleKernelLogs(w http.ResponseWriter, r *http.Request) {
|
|
|
+ // Run dmesg command
|
|
|
+ cmd := exec.Command("dmesg", "-T")
|
|
|
+ output, err := cmd.CombinedOutput()
|
|
|
+ if err != nil {
|
|
|
+ // Try without -T flag (human-readable timestamps) if not supported
|
|
|
+ cmd = exec.Command("dmesg")
|
|
|
+ output, err = cmd.CombinedOutput()
|
|
|
+ if err != nil {
|
|
|
+ s.jsonResponse(w, []string{})
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ lines := strings.Split(string(output), "\n")
|
|
|
+ // Return last 500 lines
|
|
|
+ if len(lines) > 500 {
|
|
|
+ lines = lines[len(lines)-500:]
|
|
|
+ }
|
|
|
+
|
|
|
+ // Filter out empty lines
|
|
|
+ var result []string
|
|
|
+ for _, line := range lines {
|
|
|
+ if strings.TrimSpace(line) != "" {
|
|
|
+ result = append(result, line)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ s.jsonResponse(w, result)
|
|
|
+}
|
|
|
+
|
|
|
// handleConfig returns current config (without secrets)
|
|
|
func (s *APIServer) handleConfig(w http.ResponseWriter, r *http.Request) {
|
|
|
s.daemon.mu.Lock()
|
|
|
@@ -789,6 +826,16 @@ func freqToChannel(freq int) int {
|
|
|
return 0
|
|
|
}
|
|
|
|
|
|
+// getMacAddress returns MAC address for given interface
|
|
|
+func getMacAddress(iface string) string {
|
|
|
+ addressPath := "/sys/class/net/" + iface + "/address"
|
|
|
+ data, err := os.ReadFile(addressPath)
|
|
|
+ if err != nil {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ return strings.TrimSpace(string(data))
|
|
|
+}
|
|
|
+
|
|
|
// applyWiFiSettings configures wpa_supplicant and connects to WiFi
|
|
|
func applyWiFiSettings(ssid, psk string) error {
|
|
|
// Retry the whole connection up to 3 times
|