diff --git a/controllers/api_handler.go b/controllers/api_handler.go index db30e333..b60d3929 100644 --- a/controllers/api_handler.go +++ b/controllers/api_handler.go @@ -10,14 +10,17 @@ import ( "github.com/NyaaPantsu/nyaa/config" "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/models/torrents" - "github.com/NyaaPantsu/nyaa/service" - "github.com/NyaaPantsu/nyaa/service/api" + "github.com/NyaaPantsu/nyaa/models/users" "github.com/NyaaPantsu/nyaa/utils/cookies" "github.com/NyaaPantsu/nyaa/utils/crypto" "github.com/NyaaPantsu/nyaa/utils/log" msg "github.com/NyaaPantsu/nyaa/utils/messages" "github.com/NyaaPantsu/nyaa/utils/search" + "github.com/NyaaPantsu/nyaa/utils/search/structs" "github.com/NyaaPantsu/nyaa/utils/upload" + "github.com/NyaaPantsu/nyaa/utils/validator" + "github.com/NyaaPantsu/nyaa/utils/validator/torrent" + "github.com/NyaaPantsu/nyaa/utils/validator/user" "github.com/gin-gonic/gin" ) @@ -28,8 +31,8 @@ func APIHandler(c *gin.Context) { RSSTorznabHandler(c) } else { page := c.Param("page") - whereParams := serviceBase.WhereParams{} - req := apiService.TorrentsRequest{} + whereParams := structs.WhereParams{} + req := upload.TorrentsRequest{} contentType := c.Request.Header.Get("Content-Type") if contentType == "application/json" { @@ -69,13 +72,13 @@ func APIHandler(c *gin.Context) { } } - torrents, nbTorrents, err := torrentService.GetTorrents(whereParams, req.MaxPerPage, req.MaxPerPage*(req.Page-1)) + torrents, nbTorrents, err := torrents.Find(whereParams, req.MaxPerPage, req.MaxPerPage*(req.Page-1)) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } - b := models.APIResultJSON{ + b := upload.APIResultJSON{ Torrents: models.APITorrentsToJSON(torrents), } b.QueryRecordCount = req.MaxPerPage @@ -91,9 +94,9 @@ func APIHandler(c *gin.Context) { // APIViewHandler : Controller for viewing a torrent by its ID func APIViewHandler(c *gin.Context) { - id := c.Param("id") + id, _ := strconv.ParseInt(c.Param("id"), 10, 32) - torrent, err := torrentService.GetTorrentByID(id) + torrent, err := torrents.FindByID(uint(id)) if err != nil { c.AbortWithError(http.StatusNotFound, err) @@ -113,7 +116,7 @@ func APIViewHeadHandler(c *gin.Context) { return } - _, err = torrentService.GetRawTorrentByID(uint(id)) + _, err = torrents.FindRawByID(uint(id)) if err != nil { NotFoundHandler(c) @@ -127,86 +130,53 @@ func APIViewHeadHandler(c *gin.Context) { func APIUploadHandler(c *gin.Context) { token := c.Request.Header.Get("Authorization") username := c.PostForm("username") - user, _, _, _, err := userService.RetrieveUserByAPITokenAndName(token, username) + user, _, _, _, err := users.FindByAPITokenAndName(token, username) messages := msg.GetMessages(c) if err != nil { messages.AddErrorT("errors", "error_api_token") } - if !uploadService.IsUploadEnabled(user) { + if !user.CanUpload() { messages.AddErrorT("errors", "uploads_disabled") } if user.ID == 0 { - messages.Error(apiService.ErrAPIKey) + messages.AddErrorT("errors", "error_api_token") } if !messages.HasErrors() { - upload := apiService.TorrentRequest{} + uploadForm := torrentValidator.TorrentRequest{} contentType := c.Request.Header.Get("Content-Type") if contentType != "application/json" && !strings.HasPrefix(contentType, "multipart/form-data") && contentType != "application/x-www-form-urlencoded" { - // TODO What should we do here ? upload is empty so we shouldn't + // TODO What should we do here ? uploadForm is empty so we shouldn't // create a torrent from it messages.AddErrorT("errors", "error_content_type_post") } // As long as the right content-type is sent, formValue is smart enough to parse it - err = upload.ExtractInfo(c) + err = upload.ExtractInfo(c, &uploadForm) if err != nil { messages.Error(err) } if !messages.HasErrors() { - status := models.TorrentStatusNormal - if upload.Remake { // overrides trusted - status = models.TorrentStatusRemake + uploadForm.Status = models.TorrentStatusNormal + if uploadForm.Remake { // overrides trusted + uploadForm.Status = models.TorrentStatusRemake } else if user.IsTrusted() { - status = models.TorrentStatusTrusted + uploadForm.Status = models.TorrentStatusTrusted } - err = torrentService.ExistOrDelete(upload.Infohash, user) + err = torrents.ExistOrDelete(uploadForm.Infohash, user) if err != nil { messages.Error(err) } if !messages.HasErrors() { - torrent := models.Torrent{ - Name: upload.Name, - Category: upload.CategoryID, - SubCategory: upload.SubCategoryID, - Status: status, - Hidden: upload.Hidden, - Hash: upload.Infohash, - Date: time.Now(), - Filesize: upload.Filesize, - Description: upload.Description, - WebsiteLink: upload.WebsiteLink, - UploaderID: user.ID} - torrent.ParseTrackers(upload.Trackers) - db.ORM.Create(&torrent) - - if db.ElasticSearchClient != nil { - err := torrent.AddToESIndex(db.ElasticSearchClient) - if err == nil { - log.Infof("Successfully added torrent to ES index.") - } else { - log.Errorf("Unable to add torrent to ES index: %s", err) - } - } else { - log.Errorf("Unable to create elasticsearch client: %s", err) + torrent, err := torrents.Create(user, &uploadForm) + if err != nil { + messages.Error(err) } messages.AddInfoT("infos", "torrent_uploaded") - torrentService.NewTorrentEvent(user, &torrent) - // add filelist to files db, if we have one - if len(upload.FileList) > 0 { - for _, uploadedFile := range upload.FileList { - file := models.File{TorrentID: torrent.ID, Filesize: upload.Filesize} - err := file.SetPath(uploadedFile.Path) - if err != nil { - c.AbortWithError(http.StatusBadRequest, err) - return - } - db.ORM.Create(&file) - } - } + apiResponseHandler(c, torrent.ToJSON()) return } @@ -220,7 +190,7 @@ func APIUploadHandler(c *gin.Context) { func APIUpdateHandler(c *gin.Context) { token := c.Request.Header.Get("Authorization") username := c.PostForm("username") - user, _, _, _, err := userService.RetrieveUserByAPITokenAndName(token, username) + user, _, _, _, err := users.FindByAPITokenAndName(token, username) messages := msg.GetMessages(c) @@ -228,12 +198,12 @@ func APIUpdateHandler(c *gin.Context) { messages.AddErrorT("errors", "error_api_token") } - if !uploadService.IsUploadEnabled(user) { + if !user.CanUpload() { messages.AddErrorT("errors", "uploads_disabled") } if user.ID == 0 { - messages.Error(apiService.ErrAPIKey) + messages.AddErrorT("errors", "error_api_token") } contentType := c.Request.Header.Get("Content-Type") if contentType != "application/json" && !strings.HasPrefix(contentType, "multipart/form-data") && contentType != "application/x-www-form-urlencoded" { @@ -241,22 +211,21 @@ func APIUpdateHandler(c *gin.Context) { // create a torrent from it messages.AddErrorT("errors", "error_content_type_post") } - update := apiService.UpdateRequest{} - err = update.Update.ExtractEditInfo(c) + update := torrentValidator.UpdateRequest{} + err = upload.ExtractEditInfo(c, &update.Update) if err != nil { messages.Error(err) } if !messages.HasErrors() { - torrent := models.Torrent{} - db.ORM.Where("torrent_id = ?", c.PostForm("id")).First(&torrent) - if torrent.ID == 0 { - messages.Error(apiService.ErrTorrentID) + c.Bind(&update) + torrent, err := torrents.FindByID(update.ID) + if err != nil { + messages.AddErrorTf("errors", "torrent_not_exist", strconv.Itoa(int(update.ID))) } if torrent.UploaderID != 0 && torrent.UploaderID != user.ID { //&& user.Status != mod - messages.Error(apiService.ErrRights) + messages.AddErrorT("errors", "fail_torrent_update") } - update.UpdateTorrent(&torrent, user) - torrentService.UpdateTorrent(&torrent) + upload.UpdateTorrent(&update, &torrent, user).Update(false) } apiResponseHandler(c) } @@ -293,7 +262,7 @@ func APISearchHandler(c *gin.Context) { // APILoginHandler : Login with API // This is not an OAuth api like and shouldn't be used for anything except getting the API Token in order to not store a password func APILoginHandler(c *gin.Context) { - b := form.LoginForm{} + b := userValidator.LoginForm{} messages := msg.GetMessages(c) contentType := c.Request.Header.Get("Content-type") if !strings.HasPrefix(contentType, "application/json") && !strings.HasPrefix(contentType, "multipart/form-data") && !strings.HasPrefix(contentType, "application/x-www-form-urlencoded") { @@ -302,9 +271,9 @@ func APILoginHandler(c *gin.Context) { messages.AddErrorT("errors", "error_content_type_post") } c.Bind(&b) - modelHelper.ValidateForm(&b, messages) + validator.ValidateForm(&b, messages) if !messages.HasErrors() { - user, _, errorUser := userService.CreateUserAuthenticationAPI(c, &b) + user, _, errorUser := cookies.CreateUserAuthentication(c, &b) if errorUser == nil { messages.AddInfo("infos", "Logged") apiResponseHandler(c, user.ToJSON()) @@ -319,7 +288,7 @@ func APILoginHandler(c *gin.Context) { func APIRefreshTokenHandler(c *gin.Context) { token := c.Request.Header.Get("Authorization") username := c.PostForm("username") - user, _, _, _, err := userService.RetrieveUserByAPITokenAndName(token, username) + user, _, _, _, err := users.FindByAPITokenAndName(token, username) messages := msg.GetMessages(c) if err != nil { @@ -328,7 +297,7 @@ func APIRefreshTokenHandler(c *gin.Context) { if !messages.HasErrors() { user.APIToken, _ = crypto.GenerateRandomToken32() user.APITokenExpiry = time.Unix(0, 0) - _, errorUser := userService.UpdateRawUser(user) + _, errorUser := user.UpdateRaw() if errorUser == nil { messages.AddInfoT("infos", "profile_updated") apiResponseHandler(c, user.ToJSON()) @@ -343,7 +312,7 @@ func APIRefreshTokenHandler(c *gin.Context) { func APICheckTokenHandler(c *gin.Context) { token := c.Request.Header.Get("Authorization") username := c.PostForm("username") - user, _, _, _, err := userService.RetrieveUserByAPITokenAndName(token, username) + user, _, _, _, err := users.FindByAPITokenAndName(token, username) messages := msg.GetMessages(c) if err != nil { diff --git a/controllers/helpers.go b/controllers/helpers.go index a5190dfe..6976c942 100644 --- a/controllers/helpers.go +++ b/controllers/helpers.go @@ -1,9 +1,9 @@ package controllers import ( - "github.com/NyaaPantsu/nyaa/common" "github.com/NyaaPantsu/nyaa/models" - "github.com/NyaaPantsu/nyaa/utils/cookies" + "github.com/NyaaPantsu/nyaa/models/users" + "github.com/NyaaPantsu/nyaa/utils/search/structs" "github.com/gin-gonic/gin" ) @@ -15,7 +15,7 @@ type navigation struct { } type searchForm struct { - common.SearchParam + structs.TorrentParam Category string ShowItemsPerPage bool SizeType string @@ -47,6 +47,6 @@ func newSearchForm(c *gin.Context) searchForm { } } func getUser(c *gin.Context) *models.User { - user, _, _ := userService.RetrieveCurrentUser(c) - return &user + user, _, _ := users.CurrentUser(c) + return user } diff --git a/models/reports/interaction.go b/models/reports/interaction.go index bf6180bf..0ef948bc 100644 --- a/models/reports/interaction.go +++ b/models/reports/interaction.go @@ -3,11 +3,12 @@ package reports import ( "errors" "net/http" + "nyaa-master/db" "strconv" "strings" - "github.com/NyaaPantsu/nyaa/service" "github.com/NyaaPantsu/nyaa/models" + "github.com/NyaaPantsu/nyaa/utils/search/structs" ) // Create : Return torrentReport in case we did modified it (ie: CreatedAt field) @@ -20,30 +21,30 @@ func Create(torrentReport models.TorrentReport) error { // Delete : Delete a torrent report by id func Delete(id uint) (*models.TorrentReport, int, error) { - return deleteTorrent(id, false) + return delete(id, false) } // DeleteDefinitely : Delete definitely a torrent report by id -func DeleteDefinitely(id uint) (int, error) { - return deleteTorrent(id, true) +func DeleteDefinitely(id uint) (*models.TorrentReport, int, error) { + return delete(id, true) } -func deleteTorrent(id uint, definitely bool) { +func delete(id uint, definitely bool) (*models.TorrentReport, int, error) { var torrentReport models.TorrentReport db := models.ORM - if (definitely) { + if definitely { db = models.ORM.Unscoped() } if db.First(&torrentReport, id).RecordNotFound() { - return errors.New("try_to_delete_report_inexistant"), http.StatusNotFound + return &torrentReport, http.StatusNotFound, errors.New("try_to_delete_report_inexistant") } if _, err := torrentReport.Delete(false); err != nil { - return &torrentReport, err, http.StatusInternalServerError + return &torrentReport, http.StatusInternalServerError, err } return &torrentReport, http.StatusOK, nil } -func findOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int, countAll bool) ( +func findOrderBy(parameters *structs.WhereParams, orderBy string, limit int, offset int, countAll bool) ( torrentReports []models.TorrentReport, count int, err error, ) { var conditionArray []string @@ -80,11 +81,11 @@ func findOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, } // GetOrderBy : Get torrents reports based on search parameters with order -func FindOrderBy(parameters *serviceBase.WhereParams, orderBy string, limit int, offset int) ([]models.TorrentReport, int, error) { +func FindOrderBy(parameters *structs.WhereParams, orderBy string, limit int, offset int) ([]models.TorrentReport, int, error) { return findOrderBy(parameters, orderBy, limit, offset, true) } // GetAll : Get all torrents report func GetAll(limit int, offset int) ([]models.TorrentReport, int, error) { - return findOrderBy(nil, "", limit, offset) + return FindOrderBy(nil, "", limit, offset) } diff --git a/models/torrents/create.go b/models/torrents/create.go new file mode 100644 index 00000000..a3520caf --- /dev/null +++ b/models/torrents/create.go @@ -0,0 +1,48 @@ +package torrents + +import ( + "time" + + "github.com/NyaaPantsu/nyaa/models" + "github.com/NyaaPantsu/nyaa/utils/log" + "github.com/NyaaPantsu/nyaa/utils/validator/torrent" +) + +func Create(user *models.User, uploadForm *torrentValidator.TorrentRequest) (*models.Torrent, error) { + torrent := models.Torrent{ + Name: uploadForm.Name, + Category: uploadForm.CategoryID, + SubCategory: uploadForm.SubCategoryID, + Status: uploadForm.Status, + Hidden: uploadForm.Hidden, + Hash: uploadForm.Infohash, + Date: time.Now(), + Filesize: uploadForm.Filesize, + Description: uploadForm.Description, + WebsiteLink: uploadForm.WebsiteLink, + UploaderID: user.ID} + torrent.ParseTrackers(uploadForm.Trackers) + models.ORM.Create(&torrent) + if models.ElasticSearchClient != nil { + err := torrent.AddToESIndex(models.ElasticSearchClient) + if err == nil { + log.Infof("Successfully added torrent to ES index.") + } else { + log.Errorf("Unable to add torrent to ES index: %s", err) + } + } else { + log.Error("Unable to create elasticsearch client") + } + NewTorrentEvent(user, &torrent) + if len(uploadForm.FileList) > 0 { + for _, uploadedFile := range uploadForm.FileList { + file := models.File{TorrentID: torrent.ID, Filesize: uploadForm.Filesize} + err := file.SetPath(uploadedFile.Path) + if err != nil { + return &torrent, err + } + models.ORM.Create(&file) + } + } + return &torrent, nil +} diff --git a/models/user.go b/models/user.go index 35b99f5d..6153828e 100644 --- a/models/user.go +++ b/models/user.go @@ -3,10 +3,14 @@ package models import ( "encoding/json" "fmt" - "github.com/NyaaPantsu/nyaa/utils/log" "time" + "github.com/NyaaPantsu/nyaa/utils/log" + + "net/http" + "github.com/NyaaPantsu/nyaa/config" + "github.com/NyaaPantsu/nyaa/utils/crypto" ) const ( @@ -159,6 +163,20 @@ func (u *User) NeedsCaptcha() bool { return !(u.IsTrusted() || u.IsModerator()) } +// CanUpload : Check if a user can upload or if upload is enabled in config +func (u *User) CanUpload() bool { + if config.Conf.Torrents.UploadsDisabled { + if config.Conf.Torrents.AdminsAreStillAllowedTo && u.IsModerator() { + return true + } + if config.Conf.Torrents.TrustedUsersAreStillAllowedTo && u.IsTrusted() { + return true + } + return false + } + return true +} + // GetRole : Get the status/role of a user func (u *User) GetRole() string { switch u.Status { @@ -292,3 +310,35 @@ func (u *User) ParseSettings() { u.Settings.ToDefault() } } + +// UpdateUserCore updates a user. (Applying the modifed data of user). +func (user *User) Update() (int, error) { + if user.Email == "" { + user.MD5 = "" + } else { + var err error + user.MD5, err = crypto.GenerateMD5Hash(user.Email) + if err != nil { + return http.StatusInternalServerError, err + } + } + + user.UpdatedAt = time.Now() + err := ORM.Save(user).Error + if err != nil { + return http.StatusInternalServerError, err + } + + return http.StatusOK, nil +} + +// UpdateRawUser : Function to update a user without updating his associations model +func (user *User) UpdateRaw() (int, error) { + user.UpdatedAt = time.Now() + err := ORM.Model(&user).UpdateColumn(&user).Error + if err != nil { + return http.StatusInternalServerError, err + } + + return http.StatusOK, nil +} diff --git a/models/users/update.go b/models/users/update.go index a15fbe9a..82abcb9f 100644 --- a/models/users/update.go +++ b/models/users/update.go @@ -1,33 +1 @@ package users - -// UpdateUserCore updates a user. (Applying the modifed data of user). -func Update(user *models.User) (int, error) { - if user.Email == "" { - user.MD5 = "" - } else { - var err error - user.MD5, err = crypto.GenerateMD5Hash(user.Email) - if err != nil { - return http.StatusInternalServerError, err - } - } - - user.UpdatedAt = time.Now() - err := db.ORM.Save(user).Error - if err != nil { - return http.StatusInternalServerError, err - } - - return http.StatusOK, nil -} - -// UpdateRawUser : Function to update a user without updating his associations model -func UpdateRawUser(user *models.User) (int, error) { - user.UpdatedAt = time.Now() - err := db.ORM.Model(&user).UpdateColumn(&user).Error - if err != nil { - return http.StatusInternalServerError, err - } - - return http.StatusOK, nil -} \ No newline at end of file diff --git a/utils/cookies/user.go b/utils/cookies/user.go index 75abf3d9..0b8592ed 100644 --- a/utils/cookies/user.go +++ b/utils/cookies/user.go @@ -1,14 +1,19 @@ package cookies import ( - "github.com/gin-gonic/gin" - "os/user" - "github.com/gorilla/securecookie" + "errors" "fmt" + "net/http" "strconv" "time" + + "github.com/NyaaPantsu/nyaa/config" "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/models/users" + "github.com/NyaaPantsu/nyaa/utils/timeHelper" + "github.com/NyaaPantsu/nyaa/utils/validator/user" + "github.com/gin-gonic/gin" + "github.com/gorilla/securecookie" ) const ( @@ -18,16 +23,17 @@ const ( // UserContextKey : key for user context UserContextKey = "nyaapantsu.user" ) + // CreateUserAuthentication creates user authentication. -func CreateUserAuthentication(c *gin.Context, form *formStruct.LoginForm) (int, error) { +func CreateUserAuthentication(c *gin.Context, form *userValidator.LoginForm) (*models.User, int, error) { username := form.Username pass := form.Password user, status, err := users.Exists(username, pass) if err != nil { - return status, err + return user, status, err } - status, err = SetCookieHandler(c, user) - return status, err + status, err = SetLogin(c, *user) + return user, status, err } // If you want to keep login cookies between restarts you need to make these permanent diff --git a/utils/upload/upload.go b/utils/upload/upload.go index 22ad4836..f69785ae 100644 --- a/utils/upload/upload.go +++ b/utils/upload/upload.go @@ -11,8 +11,8 @@ import ( "github.com/NyaaPantsu/nyaa/config" "github.com/NyaaPantsu/nyaa/models" - "github.com/NyaaPantsu/nyaa/service" "github.com/NyaaPantsu/nyaa/utils/sanitize" + "github.com/NyaaPantsu/nyaa/utils/search/structs" "github.com/NyaaPantsu/nyaa/utils/validator/torrent" "github.com/gin-gonic/gin" ) @@ -43,8 +43,8 @@ type APIResultJSON struct { } // ToParams : Convert a torrentsrequest to searchparams -func (r *TorrentsRequest) ToParams() serviceBase.WhereParams { - res := serviceBase.WhereParams{} +func (r *TorrentsRequest) ToParams() structs.WhereParams { + res := structs.WhereParams{} conditions := "" v := reflect.ValueOf(r.Query) @@ -146,7 +146,7 @@ func ExtractInfo(c *gin.Context, r *torrentValidator.TorrentRequest) error { // UpdateTorrent : Update torrent model //rewrite with reflect ? -func UpdateTorrent(r *torrentValidator.UpdateRequest, t *models.Torrent, currentUser *models.User) { +func UpdateTorrent(r *torrentValidator.UpdateRequest, t *models.Torrent, currentUser *models.User) *models.Torrent { if r.Update.Name != "" { t.Name = r.Update.Name } @@ -172,6 +172,8 @@ func UpdateTorrent(r *torrentValidator.UpdateRequest, t *models.Torrent, current status = models.TorrentStatusTrusted } t.Status = status + + return t } func writeTorrentToDisk(file multipart.File, name string, fullpath *string) error { diff --git a/utils/validator/torrent/forms.go b/utils/validator/torrent/forms.go index 6077cb25..80464275 100644 --- a/utils/validator/torrent/forms.go +++ b/utils/validator/torrent/forms.go @@ -26,7 +26,7 @@ type TorrentRequest struct { // UpdateRequest struct type UpdateRequest struct { - ID int `json:"id"` + ID uint `json:"id"` Update TorrentRequest `json:"update"` } diff --git a/utils/validator/torrent/helpers.go b/utils/validator/torrent/helpers.go index ab6a4ffb..513c53f6 100644 --- a/utils/validator/torrent/helpers.go +++ b/utils/validator/torrent/helpers.go @@ -1,8 +1,6 @@ package torrentValidator import ( - "github.com/NyaaPantsu/nyaa/config" - "github.com/NyaaPantsu/nyaa/models" "net/url" "strings" ) @@ -44,17 +42,3 @@ func CheckTrackers(trackers []string) []string { } return trackerRet } - -// IsUploadEnabled : Check if upload is enabled in config -func IsUploadEnabled(u *models.User) bool { - if config.Conf.Torrents.UploadsDisabled { - if config.Conf.Torrents.AdminsAreStillAllowedTo && u.IsModerator() { - return true - } - if config.Conf.Torrents.TrustedUsersAreStillAllowedTo && u.IsTrusted() { - return true - } - return false - } - return true -}