package main import ( "encoding/json" "fmt" "html" "log" "net/http" "os" "strconv" "strings" "gopkg.in/gomail.v2" ) // Copyable snippets used by /docs and dedicated snippet endpoints const docsHTMLSnippet = `
` const docsJSSnippet = ` // Contact form submission const form = document.getElementById('contact-form'); if (form) { form.addEventListener('submit', async (e) => { e.preventDefault(); const submitButton = form.querySelector('button[type="submit"]'); const originalButtonText = submitButton.innerHTML; try { // Show loading state submitButton.disabled = true; submitButton.innerHTML = 'Odesílám...'; const data = Object.fromEntries(new FormData(form)); const res = await fetch('https://sendmail.tdvorak.dev/send' + window.location.search, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const text = await res.text(); if (res.ok) { // Show success message alert('Děkujeme za vaši zprávu! Brzy se vám ozveme zpět.'); form.reset(); } else { throw new Error(text || 'Něco se pokazilo. Zkuste to prosím znovu.'); } } catch (error) { console.error('Error:', error); alert(error.message || 'Při odesílání zprávy došlo k chybě. Zkuste to prosím znovu později.'); } finally { // Reset button state submitButton.disabled = false; submitButton.innerHTML = originalButtonText; } }); }` type FormData struct { Domain string `json:"domain"` Name string `json:"name"` Email string `json:"email"` Subject string `json:"subject"` Message string `json:"message"` } // rootHandler serves a minimal HTML page with a contact form and JS to submit to /send func rootHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "GET only", http.StatusMethodNotAllowed) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") htmlEsc := html.EscapeString(docsHTMLSnippet) jsEsc := html.EscapeString(docsJSSnippet) page := fmt.Sprintf(` SendMail API – Dokumentace

SendMail API

Server: http://localhost:8080

1) POST /send

Tělo JSON: { domain?, name, email, subject, message }

Volitelné SMTP v query: ?email=&pass=&server=&port=

Příklad cURL (přednastavená doména)

curl -X POST http://localhost:8080/send \
  -H "Content-Type: application/json" \
  -d '{
    "domain":"sportcreative.eu",
    "name":"Jan Novák",
    "email":"jan@example.com",
    "subject":"Dotaz",
    "message":"Dobrý den..."
  }'
      

Příklad cURL (SMTP v URL)

curl -X POST "http://localhost:8080/send?email=USER@example.com&pass=SECRET&server=smtp.example.com&port=465" \
  -H "Content-Type: application/json" \
  -d '{
    "name":"Jan Novák",
    "email":"jan@example.com",
    "subject":"Dotaz",
    "message":"Dobrý den..."
  }'
      

2) GET /send-params

Query: ?email=&pass=&server=&port=&to=&from=&subject=&message= (když chybí to/from, použije se email).

Příklad cURL

curl "http://localhost:8080/send-params?email=USER@example.com&pass=SECRET&server=smtp.example.com&port=465&to=dest@example.com&from=USER@example.com&subject=Hello&message=Ahoj"

HTML formulář (vložit do stránky)

%s

JavaScript (vložit do stránky)

%s
`, htmlEsc, jsEsc) w.Write([]byte(page)) } type EmailConfig struct { Email string Password string SMTPHost string SMTPPort int } var domainMap = map[string]EmailConfig{ "tdvorak.dev": {"info@tdvorak.dev", "%8s3Yad*!b3*t", "smtp.purelymail.com", 465}, "vbly.org": {"info@vbly.org", "!N0^e0Q9k*mV", "smtp.purelymail.com", 465}, "sportcreative.eu": {"info@sportcreative.eu", "@dN$TZPgG5!", "smtp.purelymail.com", 465}, "reklik.net": {"info@reklik.net", "FK8eAwJbSkP2%H", "smtp.purelymail.com", 465}, "zeusport.eu": {"marketing@zeusport.cz", "r-:#sSQ?9D%C4t8", "smtp.forpsi.com", 465}, "bookra.eu": {"info@bookra.eu", "n2$6cwSwqff$J58", "smtp.purelymail.com", 465}, } func sendHandler(w http.ResponseWriter, r *http.Request) { log.Printf("[send] INCOMING %s %s from %s", r.Method, r.URL.Path, r.RemoteAddr) if r.Method != http.MethodPost { http.Error(w, "POST only", http.StatusMethodNotAllowed) return } var data FormData if err := json.NewDecoder(r.Body).Decode(&data); err != nil { log.Printf("[send] Invalid JSON from %s", r.RemoteAddr) http.Error(w, "Invalid JSON", http.StatusBadRequest) return } log.Printf("[send] request from=%s domain=%q subject=%q", data.Email, data.Domain, data.Subject) // Determine SMTP configuration: prefer query params if provided; otherwise use domain map q := r.URL.Query() qEmail := strings.TrimSpace(q.Get("email")) qPass := q.Get("pass") qServer := strings.TrimSpace(q.Get("server")) qPortStr := strings.TrimSpace(q.Get("port")) var config EmailConfig if qEmail != "" || qPass != "" || qServer != "" || qPortStr != "" { if qEmail == "" || qPass == "" || qServer == "" || qPortStr == "" { http.Error(w, "Missing required query params: email, pass, server, port", http.StatusBadRequest) return } port, err := strconv.Atoi(qPortStr) if err != nil || port <= 0 { http.Error(w, "Invalid port", http.StatusBadRequest) return } log.Printf("[send] using query params host=%s port=%d email=%s", qServer, port, qEmail) config = EmailConfig{Email: qEmail, Password: qPass, SMTPHost: qServer, SMTPPort: port} } else { data.Domain = strings.ToLower(strings.TrimSpace(data.Domain)) var ok bool config, ok = domainMap[data.Domain] if !ok { log.Printf("[send] FAILED unknown domain=%q", data.Domain) http.Error(w, "Unknown domain", http.StatusUnauthorized) return } log.Printf("[send] using domain map host=%s port=%d email=%s", config.SMTPHost, config.SMTPPort, config.Email) } m := gomail.NewMessage() m.SetHeader("From", config.Email) m.SetHeader("To", config.Email) // you can change this to forward to another inbox if strings.TrimSpace(data.Email) != "" { m.SetHeader("Reply-To", data.Email) } m.SetHeader("Subject", data.Subject) m.SetBody("text/plain", "Name: "+data.Name+"\nEmail: "+data.Email+"\n\n"+data.Message) d := gomail.NewDialer(config.SMTPHost, config.SMTPPort, config.Email, config.Password) d.SSL = true // Using SSL for secure connection if err := d.DialAndSend(m); err != nil { log.Printf("[send] FAILED to=%s subject=%q via %s:%d error=%v", config.Email, data.Subject, config.SMTPHost, config.SMTPPort, err) http.Error(w, "Failed to send email", http.StatusInternalServerError) return } log.Printf("[send] SUCCESS to=%s subject=%q via %s:%d", config.Email, data.Subject, config.SMTPHost, config.SMTPPort) w.Write([]byte("Email sent successfully")) } func enableCors(handler http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // Allow all origins origin := r.Header.Get("Origin") if origin != "" { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization") } // Handle preflight requests if r.Method == http.MethodOptions { w.WriteHeader(http.StatusOK) return } handler(w, r) } } // sendParamsHandler handles sending email with explicit SMTP parameters via query string. // Example: // // GET /send-params?email=user@example.com&pass=secret&server=smtp.example.com&port=465&to=dest@example.com&from=user@example.com&subject=Hello&message=Hi func sendParamsHandler(w http.ResponseWriter, r *http.Request) { log.Printf("[send-params] INCOMING %s %s from %s", r.Method, r.URL.Path, r.RemoteAddr) if r.Method != http.MethodGet { http.Error(w, "GET only", http.StatusMethodNotAllowed) return } q := r.URL.Query() smtpEmail := strings.TrimSpace(q.Get("email")) smtpPass := q.Get("pass") smtpServer := strings.TrimSpace(q.Get("server")) portStr := strings.TrimSpace(q.Get("port")) to := strings.TrimSpace(q.Get("to")) from := strings.TrimSpace(q.Get("from")) subject := q.Get("subject") message := q.Get("message") if smtpEmail == "" || smtpPass == "" || smtpServer == "" || portStr == "" { http.Error(w, "Missing required params: email, pass, server, port", http.StatusBadRequest) return } port, err := strconv.Atoi(portStr) if err != nil || port <= 0 { http.Error(w, "Invalid port", http.StatusBadRequest) return } if from == "" { from = smtpEmail } if to == "" { to = smtpEmail } m := gomail.NewMessage() m.SetHeader("From", from) m.SetHeader("To", to) if subject == "" { subject = "(no subject)" } m.SetHeader("Subject", subject) m.SetBody("text/plain", message) d := gomail.NewDialer(smtpServer, port, smtpEmail, smtpPass) d.SSL = true if err := d.DialAndSend(m); err != nil { log.Printf("[send-params] FAILED to=%s subject=%q via %s:%d error=%v", to, subject, smtpServer, port, err) http.Error(w, "Failed to send email", http.StatusInternalServerError) return } log.Printf("[send-params] SUCCESS to=%s subject=%q via %s:%d", to, subject, smtpServer, port) w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]string{"status": "ok", "message": "Email sent successfully"}) } // docsHandler serves a minimal JSON API description. func main() { http.HandleFunc("/", enableCors(rootHandler)) http.HandleFunc("/send", enableCors(sendHandler)) http.HandleFunc("/send-params", enableCors(sendParamsHandler)) host := os.Getenv("HOST") if host == "" { host = "localhost" } port := os.Getenv("PORT") if port == "" { port = "660" } addr := host + ":" + port log.Println("Server running on " + addr) log.Fatal(http.ListenAndServe(addr, nil)) }