├── Gopkg.lock ├── Gopkg.toml ├── README.md ├── cmd └── frontend │ └── main.go ├── config ├── config.go └── config.json ├── entries.go ├── handler ├── entries.go └── handler.go ├── server ├── entries.go ├── server.go └── views │ ├── entries_all.html │ └── entries_new.html ├── storage ├── mysql │ ├── entries.go │ └── mysql.go └── storage.go └── vendor ├── github.com ├── dgrijalva │ └── jwt-go │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── MIGRATION_GUIDE.md │ │ ├── README.md │ │ ├── VERSION_HISTORY.md │ │ ├── claims.go │ │ ├── doc.go │ │ ├── ecdsa.go │ │ ├── ecdsa_utils.go │ │ ├── errors.go │ │ ├── hmac.go │ │ ├── map_claims.go │ │ ├── none.go │ │ ├── parser.go │ │ ├── rsa.go │ │ ├── rsa_pss.go │ │ ├── rsa_utils.go │ │ ├── signing_method.go │ │ └── token.go ├── go-sql-driver │ └── mysql │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── AUTHORS │ │ ├── CHANGELOG.md │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── appengine.go │ │ ├── auth.go │ │ ├── buffer.go │ │ ├── collations.go │ │ ├── connection.go │ │ ├── connection_go18.go │ │ ├── const.go │ │ ├── driver.go │ │ ├── dsn.go │ │ ├── errors.go │ │ ├── fields.go │ │ ├── infile.go │ │ ├── packets.go │ │ ├── result.go │ │ ├── rows.go │ │ ├── statement.go │ │ ├── transaction.go │ │ ├── utils.go │ │ ├── utils_go17.go │ │ └── utils_go18.go ├── jinzhu │ ├── gorm │ │ ├── .codeclimate.yml │ │ ├── .gitignore │ │ ├── License │ │ ├── README.md │ │ ├── association.go │ │ ├── callback.go │ │ ├── callback_create.go │ │ ├── callback_delete.go │ │ ├── callback_query.go │ │ ├── callback_query_preload.go │ │ ├── callback_row_query.go │ │ ├── callback_save.go │ │ ├── callback_update.go │ │ ├── dialect.go │ │ ├── dialect_common.go │ │ ├── dialect_mysql.go │ │ ├── dialect_postgres.go │ │ ├── dialect_sqlite3.go │ │ ├── dialects │ │ │ └── mysql │ │ │ │ └── mysql.go │ │ ├── docker-compose.yml │ │ ├── errors.go │ │ ├── field.go │ │ ├── interface.go │ │ ├── join_table_handler.go │ │ ├── logger.go │ │ ├── main.go │ │ ├── model.go │ │ ├── model_struct.go │ │ ├── scope.go │ │ ├── search.go │ │ ├── test_all.sh │ │ ├── utils.go │ │ └── wercker.yml │ └── inflection │ │ ├── LICENSE │ │ ├── README.md │ │ ├── inflections.go │ │ └── wercker.yml ├── labstack │ ├── echo │ │ ├── .editorconfig │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── Gopkg.lock │ │ ├── Gopkg.toml │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── bind.go │ │ ├── context.go │ │ ├── echo.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── group.go │ │ ├── log.go │ │ ├── middleware │ │ │ ├── basic_auth.go │ │ │ ├── body_dump.go │ │ │ ├── body_limit.go │ │ │ ├── compress.go │ │ │ ├── cors.go │ │ │ ├── csrf.go │ │ │ ├── jwt.go │ │ │ ├── key_auth.go │ │ │ ├── logger.go │ │ │ ├── method_override.go │ │ │ ├── middleware.go │ │ │ ├── proxy.go │ │ │ ├── recover.go │ │ │ ├── redirect.go │ │ │ ├── request_id.go │ │ │ ├── rewrite.go │ │ │ ├── secure.go │ │ │ ├── slash.go │ │ │ └── static.go │ │ ├── response.go │ │ └── router.go │ └── gommon │ │ ├── LICENSE │ │ ├── bytes │ │ ├── README.md │ │ └── bytes.go │ │ ├── color │ │ ├── README.md │ │ └── color.go │ │ ├── log │ │ ├── README.md │ │ ├── color.go │ │ ├── log.go │ │ └── white.go │ │ └── random │ │ └── random.go ├── mattn │ ├── go-colorable │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── colorable_appengine.go │ │ ├── colorable_others.go │ │ ├── colorable_windows.go │ │ └── noncolorable.go │ └── go-isatty │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── doc.go │ │ ├── isatty_appengine.go │ │ ├── isatty_bsd.go │ │ ├── isatty_linux.go │ │ ├── isatty_linux_ppc64x.go │ │ ├── isatty_others.go │ │ ├── isatty_solaris.go │ │ └── isatty_windows.go └── valyala │ ├── bytebufferpool │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── bytebuffer.go │ ├── doc.go │ └── pool.go │ └── fasttemplate │ ├── LICENSE │ ├── README.md │ ├── template.go │ ├── unsafe.go │ └── unsafe_gae.go ├── golang.org └── x │ ├── crypto │ ├── AUTHORS │ ├── CONTRIBUTORS │ ├── LICENSE │ ├── PATENTS │ └── acme │ │ ├── acme.go │ │ ├── autocert │ │ ├── autocert.go │ │ ├── cache.go │ │ ├── listener.go │ │ └── renewal.go │ │ ├── http.go │ │ ├── jws.go │ │ └── types.go │ └── sys │ ├── AUTHORS │ ├── CONTRIBUTORS │ ├── LICENSE │ ├── PATENTS │ └── unix │ ├── .gitignore │ ├── README.md │ ├── affinity_linux.go │ ├── aliases.go │ ├── asm_aix_ppc64.s │ ├── asm_darwin_386.s │ ├── asm_darwin_amd64.s │ ├── asm_darwin_arm.s │ ├── asm_darwin_arm64.s │ ├── asm_dragonfly_amd64.s │ ├── asm_freebsd_386.s │ ├── asm_freebsd_amd64.s │ ├── asm_freebsd_arm.s │ ├── asm_linux_386.s │ ├── asm_linux_amd64.s │ ├── asm_linux_arm.s │ ├── asm_linux_arm64.s │ ├── asm_linux_mips64x.s │ ├── asm_linux_mipsx.s │ ├── asm_linux_ppc64x.s │ ├── asm_linux_s390x.s │ ├── asm_netbsd_386.s │ ├── asm_netbsd_amd64.s │ ├── asm_netbsd_arm.s │ ├── asm_openbsd_386.s │ ├── asm_openbsd_amd64.s │ ├── asm_openbsd_arm.s │ ├── asm_solaris_amd64.s │ ├── bluetooth_linux.go │ ├── cap_freebsd.go │ ├── constants.go │ ├── dev_aix_ppc.go │ ├── dev_aix_ppc64.go │ ├── dev_darwin.go │ ├── dev_dragonfly.go │ ├── dev_freebsd.go │ ├── dev_linux.go │ ├── dev_netbsd.go │ ├── dev_openbsd.go │ ├── dirent.go │ ├── endian_big.go │ ├── endian_little.go │ ├── env_unix.go │ ├── errors_freebsd_386.go │ ├── errors_freebsd_amd64.go │ ├── errors_freebsd_arm.go │ ├── fcntl.go │ ├── fcntl_linux_32bit.go │ ├── gccgo.go │ ├── gccgo_c.c │ ├── gccgo_linux_amd64.go │ ├── ioctl.go │ ├── mkall.sh │ ├── mkerrors.sh │ ├── mkpost.go │ ├── mksyscall.pl │ ├── mksyscall_aix_ppc.pl │ ├── mksyscall_aix_ppc64.pl │ ├── mksyscall_solaris.pl │ ├── mksysctl_openbsd.pl │ ├── mksysnum_darwin.pl │ ├── mksysnum_dragonfly.pl │ ├── mksysnum_freebsd.pl │ ├── mksysnum_netbsd.pl │ ├── mksysnum_openbsd.pl │ ├── openbsd_pledge.go │ ├── openbsd_unveil.go │ ├── pagesize_unix.go │ ├── race.go │ ├── race0.go │ ├── sockcmsg_linux.go │ ├── sockcmsg_unix.go │ ├── str.go │ ├── syscall.go │ ├── syscall_aix.go │ ├── syscall_aix_ppc.go │ ├── syscall_aix_ppc64.go │ ├── syscall_bsd.go │ ├── syscall_darwin.go │ ├── syscall_darwin_386.go │ ├── syscall_darwin_amd64.go │ ├── syscall_darwin_arm.go │ ├── syscall_darwin_arm64.go │ ├── syscall_dragonfly.go │ ├── syscall_dragonfly_amd64.go │ ├── syscall_freebsd.go │ ├── syscall_freebsd_386.go │ ├── syscall_freebsd_amd64.go │ ├── syscall_freebsd_arm.go │ ├── syscall_linux.go │ ├── syscall_linux_386.go │ ├── syscall_linux_amd64.go │ ├── syscall_linux_amd64_gc.go │ ├── syscall_linux_arm.go │ ├── syscall_linux_arm64.go │ ├── syscall_linux_gc.go │ ├── syscall_linux_gc_386.go │ ├── syscall_linux_gccgo_386.go │ ├── syscall_linux_gccgo_arm.go │ ├── syscall_linux_mips64x.go │ ├── syscall_linux_mipsx.go │ ├── syscall_linux_ppc64x.go │ ├── syscall_linux_riscv64.go │ ├── syscall_linux_s390x.go │ ├── syscall_linux_sparc64.go │ ├── syscall_netbsd.go │ ├── syscall_netbsd_386.go │ ├── syscall_netbsd_amd64.go │ ├── syscall_netbsd_arm.go │ ├── syscall_openbsd.go │ ├── syscall_openbsd_386.go │ ├── syscall_openbsd_amd64.go │ ├── syscall_openbsd_arm.go │ ├── syscall_solaris.go │ ├── syscall_solaris_amd64.go │ ├── syscall_unix.go │ ├── syscall_unix_gc.go │ ├── timestruct.go │ ├── types_aix.go │ ├── types_darwin.go │ ├── types_dragonfly.go │ ├── types_freebsd.go │ ├── types_netbsd.go │ ├── types_openbsd.go │ ├── types_solaris.go │ ├── xattr_bsd.go │ ├── zerrors_aix_ppc.go │ ├── zerrors_aix_ppc64.go │ ├── zerrors_darwin_386.go │ ├── zerrors_darwin_amd64.go │ ├── zerrors_darwin_arm.go │ ├── zerrors_darwin_arm64.go │ ├── zerrors_dragonfly_amd64.go │ ├── zerrors_freebsd_386.go │ ├── zerrors_freebsd_amd64.go │ ├── zerrors_freebsd_arm.go │ ├── zerrors_linux_386.go │ ├── zerrors_linux_amd64.go │ ├── zerrors_linux_arm.go │ ├── zerrors_linux_arm64.go │ ├── zerrors_linux_mips.go │ ├── zerrors_linux_mips64.go │ ├── zerrors_linux_mips64le.go │ ├── zerrors_linux_mipsle.go │ ├── zerrors_linux_ppc64.go │ ├── zerrors_linux_ppc64le.go │ ├── zerrors_linux_riscv64.go │ ├── zerrors_linux_s390x.go │ ├── zerrors_linux_sparc64.go │ ├── zerrors_netbsd_386.go │ ├── zerrors_netbsd_amd64.go │ ├── zerrors_netbsd_arm.go │ ├── zerrors_openbsd_386.go │ ├── zerrors_openbsd_amd64.go │ ├── zerrors_openbsd_arm.go │ ├── zerrors_solaris_amd64.go │ ├── zptrace386_linux.go │ ├── zptracearm_linux.go │ ├── zptracemips_linux.go │ ├── zptracemipsle_linux.go │ ├── zsyscall_aix_ppc.go │ ├── zsyscall_aix_ppc64.go │ ├── zsyscall_aix_ppc64_gc.go │ ├── zsyscall_aix_ppc64_gccgo.go │ ├── zsyscall_darwin_386.go │ ├── zsyscall_darwin_amd64.go │ ├── zsyscall_darwin_arm.go │ ├── zsyscall_darwin_arm64.go │ ├── zsyscall_dragonfly_amd64.go │ ├── zsyscall_freebsd_386.go │ ├── zsyscall_freebsd_amd64.go │ ├── zsyscall_freebsd_arm.go │ ├── zsyscall_linux_386.go │ ├── zsyscall_linux_amd64.go │ ├── zsyscall_linux_arm.go │ ├── zsyscall_linux_arm64.go │ ├── zsyscall_linux_mips.go │ ├── zsyscall_linux_mips64.go │ ├── zsyscall_linux_mips64le.go │ ├── zsyscall_linux_mipsle.go │ ├── zsyscall_linux_ppc64.go │ ├── zsyscall_linux_ppc64le.go │ ├── zsyscall_linux_riscv64.go │ ├── zsyscall_linux_s390x.go │ ├── zsyscall_linux_sparc64.go │ ├── zsyscall_netbsd_386.go │ ├── zsyscall_netbsd_amd64.go │ ├── zsyscall_netbsd_arm.go │ ├── zsyscall_openbsd_386.go │ ├── zsyscall_openbsd_amd64.go │ ├── zsyscall_openbsd_arm.go │ ├── zsyscall_solaris_amd64.go │ ├── zsysctl_openbsd_386.go │ ├── zsysctl_openbsd_amd64.go │ ├── zsysctl_openbsd_arm.go │ ├── zsysnum_darwin_386.go │ ├── zsysnum_darwin_amd64.go │ ├── zsysnum_darwin_arm.go │ ├── zsysnum_darwin_arm64.go │ ├── zsysnum_dragonfly_amd64.go │ ├── zsysnum_freebsd_386.go │ ├── zsysnum_freebsd_amd64.go │ ├── zsysnum_freebsd_arm.go │ ├── zsysnum_linux_386.go │ ├── zsysnum_linux_amd64.go │ ├── zsysnum_linux_arm.go │ ├── zsysnum_linux_arm64.go │ ├── zsysnum_linux_mips.go │ ├── zsysnum_linux_mips64.go │ ├── zsysnum_linux_mips64le.go │ ├── zsysnum_linux_mipsle.go │ ├── zsysnum_linux_ppc64.go │ ├── zsysnum_linux_ppc64le.go │ ├── zsysnum_linux_riscv64.go │ ├── zsysnum_linux_s390x.go │ ├── zsysnum_linux_sparc64.go │ ├── zsysnum_netbsd_386.go │ ├── zsysnum_netbsd_amd64.go │ ├── zsysnum_netbsd_arm.go │ ├── zsysnum_openbsd_386.go │ ├── zsysnum_openbsd_amd64.go │ ├── zsysnum_openbsd_arm.go │ ├── ztypes_aix_ppc.go │ ├── ztypes_aix_ppc64.go │ ├── ztypes_darwin_386.go │ ├── ztypes_darwin_amd64.go │ ├── ztypes_darwin_arm.go │ ├── ztypes_darwin_arm64.go │ ├── ztypes_dragonfly_amd64.go │ ├── ztypes_freebsd_386.go │ ├── ztypes_freebsd_amd64.go │ ├── ztypes_freebsd_arm.go │ ├── ztypes_linux_386.go │ ├── ztypes_linux_amd64.go │ ├── ztypes_linux_arm.go │ ├── ztypes_linux_arm64.go │ ├── ztypes_linux_mips.go │ ├── ztypes_linux_mips64.go │ ├── ztypes_linux_mips64le.go │ ├── ztypes_linux_mipsle.go │ ├── ztypes_linux_ppc64.go │ ├── ztypes_linux_ppc64le.go │ ├── ztypes_linux_riscv64.go │ ├── ztypes_linux_s390x.go │ ├── ztypes_linux_sparc64.go │ ├── ztypes_netbsd_386.go │ ├── ztypes_netbsd_amd64.go │ ├── ztypes_netbsd_arm.go │ ├── ztypes_openbsd_386.go │ ├── ztypes_openbsd_amd64.go │ ├── ztypes_openbsd_arm.go │ └── ztypes_solaris_amd64.go └── google.golang.org └── appengine ├── LICENSE └── cloudsql ├── cloudsql.go ├── cloudsql_classic.go └── cloudsql_vm.go /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | name = "github.com/jinzhu/gorm" 30 | version = "1.9.1" 31 | 32 | [[constraint]] 33 | name = "github.com/labstack/echo" 34 | version = "3.3.6" 35 | 36 | [prune] 37 | go-tests = true 38 | unused-packages = true 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Example Go Language Application 2 | 3 | This application is a guestbook, it will walk through a basic CRUD application, the logic on the server and how to work with golang templates. 4 | 5 | ## Setup 6 | 7 | ### Prerequisites 8 | 9 | 1. Ensure you know the login credentials for the MySQL instance you will be working with 10 | 2. Create a database that will be used to store entry information. 11 | 3. Modify `config/config.json` to match your environments credentials so the application can connect to MySQL. 12 | 13 | ## Run 14 | 15 | To run, from the root of the project run the following: 16 | 17 | `go run ./cmd/frontend/main.go` -------------------------------------------------------------------------------- /cmd/frontend/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/dougbarrett/example-golang-app/config" 10 | "github.com/dougbarrett/example-golang-app/handler" 11 | "github.com/dougbarrett/example-golang-app/server" 12 | "github.com/dougbarrett/example-golang-app/storage/mysql" 13 | ) 14 | 15 | const defaultConfigPath = "./config/config.json" 16 | const defaultPortVariable = "PORT" 17 | const defaultPort = "3000" 18 | 19 | /* Setup is the function `main` calls in order to start the web server bootstrapped with the handler */ 20 | func Setup(config *config.Config) { 21 | ds, err := mysql.NewFrontend( 22 | config.MySQL.User, 23 | config.MySQL.Password, 24 | config.MySQL.Host, 25 | config.MySQL.DB, 26 | ) 27 | 28 | if err != nil { 29 | log.Fatalf("Cannot set up mysql: %v", err) 30 | } 31 | 32 | // cache := cache.New(ds) 33 | 34 | frontend, err := handler.NewFrontend( 35 | ds, 36 | ) 37 | 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | e, err := server.NewFrontend( 43 | frontend, 44 | ) 45 | 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | port := defaultPort 51 | 52 | if os.Getenv(defaultPortVariable) != "" { 53 | port = os.Getenv(defaultPortVariable) 54 | } 55 | 56 | addr := fmt.Sprintf(":%s", port) 57 | 58 | e.Start(addr) 59 | } 60 | 61 | func main() { 62 | configPath := flag.String("config", defaultConfigPath, "path of th config file") 63 | 64 | flag.Parse() 65 | 66 | config, err := config.FromFile(*configPath) 67 | 68 | if err != nil { 69 | panic(err) 70 | } 71 | Setup(config) 72 | } 73 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | ) 7 | 8 | type Config struct { 9 | MySQL struct { 10 | Host string `json:"host"` 11 | User string `json:"user"` 12 | Password string `json:"password"` 13 | DB string `json:"db"` 14 | } `json:"mysql"` 15 | } 16 | 17 | func FromFile(path string) (*Config, error) { 18 | b, err := ioutil.ReadFile(path) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | var cfg Config 24 | if err := json.Unmarshal(b, &cfg); err != nil { 25 | return nil, err 26 | } 27 | 28 | return &cfg, nil 29 | } 30 | -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mysql": { 3 | "host": "127.0.0.1:3306", 4 | "user": "username", 5 | "password": "password", 6 | "db": "example_guestbook" 7 | } 8 | } -------------------------------------------------------------------------------- /entries.go: -------------------------------------------------------------------------------- 1 | package guestbook 2 | 3 | import "github.com/jinzhu/gorm" 4 | 5 | type Entry struct { 6 | gorm.Model 7 | Title string 8 | Name string 9 | Email string 10 | Message string `gorm:"type:longtext"` 11 | } 12 | -------------------------------------------------------------------------------- /handler/entries.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/dougbarrett/example-golang-app" 7 | ) 8 | 9 | func (h *handler) CreateEntry( 10 | title string, 11 | name string, 12 | email string, 13 | message string, 14 | ) ( 15 | entry guestbook.Entry, 16 | err error, 17 | ) { 18 | entry.Title = title 19 | entry.Name = name 20 | entry.Email = email 21 | entry.Message = message 22 | 23 | err = h.svc.SaveEntry(&entry) 24 | return 25 | } 26 | 27 | func (h *handler) UpdateEntry( 28 | id uint, 29 | title string, 30 | name string, 31 | email string, 32 | message string, 33 | ) ( 34 | entry guestbook.Entry, 35 | err error, 36 | ) { 37 | entry, err = h.svc.GetEntry(id) 38 | if err != nil { 39 | return 40 | } 41 | 42 | entry.Title = title 43 | entry.Name = name 44 | entry.Email = email 45 | entry.Message = message 46 | 47 | err = h.svc.SaveEntry(&entry) 48 | return 49 | } 50 | 51 | func (h *handler) ListEntries() ( 52 | entries []guestbook.Entry, 53 | err error, 54 | ) { 55 | return h.svc.GetAllEntries() 56 | } 57 | 58 | func (h *handler) DeleteEntry( 59 | entryID uint, 60 | ) ( 61 | err error, 62 | ) { 63 | entry, err := h.svc.GetEntry(entryID) 64 | 65 | if entry.ID == 0 || err != nil { 66 | return errors.New("Entry doesn't exist") 67 | } 68 | 69 | return h.svc.DeleteEntry(entryID) 70 | } 71 | -------------------------------------------------------------------------------- /handler/handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "github.com/dougbarrett/example-golang-app" 5 | "github.com/dougbarrett/example-golang-app/storage" 6 | ) 7 | 8 | type handler struct { 9 | svc storage.Service 10 | } 11 | 12 | type Service interface { 13 | CreateEntry( 14 | title string, 15 | name string, 16 | email string, 17 | message string, 18 | ) ( 19 | entry guestbook.Entry, 20 | err error, 21 | ) 22 | UpdateEntry( 23 | id uint, 24 | title string, 25 | name string, 26 | email string, 27 | message string, 28 | ) ( 29 | entry guestbook.Entry, 30 | err error, 31 | ) 32 | ListEntries() ( 33 | entries []guestbook.Entry, 34 | err error, 35 | ) 36 | DeleteEntry( 37 | entryID uint, 38 | ) ( 39 | err error, 40 | ) 41 | } 42 | 43 | func NewFrontend( 44 | svc storage.Service, 45 | ) ( 46 | Service, 47 | error, 48 | ) { 49 | h := handler{} 50 | h.svc = svc 51 | return &h, nil 52 | } 53 | -------------------------------------------------------------------------------- /server/entries.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/dougbarrett/example-golang-app" 7 | "github.com/labstack/echo" 8 | ) 9 | 10 | func (s *server) getAllEntries(c echo.Context) error { 11 | 12 | var retData struct { 13 | Entries []guestbook.Entry 14 | } 15 | var err error 16 | 17 | retData.Entries, err = s.handler.ListEntries() 18 | 19 | if err != nil { 20 | return err 21 | } 22 | 23 | return c.Render(http.StatusOK, "entries_all.html", retData) 24 | } 25 | 26 | func (s *server) newEntry(c echo.Context) error { 27 | return c.Render(http.StatusOK, "entries_new.html", nil) 28 | } 29 | 30 | type createEntryData struct { 31 | guestbook.Entry 32 | } 33 | 34 | func (s *server) createEntry(c echo.Context) error { 35 | e := new(createEntryData) 36 | if err := c.Bind(e); err != nil { 37 | return err 38 | } 39 | _, err := s.handler.CreateEntry( 40 | e.Title, 41 | e.Name, 42 | e.Email, 43 | e.Message, 44 | ) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | return c.Redirect(302, "/") 50 | } 51 | 52 | func (s *server) deleteEntry(c echo.Context) error { 53 | e := new(createEntryData) 54 | if err := c.Bind(e); err != nil { 55 | return err 56 | } 57 | 58 | err := s.handler.DeleteEntry(e.ID) 59 | 60 | if err != nil { 61 | return err 62 | } 63 | 64 | return c.Redirect(302, "/") 65 | } 66 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "html/template" 5 | "io" 6 | 7 | "github.com/dougbarrett/example-golang-app/handler" 8 | "github.com/labstack/echo" 9 | "github.com/labstack/echo/middleware" 10 | ) 11 | 12 | type server struct { 13 | handler handler.Service 14 | } 15 | 16 | type Template struct { 17 | templates *template.Template 18 | } 19 | 20 | func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { 21 | return t.templates.ExecuteTemplate(w, name, data) 22 | } 23 | 24 | func NewFrontend( 25 | handler handler.Service, 26 | ) ( 27 | *echo.Echo, 28 | error, 29 | ) { 30 | s := &server{} 31 | s.handler = handler 32 | 33 | mux := echo.New() 34 | 35 | // Middleware 36 | mux.Use(middleware.Logger()) 37 | mux.Use(middleware.Recover()) 38 | 39 | t := &Template{ 40 | templates: template.Must(template.ParseGlob("server/views/*.html")), 41 | } 42 | mux.Renderer = t 43 | 44 | mux.GET("/", s.getAllEntries) 45 | mux.GET("/entries/new", s.newEntry) 46 | mux.POST("/entries/create", s.createEntry) 47 | mux.POST("/entries/delete", s.deleteEntry) 48 | 49 | return mux, nil 50 | } 51 | -------------------------------------------------------------------------------- /server/views/entries_all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Guestbook 8 | 9 | 10 | 11 |

New Entry | List All Entries

12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {{ range .Entries }} 25 | 26 | 27 | 28 | 29 | 30 | 36 | 37 | {{ end }} 38 | 39 |
NameEmailTitleMessage 
{{ .Name }}{{ .Email }}{{ .Title }}{{ .Message }} 31 |
32 | 33 | 34 |
35 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /server/views/entries_new.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | New Entry | Guestbook 8 | 9 | 10 |

New Entry | List All Entries

11 |
12 |

New Entry

13 | 14 |
15 |
16 |
17 |
18 |