package v1 import ( "bytes" "database/sql" "encoding/json" "fmt" "image" _ "image/gif" _ "image/jpeg" _ "image/png" "io/ioutil" "log" "net/http" "path/filepath" "strconv" "strings" "github.com/gorilla/mux" ) func RegisterUsersRoutes(r *mux.Router) { r.HandleFunc("/users", resolveUsers).Methods("GET", "OPTIONS", "POST") r.HandleFunc("/users/me", resolveCurrentUser).Methods("GET", "OPTIONS") r.HandleFunc("/users/me/avatar", uploadAvatar).Methods("OPTIONS", "POST") r.HandleFunc("/users/me/settings", resolveSettings).Methods("GET", "OPTIONS", "POST") r.HandleFunc("/users/{userId:[0-9]+}", resolveUser).Methods("GET", "OPTIONS", "PUT") r.HandleFunc("/users/{userId:[0-9]+}/novels", resolveUserNovels).Methods("GET", "OPTIONS", "POST") r.HandleFunc("/users/{userId:[0-9]+}/novels/{novelId:[0-9]+}", resolveUserNovel).Methods("GET", "OPTIONS", "PUT", "DELETE") r.HandleFunc("/users/{userId:[0-9]+}/novels/comments", resolveUserNovelsComments).Methods("GET", "OPTIONS", "POST") } type Settings struct { UseGravatar bool `json:"use_gravatar" db:"use_gravatar"` Avatar string `json:"avatar"` Theme string `json:"theme"` } func resolveSettings(w http.ResponseWriter, r *http.Request) { id, err := GetCurrentUserID(r) if err != nil { Error(w, err) return } if r.Method == "GET" { settings, err := FetchUserSettings(id) Response(w, err, settings) } else if r.Method == "POST" { settings, err := FetchUserSettings(id) if err != nil { Error(w, err) return } body, err := ReadBody(r) if err != nil { Error(w, err) return } err = json.Unmarshal(body, settings) if err != nil { Error(w, err) return } user, err := UpdateUserSettings(id, settings) Response(w, err, user) } } func resolveUsers(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodGet { users, err := FetchUsers() Response(w, err, users) } else { RawError(w, "Work in progress...") } } func resolveCurrentUser(w http.ResponseWriter, r *http.Request) { user, err := FetchCurrentUser(r) if err == ErrNotAuthorized { NoToken.Ferror(w) return } if err == http.ErrNoCookie { NoToken.Ferror(w) return } Response(w, err, user) } func resolveUser(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.Atoi(vars["userId"]) if r.Method == http.MethodGet { user, err := FetchUser(id) Response(w, err, user) return } currentUser, err := FetchCurrentUser(r) if err != nil && err != ErrNotAuthorized { Error(w, err) return } else if err == ErrNotAuthorized || currentUser == nil { currentUser = &User{ID: -1} } if r.Method == http.MethodPut { if currentUser.ID != int64(id) && !hasPermission(currentUser, "users") { NoAccess.Ferror(w) return } _, err := FetchUser(id) if err != nil { Error(w, err) return } body, err := ReadBody(r) if err != nil { Error(w, err) return } var u *User err = json.Unmarshal(body, u) if err != nil { Error(w, err) return } user, err := UpdateUser(u) Response(w, err, user) } } func uploadAvatar(w http.ResponseWriter, r *http.Request) { user, err := FetchCurrentUser(r) if err != nil { w.WriteHeader(http.StatusInternalServerError) Error(w, err) return } err = r.ParseMultipartForm(user.Group.MaxAvaWeight) if err != nil { w.WriteHeader(http.StatusBadRequest) Error(w, err) return } file, handler, err := r.FormFile("avatar") if err != nil { w.WriteHeader(http.StatusBadRequest) Error(w, err) return } if !strings.HasPrefix(handler.Header["Content-Type"][0], "image") { w.WriteHeader(http.StatusBadRequest) RawError(w, "Wrong document type!") return } fileBytes, err := ioutil.ReadAll(file) if err != nil { w.WriteHeader(http.StatusInternalServerError) Error(w, err) return } err = file.Close() if err != nil { w.WriteHeader(http.StatusInternalServerError) Error(w, err) return } img, _, err := image.DecodeConfig(bytes.NewReader(fileBytes)) if err != nil { log.Println(err) } width, height := img.Width, img.Width if width > user.Group.MaxAvatarSize && height > user.Group.MaxAvatarSize { w.WriteHeader(http.StatusBadRequest) RawError(w, "Image too big!") return } ext := strings.Split(handler.Header["Content-Type"][0], "/")[1] path := filepath.Join(config.CDNPath, "users", "avatars", fmt.Sprintf("%v.%v", user.ID, ext)) err = ioutil.WriteFile(path, fileBytes, 0644) if err != nil { w.WriteHeader(http.StatusInternalServerError) Error(w, err) return } avatarPath := fmt.Sprintf("%v/users/avatars/%v.%v", config.CDNUrl, user.ID, ext) Response(w, nil, avatarPath) } func resolveUserNovels(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) userId, _ := strconv.Atoi(vars["userId"]) if r.Method == http.MethodGet { novels, err := FetchUserNovels(userId) Response(w, err, novels) } else if r.Method == http.MethodPost { body, err := ReadBody(r) if err != nil { Error(w, err) return } var novel = &UserNovels{} err = json.Unmarshal(body, novel) if err != nil { Error(w, err) return } novel.UserID = int64(userId) novel, err = CreateUserNovel(novel) if err == nil { w.WriteHeader(http.StatusCreated) w.Header().Add("Location", fmt.Sprintf("https://%v%v/%v", r.Host, r.URL, novel.NovelID)) } Response(w, err, novel) } } func resolveUserNovel(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) userId, _ := strconv.Atoi(vars["userId"]) novelId, _ := strconv.Atoi(vars["novelId"]) if r.Method == http.MethodGet { novel, err := FetchUserNovel(userId, novelId) Response(w, err, novel) } else if r.Method == http.MethodPut { _, err := FetchNovel(novelId) if err == sql.ErrNoRows { NotFound.Ferror(w) return } _, err = FetchUser(userId) if err == sql.ErrNoRows { NotFound.Ferror(w) return } novel, _ := FetchUserNovel(userId, novelId) body, err := ReadBody(r) if err != nil { Error(w, err) return } if novel == nil { NotFound.Ferror(w) return } err = json.Unmarshal(body, &novel) if err != nil { Error(w, err) return } updatedNovel, err := UpdateUserNovel(novel) lpServer.PushEvent(updatedNovel, uint64(userId), "update-user-novel") Response(w, err, updatedNovel) } else if r.Method == http.MethodDelete { _, err := FetchUserNovel(userId, novelId) if err == sql.ErrNoRows { NotFound.Ferror(w) return } err = DeleteUserNovel(userId, novelId) if err != nil { Error(w, err) } w.WriteHeader(http.StatusNoContent) } } func resolveUserNovelsComments(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id, _ := strconv.Atoi(vars["userId"]) comments, err := FetchUserNovelsComments(id) Response(w, err, comments) }