package server
import (
"fmt"
"net/http"
"net/url"
"prism/internal/notification"
"prism/internal/signal"
"prism/internal/util"
)
func (s *Server) handleFragmentHealth(w http.ResponseWriter, r *http.Request) {
linked := s.getLinkedAccount()
signalOk := s.signalDaemon.IsRunning()
hasProton := s.cfg.IsProtonEnabled()
w.Header().Set("Content-Type", "text/html")
statusClass := "status-error"
statusText := "Disconnected and Unlinked"
var tooltip string
if signalOk && linked != nil {
statusClass = "status-ok"
statusText = "Connected and Linked"
tooltip = fmt.Sprintf(`%s`, util.FormatPhoneNumber(linked.Number))
} else if signalOk {
statusText = "Connected and Unlinked"
}
html := fmt.Sprintf(`
Signal: %s%s
`, statusClass, statusText, tooltip)
if hasProton {
protonStatus := "Disconnected"
protonClass := "status-error"
protonTooltip := ""
// TODO: check actual IMAP connection status with protonMonitor
html += fmt.Sprintf(`
Proton Mail: %s%s
`, protonClass, protonStatus, protonTooltip)
}
html += `
`
html += s.getSignalInfoHTML()
html += `
`
_, _ = fmt.Fprint(w, html) //nolint:errcheck // Error writing to ResponseWriter is handled by HTTP server
}
func (s *Server) handleFragmentSignalInfo(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
_, _ = fmt.Fprint(w, s.getSignalInfoHTML()) //nolint:errcheck // Error writing to ResponseWriter is handled by HTTP server
}
func (s *Server) getSignalInfoHTML() string {
if s.getLinkedAccount() != nil {
return fmt.Sprintf(`
Unlink and remove device
- Open Signal app → Settings → Linked Devices
- Find "%s" and tap it
- Tap "Unlink Device"
`, s.cfg.DeviceName)
}
return s.getQRCodeHTML()
}
func (s *Server) getLinkedAccount() *signal.AccountInfo {
client := signal.NewClient(s.cfg.SignalCLISocketPath)
account, _ := client.GetLinkedAccount() //nolint:errcheck // Nil account is valid, indicates not linked
return account
}
func (s *Server) getQRCodeHTML() string {
if s.getLinkedAccount() != nil {
return `Account already linked
`
}
linkDevice := signal.NewLinkDevice(signal.NewClient(s.cfg.SignalCLISocketPath))
qrCode, err := linkDevice.GenerateQR()
if err != nil {
s.logger.Error("Failed to generate QR code", "error", err)
return `Signal daemon is starting up, please refresh in a few seconds...
`
}
return fmt.Sprintf(`Scan this QR code with your Signal app:
Settings → Linked Devices → Link New Device
`, qrCode)
}
func (s *Server) handleFragmentEndpoints(w http.ResponseWriter, r *http.Request) {
mappings, err := s.store.GetAllMappings()
if err != nil {
s.logger.Error("Failed to get mappings", "error", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html")
if len(mappings) == 0 {
_, _ = fmt.Fprint(w, `No endpoints registered
`) //nolint:errcheck // Error writing to ResponseWriter is handled by HTTP server
return
}
_, _ = fmt.Fprint(w, ``) //nolint:errcheck // Error writing to ResponseWriter is handled by HTTP server
for _, m := range mappings {
isSignal := m.Channel == notification.ChannelSignal
isWebhook := m.Channel == notification.ChannelWebhook
channelBadge := "Signal"
if isWebhook {
channelBadge = "Webhook"
}
html := fmt.Sprintf(`-
%s
%s`, m.AppName, m.Channel, channelBadge)
if m.UpEndpoint != nil && isWebhook {
// Extract hostname from webhook URL
if u, err := url.Parse(*m.UpEndpoint); err == nil {
html += fmt.Sprintf(`%s`, u.Hostname())
}
}
if m.GroupID != nil && isSignal {
html += fmt.Sprintf(`%s`, *m.GroupID)
}
html += `
`
if m.UpEndpoint != nil {
html += fmt.Sprintf(``, m.Endpoint, map[bool]string{true: " selected", false: ""}[isSignal], map[bool]string{true: " selected", false: ""}[isWebhook])
}
html += fmt.Sprintf(`
`, m.Endpoint)
_, _ = fmt.Fprint(w, html) //nolint:errcheck // Error writing to ResponseWriter is handled by HTTP server
}
_, _ = fmt.Fprint(w, `
`) //nolint:errcheck // Error writing to ResponseWriter is handled by HTTP server
}