diff --git a/main.go b/main.go index 20d6631..365c5eb 100644 --- a/main.go +++ b/main.go @@ -1,359 +1,360 @@ -package main - -import ( - "encoding/json" - "fmt" - "html" - "log" - "net/http" - "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}, -} - -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 /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) { - 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("/", enableCors(rootHandler)) - http.HandleFunc("/send", enableCors(sendHandler)) - http.HandleFunc("/send-params", enableCors(sendParamsHandler)) - log.Println("Server running on :660") - log.Fatal(http.ListenAndServe(":660", nil)) -} +package main + +import ( + "encoding/json" + "fmt" + "html" + "log" + "net/http" + "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) { + 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 /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) { + 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("/", enableCors(rootHandler)) + http.HandleFunc("/send", enableCors(sendHandler)) + http.HandleFunc("/send-params", enableCors(sendParamsHandler)) + log.Println("Server running on :660") + log.Fatal(http.ListenAndServe("0.0.0.0:660", nil)) +}