From 605d4299b911c9c6dc692d5bd041473c2b09baad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A1=9C=E8=8F=AF?= Date: Fri, 12 May 2023 22:53:49 +0800 Subject: [PATCH] sign in --- README.md | 6 +++- configs/sqlite3.go | 6 ++++ go.mod | 1 + go.sum | 5 +++ main.go | 17 ++++++++-- models/Tag.go | 19 +++++++++-- models/User.go | 70 ++++++++++++++++++++++++++++++++------- models/session.go | 79 ++++++++++++++++++++++----------------------- routers/sessions.go | 63 +++++++++++++++++++++++++++++++++--- routers/tags.go | 62 +++++++++++++++++++++++++++++++++++ 10 files changed, 268 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 7059d0c..3110a30 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,12 @@ TEST: - [x] GET [/api/models](/api/models) 模型列表 - [x] GET [/api/images](/api/images) 圖片列表 - [x] GET [/api/tasks](/api/tasks) 任務列表 -- [ ] GET [/api/tags](/api/tags) 標籤列表 +- [x] GET [/api/tags](/api/tags) 標籤列表 +TEST: + +- [x] POST [/api/users](/api/users) 創建用戶 +- [x] POST [/api/sesions](/api/sessions) 創建會話 列表接口: diff --git a/configs/sqlite3.go b/configs/sqlite3.go index c7987e3..8e043b5 100644 --- a/configs/sqlite3.go +++ b/configs/sqlite3.go @@ -82,6 +82,12 @@ func init() { updated_at TEXT, user_id INTEGER ); + CREATE TABLE IF NOT EXISTS sessions( + id TEXT PRIMARY KEY, + user_id INTEGER, + created_at TEXT, + updated_at TEXT + ); `) defer db.Close() if err != nil { diff --git a/go.mod b/go.mod index 538582e..2f19b72 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 + github.com/jmoiron/sqlx v1.3.5 github.com/mattn/go-sqlite3 v1.14.16 github.com/russross/blackfriday v1.6.0 ) diff --git a/go.sum b/go.sum index 76cf8d1..2eb2659 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -6,6 +7,10 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= diff --git a/main.go b/main.go index a8413e0..86d1bf4 100644 --- a/main.go +++ b/main.go @@ -26,8 +26,9 @@ func main() { w.Header().Set("Access-Control-Allow-Origin", "*") // 處理跨域請求 w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") + // 處理OPTIONS請求 if r.Method == "OPTIONS" { - w.WriteHeader(http.StatusOK) // 處理OPTIONS請求 + w.WriteHeader(http.StatusOK) return } next.ServeHTTP(w, r) @@ -42,7 +43,13 @@ func main() { return } output := blackfriday.Markdown(input, blackfriday.HtmlRenderer(0, "", ""), blackfriday.EXTENSION_TABLES|blackfriday.EXTENSION_FENCED_CODE|blackfriday.EXTENSION_AUTOLINK) - html := "API Document" + string(output) + "" + css := `` + html := "API Document" + css + "" + string(output) + "" w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Write([]byte(html)) }) @@ -77,6 +84,12 @@ func main() { r.HandleFunc("/api/tasks/{id}", routers.TasksItemPatch).Methods("PATCH") r.HandleFunc("/api/tasks/{id}", routers.TasksItemDelete).Methods("DELETE") + r.HandleFunc("/api/tags", routers.TagsGet).Methods("GET") + r.HandleFunc("/api/tags", routers.TagsPost).Methods("POST") + r.HandleFunc("/api/tags/{id}", routers.TagsItemGet).Methods("GET") + r.HandleFunc("/api/tags/{id}", routers.TagsItemPatch).Methods("PATCH") + r.HandleFunc("/api/tags/{id}", routers.TagsItemDelete).Methods("DELETE") + r.HandleFunc("/api/servers", routers.ServersGet).Methods("GET") r.HandleFunc("/api/servers", routers.ServersPost).Methods("POST") r.HandleFunc("/api/servers/{id}", routers.ServersItemGet).Methods("GET") diff --git a/models/Tag.go b/models/Tag.go index 5614916..49e6435 100644 --- a/models/Tag.go +++ b/models/Tag.go @@ -59,7 +59,7 @@ func (tag *Tag) Delete() error { return nil } -func (tag *Tag) Update() error { +func (tag *Tag) Update(name string) error { db, err := configs.GetDB() if err != nil { log.Println(err) @@ -72,7 +72,22 @@ func (tag *Tag) Update() error { return err } defer stmt.Close() - _, err = stmt.Exec(tag.Name, tag.UpdatedAt, tag.ID) + _, err = stmt.Exec(name, tag.UpdatedAt, tag.ID) + if err != nil { + log.Println(err) + return err + } + return nil +} + +func (tag *Tag) Get() error { + db, err := configs.GetDB() + if err != nil { + log.Println(err) + return err + } + defer db.Close() + err = db.QueryRow("SELECT * FROM tags WHERE id = ?", tag.ID).Scan(&tag.ID, &tag.Name, &tag.CreatedAt, &tag.UpdatedAt) if err != nil { log.Println(err) return err diff --git a/models/User.go b/models/User.go index 515c265..4b60e36 100644 --- a/models/User.go +++ b/models/User.go @@ -15,8 +15,8 @@ type User struct { Email string `json:"email"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` - password string - slat string + Password string `json:"-"` + Slat string `json:"-"` } func (user *User) Create(name, email, password string) error { @@ -25,8 +25,8 @@ func (user *User) Create(name, email, password string) error { return fmt.Errorf("name, email and password can not be empty") } - user.slat = utils.RandomString(16) - user.password = fmt.Sprintf("%x", md5.Sum([]byte(password+user.slat))) + user.Slat = utils.RandomString(16) + user.Password = fmt.Sprintf("%x", md5.Sum([]byte(password+user.Slat))) user.Name = name user.Email = email user.CreatedAt = time.Now().Format("2006-01-02 15:04:05") @@ -45,7 +45,7 @@ func (user *User) Create(name, email, password string) error { return err } defer stmt.Close() - result, err := stmt.Exec(user.Name, user.Email, user.password, user.slat, user.CreatedAt, user.UpdatedAt) + result, err := stmt.Exec(user.Name, user.Email, user.Password, user.Slat, user.CreatedAt, user.UpdatedAt) if err != nil { log.Println(err) return err @@ -107,7 +107,7 @@ func (user *User) Get() error { return err } defer db.Close() - err = db.QueryRow("SELECT name, email, created_at, updated_at FROM users WHERE id = ?", user.ID).Scan(&user.Name, &user.Email, &user.CreatedAt, &user.UpdatedAt) + err = db.QueryRow("SELECT id, name, email, password, slat, created_at, updated_at FROM users WHERE email = ?", user.ID).Scan(&user.ID, &user.Name, &user.Email, &user.Password, &user.Slat, &user.CreatedAt, &user.UpdatedAt) if err != nil { log.Println(err) return err @@ -141,19 +141,67 @@ func (user *User) GetAll() ([]User, error) { return users, nil } -func (user *User) GetByEmail() error { +// 驗證用戶密碼 +func (user *User) CheckPassword(password string) bool { + return user.Password == fmt.Sprintf("%x", md5.Sum([]byte(password+user.Slat))) +} + +// 使用Email和Password驗證登錄 +func (user *User) CheckLogin(email, password string) bool { db, err := configs.GetDB() if err != nil { log.Println(err) - return err + return false } defer db.Close() - err = db.QueryRow("SELECT id, name, email, created_at, updated_at FROM users WHERE email = ?", user.Email).Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt, &user.UpdatedAt) + err = db.QueryRow("SELECT id, name, email, password, slat, created_at, updated_at FROM users WHERE email = ?", email).Scan(&user.ID, &user.Name, &user.Email, &user.Password, &user.Slat, &user.CreatedAt, &user.UpdatedAt) if err != nil { log.Println(err) - return err + return false } - return nil + if user.ID == 0 { + fmt.Println("user not found") + return false + } + if user.Password == "" { + fmt.Println("password is empty") + return false + } + if user.Password == fmt.Sprintf("%x", md5.Sum([]byte(password+user.Slat))) { + return true + } + return false +} + +// 獲取用戶實體 +func GetUserByEmail(email string) (user User, err error) { + db, err := configs.GetDB() + if err != nil { + log.Println(err) + return + } + defer db.Close() + err = db.QueryRow("SELECT id, name, email, password, slat, created_at, updated_at FROM users WHERE email = ?", email).Scan(&user.ID, &user.Name, &user.Email, &user.Password, &user.Slat, &user.CreatedAt, &user.UpdatedAt) + if err != nil { + log.Println(err) + return + } + return +} + +func QueryUserByEmail(email string) (user User, err error) { + db, err := configs.GetDB() + if err != nil { + log.Println(err) + return + } + defer db.Close() + err = db.QueryRow("SELECT id, name, email, created_at, updated_at FROM users WHERE email = ?", email).Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt, &user.UpdatedAt) + if err != nil { + log.Println(err) + return + } + return } func QueryUsers(page, pagesize int) (list []interface{}) { diff --git a/models/session.go b/models/session.go index 5555d3d..7cd1e13 100644 --- a/models/session.go +++ b/models/session.go @@ -3,11 +3,12 @@ package models import ( "log" "main/configs" + "time" ) type Session struct { - ID int `json:"id"` - Name string `json:"name"` + ID string `json:"id"` + UserID int `json:"user_id"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } @@ -20,7 +21,7 @@ func (session *Session) Get() { } defer db.Close() row := db.QueryRow("SELECT * FROM sessions WHERE id = ?", session.ID) - err = row.Scan(&session.ID, &session.Name, &session.CreatedAt, &session.UpdatedAt) + err = row.Scan(&session.ID, &session.UserID, &session.CreatedAt, &session.UpdatedAt) if err != nil { log.Println(err) return @@ -28,28 +29,25 @@ func (session *Session) Get() { } func (session *Session) Create() error { + session.CreatedAt = time.Now().Format("2006-01-02 15:04:05") + session.UpdatedAt = session.CreatedAt db, err := configs.GetDB() if err != nil { log.Println(err) return err } defer db.Close() - stmt, err := db.Prepare("INSERT INTO sessions(name) values(?)") + stmt, err := db.Prepare("INSERT INTO sessions (id, user_id, created_at, updated_at) VALUES (?, ?, ?, ?)") if err != nil { log.Println(err) return err } defer stmt.Close() - result, err := stmt.Exec(session.Name) + _, err = stmt.Exec(session.ID, session.UserID, session.CreatedAt, session.UpdatedAt) if err != nil { log.Println(err) return err } - id, err := result.LastInsertId() - if err != nil { - return err - } - session.ID = int(id) return nil } @@ -75,19 +73,20 @@ func (session *Session) Delete() error { } func (session *Session) Update() error { + session.UpdatedAt = time.Now().Format("2006-01-02 15:04:05") db, err := configs.GetDB() if err != nil { log.Println(err) return err } defer db.Close() - stmt, err := db.Prepare("UPDATE sessions SET name = ? WHERE id = ?") + stmt, err := db.Prepare("UPDATE sessions SET user_id = ?, updated_at = ? WHERE id = ?") if err != nil { log.Println(err) return err } defer stmt.Close() - _, err = stmt.Exec(session.Name, session.ID) + _, err = stmt.Exec(session.UpdatedAt, session.UserID, session.ID) if err != nil { log.Println(err) return err @@ -95,30 +94,30 @@ func (session *Session) Update() error { return nil } -func GetSessions() ([]Session, error) { - db, err := configs.GetDB() - if err != nil { - log.Println(err) - return nil, err - } - defer db.Close() - rows, err := db.Query("SELECT id, name FROM sessions") - if err != nil { - log.Println(err) - return nil, err - } - defer rows.Close() - sessions := []Session{} - for rows.Next() { - var session Session - if err := rows.Scan(&session.ID, &session.Name); err != nil { - log.Println(err) - return nil, err - } - sessions = append(sessions, session) - } - return sessions, nil -} +//func GetSessions() ([]Session, error) { +// db, err := configs.GetDB() +// if err != nil { +// log.Println(err) +// return nil, err +// } +// defer db.Close() +// rows, err := db.Query("SELECT id, name FROM sessions") +// if err != nil { +// log.Println(err) +// return nil, err +// } +// defer rows.Close() +// sessions := []Session{} +// for rows.Next() { +// var session Session +// if err := rows.Scan(&session.ID, &session.Name); err != nil { +// log.Println(err) +// return nil, err +// } +// sessions = append(sessions, session) +// } +// return sessions, nil +//} func GetSession(id int) (*Session, error) { db, err := configs.GetDB() @@ -127,9 +126,9 @@ func GetSession(id int) (*Session, error) { return nil, err } defer db.Close() - row := db.QueryRow("SELECT id, name FROM sessions WHERE id = ?", id) + row := db.QueryRow("SELECT id, user_id, created_at, updated_at FROM sessions WHERE id = ?", id) var session Session - if err := row.Scan(&session.ID, &session.Name); err != nil { + if err := row.Scan(&session.ID, &session.UserID, &session.CreatedAt, &session.UpdatedAt); err != nil { log.Println(err) return nil, err } @@ -143,7 +142,7 @@ func QuerySessions(page, pagesize int) (list []interface{}) { return } defer db.Close() - rows, err := db.Query("SELECT id, name FROM sessions LIMIT ?, ?", (page-1)*pagesize, pagesize) + rows, err := db.Query("SELECT id, user_id, created_at, updated_at FROM sessions LIMIT ?, ?", (page-1)*pagesize, pagesize) if err != nil { log.Println(err) return @@ -151,7 +150,7 @@ func QuerySessions(page, pagesize int) (list []interface{}) { defer rows.Close() for rows.Next() { var session Session - if err := rows.Scan(&session.ID, &session.Name); err != nil { + if err := rows.Scan(&session.ID, &session.UserID, &session.CreatedAt, &session.UpdatedAt); err != nil { log.Println(err) return } diff --git a/routers/sessions.go b/routers/sessions.go index a1b760a..9dc995e 100644 --- a/routers/sessions.go +++ b/routers/sessions.go @@ -1,10 +1,14 @@ package routers import ( + "encoding/json" + "fmt" + "io/ioutil" "main/models" "main/utils" "net/http" + "github.com/google/uuid" "github.com/gorilla/mux" ) @@ -19,17 +23,68 @@ func SessionsGet(w http.ResponseWriter, r *http.Request) { listview.WriteJSON(w) } +func GetForm(r *http.Request) (form []interface{}) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + fmt.Println(err) + return + } + defer r.Body.Close() + if err = json.Unmarshal(body, &form); err != nil { + fmt.Println(err) + return + } + return +} + // 創建會話 func SessionsPost(w http.ResponseWriter, r *http.Request) { - var session models.Session + var form struct { + Email string `json:"email"` + Password string `json:"password"` + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + fmt.Println(err) + return + } + defer r.Body.Close() + if err = json.Unmarshal(body, &form); err != nil { + fmt.Println(err) + return + } + + // 使用Email獲取用戶 + user, err := models.GetUserByEmail(form.Email) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte("404 - User Not Found")) + return + } + + // 使用密碼驗證登錄 + if !user.CheckPassword(form.Password) { + w.WriteHeader(http.StatusUnauthorized) + w.Write([]byte("401 - Unauthorized")) + return + } + + // 創建會話(生成一個不重複的 uuid 作爲 sid) + session := &models.Session{ID: uuid.New().String(), UserID: user.ID} session.Create() + + // 寫入Cookie + cookie := http.Cookie{Name: "session_id", Value: session.ID, Path: "/", HttpOnly: true} + http.SetCookie(w, &cookie) + + // 返回信息 w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Write(utils.ToJSON(session)) } // 獲取會話 func SessionsItemGet(w http.ResponseWriter, r *http.Request) { - session := models.Session{ID: utils.ParamInt(mux.Vars(r)["id"], 0)} + session := models.Session{ID: mux.Vars(r)["id"]} session.Get() w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Write(utils.ToJSON(session)) @@ -37,7 +92,7 @@ func SessionsItemGet(w http.ResponseWriter, r *http.Request) { // 更新會話 func SessionsItemPatch(w http.ResponseWriter, r *http.Request) { - session := models.Session{ID: utils.ParamInt(mux.Vars(r)["id"], 0)} + session := models.Session{ID: mux.Vars(r)["id"]} session.Get() session.Update() w.Header().Set("Content-Type", "application/json; charset=utf-8") @@ -46,7 +101,7 @@ func SessionsItemPatch(w http.ResponseWriter, r *http.Request) { // 刪除會話 func SessionsItemDelete(w http.ResponseWriter, r *http.Request) { - session := models.Session{ID: utils.ParamInt(mux.Vars(r)["id"], 0)} + session := models.Session{ID: mux.Vars(r)["id"]} session.Get() session.Delete() w.Header().Set("Content-Type", "application/json; charset=utf-8") diff --git a/routers/tags.go b/routers/tags.go index 322b76c..3db2001 100644 --- a/routers/tags.go +++ b/routers/tags.go @@ -7,6 +7,8 @@ import ( "main/models" "main/utils" "net/http" + + "github.com/gorilla/mux" ) // 獲取標籤列表 @@ -48,4 +50,64 @@ func TagsPost(w http.ResponseWriter, r *http.Request) { // 獲取標籤 func TagsItemGet(w http.ResponseWriter, r *http.Request) { + var tag models.Tag + tag.ID = utils.ParamInt(mux.Vars(r)["id"], 0) + if tag.ID == 0 { + w.WriteHeader(http.StatusNotFound) + return + } + if err := tag.Get(); err != nil { + fmt.Println(err) + return + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Write(utils.ToJSON(tag)) +} + +// 更新標籤 +func TagsItemPatch(w http.ResponseWriter, r *http.Request) { + var tag models.Tag + tag.ID = utils.ParamInt(mux.Vars(r)["id"], 0) + if tag.ID == 0 { + w.WriteHeader(http.StatusNotFound) + return + } + if err := tag.Get(); err != nil { + fmt.Println(err) + return + } + var form struct { + Name string `json:"name"` + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + fmt.Println(err) + return + } + defer r.Body.Close() + if err = json.Unmarshal(body, &form); err != nil { + fmt.Println(err) + return + } + if err := tag.Update(form.Name); err != nil { + fmt.Println(err) + return + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Write(utils.ToJSON(tag)) +} + +// 刪除標籤 +func TagsItemDelete(w http.ResponseWriter, r *http.Request) { + var tag models.Tag + tag.ID = utils.ParamInt(mux.Vars(r)["id"], 0) + if tag.ID == 0 { + w.WriteHeader(http.StatusNotFound) + return + } + if err := tag.Delete(); err != nil { + fmt.Println(err) + return + } + w.WriteHeader(http.StatusNoContent) }