Initial commit

This commit is contained in:
Adam Štrauch 2024-01-05 15:49:46 +01:00
commit 73fd1ca90e
Signed by: cx
GPG Key ID: 7262DAFE292BCE20
29 changed files with 2102 additions and 0 deletions

29
Makefile Normal file
View File

@ -0,0 +1,29 @@
APP_SSH_PORT=25658
.PHONY: all
all: watch
.PHONY: install
install:
npm install -D @tailwindcss/forms
.PHONY: watch
watch:
tailwindcss -i ./src/input.css -o ./dist/output.css --watch
.PHONY: static
static:
tailwindcss ./src/input.css -o ./dist/output.css
.PHONY: deploy
deploy: build static
rsync -av --delete -e "ssh -p ${APP_SSH_PORT}" ./src app@ssh.rosti.cz:/srv/app/
rsync -av --delete -e "ssh -p ${APP_SSH_PORT}" ./dist app@ssh.rosti.cz:/srv/app/
rsync -av --delete -e "ssh -p ${APP_SSH_PORT}" ./autosklo app@ssh.rosti.cz:/srv/app/autosklo
rsync -av --delete -e "ssh -p ${APP_SSH_PORT}" ./.env app@ssh.rosti.cz:/srv/app/.env
ssh -p ${APP_SSH_PORT} app@ssh.rosti.cz supervisorctl restart app
.PHONY: build
build:
CGO_ENABLED=0 go build -o autosklo *.go

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# Autosklo homepage
Homepage for noveautosklo.cz.
## Quickstart guide
```
npm install
make static
make build
./autosklo
```
Then go to http://localhost:1323
To track usage of tailwindcss classes and autogenerate CSS run:
```
make watch
```

45
config.go Normal file
View File

@ -0,0 +1,45 @@
package main
import (
"log"
"github.com/joho/godotenv"
"github.com/kelseyhightower/envconfig"
)
const configPath = ".env"
type Config struct {
CouchDBUsername string `envconfig:"COUCHDB_USERNAME" required:"true"`
CouchDBPassword string `envconfig:"COUCHDB_PASSWORD" required:"true"`
CouchDBProto string `envconfig:"COUCHDB_PROTO" required:"true" default:"https"`
CouchDBPort string `envconfig:"COUCHDB_PORT" required:"true" default:""`
CouchDBHost string `envconfig:"COUCHDB_HOST" required:"true"`
CouchDBName string `envconfig:"COUCHDB_DBNAME" default:"autosklo"`
SMTPHostname string `envconfig:"SMTP_HOSTNAME" default:"mailer.rosti.cz"`
SMTPUsername string `envconfig:"SMTP_USERNAME" default:"web@nove-autosklo.cz"`
SMTPPassword string `envconfig:"SMTP_PASSWORD" require:"true"`
SMTPPort int `envconfig:"SMTP_PORT" default:"587"`
EmailTo string `envconfig:"EMAIL_TO" default:"web@nove-autosklo.cz"`
EmailFrom string `envconfig:"EMAIL_FROM" default:"no-reply@nove-autosklo.cz"`
EmailSubject string `envconfig:"EMAIL_SUBJECT" default:"[WEB] Zpráva z rezervačního formuláře"`
}
func getConfig() Config {
var config Config
if len(configPath) != 0 {
err := godotenv.Load(configPath)
if err != nil {
log.Fatalln(err)
}
}
err := envconfig.Process("", &config)
if err != nil {
log.Fatalln(err)
}
return config
}

15
db.go Normal file
View File

@ -0,0 +1,15 @@
package main
import (
"fmt"
_ "github.com/go-kivik/couchdb/v3"
kivik "github.com/go-kivik/kivik/v3"
)
func getDBClient() (*kivik.Client, error) {
config := getConfig()
client, err := kivik.New("couch", fmt.Sprintf("%s://%s:%s@%s", config.CouchDBProto, config.CouchDBUsername, config.CouchDBPassword, config.CouchDBHost))
return client, err
}

27
email.go Normal file
View File

@ -0,0 +1,27 @@
package main
import (
"fmt"
"net/smtp"
)
func sendEmail(hostname, username, password, to, from, subject, message string, port int) error {
// Set up authentication information.
auth := smtp.PlainAuth("", username, password, hostname)
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.
err := smtp.SendMail(
fmt.Sprintf("%s:%d", hostname, port),
auth,
from,
[]string{to},
[]byte(fmt.Sprintf("Content-Type: text/plain; charset=UTF-8\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s\r\n", from, to, subject, message)),
)
if err != nil {
return err
}
return nil
}

28
go.mod Normal file
View File

@ -0,0 +1,28 @@
module gitea.ceperka.net/cx/autosklo
go 1.18
require (
github.com/go-kivik/couchdb/v3 v3.3.0
github.com/go-kivik/kivik/v3 v3.2.0
github.com/joho/godotenv v1.4.0
github.com/kelseyhightower/envconfig v1.4.0
github.com/labstack/echo/v4 v4.7.2
github.com/satori/go.uuid v1.2.0
)
require (
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/labstack/gommon v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
)

91
go.sum Normal file
View File

@ -0,0 +1,91 @@
bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/flimzy/diff v0.1.5/go.mod h1:lFJtC7SPsK0EroDmGTSrdtWKAxOk3rO+q+e04LL05Hs=
github.com/flimzy/testy v0.1.17-0.20190521133342-95b386c3ece6/go.mod h1:3szguN8NXqgq9bt9Gu8TQVj698PJWmyx/VY1frwwKrM=
github.com/go-kivik/couchdb/v3 v3.3.0 h1:xipDIw66Sqei550OzSXWlLmJ9y01fMbBLkX0F/snjnw=
github.com/go-kivik/couchdb/v3 v3.3.0/go.mod h1:idTUN/G5HGE+YP2EgW+dvZ5ODkdhvddcU7ZzFZfwN6Q=
github.com/go-kivik/kivik/v3 v3.0.1/go.mod h1:7tmQDvkta/pcijpUjLMsQ9HJUELiKD5zm6jQ3Gb9cxE=
github.com/go-kivik/kivik/v3 v3.2.0 h1:YQ6j89DrQwNdMdpWioObEP+pYTSAtF58HomS5ROfKc0=
github.com/go-kivik/kivik/v3 v3.2.0/go.mod h1:chqVuHKAU9j2C7qL0cAH2FCO26oL+0B4aIBeCRMnLa8=
github.com/go-kivik/kiviktest/v3 v3.0.4 h1:mHX/9gpz5VdSOOOs7sN0/6iLK6jQcLiYbteEd33cKNo=
github.com/go-kivik/kiviktest/v3 v3.0.4/go.mod h1:sqsz3M2sJxTxAUdOj+2SU21y4phcpYc0FJIn+hbf1D0=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/labstack/echo/v4 v4.7.2 h1:Kv2/p8OaQ+M6Ex4eGimg9b9e6icoxA42JSlOR3msKtI=
github.com/labstack/echo/v4 v4.7.2/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/otiai10/copy v1.0.2/go.mod h1:c7RpqBkwMom4bYTSkLSym4VSJz/XtncWRAj/J4PEIMY=
github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE=
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
gitlab.com/flimzy/testy v0.0.3/go.mod h1:YObF4cq711ubd/3U0ydRQQVz7Cnq/ChgJpVwNr/AJac=
gitlab.com/flimzy/testy v0.9.1 h1:c255KNz+9lqkMAmC4TYngLP1mOkBJ30KziNgz9r7apk=
gitlab.com/flimzy/testy v0.9.1/go.mod h1:4Yy2aelUY6IgRmd3jHgGWewZLNMQZWQTlwyuMW642Mc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

111
main.go Normal file
View File

@ -0,0 +1,111 @@
package main
import (
"context"
"encoding/json"
"fmt"
"html/template"
"io"
"log"
"net/http"
"strings"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
uuid "github.com/satori/go.uuid"
)
type Template struct {
templates *template.Template
}
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
func main() {
e := echo.New()
e.Use(middleware.Logger())
t := &Template{
templates: template.Must(template.ParseGlob("src/templates/*.html")),
}
e.Renderer = t
e.Static("/static", "src/static")
e.Static("/dist", "dist/")
e.GET("/", HomeHandler)
e.POST("/", HomeHandler)
e.Logger.Fatal(e.Start(":1323"))
}
func HomeHandler(c echo.Context) error {
config := getConfig()
if c.Request().Method == "POST" {
message := Message{
Name: c.FormValue("name"),
Phone: c.FormValue("phone"),
Email: c.FormValue("email"),
Service: c.FormValue("service"),
Note: c.FormValue("note"),
VIN: c.FormValue("vin"),
}
// Validate
message.Validate()
if message.Errors > 0 {
return c.Render(http.StatusOK, "index.html", Response{Error: "Opravte prosím chyby ve formuláři", Message: message})
}
// Store
client, err := getDBClient()
if err != nil {
return c.Render(http.StatusOK, "index.html", Response{Error: err.Error(), Message: message})
}
db := client.DB(context.TODO(), config.CouchDBName)
if err != nil {
return c.Render(http.StatusOK, "index.html", Response{Error: err.Error(), Message: message})
}
body, err := json.Marshal(message)
if err != nil {
return c.Render(http.StatusOK, "index.html", Response{Error: err.Error(), Message: message})
}
messageUUID := uuid.NewV4()
_, err = db.Put(context.TODO(), strings.Replace(messageUUID.String(), "-", "", -1), body)
if err != nil {
return c.Render(http.StatusOK, "index.html", Response{Error: err.Error(), Message: message})
}
go func() {
msg := "Zpráva z rezervačního formuláře\n\n"
msg += fmt.Sprintf(" Jméno: %s\n", message.Name)
msg += fmt.Sprintf(" Telefon: %s\n", message.Phone)
if message.Email != "" {
msg += fmt.Sprintf(" Email: %s\n", message.Email)
}
if message.Email != "" {
msg += fmt.Sprintf(" Služba: %s\n", message.Service)
}
if message.Email != "" {
msg += fmt.Sprintf(" VIN: %s\n", message.VIN)
}
if message.Email != "" {
msg += fmt.Sprintf("\nPoznámka:\n\n%s\n", message.Note)
}
err := sendEmail(config.SMTPHostname, config.SMTPUsername, config.SMTPPassword, config.EmailTo, config.EmailFrom, config.EmailSubject, msg, config.SMTPPort)
if err != nil {
log.Println("SMTP ERROR:", err)
}
}()
return c.Redirect(http.StatusFound, "/?message=Zpráva odeslána")
}
return c.Render(http.StatusOK, "index.html", Response{StatusMessage: c.QueryParam("message")})
}

25
models.go Normal file
View File

@ -0,0 +1,25 @@
package main
type Message struct {
Name string `json:"name"`
NameError string `json:"name_error"`
Phone string `json:"phone"`
PhoneError string `json:"phone_error"`
Email string `json:"email"`
Service string `json:"service"`
Note string `json:"note"`
VIN string `json:"vin"`
Errors int `json:"errors"`
}
func (m *Message) Validate() {
m.Errors = 0
if m.Name == "" {
m.NameError = "Jméno je povinné"
m.Errors += 1
}
if m.Phone == "" {
m.PhoneError = "Telefonní číslo je povinné"
m.Errors += 1
}
}

1418
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

5
package.json Normal file
View File

@ -0,0 +1,5 @@
{
"devDependencies": {
"@tailwindcss/forms": "^0.5.2"
}
}

3
src/input.css Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
src/static/kalibrace.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
src/static/kolin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
src/static/kolin_close.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

BIN
src/static/kolin_mini.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 407 KiB

BIN
src/static/pardubice.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
src/static/xoJB0C.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

244
src/templates/index.html Normal file
View File

@ -0,0 +1,244 @@
<!doctype html>
<html>
<head>
<title>Nové-Autosklo.cz</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="/dist/output.css" rel="stylesheet" />
<link rel="icon" href="">
</head>
<body>
<div class="h-16 w-full bg-slate-500">
<div class="max-w-4xl mx-auto h-full flex flex-col justify-center">
<a href="/"><h1 class="font-bold text-3xl md:text-4xl text-white ml-8">Nové-Autosklo.cz</h1></a>
</div>
</div>
<div class="container max-w-4xl mx-auto px-8">
<!-- END: Header -->
<div class="flex flex-col md:flex-row pt-16">
<div class="flex-1 w-full md:w-1/2 border rounded-lg mx-auto mb-4 md:mb-0 md:mr-8 min-h-800">
<img src="/static/kalibrace_mini.jpg" alt="Výměna či oprava autoskla">
<h2 class="font-bold text-lg pb-4 px-12 pt-4">Výměna či oprava autoskel</h2>
<p class="pb-12 px-12">
Poškozené sklo se snažíme primárně opravit. V případě většího poškození sklo vyměníme a zkalibrujeme senzory,
které jsou na něm umístěné. Opravu i výměnu provádíme buď v naší garáží nebo na domluveném místě, například
u vás doma nebo v práci.
</p>
</div>
<div class="flex-1 w-full md:w-1/2 border rounded-lg mx-auto md:ml-8 min-h-800">
<img src="/static/dekarbonizace_mini.jpg" alt="Dekarbonizace motoru">
<h2 class="font-bold text-lg pb-4 px-12 pt-4">Dekarbonizace motoru</h2>
<p class="pb-12 px-12">Během procesu dekarbonizace se do směsy paliva se vzduchem přidává vodík, čímž dochází
ke spalování při větší teplotě a vypálí se nečistoty, které se v motoru za dobu jeho
provozu usadily. Dekarbonizace trvá přibližně hodinu a může zlepšit odezvu motoru,
zvýšit jeho kroutící moment a výkon, zlepšit emise a prodloužit životnost.</p>
</div>
</div>
<!-- END: About -->
<div>
<h2 class="font-bold text-4xl pb-8 pt-16">Kontakt</h2>
<div>
<p class="pt-8 pb-4 text-center font-bold text-3xl md:text-4xl">
<a href="tel:+420736640805">+420 736 640 805</a>
</p>
<p class="pb-8 pt-4 text-center font-bold text-2xl md:text-4xl">
<a class="text-gray-400 hover:text-gray-600 hover:underline"
href="mailto:info@nove-autosklo.cz">info@nove-autosklo.cz</a>
</p>
<p class="pb-8 text-center text-lg">
<strong>IČ:</strong> 123475
<strong>DIČ:</strong> CZ123475
</p>
</div>
<div class="flex flex-col md:flex-row pt-8">
<div class="flex-1 w-full md:w-1/2 border rounded-lg mx-auto mb-4 md:mb-0 md:mr-8 min-h-800">
<img src="/static/pardubice_mini.jpg" alt="Pardubice">
<h2 class="font-bold text-lg px-12 pt-8 pb-4">Pardubice</h2>
<p class="pb-4 px-12">
Železničního pluku 673<br>
530 02 Pradubice<br>
<br>
2QHF+24
</p>
<iframe class="pt-8"
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d1281.5668937477412!2d15.773476638710402!3d50.02758886237058!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x470dcc9a7874c32d%3A0x3e85d448a50bd3fc!2zxb1lbGV6bmnEjW7DrWhvIHBsdWt1IDY3MywgNTMwIDAyIFBhcmR1YmljZSBWLVplbGVuw6kgUMWZZWRtxJtzdMOt!5e0!3m2!1scs!2scz!4v1656883790129!5m2!1scs!2scz"
width="100%" height="400" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
</div>
<div class="flex-1 w-full md:w-1/2 border rounded-lg mx-auto md:ml-8 min-h-800">
<img src="/static/kolin_further_mini.jpg" alt="Kolín">
<h2 class="font-bold text-lg px-12 pt-8 pb-4">Kolín</h2>
<p class="pb-4 px-12">
Havlíčkova 915<br>
280 02 Kolín<br>
<br>
26C6+QP4
</p>
<iframe class="pt-8"
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d640.8597263818365!2d15.211311529298662!3d50.02187109871369!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x470c1540bcb1812b%3A0xb2ac1c9c7efa2dd9!2zSGF2bMOtxI1rb3ZhIDkxNSwgMjgwIDAyIEtvbMOtbg!5e0!3m2!1scs!2scz!4v1656883695590!5m2!1scs!2scz"
width="100%" height="400" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
</div>
</div>
</div>
<!-- END: Map -->
<div class="py-12">
<h2 class="font-bold text-4xl pt-16 pb-4"><a name="rezervace">Rezervace</a></h2>
<p class="py-4">Rezervaci můžete provést na telefonním čísle:</p>
<p class="pt-8 pb-4 text-center font-bold text-3xl md:text-4xl">
<a href="tel:+420736640805">+420 736 640 805</a>
</p>
<p class="pt-6">nebo nám zanechte kontaktní údaje s informacemi o vašem vozidle a my se vám ozveme zpátky s
přibližnou cenou a termínem.</p>
<div class="mt-8">
{{ if ne .StatusMessage "" }}
<div class="font-bold text-2xl text-lime-500">{{ .StatusMessage }}</div>
{{ else }}
{{ if ne .Error "" }}
<div class="font-bold text-2xl text-red-500 pt-4 pb-8">{{ .Error }}</div>
{{ end }}
<div class="grid grid-cols-1 gap-6">
<form action="/#rezervace" method="post" enctype="application/x-www-form-urlencoded">
<label class="block">
<span class="text-gray-700">Jméno a přijmení</span> <span class="text-red-500 font-bold">*</span>
<input type="text" name="name" value="{{ .Message.Name }}" class="
mt-1
block
w-full
rounded-md
border-gray-300
shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
{{ if ne .Message.NameError "" }}border-red-500{{ end }}
" placeholder="" />
</label>
<label class="block">
<span class="text-gray-700">Telefonní číslo</span> <span class="text-red-500 font-bold">*</span>
<input type="tel" name="phone" value="{{ .Message.Phone }}" class="
mt-1
block
w-full
rounded-md
border-gray-300
shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
{{ if ne .Message.PhoneError "" }}border-red-500{{ end }}
" placeholder="+420" />
</label>
<label class="block">
<span class="text-gray-700">E-mailová adresa</span>
<input type="email" name="email" value="{{ .Message.Email }}" class="
mt-1
block
w-full
rounded-md
border-gray-300
shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
" placeholder="Např. john@example.com" />
</label>
<!--<label class="block">
<span class="text-gray-700">Předběžný termín rezervace v naší garáži</span>
<select name="date"
class="
block
w-full
mt-1
rounded-md
border-gray-300
shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
"
>
<option>Oprava na místě</option>
<option>30. května, Pondělí 10:00</option>
<option>30. května, Pondělí 11:00</option>
<option>30. května, Pondělí 12:00</option>
<option>30. května, Pondělí 13:00</option>
<option>30. května, Pondělí 14:00</option>
<option>30. května, Pondělí 15:00</option>
</select>
<em>Termín si potvrdíme po telefonu</em>
</label>
-->
<label class="block">
<span class="text-gray-700">Služba</span>
<select class="
block
w-full
mt-1
rounded-md
border-gray-300
shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
" name="service">
<option value=""{{ if eq .Message.Service "" }} selected{{ end }}></option>
<option value="Výměna či oprava autoskla"{{ if eq .Message.Service "Výměna či oprava autoskla" }} selected{{ end }}>Výměna či oprava autoskla</option>
<option value="Tónování autoskel"{{ if eq .Message.Service "Tónování autoskel" }} selected{{ end }}>Tónování autoskel</option>
<option value="Dekarbonizace motoru"{{ if eq .Message.Service "Dekarbonizace motoru" }} selected{{ end }}>Dekarbonizace motoru</option>
<option value="Jiné"{{ if eq .Message.Service "Jiné" }} selected{{ end }}>Jiné</option>
</select>
</label>
<label class="block">
<span class="text-gray-700">Značka a typ vozidla / VIN kód vozidla</span>
<input type="text" name="vin" value="{{ .Message.VIN }}" class="
mt-1
block
w-full
rounded-md
border-gray-300
shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
" />
<em class="font-light text-sm text-gray-400">Vyplněním nám pomůžete připravit cenový odhad</em>
</label>
<label class="block">
<span class="text-gray-700">Poznámka</span>
<textarea name="note" class="
mt-1
block
w-full
rounded-md
border-gray-300
shadow-sm
focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
" rows="3">{{ .Message.Note }}</textarea>
</label>
<button class="bg-slate-200 rounded mx-auto w-48 hover:bg-slate-400 py-4 my-4">Odeslat</button>
</form>
</div>
{{ end }}
</div>
</div>
<!-- END: Reservation -->
</div>
<div class="h-16 w-full bg-slate-500">
<div class="max-w-4xl mx-auto h-full flex flex-col justify-center text-right">
<p class="text-slate-800">Hosted with love on <a href="https://rosti.cz" class="text-gray-200 hover:text-gray-400">Roští.cz</a>.</p>
</div>
</div>
</body>
</html>

11
tailwind.config.js Normal file
View File

@ -0,0 +1,11 @@
module.exports = {
content: [
"./src/**/*.{html,js}"
],
theme: {
extend: {},
},
plugins: [
require('@tailwindcss/forms'),
],
}

22
texty.txt Normal file
View File

@ -0,0 +1,22 @@
cx@initd.cz
Ahoj, potřebuju text na web patřící autoservisu specializujícího se na opravdu autoskel. Rozveď prosím text: Poškozené sklo se snažíme primárně opravit. V případě většího poškození sklo vyměníme a zkalibrujeme senzory, které jsou na něm umístěné. Opravu i výměnu provádíme buď v naší garáží nebo na domluveném místě, například u vás doma nebo v práci.
Vítáme vás na našem webu, kde se specializujeme na opravu a výměnu autoskel. Pokud jste narazili na problém s poškozeným sklem vašeho vozidla, neváhejte se na nás obrátit. Snažíme se vždy primárně opravit poškozené sklo, aby byla oprava co nejefektivnější a nejšetrnější k vašemu rozpočtu.
V případě, že je poškození skla příliš velké a není možné ho opravit, vyměníme celé sklo za nové. Při výměně skla zajistíme také kalibraci senzorů, které jsou na něm umístěné. Díky tomu bude vaše vozidlo opět plně funkční a bezpečné pro řízení.
Nabízíme možnost opravy i výměny skla na místě, které si společně domluvíme. Pokud preferujete opravu ve vaší garáži, jsme připraveni k vám přijet. Pokud byste chtěli mít opravu provedenu přímo u vás doma nebo v práci, není to pro nás žádný problém. Naši technici jsou vybaveni vším potřebným vybavením a nástroji, aby mohli provést opravu rychle a efektivně.
V našem autoservisu se snažíme poskytovat nejen kvalitní služby, ale také osobní přístup k zákazníkům. Vždy se snažíme poradit a pomoci s jakýmkoliv problémem, který s vaším vozidlem máte. Pokud máte zájem o naše služby, neváhejte nás kontaktovat a rádi vám pomůžeme.
cx@initd.cz
Stejný servis nabízí i dekarbonizaci motorů. Rozveď prosím text: Během procesu dekarbonizace se do směsy paliva se vzduchem přidává vodík, čímž dochází ke spalování při větší teplotě a vypálí se nečistoty, které se v motoru za dobu jeho provozu usadily. Dekarbonizace trvá přibližně hodinu a může zlepšit odezvu motoru, zvýšit jeho kroutící moment a výkon, zlepšit emise a prodloužit životnost.
Jsme autoservis, který se specializuje nejen na opravu a výměnu autoskel, ale také na dekarbonizaci motorů. Tato služba je určena pro motoristy, kteří chtějí maximalizovat výkon a životnost svého motoru.
Během procesu dekarbonizace se do směsi paliva se vzduchem přidává vodík. Tento proces umožňuje spálení paliva při vyšší teplotě, což vede k vypálení nečistot, které se v motoru během jeho provozu usadily. V důsledku toho se zlepšuje výkon a odezva motoru, zvyšuje se jeho kroutící moment a výkon a také se zlepšují emise.
Dekarbonizace motoru trvá přibližně jednu hodinu, během které se vodík přivádí do motoru a směs se nechá hořet. Tento proces není pouze prospěšný pro výkon a životnost motoru, ale také může pomoci snížit spotřebu paliva.
Pokud hledáte způsob, jak zlepšit výkon a životnost svého motoru, dekarbonizace motoru je jedním z nejlepších řešení. Naše zkušení technici jsou vybaveni nejmodernější technikou a přístroji pro dekarbonizaci motoru, aby mohli poskytnout kvalitní a efektivní služby pro naše zákazníky. Pokud máte zájem o tuto službu, neváhejte nás kontaktovat a rádi vám pomůžeme s jakýmkoliv dotazem.

8
types.go Normal file
View File

@ -0,0 +1,8 @@
package main
type Response struct {
Error string `json:"error"`
Success string `json:"success"`
StatusMessage string
Message Message
}