package handlers import ( "encoding/json" "fmt" "net/http" "os" "strconv" "sync" "github.com/go-chi/chi/v5" "boc/email" ) var ( mailConfig *MailConfig mailConfigMu sync.RWMutex defaultIMAP *email.IMAPClient ) // MailConfig stores IMAP configuration type MailConfig struct { Email string `json:"email"` Password string `json:"password"` Server string `json:"server"` Port int `json:"port"` UseTLS bool `json:"useTLS"` } func init() { // Try to initialize from environment imapURL := os.Getenv("IMAP_URL") if imapURL != "" { var err error defaultIMAP, err = email.ParseIMAPURL(imapURL) if err != nil { fmt.Println("Failed to parse IMAP_URL:", err.Error()) } } } func getIMAPClient() *email.IMAPClient { mailConfigMu.RLock() defer mailConfigMu.RUnlock() if mailConfig != nil && mailConfig.Email != "" { return email.NewIMAPClient(mailConfig.Server, mailConfig.Port, mailConfig.Email, mailConfig.Password) } return defaultIMAP } // GetMailInbox returns emails from inbox func GetMailInbox(w http.ResponseWriter, r *http.Request) { client := getIMAPClient() if client == nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusServiceUnavailable) json.NewEncoder(w).Encode(map[string]interface{}{ "ok": false, "error": "IMAP not configured. Set IMAP_URL environment variable or configure via /api/v1/mail/config", }) return } limit := 50 if l := r.URL.Query().Get("limit"); l != "" { if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 { limit = parsed } } messages, err := client.ListMessages(limit) if err != nil { http.Error(w, fmt.Sprintf(`{"error":"%s"}`, err.Error()), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, "messages": messages, "total": len(messages), }) } // GetMailMessage returns a single email func GetMailMessage(w http.ResponseWriter, r *http.Request) { client := getIMAPClient() if client == nil { http.Error(w, `{"error":"IMAP not configured"}`, http.StatusServiceUnavailable) return } uidStr := chi.URLParam(r, "uid") uid, err := strconv.ParseUint(uidStr, 10, 32) if err != nil { http.Error(w, `{"error":"invalid uid"}`, http.StatusBadRequest) return } msg, err := client.GetMessage(uint32(uid)) if err != nil { http.Error(w, fmt.Sprintf(`{"error":"%s"}`, err.Error()), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, "message": msg, }) } // MarkMailAsRead marks an email as read func MarkMailAsRead(w http.ResponseWriter, r *http.Request) { client := getIMAPClient() if client == nil { http.Error(w, `{"error":"IMAP not configured"}`, http.StatusServiceUnavailable) return } uidStr := chi.URLParam(r, "uid") uid, err := strconv.ParseUint(uidStr, 10, 32) if err != nil { http.Error(w, `{"error":"invalid uid"}`, http.StatusBadRequest) return } if err := client.MarkAsRead(uint32(uid)); err != nil { http.Error(w, fmt.Sprintf(`{"error":"%s"}`, err.Error()), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, }) } // GetMailUnreadCount returns unread message count func GetMailUnreadCount(w http.ResponseWriter, r *http.Request) { client := getIMAPClient() if client == nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusServiceUnavailable) json.NewEncoder(w).Encode(map[string]interface{}{ "ok": false, "error": "IMAP not configured. Set IMAP_URL environment variable or configure via /api/v1/mail/config", }) return } count, err := client.GetUnreadCount() if err != nil { http.Error(w, fmt.Sprintf(`{"error":"%s"}`, err.Error()), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, "count": count, }) } // SaveMailConfig saves mail configuration func SaveMailConfig(w http.ResponseWriter, r *http.Request) { var config MailConfig if err := json.NewDecoder(r.Body).Decode(&config); err != nil { http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) return } mailConfigMu.Lock() mailConfig = &config mailConfigMu.Unlock() w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, }) } // TestMailConnection tests IMAP connection func TestMailConnection(w http.ResponseWriter, r *http.Request) { var config MailConfig if err := json.NewDecoder(r.Body).Decode(&config); err != nil { http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) return } client := email.NewIMAPClient(config.Server, config.Port, config.Email, config.Password) count, err := client.GetUnreadCount() if err != nil { http.Error(w, fmt.Sprintf(`{"error":"%s"}`, err.Error()), http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, "messageCount": count, }) }