package notification import ( "encoding/json" "fmt" "log" "net/http" "net/smtp" "os" ) // SendMsgRequest represents a sendMsg API request. type SendMsgRequest struct { Format string `json:"format"` // "smtp" or "sms" Title string `json:"title"` Message string `json:"message"` Priority int `json:"priority,omitempty"` Extras map[string]string `json:"extras,omitempty"` // e.g., {"to_email": "user@example.com", "phone": "+1234567890"} } // SendMsgResponse represents a sendMsg API response. type SendMsgResponse struct { Status string `json:"status"` MessageID string `json:"messageId,omitempty"` Error string `json:"error,omitempty"` } // Handler handles sendMsg requests and forwards to appropriate channel (email, SMS, or Gotify push). type Handler struct { smtpHost string smtpPort string smtpFrom string smtpUser string smtpPass string smsAPIURL string smsAPIKey string gotifyURL string gotifyToken string } // NewHandler creates a new notification handler from environment variables. func NewHandler() *Handler { return &Handler{ smtpHost: os.Getenv("SMTP_HOST"), smtpPort: os.Getenv("SMTP_PORT"), smtpFrom: os.Getenv("SMTP_FROM"), smtpUser: os.Getenv("SMTP_USER"), smtpPass: os.Getenv("SMTP_PASS"), smsAPIURL: os.Getenv("SMS_API_URL"), smsAPIKey: os.Getenv("SMS_API_KEY"), gotifyURL: os.Getenv("GOTIFY_URL"), gotifyToken: os.Getenv("GOTIFY_TOKEN"), } } // ServeHTTP handles sendMsg requests. func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } var req SendMsgRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(SendMsgResponse{ Status: "error", Error: "invalid request: " + err.Error(), }) return } // Route based on format var resp SendMsgResponse switch req.Format { case "smtp": resp = h.sendEmail(req) case "sms": resp = h.sendSMS(req) default: resp = SendMsgResponse{ Status: "error", Error: "unsupported format: " + req.Format, } } w.Header().Set("Content-Type", "application/json") if resp.Error != "" { w.WriteHeader(http.StatusInternalServerError) } else { w.WriteHeader(http.StatusOK) } json.NewEncoder(w).Encode(resp) } // sendEmail sends an email via SMTP. func (h *Handler) sendEmail(req SendMsgRequest) SendMsgResponse { toEmail := req.Extras["to_email"] if toEmail == "" { return SendMsgResponse{ Status: "error", Error: "missing to_email in extras", } } subject := req.Title if subject == "" { subject = "Notification" } // Construct email body body := req.Message if req.Extras != nil { if cc := req.Extras["cc"]; cc != "" { body = fmt.Sprintf("CC: %s\n\n%s", cc, body) } } msg := fmt.Sprintf( "From: %s\r\nTo: %s\r\nSubject: %s\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n%s", h.smtpFrom, toEmail, subject, body, ) // Send via SMTP smtpAddr := fmt.Sprintf("%s:%s", h.smtpHost, h.smtpPort) auth := smtp.PlainAuth("", h.smtpUser, h.smtpPass, h.smtpHost) if err := smtp.SendMail(smtpAddr, auth, h.smtpFrom, []string{toEmail}, []byte(msg)); err != nil { log.Printf("error sending email to %s: %v", toEmail, err) return SendMsgResponse{ Status: "error", Error: "failed to send email: " + err.Error(), } } return SendMsgResponse{ Status: "success", MessageID: fmt.Sprintf("email-%s", toEmail), } } // sendSMS sends an SMS via configured provider. // Placeholder: integrate with Twilio, AWS SNS, or similar. func (h *Handler) sendSMS(req SendMsgRequest) SendMsgResponse { phone := req.Extras["phone"] if phone == "" { return SendMsgResponse{ Status: "error", Error: "missing phone in extras", } } // TODO: Implement SMS provider integration (Twilio, AWS SNS, etc.) // For now, return error return SendMsgResponse{ Status: "error", Error: "SMS not implemented yet", } }