mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
update restful api
This commit is contained in:
42
api/auth.go
42
api/auth.go
@ -2,7 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"memos/common/error"
|
"memos/api/e"
|
||||||
"memos/store"
|
"memos/store"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@ -15,22 +15,34 @@ type UserSignUp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleUserSignUp(w http.ResponseWriter, r *http.Request) {
|
func handleUserSignUp(w http.ResponseWriter, r *http.Request) {
|
||||||
var userSignup UserSignUp
|
userSignup := UserSignUp{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&userSignup)
|
err := json.NewDecoder(r.Body).Decode(&userSignup)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "REQUEST_BODY_ERROR")
|
e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := store.CreateNewUser(userSignup.Username, userSignup.Password, "", "")
|
user, err := store.CreateNewUser(userSignup.Username, userSignup.Password, "", "")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "")
|
e.ErrorHandler(w, "DATABASE_ERROR", err.Error())
|
||||||
return
|
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 {
|
type UserSignin struct {
|
||||||
@ -39,38 +51,50 @@ type UserSignin struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleUserSignIn(w http.ResponseWriter, r *http.Request) {
|
func handleUserSignIn(w http.ResponseWriter, r *http.Request) {
|
||||||
var userSignin UserSignin
|
userSignin := UserSignin{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&userSignin)
|
err := json.NewDecoder(r.Body).Decode(&userSignin)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "")
|
e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := store.GetUserByUsernameAndPassword(userSignin.Username, userSignin.Password)
|
user, err := store.GetUserByUsernameAndPassword(userSignin.Username, userSignin.Password)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "")
|
e.ErrorHandler(w, "DATABASE_ERROR", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userIdCookie := &http.Cookie{
|
userIdCookie := &http.Cookie{
|
||||||
Name: "user_id",
|
Name: "user_id",
|
||||||
Value: user.Id,
|
Value: user.Id,
|
||||||
|
Path: "/",
|
||||||
MaxAge: 3600 * 24 * 30,
|
MaxAge: 3600 * 24 * 30,
|
||||||
}
|
}
|
||||||
http.SetCookie(w, userIdCookie)
|
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) {
|
func handleUserSignOut(w http.ResponseWriter, r *http.Request) {
|
||||||
userIdCookie := &http.Cookie{
|
userIdCookie := &http.Cookie{
|
||||||
Name: "user_id",
|
Name: "user_id",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
Path: "/",
|
||||||
MaxAge: 0,
|
MaxAge: 0,
|
||||||
}
|
}
|
||||||
http.SetCookie(w, userIdCookie)
|
http.SetCookie(w, userIdCookie)
|
||||||
|
|
||||||
|
json.NewEncoder(w).Encode(Response{
|
||||||
|
Succeed: true,
|
||||||
|
Message: "",
|
||||||
|
Data: nil,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterAuthRoutes(r *mux.Router) {
|
func RegisterAuthRoutes(r *mux.Router) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package error
|
package e
|
||||||
|
|
||||||
var Codes = map[string]int{
|
var Codes = map[string]int{
|
||||||
"NOT_AUTH": 20001,
|
"NOT_AUTH": 20001,
|
@ -1,4 +1,4 @@
|
|||||||
package error
|
package e
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -11,17 +11,19 @@ type ServerError struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ErrorResponse struct {
|
type ErrorResponse struct {
|
||||||
StatusCode int `json:"statusCode"`
|
Succeed bool `json:"succeed"`
|
||||||
StatusMessage string `json:"statusMessage"`
|
Message string `json:"message"`
|
||||||
Succeed bool `json:"succeed"`
|
StatusCode int `json:"statusCode"`
|
||||||
Data interface{} `json:"data"`
|
Data interface{} `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServerError(err string) ServerError {
|
func getServerError(err string) ServerError {
|
||||||
code, exists := Codes[err]
|
code, exists := Codes[err]
|
||||||
|
|
||||||
|
println(err)
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
err = "Bad Request"
|
err = "BAD_REQUEST"
|
||||||
code = 40000
|
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)
|
serverError := getServerError(err)
|
||||||
|
|
||||||
res := ErrorResponse{
|
res := ErrorResponse{
|
||||||
StatusCode: serverError.Code,
|
Succeed: false,
|
||||||
StatusMessage: serverError.Message,
|
Message: message,
|
||||||
Succeed: false,
|
StatusCode: serverError.Code,
|
||||||
Data: nil,
|
Data: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
statusCode := int(serverError.Code / 100)
|
statusCode := int(serverError.Code / 100)
|
66
api/memo.go
66
api/memo.go
@ -2,7 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"memos/common/error"
|
"memos/api/e"
|
||||||
"memos/store"
|
"memos/store"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@ -11,15 +11,22 @@ import (
|
|||||||
|
|
||||||
func handleGetMyMemos(w http.ResponseWriter, r *http.Request) {
|
func handleGetMyMemos(w http.ResponseWriter, r *http.Request) {
|
||||||
userId, _ := GetUserIdInCookie(r)
|
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 {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "DATABASE_ERROR")
|
e.ErrorHandler(w, "DATABASE_ERROR", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(memos)
|
json.NewEncoder(w).Encode(Response{
|
||||||
|
Succeed: true,
|
||||||
|
Message: "",
|
||||||
|
Data: memos,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateMemo struct {
|
type CreateMemo struct {
|
||||||
@ -29,54 +36,77 @@ type CreateMemo struct {
|
|||||||
func handleCreateMemo(w http.ResponseWriter, r *http.Request) {
|
func handleCreateMemo(w http.ResponseWriter, r *http.Request) {
|
||||||
userId, _ := GetUserIdInCookie(r)
|
userId, _ := GetUserIdInCookie(r)
|
||||||
|
|
||||||
var createMemo CreateMemo
|
createMemo := CreateMemo{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&createMemo)
|
err := json.NewDecoder(r.Body).Decode(&createMemo)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "")
|
e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
memo, err := store.CreateNewMemo(createMemo.Content, userId)
|
memo, err := store.CreateNewMemo(createMemo.Content, userId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "")
|
e.ErrorHandler(w, "DATABASE_ERROR", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(memo)
|
json.NewEncoder(w).Encode(Response{
|
||||||
|
Succeed: true,
|
||||||
|
Message: "",
|
||||||
|
Data: memo,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleUpdateMemo(w http.ResponseWriter, r *http.Request) {
|
func handleUpdateMemo(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
memoId := vars["id"]
|
memoId := vars["id"]
|
||||||
|
|
||||||
userId, _ := GetUserIdInCookie(r)
|
memoPatch := store.MemoPatch{}
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&memoPatch)
|
||||||
var createMemo CreateMemo
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&createMemo)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "")
|
e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
memo, err := store.UpdateMemo(memoId, createMemo.Content, userId)
|
memo, err := store.UpdateMemo(memoId, &memoPatch)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "")
|
e.ErrorHandler(w, "DATABASE_ERROR", err.Error())
|
||||||
return
|
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) {
|
func RegisterMemoRoutes(r *mux.Router) {
|
||||||
memoRouter := r.PathPrefix("/api/memo").Subrouter()
|
memoRouter := r.PathPrefix("/api/memo").Subrouter()
|
||||||
|
|
||||||
memoRouter.Use(AuthCheckerMiddleWare)
|
|
||||||
|
|
||||||
memoRouter.HandleFunc("/all", handleGetMyMemos).Methods("GET")
|
memoRouter.HandleFunc("/all", handleGetMyMemos).Methods("GET")
|
||||||
memoRouter.HandleFunc("/", handleCreateMemo).Methods("PUT")
|
memoRouter.HandleFunc("/", handleCreateMemo).Methods("PUT")
|
||||||
memoRouter.HandleFunc("/{id}", handleUpdateMemo).Methods("PATCH")
|
memoRouter.HandleFunc("/{id}", handleUpdateMemo).Methods("PATCH")
|
||||||
|
memoRouter.HandleFunc("/{id}", handleDeleteMemo).Methods("DELETE")
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"memos/common/error"
|
"memos/api/e"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -10,10 +10,19 @@ func AuthCheckerMiddleWare(next http.Handler) http.Handler {
|
|||||||
userId, err := GetUserIdInCookie(r)
|
userId, err := GetUserIdInCookie(r)
|
||||||
|
|
||||||
if err != nil || userId == "" {
|
if err != nil || userId == "" {
|
||||||
error.ErrorHandler(w, "NOT_AUTH")
|
e.ErrorHandler(w, "NOT_AUTH", "Need authorize")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
next.ServeHTTP(w, r)
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
110
api/query.go
Normal file
110
api/query.go
Normal file
@ -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")
|
||||||
|
}
|
34
api/spa_handler.go
Normal file
34
api/spa_handler.go
Normal file
@ -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)
|
||||||
|
}
|
90
api/user.go
90
api/user.go
@ -2,7 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"memos/common/error"
|
"memos/api/e"
|
||||||
"memos/store"
|
"memos/store"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@ -15,39 +15,95 @@ func handleGetMyUserInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
user, err := store.GetUserById(userId)
|
user, err := store.GetUserById(userId)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "DATABASE_ERROR")
|
e.ErrorHandler(w, "DATABASE_ERROR", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
json.NewEncoder(w).Encode(user)
|
json.NewEncoder(w).Encode(Response{
|
||||||
}
|
Succeed: true,
|
||||||
|
Message: "",
|
||||||
type UpdateUser struct {
|
Data: user,
|
||||||
Username string `json:"username"`
|
})
|
||||||
Password string `json:"password"`
|
|
||||||
GithubName string `json:"githubName"`
|
|
||||||
WxOpenId string `json:"wxOpenId"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleUpdateMyUserInfo(w http.ResponseWriter, r *http.Request) {
|
func handleUpdateMyUserInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
userId, _ := GetUserIdInCookie(r)
|
userId, _ := GetUserIdInCookie(r)
|
||||||
|
|
||||||
user, err := store.GetUserById(userId)
|
userPatch := store.UserPatch{}
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&userPatch)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "DATABASE_ERROR")
|
e.ErrorHandler(w, "REQUEST_BODY_ERROR", "Bad request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var updateUser UpdateUser
|
user, err := store.UpdateUser(userId, &userPatch)
|
||||||
err = json.NewDecoder(r.Body).Decode(&updateUser)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
error.ErrorHandler(w, "REQUEST_BODY_ERROR")
|
e.ErrorHandler(w, "DATABASE_ERROR", err.Error())
|
||||||
return
|
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) {
|
func RegisterUserRoutes(r *mux.Router) {
|
||||||
@ -57,4 +113,6 @@ func RegisterUserRoutes(r *mux.Router) {
|
|||||||
|
|
||||||
userRouter.HandleFunc("/me", handleGetMyUserInfo).Methods("GET")
|
userRouter.HandleFunc("/me", handleGetMyUserInfo).Methods("GET")
|
||||||
userRouter.HandleFunc("/me", handleUpdateMyUserInfo).Methods("PATCH")
|
userRouter.HandleFunc("/me", handleUpdateMyUserInfo).Methods("PATCH")
|
||||||
|
userRouter.HandleFunc("/checkusername", handleCheckUsername).Methods("POST")
|
||||||
|
userRouter.HandleFunc("/validpassword", handleValidPassword).Methods("POST")
|
||||||
}
|
}
|
||||||
|
10
api/utils.go
10
api/utils.go
@ -4,8 +4,18 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Succeed bool `json:"succeed"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
func GetUserIdInCookie(r *http.Request) (string, error) {
|
func GetUserIdInCookie(r *http.Request) (string, error) {
|
||||||
userIdCookie, err := r.Cookie("user_id")
|
userIdCookie, err := r.Cookie("user_id")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
return userIdCookie.Value, err
|
return userIdCookie.Value, err
|
||||||
}
|
}
|
||||||
|
@ -11,5 +11,5 @@ func GenUUID() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetNowDateTimeStr() string {
|
func GetNowDateTimeStr() string {
|
||||||
return time.Now().Format("RFC3339")
|
return time.Now().Format("2006/01/02 15:04:05")
|
||||||
}
|
}
|
||||||
|
9
main.go
9
main.go
@ -15,6 +15,15 @@ func main() {
|
|||||||
|
|
||||||
api.RegisterUserRoutes(r)
|
api.RegisterUserRoutes(r)
|
||||||
api.RegisterAuthRoutes(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)
|
http.ListenAndServe("localhost:8080", r)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
/*
|
||||||
|
* Re-create tables and insert initial data(todo)
|
||||||
|
*/
|
||||||
CREATE TABLE `users` (
|
CREATE TABLE `users` (
|
||||||
`id` TEXT NOT NULL PRIMARY KEY,
|
`id` TEXT NOT NULL PRIMARY KEY,
|
||||||
`username` TEXT NOT NULL,
|
`username` TEXT NOT NULL,
|
Binary file not shown.
@ -5,7 +5,7 @@ tmp_dir = "tmp"
|
|||||||
bin = "./tmp/main"
|
bin = "./tmp/main"
|
||||||
cmd = "go build -o ./tmp/main ."
|
cmd = "go build -o ./tmp/main ."
|
||||||
delay = 1000
|
delay = 1000
|
||||||
exclude_dir = ["assets", "tmp", "vendor"]
|
exclude_dir = ["assets", "tmp", "vendor", "web"]
|
||||||
exclude_file = []
|
exclude_file = []
|
||||||
exclude_regex = []
|
exclude_regex = []
|
||||||
exclude_unchanged = false
|
exclude_unchanged = false
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import "memos/common"
|
import (
|
||||||
|
"memos/common"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type Memo struct {
|
type Memo struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
@ -28,45 +31,68 @@ func CreateNewMemo(content string, userId string) (Memo, error) {
|
|||||||
return newMemo, err
|
return newMemo, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateMemo(id string, content string, deletedAt string) (Memo, error) {
|
type MemoPatch struct {
|
||||||
nowDateTimeStr := common.GetNowDateTimeStr()
|
Content *string
|
||||||
|
DeletedAt *string
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateMemo(id string, memoPatch *MemoPatch) (Memo, error) {
|
||||||
memo, _ := GetMemoById(id)
|
memo, _ := GetMemoById(id)
|
||||||
|
set, args := []string{}, []interface{}{}
|
||||||
|
|
||||||
if content != "" {
|
if v := memoPatch.Content; v != nil {
|
||||||
memo.Content = content
|
memo.Content = *v
|
||||||
|
set, args = append(set, "content=?"), append(args, *v)
|
||||||
}
|
}
|
||||||
if deletedAt != "" {
|
if v := memoPatch.DeletedAt; v != nil {
|
||||||
memo.DeletedAt = deletedAt
|
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
|
sqlQuery := `UPDATE memos SET ` + strings.Join(set, ",") + ` WHERE id=?`
|
||||||
|
_, err := DB.Exec(sqlQuery, args...)
|
||||||
query := `UPDATE memos SET (content, deleted_at, updated_at) VALUES (?, ?, ?)`
|
|
||||||
_, err := DB.Exec(query, memo.Content, memo.DeletedAt, memo.UpdatedAt)
|
|
||||||
|
|
||||||
return memo, err
|
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) {
|
func GetMemoById(id string) (Memo, error) {
|
||||||
query := `SELECT id, content, user_id, deleted_at, created_at, updated_at FROM memos WHERE id=?`
|
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)
|
err := DB.QueryRow(query, id).Scan(&memo.Id, &memo.Content, &memo.UserId, &memo.DeletedAt, &memo.CreatedAt, &memo.UpdatedAt)
|
||||||
return memo, err
|
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=?`
|
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() {
|
for rows.Next() {
|
||||||
var memo Memo
|
memo := Memo{}
|
||||||
err = rows.Scan(&memo.Id, &memo.Content, &memo.UserId, &memo.DeletedAt, &memo.CreatedAt, &memo.UpdatedAt)
|
rows.Scan(&memo.Id, &memo.Content, &memo.UserId, &memo.DeletedAt, &memo.CreatedAt, &memo.UpdatedAt)
|
||||||
|
|
||||||
memos = append(memos, memo)
|
memos = append(memos, memo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := rows.Err()
|
||||||
|
|
||||||
return memos, err
|
return memos, err
|
||||||
}
|
}
|
||||||
|
99
store/query.go
Normal file
99
store/query.go
Normal file
@ -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
|
||||||
|
}
|
@ -4,6 +4,7 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"memos/common"
|
"memos/common"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
@ -34,55 +35,66 @@ func CreateNewUser(username string, password string, githubName string, wxOpenId
|
|||||||
return newUser, err
|
return newUser, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateUser(id string, username string, password string, githubName string, wxOpenId string) (User, error) {
|
type UserPatch struct {
|
||||||
nowDateTimeStr := common.GetNowDateTimeStr()
|
Username *string
|
||||||
|
Password *string
|
||||||
|
GithubName *string
|
||||||
|
WxOpenId *string
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateUser(id string, userPatch *UserPatch) (User, error) {
|
||||||
user, _ := GetUserById(id)
|
user, _ := GetUserById(id)
|
||||||
|
set, args := []string{}, []interface{}{}
|
||||||
|
|
||||||
if username != "" {
|
if v := userPatch.Username; v != nil {
|
||||||
user.Username = username
|
user.Username = *v
|
||||||
|
set, args = append(set, "username=?"), append(args, *v)
|
||||||
}
|
}
|
||||||
if password != "" {
|
if v := userPatch.Password; v != nil {
|
||||||
user.Password = password
|
user.Password = *v
|
||||||
|
set, args = append(set, "password=?"), append(args, *v)
|
||||||
}
|
}
|
||||||
if githubName != "" {
|
if v := userPatch.GithubName; v != nil {
|
||||||
user.GithubName = githubName
|
user.GithubName = *v
|
||||||
|
set, args = append(set, "github_name=?"), append(args, *v)
|
||||||
}
|
}
|
||||||
if wxOpenId != "" {
|
if v := userPatch.WxOpenId; v != nil {
|
||||||
user.WxOpenId = wxOpenId
|
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
|
sqlQuery := `UPDATE users SET ` + strings.Join(set, ",") + ` WHERE id=?`
|
||||||
|
_, err := DB.Exec(sqlQuery, args...)
|
||||||
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)
|
|
||||||
|
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserById(id string) (User, error) {
|
func GetUserById(id string) (User, error) {
|
||||||
query := `SELECT id, username, password, wx_open_id, github_name, created_at, updated_at FROM users WHERE id=?`
|
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)
|
err := DB.QueryRow(query, id).Scan(&user.Id, &user.Username, &user.Password, &user.WxOpenId, &user.GithubName, &user.CreatedAt, &user.UpdatedAt)
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserByUsernameAndPassword(username string, password string) (User, error) {
|
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=?`
|
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)
|
err := DB.QueryRow(query, username, password).Scan(&user.Id, &user.Username, &user.Password, &user.WxOpenId, &user.GithubName, &user.CreatedAt, &user.UpdatedAt)
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserByGithubName(githubName string) (User, error) {
|
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=?`
|
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)
|
err := DB.QueryRow(query, githubName).Scan(&user.Id, &user.Username, &user.Password, &user.WxOpenId, &user.GithubName, &user.CreatedAt, &user.UpdatedAt)
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserByWxOpenId(wxOpenId string) (User, error) {
|
func GetUserByWxOpenId(wxOpenId string) (User, error) {
|
||||||
query := `SELECT id, username, password, wx_open_id, github_name, created_at, updated_at FROM users WHERE id=?`
|
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)
|
err := DB.QueryRow(query, wxOpenId).Scan(&user.Id, &user.Username, &user.Password, &user.WxOpenId, &user.GithubName, &user.CreatedAt, &user.UpdatedAt)
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user