├── 1-cmd ├── 0-empty │ ├── .gitignore │ ├── build.sh │ ├── main.go │ └── usage.sh ├── 1-hello │ ├── .gitignore │ ├── build.sh │ ├── main.go │ └── usage.sh └── 2-environment │ ├── .gitignore │ ├── .env │ ├── build.sh │ ├── install.sh │ ├── usage.sh │ └── main.go ├── 2-srv ├── 0-hello │ ├── .gitignore │ ├── 1-mux │ │ ├── usage.sh │ │ ├── serve.go │ │ ├── serve_test.go │ │ └── main.go │ └── 0-default │ │ ├── usage.sh │ │ ├── main_test.go │ │ └── main.go ├── 1-simple │ ├── html │ │ ├── js │ │ │ └── index.js │ │ ├── .body.html │ │ ├── 404.html │ │ ├── -.html │ │ ├── .head.html │ │ ├── css │ │ │ └── index.css │ │ ├── bbb.html │ │ └── aaa.html │ ├── install.sh │ ├── usage.sh │ ├── test │ │ ├── serve-.txt │ │ ├── serve-bbb.txt │ │ └── serve-aaa.txt │ ├── main.go │ ├── serve_test.go │ └── serve.go ├── 3-crud │ ├── 0-file │ │ ├── html │ │ │ ├── js │ │ │ │ └── index.js │ │ │ ├── .body.html │ │ │ ├── 404.html │ │ │ ├── all.html │ │ │ ├── .head.html │ │ │ ├── -.html │ │ │ ├── new.html │ │ │ ├── get.html │ │ │ ├── del.html │ │ │ ├── set.html │ │ │ └── css │ │ │ │ └── index.css │ │ ├── .gitignore │ │ ├── build.sh │ │ ├── usage-stop.sh │ │ ├── install.sh │ │ ├── usage-start.sh │ │ ├── app.go │ │ ├── data.go │ │ ├── main.go │ │ └── serve.go │ └── 1-sql │ │ ├── html │ │ ├── js │ │ │ └── index.js │ │ ├── .body.html │ │ ├── 404.html │ │ ├── all.html │ │ ├── .head.html │ │ ├── -.html │ │ ├── new.html │ │ ├── get.html │ │ ├── del.html │ │ ├── set.html │ │ └── css │ │ │ └── index.css │ │ ├── .gitignore │ │ ├── build.sh │ │ ├── usage-stop.sh │ │ ├── usage-start.sh │ │ ├── install.sh │ │ ├── app.go │ │ ├── main.go │ │ ├── data.go │ │ └── serve.go ├── 4-api │ ├── 1-client │ │ ├── html │ │ │ ├── js │ │ │ │ └── index.js │ │ │ ├── .body.html │ │ │ ├── 404.html │ │ │ ├── 500.html │ │ │ ├── all.html │ │ │ ├── .head.html │ │ │ ├── new.html │ │ │ ├── -.html │ │ │ ├── get.html │ │ │ ├── del.html │ │ │ ├── set.html │ │ │ └── css │ │ │ │ └── index.css │ │ ├── .gitignore │ │ ├── build.sh │ │ ├── usage-stop.sh │ │ ├── install.sh │ │ ├── usage-start.sh │ │ ├── app.go │ │ ├── main.go │ │ ├── data.go │ │ └── serve.go │ └── 0-server │ │ ├── .gitignore │ │ ├── build.sh │ │ ├── usage-stop.sh │ │ ├── usage-start.sh │ │ ├── install.sh │ │ ├── app.go │ │ ├── main.go │ │ ├── data.go │ │ └── serve.go └── 2-system │ ├── 0-notify │ ├── .gitignore │ ├── build.sh │ ├── usage.sh │ ├── install.sh │ ├── system0.service │ └── main.go │ └── 1-socket │ ├── .gitignore │ ├── build.sh │ ├── usage.sh │ ├── system1.socket │ ├── system1.service │ ├── install.sh │ └── main.go ├── 0-ini ├── 1-ide-setup.md └── 0-install.md └── README.md /1-cmd/0-empty/.gitignore: -------------------------------------------------------------------------------- 1 | empty* -------------------------------------------------------------------------------- /1-cmd/1-hello/.gitignore: -------------------------------------------------------------------------------- 1 | hello* -------------------------------------------------------------------------------- /2-srv/0-hello/.gitignore: -------------------------------------------------------------------------------- 1 | hello* -------------------------------------------------------------------------------- /2-srv/1-simple/html/js/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/js/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/js/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/js/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /1-cmd/2-environment/.gitignore: -------------------------------------------------------------------------------- 1 | environment* -------------------------------------------------------------------------------- /2-srv/2-system/0-notify/.gitignore: -------------------------------------------------------------------------------- 1 | system0 -------------------------------------------------------------------------------- /2-srv/2-system/1-socket/.gitignore: -------------------------------------------------------------------------------- 1 | system1 -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/.gitignore: -------------------------------------------------------------------------------- 1 | db/* 2 | file -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/.gitignore: -------------------------------------------------------------------------------- 1 | db/* 2 | sql -------------------------------------------------------------------------------- /2-srv/4-api/1-client/.gitignore: -------------------------------------------------------------------------------- 1 | api-client 2 | -------------------------------------------------------------------------------- /2-srv/4-api/0-server/.gitignore: -------------------------------------------------------------------------------- 1 | db/* 2 | api-server 3 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go build -o sql 4 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/usage-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pkill sql 4 | -------------------------------------------------------------------------------- /1-cmd/2-environment/.env: -------------------------------------------------------------------------------- 1 | word = bar 2 | numb = 20 3 | bool = false 4 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go build -o file 4 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/usage-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pkill file 4 | -------------------------------------------------------------------------------- /1-cmd/2-environment/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go build -o environment 4 | -------------------------------------------------------------------------------- /2-srv/2-system/0-notify/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go build -o system0 4 | -------------------------------------------------------------------------------- /2-srv/2-system/1-socket/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go build -o system1 4 | -------------------------------------------------------------------------------- /2-srv/4-api/0-server/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go build -o api-server 4 | -------------------------------------------------------------------------------- /2-srv/4-api/0-server/usage-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pkill api-server 4 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go build -o api-client 4 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/usage-stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pkill api-client 4 | -------------------------------------------------------------------------------- /2-srv/1-simple/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go get github.com/gorilla/mux 4 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/usage-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./sql & 4 | 5 | sleep 2 6 | -------------------------------------------------------------------------------- /1-cmd/2-environment/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go get github.com/joho/godotenv 4 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go get github.com/gorilla/mux 4 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/usage-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./file & 4 | 5 | sleep 2 6 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go get github.com/gorilla/mux 4 | -------------------------------------------------------------------------------- /1-cmd/0-empty/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go build -o empty 4 | tinygo build -o empty0 5 | -------------------------------------------------------------------------------- /1-cmd/1-hello/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go build -o hello 4 | #tinygo build -o hello0 5 | -------------------------------------------------------------------------------- /2-srv/4-api/0-server/usage-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./api-server & 4 | 5 | sleep 2 6 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/usage-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./api-client & 4 | 5 | sleep 2 6 | -------------------------------------------------------------------------------- /2-srv/2-system/0-notify/usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | wget -q -O - http://how-srv-system0:8080/ 4 | -------------------------------------------------------------------------------- /2-srv/2-system/1-socket/usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | wget -q -O - http://how-srv-system1:8080/ 4 | -------------------------------------------------------------------------------- /1-cmd/0-empty/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // --- the empty go app --- 4 | 5 | func main() { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /2-srv/2-system/1-socket/system1.socket: -------------------------------------------------------------------------------- 1 | [Socket] 2 | ListenStream=127.1.2.1:8080 3 | 4 | [Install] 5 | WantedBy=sockets.target 6 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go get github.com/gorilla/mux 4 | 5 | go get -u gorm.io/gorm 6 | go get -u gorm.io/driver/sqlite 7 | -------------------------------------------------------------------------------- /2-srv/1-simple/html/.body.html: -------------------------------------------------------------------------------- 1 | {{define ".body0"}} 2 |
3 | {{end}} 4 | {{define ".body1"}} 5 |
6 | {{end}} -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/.body.html: -------------------------------------------------------------------------------- 1 | {{define ".body0"}} 2 |
3 | {{end}} 4 | {{define ".body1"}} 5 |
6 | {{end}} -------------------------------------------------------------------------------- /2-srv/4-api/0-server/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go get github.com/gorilla/mux 4 | 5 | go get -u gorm.io/gorm 6 | go get -u gorm.io/driver/sqlite 7 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/.body.html: -------------------------------------------------------------------------------- 1 | {{define ".body0"}} 2 |
3 | {{end}} 4 | {{define ".body1"}} 5 |
6 | {{end}} -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/.body.html: -------------------------------------------------------------------------------- 1 | {{define ".body0"}} 2 |
3 | {{end}} 4 | {{define ".body1"}} 5 |
6 | {{end}} -------------------------------------------------------------------------------- /0-ini/1-ide-setup.md: -------------------------------------------------------------------------------- 1 | 2 | **Install Golang on Atom Editor** 3 | 4 | See https://atom.io/packages/go-plus 5 | 6 | **Install Golang on VS Code** 7 | 8 | See https://marketplace.visualstudio.com/items?itemName=golang.go 9 | -------------------------------------------------------------------------------- /1-cmd/0-empty/usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./empty 4 | ./empty0 5 | 6 | # this empty project is interesting for measuring the size of 7 | # the go language overhead in different versions and compilers: 8 | 9 | ls -la | grep empty 10 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 |

404 not found

12 | 13 | {{template ".body1" .}} 14 | 15 | 16 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 |

404 not found

12 | 13 | {{template ".body1" .}} 14 | 15 | 16 | -------------------------------------------------------------------------------- /2-srv/1-simple/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | 10 | Back 11 | 12 |

404 not found

13 | 14 | {{template ".body1" .}} 15 | 16 | 17 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 |

404 not found

12 | 13 | {{template ".body1" .}} 14 | 15 | 16 | -------------------------------------------------------------------------------- /2-srv/1-simple/usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go run . & 4 | 5 | sleep 2 6 | 7 | go test . && 8 | { 9 | wget -q -O - http://how-srv-simple:8080/ 10 | wget -q -O - http://how-srv-simple:8080/aaa 11 | wget -q -O - http://how-srv-simple:8080/bbb 12 | } 13 | pkill 1-simple 14 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 |

500 internal server error

12 | 13 | {{template ".body1" .}} 14 | 15 | 16 | -------------------------------------------------------------------------------- /2-srv/0-hello/1-mux/usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go run . & 4 | 5 | sleep 2 6 | 7 | go test . && 8 | { 9 | wget -q -O - http://how-srv-hello:8080/ # Should display: "Hello " 10 | wget -q -O - http://how-srv-hello:8080/World # Should display: "Hello World" 11 | } 12 | pkill 1-mux 13 | -------------------------------------------------------------------------------- /2-srv/0-hello/0-default/usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go run . & 4 | 5 | sleep 2 6 | 7 | go test . && 8 | { 9 | wget -q -O - http://how-srv-hello:8080/ # Should display: "Hello " 10 | wget -q -O - http://how-srv-hello:8080/World # Should display: "Hello World" 11 | } 12 | pkill 0-default 13 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // app init 8 | 9 | var dirRoot string 10 | 11 | func init() { 12 | var err error 13 | 14 | // gets the current directory 15 | 16 | dirRoot, err = os.Getwd() 17 | if err != nil { 18 | dirRoot = "." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // app init 8 | 9 | var dirRoot string 10 | 11 | func init() { 12 | var err error 13 | 14 | // gets the current directory 15 | 16 | dirRoot, err = os.Getwd() 17 | if err != nil { 18 | dirRoot = "." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /2-srv/4-api/0-server/app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // app init 8 | 9 | var dirRoot string 10 | 11 | func init() { 12 | var err error 13 | 14 | // gets the current directory 15 | 16 | dirRoot, err = os.Getwd() 17 | if err != nil { 18 | dirRoot = "." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // app init 8 | 9 | var dirRoot string 10 | 11 | func init() { 12 | var err error 13 | 14 | // gets the current directory 15 | 16 | dirRoot, err = os.Getwd() 17 | if err != nil { 18 | dirRoot = "." 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 |

All

12 | 13 | {{range .Docs}} 14 |
  • {{.Name}}
  • 15 | {{end}} 16 | 17 | {{template ".body1" .}} 18 | 19 | 20 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 |

    All

    12 | 13 | {{range .Docs}} 14 |
  • {{.Name}}
  • 15 | {{end}} 16 | 17 | {{template ".body1" .}} 18 | 19 | 20 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 |

    All

    12 | 13 | {{range .Docs}} 14 |
  • {{.Name}}
  • 15 | {{end}} 16 | 17 | {{template ".body1" .}} 18 | 19 | 20 | -------------------------------------------------------------------------------- /2-srv/1-simple/html/-.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | 10 |

    Simple server

    11 | 12 |

    AAA

    13 |

    BBB

    14 |

    CCC

    15 | 16 | {{template ".body1" .}} 17 | 18 | 19 | -------------------------------------------------------------------------------- /2-srv/2-system/0-notify/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go get github.com/coreos/go-systemd/daemon 4 | 5 | cd /etc/systemd/system/ 6 | 7 | sudo ln -sf /var/www/go/how-to-go/2-srv/2-system/0-notify/system.service how-srv-system0.service 8 | 9 | sudo systemctl daemon-reload 10 | 11 | sudo systemctl start how-srv-system0.service 12 | 13 | sudo systemctl enable how-srv-system0.service 14 | -------------------------------------------------------------------------------- /2-srv/2-system/0-notify/system0.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=how-srv-system0 3 | Requires=network.target 4 | After=multi-user.target 5 | 6 | [Service] 7 | Type=notify 8 | User=www-data 9 | Group=www-data 10 | ExecStart=/var/www/go/how-to-go/2-srv/2-system/0-notify/system0 11 | Restart=always 12 | RestartSec=1s 13 | WatchdogSec=30s 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /2-srv/2-system/1-socket/system1.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=how-srv-system1 3 | Requires=network.target 4 | After=multi-user.target 5 | 6 | [Service] 7 | Type=notify 8 | User=www-data 9 | Group=www-data 10 | ExecStart=/var/www/go/how-to-go/2-srv/2-system/1-socket/system1 11 | Restart=always 12 | RestartSec=1s 13 | WatchdogSec=30s 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /2-srv/0-hello/1-mux/serve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | ) 7 | 8 | func sayHello(w http.ResponseWriter, r *http.Request) { 9 | 10 | message := r.URL.Path 11 | message = strings.TrimPrefix(message, "/") 12 | message = "Hello " + message + "\n" 13 | 14 | w.Write([]byte(message)) 15 | } 16 | 17 | func init() { 18 | 19 | Mux.HandleFunc("/", sayHello) 20 | } 21 | -------------------------------------------------------------------------------- /2-srv/1-simple/html/.head.html: -------------------------------------------------------------------------------- 1 | {{define ".head"}} 2 | 3 | 4 | Simple server 5 | 6 | 7 | 8 | 9 | 10 | {{end}} -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/.head.html: -------------------------------------------------------------------------------- 1 | {{define ".head"}} 2 | 3 | 4 | Crud file server 5 | 6 | 7 | 8 | 9 | 10 | {{end}} -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/.head.html: -------------------------------------------------------------------------------- 1 | {{define ".head"}} 2 | 3 | 4 | Crud file server 5 | 6 | 7 | 8 | 9 | 10 | {{end}} -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/.head.html: -------------------------------------------------------------------------------- 1 | {{define ".head"}} 2 | 3 | 4 | Crud file server 5 | 6 | 7 | 8 | 9 | 10 | {{end}} -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/-.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 |

    Crud file server

    10 | 11 |

    All

    12 |

    Get

    13 |

    Set

    14 |

    Del

    15 |

    New

    16 | {{template ".body1" .}} 17 | 18 | 19 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/-.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 |

    Crud SQL server

    10 | 11 |

    All

    12 |

    Get

    13 |

    Set

    14 |

    Del

    15 |

    New

    16 | {{template ".body1" .}} 17 | 18 | 19 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/new.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 |

    New

    12 | 13 |
    14 |
    15 | 16 | 17 |
    18 | 19 | {{template ".body1" .}} 20 | 21 | 22 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/new.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 |

    New

    12 | 13 |
    14 |
    15 | 16 | 17 |
    18 | 19 | {{template ".body1" .}} 20 | 21 | 22 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/new.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 |

    New

    12 | 13 |
    14 |
    15 | 16 | 17 |
    18 | 19 | {{template ".body1" .}} 20 | 21 | 22 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/-.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 |

    Crud API client and server

    10 | 11 |

    All

    12 |

    Get

    13 |

    Set

    14 |

    Del

    15 |

    New

    16 | {{template ".body1" .}} 17 | 18 | 19 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/get.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 | {{if .Doc}} 12 |

    Get {{.Doc.Name}}

    13 | 14 |
    {{.Doc.Content}}
    15 | {{end}} 16 | 17 | {{template ".body1" .}} 18 | 19 | 20 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/get.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 | {{if .Doc}} 12 |

    Get {{.Doc.Name}}

    13 | 14 |
    {{.Doc.Content}}
    15 | {{end}} 16 | 17 | {{template ".body1" .}} 18 | 19 | 20 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/get.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | Back 10 | 11 | {{if .Doc}} 12 |

    Get {{.Doc.Name}}

    13 | 14 |
    {{.Doc.Content}}
    15 | {{end}} 16 | 17 | {{template ".body1" .}} 18 | 19 | 20 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/del.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | 10 | {{if .Doc}} 11 | Back 12 | 13 |

    Del {{.Doc.Name}}

    14 | 15 |
    {{.Doc.Content}}
    16 | 17 |
    18 | 19 |
    20 | {{end}} 21 | 22 | {{template ".body1" .}} 23 | 24 | 25 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/del.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | 10 | {{if .Doc}} 11 | Back 12 | 13 |

    Del {{.Doc.Name}}

    14 | 15 |
    {{.Doc.Content}}
    16 | 17 |
    18 | 19 |
    20 | {{end}} 21 | 22 | {{template ".body1" .}} 23 | 24 | 25 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/del.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | 10 | {{if .Doc}} 11 | Back 12 | 13 |

    Del {{.Doc.Name}}

    14 | 15 |
    {{.Doc.Content}}
    16 | 17 |
    18 | 19 |
    20 | {{end}} 21 | 22 | {{template ".body1" .}} 23 | 24 | 25 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/data.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | // data storage init 10 | 11 | var dirData string 12 | 13 | func init() { 14 | 15 | dirData = "db" 16 | 17 | dir := filepath.Join(dirRoot, dirData) 18 | _, err := os.Stat(dir) 19 | if !os.IsNotExist(err) { 20 | return 21 | } 22 | 23 | err = os.Mkdir(dir, 0770) 24 | if err != nil { 25 | error := fmt.Sprintf(`os.Mkdir("%s", 0770) error: %s`, dir, err) 26 | panic(error) 27 | } 28 | 29 | os.Chmod(dir, 0770) // because of https://github.com/golang/go/issues/25539#issuecomment-394472286 30 | } 31 | -------------------------------------------------------------------------------- /2-srv/2-system/1-socket/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go get github.com/coreos/go-systemd/activation 4 | go get github.com/coreos/go-systemd/daemon 5 | 6 | cd /etc/systemd/system/ 7 | 8 | sudo ln -sf /var/www/go/how-to-go/2-srv/2-system/1-socket/system.service how-srv-system1.service 9 | sudo ln -sf /var/www/go/how-to-go/2-srv/2-system/1-socket/system.socket how-srv-system1.socket 10 | 11 | sudo systemctl daemon-reload 12 | 13 | sudo systemctl start how-srv-system1.service 14 | sudo systemctl start how-srv-system1.socket 15 | 16 | sudo systemctl enable how-srv-system1.service 17 | sudo systemctl enable how-srv-system1.socket 18 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/set.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | 10 | {{if .Doc}} 11 | Back 12 | 13 |

    Set {{.Doc.Name}}

    14 | 15 |
    16 | 17 | 18 | 19 |
    20 | {{end}} 21 | 22 | {{template ".body1" .}} 23 | 24 | 25 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/set.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | 10 | {{if .Doc}} 11 | Back 12 | 13 |

    Set {{.Doc.Name}}

    14 | 15 |
    16 | 17 | 18 | 19 |
    20 | {{end}} 21 | 22 | {{template ".body1" .}} 23 | 24 | 25 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/set.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | 10 | {{if .Doc}} 11 | Back 12 | 13 |

    Set {{.Doc.Name}}

    14 | 15 |
    16 | 17 | 18 | 19 |
    20 | {{end}} 21 | 22 | {{template ".body1" .}} 23 | 24 | 25 | -------------------------------------------------------------------------------- /2-srv/1-simple/test/serve-.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Simple server 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
    19 | 20 | 21 |

    Simple server

    22 | 23 |

    AAA

    24 |

    BBB

    25 |

    CCC

    26 | 27 | 28 |
    29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /1-cmd/1-hello/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | // --- command line parameters parsing --- 9 | 10 | // from https://gobyexample.com/command-line-flags 11 | 12 | // default values for command line parameters 13 | 14 | var wordPtr *string 15 | var numbPtr *int 16 | var boolPtr *bool 17 | 18 | // app init 19 | 20 | func init() { 21 | 22 | // command line parameters setup 23 | 24 | wordPtr = flag.String("word", "foo", "a string") 25 | numbPtr = flag.Int("numb", 42, "an int") 26 | boolPtr = flag.Bool("bool", false, "a bool") 27 | 28 | flag.Parse() 29 | } 30 | 31 | // main function 32 | 33 | func main() { 34 | 35 | fmt.Println("word:", *wordPtr) 36 | fmt.Println("numb:", *numbPtr) 37 | fmt.Println("bool:", *boolPtr) 38 | } 39 | -------------------------------------------------------------------------------- /2-srv/0-hello/1-mux/serve_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | func TestSayHello(t *testing.T) { 11 | 12 | tests := []struct { 13 | url string 14 | str string 15 | }{ 16 | {"", "Hello \n"}, 17 | {"World", "Hello World\n"}, 18 | } 19 | 20 | for _, test := range tests { 21 | 22 | say := Say(test.url) 23 | 24 | if say != test.str { 25 | t.Error(fmt.Sprintf(`say != "%s"`, test.str)) 26 | } 27 | } 28 | } 29 | 30 | func Say(url string) string { 31 | 32 | res, err := http.Get("http://how-srv-hello:8080/" + url) 33 | if err != nil { 34 | panic(err) 35 | } 36 | defer res.Body.Close() 37 | 38 | body, err := ioutil.ReadAll(res.Body) 39 | if err != nil { 40 | panic(err) 41 | } 42 | s := fmt.Sprintf("%s", body) 43 | 44 | return s 45 | } 46 | -------------------------------------------------------------------------------- /2-srv/0-hello/0-default/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "testing" 8 | ) 9 | 10 | func TestSayHello(t *testing.T) { 11 | 12 | tests := []struct { 13 | url string 14 | str string 15 | }{ 16 | {"", "Hello \n"}, 17 | {"World", "Hello World\n"}, 18 | } 19 | 20 | for _, test := range tests { 21 | 22 | say := Say(test.url) 23 | 24 | if say != test.str { 25 | t.Error(fmt.Sprintf(`say != "%s"`, test.str)) 26 | } 27 | } 28 | } 29 | 30 | func Say(url string) string { 31 | 32 | res, err := http.Get("http://how-srv-hello:8080/" + url) 33 | if err != nil { 34 | panic(err) 35 | } 36 | defer res.Body.Close() 37 | 38 | body, err := ioutil.ReadAll(res.Body) 39 | if err != nil { 40 | panic(err) 41 | } 42 | s := fmt.Sprintf("%s", body) 43 | 44 | return s 45 | } 46 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | 8 | router "github.com/gorilla/mux" 9 | ) 10 | 11 | // different files are linked and compiled in alphabetical order, 12 | // so the init functions are run in that order too 13 | 14 | // app init 15 | 16 | var Listen net.Listener 17 | 18 | func init() { 19 | var err error 20 | 21 | // this will open the listen on the network's interface 22 | 23 | Listen, err = net.Listen("tcp", "how-srv-crud0:8080") 24 | if err != nil { 25 | error := fmt.Sprintf(`net.Listen("tcp", "how-srv-crud0:8080") error: %s`, err) 26 | panic(error) 27 | } 28 | } 29 | 30 | var Mux *router.Router 31 | 32 | func init() { 33 | 34 | // this will create a Gorilla Mux router 35 | 36 | Mux = router.NewRouter() 37 | } 38 | 39 | // main function 40 | 41 | func main() { 42 | 43 | if err := http.Serve(Listen, Mux); err != nil { 44 | error := fmt.Sprintf("http.Serve(Listen, Mux) error: %s", err) 45 | panic(error) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | 8 | router "github.com/gorilla/mux" 9 | ) 10 | 11 | // different files are linked and compiled in alphabetical order, 12 | // so the init functions are run in that order too 13 | 14 | // app init 15 | 16 | var Listen net.Listener 17 | 18 | func init() { 19 | var err error 20 | 21 | // this will open the listen on the network's interface 22 | 23 | Listen, err = net.Listen("tcp", "how-srv-crud1:8080") 24 | if err != nil { 25 | error := fmt.Sprintf(`net.Listen("tcp", "how-srv-crud1:8080") error: %s`, err) 26 | panic(error) 27 | } 28 | } 29 | 30 | var Mux *router.Router 31 | 32 | func init() { 33 | 34 | // this will create a Gorilla Mux router 35 | 36 | Mux = router.NewRouter() 37 | } 38 | 39 | // main function 40 | 41 | func main() { 42 | 43 | if err := http.Serve(Listen, Mux); err != nil { 44 | error := fmt.Sprintf("http.Serve(Listen, Mux) error: %s", err) 45 | panic(error) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /2-srv/4-api/0-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | 8 | router "github.com/gorilla/mux" 9 | ) 10 | 11 | // different files are linked and compiled in alphabetical order, 12 | // so the init functions are run in that order too 13 | 14 | // app init 15 | 16 | var Listen net.Listener 17 | 18 | func init() { 19 | var err error 20 | 21 | // this will open the listen on the network's interface 22 | 23 | Listen, err = net.Listen("tcp", "how-srv-api0:8080") 24 | if err != nil { 25 | error := fmt.Sprintf(`net.Listen("tcp", "how-srv-api0:8080") error: %s`, err) 26 | panic(error) 27 | } 28 | } 29 | 30 | var Mux *router.Router 31 | 32 | func init() { 33 | 34 | // this will create a Gorilla Mux router 35 | 36 | Mux = router.NewRouter() 37 | } 38 | 39 | // main function 40 | 41 | func main() { 42 | 43 | if err := http.Serve(Listen, Mux); err != nil { 44 | error := fmt.Sprintf("http.Serve(Listen, Mux) error: %s", err) 45 | panic(error) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | 8 | router "github.com/gorilla/mux" 9 | ) 10 | 11 | // different files are linked and compiled in alphabetical order, 12 | // so the init functions are run in that order too 13 | 14 | // app init 15 | 16 | var Listen net.Listener 17 | 18 | func init() { 19 | var err error 20 | 21 | // this will open the listen on the network's interface 22 | 23 | Listen, err = net.Listen("tcp", "how-srv-api1:8080") 24 | if err != nil { 25 | error := fmt.Sprintf(`net.Listen("tcp", "how-srv-api1:8080") error: %s`, err) 26 | panic(error) 27 | } 28 | } 29 | 30 | var Mux *router.Router 31 | 32 | func init() { 33 | 34 | // this will create a Gorilla Mux router 35 | 36 | Mux = router.NewRouter() 37 | } 38 | 39 | // main function 40 | 41 | func main() { 42 | 43 | if err := http.Serve(Listen, Mux); err != nil { 44 | error := fmt.Sprintf("http.Serve(Listen, Mux) error: %s", err) 45 | panic(error) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /2-srv/2-system/0-notify/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/coreos/go-systemd/daemon" 10 | ) 11 | 12 | // from https://vincent.bernat.ch/en/blog/2017-systemd-golang 13 | 14 | func notify() { 15 | 16 | daemon.SdNotify(false, daemon.SdNotifyReady) 17 | go func() { 18 | interval, err := daemon.SdWatchdogEnabled(false) 19 | if err != nil || interval == 0 { 20 | return 21 | } 22 | for { 23 | daemon.SdNotify(false, daemon.SdNotifyWatchdog) 24 | time.Sleep(interval / 3) 25 | } 26 | }() 27 | } 28 | 29 | func main() { 30 | 31 | listen, err := net.Listen("tcp", "how-srv-system0:8080") 32 | if err != nil { 33 | log.Panicf(`net.Listen("tcp", "how-srv-system0:8080") error: %s`, err) 34 | } 35 | 36 | notify() // notify must always be placed after "listen" and before "serve" 37 | 38 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 39 | 40 | w.Write([]byte("system" + "\n")) 41 | }) 42 | 43 | http.Serve(listen, nil) 44 | } 45 | -------------------------------------------------------------------------------- /2-srv/0-hello/0-default/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | func sayHello(w http.ResponseWriter, r *http.Request) { 10 | 11 | message := r.URL.Path 12 | message = strings.TrimPrefix(message, "/") 13 | message = "Hello " + message + "\n" 14 | 15 | w.Write([]byte(message)) 16 | } 17 | 18 | func init() { 19 | 20 | // this will register this handle to DefaultServeMux 21 | 22 | http.HandleFunc("/", sayHello) 23 | } 24 | 25 | func main() { 26 | 27 | // it's a good practice to name all your projects 28 | // in /etc/hosts and give them unique IPs, like: 29 | // 30 | // 127.0.0.1 localhost 31 | // 127.1.0.0 how-srv-hello 32 | // 127.1.1.0 how-srv-... 33 | // 34 | // then in a project, different services can each receive a different port 35 | 36 | // this will serve DefaultServeMux 37 | 38 | if err := http.ListenAndServe("how-srv-hello:8080", nil); err != nil { 39 | error := fmt.Sprintf(`http.ListenAndServe("how-srv-hello:8080", nil) error: %s`, err) 40 | panic(error) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /2-srv/0-hello/1-mux/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | ) 8 | 9 | // different files are linked and compiled in alphabetical order, 10 | // so the init functions are run in that order too 11 | 12 | // app init 13 | 14 | var Listen net.Listener 15 | 16 | // the following can't be called "init" otherwise it's started when testing 17 | 18 | func initListen() { 19 | var err error 20 | 21 | // this will open the listen on the network's interface 22 | 23 | Listen, err = net.Listen("tcp", "how-srv-hello:8080") 24 | if err != nil { 25 | error := fmt.Sprintf(`net.Listen("tcp", "how-srv-hello:8080") error: %s`, err) 26 | panic(error) 27 | } 28 | } 29 | 30 | var Mux *http.ServeMux 31 | 32 | func init() { 33 | 34 | // this will create a Mux handler (multiplexing handler) 35 | 36 | Mux = http.NewServeMux() 37 | } 38 | 39 | // main function 40 | 41 | func main() { 42 | 43 | initListen() 44 | 45 | if err := http.Serve(Listen, Mux); err != nil { 46 | error := fmt.Sprintf("http.Serve(Listen, Mux) error: %s", err) 47 | panic(error) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /0-ini/0-install.md: -------------------------------------------------------------------------------- 1 | 2 | **Install Golang on Linux** 3 | 4 | ``` 5 | sudo apt install golang 6 | ``` 7 | 8 | **Upgrade Golang manually** 9 | 10 | ``` 11 | # 1/ download the desired packages from (for example): 12 | # https://launchpad.net/ubuntu/+source/golang-1.14 13 | # 2/ install them: 14 | sudo dpkg -i *.deb 15 | # 3/ redirect links to the new version: 16 | sudo rm /usr/lib/go && sudo ln -s go-1.14 /usr/lib/go 17 | sudo ln -sf ../lib/go/bin/go /usr/bin/go 18 | sudo ln -sf ../lib/go/bin/gofmt /usr/bin/gofmt 19 | ``` 20 | 21 | **[Install TinyGo on Linux](https://tinygo.org/getting-started/linux/)** 22 | 23 | ``` 24 | wget https://github.com/tinygo-org/tinygo/releases/download/v0.15.0/tinygo_0.15.0_amd64.deb 25 | sudo dpkg -i tinygo_0.15.0_amd64.deb 26 | ``` 27 | 28 | **Setup your `/etc/hosts`** 29 | 30 | ``` 31 | ### How to Go ### 32 | 33 | 127.1.0.0 how-srv-hello 34 | 127.1.1.0 how-srv-simple 35 | 127.1.2.0 how-srv-system0 36 | 127.1.2.1 how-srv-system1 37 | 127.1.3.0 how-srv-crud0 38 | 127.1.3.1 how-srv-crud1 39 | 127.1.4.0 how-srv-api0 40 | 127.1.4.1 how-srv-api1 41 | ``` 42 | -------------------------------------------------------------------------------- /2-srv/1-simple/html/css/index.css: -------------------------------------------------------------------------------- 1 | /* --- fonts --- */ 2 | 3 | body, p, li, h1, h2, h3, h4, h5, h6, input, textarea, select, [contenteditable=true], 4 | .mdc-text-field__input, .mdc-floating-label { 5 | font-family: 'Josefin Sans', sans-serif; 6 | font-size: 22px; 7 | } 8 | 9 | /* --- site --- */ 10 | 11 | body { 12 | display: flex; 13 | flex-direction: column; 14 | 15 | align-items: center; 16 | justify-content: center; 17 | 18 | /* https://digitalsynopsis.com/design/beautiful-color-ui-gradients-backgrounds/ - 27 Decent */ 19 | background: linear-gradient(to right, #4ca1af 0%, #c4e0e5 100%); 20 | } 21 | 22 | section { 23 | width: 85%; 24 | padding: 1em; 25 | margin-top: 1em; 26 | 27 | background-color: rgba(255,255,255,0.75); 28 | border-radius: 0.5em; 29 | } 30 | 31 | a { 32 | text-decoration: none; 33 | } 34 | 35 | .top { 36 | margin-top: 0; 37 | } 38 | 39 | /* --- material design --- */ 40 | 41 | /* https://material.io/develop/web/ */ 42 | 43 | .mdc-elevation--z6 { 44 | box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0,0,0,.12); 45 | } 46 | -------------------------------------------------------------------------------- /2-srv/1-simple/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | 8 | router "github.com/gorilla/mux" 9 | ) 10 | 11 | // different files are linked and compiled in alphabetical order, 12 | // so the init functions are run in that order too 13 | 14 | // app init 15 | 16 | var Listen net.Listener 17 | 18 | // the following can't be called "init" otherwise it's started when testing 19 | 20 | func initListen() { 21 | var err error 22 | 23 | // this will open the listen on the network's interface 24 | 25 | Listen, err = net.Listen("tcp", "how-srv-simple:8080") 26 | if err != nil { 27 | error := fmt.Sprintf(`net.Listen("tcp", "how-srv-simple:8080") error: %s`, err) 28 | panic(error) 29 | } 30 | } 31 | 32 | var Mux *router.Router 33 | 34 | func init() { 35 | 36 | // this will create a Gorilla Mux router 37 | 38 | Mux = router.NewRouter() 39 | } 40 | 41 | // main function 42 | 43 | func main() { 44 | 45 | initListen() 46 | 47 | if err := http.Serve(Listen, Mux); err != nil { 48 | error := fmt.Sprintf("http.Serve(Listen, Mux) error: %s", err) 49 | panic(error) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /1-cmd/2-environment/usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./environment --help 4 | ./environment -help 5 | ./environment --h 6 | ./environment -h 7 | ./environment -wrong -command 8 | 9 | ./environment 10 | 11 | ./environment --word=baz --numb=100 --bool=true 12 | ./environment --word baz --numb 100 --bool=true 13 | 14 | ./environment --word=test 15 | ./environment --word test 16 | ./environment -word=test 17 | ./environment -word test 18 | ./environment --word="test space" 19 | ./environment --word=test\ space 20 | ./environment --word="test \"quotation mark\"" 21 | ./environment --word=test\ \"quotation\ mark\" 22 | 23 | ./environment -numb 1234 # decimal notation 24 | ./environment -numb 0664 # octal notation 25 | ./environment -numb 0x1234 # hexadecimal notation 26 | 27 | ./environment -bool # this bool will be "true" 28 | ./environment -bool=1 29 | ./environment -bool=0 30 | ./environment -bool=true 31 | ./environment -bool=false 32 | ./environment -bool=t 33 | ./environment -bool=f 34 | ./environment -bool=T 35 | ./environment -bool=F 36 | ./environment -bool=TRUE 37 | ./environment -bool=FALSE 38 | ./environment -bool=True 39 | ./environment -bool=False 40 | -------------------------------------------------------------------------------- /2-srv/2-system/1-socket/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "time" 7 | 8 | "github.com/coreos/go-systemd/activation" 9 | "github.com/coreos/go-systemd/daemon" 10 | ) 11 | 12 | // from https://vincent.bernat.ch/en/blog/2018-systemd-golang-socket-activation 13 | 14 | func notify() { 15 | 16 | daemon.SdNotify(false, daemon.SdNotifyReady) 17 | go func() { 18 | interval, err := daemon.SdWatchdogEnabled(false) 19 | if err != nil || interval == 0 { 20 | return 21 | } 22 | for { 23 | daemon.SdNotify(false, daemon.SdNotifyWatchdog) 24 | time.Sleep(interval / 3) 25 | } 26 | }() 27 | } 28 | 29 | func main() { 30 | 31 | listeners, err := activation.Listeners() 32 | if err != nil { 33 | log.Panicf("activation.Listeners(true) error: %s", err) 34 | } 35 | if len(listeners) != 1 { 36 | log.Panicf("unexpected number of socket activation (%d != 1)", len(listeners)) 37 | } 38 | 39 | notify() // notify must always be placed after "listen" and before "serve" 40 | 41 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 42 | 43 | w.Write([]byte("system" + "\n")) 44 | }) 45 | 46 | http.Serve(listeners[0], nil) 47 | } 48 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/data.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | 8 | "gorm.io/driver/sqlite" 9 | "gorm.io/gorm" 10 | ) 11 | 12 | // database directory init 13 | 14 | var dirData string 15 | 16 | func init() { 17 | 18 | dirData = "db" 19 | 20 | dir := filepath.Join(dirRoot, dirData) 21 | _, err := os.Stat(dir) 22 | if !os.IsNotExist(err) { 23 | return 24 | } 25 | 26 | err = os.Mkdir(dir, 0770) 27 | if err != nil { 28 | error := fmt.Sprintf(`os.Mkdir("%s", 0770) error: %s`, dir, err) 29 | panic(error) 30 | } 31 | 32 | os.Chmod(dir, 0770) // because of https://github.com/golang/go/issues/25539#issuecomment-394472286 33 | } 34 | 35 | // database structure 36 | 37 | type DBDoc struct { 38 | gorm.Model 39 | Name string 40 | Content string 41 | } 42 | 43 | // database connection 44 | 45 | var db *gorm.DB 46 | 47 | func init() { 48 | var err error 49 | 50 | db, err = gorm.Open(sqlite.Open(filepath.Join(dirRoot, dirData, "sqlite.db")), &gorm.Config{}) 51 | if err != nil { 52 | error := fmt.Sprintf(`gorm.Open(sqlite.Open("db/sqlite.db") error: %s`, err) 53 | panic(error) 54 | } 55 | 56 | db.AutoMigrate(&DBDoc{}) 57 | } 58 | -------------------------------------------------------------------------------- /1-cmd/1-hello/usage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # "--" or "-" is equivalent: 4 | 5 | ./hello --help 6 | ./hello -help 7 | 8 | # "help" or "h" is equivalent: 9 | 10 | ./hello --h 11 | ./hello -h 12 | 13 | # you get help automatically if your command is wrong: 14 | 15 | ./hello -wrong -command 16 | 17 | # this works: 18 | 19 | ./hello 20 | 21 | # "=" is optional, except for booleans 22 | 23 | ./hello --word=baz --numb=100 --bool=true 24 | ./hello --word baz --numb 100 --bool=true 25 | 26 | ./hello --word=test 27 | ./hello --word test 28 | ./hello -word=test 29 | ./hello -word test 30 | ./hello --word="test space" 31 | ./hello --word=test\ space 32 | ./hello --word="test \"quotation mark\"" 33 | ./hello --word=test\ \"quotation\ mark\" 34 | 35 | ./hello -numb 1234 # decimal notation 36 | ./hello -numb 0664 # octal notation 37 | ./hello -numb 0x1234 # hexadecimal notation 38 | 39 | # booleans only always need the "=" 40 | 41 | ./hello -bool # this bool will be "true" 42 | ./hello -bool=1 43 | ./hello -bool=0 44 | ./hello -bool=true 45 | ./hello -bool=false 46 | ./hello -bool=t 47 | ./hello -bool=f 48 | ./hello -bool=T 49 | ./hello -bool=F 50 | ./hello -bool=TRUE 51 | ./hello -bool=FALSE 52 | ./hello -bool=True 53 | ./hello -bool=False 54 | -------------------------------------------------------------------------------- /2-srv/1-simple/serve_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "testing" 8 | 9 | "github.com/google/go-cmp/cmp" 10 | ) 11 | 12 | func TestSimple(t *testing.T) { 13 | 14 | tests := []struct { 15 | url string 16 | file string 17 | str string 18 | }{ 19 | {"", "test/serve-.txt", ``}, 20 | {"aaa", "test/serve-aaa.txt", ``}, 21 | {"bbb", "test/serve-bbb.txt", ``}, 22 | } 23 | 24 | for _, test := range tests { 25 | 26 | // load expected output from files, to avoid editor formatting 27 | if binary, err := ioutil.ReadFile(test.file); err == nil { 28 | test.str = string(binary) 29 | } 30 | 31 | simple := Simple(test.url) 32 | 33 | if len(simple) != len(test.str) { 34 | t.Error(fmt.Sprintf(`len(simple) != len(test.str): %d != %d`, len(simple), len(test.str))) 35 | } 36 | if simple != test.str { 37 | t.Error(cmp.Diff(simple, test.str)) 38 | } 39 | } 40 | } 41 | 42 | func Simple(url string) string { 43 | 44 | res, err := http.Get("http://how-srv-simple:8080/" + url) 45 | if err != nil { 46 | panic(err) 47 | } 48 | defer res.Body.Close() 49 | 50 | body, err := ioutil.ReadAll(res.Body) 51 | if err != nil { 52 | panic(err) 53 | } 54 | s := fmt.Sprintf("%s", body) 55 | 56 | return s 57 | } 58 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/html/css/index.css: -------------------------------------------------------------------------------- 1 | 2 | /* --- fonts --- */ 3 | 4 | body, p, li, h1, h2, h3, h4, h5, h6, input, textarea, select, [contenteditable=true], 5 | .mdc-text-field__input, .mdc-floating-label { 6 | font-family: 'Josefin Sans', sans-serif; 7 | font-size: 22px; 8 | } 9 | 10 | /* --- site --- */ 11 | 12 | body { 13 | display: flex; 14 | flex-direction: column; 15 | 16 | align-items: center; 17 | justify-content: center; 18 | 19 | /* https://digitalsynopsis.com/design/beautiful-color-ui-gradients-backgrounds/ - 27 Decent */ 20 | background: linear-gradient(to right, #4ca1af 0%, #c4e0e5 100%); 21 | } 22 | 23 | section { 24 | width: 85%; 25 | padding: 1em; 26 | margin-top: 1em; 27 | 28 | background-color: rgba(255,255,255,0.75); 29 | border-radius: 0.5em; 30 | } 31 | 32 | a { 33 | text-decoration: none; 34 | } 35 | 36 | .top { 37 | margin-top: 0; 38 | } 39 | 40 | /* --- material design --- */ 41 | 42 | /* https://material.io/develop/web/ */ 43 | 44 | .mdc-elevation--z6 { 45 | box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0,0,0,.12); 46 | } 47 | 48 | /* --- forms --- */ 49 | 50 | form input, 51 | form textarea { 52 | width: 100%; 53 | } 54 | 55 | textarea { 56 | min-height: 5em; 57 | } 58 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/html/css/index.css: -------------------------------------------------------------------------------- 1 | 2 | /* --- fonts --- */ 3 | 4 | body, p, li, h1, h2, h3, h4, h5, h6, input, textarea, select, [contenteditable=true], 5 | .mdc-text-field__input, .mdc-floating-label { 6 | font-family: 'Josefin Sans', sans-serif; 7 | font-size: 22px; 8 | } 9 | 10 | /* --- site --- */ 11 | 12 | body { 13 | display: flex; 14 | flex-direction: column; 15 | 16 | align-items: center; 17 | justify-content: center; 18 | 19 | /* https://digitalsynopsis.com/design/beautiful-color-ui-gradients-backgrounds/ - 27 Decent */ 20 | background: linear-gradient(to right, #4ca1af 0%, #c4e0e5 100%); 21 | } 22 | 23 | section { 24 | width: 85%; 25 | padding: 1em; 26 | margin-top: 1em; 27 | 28 | background-color: rgba(255,255,255,0.75); 29 | border-radius: 0.5em; 30 | } 31 | 32 | a { 33 | text-decoration: none; 34 | } 35 | 36 | .top { 37 | margin-top: 0; 38 | } 39 | 40 | /* --- material design --- */ 41 | 42 | /* https://material.io/develop/web/ */ 43 | 44 | .mdc-elevation--z6 { 45 | box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0,0,0,.12); 46 | } 47 | 48 | /* --- forms --- */ 49 | 50 | form input, 51 | form textarea { 52 | width: 100%; 53 | } 54 | 55 | textarea { 56 | min-height: 5em; 57 | } 58 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/html/css/index.css: -------------------------------------------------------------------------------- 1 | 2 | /* --- fonts --- */ 3 | 4 | body, p, li, h1, h2, h3, h4, h5, h6, input, textarea, select, [contenteditable=true], 5 | .mdc-text-field__input, .mdc-floating-label { 6 | font-family: 'Josefin Sans', sans-serif; 7 | font-size: 22px; 8 | } 9 | 10 | /* --- site --- */ 11 | 12 | body { 13 | display: flex; 14 | flex-direction: column; 15 | 16 | align-items: center; 17 | justify-content: center; 18 | 19 | /* https://digitalsynopsis.com/design/beautiful-color-ui-gradients-backgrounds/ - 27 Decent */ 20 | background: linear-gradient(to right, #4ca1af 0%, #c4e0e5 100%); 21 | } 22 | 23 | section { 24 | width: 85%; 25 | padding: 1em; 26 | margin-top: 1em; 27 | 28 | background-color: rgba(255,255,255,0.75); 29 | border-radius: 0.5em; 30 | } 31 | 32 | a { 33 | text-decoration: none; 34 | } 35 | 36 | .top { 37 | margin-top: 0; 38 | } 39 | 40 | /* --- material design --- */ 41 | 42 | /* https://material.io/develop/web/ */ 43 | 44 | .mdc-elevation--z6 { 45 | box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0,0,0,.12); 46 | } 47 | 48 | /* --- forms --- */ 49 | 50 | form input, 51 | form textarea { 52 | width: 100%; 53 | } 54 | 55 | textarea { 56 | min-height: 5em; 57 | } 58 | -------------------------------------------------------------------------------- /2-srv/4-api/0-server/data.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | 8 | "gorm.io/driver/sqlite" 9 | "gorm.io/gorm" 10 | ) 11 | 12 | // database directory init 13 | 14 | var dirData string 15 | 16 | func init() { 17 | 18 | dirData = "db" 19 | 20 | dir := filepath.Join(dirRoot, dirData) 21 | _, err := os.Stat(dir) 22 | if !os.IsNotExist(err) { 23 | return 24 | } 25 | 26 | err = os.Mkdir(dir, 0770) 27 | if err != nil { 28 | error := fmt.Sprintf(`os.Mkdir("%s", 0770) error: %s`, dir, err) 29 | panic(error) 30 | } 31 | 32 | os.Chmod(dir, 0770) // because of https://github.com/golang/go/issues/25539#issuecomment-394472286 33 | if err != nil { 34 | error := fmt.Sprintf(`os.Chmod("%s", 0770) error: %`, dir, err) 35 | panic(error) 36 | } 37 | } 38 | 39 | // database structure 40 | 41 | type DBDoc struct { 42 | gorm.Model 43 | Name string 44 | Content string 45 | } 46 | 47 | // database connection 48 | 49 | var db *gorm.DB 50 | 51 | func init() { 52 | var err error 53 | 54 | db, err = gorm.Open(sqlite.Open(filepath.Join(dirRoot, dirData, "sqlite.db")), &gorm.Config{}) 55 | if err != nil { 56 | error := fmt.Sprintf(`gorm.Open(sqlite.Open("db/sqlite.db") error: %s`, err) 57 | panic(error) 58 | } 59 | 60 | db.AutoMigrate(&DBDoc{}) 61 | } 62 | -------------------------------------------------------------------------------- /2-srv/1-simple/serve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "html/template" 5 | "net/http" 6 | "strings" 7 | 8 | router "github.com/gorilla/mux" 9 | ) 10 | 11 | type Obj map[string]interface{} 12 | 13 | func init() { 14 | 15 | muxStatic(Mux, "/css/", "./html/css/") 16 | muxStatic(Mux, "/img/", "./html/img/") 17 | muxStatic(Mux, "/js/", "./html/js/") 18 | 19 | funcMap := template.FuncMap{ 20 | "trim": strings.Trim, 21 | } 22 | 23 | tpl, err := template.New("glob").Funcs(funcMap).ParseGlob("./html/*.html") 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 29 | tpl.ExecuteTemplate(w, "-.html", Obj{}) 30 | }) 31 | 32 | Mux.HandleFunc("/aaa", func(w http.ResponseWriter, r *http.Request) { 33 | tpl.ExecuteTemplate(w, "aaa.html", Obj{}) 34 | }) 35 | 36 | Mux.HandleFunc("/bbb", func(w http.ResponseWriter, r *http.Request) { 37 | tpl.ExecuteTemplate(w, "bbb.html", Obj{}) 38 | }) 39 | 40 | muxNotFound := func(w http.ResponseWriter, r *http.Request) { 41 | 42 | w.WriteHeader(http.StatusNotFound) 43 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 44 | } 45 | Mux.NotFoundHandler = http.HandlerFunc(muxNotFound) 46 | } 47 | 48 | func muxStatic(mux *router.Router, relativePath, root string) { 49 | mux.PathPrefix(relativePath).Handler(http.StripPrefix(relativePath, http.FileServer(http.Dir(root)))) 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | The goal of this **repository** is to teach you the "**way to Go**" with concrete examples and good practices. It could be a starting point for a company who wants to explore the possibilities of the Go language, or a support for a quick presentation of the language, or save there apps templates. 3 | 4 | Using it needs: 5 | * being introduced first to the [tour of Go](https://tour.golang.org/) 6 | * having some sense of the [`pkg` repository](https://golang.org/pkg/) 7 | * the target environment is [Ubuntu, Linux](https://ubuntu.com/) 8 | 9 | It is composed of: 10 | * 0.[`ini`](0-ini), the **documentation** 11 | * 0.**install** instructions 12 | * 1.**IDE setup** 13 | * 1.[`cmd`](1-cmd), the **command line** apps who quit when the work is done 14 | * 0.**empty** 15 | * the empty app is interesting to measure the Go language overhead 16 | * 1.**hello** 17 | * a classic, but will teach the command line options and writing styles 18 | * 2.**environment** 19 | * setup an environment to configure your app, overwrite it with the command line 20 | * 2.[`srv`](2-srv), the **services** apps who stay alive and never quit 21 | * 0.**hello** 22 | * 0.**default** 23 | * the simplest "hello world" service 24 | * 1.**mux** 25 | * same "hello world" but using the famous `gorilla/mux` package 26 | * 1.**simple** 27 | * a simple web server with HTML templates and static directories 28 | * 2.**system**, a web server started and controlled by `systemd` 29 | * 0.**notify** 30 | * a simple server that answers to *readiness* and *liveness* 31 | * 1.**socket** 32 | * a more robust server that lets `systemd` deal with sockets 33 | * 3.**crud**, a simple "create retrieve update delete" app 34 | * 0.**file**, on a file system 35 | * 1.**sql**, on SQL using [Gorm](https://gorm.io/docs/) 36 | * 4.**api**, a simple CRUD api with a server and a client 37 | * 0.**server** 38 | * 1.**client** 39 | 40 | Numbers are used in the directory names for ordering them. 41 | -------------------------------------------------------------------------------- /2-srv/1-simple/html/bbb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | 10 | Back 11 | 12 |

    BBB title

    13 | 14 |

    Donec vel semper sem. Quisque rhoncus erat efficitur, tempor arcu id, tincidunt mi. Pellentesque pretium tellus eu mi maximus laoreet. Ut non venenatis lectus. Sed felis velit, efficitur viverra tempus ac, auctor at enim. Cras eget lacus condimentum, iaculis diam vitae, aliquam velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ac magna mauris. Donec vel enim vitae tortor pellentesque bibendum. Integer ipsum tortor, porttitor id sem nec, maximus ultricies nibh. Sed ipsum lectus, pellentesque et ligula vitae, sollicitudin tincidunt erat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec risus nulla, tincidunt ut sagittis in, gravida et mauris. Integer non tincidunt nisi, vel scelerisque nunc. Sed malesuada at ipsum quis vehicula.

    15 | 16 |

    Suspendisse at accumsan mi. Nunc eu augue ut augue posuere aliquet. Suspendisse consequat tortor nec ligula pellentesque sollicitudin. Curabitur eu luctus dui. Integer facilisis eget sem ac efficitur. Proin purus justo, vestibulum quis turpis sed, vestibulum tempus mi. Praesent varius, elit id feugiat interdum, tortor nunc vulputate ligula, a egestas mauris dolor in urna. Donec mi est, consequat quis porta nec, ultrices et magna. Proin at condimentum leo, sed scelerisque velit. Vestibulum tristique, eros id mattis malesuada, est ex feugiat lorem, eu consequat arcu nulla in nisi. Praesent quam risus, pretium ut imperdiet sed, accumsan quis nulla. Nam sapien libero, tincidunt in lectus eu, efficitur commodo nulla. Mauris laoreet ex nulla, eu commodo nibh aliquam ut. Fusce sapien libero, placerat sed arcu sodales, convallis venenatis turpis. Pellentesque mattis faucibus velit, sed iaculis diam tincidunt et.

    17 | 18 | {{template ".body1" .}} 19 | 20 | 21 | -------------------------------------------------------------------------------- /2-srv/1-simple/html/aaa.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{template ".head" .}} 5 | 6 | 7 | 8 | {{template ".body0" .}} 9 | 10 | Back 11 | 12 |

    AAA title

    13 | 14 |

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sit amet ligula quam. Nulla mattis varius tincidunt. Aliquam enim leo, pharetra sed maximus gravida, porttitor sit amet augue. In hac habitasse platea dictumst. Sed elementum tortor vestibulum, consectetur neque non, dignissim augue. Cras ut massa vulputate est aliquet tempus vitae ac ipsum. Vestibulum malesuada augue vitae est consequat cursus. Quisque et sem a ex pharetra tincidunt nec non ligula. Nullam in lacus nec velit feugiat fringilla. Sed luctus sapien eu placerat accumsan. Nunc ultrices libero at sem eleifend, sit amet consectetur nulla ornare. Integer tincidunt eros tellus, vehicula interdum nulla cursus et. Nullam vel lacinia lectus, id pharetra tortor. Donec a cursus nunc.

    15 | 16 |

    Donec et velit consectetur, tempus nisi nec, aliquam urna. Vestibulum massa justo, congue a lacus nec, sodales cursus dui. Sed non dignissim turpis, non rhoncus enim. Nunc ultrices est quis eleifend mattis. Nunc nec tincidunt tellus. Ut eu massa purus. Duis in condimentum tortor. Integer congue fringilla metus, et rhoncus velit molestie a. Mauris feugiat, tellus in semper pharetra, tortor justo aliquam velit, sed vehicula eros ipsum quis turpis. Aliquam quis congue dui.

    17 | 18 |

    Sed ullamcorper, mauris ac interdum congue, turpis ex posuere erat, rhoncus aliquam dui velit sit amet quam. Praesent suscipit urna turpis, at dapibus urna viverra vitae. Vivamus auctor metus mauris, consectetur aliquam massa laoreet ac. Aliquam erat volutpat. Etiam lacinia tempus volutpat. Integer semper ante nec posuere interdum. Nulla facilisi. Pellentesque gravida, nisl in vehicula vehicula, risus nisi ultrices lacus, vel vehicula velit tellus sit amet lectus. Sed luctus sapien non nunc facilisis, vel volutpat magna iaculis.

    19 | 20 | {{template ".body1" .}} 21 | 22 | 23 | -------------------------------------------------------------------------------- /1-cmd/2-environment/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "path" 8 | "strconv" 9 | 10 | dotenv "github.com/joho/godotenv" 11 | ) 12 | 13 | // --- initialization of the app with an env file, --- 14 | // --- then overwriting parameters with the command line --- 15 | 16 | // we can put multiple "init" functions in one go file, like that: 17 | 18 | // app init 19 | 20 | func init() { 21 | 22 | // sets the current directory to where the app is located 23 | 24 | // it is useful when the app is run as a systemd service, because the 25 | // current directory for a service is "/" 26 | 27 | // /!\ when this is used, then we can't "go run" or "go test" anymore, 28 | // because in that case golang compiles in /tmp/... and runs there 29 | 30 | executable, err := os.Executable() 31 | if err != nil { 32 | error := fmt.Sprintf("os.Executable() error: %s", err) 33 | panic(error) 34 | } 35 | err = os.Chdir(path.Dir(executable)) 36 | if err != nil { 37 | error := fmt.Sprintf("os.Chdir() error: %s", err) 38 | panic(error) 39 | } 40 | } 41 | 42 | func init() { 43 | 44 | // env file load and parsing 45 | 46 | // see https://github.com/joho/godotenv 47 | 48 | dotenv.Load() 49 | 50 | if envWord := os.Getenv("word"); envWord != "" { 51 | Word = envWord 52 | } 53 | if envNumb := os.Getenv("numb"); envNumb != "" { 54 | n, err := strconv.ParseInt(envNumb, 10, 0) 55 | if err != nil { 56 | panic(err) 57 | } 58 | Numb = int(n) 59 | } 60 | if envBool := os.Getenv("bool"); envBool != "" { 61 | b, err := strconv.ParseBool(envBool) 62 | if err != nil { 63 | panic(err) 64 | } 65 | Bool = b 66 | } 67 | } 68 | 69 | // default values for command line parameters 70 | 71 | var Word string = "foo" 72 | var Numb int = 42 73 | var Bool bool = true 74 | 75 | func init() { 76 | 77 | // command line parameters setup 78 | 79 | flag.StringVar(&Word, "word", Word, "a string") 80 | flag.IntVar(&Numb, "numb", Numb, "an int") 81 | flag.BoolVar(&Bool, "bool", Bool, "a bool") 82 | 83 | flag.Parse() 84 | } 85 | 86 | // main function 87 | 88 | // entry point of the app AFTER all the init functions have run 89 | // from top to bottom in each file, and files in alphabetical order 90 | 91 | func main() { 92 | 93 | fmt.Println("word:", Word) 94 | fmt.Println("numb:", Numb) 95 | fmt.Println("bool:", Bool) 96 | } 97 | -------------------------------------------------------------------------------- /2-srv/1-simple/test/serve-bbb.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Simple server 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
    19 | 20 | 21 | Back 22 | 23 |

    BBB title

    24 | 25 |

    Donec vel semper sem. Quisque rhoncus erat efficitur, tempor arcu id, tincidunt mi. Pellentesque pretium tellus eu mi maximus laoreet. Ut non venenatis lectus. Sed felis velit, efficitur viverra tempus ac, auctor at enim. Cras eget lacus condimentum, iaculis diam vitae, aliquam velit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed ac magna mauris. Donec vel enim vitae tortor pellentesque bibendum. Integer ipsum tortor, porttitor id sem nec, maximus ultricies nibh. Sed ipsum lectus, pellentesque et ligula vitae, sollicitudin tincidunt erat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec risus nulla, tincidunt ut sagittis in, gravida et mauris. Integer non tincidunt nisi, vel scelerisque nunc. Sed malesuada at ipsum quis vehicula.

    26 | 27 |

    Suspendisse at accumsan mi. Nunc eu augue ut augue posuere aliquet. Suspendisse consequat tortor nec ligula pellentesque sollicitudin. Curabitur eu luctus dui. Integer facilisis eget sem ac efficitur. Proin purus justo, vestibulum quis turpis sed, vestibulum tempus mi. Praesent varius, elit id feugiat interdum, tortor nunc vulputate ligula, a egestas mauris dolor in urna. Donec mi est, consequat quis porta nec, ultrices et magna. Proin at condimentum leo, sed scelerisque velit. Vestibulum tristique, eros id mattis malesuada, est ex feugiat lorem, eu consequat arcu nulla in nisi. Praesent quam risus, pretium ut imperdiet sed, accumsan quis nulla. Nam sapien libero, tincidunt in lectus eu, efficitur commodo nulla. Mauris laoreet ex nulla, eu commodo nibh aliquam ut. Fusce sapien libero, placerat sed arcu sodales, convallis venenatis turpis. Pellentesque mattis faucibus velit, sed iaculis diam tincidunt et.

    28 | 29 | 30 |
    31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /2-srv/1-simple/test/serve-aaa.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Simple server 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
    19 | 20 | 21 | Back 22 | 23 |

    AAA title

    24 | 25 |

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sit amet ligula quam. Nulla mattis varius tincidunt. Aliquam enim leo, pharetra sed maximus gravida, porttitor sit amet augue. In hac habitasse platea dictumst. Sed elementum tortor vestibulum, consectetur neque non, dignissim augue. Cras ut massa vulputate est aliquet tempus vitae ac ipsum. Vestibulum malesuada augue vitae est consequat cursus. Quisque et sem a ex pharetra tincidunt nec non ligula. Nullam in lacus nec velit feugiat fringilla. Sed luctus sapien eu placerat accumsan. Nunc ultrices libero at sem eleifend, sit amet consectetur nulla ornare. Integer tincidunt eros tellus, vehicula interdum nulla cursus et. Nullam vel lacinia lectus, id pharetra tortor. Donec a cursus nunc.

    26 | 27 |

    Donec et velit consectetur, tempus nisi nec, aliquam urna. Vestibulum massa justo, congue a lacus nec, sodales cursus dui. Sed non dignissim turpis, non rhoncus enim. Nunc ultrices est quis eleifend mattis. Nunc nec tincidunt tellus. Ut eu massa purus. Duis in condimentum tortor. Integer congue fringilla metus, et rhoncus velit molestie a. Mauris feugiat, tellus in semper pharetra, tortor justo aliquam velit, sed vehicula eros ipsum quis turpis. Aliquam quis congue dui.

    28 | 29 |

    Sed ullamcorper, mauris ac interdum congue, turpis ex posuere erat, rhoncus aliquam dui velit sit amet quam. Praesent suscipit urna turpis, at dapibus urna viverra vitae. Vivamus auctor metus mauris, consectetur aliquam massa laoreet ac. Aliquam erat volutpat. Etiam lacinia tempus volutpat. Integer semper ante nec posuere interdum. Nulla facilisi. Pellentesque gravida, nisl in vehicula vehicula, risus nisi ultrices lacus, vel vehicula velit tellus sit amet lectus. Sed luctus sapien non nunc facilisis, vel volutpat magna iaculis.

    30 | 31 | 32 |
    33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/data.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "net/url" 9 | ) 10 | 11 | // database structure 12 | 13 | type DBDoc struct { 14 | ID int 15 | CreatedAt *string 16 | UpdatedAt *string 17 | DeletedAt *string 18 | Name string 19 | Content string 20 | } 21 | 22 | type Json struct { 23 | All []*DBDoc 24 | Get DBDoc 25 | Set DBDoc 26 | New DBDoc 27 | Len int 28 | Success bool 29 | Error string 30 | } 31 | 32 | // api connection 33 | 34 | var api Api 35 | 36 | type Api struct { 37 | } 38 | 39 | func (api *Api) All() ([]*DBDoc, error) { 40 | var err error 41 | var dbDocs []*DBDoc 42 | 43 | res, err0 := http.PostForm("http://how-srv-api0:8080/all", url.Values{}) 44 | if err0 != nil { 45 | return dbDocs, err0 46 | } 47 | 48 | bin, err1 := ioutil.ReadAll(res.Body) 49 | res.Body.Close() 50 | if err1 != nil { 51 | return dbDocs, err1 52 | } 53 | 54 | var obj Json 55 | err2 := json.Unmarshal(bin, &obj) 56 | if err2 != nil { 57 | return dbDocs, err2 58 | } 59 | 60 | return obj.All, err 61 | } 62 | 63 | func (api *Api) Get(name string) (*DBDoc, error) { 64 | var err error 65 | var dbDoc DBDoc 66 | 67 | res, err0 := http.PostForm(fmt.Sprintf("http://how-srv-api0:8080/get/%s", name), url.Values{}) 68 | if err0 != nil { 69 | return &dbDoc, err0 70 | } 71 | 72 | bin, err1 := ioutil.ReadAll(res.Body) 73 | res.Body.Close() 74 | if err1 != nil { 75 | return &dbDoc, err1 76 | } 77 | 78 | var obj Json 79 | err2 := json.Unmarshal(bin, &obj) 80 | if err2 != nil { 81 | return &dbDoc, err2 82 | } 83 | 84 | return &obj.Get, err 85 | } 86 | 87 | func (api *Api) Set(name string, attr Obj) (*DBDoc, error) { 88 | var err error 89 | var dbDoc DBDoc 90 | 91 | res, err0 := http.PostForm(fmt.Sprintf("http://how-srv-api0:8080/set/%s", name), url.Values{"content": {attr["content"].(string)}}) 92 | if err0 != nil { 93 | return &dbDoc, err0 94 | } 95 | 96 | bin, err1 := ioutil.ReadAll(res.Body) 97 | res.Body.Close() 98 | if err1 != nil { 99 | return &dbDoc, err1 100 | } 101 | 102 | var obj Json 103 | err2 := json.Unmarshal(bin, &obj) 104 | if err2 != nil { 105 | return &dbDoc, err2 106 | } 107 | 108 | return &obj.Set, err 109 | } 110 | 111 | func (api *Api) Del(name string) (bool, error) { 112 | var err error 113 | 114 | res, err0 := http.PostForm(fmt.Sprintf("http://how-srv-api0:8080/del/%s", name), url.Values{}) 115 | if err0 != nil { 116 | return false, err0 117 | } 118 | 119 | bin, err1 := ioutil.ReadAll(res.Body) 120 | res.Body.Close() 121 | if err1 != nil { 122 | return false, err1 123 | } 124 | 125 | var obj Json 126 | err2 := json.Unmarshal(bin, &obj) 127 | if err2 != nil { 128 | return false, err2 129 | } 130 | 131 | if obj.Success != true { 132 | return false, err 133 | } 134 | 135 | return true, err 136 | } 137 | 138 | func (api *Api) New(name string, attr Obj) (*DBDoc, error) { 139 | var err error 140 | var dbDoc DBDoc 141 | 142 | res, err0 := http.PostForm(fmt.Sprintf("http://how-srv-api0:8080/new/%s", name), url.Values{"content": {attr["content"].(string)}}) 143 | if err0 != nil { 144 | return &dbDoc, err0 145 | } 146 | 147 | bin, err1 := ioutil.ReadAll(res.Body) 148 | res.Body.Close() 149 | if err1 != nil { 150 | return &dbDoc, err1 151 | } 152 | 153 | var obj Json 154 | err2 := json.Unmarshal(bin, &obj) 155 | if err2 != nil { 156 | return &dbDoc, err2 157 | } 158 | 159 | return &obj.New, err 160 | } 161 | -------------------------------------------------------------------------------- /2-srv/4-api/0-server/serve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "strings" 8 | 9 | router "github.com/gorilla/mux" 10 | ) 11 | 12 | type Obj map[string]interface{} 13 | 14 | func init() { 15 | 16 | Mux.HandleFunc("/all", func(w http.ResponseWriter, r *http.Request) { 17 | 18 | { 19 | 20 | var dbDocs []DBDoc 21 | db.Find(&dbDocs) 22 | 23 | o := Obj{} 24 | o["all"] = dbDocs 25 | o["len"] = len(dbDocs) 26 | b, _ := json.Marshal(o) 27 | w.Write(b) 28 | } 29 | }) 30 | 31 | Mux.HandleFunc("/get/{name}", func(w http.ResponseWriter, r *http.Request) { 32 | route := router.Vars(r) 33 | 34 | { 35 | name := muxPath(route["name"]) 36 | 37 | var dbDoc DBDoc 38 | db.First(&dbDoc, "name = ?", name) 39 | 40 | if dbDoc.ID == 0 { 41 | w.WriteHeader(http.StatusNotFound) 42 | error := fmt.Sprintf("document '%s' does not exist", name) 43 | 44 | o := Obj{} 45 | o["error"] = error 46 | b, _ := json.Marshal(o) 47 | w.Write(b) 48 | 49 | return 50 | } 51 | 52 | o := Obj{} 53 | o["get"] = dbDoc 54 | b, _ := json.Marshal(o) 55 | w.Write(b) 56 | } 57 | }) 58 | 59 | Mux.HandleFunc("/set/{name}", func(w http.ResponseWriter, r *http.Request) { 60 | route := router.Vars(r) 61 | 62 | { 63 | r.ParseForm() 64 | 65 | name := muxPath(route["name"]) 66 | content := r.FormValue("content") 67 | 68 | var dbDoc DBDoc 69 | db.First(&dbDoc, "name = ?", name) 70 | 71 | if dbDoc.ID == 0 { 72 | w.WriteHeader(http.StatusNotFound) 73 | error := fmt.Sprintf("document '%s' does not exist", name) 74 | 75 | o := Obj{} 76 | o["error"] = error 77 | b, _ := json.Marshal(o) 78 | w.Write(b) 79 | 80 | return 81 | } 82 | 83 | db.Model(&dbDoc).Update("content", content) 84 | 85 | db.First(&dbDoc, "name = ?", name) 86 | 87 | o := Obj{} 88 | o["success"] = true 89 | o["set"] = dbDoc 90 | b, _ := json.Marshal(o) 91 | w.Write(b) 92 | } 93 | }) 94 | 95 | Mux.HandleFunc("/del/{name}", func(w http.ResponseWriter, r *http.Request) { 96 | route := router.Vars(r) 97 | 98 | { 99 | name := muxPath(route["name"]) 100 | 101 | var dbDoc DBDoc 102 | db.First(&dbDoc, "name = ?", name) 103 | 104 | if dbDoc.ID == 0 { 105 | w.WriteHeader(http.StatusNotFound) 106 | error := fmt.Sprintf("document '%s' does not exist", name) 107 | 108 | o := Obj{} 109 | o["error"] = error 110 | b, _ := json.Marshal(o) 111 | w.Write(b) 112 | 113 | return 114 | } 115 | 116 | db.Delete(&dbDoc, dbDoc.ID) 117 | 118 | o := Obj{} 119 | o["success"] = true 120 | b, _ := json.Marshal(o) 121 | w.Write(b) 122 | } 123 | }) 124 | 125 | Mux.HandleFunc("/new/{name}", func(w http.ResponseWriter, r *http.Request) { 126 | route := router.Vars(r) 127 | 128 | { 129 | r.ParseForm() 130 | 131 | name := muxPath(route["name"]) 132 | content := r.FormValue("content") 133 | 134 | var dbDoc DBDoc 135 | db.First(&dbDoc, "name = ?", name) 136 | 137 | if dbDoc.ID != 0 { 138 | error := fmt.Sprintf("document '%s' already exists", name) 139 | 140 | o := Obj{} 141 | o["error"] = error 142 | b, _ := json.Marshal(o) 143 | w.Write(b) 144 | 145 | return 146 | } 147 | 148 | db.Create(&DBDoc{Name: name, Content: content}) 149 | 150 | db.First(&dbDoc, "name = ?", name) 151 | 152 | o := Obj{} 153 | o["success"] = true 154 | o["new"] = dbDoc 155 | b, _ := json.Marshal(o) 156 | w.Write(b) 157 | } 158 | }) 159 | 160 | muxNotFound := func(w http.ResponseWriter, r *http.Request) { 161 | 162 | w.WriteHeader(http.StatusBadRequest) 163 | } 164 | Mux.NotFoundHandler = http.HandlerFunc(muxNotFound) 165 | } 166 | 167 | func muxStatic(mux *router.Router, relativePath, root string) { 168 | mux.PathPrefix(relativePath).Handler(http.StripPrefix(relativePath, http.FileServer(http.Dir(root)))) 169 | } 170 | 171 | func muxError(s string) { 172 | fmt.Println(s) 173 | } 174 | 175 | func muxPath(s string) string { 176 | return strings.ReplaceAll(s, "/", "-") 177 | } 178 | -------------------------------------------------------------------------------- /2-srv/3-crud/1-sql/serve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "html/template" 6 | "net/http" 7 | "path" 8 | "strings" 9 | 10 | router "github.com/gorilla/mux" 11 | ) 12 | 13 | type Obj map[string]interface{} 14 | 15 | type Doc struct { 16 | URL string 17 | Name string 18 | Content string 19 | } 20 | 21 | func init() { 22 | 23 | muxStatic(Mux, "/css/", "./html/css/") 24 | muxStatic(Mux, "/img/", "./html/img/") 25 | muxStatic(Mux, "/js/", "./html/js/") 26 | 27 | funcMap := template.FuncMap{ 28 | "trim": strings.Trim, 29 | } 30 | 31 | tpl, err := template.New("glob").Funcs(funcMap).ParseGlob("./html/*.html") 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 37 | 38 | tpl.ExecuteTemplate(w, "-.html", Obj{}) 39 | }) 40 | 41 | Mux.HandleFunc("/all", func(w http.ResponseWriter, r *http.Request) { 42 | 43 | if r.Method == "GET" { 44 | 45 | var dbDocs []DBDoc 46 | db.Find(&dbDocs) 47 | 48 | docs := make(map[string]Doc) 49 | 50 | for _, dbDoc := range dbDocs { 51 | 52 | docs[dbDoc.Name] = Doc{ 53 | URL: path.Join("/", "get", muxPath(dbDoc.Name)), 54 | Name: dbDoc.Name, 55 | } 56 | } 57 | 58 | o := Obj{} 59 | o["Docs"] = docs 60 | tpl.ExecuteTemplate(w, "all.html", o) 61 | } 62 | 63 | if r.Method == "POST" { 64 | url := path.Join("/all") 65 | http.Redirect(w, r, url, http.StatusSeeOther) 66 | } 67 | }) 68 | 69 | Mux.HandleFunc("/get/{name}", func(w http.ResponseWriter, r *http.Request) { 70 | route := router.Vars(r) 71 | 72 | if r.Method == "GET" { 73 | name := muxPath(route["name"]) 74 | 75 | var dbDoc DBDoc 76 | db.First(&dbDoc, "name = ?", name) 77 | 78 | if dbDoc.ID == 0 { 79 | w.WriteHeader(http.StatusNotFound) 80 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 81 | return 82 | } 83 | 84 | var doc Doc 85 | doc.Name = dbDoc.Name 86 | doc.Content = dbDoc.Content 87 | 88 | o := Obj{} 89 | o["Doc"] = doc 90 | tpl.ExecuteTemplate(w, "get.html", o) 91 | } 92 | 93 | if r.Method == "POST" { 94 | url := path.Join("/get") 95 | http.Redirect(w, r, url, http.StatusSeeOther) 96 | } 97 | }) 98 | 99 | Mux.HandleFunc("/set/{name}", func(w http.ResponseWriter, r *http.Request) { 100 | route := router.Vars(r) 101 | 102 | if r.Method == "GET" { 103 | name := muxPath(route["name"]) 104 | 105 | var dbDoc DBDoc 106 | db.First(&dbDoc, "name = ?", name) 107 | 108 | if dbDoc.ID == 0 { 109 | w.WriteHeader(http.StatusNotFound) 110 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 111 | return 112 | } 113 | 114 | var doc Doc 115 | doc.Name = dbDoc.Name 116 | doc.Content = dbDoc.Content 117 | 118 | o := Obj{} 119 | o["Doc"] = doc 120 | tpl.ExecuteTemplate(w, "set.html", o) 121 | } 122 | 123 | if r.Method == "POST" { 124 | r.ParseForm() 125 | 126 | name := muxPath(r.FormValue("name")) 127 | content := r.FormValue("content") 128 | 129 | var dbDoc DBDoc 130 | db.First(&dbDoc, "name = ?", name) 131 | 132 | if dbDoc.ID == 0 { 133 | error := fmt.Sprintf("document '%s' does not exist", name) 134 | muxError(error) 135 | url := path.Join("/set", name) 136 | http.Redirect(w, r, url, http.StatusSeeOther) 137 | return 138 | } 139 | 140 | db.Model(&dbDoc).Update("content", content) 141 | 142 | url := path.Join("/get", name) 143 | http.Redirect(w, r, url, http.StatusSeeOther) 144 | } 145 | }) 146 | 147 | Mux.HandleFunc("/del/{name}", func(w http.ResponseWriter, r *http.Request) { 148 | route := router.Vars(r) 149 | 150 | if r.Method == "GET" { 151 | name := muxPath(route["name"]) 152 | 153 | var dbDoc DBDoc 154 | db.First(&dbDoc, "name = ?", name) 155 | 156 | if dbDoc.ID == 0 { 157 | w.WriteHeader(http.StatusNotFound) 158 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 159 | return 160 | } 161 | 162 | var doc Doc 163 | doc.Name = name 164 | doc.Content = dbDoc.Content 165 | 166 | o := Obj{} 167 | o["Doc"] = doc 168 | tpl.ExecuteTemplate(w, "del.html", o) 169 | } 170 | 171 | if r.Method == "POST" { 172 | name := muxPath(route["name"]) 173 | 174 | var dbDoc DBDoc 175 | db.First(&dbDoc, "name = ?", name) 176 | 177 | if dbDoc.ID == 0 { 178 | error := fmt.Sprintf("document '%s' does not exist", name) 179 | muxError(error) 180 | url := path.Join("/del", name) 181 | http.Redirect(w, r, url, http.StatusSeeOther) 182 | return 183 | } 184 | 185 | db.Delete(&dbDoc, dbDoc.ID) 186 | 187 | url := path.Join("/all") 188 | http.Redirect(w, r, url, http.StatusSeeOther) 189 | } 190 | }) 191 | 192 | Mux.HandleFunc("/new", func(w http.ResponseWriter, r *http.Request) { 193 | 194 | if r.Method == "GET" { 195 | tpl.ExecuteTemplate(w, "new.html", Obj{}) 196 | } 197 | 198 | if r.Method == "POST" { 199 | r.ParseForm() 200 | 201 | name := muxPath(r.FormValue("name")) 202 | content := r.FormValue("content") 203 | 204 | var dbDoc DBDoc 205 | db.First(&dbDoc, "name = ?", name) 206 | 207 | if dbDoc.ID != 0 { 208 | error := fmt.Sprintf("document '%s' already exists", name) 209 | muxError(error) 210 | url := path.Join("/new") 211 | http.Redirect(w, r, url, http.StatusSeeOther) 212 | return 213 | } 214 | 215 | db.Create(&DBDoc{Name: name, Content: content}) 216 | 217 | url := path.Join("/get", name) 218 | http.Redirect(w, r, url, http.StatusSeeOther) 219 | } 220 | }) 221 | 222 | muxNotFound := func(w http.ResponseWriter, r *http.Request) { 223 | 224 | w.WriteHeader(http.StatusNotFound) 225 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 226 | } 227 | Mux.NotFoundHandler = http.HandlerFunc(muxNotFound) 228 | } 229 | 230 | func muxStatic(mux *router.Router, relativePath, root string) { 231 | mux.PathPrefix(relativePath).Handler(http.StripPrefix(relativePath, http.FileServer(http.Dir(root)))) 232 | } 233 | 234 | func muxError(s string) { 235 | fmt.Println(s) 236 | } 237 | 238 | func muxPath(s string) string { 239 | return strings.ReplaceAll(s, "/", "-") 240 | } 241 | -------------------------------------------------------------------------------- /2-srv/3-crud/0-file/serve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "html/template" 6 | "io/ioutil" 7 | "net/http" 8 | "os" 9 | "path" 10 | "path/filepath" 11 | "strings" 12 | 13 | router "github.com/gorilla/mux" 14 | ) 15 | 16 | type Obj map[string]interface{} 17 | 18 | type Doc struct { 19 | URL string 20 | Name string 21 | Content string 22 | } 23 | 24 | func init() { 25 | 26 | muxStatic(Mux, "/css/", "./html/css/") 27 | muxStatic(Mux, "/img/", "./html/img/") 28 | muxStatic(Mux, "/js/", "./html/js/") 29 | 30 | funcMap := template.FuncMap{ 31 | "trim": strings.Trim, 32 | } 33 | 34 | tpl, err := template.New("glob").Funcs(funcMap).ParseGlob("./html/*.html") 35 | if err != nil { 36 | panic(err) 37 | } 38 | 39 | Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 40 | 41 | tpl.ExecuteTemplate(w, "-.html", Obj{}) 42 | }) 43 | 44 | Mux.HandleFunc("/all", func(w http.ResponseWriter, r *http.Request) { 45 | 46 | if r.Method == "GET" { 47 | 48 | read := filepath.Join(dirRoot, dirData) 49 | dirs, err := ioutil.ReadDir(read) 50 | if err != nil { 51 | error := fmt.Sprintf("can't read directory '%s'", read) 52 | muxError(error) 53 | url := path.Join("/") 54 | http.Redirect(w, r, url, http.StatusSeeOther) 55 | return 56 | } 57 | 58 | docs := make(map[string]Doc) 59 | 60 | for _, dir := range dirs { 61 | 62 | docs[dir.Name()] = Doc{ 63 | URL: path.Join("/", "get", muxPath(dir.Name())), 64 | Name: dir.Name(), 65 | } 66 | } 67 | 68 | o := Obj{} 69 | o["Docs"] = docs 70 | tpl.ExecuteTemplate(w, "all.html", o) 71 | } 72 | 73 | if r.Method == "POST" { 74 | url := path.Join("/all") 75 | http.Redirect(w, r, url, http.StatusSeeOther) 76 | } 77 | }) 78 | 79 | Mux.HandleFunc("/get/{name}", func(w http.ResponseWriter, r *http.Request) { 80 | route := router.Vars(r) 81 | 82 | if r.Method == "GET" { 83 | name := muxPath(route["name"]) 84 | 85 | dir := filepath.Join(dirRoot, dirData, name) 86 | _, err := os.Stat(dir) 87 | 88 | if os.IsNotExist(err) { 89 | w.WriteHeader(http.StatusNotFound) 90 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 91 | return 92 | } 93 | 94 | var doc Doc 95 | doc.Name = name 96 | 97 | if binary, err := ioutil.ReadFile(filepath.Join(dir, "content.html")); err == nil { 98 | doc.Content = string(binary) 99 | } 100 | 101 | o := Obj{} 102 | o["Doc"] = doc 103 | tpl.ExecuteTemplate(w, "get.html", o) 104 | } 105 | 106 | if r.Method == "POST" { 107 | url := path.Join("/get") 108 | http.Redirect(w, r, url, http.StatusSeeOther) 109 | } 110 | }) 111 | 112 | Mux.HandleFunc("/set/{name}", func(w http.ResponseWriter, r *http.Request) { 113 | route := router.Vars(r) 114 | 115 | if r.Method == "GET" { 116 | name := muxPath(route["name"]) 117 | 118 | dir := filepath.Join(dirRoot, dirData, name) 119 | _, err := os.Stat(dir) 120 | 121 | if os.IsNotExist(err) { 122 | w.WriteHeader(http.StatusNotFound) 123 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 124 | return 125 | } 126 | 127 | var doc Doc 128 | doc.Name = name 129 | 130 | if binary, err := ioutil.ReadFile(filepath.Join(dir, "content.html")); err == nil { 131 | doc.Content = string(binary) 132 | } 133 | 134 | o := Obj{} 135 | o["Doc"] = doc 136 | tpl.ExecuteTemplate(w, "set.html", o) 137 | } 138 | 139 | if r.Method == "POST" { 140 | r.ParseForm() 141 | 142 | name := muxPath(r.FormValue("name")) 143 | content := r.FormValue("content") 144 | 145 | dir := filepath.Join(dirRoot, dirData, name) 146 | _, err := os.Stat(dir) 147 | 148 | if os.IsNotExist(err) { 149 | error := fmt.Sprintf("document '%s' does not exist", name) 150 | muxError(error) 151 | url := path.Join("/set", name) 152 | http.Redirect(w, r, url, http.StatusSeeOther) 153 | return 154 | } 155 | 156 | f, err := os.Create(filepath.Join(dir, "content.html")) 157 | defer f.Close() 158 | 159 | if err != nil { 160 | url := path.Join("/set", name) 161 | http.Redirect(w, r, url, http.StatusSeeOther) 162 | return 163 | } 164 | 165 | many, err := fmt.Fprint(f, content) 166 | if err != nil || many != len(content) { 167 | // error 168 | } else { 169 | // success 170 | } 171 | 172 | url := path.Join("/get", name) 173 | http.Redirect(w, r, url, http.StatusSeeOther) 174 | } 175 | }) 176 | 177 | Mux.HandleFunc("/del/{name}", func(w http.ResponseWriter, r *http.Request) { 178 | route := router.Vars(r) 179 | 180 | if r.Method == "GET" { 181 | name := muxPath(route["name"]) 182 | 183 | dir := filepath.Join(dirRoot, dirData, name) 184 | _, err := os.Stat(dir) 185 | 186 | if os.IsNotExist(err) { 187 | w.WriteHeader(http.StatusNotFound) 188 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 189 | return 190 | } 191 | 192 | var doc Doc 193 | doc.Name = name 194 | 195 | if binary, err := ioutil.ReadFile(filepath.Join(dir, "content.html")); err == nil { 196 | doc.Content = string(binary) 197 | } 198 | 199 | o := Obj{} 200 | o["Doc"] = doc 201 | tpl.ExecuteTemplate(w, "del.html", o) 202 | } 203 | 204 | if r.Method == "POST" { 205 | name := muxPath(route["name"]) 206 | 207 | dir := filepath.Join(dirRoot, dirData, name) 208 | _, err := os.Stat(dir) 209 | 210 | if os.IsNotExist(err) { 211 | error := fmt.Sprintf("document '%s' does not exist", name) 212 | muxError(error) 213 | url := path.Join("/del", name) 214 | http.Redirect(w, r, url, http.StatusSeeOther) 215 | return 216 | } 217 | 218 | os.Remove(filepath.Join(dir, "content.html")) 219 | os.Remove(dir) 220 | 221 | url := path.Join("/all") 222 | http.Redirect(w, r, url, http.StatusSeeOther) 223 | } 224 | }) 225 | 226 | Mux.HandleFunc("/new", func(w http.ResponseWriter, r *http.Request) { 227 | 228 | if r.Method == "GET" { 229 | tpl.ExecuteTemplate(w, "new.html", Obj{}) 230 | } 231 | 232 | if r.Method == "POST" { 233 | r.ParseForm() 234 | 235 | name := muxPath(r.FormValue("name")) 236 | content := r.FormValue("content") 237 | 238 | dir := filepath.Join(dirRoot, dirData, name) 239 | _, err := os.Stat(dir) 240 | 241 | if !os.IsNotExist(err) { 242 | error := fmt.Sprintf("document '%s' already exists", name) 243 | muxError(error) 244 | url := path.Join("/new") 245 | http.Redirect(w, r, url, http.StatusSeeOther) 246 | return 247 | } 248 | 249 | err = os.MkdirAll(dir, 0770) 250 | 251 | if err != nil { 252 | url := path.Join("/new") 253 | http.Redirect(w, r, url, http.StatusSeeOther) 254 | return 255 | } 256 | 257 | os.Chmod(dir, 0770) 258 | if filepath.Dir(dir) != "." { 259 | os.Chmod(filepath.Dir(dir), 0770) 260 | } 261 | 262 | { 263 | f, err := os.Create(filepath.Join(dir, "content.html")) 264 | defer f.Close() 265 | 266 | if err != nil { 267 | url := path.Join("/new") 268 | http.Redirect(w, r, url, http.StatusSeeOther) 269 | return 270 | } 271 | 272 | many, err := fmt.Fprint(f, content) 273 | if err != nil || many != len(content) { 274 | // error 275 | } else { 276 | // success 277 | } 278 | } 279 | 280 | url := path.Join("/get", name) 281 | http.Redirect(w, r, url, http.StatusSeeOther) 282 | } 283 | }) 284 | 285 | muxNotFound := func(w http.ResponseWriter, r *http.Request) { 286 | 287 | w.WriteHeader(http.StatusNotFound) 288 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 289 | } 290 | Mux.NotFoundHandler = http.HandlerFunc(muxNotFound) 291 | } 292 | 293 | func muxStatic(mux *router.Router, relativePath, root string) { 294 | mux.PathPrefix(relativePath).Handler(http.StripPrefix(relativePath, http.FileServer(http.Dir(root)))) 295 | } 296 | 297 | func muxError(s string) { 298 | fmt.Println(s) 299 | } 300 | 301 | func muxPath(s string) string { 302 | return strings.ReplaceAll(s, "/", "-") 303 | } 304 | -------------------------------------------------------------------------------- /2-srv/4-api/1-client/serve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "html/template" 7 | "net/http" 8 | "path" 9 | "strings" 10 | 11 | router "github.com/gorilla/mux" 12 | ) 13 | 14 | type Obj map[string]interface{} 15 | 16 | type Doc struct { 17 | URL string 18 | Name string 19 | Content string 20 | } 21 | 22 | func init() { 23 | 24 | muxStatic(Mux, "/css/", "./html/css/") 25 | muxStatic(Mux, "/img/", "./html/img/") 26 | muxStatic(Mux, "/js/", "./html/js/") 27 | 28 | funcMap := template.FuncMap{ 29 | "trim": strings.Trim, 30 | } 31 | 32 | tpl, err := template.New("glob").Funcs(funcMap).ParseGlob("./html/*.html") 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 38 | 39 | tpl.ExecuteTemplate(w, "-.html", Obj{}) 40 | }) 41 | 42 | Mux.HandleFunc("/all", func(w http.ResponseWriter, r *http.Request) { 43 | 44 | if r.Method == "GET" { 45 | 46 | dbDocs, err := api.All() 47 | 48 | if err != nil { 49 | muxError(err) 50 | w.WriteHeader(http.StatusInternalServerError) 51 | tpl.ExecuteTemplate(w, "500.html", Obj{}) 52 | return 53 | } 54 | 55 | docs := make(map[string]Doc) 56 | 57 | for _, dbDoc := range dbDocs { 58 | 59 | docs[dbDoc.Name] = Doc{ 60 | URL: path.Join("/", "get", muxPath(dbDoc.Name)), 61 | Name: dbDoc.Name, 62 | } 63 | } 64 | 65 | o := Obj{} 66 | o["Docs"] = docs 67 | tpl.ExecuteTemplate(w, "all.html", o) 68 | } 69 | 70 | if r.Method == "POST" { 71 | url := path.Join("/all") 72 | http.Redirect(w, r, url, http.StatusSeeOther) 73 | } 74 | }) 75 | 76 | Mux.HandleFunc("/get/{name}", func(w http.ResponseWriter, r *http.Request) { 77 | route := router.Vars(r) 78 | 79 | if r.Method == "GET" { 80 | name := muxPath(route["name"]) 81 | 82 | dbDoc, err := api.Get(name) 83 | 84 | if err != nil { 85 | muxError(err) 86 | w.WriteHeader(http.StatusInternalServerError) 87 | tpl.ExecuteTemplate(w, "500.html", Obj{}) 88 | return 89 | } 90 | if dbDoc.ID == 0 { 91 | w.WriteHeader(http.StatusNotFound) 92 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 93 | return 94 | } 95 | 96 | var doc Doc 97 | doc.Name = dbDoc.Name 98 | doc.Content = dbDoc.Content 99 | 100 | o := Obj{} 101 | o["Doc"] = doc 102 | tpl.ExecuteTemplate(w, "get.html", o) 103 | } 104 | 105 | if r.Method == "POST" { 106 | url := path.Join("/get") 107 | http.Redirect(w, r, url, http.StatusSeeOther) 108 | } 109 | }) 110 | 111 | Mux.HandleFunc("/set/{name}", func(w http.ResponseWriter, r *http.Request) { 112 | route := router.Vars(r) 113 | 114 | if r.Method == "GET" { 115 | name := muxPath(route["name"]) 116 | 117 | dbDoc, err := api.Get(name) 118 | 119 | if err != nil { 120 | muxError(err) 121 | w.WriteHeader(http.StatusInternalServerError) 122 | tpl.ExecuteTemplate(w, "500.html", Obj{}) 123 | return 124 | } 125 | if dbDoc.ID == 0 { 126 | w.WriteHeader(http.StatusNotFound) 127 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 128 | return 129 | } 130 | 131 | var doc Doc 132 | doc.Name = dbDoc.Name 133 | doc.Content = dbDoc.Content 134 | 135 | o := Obj{} 136 | o["Doc"] = doc 137 | tpl.ExecuteTemplate(w, "set.html", o) 138 | } 139 | 140 | if r.Method == "POST" { 141 | r.ParseForm() 142 | 143 | name := muxPath(r.FormValue("name")) 144 | content := r.FormValue("content") 145 | 146 | dbDoc0, err0 := api.Get(name) 147 | 148 | if err0 != nil { 149 | muxError(err0) 150 | w.WriteHeader(http.StatusInternalServerError) 151 | tpl.ExecuteTemplate(w, "500.html", Obj{}) 152 | return 153 | } 154 | if dbDoc0.ID == 0 { 155 | err := errors.New(fmt.Sprintf("document '%s' does not exist", name)) 156 | muxError(err) 157 | url := path.Join("/set", name) 158 | http.Redirect(w, r, url, http.StatusSeeOther) 159 | return 160 | } 161 | 162 | dbDoc1, err1 := api.Set(name, Obj{"content": content}) 163 | 164 | if err1 != nil { 165 | muxError(err1) 166 | w.WriteHeader(http.StatusInternalServerError) 167 | tpl.ExecuteTemplate(w, "500.html", Obj{}) 168 | return 169 | } 170 | if dbDoc1.ID == 0 { 171 | err := errors.New(fmt.Sprintf("modification of document '%s' impossible", name)) 172 | muxError(err) 173 | url := path.Join("/set", name) 174 | http.Redirect(w, r, url, http.StatusSeeOther) 175 | return 176 | } 177 | 178 | url := path.Join("/get", name) 179 | http.Redirect(w, r, url, http.StatusSeeOther) 180 | } 181 | }) 182 | 183 | Mux.HandleFunc("/del/{name}", func(w http.ResponseWriter, r *http.Request) { 184 | route := router.Vars(r) 185 | 186 | if r.Method == "GET" { 187 | name := muxPath(route["name"]) 188 | 189 | dbDoc, err := api.Get(name) 190 | 191 | if err != nil { 192 | muxError(err) 193 | w.WriteHeader(http.StatusInternalServerError) 194 | tpl.ExecuteTemplate(w, "500.html", Obj{}) 195 | return 196 | } 197 | if dbDoc.ID == 0 { 198 | err := errors.New(fmt.Sprintf("can't delete '%s'", name)) 199 | muxError(err) 200 | w.WriteHeader(http.StatusNotFound) 201 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 202 | return 203 | } 204 | 205 | var doc Doc 206 | doc.Name = name 207 | doc.Content = dbDoc.Content 208 | 209 | o := Obj{} 210 | o["Doc"] = doc 211 | tpl.ExecuteTemplate(w, "del.html", o) 212 | } 213 | 214 | if r.Method == "POST" { 215 | name := muxPath(route["name"]) 216 | 217 | dbDoc0, err0 := api.Get(name) 218 | 219 | if err0 != nil { 220 | muxError(err0) 221 | w.WriteHeader(http.StatusInternalServerError) 222 | tpl.ExecuteTemplate(w, "500.html", Obj{}) 223 | return 224 | } 225 | if dbDoc0.ID == 0 { 226 | err := errors.New(fmt.Sprintf("document '%s' does not exist", name)) 227 | muxError(err) 228 | url := path.Join("/del", name) 229 | http.Redirect(w, r, url, http.StatusSeeOther) 230 | return 231 | } 232 | 233 | ok, err1 := api.Del(name) 234 | 235 | if err1 != nil { 236 | muxError(err1) 237 | w.WriteHeader(http.StatusInternalServerError) 238 | tpl.ExecuteTemplate(w, "500.html", Obj{}) 239 | return 240 | } 241 | if ok != true { 242 | err := errors.New(fmt.Sprintf("deletion of document '%s' impossible", name)) 243 | muxError(err) 244 | url := path.Join("/set", name) 245 | http.Redirect(w, r, url, http.StatusSeeOther) 246 | return 247 | } 248 | 249 | url := path.Join("/all") 250 | http.Redirect(w, r, url, http.StatusSeeOther) 251 | } 252 | }) 253 | 254 | Mux.HandleFunc("/new", func(w http.ResponseWriter, r *http.Request) { 255 | 256 | if r.Method == "GET" { 257 | tpl.ExecuteTemplate(w, "new.html", Obj{}) 258 | } 259 | 260 | if r.Method == "POST" { 261 | r.ParseForm() 262 | 263 | name := muxPath(r.FormValue("name")) 264 | content := r.FormValue("content") 265 | 266 | dbDoc0, err0 := api.Get(name) 267 | 268 | if err0 != nil { 269 | muxError(err0) 270 | w.WriteHeader(http.StatusInternalServerError) 271 | tpl.ExecuteTemplate(w, "500.html", Obj{}) 272 | return 273 | } 274 | if dbDoc0.ID != 0 { 275 | err := errors.New(fmt.Sprintf("document '%s' already exists", name)) 276 | muxError(err) 277 | url := path.Join("/new") 278 | http.Redirect(w, r, url, http.StatusSeeOther) 279 | return 280 | } 281 | 282 | dbDoc1, err1 := api.New(name, Obj{"content": content}) 283 | 284 | if err1 != nil { 285 | muxError(err1) 286 | w.WriteHeader(http.StatusInternalServerError) 287 | tpl.ExecuteTemplate(w, "500.html", Obj{}) 288 | return 289 | } 290 | if dbDoc1.ID == 0 { 291 | err := errors.New(fmt.Sprintf("creation of document '%s' impossible", name)) 292 | muxError(err) 293 | url := path.Join("/new", name) 294 | http.Redirect(w, r, url, http.StatusSeeOther) 295 | return 296 | } 297 | 298 | url := path.Join("/get", name) 299 | http.Redirect(w, r, url, http.StatusSeeOther) 300 | } 301 | }) 302 | 303 | muxNotFound := func(w http.ResponseWriter, r *http.Request) { 304 | 305 | w.WriteHeader(http.StatusNotFound) 306 | tpl.ExecuteTemplate(w, "404.html", Obj{}) 307 | } 308 | Mux.NotFoundHandler = http.HandlerFunc(muxNotFound) 309 | } 310 | 311 | func muxStatic(mux *router.Router, relativePath, root string) { 312 | mux.PathPrefix(relativePath).Handler(http.StripPrefix(relativePath, http.FileServer(http.Dir(root)))) 313 | } 314 | 315 | func muxError(err error) { 316 | fmt.Println(err.Error()) 317 | } 318 | 319 | func muxPath(s string) string { 320 | return strings.ReplaceAll(s, "/", "-") 321 | } 322 | --------------------------------------------------------------------------------