Files
SendMail/main.go
T
2025-09-10 14:08:19 +02:00

360 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"encoding/json"
"fmt"
"html"
"log"
"net/http"
"strings"
"strconv"
"gopkg.in/gomail.v2"
)
// Copyable snippets used by /docs and dedicated snippet endpoints
const docsHTMLSnippet = `
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="md:flex">
<!-- Contact Form -->
<div class="w-full md:w-1/2 p-8">
<form id="contact-form" class="space-y-6">
<input type="hidden" name="domain" value="sportcreative.eu" />
<div>
<label for="name" class="block text-sm font-medium text-gray-700 mb-1">Jméno a příjmení *</label>
<input type="text" id="name" name="name" required
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-sport-purple focus:border-transparent transition duration-200"
placeholder="Vaše celé jméno">
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 mb-1">E-mail *</label>
<input type="email" id="email" name="email" required
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-sport-purple focus:border-transparent transition duration-200"
placeholder="[email protected]">
</div>
<div>
<label for="subject" class="block text-sm font-medium text-gray-700 mb-1">Předmět *</label>
<input type="text" id="subject" name="subject" required
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-sport-purple focus:border-transparent transition duration-200"
placeholder="O co se zajímáte?">
</div>
<div>
<label for="message" class="block text-sm font-medium text-gray-700 mb-1">Zpráva *</label>
<textarea id="message" name="message" rows="5" required
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-sport-purple focus:border-transparent transition duration-200"
placeholder="Jak vám můžeme pomoci?"></textarea>
</div>
<div class="pt-2">
<button type="submit"
class="w-full bg-sport-purple text-white font-medium py-3 px-6 rounded-full hover:shadow-lg transition duration-300 hover:bg-sport-orange">
Odeslat zprávu
<ion-icon name="arrow-forward-outline" class="ml-2"></ion-icon>
</button>
</div>
</form>
</div>
</div>
</div>`
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(`<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>SendMail API Dokumentace</title>
<style>
body { font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; background: #0b1020; color: #e2e8f0; margin:0; }
.container { max-width: 980px; margin: 0 auto; padding: 2rem; }
h1 { margin: 0 0 .5rem; font-size: 2rem; }
.muted { color:#94a3b8; }
.card { background: #0f172a; border: 1px solid rgba(148,163,184,.18); border-radius: 12px; padding: 1rem 1.25rem; margin-bottom: 1rem; }
code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
pre { background: #0b1220; border:1px solid rgba(148,163,184,.18); border-radius:10px; padding:.75rem; overflow:auto; }
</style>
</head>
<body>
<div class="container">
<h1>SendMail API</h1>
<p class="muted">Server: <code>http://localhost:8080</code></p>
<div class="card">
<h2>1) POST /send</h2>
<p>Tělo JSON: <code>{ domain?, name, email, subject, message }</code></p>
<p>Volitelné SMTP v query: <code>?email=&pass=&server=&port=</code></p>
<h3>Příklad cURL (přednastavená doména)</h3>
<pre><code>curl -X POST http://localhost:8080/send \
-H "Content-Type: application/json" \
-d '{
"domain":"sportcreative.eu",
"name":"Jan Novák",
"email":"[email protected]",
"subject":"Dotaz",
"message":"Dobrý den..."
}'
</code></pre>
<h3>Příklad cURL (SMTP v URL)</h3>
<pre><code>curl -X POST "http://localhost:8080/[email protected]&pass=SECRET&server=smtp.example.com&port=465" \
-H "Content-Type: application/json" \
-d '{
"name":"Jan Novák",
"email":"[email protected]",
"subject":"Dotaz",
"message":"Dobrý den..."
}'
</code></pre>
</div>
<div class="card">
<h2>2) GET /send-params</h2>
<p>Query: <code>?email=&pass=&server=&port=&to=&from=&subject=&message=</code> (když chybí <code>to</code>/<code>from</code>, použije se <code>email</code>).</p>
<h3>Příklad cURL</h3>
<pre><code>curl "http://localhost:8080/[email protected]&pass=SECRET&server=smtp.example.com&port=465&[email protected]&[email protected]&subject=Hello&message=Ahoj"</code></pre>
</div>
<div class="card">
<h2>HTML formulář (vložit do stránky)</h2>
<pre><code>%s</code></pre>
</div>
<div class="card">
<h2>JavaScript (vložit do stránky)</h2>
<pre><code>%s</code></pre>
</div>
</div>
</body>
</html>`, htmlEsc, jsEsc)
w.Write([]byte(page))
}
type EmailConfig struct {
Email string
Password string
SMTPHost string
SMTPPort int
}
var domainMap = map[string]EmailConfig{
"tdvorak.dev": {"[email protected]", "%8s3Yad*!b3*t", "smtp.purelymail.com", 465},
"vbly.org": {"[email protected]", "!N0^e0Q9k*mV", "smtp.purelymail.com", 465},
"sportcreative.eu": {"[email protected]", "@dN$TZPgG5!", "smtp.purelymail.com", 465},
"reklik.net": {"[email protected]", "FK8eAwJbSkP2%H", "smtp.purelymail.com", 465},
"zeusport.eu": {"[email protected]", "r-:#sSQ?9D%C4t8", "smtp.forpsi.com", 465},
}
func sendHandler(w http.ResponseWriter, r *http.Request) {
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 {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// 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
}
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 {
http.Error(w, "Unknown domain", http.StatusUnauthorized)
return
}
}
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.Println("Email send error:", err)
http.Error(w, "Failed to send email", http.StatusInternalServerError)
return
}
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 /[email protected]&pass=secret&server=smtp.example.com&port=465&[email protected]&[email protected]&subject=Hello&message=Hi
func sendParamsHandler(w http.ResponseWriter, r *http.Request) {
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.Println("Email send error:", err)
http.Error(w, "Failed to send email", http.StatusInternalServerError)
return
}
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("/send", enableCors(sendHandler))
http.HandleFunc("/send-params", enableCors(sendParamsHandler))
http.HandleFunc("/", enableCors(rootHandler))
log.Println("Server running on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}