diff --git a/api/auth.go b/api/auth.go index 1d408b12..70c9bbf5 100644 --- a/api/auth.go +++ b/api/auth.go @@ -2,7 +2,7 @@ package api import ( "encoding/json" - "memos/common/error" + "memos/api/e" "memos/store" "net/http" @@ -15,22 +15,34 @@ type UserSignUp struct { } func handleUserSignUp(w http.ResponseWriter, r *http.Request) { - var userSignup UserSignUp + userSignup := UserSignUp{} err := json.NewDecoder(r.Body).Decode(&userSignup) if err != nil { - error.ErrorHandler(w, "REQUEST_BODY_ERROR") + e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request") return } user, err := store.CreateNewUser(userSignup.Username, userSignup.Password, "", "") if err != nil { - error.ErrorHandler(w, "") + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) return } - json.NewEncoder(w).Encode(user) + userIdCookie := &http.Cookie{ + Name: "user_id", + Value: user.Id, + Path: "/", + MaxAge: 3600 * 24 * 30, + } + http.SetCookie(w, userIdCookie) + + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: user, + }) } type UserSignin struct { @@ -39,38 +51,50 @@ type UserSignin struct { } func handleUserSignIn(w http.ResponseWriter, r *http.Request) { - var userSignin UserSignin + userSignin := UserSignin{} err := json.NewDecoder(r.Body).Decode(&userSignin) if err != nil { - error.ErrorHandler(w, "") + e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request") return } user, err := store.GetUserByUsernameAndPassword(userSignin.Username, userSignin.Password) if err != nil { - error.ErrorHandler(w, "") + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) return } userIdCookie := &http.Cookie{ Name: "user_id", Value: user.Id, + Path: "/", MaxAge: 3600 * 24 * 30, } http.SetCookie(w, userIdCookie) - json.NewEncoder(w).Encode(user) + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: user, + }) } func handleUserSignOut(w http.ResponseWriter, r *http.Request) { userIdCookie := &http.Cookie{ Name: "user_id", Value: "", + Path: "/", MaxAge: 0, } http.SetCookie(w, userIdCookie) + + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: nil, + }) } func RegisterAuthRoutes(r *mux.Router) { diff --git a/common/error/codes.go b/api/e/codes.go similarity index 88% rename from common/error/codes.go rename to api/e/codes.go index 35d9d6b2..d7036202 100644 --- a/common/error/codes.go +++ b/api/e/codes.go @@ -1,4 +1,4 @@ -package error +package e var Codes = map[string]int{ "NOT_AUTH": 20001, diff --git a/common/error/error.go b/api/e/error.go similarity index 54% rename from common/error/error.go rename to api/e/error.go index f52d866f..86741440 100644 --- a/common/error/error.go +++ b/api/e/error.go @@ -1,4 +1,4 @@ -package error +package e import ( "encoding/json" @@ -11,17 +11,19 @@ type ServerError struct { } type ErrorResponse struct { - StatusCode int `json:"statusCode"` - StatusMessage string `json:"statusMessage"` - Succeed bool `json:"succeed"` - Data interface{} `json:"data"` + Succeed bool `json:"succeed"` + Message string `json:"message"` + StatusCode int `json:"statusCode"` + Data interface{} `json:"data"` } func getServerError(err string) ServerError { code, exists := Codes[err] + println(err) + if !exists { - err = "Bad Request" + err = "BAD_REQUEST" code = 40000 } @@ -31,14 +33,14 @@ func getServerError(err string) ServerError { } } -func ErrorHandler(w http.ResponseWriter, err string) { +func ErrorHandler(w http.ResponseWriter, err string, message string) { serverError := getServerError(err) res := ErrorResponse{ - StatusCode: serverError.Code, - StatusMessage: serverError.Message, - Succeed: false, - Data: nil, + Succeed: false, + Message: message, + StatusCode: serverError.Code, + Data: nil, } statusCode := int(serverError.Code / 100) diff --git a/api/memo.go b/api/memo.go index 1b8a1c01..d37534ec 100644 --- a/api/memo.go +++ b/api/memo.go @@ -2,7 +2,7 @@ package api import ( "encoding/json" - "memos/common/error" + "memos/api/e" "memos/store" "net/http" @@ -11,15 +11,22 @@ import ( func handleGetMyMemos(w http.ResponseWriter, r *http.Request) { userId, _ := GetUserIdInCookie(r) + urlParams := r.URL.Query() + deleted := urlParams.Get("deleted") + onlyDeletedFlag := deleted == "true" - memos, err := store.GetMemosByUserId(userId) + memos, err := store.GetMemosByUserId(userId, onlyDeletedFlag) if err != nil { - error.ErrorHandler(w, "DATABASE_ERROR") + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) return } - json.NewEncoder(w).Encode(memos) + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: memos, + }) } type CreateMemo struct { @@ -29,54 +36,77 @@ type CreateMemo struct { func handleCreateMemo(w http.ResponseWriter, r *http.Request) { userId, _ := GetUserIdInCookie(r) - var createMemo CreateMemo + createMemo := CreateMemo{} err := json.NewDecoder(r.Body).Decode(&createMemo) if err != nil { - error.ErrorHandler(w, "") + e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request") return } memo, err := store.CreateNewMemo(createMemo.Content, userId) if err != nil { - error.ErrorHandler(w, "") + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) return } - json.NewEncoder(w).Encode(memo) + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: memo, + }) } func handleUpdateMemo(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) memoId := vars["id"] - userId, _ := GetUserIdInCookie(r) - - var createMemo CreateMemo - err := json.NewDecoder(r.Body).Decode(&createMemo) + memoPatch := store.MemoPatch{} + err := json.NewDecoder(r.Body).Decode(&memoPatch) if err != nil { - error.ErrorHandler(w, "") + e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request") return } - memo, err := store.UpdateMemo(memoId, createMemo.Content, userId) + memo, err := store.UpdateMemo(memoId, &memoPatch) if err != nil { - error.ErrorHandler(w, "") + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) return } - json.NewEncoder(w).Encode(memo) + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: memo, + }) +} + +func handleDeleteMemo(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + memoId := vars["id"] + + _, err := store.DeleteMemo(memoId) + + if err != nil { + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) + return + } + + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: nil, + }) } func RegisterMemoRoutes(r *mux.Router) { memoRouter := r.PathPrefix("/api/memo").Subrouter() - memoRouter.Use(AuthCheckerMiddleWare) - memoRouter.HandleFunc("/all", handleGetMyMemos).Methods("GET") memoRouter.HandleFunc("/", handleCreateMemo).Methods("PUT") memoRouter.HandleFunc("/{id}", handleUpdateMemo).Methods("PATCH") + memoRouter.HandleFunc("/{id}", handleDeleteMemo).Methods("DELETE") } diff --git a/api/middlewares.go b/api/middlewares.go index 095abaf8..ca37d443 100644 --- a/api/middlewares.go +++ b/api/middlewares.go @@ -1,7 +1,7 @@ package api import ( - "memos/common/error" + "memos/api/e" "net/http" ) @@ -10,10 +10,19 @@ func AuthCheckerMiddleWare(next http.Handler) http.Handler { userId, err := GetUserIdInCookie(r) if err != nil || userId == "" { - error.ErrorHandler(w, "NOT_AUTH") + e.ErrorHandler(w, "NOT_AUTH", "Need authorize") return } next.ServeHTTP(w, r) }) } + +func CorsMiddleWare(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + + next.ServeHTTP(w, r) + }) +} diff --git a/api/query.go b/api/query.go new file mode 100644 index 00000000..6a20b36c --- /dev/null +++ b/api/query.go @@ -0,0 +1,110 @@ +package api + +import ( + "encoding/json" + "memos/api/e" + "memos/store" + "net/http" + + "github.com/gorilla/mux" +) + +func handleGetMyQueries(w http.ResponseWriter, r *http.Request) { + userId, _ := GetUserIdInCookie(r) + + queries, err := store.GetQueriesByUserId(userId) + + if err != nil { + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) + return + } + + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: queries, + }) +} + +type QueryPut struct { + Title string `json:"title"` + Querystring string `json:"querystring"` +} + +func handleCreateQuery(w http.ResponseWriter, r *http.Request) { + userId, _ := GetUserIdInCookie(r) + + queryPut := QueryPut{} + err := json.NewDecoder(r.Body).Decode(&queryPut) + + if err != nil { + e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request") + return + } + + query, err := store.CreateNewQuery(queryPut.Title, queryPut.Querystring, userId) + + if err != nil { + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) + return + } + + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: query, + }) +} + +func handleUpdateQuery(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + queryId := vars["id"] + + queryPatch := store.QueryPatch{} + err := json.NewDecoder(r.Body).Decode(&queryPatch) + + if err != nil { + e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request") + return + } + + query, err := store.UpdateQuery(queryId, &queryPatch) + + if err != nil { + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) + return + } + + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: query, + }) +} + +func handleDeleteQuery(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + queryId := vars["id"] + + _, err := store.DeleteQuery(queryId) + + if err != nil { + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) + return + } + + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: nil, + }) +} + +func RegisterQueryRoutes(r *mux.Router) { + queryRouter := r.PathPrefix("/api/query").Subrouter() + + queryRouter.HandleFunc("/all", handleGetMyQueries).Methods("GET") + queryRouter.HandleFunc("/", handleCreateQuery).Methods("PUT") + queryRouter.HandleFunc("/{id}", handleUpdateQuery).Methods("PATCH") + queryRouter.HandleFunc("/{id}", handleDeleteQuery).Methods("DELETE") +} diff --git a/api/spa_handler.go b/api/spa_handler.go new file mode 100644 index 00000000..5c57126e --- /dev/null +++ b/api/spa_handler.go @@ -0,0 +1,34 @@ +package api + +import ( + "net/http" + "os" + "path/filepath" +) + +type SPAHandler struct { + StaticPath string + IndexPath string +} + +func (h SPAHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + path, err := filepath.Abs(r.URL.Path) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + path = filepath.Join(h.StaticPath, path) + + _, err = os.Stat(path) + if os.IsNotExist(err) { + // file does not exist, serve index.html + http.ServeFile(w, r, filepath.Join(h.StaticPath, h.IndexPath)) + return + } else if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + http.FileServer(http.Dir(h.StaticPath)).ServeHTTP(w, r) +} diff --git a/api/user.go b/api/user.go index ffa58836..f627ccd8 100644 --- a/api/user.go +++ b/api/user.go @@ -2,7 +2,7 @@ package api import ( "encoding/json" - "memos/common/error" + "memos/api/e" "memos/store" "net/http" @@ -15,39 +15,95 @@ func handleGetMyUserInfo(w http.ResponseWriter, r *http.Request) { user, err := store.GetUserById(userId) if err != nil { - error.ErrorHandler(w, "DATABASE_ERROR") + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) return } - json.NewEncoder(w).Encode(user) -} - -type UpdateUser struct { - Username string `json:"username"` - Password string `json:"password"` - GithubName string `json:"githubName"` - WxOpenId string `json:"wxOpenId"` + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: user, + }) } func handleUpdateMyUserInfo(w http.ResponseWriter, r *http.Request) { userId, _ := GetUserIdInCookie(r) - user, err := store.GetUserById(userId) + userPatch := store.UserPatch{} + err := json.NewDecoder(r.Body).Decode(&userPatch) if err != nil { - error.ErrorHandler(w, "DATABASE_ERROR") + e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request") return } - var updateUser UpdateUser - err = json.NewDecoder(r.Body).Decode(&updateUser) + user, err := store.UpdateUser(userId, &userPatch) if err != nil { - error.ErrorHandler(w, "REQUEST_BODY_ERROR") + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) return } - json.NewEncoder(w).Encode(user) + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: user, + }) +} + +type CheckUsername struct { + Username string +} + +func handleCheckUsername(w http.ResponseWriter, r *http.Request) { + checkUsername := CheckUsername{} + err := json.NewDecoder(r.Body).Decode(&checkUsername) + + if err != nil { + e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request") + return + } + + usable, err := store.CheckUsernameUsable(checkUsername.Username) + + if err != nil { + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) + return + } + + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: usable, + }) +} + +type ValidPassword struct { + Password string +} + +func handleValidPassword(w http.ResponseWriter, r *http.Request) { + userId, _ := GetUserIdInCookie(r) + validPassword := ValidPassword{} + err := json.NewDecoder(r.Body).Decode(&validPassword) + + if err != nil { + e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request") + return + } + + valid, err := store.CheckPasswordValid(userId, validPassword.Password) + + if err != nil { + e.ErrorHandler(w, "DATABASE_ERROR", err.Error()) + return + } + + json.NewEncoder(w).Encode(Response{ + Succeed: true, + Message: "", + Data: valid, + }) } func RegisterUserRoutes(r *mux.Router) { @@ -57,4 +113,6 @@ func RegisterUserRoutes(r *mux.Router) { userRouter.HandleFunc("/me", handleGetMyUserInfo).Methods("GET") userRouter.HandleFunc("/me", handleUpdateMyUserInfo).Methods("PATCH") + userRouter.HandleFunc("/checkusername", handleCheckUsername).Methods("POST") + userRouter.HandleFunc("/validpassword", handleValidPassword).Methods("POST") } diff --git a/api/utils.go b/api/utils.go index c4eec6cb..149a0735 100644 --- a/api/utils.go +++ b/api/utils.go @@ -4,8 +4,18 @@ import ( "net/http" ) +type Response struct { + Succeed bool `json:"succeed"` + Message string `json:"message"` + Data interface{} `json:"data"` +} + func GetUserIdInCookie(r *http.Request) (string, error) { userIdCookie, err := r.Cookie("user_id") + if err != nil { + return "", err + } + return userIdCookie.Value, err } diff --git a/common/utils.go b/common/utils.go index ac89f3a8..db76158a 100644 --- a/common/utils.go +++ b/common/utils.go @@ -11,5 +11,5 @@ func GenUUID() string { } func GetNowDateTimeStr() string { - return time.Now().Format("RFC3339") + return time.Now().Format("2006/01/02 15:04:05") } diff --git a/main.go b/main.go index 181b5951..50bc2e90 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,15 @@ func main() { api.RegisterUserRoutes(r) api.RegisterAuthRoutes(r) + api.RegisterMemoRoutes(r) + api.RegisterQueryRoutes(r) + + spa := api.SPAHandler{ + StaticPath: "./web/dist", + IndexPath: "index.html", + } + + r.PathPrefix("/").Handler(spa) http.ListenAndServe("localhost:8080", r) } diff --git a/resources/sqlite.sql b/resources/initial_db.sql similarity index 94% rename from resources/sqlite.sql rename to resources/initial_db.sql index 2f8b6d18..a34fe781 100644 --- a/resources/sqlite.sql +++ b/resources/initial_db.sql @@ -1,3 +1,6 @@ +/* + * Re-create tables and insert initial data(todo) + */ CREATE TABLE `users` ( `id` TEXT NOT NULL PRIMARY KEY, `username` TEXT NOT NULL, diff --git a/resources/memos.db b/resources/memos.db index 71a6abcd..ef548529 100644 Binary files a/resources/memos.db and b/resources/memos.db differ diff --git a/scripts/.air.toml b/scripts/.air.toml index d845bf98..c4ba8a3c 100644 --- a/scripts/.air.toml +++ b/scripts/.air.toml @@ -5,7 +5,7 @@ tmp_dir = "tmp" bin = "./tmp/main" cmd = "go build -o ./tmp/main ." delay = 1000 - exclude_dir = ["assets", "tmp", "vendor"] + exclude_dir = ["assets", "tmp", "vendor", "web"] exclude_file = [] exclude_regex = [] exclude_unchanged = false diff --git a/store/memo.go b/store/memo.go index e0e0a67a..2034f7e4 100644 --- a/store/memo.go +++ b/store/memo.go @@ -1,6 +1,9 @@ package store -import "memos/common" +import ( + "memos/common" + "strings" +) type Memo struct { Id string `json:"id"` @@ -28,45 +31,68 @@ func CreateNewMemo(content string, userId string) (Memo, error) { return newMemo, err } -func UpdateMemo(id string, content string, deletedAt string) (Memo, error) { - nowDateTimeStr := common.GetNowDateTimeStr() +type MemoPatch struct { + Content *string + DeletedAt *string +} + +func UpdateMemo(id string, memoPatch *MemoPatch) (Memo, error) { memo, _ := GetMemoById(id) + set, args := []string{}, []interface{}{} - if content != "" { - memo.Content = content + if v := memoPatch.Content; v != nil { + memo.Content = *v + set, args = append(set, "content=?"), append(args, *v) } - if deletedAt != "" { - memo.DeletedAt = deletedAt + if v := memoPatch.DeletedAt; v != nil { + memo.DeletedAt = *v + set, args = append(set, "deleted_at=?"), append(args, *v) } + set, args = append(set, "updated_at=?"), append(args, common.GetNowDateTimeStr()) + args = append(args, id) - memo.UpdatedAt = nowDateTimeStr - - query := `UPDATE memos SET (content, deleted_at, updated_at) VALUES (?, ?, ?)` - _, err := DB.Exec(query, memo.Content, memo.DeletedAt, memo.UpdatedAt) + sqlQuery := `UPDATE memos SET ` + strings.Join(set, ",") + ` WHERE id=?` + _, err := DB.Exec(sqlQuery, args...) return memo, err } +func DeleteMemo(memoId string) (error, error) { + query := `DELETE FROM memos WHERE id=?` + _, err := DB.Exec(query, memoId) + + return nil, err +} + func GetMemoById(id string) (Memo, error) { query := `SELECT id, content, user_id, deleted_at, created_at, updated_at FROM memos WHERE id=?` - var memo Memo + memo := Memo{} err := DB.QueryRow(query, id).Scan(&memo.Id, &memo.Content, &memo.UserId, &memo.DeletedAt, &memo.CreatedAt, &memo.UpdatedAt) return memo, err } -func GetMemosByUserId(userId string) ([]Memo, error) { +func GetMemosByUserId(userId string, deleted bool) ([]Memo, error) { query := `SELECT id, content, user_id, deleted_at, created_at, updated_at FROM memos WHERE user_id=?` - rows, err := DB.Query(query, userId) + if deleted { + query = query + ` AND deleted_at!=""` + } else { + query = query + ` AND deleted_at=""` + } - var memos []Memo + rows, _ := DB.Query(query, userId) + defer rows.Close() + + memos := []Memo{} for rows.Next() { - var memo Memo - err = rows.Scan(&memo.Id, &memo.Content, &memo.UserId, &memo.DeletedAt, &memo.CreatedAt, &memo.UpdatedAt) + memo := Memo{} + rows.Scan(&memo.Id, &memo.Content, &memo.UserId, &memo.DeletedAt, &memo.CreatedAt, &memo.UpdatedAt) memos = append(memos, memo) } + err := rows.Err() + return memos, err } diff --git a/store/query.go b/store/query.go new file mode 100644 index 00000000..1200365d --- /dev/null +++ b/store/query.go @@ -0,0 +1,99 @@ +package store + +import ( + "memos/common" + "strings" +) + +type Query struct { + Id string `json:"id"` + UserId string `json:"userId"` + Title string `json:"title"` + Querystring string `json:"querystring"` + PinnedAt string `json:"pinnedAt"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} + +func CreateNewQuery(title string, querystring string, userId string) (Query, error) { + nowDateTimeStr := common.GetNowDateTimeStr() + newQuery := Query{ + Id: common.GenUUID(), + Title: title, + Querystring: querystring, + UserId: userId, + PinnedAt: "", + CreatedAt: nowDateTimeStr, + UpdatedAt: nowDateTimeStr, + } + + query := `INSERT INTO queries (id, title, querystring, user_id, pinned_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)` + _, err := DB.Exec(query, newQuery.Id, newQuery.Title, newQuery.Querystring, newQuery.UserId, newQuery.PinnedAt, newQuery.CreatedAt, newQuery.UpdatedAt) + + return newQuery, err +} + +type QueryPatch struct { + Title *string + Querystring *string + PinnedAt *string +} + +func UpdateQuery(id string, queryPatch *QueryPatch) (Query, error) { + query, _ := GetQueryById(id) + set, args := []string{}, []interface{}{} + + if v := queryPatch.Title; v != nil { + query.Title = *v + set, args = append(set, "title=?"), append(args, *v) + } + if v := queryPatch.Querystring; v != nil { + query.Querystring = *v + set, args = append(set, "querystring=?"), append(args, *v) + } + if v := queryPatch.PinnedAt; v != nil { + query.PinnedAt = *v + set, args = append(set, "pinned_at=?"), append(args, *v) + } + set, args = append(set, "updated_at=?"), append(args, common.GetNowDateTimeStr()) + args = append(args, id) + + sqlQuery := `UPDATE queries SET ` + strings.Join(set, ",") + ` WHERE id=?` + _, err := DB.Exec(sqlQuery, args...) + + return query, err +} + +func DeleteQuery(queryId string) (error, error) { + query := `DELETE FROM queries WHERE id=?` + _, err := DB.Exec(query, queryId) + + return nil, err +} + +func GetQueryById(queryId string) (Query, error) { + sqlQuery := `SELECT id, title, querystring, user_id, pinned_at, created_at, updated_at FROM queries WHERE id=?` + query := Query{} + err := DB.QueryRow(sqlQuery, queryId).Scan(&query.Id, &query.Title, &query.Querystring, &query.UserId, &query.PinnedAt, &query.CreatedAt, &query.UpdatedAt) + return query, err +} + +func GetQueriesByUserId(userId string) ([]Query, error) { + query := `SELECT id, title, querystring, user_id, pinned_at, created_at, updated_at FROM queries WHERE user_id=?` + + rows, _ := DB.Query(query, userId) + defer rows.Close() + + queries := []Query{} + + for rows.Next() { + query := Query{} + rows.Scan(&query.Id, &query.Title, &query.Querystring, &query.UserId, &query.PinnedAt, &query.CreatedAt, &query.UpdatedAt) + + queries = append(queries, query) + } + + err := rows.Err() + + return queries, err +} diff --git a/store/user.go b/store/user.go index 0a0d855c..67f77827 100644 --- a/store/user.go +++ b/store/user.go @@ -4,6 +4,7 @@ import ( "database/sql" "fmt" "memos/common" + "strings" ) type User struct { @@ -34,55 +35,66 @@ func CreateNewUser(username string, password string, githubName string, wxOpenId return newUser, err } -func UpdateUser(id string, username string, password string, githubName string, wxOpenId string) (User, error) { - nowDateTimeStr := common.GetNowDateTimeStr() +type UserPatch struct { + Username *string + Password *string + GithubName *string + WxOpenId *string +} + +func UpdateUser(id string, userPatch *UserPatch) (User, error) { user, _ := GetUserById(id) + set, args := []string{}, []interface{}{} - if username != "" { - user.Username = username + if v := userPatch.Username; v != nil { + user.Username = *v + set, args = append(set, "username=?"), append(args, *v) } - if password != "" { - user.Password = password + if v := userPatch.Password; v != nil { + user.Password = *v + set, args = append(set, "password=?"), append(args, *v) } - if githubName != "" { - user.GithubName = githubName + if v := userPatch.GithubName; v != nil { + user.GithubName = *v + set, args = append(set, "github_name=?"), append(args, *v) } - if wxOpenId != "" { - user.WxOpenId = wxOpenId + if v := userPatch.WxOpenId; v != nil { + user.WxOpenId = *v + set, args = append(set, "wx_open_id=?"), append(args, *v) } + set, args = append(set, "updated_at=?"), append(args, common.GetNowDateTimeStr()) + args = append(args, id) - user.UpdatedAt = nowDateTimeStr - - query := `UPDATE users SET (username, password, wx_open_id, github_name, updated_at) VALUES (?, ?, ?, ?, ?)` - _, err := DB.Exec(query, user.Username, user.Password, user.WxOpenId, user.GithubName, user.UpdatedAt) + sqlQuery := `UPDATE users SET ` + strings.Join(set, ",") + ` WHERE id=?` + _, err := DB.Exec(sqlQuery, args...) return user, err } func GetUserById(id string) (User, error) { query := `SELECT id, username, password, wx_open_id, github_name, created_at, updated_at FROM users WHERE id=?` - var user User + user := User{} err := DB.QueryRow(query, id).Scan(&user.Id, &user.Username, &user.Password, &user.WxOpenId, &user.GithubName, &user.CreatedAt, &user.UpdatedAt) return user, err } func GetUserByUsernameAndPassword(username string, password string) (User, error) { query := `SELECT id, username, password, wx_open_id, github_name, created_at, updated_at FROM users WHERE username=? AND password=?` - var user User + user := User{} err := DB.QueryRow(query, username, password).Scan(&user.Id, &user.Username, &user.Password, &user.WxOpenId, &user.GithubName, &user.CreatedAt, &user.UpdatedAt) return user, err } func GetUserByGithubName(githubName string) (User, error) { query := `SELECT id, username, password, wx_open_id, github_name, created_at, updated_at FROM users WHERE github_name=?` - var user User + user := User{} err := DB.QueryRow(query, githubName).Scan(&user.Id, &user.Username, &user.Password, &user.WxOpenId, &user.GithubName, &user.CreatedAt, &user.UpdatedAt) return user, err } func GetUserByWxOpenId(wxOpenId string) (User, error) { query := `SELECT id, username, password, wx_open_id, github_name, created_at, updated_at FROM users WHERE id=?` - var user User + user := User{} err := DB.QueryRow(query, wxOpenId).Scan(&user.Id, &user.Username, &user.Password, &user.WxOpenId, &user.GithubName, &user.CreatedAt, &user.UpdatedAt) return user, err }