From 82442fed56f144a7db31f80c870dab2282bdae45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Dvo=C5=99=C3=A1k?= <150935816+Dvorinka@users.noreply.github.com> Date: Tue, 20 May 2025 08:16:48 +0200 Subject: [PATCH] Add files via upload --- go.mod | 2 + go.sum | 4 + index.html | 402 +++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 340 ++++++++++++++++++++++++++++---------------- 4 files changed, 631 insertions(+), 117 deletions(-) create mode 100644 index.html diff --git a/go.mod b/go.mod index 932e4c1..c6d2f62 100644 --- a/go.mod +++ b/go.mod @@ -30,5 +30,7 @@ require ( golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect + gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 0bd8dbf..04e191e 100644 --- a/go.sum +++ b/go.sum @@ -104,7 +104,11 @@ google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFW google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= +gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= +gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/index.html b/index.html new file mode 100644 index 0000000..a2fef9b --- /dev/null +++ b/index.html @@ -0,0 +1,402 @@ + + + + + + Poppe Potthoff - Záznam jízdy služebního vozu + + + + + + + + + + + +
+
+

Záznam jízdy služebního vozu

+

Systém pro evidenci služebních jízd společnosti Poppe + Potthoff

+
+
+ + +
+
+ +
+ +

Nový záznam jízdy

+
+ + +
+
+
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+ + +

Powered by Mapy.cz

+
+ +
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+ +
+ km +
+
+
+ +
+ +
+
+ +
+ +
+ km +
+
+
+
+ +
+
+
+ Celkem ujetá vzdálenost: + 0 km +
+
+ +
+ +
+ + + + +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/main.go b/main.go index 8502f5b..f14c1fe 100644 --- a/main.go +++ b/main.go @@ -1,117 +1,223 @@ -package main - -import ( - "fmt" - "log" - "net/http" - "net/smtp" - - "github.com/gin-contrib/cors" - "github.com/gin-gonic/gin" -) - -type TripEntry struct { - Name string `json:"name" binding:"required"` - Destination string `json:"destination" binding:"required"` - Date string `json:"date" binding:"required"` - Purpose string `json:"purpose" binding:"required"` - KmStart int `json:"km_start" binding:"required"` - KmEnd int `json:"km_end" binding:"required"` -} - -func main() { - r := gin.Default() - - // Enable CORS for all origins - r.Use(cors.Default()) - - r.POST("/submit", func(c *gin.Context) { - var entry TripEntry - if err := c.ShouldBindJSON(&entry); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - // Send email with trip details - err := sendEmail(entry) - if err != nil { - log.Println("Failed to send email:", err) - c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to send email"}) - return - } - - c.JSON(http.StatusOK, gin.H{"message": "Entry submitted and email sent successfully"}) - }) - - r.Run(":8080") -} - -func sendEmail(entry TripEntry) error { - smtpHost := "smtp.gmail.com" - smtpPort := "465" - sender := "contact.dvorak@gmail.com" - password := "pnhkcsahbwsbpyqj" - recipient := "contact.dvorak@gmail.com" - - auth := smtp.PlainAuth("", sender, password, smtpHost) - - subject := "Nový záznam o jízdě služebním autem" - - body := fmt.Sprintf(` - - - - - -
-

Záznam o jízdě služebním autem

-

Řidič: %s

-

Kam: %s

-

Datum: %s

-

Účel jízdy: %s

-

Kilometry na začátku: %d km

-

Kilometry na konci: %d km

-

Ujeté kilometry: %d km

-
- - - `, entry.Name, entry.Destination, entry.Date, entry.Purpose, entry.KmStart, entry.KmEnd, entry.KmEnd-entry.KmStart) - - msg := []byte( - "MIME-Version: 1.0\r\n" + - "Content-Type: text/html; charset=\"UTF-8\"\r\n" + - "To: " + recipient + "\r\n" + - "Subject: " + subject + "\r\n" + - "\r\n" + body + "\r\n", - ) - - return smtp.SendMail(smtpHost+":"+smtpPort, auth, sender, []string{recipient}, msg) -} +package main + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "strings" + "time" + + "gopkg.in/gomail.v2" +) + +type TripEntry struct { + Name string `json:"name"` + Destination string `json:"destination"` + Date string `json:"date"` + Purpose string `json:"purpose"` + KmStart int `json:"km_start"` + KmEnd int `json:"km_end"` + Coordinates *GeoCoords `json:"coordinates,omitempty"` +} + +type GeoCoords struct { + Lat string `json:"lat"` + Lng string `json:"lng"` +} + +func main() { + log.SetFlags(log.LstdFlags | log.Lshortfile) + + http.HandleFunc("/submit", enableCORS(handleSubmit)) + http.HandleFunc("/health", enableCORS(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"status":"ok"}`)) + })) + + http.HandleFunc("/", enableCORS(func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "index.html") + })) + + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + + log.Printf("Server běží na portu %s", port) + err := http.ListenAndServe(":"+port, nil) + if err != nil { + log.Fatalf("Chyba při spuštění serveru: %v", err) + } +} + +func enableCORS(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") + w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") + + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusOK) + return + } + + if next != nil { + next(w, r) + } + } +} + +func handleSubmit(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + if r.Method != http.MethodPost { + if r.Method == http.MethodOptions { + w.WriteHeader(http.StatusOK) + return + } + w.WriteHeader(http.StatusMethodNotAllowed) + w.Write([]byte(`{"error":"Only POST method is allowed"}`)) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + log.Printf("Chyba při čtení těla požadavku: %v", err) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(`{"error":"Failed to read request body"}`)) + return + } + defer r.Body.Close() + + log.Printf("Přijatá data: %s", string(body)) + + var entry TripEntry + err = json.Unmarshal(body, &entry) + if err != nil { + log.Printf("Chyba při parsování JSON: %v", err) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(fmt.Sprintf(`{"error":"Failed to parse JSON: %v"}`, err))) + return + } + + if entry.Name == "" || entry.Destination == "" || entry.Date == "" || entry.Purpose == "" { + log.Printf("Chybějící povinná pole: %+v", entry) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(`{"error":"Missing required fields"}`)) + return + } + + if entry.KmEnd < entry.KmStart { + log.Printf("Neplatný stav tachometru: %d -> %d", entry.KmStart, entry.KmEnd) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(`{"error":"End kilometers must be greater than or equal to start kilometers"}`)) + return + } + + // Formátování data do českého formátu + parsedDate, err := time.Parse("2006-01-02", entry.Date) + if err == nil { + czechMonths := []string{ + "ledna", "února", "března", "dubna", "května", "června", + "července", "srpna", "září", "října", "listopadu", "prosince", + } + monthName := czechMonths[parsedDate.Month()-1] + entry.Date = fmt.Sprintf("%d. %s %d", parsedDate.Day(), monthName, parsedDate.Year()) + } else { + log.Printf("Chyba při parsování data: %v", err) + } + + err = sendEmail(entry) + if err != nil { + log.Printf("Chyba při odesílání emailu: %v", err) + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf(`{"error":"Failed to send email: %v"}`, err))) + return + } + + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"message":"Záznam byl úspěšně uložen a email odeslán"}`)) +} + +func sendEmail(entry TripEntry) error { + smtpHost := "mail.pp-kunovice.cz" + smtpPort := 465 + sender := "sluzebnicek@pp-kunovice.cz" + password := "7g}qznB5bj" + recipient := "sluzebnicek@pp-kunovice.cz" + + m := gomail.NewMessage() + m.SetHeader("From", sender) + m.SetHeader("To", recipient) + m.SetHeader("Subject", "Nový záznam o jízdě služebním autem") + + var htmlContent strings.Builder + + htmlContent.WriteString(` + + + + + +
+

Záznam o jízdě služebním autem

+ `) + + fmt.Fprintf(&htmlContent, `

Řidič: %s

`, entry.Name) + fmt.Fprintf(&htmlContent, `

Kam: %s

`, entry.Destination) + fmt.Fprintf(&htmlContent, `

Datum: %s

`, entry.Date) + fmt.Fprintf(&htmlContent, `

Účel jízdy: %s

`, entry.Purpose) + fmt.Fprintf(&htmlContent, `

Kilometry na začátku: %d km

`, entry.KmStart) + fmt.Fprintf(&htmlContent, `

Kilometry na konci: %d km

`, entry.KmEnd) + fmt.Fprintf(&htmlContent, `

Ujeté kilometry: %d km

`, entry.KmEnd-entry.KmStart) + + if entry.Coordinates != nil { + fmt.Fprintf(&htmlContent, `

GPS souřadnice: %s, %s

`, entry.Coordinates.Lat, entry.Coordinates.Lng) + fmt.Fprintf(&htmlContent, `

Zobrazit na mapě

`, entry.Coordinates.Lng, entry.Coordinates.Lat) + } + + htmlContent.WriteString(` +
+ + + `) + + m.SetBody("text/html", htmlContent.String()) + + d := gomail.NewDialer(smtpHost, smtpPort, sender, password) + d.TLSConfig = &tls.Config{InsecureSkipVerify: true} + + return d.DialAndSend(m) +}