mirror of
https://github.com/Dvorinka/SendMail.git
synced 2026-07-29 10:03:48 +00:00
refactor(main): remove embedded HTML and JS snippets from main.go
This commit is contained in:
@@ -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 = `
|
||||
<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("/", 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 = `
|
||||
<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},
|
||||
"bookra.eu": {"[email protected]", "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 /[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("/", 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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user