├── .gitignore
├── LICENSE
├── README.md
├── lesson1
└── hello.go
├── lesson2
├── functions
│ └── functions.go
├── looping
│ └── looping.go
├── main.go
└── variables
│ └── variables.go
├── lesson3
├── main.go
└── public
│ └── index.html
├── lesson4
├── .gitignore
├── data.sql
└── main.go
├── lesson5
├── .gitignore
├── data.sql
├── main.go
└── templates
│ ├── books.tmpl
│ └── layout.tmpl
├── lesson6
├── data.sql
├── main.go
└── templates
│ ├── books.tmpl
│ ├── create.tmpl
│ └── layout.tmpl
├── lesson7
├── main.go
└── public
│ └── login
│ └── index.html
├── lesson8
├── main.go
└── public
│ └── login
│ └── index.html
└── lesson9
├── main.go
└── public
├── login
└── index.html
└── register
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 |
24 | .DS_Store
25 | *.iml
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 GopherCasts
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Lessons
2 | =======
3 |
4 | GopherCasts Lessons
5 |
--------------------------------------------------------------------------------
/lesson1/hello.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func main() {
8 | fmt.Println("Hello World!")
9 | }
10 |
--------------------------------------------------------------------------------
/lesson2/functions/functions.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 | import "strings"
5 |
6 | func main() {
7 | fmt.Println(double(5))
8 |
9 | first, last := parseName("Jeremy Saenz")
10 | fmt.Println(first)
11 | fmt.Println(last)
12 | }
13 |
14 | func double(n int) int {
15 | return n + n
16 | }
17 |
18 | func parseName(name string) (first, last string) {
19 | parsed := strings.Split(name, " ")
20 | return parsed[0], parsed[1]
21 | }
22 |
--------------------------------------------------------------------------------
/lesson2/looping/looping.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 |
7 | list := []string{"one", "two", "three"}
8 |
9 | for index, val := range list {
10 | fmt.Println(index, val)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/lesson2/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "fmt"
4 |
5 | func main() {
6 | fmt.Println("To run each example, cd into the example directory and run the go file:")
7 | fmt.Println("\n\tcd variables && go build && ./variables\n")
8 | }
9 |
--------------------------------------------------------------------------------
/lesson2/variables/variables.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | func main() {
8 | var one int = 1
9 | fmt.Println(one)
10 |
11 | var two, three string = "two", "three"
12 | fmt.Println(two, three)
13 |
14 | var four = true
15 | fmt.Println(four)
16 |
17 | five := "I know what I am"
18 | five = "???"
19 | fmt.Println(five)
20 | }
21 |
--------------------------------------------------------------------------------
/lesson3/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/go-martini/martini"
5 | "github.com/russross/blackfriday"
6 | "net/http"
7 | )
8 |
9 | func main() {
10 | m := martini.Classic()
11 |
12 | m.Post("/generate", func(r *http.Request) []byte {
13 | body := r.FormValue("body")
14 | return blackfriday.MarkdownBasic([]byte(body))
15 | })
16 |
17 | m.Run()
18 | }
19 |
--------------------------------------------------------------------------------
/lesson3/public/index.html:
--------------------------------------------------------------------------------
1 |
Generate Markdown
2 |
3 |
7 |
--------------------------------------------------------------------------------
/lesson4/.gitignore:
--------------------------------------------------------------------------------
1 | gin-bin
--------------------------------------------------------------------------------
/lesson4/data.sql:
--------------------------------------------------------------------------------
1 |
2 | -- ----------------------------
3 | -- Sequence structure for books_id_seq
4 | -- ----------------------------
5 | DROP SEQUENCE IF EXISTS "public"."books_id_seq";
6 | CREATE SEQUENCE "public"."books_id_seq" INCREMENT 1 START 5 MAXVALUE 9223372036854775807 MINVALUE 1 CACHE 1;
7 |
8 | -- ----------------------------
9 | -- Table structure for books
10 | -- ----------------------------
11 | DROP TABLE IF EXISTS "public"."books";
12 | CREATE TABLE "public"."books" (
13 | "id" int4 NOT NULL DEFAULT nextval('books_id_seq'::regclass),
14 | "title" varchar(255) NOT NULL COLLATE "default",
15 | "author" varchar(40) NOT NULL COLLATE "default",
16 | "description" text COLLATE "default"
17 | )
18 | WITH (OIDS=FALSE);
19 |
20 | -- ----------------------------
21 | -- Records of books
22 | -- ----------------------------
23 | BEGIN;
24 | INSERT INTO "public"."books" VALUES ('1', 'JerBear goes to the City', 'Garnee Smashington', 'A young hipster bear seeks his fortune in the wild city of Irvine.');
25 | INSERT INTO "public"."books" VALUES ('2', 'Swarley''s Big Day', 'Barney Stinson', 'Putting his Playbook aside, one man seeks a lifetime of happiness.');
26 | INSERT INTO "public"."books" VALUES ('3', 'All Around the Roundabound', 'Anakin Groundsitter', 'The riveting tale of a young lad taking pod-racing lessons from an instructor with a dark secret.');
27 | INSERT INTO "public"."books" VALUES ('4', 'Mastering Crossfire: You''ll get caught up in it', 'Freddie Wong', 'It''s sometime in the future, the ultimate challenge... Crossfire!');
28 | COMMIT;
29 |
30 |
31 | -- ----------------------------
32 | -- Alter sequences owned by
33 | -- ----------------------------
34 | ALTER SEQUENCE "public"."books_id_seq" RESTART 4 OWNED BY "books"."id";
35 |
36 | -- ----------------------------
37 | -- Primary key structure for table books
38 | -- ----------------------------
39 | ALTER TABLE "public"."books" ADD PRIMARY KEY ("id") NOT DEFERRABLE INITIALLY IMMEDIATE;
40 |
--------------------------------------------------------------------------------
/lesson4/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "fmt"
6 | "github.com/go-martini/martini"
7 | _ "github.com/lib/pq"
8 | "net/http"
9 | )
10 |
11 | func SetupDB() *sql.DB {
12 | db, err := sql.Open("postgres", "dbname=lesson4 sslmode=disable")
13 | PanicIf(err)
14 |
15 | return db
16 | }
17 |
18 | func PanicIf(err error) {
19 | if err != nil {
20 | panic(err)
21 | }
22 | }
23 |
24 | func main() {
25 | m := martini.Classic()
26 | m.Map(SetupDB())
27 |
28 | m.Get("/", func(db *sql.DB, r *http.Request, rw http.ResponseWriter) {
29 | search := "%" + r.URL.Query().Get("search") + "%"
30 | rows, err := db.Query(`SELECT title, author, description
31 | FROM books
32 | WHERE title ILIKE $1
33 | OR author ILIKE $1
34 | OR description ILIKE $1`, search)
35 | PanicIf(err)
36 | defer rows.Close()
37 |
38 | var title, author, description string
39 | for rows.Next() {
40 | err := rows.Scan(&title, &author, &description)
41 | PanicIf(err)
42 | fmt.Fprintf(rw, "Title: %s\nAuthor: %s\nDescription: %s\n\n", title, author, description)
43 | }
44 | })
45 |
46 | m.Run()
47 | }
48 |
--------------------------------------------------------------------------------
/lesson5/.gitignore:
--------------------------------------------------------------------------------
1 | gin-bin
--------------------------------------------------------------------------------
/lesson5/data.sql:
--------------------------------------------------------------------------------
1 |
2 | -- ----------------------------
3 | -- Sequence structure for books_id_seq
4 | -- ----------------------------
5 | DROP SEQUENCE IF EXISTS "public"."books_id_seq";
6 | CREATE SEQUENCE "public"."books_id_seq" INCREMENT 1 START 5 MAXVALUE 9223372036854775807 MINVALUE 1 CACHE 1;
7 |
8 | -- ----------------------------
9 | -- Table structure for books
10 | -- ----------------------------
11 | DROP TABLE IF EXISTS "public"."books";
12 | CREATE TABLE "public"."books" (
13 | "id" int4 NOT NULL DEFAULT nextval('books_id_seq'::regclass),
14 | "title" varchar(255) NOT NULL COLLATE "default",
15 | "author" varchar(40) NOT NULL COLLATE "default",
16 | "description" text COLLATE "default"
17 | )
18 | WITH (OIDS=FALSE);
19 |
20 | -- ----------------------------
21 | -- Records of books
22 | -- ----------------------------
23 | BEGIN;
24 | INSERT INTO "public"."books" VALUES ('1', 'JerBear goes to the City', 'Garnee Smashington', 'A young hipster bear seeks his fortune in the wild city of Irvine.');
25 | INSERT INTO "public"."books" VALUES ('2', 'Swarley''s Big Day', 'Barney Stinson', 'Putting his Playbook aside, one man seeks a lifetime of happiness.');
26 | INSERT INTO "public"."books" VALUES ('3', 'All Around the Roundabound', 'Anakin Groundsitter', 'The riveting tale of a young lad taking pod-racing lessons from an instructor with a dark secret.');
27 | INSERT INTO "public"."books" VALUES ('4', 'Mastering Crossfire: You''ll get caught up in it', 'Freddie Wong', 'It''s sometime in the future, the ultimate challenge... Crossfire!');
28 | COMMIT;
29 |
30 |
31 | -- ----------------------------
32 | -- Alter sequences owned by
33 | -- ----------------------------
34 | ALTER SEQUENCE "public"."books_id_seq" RESTART 4 OWNED BY "books"."id";
35 |
36 | -- ----------------------------
37 | -- Primary key structure for table books
38 | -- ----------------------------
39 | ALTER TABLE "public"."books" ADD PRIMARY KEY ("id") NOT DEFERRABLE INITIALLY IMMEDIATE;
40 |
--------------------------------------------------------------------------------
/lesson5/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "github.com/go-martini/martini"
6 | _ "github.com/lib/pq"
7 | "github.com/martini-contrib/render"
8 | "net/http"
9 | )
10 |
11 | type Book struct {
12 | Title string
13 | Author string
14 | Description string
15 | }
16 |
17 | func SetupDB() *sql.DB {
18 | db, err := sql.Open("postgres", "dbname=lesson4 sslmode=disable")
19 | PanicIf(err)
20 | return db
21 | }
22 |
23 | func PanicIf(err error) {
24 | if err != nil {
25 | panic(err)
26 | }
27 | }
28 |
29 | func main() {
30 | m := martini.Classic()
31 | m.Map(SetupDB())
32 | m.Use(render.Renderer(render.Options{
33 | Layout: "layout",
34 | }))
35 |
36 | m.Get("/", func(ren render.Render, r *http.Request, db *sql.DB) {
37 | searchTerm := "%" + r.URL.Query().Get("search") + "%"
38 | rows, err := db.Query(`SELECT title, author, description FROM books
39 | WHERE title ILIKE $1
40 | OR author ILIKE $1
41 | OR description ILIKE $1`, searchTerm)
42 | PanicIf(err)
43 | defer rows.Close()
44 |
45 | books := []Book{}
46 | for rows.Next() {
47 | book := Book{}
48 | err := rows.Scan(&book.Title, &book.Author, &book.Description)
49 | PanicIf(err)
50 | books = append(books, book)
51 | }
52 |
53 | ren.HTML(200, "books", books)
54 | })
55 |
56 | m.Run()
57 | }
58 |
--------------------------------------------------------------------------------
/lesson5/templates/books.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 | Title |
4 | Author |
5 | Description |
6 |
7 | {{ range . }}
8 |
9 | {{.Title}} |
10 | {{.Author}} |
11 | {{.Description}} |
12 |
13 | {{ end }}
14 |
15 |
--------------------------------------------------------------------------------
/lesson5/templates/layout.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ yield }}
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/lesson6/data.sql:
--------------------------------------------------------------------------------
1 |
2 | -- ----------------------------
3 | -- Sequence structure for books_id_seq
4 | -- ----------------------------
5 | DROP SEQUENCE IF EXISTS "public"."books_id_seq";
6 | CREATE SEQUENCE "public"."books_id_seq" INCREMENT 1 START 5 MAXVALUE 9223372036854775807 MINVALUE 1 CACHE 1;
7 |
8 | -- ----------------------------
9 | -- Table structure for books
10 | -- ----------------------------
11 | DROP TABLE IF EXISTS "public"."books";
12 | CREATE TABLE "public"."books" (
13 | "id" int4 NOT NULL DEFAULT nextval('books_id_seq'::regclass),
14 | "title" varchar(255) NOT NULL COLLATE "default",
15 | "author" varchar(40) NOT NULL COLLATE "default",
16 | "description" text COLLATE "default"
17 | )
18 | WITH (OIDS=FALSE);
19 |
20 | -- ----------------------------
21 | -- Records of books
22 | -- ----------------------------
23 | BEGIN;
24 | INSERT INTO "public"."books" VALUES ('1', 'JerBear goes to the City', 'Garnee Smashington', 'A young hipster bear seeks his fortune in the wild city of Irvine.');
25 | INSERT INTO "public"."books" VALUES ('2', 'Swarley''s Big Day', 'Barney Stinson', 'Putting his Playbook aside, one man seeks a lifetime of happiness.');
26 | INSERT INTO "public"."books" VALUES ('3', 'All Around the Roundabound', 'Anakin Groundsitter', 'The riveting tale of a young lad taking pod-racing lessons from an instructor with a dark secret.');
27 | INSERT INTO "public"."books" VALUES ('4', 'Mastering Crossfire: You''ll get caught up in it', 'Freddie Wong', 'It''s sometime in the future, the ultimate challenge... Crossfire!');
28 | COMMIT;
29 |
30 |
31 | -- ----------------------------
32 | -- Alter sequences owned by
33 | -- ----------------------------
34 | ALTER SEQUENCE "public"."books_id_seq" RESTART 4 OWNED BY "books"."id";
35 |
36 | -- ----------------------------
37 | -- Primary key structure for table books
38 | -- ----------------------------
39 | ALTER TABLE "public"."books" ADD PRIMARY KEY ("id") NOT DEFERRABLE INITIALLY IMMEDIATE;
40 |
--------------------------------------------------------------------------------
/lesson6/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "github.com/go-martini/martini"
6 | _ "github.com/lib/pq"
7 | "github.com/martini-contrib/render"
8 | "net/http"
9 | )
10 |
11 | type Book struct {
12 | Title string
13 | Author string
14 | Description string
15 | }
16 |
17 | func SetupDB() *sql.DB {
18 | db, err := sql.Open("postgres", "dbname=lesson4 sslmode=disable")
19 | PanicIf(err)
20 | return db
21 | }
22 |
23 | func PanicIf(err error) {
24 | if err != nil {
25 | panic(err)
26 | }
27 | }
28 |
29 | func main() {
30 | m := martini.Classic()
31 | m.Map(SetupDB())
32 | m.Use(render.Renderer(render.Options{
33 | Layout: "layout",
34 | }))
35 |
36 | m.Get("/", ShowBooks)
37 | m.Post("/books", CreateBook)
38 | m.Get("/create", NewBooks)
39 |
40 | m.Run()
41 | }
42 |
43 | func NewBooks(r render.Render) {
44 | r.HTML(200, "create", nil)
45 | }
46 |
47 | func CreateBook(ren render.Render, r *http.Request, db *sql.DB) {
48 | _, err := db.Query("INSERT INTO books (title, author, description) VALUES ($1, $2, $3)",
49 | r.FormValue("title"),
50 | r.FormValue("author"),
51 | r.FormValue("description"))
52 |
53 | PanicIf(err)
54 | ren.Redirect("/")
55 | }
56 |
57 | func ShowBooks(ren render.Render, r *http.Request, db *sql.DB) {
58 | searchTerm := "%" + r.URL.Query().Get("search") + "%"
59 | rows, err := db.Query(`SELECT title, author, description FROM books
60 | WHERE title ILIKE $1
61 | OR author ILIKE $1
62 | OR description ILIKE $1`, searchTerm)
63 | PanicIf(err)
64 | defer rows.Close()
65 |
66 | books := []Book{}
67 | for rows.Next() {
68 | book := Book{}
69 | err := rows.Scan(&book.Title, &book.Author, &book.Description)
70 | PanicIf(err)
71 | books = append(books, book)
72 | }
73 |
74 | ren.HTML(200, "books", books)
75 | }
76 |
--------------------------------------------------------------------------------
/lesson6/templates/books.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 | Title |
4 | Author |
5 | Description |
6 |
7 | {{ range . }}
8 |
9 | {{.Title}} |
10 | {{.Author}} |
11 | {{.Description}} |
12 |
13 | {{ end }}
14 |
15 |
--------------------------------------------------------------------------------
/lesson6/templates/create.tmpl:
--------------------------------------------------------------------------------
1 | Create New Book
2 |
18 |
--------------------------------------------------------------------------------
/lesson6/templates/layout.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ yield }}
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/lesson7/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "net/http"
6 |
7 | "github.com/go-martini/martini"
8 | _ "github.com/lib/pq"
9 | )
10 |
11 | func SetupDB() *sql.DB {
12 | db, err := sql.Open("postgres", "dbname=lesson7 sslmode=disable")
13 | PanicIf(err)
14 | return db
15 | }
16 |
17 | func PanicIf(err error) {
18 | if err != nil {
19 | panic(err)
20 | }
21 | }
22 |
23 | func main() {
24 | m := martini.Classic()
25 | m.Map(SetupDB())
26 |
27 | m.Post("/login", PostLogin)
28 |
29 | m.Run()
30 | }
31 |
32 | func PostLogin(req *http.Request, db *sql.DB) (int, string) {
33 | var id string
34 |
35 | email, password := req.FormValue("email"), req.FormValue("password")
36 | err := db.QueryRow("select id from users where email=$1 and password=$2", email, password).Scan(&id)
37 |
38 | if err != nil {
39 | return 401, "Unauthorized"
40 | }
41 |
42 | return 200, "User id is " + id
43 | }
44 |
--------------------------------------------------------------------------------
/lesson7/public/login/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Log in
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lesson8/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "net/http"
6 |
7 | "github.com/go-martini/martini"
8 | _ "github.com/lib/pq"
9 | "github.com/martini-contrib/sessions"
10 | )
11 |
12 | type User struct {
13 | Name string
14 | Email string
15 | }
16 |
17 | func SetupDB() *sql.DB {
18 | db, err := sql.Open("postgres", "dbname=lesson7 sslmode=disable")
19 | PanicIf(err)
20 | return db
21 | }
22 |
23 | func PanicIf(err error) {
24 | if err != nil {
25 | panic(err)
26 | }
27 | }
28 |
29 | func main() {
30 | m := martini.Classic()
31 | m.Map(SetupDB())
32 |
33 | // Sessions
34 | store := sessions.NewCookieStore([]byte("secret123"))
35 | m.Use(sessions.Sessions("lesson8", store))
36 |
37 | m.Get("/", RequireLogin, SecretPath)
38 | m.Post("/login", PostLogin)
39 | m.Get("/logout", Logout)
40 |
41 | m.Run()
42 | }
43 |
44 | func Logout(rw http.ResponseWriter, req *http.Request, s sessions.Session) {
45 | s.Delete("userId")
46 | http.Redirect(rw, req, "/", http.StatusFound)
47 | }
48 |
49 | func RequireLogin(rw http.ResponseWriter, req *http.Request,
50 | s sessions.Session, db *sql.DB, c martini.Context) {
51 | user := &User{}
52 | err := db.QueryRow("select name, email from users where id=$1", s.Get("userId")).Scan(&user.Name, &user.Email)
53 |
54 | if err != nil {
55 | http.Redirect(rw, req, "/login", http.StatusFound)
56 | return
57 | }
58 |
59 | c.Map(user)
60 | }
61 |
62 | func SecretPath(user *User) string {
63 | return "Great Job " + user.Name
64 | }
65 |
66 | func PostLogin(req *http.Request, db *sql.DB, s sessions.Session) (int, string) {
67 | var id string
68 |
69 | email, password := req.FormValue("email"), req.FormValue("password")
70 | err := db.QueryRow("select id from users where email=$1 and password=$2", email, password).Scan(&id)
71 |
72 | if err != nil {
73 | return 401, "Unauthorized"
74 | }
75 |
76 | s.Set("userId", id)
77 |
78 | return 200, "User id is " + id
79 | }
80 |
--------------------------------------------------------------------------------
/lesson8/public/login/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Log in
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lesson9/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "net/http"
6 |
7 | "code.google.com/p/go.crypto/bcrypt"
8 | "github.com/go-martini/martini"
9 | _ "github.com/lib/pq"
10 | "github.com/martini-contrib/sessions"
11 | )
12 |
13 | type User struct {
14 | Name string
15 | Email string
16 | }
17 |
18 | func SetupDB() *sql.DB {
19 | db, err := sql.Open("postgres", "dbname=lesson7 sslmode=disable")
20 | PanicIf(err)
21 | return db
22 | }
23 |
24 | func PanicIf(err error) {
25 | if err != nil {
26 | panic(err)
27 | }
28 | }
29 |
30 | func main() {
31 | m := martini.Classic()
32 | m.Map(SetupDB())
33 |
34 | // Sessions
35 | store := sessions.NewCookieStore([]byte("secret123"))
36 | m.Use(sessions.Sessions("lesson8", store))
37 |
38 | m.Get("/", RequireLogin, SecretPath)
39 | m.Post("/login", PostLogin)
40 | m.Get("/logout", Logout)
41 | m.Post("/signup", Signup)
42 |
43 | m.Run()
44 | }
45 |
46 | func Signup(rw http.ResponseWriter, r *http.Request, db *sql.DB) {
47 | name, email, password := r.FormValue("name"), r.FormValue("email"), r.FormValue("password")
48 | hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
49 | PanicIf(err)
50 |
51 | _, err = db.Exec("insert into users (name, email, password) values ($1, $2, $3)",
52 | name, email, hashedPassword)
53 |
54 | PanicIf(err)
55 |
56 | http.Redirect(rw, r, "/login", http.StatusFound)
57 | }
58 |
59 | func Logout(rw http.ResponseWriter, req *http.Request, s sessions.Session) {
60 | s.Delete("userId")
61 | http.Redirect(rw, req, "/", http.StatusFound)
62 | }
63 |
64 | func RequireLogin(rw http.ResponseWriter, req *http.Request,
65 | s sessions.Session, db *sql.DB, c martini.Context) {
66 | user := &User{}
67 | err := db.QueryRow("select name, email from users where id=$1", s.Get("userId")).Scan(&user.Name, &user.Email)
68 |
69 | if err != nil {
70 | http.Redirect(rw, req, "/login", http.StatusFound)
71 | return
72 | }
73 |
74 | c.Map(user)
75 | }
76 |
77 | func SecretPath(user *User) string {
78 | return "Great Job " + user.Name
79 | }
80 |
81 | func PostLogin(req *http.Request, db *sql.DB, s sessions.Session) (int, string) {
82 | var id string
83 | var pw string
84 |
85 | email, password := req.FormValue("email"), req.FormValue("password")
86 | err := db.QueryRow("select id, password from users where email=$1", email).Scan(&id, &pw)
87 |
88 | if err != nil || bcrypt.CompareHashAndPassword([]byte(pw), []byte(password)) != nil {
89 | return 401, "Unauthorized"
90 | }
91 |
92 | s.Set("userId", id)
93 |
94 | return 200, "User id is " + id
95 | }
96 |
--------------------------------------------------------------------------------
/lesson9/public/login/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Log in
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
33 |
34 |
35 |
36 |
37 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/lesson9/public/register/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Log in
11 |
12 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
34 |
35 |
36 |
37 |
38 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------