mailkarow / services /mailservice.go
sarveshpatel's picture
Create services/mailservice.go
7c96971 verified
package services
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"math/rand"
"net/http"
"tempmail-service/config"
"tempmail-service/database"
"tempmail-service/models"
"time"
"github.com/google/uuid"
)
type MailService struct {
baseURL string
httpClient *http.Client
}
func NewMailService() *MailService {
cfg := config.Load()
return &MailService{
baseURL: cfg.APIBaseURL,
httpClient: &http.Client{
Timeout: 30 * time.Second,
},
}
}
// GetDomains fetches available domains
func (s *MailService) GetDomains() ([]models.Domain, error) {
resp, err := s.httpClient.Get(s.baseURL + "/domains")
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to fetch domains: status %d", resp.StatusCode)
}
var domainResp models.DomainResponse
if err := json.NewDecoder(resp.Body).Decode(&domainResp); err != nil {
return nil, err
}
// Filter active domains
var activeDomains []models.Domain
for _, d := range domainResp.HydraMember {
if d.IsActive && !d.IsPrivate {
activeDomains = append(activeDomains, d)
}
}
return activeDomains, nil
}
// CreateAccount creates a new email account
func (s *MailService) CreateAccount(address, password string) (*models.AccountResponse, error) {
reqBody := models.AccountRequest{
Address: address,
Password: password,
}
jsonBody, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", s.baseURL+"/accounts", bytes.NewBuffer(jsonBody))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("failed to create account: status %d, body: %s", resp.StatusCode, string(body))
}
var account models.AccountResponse
if err := json.NewDecoder(resp.Body).Decode(&account); err != nil {
return nil, err
}
return &account, nil
}
// GetToken gets authentication token
func (s *MailService) GetToken(address, password string) (*models.TokenResponse, error) {
reqBody := models.TokenRequest{
Address: address,
Password: password,
}
jsonBody, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", s.baseURL+"/token", bytes.NewBuffer(jsonBody))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("failed to get token: status %d, body: %s", resp.StatusCode, string(body))
}
var token models.TokenResponse
if err := json.NewDecoder(resp.Body).Decode(&token); err != nil {
return nil, err
}
return &token, nil
}
// GetMessages fetches all messages for an account
func (s *MailService) GetMessages(token string, page int) (*models.MessagesResponse, error) {
url := fmt.Sprintf("%s/messages?page=%d", s.baseURL, page)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusUnauthorized {
return nil, errors.New("unauthorized: invalid token")
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to fetch messages: status %d", resp.StatusCode)
}
var messages models.MessagesResponse
if err := json.NewDecoder(resp.Body).Decode(&messages); err != nil {
return nil, err
}
return &messages, nil
}
// GetMessage fetches a specific message
func (s *MailService) GetMessage(token, messageID string) (*models.MessageDetail, error) {
req, err := http.NewRequest("GET", s.baseURL+"/messages/"+messageID, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusUnauthorized {
return nil, errors.New("unauthorized: invalid token")
}
if resp.StatusCode == http.StatusNotFound {
return nil, errors.New("message not found")
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to fetch message: status %d", resp.StatusCode)
}
var message models.MessageDetail
if err := json.NewDecoder(resp.Body).Decode(&message); err != nil {
return nil, err
}
return &message, nil
}
// DeleteMessage deletes a specific message
func (s *MailService) DeleteMessage(token, messageID string) error {
req, err := http.NewRequest("DELETE", s.baseURL+"/messages/"+messageID, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to delete message: status %d", resp.StatusCode)
}
return nil
}
// MarkAsRead marks a message as read
func (s *MailService) MarkAsRead(token, messageID string) error {
req, err := http.NewRequest("PATCH", s.baseURL+"/messages/"+messageID, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to mark as read: status %d", resp.StatusCode)
}
return nil
}
// DeleteAccount deletes an account
func (s *MailService) DeleteAccount(token, accountID string) error {
req, err := http.NewRequest("DELETE", s.baseURL+"/accounts/"+accountID, nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to delete account: status %d", resp.StatusCode)
}
return nil
}
// GetAccountInfo gets account information
func (s *MailService) GetAccountInfo(token string) (*models.AccountResponse, error) {
req, err := http.NewRequest("GET", s.baseURL+"/me", nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get account info: status %d", resp.StatusCode)
}
var account models.AccountResponse
if err := json.NewDecoder(resp.Body).Decode(&account); err != nil {
return nil, err
}
return &account, nil
}
// Session Management
// CreateSession creates a new email session
func (s *MailService) CreateSession(username, domain string) (*models.Session, error) {
// Generate password
password := generatePassword(16)
address := username + "@" + domain
// Create account
account, err := s.CreateAccount(address, password)
if err != nil {
return nil, fmt.Errorf("failed to create account: %w", err)
}
// Get token
token, err := s.GetToken(address, password)
if err != nil {
return nil, fmt.Errorf("failed to get token: %w", err)
}
// Create session
session := &models.Session{
SessionID: uuid.New().String(),
AccountID: account.ID,
Email: address,
Password: password,
Token: token.Token,
ExpiresAt: time.Now().Add(24 * time.Hour),
}
// Save to database
if err := database.DB.Create(session).Error; err != nil {
return nil, fmt.Errorf("failed to save session: %w", err)
}
return session, nil
}
// GetSession retrieves a session by ID
func (s *MailService) GetSession(sessionID string) (*models.Session, error) {
var session models.Session
if err := database.DB.Where("session_id = ?", sessionID).First(&session).Error; err != nil {
return nil, errors.New("session not found")
}
if time.Now().After(session.ExpiresAt) {
return nil, errors.New("session expired")
}
return &session, nil
}
// DeleteSession deletes a session and its account
func (s *MailService) DeleteSession(sessionID string) error {
session, err := s.GetSession(sessionID)
if err != nil {
return err
}
// Delete from API
_ = s.DeleteAccount(session.Token, session.AccountID)
// Delete from database
database.DB.Where("session_id = ?", sessionID).Delete(&models.CachedMessage{})
database.DB.Where("session_id = ?", sessionID).Delete(&models.Session{})
return nil
}
// RefreshToken refreshes the session token
func (s *MailService) RefreshToken(session *models.Session) error {
token, err := s.GetToken(session.Email, session.Password)
if err != nil {
return err
}
session.Token = token.Token
return database.DB.Save(session).Error
}
// Helper function to generate random password
func generatePassword(length int) string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%"
seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
b := make([]byte, length)
for i := range b {
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}
// GenerateUsername generates a random username
func GenerateUsername() string {
const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
length := 8 + seededRand.Intn(5) // 8-12 characters
b := make([]byte, length)
for i := range b {
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}