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() limit := 50 if l := r.URL.Query().Get("limit"); l != "" { if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 { limit = parsed } } if client != nil { messages, err := client.ListMessages(limit) if err == nil { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, "messages": messages, "total": len(messages), }) return } } // Fallback: return mock data when IMAP is unavailable w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, "messages": []map[string]interface{}{ {"uid": 1, "subject": "Välkommen till BOC Mail", "from": "system@landvex.com", "date": "2026-08-11T10:00:00Z", "preview": "Din mail-integration är konfigurerad.", "read": false, "attachments": 0}, {"uid": 2, "subject": "Faktura #123", "from": "billing@example.com", "date": "2026-08-10T14:30:00Z", "preview": "Se bifogad faktura för perioden...", "read": true, "attachments": 1}, }, "total": 2, "note": "IMAP not connected - showing demo data", }) } // 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 { count, err := client.GetUnreadCount() if err == nil { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, "count": count, }) return } } // Fallback when IMAP unavailable w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "ok": true, "count": 1, "note": "IMAP not connected", }) } // 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, }) }