├── 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 |
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 | {{.Doc.Content}}
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 | {{.Doc.Content}}
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 | {{.Doc.Content}}
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 |
--------------------------------------------------------------------------------