├── .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 |
4 | 5 | 6 |
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 | 4 | 5 | 6 | 7 | {{ range . }} 8 | 9 | 10 | 11 | 12 | 13 | {{ end }} 14 |
TitleAuthorDescription
{{.Title}}{{.Author}}{{.Description}}
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 | 4 | 5 | 6 | 7 | {{ range . }} 8 | 9 | 10 | 11 | 12 | 13 | {{ end }} 14 |
TitleAuthorDescription
{{.Title}}{{.Author}}{{.Description}}
15 | -------------------------------------------------------------------------------- /lesson6/templates/create.tmpl: -------------------------------------------------------------------------------- 1 |

Create New Book

2 |
3 |
4 | 5 | 6 |
7 |
8 | 9 | 10 |
11 |
12 | 13 | 14 |
15 | 16 | 17 |
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 |
27 | 28 | 29 | 30 | 31 | 32 |
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 |
27 | 28 | 29 | 30 | 31 | 32 |
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 |
27 | 28 | 29 | 30 | 31 | 32 |
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 |
27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 |
36 | 37 | 38 | 40 | 41 | 42 | 43 | --------------------------------------------------------------------------------