Initial commit
29
Makefile
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
5
package.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.2"
|
||||
}
|
||||
}
|
3
src/input.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
BIN
src/static/dekarbonizace.jpg
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
src/static/dekarbonizace_mini.jpg
Normal file
After Width: | Height: | Size: 115 KiB |
BIN
src/static/kalibrace.jpg
Normal file
After Width: | Height: | Size: 917 KiB |
BIN
src/static/kalibrace_mini.jpg
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
src/static/kolin.png
Normal file
After Width: | Height: | Size: 2 MiB |
BIN
src/static/kolin_close.jpg
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
src/static/kolin_close_mini.jpg
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
src/static/kolin_further.jpg
Normal file
After Width: | Height: | Size: 895 KiB |
BIN
src/static/kolin_further_mini.jpg
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
src/static/kolin_mini.png
Normal file
After Width: | Height: | Size: 407 KiB |
BIN
src/static/pardubice.png
Normal file
After Width: | Height: | Size: 3.9 MiB |
BIN
src/static/pardubice_mini.jpg
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
src/static/xoJB0C.webp
Normal file
After Width: | Height: | Size: 51 KiB |
244
src/templates/index.html
Normal 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
|
@ -0,0 +1,11 @@
|
|||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{html,js}"
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
],
|
||||
}
|
22
texty.txt
Normal 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
|
@ -0,0 +1,8 @@
|
|||
package main
|
||||
|
||||
type Response struct {
|
||||
Error string `json:"error"`
|
||||
Success string `json:"success"`
|
||||
StatusMessage string
|
||||
Message Message
|
||||
}
|