package routes import ( "backend/database" "bytes" "database/sql" "encoding/json" "errors" "fmt" "github.com/go-chi/chi/v5" "image" _ "image/gif" _ "image/jpeg" _ "image/png" "io" "log" "net/http" "os" "path/filepath" "strconv" "strings" ) func RegisterUsersRoutes(r chi.Router) { r.Get("/", getUsers) r.Get("/me", getCurrentUser) r.Post("/me/avatar", uploadAvatar) r.Route("/me/settings", func(s chi.Router) { s.Get("/", getSettings) s.Post("/", updateSettings) }) r.Route("/{userId:[0-9]+}", func(s chi.Router) { s.Get("/", getUser) s.Put("/", updateUser) }) r.Route("/{userId:[0-9]+}/novels", func(s chi.Router) { s.Get("/", getUserNovels) s.Post("/", addUserNovel) }) r.Route("/{userId:[0-9]+}/novels/{novelId:[0-9]+}", func(s chi.Router) { s.Get("/", getUserNovel) s.Put("/", updateUserNovel) s.Delete("/", deleteUserNovel) }) r.Get("/{userId:[0-9]+}/novels/comments", resolveUserNovelsComments) } func getUsers(w http.ResponseWriter, r *http.Request) { users, err := database.FetchUsers() Response(w, err, users) } func getCurrentUser(w http.ResponseWriter, r *http.Request) { user, err := GetCurrentUser(r) if errors.Is(err, database.ErrNotAuthorized) { NoToken.Ferror(w) return } if errors.Is(err, http.ErrNoCookie) { NoToken.Ferror(w) return } Response(w, err, user) } func uploadAvatar(w http.ResponseWriter, r *http.Request) { user, err := GetCurrentUser(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 := io.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(os.Getenv("CDN_PATH"), "users", "avatars", fmt.Sprintf("%v.%v", user.ID, ext)) err = os.WriteFile(path, fileBytes, 0644) if err != nil { w.WriteHeader(http.StatusInternalServerError) Error(w, err) return } avatarPath := fmt.Sprintf("%v/avatars/%v.%v", os.Getenv("CDN_URL"), user.ID, ext) Response(w, nil, avatarPath) } func getSettings(w http.ResponseWriter, r *http.Request) { id, err := GetCurrentUserID(r) if err != nil { Error(w, err) return } settings, err := database.FetchUserSettings(id) Response(w, err, settings) } func updateSettings(w http.ResponseWriter, r *http.Request) { id, err := GetCurrentUserID(r) settings, err := database.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 := database.UpdateUserSettings(id, settings) Response(w, err, user) } func getUser(w http.ResponseWriter, r *http.Request) { id, _ := strconv.Atoi(chi.URLParam(r, "userId")) user, err := database.FetchUser(id) Response(w, err, user) } func updateUser(w http.ResponseWriter, r *http.Request) { id, _ := strconv.Atoi(chi.URLParam(r, "userId")) currentUser, err := GetCurrentUser(r) if err != nil && !errors.Is(err, database.ErrNotAuthorized) { Error(w, err) return } else if errors.Is(err, database.ErrNotAuthorized) || currentUser == nil { currentUser = &database.User{ID: -1} } if currentUser.ID != int64(id) && !hasPermission(currentUser, "users") { NoAccess.Ferror(w) return } _, err = database.FetchUser(id) if err != nil { Error(w, err) return } body, err := ReadBody(r) if err != nil { Error(w, err) return } var u *database.User err = json.Unmarshal(body, u) if err != nil { Error(w, err) return } user, err := database.UpdateUser(u) Response(w, err, user) } func getUserNovels(w http.ResponseWriter, r *http.Request) { userId, _ := strconv.Atoi(chi.URLParam(r, "userId")) novels, err := database.FetchUserNovels(userId) Response(w, err, novels) } func addUserNovel(w http.ResponseWriter, r *http.Request) { userId, _ := strconv.Atoi(chi.URLParam(r, "userId")) body, err := ReadBody(r) if err != nil { Error(w, err) return } var novel = &database.UserNovels{} err = json.Unmarshal(body, novel) if err != nil { Error(w, err) return } novel.UserID = int64(userId) novel, err = database.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 getUserNovel(w http.ResponseWriter, r *http.Request) { userId := IURLParam(r, "userId") novelId := IURLParam(r, "novelId") novel, err := database.FetchUserNovel(userId, novelId) Response(w, err, novel) } func updateUserNovel(w http.ResponseWriter, r *http.Request) { userId := IURLParam(r, "userId") novelId := IURLParam(r, "novelId") _, err := database.FetchNovel(novelId) if errors.Is(err, sql.ErrNoRows) { NotFound.Ferror(w) return } _, err = database.FetchUser(userId) if errors.Is(err, sql.ErrNoRows) { NotFound.Ferror(w) return } novel, _ := database.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 := database.UpdateUserNovel(novel) Response(w, err, updatedNovel) } func deleteUserNovel(w http.ResponseWriter, r *http.Request) { userId := IURLParam(r, "userId") novelId := IURLParam(r, "novelId") _, err := database.FetchUserNovel(userId, novelId) if errors.Is(err, sql.ErrNoRows) { NotFound.Ferror(w) return } err = database.DeleteUserNovel(userId, novelId) if err != nil { Error(w, err) } w.WriteHeader(http.StatusNoContent) } func resolveUserNovelsComments(w http.ResponseWriter, r *http.Request) { id := IURLParam(r, "userId") comments, err := database.FetchUserNovelsComments(id) Response(w, err, comments) }