diff --git a/controllers/activity_handler.go b/controllers/activity_handler.go index 8ec7b191..8858aaaf 100644 --- a/controllers/activity_handler.go +++ b/controllers/activity_handler.go @@ -6,9 +6,7 @@ import ( "strconv" "strings" - "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/models/activities" - "github.com/NyaaPantsu/nyaa/utils/cookies" "github.com/NyaaPantsu/nyaa/utils/log" "github.com/gin-gonic/gin" ) @@ -32,7 +30,7 @@ func ActivityListHandler(c *gin.Context) { } var conditions []string var values []interface{} - if userid != "" && userPermission.HasAdmin(currentUser) { + if userid != "" && currentUser.HasAdmin() { conditions = append(conditions, "user_id = ?") values = append(values, userid) } @@ -41,8 +39,8 @@ func ActivityListHandler(c *gin.Context) { values = append(values, filter) } - activities, nbActivities := activity.GetAllActivities(offset, (pagenum-1)*offset, strings.Join(conditions, " AND "), values...) + activity, nbActivities := activities.FindAll(offset, (pagenum-1)*offset, strings.Join(conditions, " AND "), values...) nav := navigation{nbActivities, offset, pagenum, "activities"} - modelList(c, "site/torrents/activities.jet.html", activities, nav, newSearchForm(c)) + modelList(c, "site/torrents/activities.jet.html", activity, nav, newSearchForm(c)) } diff --git a/controllers/api_handler.go b/controllers/api_handler.go index b60d3929..1a02ddec 100644 --- a/controllers/api_handler.go +++ b/controllers/api_handler.go @@ -72,14 +72,14 @@ func APIHandler(c *gin.Context) { } } - torrents, nbTorrents, err := torrents.Find(whereParams, req.MaxPerPage, req.MaxPerPage*(req.Page-1)) + torrentSearch, nbTorrents, err := torrents.Find(whereParams, req.MaxPerPage, req.MaxPerPage*(req.Page-1)) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } b := upload.APIResultJSON{ - Torrents: models.APITorrentsToJSON(torrents), + Torrents: torrents.APITorrentsToJSON(torrentSearch), } b.QueryRecordCount = req.MaxPerPage b.TotalRecordCount = nbTorrents @@ -146,7 +146,7 @@ func APIUploadHandler(c *gin.Context) { } if !messages.HasErrors() { - uploadForm := torrentValidator.TorrentRequest{} + uploadForm := upload.NewTorrentRequest() 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 ? uploadForm is empty so we shouldn't @@ -154,7 +154,7 @@ func APIUploadHandler(c *gin.Context) { 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, &uploadForm) + err = upload.ExtractInfo(c, uploadForm) if err != nil { messages.Error(err) } @@ -171,7 +171,7 @@ func APIUploadHandler(c *gin.Context) { messages.Error(err) } if !messages.HasErrors() { - torrent, err := torrents.Create(user, &uploadForm) + torrent, err := torrents.Create(user, uploadForm) if err != nil { messages.Error(err) } @@ -249,13 +249,13 @@ func APISearchHandler(c *gin.Context) { } } - _, torrents, _, err := search.SearchByQueryWithUser(c, pagenum) + _, torrentSearch, _, err := search.SearchByQueryWithUser(c, pagenum) if err != nil { c.AbortWithError(http.StatusInternalServerError, err) return } - b := models.APITorrentsToJSON(torrents) + b := torrents.APITorrentsToJSON(torrentSearch) c.JSON(http.StatusOK, b) } diff --git a/controllers/helpers.go b/controllers/helpers.go index 6976c942..c56fec94 100644 --- a/controllers/helpers.go +++ b/controllers/helpers.go @@ -2,7 +2,7 @@ package controllers import ( "github.com/NyaaPantsu/nyaa/models" - "github.com/NyaaPantsu/nyaa/models/users" + "github.com/NyaaPantsu/nyaa/utils/cookies" "github.com/NyaaPantsu/nyaa/utils/search/structs" "github.com/gin-gonic/gin" ) @@ -47,6 +47,6 @@ func newSearchForm(c *gin.Context) searchForm { } } func getUser(c *gin.Context) *models.User { - user, _, _ := users.CurrentUser(c) + user, _, _ := cookies.CurrentUser(c) return user } diff --git a/controllers/middlewares.go b/controllers/middlewares.go index 37a6c3cb..e89d8f11 100644 --- a/controllers/middlewares.go +++ b/controllers/middlewares.go @@ -3,7 +3,6 @@ package controllers import ( "net/http" - "github.com/NyaaPantsu/nyaa/utils/cookies" "github.com/gin-gonic/gin" ) @@ -20,7 +19,7 @@ func errorMiddleware() gin.HandlerFunc { func modMiddleware() gin.HandlerFunc { return func(c *gin.Context) { currentUser := getUser(c) - if !userPermission.HasAdmin(currentUser) { + if !currentUser.HasAdmin() { NotFoundHandler(c) } c.Next() diff --git a/controllers/modpanel.go b/controllers/modpanel.go index f9b563b3..115a7034 100644 --- a/controllers/modpanel.go +++ b/controllers/modpanel.go @@ -13,11 +13,15 @@ import ( "github.com/NyaaPantsu/nyaa/models/comments" "github.com/NyaaPantsu/nyaa/models/reports" "github.com/NyaaPantsu/nyaa/models/torrents" + "github.com/NyaaPantsu/nyaa/models/users" "github.com/NyaaPantsu/nyaa/utils/categories" "github.com/NyaaPantsu/nyaa/utils/cookies" "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/torrent" "github.com/gin-gonic/gin" ) @@ -66,13 +70,13 @@ func (f *ReassignForm) ExtractInfo(c *gin.Context) bool { } tmpID := c.PostForm("to") - parsed, err := strconv.ParseUint(tmpID, 10, 0) + parsed, err := strconv.ParseUint(tmpID, 10, 32) if err != nil { messages.Error(err) return false } f.AssignTo = uint(parsed) - _, _, _, _, err = userService.RetrieveUser(c, tmpID) + _, _, _, _, err = cookies.RetrieveUserFromRequest(c, uint(parsed)) if err != nil { messages.AddErrorTf("errors", "no_user_found_id", int(parsed)) return false @@ -86,7 +90,7 @@ func (f *ReassignForm) ExecuteAction() (int, error) { var toBeChanged []uint var err error if f.By == "olduser" { - toBeChanged, err = userService.RetrieveOldUploadsByUsername(f.Data) + toBeChanged, err = users.FindOldUploadsByUsername(f.Data) if err != nil { return 0, err } @@ -96,10 +100,10 @@ func (f *ReassignForm) ExecuteAction() (int, error) { num := 0 for _, torrentID := range toBeChanged { - torrent, err2 := torrentService.GetRawTorrentByID(torrentID) + torrent, err2 := torrents.FindRawByID(torrentID) if err2 == nil { torrent.UploaderID = f.AssignTo - db.ORM.Model(&torrent).UpdateColumn(&torrent) + torrent.Update(true) num++ } } @@ -109,10 +113,10 @@ func (f *ReassignForm) ExecuteAction() (int, error) { // IndexModPanel : Controller for showing index page of Mod Panel func IndexModPanel(c *gin.Context) { offset := 10 - torrents, _, _ := torrentService.GetAllTorrents(offset, 0) - users, _ := userService.RetrieveUsersForAdmin(offset, 0) - comments, _ := commentService.GetAllComments(offset, 0, "", "") - torrentReports, _, _ := reportService.GetAllTorrentReports(offset, 0) + torrents, _, _ := torrents.FindAll(offset, 0) + users, _ := users.FindUsersForAdmin(offset, 0) + comments, _ := comments.FindAll(offset, 0, "", "") + torrentReports, _, _ := reports.GetAll(offset, 0) panelAdminTemplate(c, torrents, models.TorrentReportsToJSON(torrentReports), users, comments) } @@ -153,7 +157,7 @@ func TorrentsListPanel(c *gin.Context) { category = searchParam.Category[0].String() } searchForm := searchForm{ - SearchParam: searchParam, + TorrentParam: searchParam, Category: category, ShowItemsPerPage: true, } @@ -178,7 +182,7 @@ func TorrentReportListPanel(c *gin.Context) { } } - torrentReports, nbReports, _ := reportService.GetAllTorrentReports(offset, (pagenum-1)*offset) + torrentReports, nbReports, _ := reports.GetAll(offset, (pagenum-1)*offset) reportJSON := models.TorrentReportsToJSON(torrentReports) nav := navigation{nbReports, offset, pagenum, "mod_trlist_page"} @@ -200,7 +204,7 @@ func UsersListPanel(c *gin.Context) { } } - users, nbUsers := userService.RetrieveUsersForAdmin(offset, (pagenum-1)*offset) + users, nbUsers := users.FindUsersForAdmin(offset, (pagenum-1)*offset) nav := navigation{nbUsers, offset, pagenum, "mod_ulist_page"} modelList(c, "admin/userlist.jet.html", users, nav, newSearchForm(c)) } @@ -227,37 +231,37 @@ func CommentsListPanel(c *gin.Context) { values = append(values, userid) } - comments, nbComments := commentService.GetAllComments(offset, (pagenum-1)*offset, conditions, values...) + comments, nbComments := comments.FindAll(offset, (pagenum-1)*offset, conditions, values...) nav := navigation{nbComments, offset, pagenum, "mod_clist_page"} modelList(c, "admin/commentlist.jet.html", comments, nav, newSearchForm(c)) } // TorrentEditModPanel : Controller for editing a torrent after GET request func TorrentEditModPanel(c *gin.Context) { - id := c.Query("id") - torrent, _ := torrentService.GetTorrentByID(id) + id, _ := strconv.ParseInt(c.Query("id"), 10, 32) + torrent, _ := torrents.FindByID(uint(id)) torrentJSON := torrent.ToJSON() - uploadForm := apiService.NewTorrentRequest() + uploadForm := upload.NewTorrentRequest() uploadForm.Name = torrentJSON.Name uploadForm.Category = torrentJSON.Category + "_" + torrentJSON.SubCategory uploadForm.Status = torrentJSON.Status uploadForm.Hidden = torrent.Hidden uploadForm.WebsiteLink = string(torrentJSON.WebsiteLink) uploadForm.Description = string(torrentJSON.Description) - uploadForm.Language = torrent.Language + uploadForm.Languages = torrent.Languages formTemplate(c, "admin/paneltorrentedit.jet.html", uploadForm) } // TorrentPostEditModPanel : Controller for editing a torrent after POST request func TorrentPostEditModPanel(c *gin.Context) { - var uploadForm apiService.TorrentRequest - id := c.Query("id") + var uploadForm torrentValidator.TorrentRequest + id, _ := strconv.ParseInt(c.Query("id"), 10, 32) messages := msg.GetMessages(c) - torrent, _ := torrentService.GetTorrentByID(id) + torrent, _ := torrents.FindByID(uint(id)) if torrent.ID > 0 { - errUp := uploadForm.ExtractEditInfo(c) + errUp := upload.ExtractEditInfo(c, &uploadForm) if errUp != nil { messages.AddErrorT("errors", "fail_torrent_update") } @@ -270,12 +274,12 @@ func TorrentPostEditModPanel(c *gin.Context) { torrent.Hidden = uploadForm.Hidden torrent.WebsiteLink = uploadForm.WebsiteLink torrent.Description = uploadForm.Description - torrent.Language = uploadForm.Language - _, err := torrentService.UpdateUnscopeTorrent(&torrent) + torrent.Languages = uploadForm.Languages + _, err := torrent.UpdateUnscope() messages.AddInfoT("infos", "torrent_updated") if err == nil { // We only log edit torrent for admins - _, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) - activity.Log(&models.User{}, torrent.Identifier(), "edit", "torrent_edited_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username) + _, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) + activities.Log(&models.User{}, torrent.Identifier(), "edit", "torrent_edited_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username) } } } @@ -284,11 +288,10 @@ func TorrentPostEditModPanel(c *gin.Context) { // CommentDeleteModPanel : Controller for deleting a comment func CommentDeleteModPanel(c *gin.Context) { - id := c.Query("id") - - comment, _, err := commentService.DeleteComment(id) + id, _ := strconv.ParseInt(c.Query("id"), 10, 32) + comment, _, err := comments.Delete(uint(id)) if err == nil { - activity.Log(&models.User{}, comment.Identifier(), "delete", "comment_deleted_by", strconv.Itoa(int(comment.ID)), comment.User.Username, getUser(c).Username) + activities.Log(&models.User{}, comment.Identifier(), "delete", "comment_deleted_by", strconv.Itoa(int(comment.ID)), comment.User.Username, getUser(c).Username) } c.Redirect(http.StatusSeeOther, "/mod/comments?deleted") @@ -296,36 +299,37 @@ func CommentDeleteModPanel(c *gin.Context) { // TorrentDeleteModPanel : Controller for deleting a torrent func TorrentDeleteModPanel(c *gin.Context) { - id := c.Query("id") + id, _ := strconv.ParseInt(c.Query("id"), 10, 32) definitely := c.Request.URL.Query()["definitely"] - var returnRoute string - var err error - var torrent *models.Torrent - if definitely != nil { - torrent, _, err = torrentService.DefinitelyDeleteTorrent(id) + var returnRoute = "/mod/torrents" + torrent, errFind := torrents.FindByID(uint(id)) + if errFind == nil { + var err error + if definitely != nil { + _, _, err = torrent.DefinitelyDelete() - //delete reports of torrent - whereParams := serviceBase.CreateWhereParams("torrent_id = ?", id) - reports, _, _ := reportService.GetTorrentReportsOrderBy(&whereParams, "", 0, 0) - for _, report := range reports { - reportService.DeleteDefinitelyTorrentReport(report.ID) - } - returnRoute = "/mod/torrents/deleted" - } else { - torrent, _, err = torrentService.DeleteTorrent(id) + //delete reports of torrent + whereParams := structs.CreateWhereParams("torrent_id = ?", id) + reports, _, _ := reports.FindOrderBy(&whereParams, "", 0, 0) + for _, report := range reports { + report.Delete(true) + } + returnRoute = "/mod/torrents/deleted" + } else { + _, _, err = torrent.Delete(false) - //delete reports of torrent - whereParams := serviceBase.CreateWhereParams("torrent_id = ?", id) - reports, _, _ := reportService.GetTorrentReportsOrderBy(&whereParams, "", 0, 0) - for _, report := range reports { - reportService.DeleteTorrentReport(report.ID) + //delete reports of torrent + whereParams := structs.CreateWhereParams("torrent_id = ?", id) + reports, _, _ := reports.FindOrderBy(&whereParams, "", 0, 0) + for _, report := range reports { + report.Delete(false) + } + } + if err == nil { + _, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) + activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username) } - returnRoute = "/mod/torrents" - } - if err == nil { - _, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) - activity.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username) } c.Redirect(http.StatusSeeOther, returnRoute+"?deleted") @@ -337,7 +341,7 @@ func TorrentReportDeleteModPanel(c *gin.Context) { fmt.Println(id) idNum, _ := strconv.ParseUint(id, 10, 64) - _, _, _ = reportService.DeleteTorrentReport(uint(idNum)) + _, _, _ = reports.Delete(uint(idNum)) /* If we need to log report delete activity if err == nil { activity.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_report_deleted_by", strconv.Itoa(int(report.ID)), getUser(c).Username) @@ -436,7 +440,7 @@ func DeletedTorrentsModPanel(c *gin.Context) { category = searchParam.Category[0].String() } searchForm := searchForm{ - SearchParam: searchParam, + TorrentParam: searchParam, Category: category, ShowItemsPerPage: true, } @@ -454,8 +458,8 @@ func DeletedTorrentsPostPanel(c *gin.Context) { // TorrentBlockModPanel : Controller to lock torrents, redirecting to previous page func TorrentBlockModPanel(c *gin.Context) { - id := c.Query("id") - torrent, _, err := torrentService.ToggleBlockTorrent(id) + id, _ := strconv.ParseInt(c.Query("id"), 10, 32) + torrent, _, err := torrents.ToggleBlock(uint(id)) var returnRoute, action string if torrent.IsDeleted() { returnRoute = "/mod/torrents/deleted" @@ -468,8 +472,8 @@ func TorrentBlockModPanel(c *gin.Context) { action = "unblocked" } if err == nil { - _, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) - activity.Log(&models.User{}, torrent.Identifier(), action, "torrent_"+action+"_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username) + _, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) + activities.Log(&models.User{}, torrent.Identifier(), action, "torrent_"+action+"_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username) } c.Redirect(http.StatusSeeOther, returnRoute+"?"+action) @@ -511,7 +515,7 @@ func torrentManyAction(c *gin.Context) { messages.AddErrorTf("errors", "no_status_exist", status) status = -1 } - if !userPermission.HasAdmin(currentUser) { + if !currentUser.HasAdmin() { if c.PostForm("status") != "" { // Condition to check if a user try to change torrent status without having the right permission if (status == models.TorrentStatusTrusted && !currentUser.IsTrusted()) || status == models.TorrentStatusAPlus || status == 0 { status = models.TorrentStatusNormal @@ -522,8 +526,8 @@ func torrentManyAction(c *gin.Context) { } withReport = false // Users should not be able to remove reports } - if c.PostForm("owner") != "" && userPermission.HasAdmin(currentUser) { // We check that the user given exist and if not we return an error - _, _, errorUser := userService.RetrieveUserForAdmin(strconv.Itoa(owner)) + if c.PostForm("owner") != "" && currentUser.HasAdmin() { // We check that the user given exist and if not we return an error + _, _, errorUser := users.FindForAdmin(uint(owner)) if errorUser != nil { messages.AddErrorTf("errors", "no_user_found_id", owner) owner = -1 @@ -550,8 +554,9 @@ func torrentManyAction(c *gin.Context) { if !messages.HasErrors() { for _, torrentID := range torrentsSelected { - torrent, _ := torrentService.GetTorrentByID(torrentID) - if torrent.ID > 0 && userPermission.CurrentOrAdmin(currentUser, torrent.UploaderID) { + id, _ := strconv.Atoi(torrentID) + torrent, _ := torrents.FindByID(uint(id)) + if torrent.ID > 0 && currentUser.CurrentOrAdmin(torrent.UploaderID) { if action == "status" || action == "multiple" || action == "category" || action == "owner" { /* If we don't delete, we make changes according to the form posted and we save at the end */ @@ -570,33 +575,33 @@ func torrentManyAction(c *gin.Context) { } /* Changes are done, we save */ - _, err := torrentService.UpdateUnscopeTorrent(&torrent) + _, err := torrent.UpdateUnscope() if err == nil { - _, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) - activity.Log(&models.User{}, torrent.Identifier(), "edited", "torrent_edited_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username) + _, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) + activities.Log(&models.User{}, torrent.Identifier(), "edited", "torrent_edited_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username) } } else if action == "delete" { if status == models.TorrentStatusBlocked { // Then we should lock torrents before deleting them torrent.Status = status messages.AddInfoTf("infos", "torrent_moved", torrent.Name) - torrentService.UpdateUnscopeTorrent(&torrent) + torrent.UpdateUnscope() } - _, _, err = torrentService.DeleteTorrent(torrentID) + _, _, err = torrent.Delete(false) if err != nil { messages.ImportFromError("errors", err) } else { messages.AddInfoTf("infos", "torrent_deleted", torrent.Name) - _, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) - activity.Log(&models.User{}, torrent.Identifier(), "deleted", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username) + _, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) + activities.Log(&models.User{}, torrent.Identifier(), "deleted", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username) } } else { messages.AddErrorTf("errors", "no_action_exist", action) } if withReport { - whereParams := serviceBase.CreateWhereParams("torrent_id = ?", torrentID) - reports, _, _ := reportService.GetTorrentReportsOrderBy(&whereParams, "", 0, 0) + whereParams := structs.CreateWhereParams("torrent_id = ?", torrentID) + reports, _, _ := reports.FindOrderBy(&whereParams, "", 0, 0) for _, report := range reports { - reportService.DeleteTorrentReport(report.ID) + report.Delete(false) } messages.AddInfoTf("infos", "torrent_reports_deleted", torrent.Name) } diff --git a/controllers/publicSettingsHandler.go b/controllers/publicSettingsHandler.go index ce120b85..040bdd1d 100644 --- a/controllers/publicSettingsHandler.go +++ b/controllers/publicSettingsHandler.go @@ -58,13 +58,13 @@ func ChangePublicSettingsHandler(c *gin.Context) { } // If logged in, update user settings. - user, _ := userService.CurrentUser(c) + user, _, _ := cookies.CurrentUser(c) if user.ID > 0 { user.Language = lang user.Theme = theme user.Mascot = mascot user.MascotURL = mascotURL - userService.UpdateRawUser(&user) + user.UpdateRaw() } // Set cookie with http and not gin for expires (maxage not supported in 0 { category = searchParam.Category[0].String() } - nav := navigation{int(nbTorrents), int(searchParam.Max), int(searchParam.Page), "search"} + nav := navigation{int(nbTorrents), int(searchParam.Max), int(searchParam.Offset), "search"} searchForm := newSearchForm(c) - searchForm.SearchParam, searchForm.Category = searchParam, category + searchForm.TorrentParam, searchForm.Category = searchParam, category modelList(c, "site/torrents/listing.jet.html", models.TorrentsToJSON(torrents), nav, searchForm) } diff --git a/controllers/template.go b/controllers/template.go index 22b1ba60..3b8cf35a 100644 --- a/controllers/template.go +++ b/controllers/template.go @@ -6,7 +6,6 @@ import ( "github.com/NyaaPantsu/nyaa/config" "github.com/NyaaPantsu/nyaa/models" - userForms "github.com/NyaaPantsu/nyaa/utils/cookies" "github.com/NyaaPantsu/nyaa/utils/filelist" "github.com/NyaaPantsu/nyaa/utils/messages" "github.com/NyaaPantsu/nyaa/utils/publicSettings" @@ -16,6 +15,7 @@ import ( "fmt" "github.com/CloudyKit/jet" + "github.com/NyaaPantsu/nyaa/utils/validator/user" ) // TemplateDir : Variable to the template directory @@ -136,7 +136,7 @@ func torrentTemplate(c *gin.Context, torrent models.TorrentJSON, rootFolder *fil renderTemplate(c, path.Join(SiteDir, "torrents/view.jet.html"), vars) } -func userProfileEditTemplate(c *gin.Context, userProfile *models.User, userForm userForms.UserForm, languages map[string]string) { +func userProfileEditTemplate(c *gin.Context, userProfile *models.User, userForm userValidator.UserForm, languages map[string]string) { vars := commonVars(c) vars.Set("UserProfile", userProfile) vars.Set("UserForm", userForm) diff --git a/controllers/template_functions.go b/controllers/template_functions.go index 83aa20d8..fc8e6505 100644 --- a/controllers/template_functions.go +++ b/controllers/template_functions.go @@ -11,12 +11,10 @@ import ( "github.com/CloudyKit/jet" "github.com/NyaaPantsu/nyaa/config" "github.com/NyaaPantsu/nyaa/models" - "github.com/NyaaPantsu/nyaa/models/activities" "github.com/NyaaPantsu/nyaa/models/torrents" - "github.com/NyaaPantsu/nyaa/utils" "github.com/NyaaPantsu/nyaa/utils/categories" - "github.com/NyaaPantsu/nyaa/utils/cookies" "github.com/NyaaPantsu/nyaa/utils/filelist" + "github.com/NyaaPantsu/nyaa/utils/format" "github.com/NyaaPantsu/nyaa/utils/publicSettings" "github.com/NyaaPantsu/nyaa/utils/torrentLanguages" ) @@ -145,14 +143,9 @@ func templateFunctions(vars jet.VarMap) jet.VarMap { vars.Set("getAvatar", func(hash string, size int) string { return "https://www.gravatar.com/avatar/" + hash + "?s=" + strconv.Itoa(size) }) - vars.Set("CurrentOrAdmin", userPermission.CurrentOrAdmin) - vars.Set("CurrentUserIdentical", userPermission.CurrentUserIdentical) - vars.Set("HasAdmin", userPermission.HasAdmin) - vars.Set("NeedsCaptcha", userPermission.NeedsCaptcha) - vars.Set("GetRole", userPermission.GetRole) - vars.Set("IsFollower", userPermission.IsFollower) + vars.Set("DisplayTorrent", func(t models.Torrent, u *models.User) bool { - return ((!t.Hidden && t.Status != 0) || userPermission.CurrentOrAdmin(u, t.UploaderID)) + return (!t.Hidden && t.Status != 0) || u.CurrentOrAdmin(t.UploaderID) }) vars.Set("NoEncode", func(str string) template.HTML { return template.HTML(str) @@ -167,7 +160,7 @@ func templateFunctions(vars jet.VarMap) jet.VarMap { // because time.* isn't available in templates... return t.Format(time.RFC3339) }) - vars.Set("GetHostname", util.GetHostname) + vars.Set("GetHostname", format.GetHostname) vars.Set("GetCategories", func(keepParent bool, keepChild bool) map[string]string { return categories.GetCategoriesSelect(keepParent, keepChild) }) @@ -226,7 +219,7 @@ func templateFunctions(vars jet.VarMap) jet.VarMap { if filesize == 0 { return T("unknown") } - return template.HTML(util.FormatFilesize(filesize)) + return template.HTML(format.FileSize(filesize)) }) vars.Set("makeCaptchaData", func(captchaID string, T publicSettings.TemplateTfunc) captchaData { return captchaData{captchaID, T} @@ -277,7 +270,7 @@ func templateFunctions(vars jet.VarMap) jet.VarMap { return string(T(d)) }) vars.Set("genUploaderLink", func(uploaderID uint, uploaderName template.HTML, torrentHidden bool) template.HTML { - uploaderID, username := torrentService.HideTorrentUser(uploaderID, string(uploaderName), torrentHidden) + uploaderID, username := torrents.HideUser(uploaderID, string(uploaderName), torrentHidden) if uploaderID == 0 { return template.HTML(username) } @@ -286,7 +279,7 @@ func templateFunctions(vars jet.VarMap) jet.VarMap { return template.HTML("" + username + "") }) vars.Set("genActivityContent", func(a models.Activity, T publicSettings.TemplateTfunc) template.HTML { - return activity.ToLocale(&a, T) + return a.ToLocale(T) }) return vars } diff --git a/controllers/upload_handler.go b/controllers/upload_handler.go index d97c6dc0..e92363cd 100644 --- a/controllers/upload_handler.go +++ b/controllers/upload_handler.go @@ -4,24 +4,20 @@ import ( "errors" "net/http" "strconv" - "time" - "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/models/torrents" - "github.com/NyaaPantsu/nyaa/service/api" "github.com/NyaaPantsu/nyaa/utils/captcha" - "github.com/NyaaPantsu/nyaa/utils/cookies" - "github.com/NyaaPantsu/nyaa/utils/log" msg "github.com/NyaaPantsu/nyaa/utils/messages" "github.com/NyaaPantsu/nyaa/utils/publicSettings" "github.com/NyaaPantsu/nyaa/utils/upload" + "github.com/NyaaPantsu/nyaa/utils/validator/torrent" "github.com/gin-gonic/gin" ) // UploadHandler : Main Controller for uploading a torrent func UploadHandler(c *gin.Context) { user := getUser(c) - if !uploadService.IsUploadEnabled(user) { + if !user.CanUpload() { T := publicSettings.GetTfuncFromRequest(c) c.AbortWithError(http.StatusBadRequest, errors.New(string(T("uploads_disabled")))) return @@ -36,11 +32,11 @@ func UploadHandler(c *gin.Context) { // UploadPostHandler : Controller for uploading a torrent, after POST request, redirect or makes error in messages func UploadPostHandler(c *gin.Context) { - var uploadForm apiService.TorrentRequest + var uploadForm torrentValidator.TorrentRequest user := getUser(c) messages := msg.GetMessages(c) // new utils for errors and infos - if userPermission.NeedsCaptcha(user) { + if user.NeedsCaptcha() { userCaptcha := captcha.Extract(c) if !captcha.Authenticate(userCaptcha) { messages.AddError("errors", captcha.ErrInvalidCaptcha.Error()) @@ -48,62 +44,19 @@ func UploadPostHandler(c *gin.Context) { } // validation is done in ExtractInfo() - err := uploadForm.ExtractInfo(c) + err := upload.ExtractInfo(c, &uploadForm) if err != nil { messages.AddError("errors", err.Error()) } - status := models.TorrentStatusNormal - if uploadForm.Remake { // overrides trusted - status = models.TorrentStatusRemake - } else if user.IsTrusted() { - status = models.TorrentStatusTrusted - } - err = torrentService.ExistOrDelete(uploadForm.Infohash, user) + err = torrents.ExistOrDelete(uploadForm.Infohash, user) if err != nil { messages.AddError("errors", err.Error()) } if !messages.HasErrors() { // add to db and redirect - torrent := models.Torrent{ - Name: uploadForm.Name, - Category: uploadForm.CategoryID, - SubCategory: uploadForm.SubCategoryID, - Status: status, - Hidden: uploadForm.Hidden, - Hash: uploadForm.Infohash, - Date: time.Now(), - Filesize: uploadForm.Filesize, - Description: uploadForm.Description, - WebsiteLink: uploadForm.WebsiteLink, - UploaderID: user.ID, - Language: uploadForm.Language} - torrent.ParseTrackers(uploadForm.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) - } - } - - torrentService.NewTorrentEvent(user, &torrent) - - // add filelist to files db, if we have one - if len(uploadForm.FileList) > 0 { - for _, uploadedFile := range uploadForm.FileList { - file := models.File{TorrentID: torrent.ID, Filesize: uploadedFile.Filesize} - err := file.SetPath(uploadedFile.Path) - if err != nil { - messages.AddError("errors", err.Error()) - } - db.ORM.Create(&file) - } - } + torrent, _ := torrents.Create(user, &uploadForm) url := "/view/" + strconv.FormatUint(uint64(torrent.ID), 10) c.Redirect(302, url+"?success") @@ -112,10 +65,10 @@ func UploadPostHandler(c *gin.Context) { // UploadGetHandler : Controller for uploading a torrent, after GET request or Failed Post request func UploadGetHandler(c *gin.Context) { - var uploadForm apiService.TorrentRequest - _ = uploadForm.ExtractInfo(c) + var uploadForm torrentValidator.TorrentRequest + _ = upload.ExtractInfo(c, &uploadForm) user := getUser(c) - if userPermission.NeedsCaptcha(user) { + if user.NeedsCaptcha() { uploadForm.CaptchaID = captcha.GetID() } else { uploadForm.CaptchaID = "" diff --git a/controllers/user_handler.go b/controllers/user_handler.go index a0a1fab0..08b787fe 100644 --- a/controllers/user_handler.go +++ b/controllers/user_handler.go @@ -8,24 +8,28 @@ import ( "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/models/notifications" + "github.com/NyaaPantsu/nyaa/models/users" "github.com/NyaaPantsu/nyaa/utils/captcha" "github.com/NyaaPantsu/nyaa/utils/cookies" "github.com/NyaaPantsu/nyaa/utils/crypto" + "github.com/NyaaPantsu/nyaa/utils/email" msg "github.com/NyaaPantsu/nyaa/utils/messages" "github.com/NyaaPantsu/nyaa/utils/publicSettings" "github.com/NyaaPantsu/nyaa/utils/search" + "github.com/NyaaPantsu/nyaa/utils/validator" + "github.com/NyaaPantsu/nyaa/utils/validator/user" "github.com/gin-gonic/gin" ) // UserRegisterFormHandler : Getting View User Registration func UserRegisterFormHandler(c *gin.Context) { - _, errorUser := userService.CurrentUser(c) + _, _, errorUser := cookies.CurrentUser(c) // User is already connected, redirect to home if errorUser == nil { SearchHandler(c) return } - registrationForm := form.RegistrationForm{} + registrationForm := userValidator.RegistrationForm{} c.Bind(®istrationForm) registrationForm.CaptchaID = captcha.GetID() formTemplate(c, "site/user/register.jet.html", registrationForm) @@ -33,33 +37,36 @@ func UserRegisterFormHandler(c *gin.Context) { // UserLoginFormHandler : Getting View User Login func UserLoginFormHandler(c *gin.Context) { - _, errorUser := userService.CurrentUser(c) + _, _, errorUser := cookies.CurrentUser(c) // User is already connected, redirect to home if errorUser == nil { SearchHandler(c) return } - loginForm := form.LoginForm{} + loginForm := userValidator.LoginForm{} formTemplate(c, "site/user/login.jet.html", loginForm) } // UserProfileHandler : Getting User Profile func UserProfileHandler(c *gin.Context) { - id := c.Param("id") + id, _ := strconv.ParseUint(c.Param("id"), 10, 32) fmt.Printf("User ID: %s", id) Ts, _ := publicSettings.GetTfuncAndLanguageFromRequest(c) messages := msg.GetMessages(c) - userProfile, _, errorUser := userService.RetrieveUserForAdmin(id) + userProfile, _, errorUser := users.FindForAdmin(uint(id)) if errorUser == nil { currentUser := getUser(c) follow := c.Request.URL.Query()["followed"] unfollow := c.Request.URL.Query()["unfollowed"] deleteVar := c.Request.URL.Query()["delete"] - if (deleteVar != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) { - _ = userService.DeleteUser(c, currentUser, id) + if (deleteVar != nil) && (currentUser.CurrentOrAdmin(userProfile.ID)) { + _, err := userProfile.Delete(currentUser) + if err == nil && currentUser.CurrentUserIdentical(userProfile.ID) { + cookies.Clear(c) + } staticTemplate(c, "site/delete_success.jet.html") } else { if follow != nil { @@ -70,12 +77,12 @@ func UserProfileHandler(c *gin.Context) { } userProfile.ParseSettings() query := c.Request.URL.Query() - query.Set("userID", id) + query.Set("userID", strconv.Itoa(int(id))) query.Set("max", "16") c.Request.URL.RawQuery = query.Encode() var torrents []models.Torrent var err error - if userPermission.CurrentOrAdmin(currentUser, userProfile.ID) { + if currentUser.CurrentOrAdmin(userProfile.ID) { _, torrents, _, err = search.SearchByQuery(c, 1) } else { _, torrents, _, err = search.SearchByQueryNoHidden(c, 1) @@ -84,7 +91,7 @@ func UserProfileHandler(c *gin.Context) { messages.AddErrorT("errors", "retrieve_torrents_error") } userProfile.Torrents = torrents - userProfileTemplate(c, &userProfile) + userProfileTemplate(c, userProfile) } } else { NotFoundHandler(c) @@ -93,18 +100,16 @@ func UserProfileHandler(c *gin.Context) { // UserDetailsHandler : Getting User Profile Details View func UserDetailsHandler(c *gin.Context) { - id := c.Param("id") + id, _ := strconv.ParseUint(c.Param("id"), 10, 32) currentUser := getUser(c) - userProfile, _, errorUser := userService.RetrieveUserForAdmin(id) - if errorUser == nil && userPermission.CurrentOrAdmin(currentUser, userProfile.ID) { - if userPermission.CurrentOrAdmin(currentUser, userProfile.ID) { - b := form.UserForm{} - c.Bind(&b) - availableLanguages := publicSettings.GetAvailableLanguages() - userProfile.ParseSettings() - userProfileEditTemplate(c, &userProfile, b, availableLanguages) - } + userProfile, _, errorUser := users.FindForAdmin(uint(id)) + if errorUser == nil && currentUser.CurrentOrAdmin(userProfile.ID) { + b := userValidator.UserForm{} + c.Bind(&b) + availableLanguages := publicSettings.GetAvailableLanguages() + userProfile.ParseSettings() + userProfileEditTemplate(c, userProfile, b, availableLanguages) } else { NotFoundHandler(c) } @@ -112,29 +117,32 @@ func UserDetailsHandler(c *gin.Context) { // UserProfileFormHandler : Getting View User Profile Update func UserProfileFormHandler(c *gin.Context) { - id := c.Param("id") + id, _ := strconv.ParseUint(c.Param("id"), 10, 32) currentUser := getUser(c) - userProfile, _, errorUser := userService.RetrieveUserForAdmin(id) - if errorUser != nil || !userPermission.CurrentOrAdmin(currentUser, userProfile.ID) || userProfile.ID == 0 { + userProfile, _, errorUser := users.FindForAdmin(uint(id)) + if errorUser != nil || !currentUser.CurrentOrAdmin(userProfile.ID) || userProfile.ID == 0 { NotFoundHandler(c) return } userProfile.ParseSettings() messages := msg.GetMessages(c) - userForm := form.UserForm{} - userSettingsForm := form.UserSettingsForm{} + userForm := userValidator.UserForm{} + userSettingsForm := userValidator.UserSettingsForm{} if len(c.PostForm("email")) > 0 { - form.EmailValidation(c.PostForm("email"), messages) + if userValidator.EmailValidation(c.PostForm("email")) { + messages.AddErrorf("email", "email_not_valid") + } } if len(c.PostForm("username")) > 0 { - form.ValidateUsername(c.PostForm("username"), messages) + userValidator.ValidateUsername(c.PostForm("username")) + messages.AddErrorf("username", "username_illegal") } if !messages.HasErrors() { c.Bind(&userForm) c.Bind(&userSettingsForm) - if !userPermission.HasAdmin(currentUser) { + if !currentUser.HasAdmin() { userForm.Username = userProfile.Username userForm.Status = userProfile.Status } else { @@ -142,26 +150,33 @@ func UserProfileFormHandler(c *gin.Context) { messages.AddErrorT("errors", "elevating_user_error") } } - modelHelper.ValidateForm(&userForm, messages) + validator.ValidateForm(&userForm, messages) if !messages.HasErrors() { if userForm.Email != userProfile.Email { - userService.SendVerificationToUser(*currentUser, userForm.Email) + email.SendVerificationToUser(currentUser, userForm.Email) messages.AddInfoTf("infos", "email_changed", userForm.Email) userForm.Email = userProfile.Email // reset, it will be set when user clicks verification } - userProfile, _ = userService.UpdateUser(c, &userForm, &userSettingsForm, currentUser, id) + user, _, err := users.UpdateFromRequest(c, &userForm, &userSettingsForm, currentUser, uint(id)) + if err != nil { + messages.Error(err) + } + if userForm.Email != user.Email { + // send verification to new email and keep old + email.SendVerificationToUser(user, userForm.Email) + } if !messages.HasErrors() { messages.AddInfoT("infos", "profile_updated") } } } availableLanguages := publicSettings.GetAvailableLanguages() - userProfileEditTemplate(c, &userProfile, userForm, availableLanguages) + userProfileEditTemplate(c, userProfile, userForm, availableLanguages) } // UserRegisterPostHandler : Post Registration controller, we do some check on the form here, the rest on user service func UserRegisterPostHandler(c *gin.Context) { - b := form.RegistrationForm{} + b := userValidator.RegistrationForm{} messages := msg.GetMessages(c) if !captcha.Authenticate(captcha.Extract(c)) { @@ -169,14 +184,26 @@ func UserRegisterPostHandler(c *gin.Context) { } if !messages.HasErrors() { if len(c.PostForm("email")) > 0 { - form.EmailValidation(c.PostForm("email"), messages) + if !userValidator.EmailValidation(c.PostForm("email")) { + messages.AddErrorT("email", "email_not_valid") + } } - form.ValidateUsername(c.PostForm("username"), messages) + if !userValidator.ValidateUsername(c.PostForm("username")) { + messages.AddErrorT("username", "username_illegal") + } + if !messages.HasErrors() { c.Bind(&b) - modelHelper.ValidateForm(&b, messages) + validator.ValidateForm(&b, messages) if !messages.HasErrors() { - _ = userService.CreateUser(c) + user, _ := users.CreateUser(c) + _, err := cookies.SetLogin(c, user) + if err != nil { + messages.Error(err) + } + if b.Email != "" { + email.SendVerificationToUser(user, b.Email) + } if !messages.HasErrors() { staticTemplate(c, "site/static/signup_success.jet.html") } @@ -193,7 +220,7 @@ func UserVerifyEmailHandler(c *gin.Context) { token := c.Param("token") messages := msg.GetMessages(c) - _, errEmail := userService.EmailVerification(token, c) + _, errEmail := email.EmailVerification(token, c) if errEmail != nil { messages.ImportFromError("errors", errEmail) } @@ -202,13 +229,13 @@ func UserVerifyEmailHandler(c *gin.Context) { // UserLoginPostHandler : Post Login controller func UserLoginPostHandler(c *gin.Context) { - b := form.LoginForm{} + b := userValidator.LoginForm{} c.Bind(&b) messages := msg.GetMessages(c) - modelHelper.ValidateForm(&b, messages) + validator.ValidateForm(&b, messages) if !messages.HasErrors() { - _, errorUser := userService.CreateUserAuthentication(c) + _, _, errorUser := cookies.CreateUserAuthentication(c, &b) if errorUser == nil { c.Redirect(http.StatusSeeOther, "/") return @@ -222,7 +249,7 @@ func UserLoginPostHandler(c *gin.Context) { func UserLogoutHandler(c *gin.Context) { logout := c.PostForm("logout") if logout != "" { - userService.ClearCookie(c) + cookies.Clear(c) url := c.DefaultPostForm("redirectTo", "/") c.Redirect(http.StatusSeeOther, url) } else { @@ -233,16 +260,16 @@ func UserLogoutHandler(c *gin.Context) { // UserFollowHandler : Controller to follow/unfollow users, need user id to follow func UserFollowHandler(c *gin.Context) { var followAction string - id := c.Param("id") + id, _ := strconv.ParseUint(c.Param("id"), 10, 32) currentUser := getUser(c) - user, _, errorUser := userService.RetrieveUserForAdmin(id) + user, _, errorUser := users.FindForAdmin(uint(id)) if errorUser == nil && user.ID > 0 { - if !userPermission.IsFollower(&user, currentUser) { + if !currentUser.IsFollower(user) { followAction = "followed" - userService.SetFollow(&user, currentUser) + currentUser.SetFollow(user) } else { followAction = "unfollowed" - userService.RemoveFollow(&user, currentUser) + currentUser.RemoveFollow(user) } } url := "/user/" + strconv.Itoa(int(user.ID)) + "/" + user.Username + "?" + followAction @@ -255,7 +282,7 @@ func UserNotificationsHandler(c *gin.Context) { if currentUser.ID > 0 { messages := msg.GetMessages(c) if c.Request.URL.Query()["clear"] != nil { - notifierService.DeleteAllNotifications(currentUser.ID) + notifications.DeleteAllNotifications(currentUser.ID) messages.AddInfoT("infos", "notifications_cleared") currentUser.Notifications = []models.Notification{} } @@ -267,18 +294,18 @@ func UserNotificationsHandler(c *gin.Context) { // UserAPIKeyResetHandler : Controller to reset user api key func UserAPIKeyResetHandler(c *gin.Context) { - id := c.Param("id") + id, _ := strconv.ParseUint(c.Param("id"), 10, 32) currentUser := getUser(c) messages := msg.GetMessages(c) - userProfile, _, errorUser := userService.RetrieveUserForAdmin(id) - if errorUser != nil || !userPermission.CurrentOrAdmin(currentUser, userProfile.ID) || userProfile.ID == 0 { + userProfile, _, errorUser := users.FindForAdmin(uint(id)) + if errorUser != nil || !currentUser.CurrentOrAdmin(userProfile.ID) || userProfile.ID == 0 { NotFoundHandler(c) return } userProfile.APIToken, _ = crypto.GenerateRandomToken32() userProfile.APITokenExpiry = time.Unix(0, 0) - _, errorUser = userService.UpdateRawUser(&userProfile) + _, errorUser = userProfile.UpdateRaw() if errorUser != nil { messages.Error(errorUser) } else { diff --git a/controllers/view_torrent_handler.go b/controllers/view_torrent_handler.go index d6af6408..a81243f5 100644 --- a/controllers/view_torrent_handler.go +++ b/controllers/view_torrent_handler.go @@ -7,29 +7,30 @@ import ( "net/http" "strconv" "strings" - "time" "os" "github.com/NyaaPantsu/nyaa/config" "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/models/activities" + "github.com/NyaaPantsu/nyaa/models/comments" "github.com/NyaaPantsu/nyaa/models/notifications" "github.com/NyaaPantsu/nyaa/models/reports" "github.com/NyaaPantsu/nyaa/models/torrents" - "github.com/NyaaPantsu/nyaa/service/api" - "github.com/NyaaPantsu/nyaa/utils" "github.com/NyaaPantsu/nyaa/utils/captcha" - "github.com/NyaaPantsu/nyaa/utils/cookies" "github.com/NyaaPantsu/nyaa/utils/filelist" msg "github.com/NyaaPantsu/nyaa/utils/messages" "github.com/NyaaPantsu/nyaa/utils/publicSettings" + "github.com/NyaaPantsu/nyaa/utils/sanitize" + "github.com/NyaaPantsu/nyaa/utils/search/structs" + "github.com/NyaaPantsu/nyaa/utils/upload" + "github.com/NyaaPantsu/nyaa/utils/validator/torrent" "github.com/gin-gonic/gin" ) // ViewHandler : Controller for displaying a torrent func ViewHandler(c *gin.Context) { - id := c.Param("id") + id, _ := strconv.ParseInt(c.Param("id"), 10, 32) messages := msg.GetMessages(c) user := getUser(c) @@ -37,10 +38,10 @@ func ViewHandler(c *gin.Context) { messages.AddInfo("infos", "Torrent uploaded successfully!") } - torrent, err := torrentService.GetTorrentByID(id) + torrent, err := torrents.FindByID(uint(id)) if c.Request.URL.Query()["notif"] != nil { - notifierService.ToggleReadNotification(torrent.Identifier(), user.ID) + notifications.ToggleReadNotification(torrent.Identifier(), user.ID) } if err != nil { @@ -50,7 +51,7 @@ func ViewHandler(c *gin.Context) { b := torrent.ToJSON() folder := filelist.FileListToFolder(torrent.FileList, "root") captchaID := "" - if userPermission.NeedsCaptcha(user) { + if user.NeedsCaptcha() { captchaID = captcha.GetID() } torrentTemplate(c, b, folder, captchaID) @@ -63,7 +64,7 @@ func ViewHeadHandler(c *gin.Context) { return } - _, err = torrentService.GetRawTorrentByID(uint(id)) + _, err = torrents.FindRawByID(uint(id)) if err != nil { NotFoundHandler(c) @@ -75,9 +76,9 @@ func ViewHeadHandler(c *gin.Context) { // PostCommentHandler : Controller for posting a comment func PostCommentHandler(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 { NotFoundHandler(c) return @@ -86,13 +87,13 @@ func PostCommentHandler(c *gin.Context) { currentUser := getUser(c) messages := msg.GetMessages(c) - if userPermission.NeedsCaptcha(currentUser) { + if currentUser.NeedsCaptcha() { userCaptcha := captcha.Extract(c) if !captcha.Authenticate(userCaptcha) { messages.AddErrorT("errors", "bad_captcha") } } - content := util.Sanitize(c.PostForm("comment"), "comment") + content := sanitize.Sanitize(c.PostForm("comment"), "comment") if strings.TrimSpace(content) == "" { messages.AddErrorT("errors", "comment_empty") @@ -101,20 +102,13 @@ func PostCommentHandler(c *gin.Context) { messages.AddErrorT("errors", "comment_toolong") } if !messages.HasErrors() { - userID := currentUser.ID - - comment := models.Comment{TorrentID: torrent.ID, UserID: userID, Content: content, CreatedAt: time.Now()} - err := db.ORM.Create(&comment).Error - if err != nil { - messages.Error(err) - } - comment.Torrent = &torrent + comment, err := comments.Create(content, &torrent, currentUser) url := "/view/" + strconv.FormatUint(uint64(torrent.ID), 10) torrent.Uploader.ParseSettings() if torrent.Uploader.Settings.Get("new_comment") { T, _, _ := publicSettings.TfuncAndLanguageWithFallback(torrent.Uploader.Language, torrent.Uploader.Language) // We need to send the notification to every user in their language - notifierService.NotifyUser(torrent.Uploader, comment.Identifier(), fmt.Sprintf(T("new_comment_on_torrent"), torrent.Name), url, torrent.Uploader.Settings.Get("new_comment_email")) + notifications.NotifyUser(torrent.Uploader, comment.Identifier(), fmt.Sprintf(T("new_comment_on_torrent"), torrent.Name), url, torrent.Uploader.Settings.Get("new_comment_email")) } if err != nil { @@ -126,27 +120,21 @@ func PostCommentHandler(c *gin.Context) { // ReportTorrentHandler : Controller for sending a torrent report func ReportTorrentHandler(c *gin.Context) { - id := c.Param("id") + id, _ := strconv.ParseInt(c.Param("id"), 10, 32) messages := msg.GetMessages(c) currentUser := getUser(c) - if userPermission.NeedsCaptcha(currentUser) { + if currentUser.NeedsCaptcha() { userCaptcha := captcha.Extract(c) if !captcha.Authenticate(userCaptcha) { messages.AddErrorT("errors", "bad_captcha") } } + torrent, err := torrents.FindByID(uint(id)) + if err != nil { + messages.Error(err) + } if !messages.HasErrors() { - idNum, _ := strconv.Atoi(id) - userID := currentUser.ID - - report := models.TorrentReport{ - Description: c.PostForm("report_type"), - TorrentID: uint(idNum), - UserID: userID, - CreatedAt: time.Now(), - } - - err := db.ORM.Create(&report).Error + _, err := reports.Create(c.PostForm("report_type"), &torrent, currentUser) messages.AddInfoTf("infos", "report_msg", id) if err != nil { messages.ImportFromError("errors", err) @@ -157,18 +145,18 @@ func ReportTorrentHandler(c *gin.Context) { // TorrentEditUserPanel : Controller for editing a user torrent by a user, after GET request func TorrentEditUserPanel(c *gin.Context) { - id := c.Query("id") - torrent, _ := torrentService.GetTorrentByID(id) + id, _ := strconv.ParseInt(c.Param("id"), 10, 32) + torrent, _ := torrents.FindByID(uint(id)) currentUser := getUser(c) - if userPermission.CurrentOrAdmin(currentUser, torrent.UploaderID) { - uploadForm := apiService.NewTorrentRequest() + if currentUser.CurrentOrAdmin(torrent.UploaderID) { + uploadForm := torrentValidator.TorrentRequest{} uploadForm.Name = torrent.Name uploadForm.Category = strconv.Itoa(torrent.Category) + "_" + strconv.Itoa(torrent.SubCategory) uploadForm.Remake = torrent.Status == models.TorrentStatusRemake uploadForm.WebsiteLink = string(torrent.WebsiteLink) uploadForm.Description = string(torrent.Description) uploadForm.Hidden = torrent.Hidden - uploadForm.Language = torrent.Language + uploadForm.Languages = torrent.Languages formTemplate(c, "site/torrents/edit.jet.html", uploadForm) } else { NotFoundHandler(c) @@ -177,33 +165,19 @@ func TorrentEditUserPanel(c *gin.Context) { // TorrentPostEditUserPanel : Controller for editing a user torrent by a user, after post request func TorrentPostEditUserPanel(c *gin.Context) { - var uploadForm apiService.TorrentRequest - id := c.Query("id") + var uploadForm torrentValidator.UpdateRequest + id, _ := strconv.ParseInt(c.Param("id"), 10, 32) + uploadForm.ID = uint(id) messages := msg.GetMessages(c) - torrent, _ := torrentService.GetTorrentByID(id) + torrent, _ := torrents.FindByID(uint(id)) currentUser := getUser(c) - if torrent.ID > 0 && userPermission.CurrentOrAdmin(currentUser, torrent.UploaderID) { - errUp := uploadForm.ExtractEditInfo(c) + if torrent.ID > 0 && currentUser.CurrentOrAdmin(torrent.UploaderID) { + errUp := upload.ExtractEditInfo(c, &uploadForm.Update) if errUp != nil { messages.AddErrorT("errors", "fail_torrent_update") } if !messages.HasErrors() { - status := models.TorrentStatusNormal - if uploadForm.Remake { // overrides trusted - status = models.TorrentStatusRemake - } else if currentUser.IsTrusted() { - status = models.TorrentStatusTrusted - } - // update some (but not all!) values - torrent.Name = uploadForm.Name - torrent.Category = uploadForm.CategoryID - torrent.SubCategory = uploadForm.SubCategoryID - torrent.Status = status - torrent.Hidden = uploadForm.Hidden - torrent.WebsiteLink = uploadForm.WebsiteLink - torrent.Description = uploadForm.Description - torrent.Language = uploadForm.Language - db.ORM.Model(&torrent).UpdateColumn(&torrent) + upload.UpdateTorrent(&uploadForm, &torrent, currentUser).Update(currentUser.HasAdmin()) messages.AddInfoT("infos", "torrent_updated") } formTemplate(c, "site/torrents/edit.jet.html", uploadForm) @@ -214,23 +188,23 @@ func TorrentPostEditUserPanel(c *gin.Context) { // TorrentDeleteUserPanel : Controller for deleting a user torrent by a user func TorrentDeleteUserPanel(c *gin.Context) { - id := c.Query("id") + id, _ := strconv.ParseInt(c.Param("id"), 10, 32) currentUser := getUser(c) - torrent, _ := torrentService.GetTorrentByID(id) - if userPermission.CurrentOrAdmin(currentUser, torrent.UploaderID) { - _, _, err := torrentService.DeleteTorrent(id) + torrent, _ := torrents.FindByID(uint(id)) + if currentUser.CurrentOrAdmin(torrent.UploaderID) { + _, _, err := torrent.Delete(false) if err == nil { - _, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) - if userPermission.HasAdmin(currentUser) { // We hide username on log activity if user is not admin and torrent is hidden - activity.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, currentUser.Username) + _, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden) + if currentUser.HasAdmin() { // We hide username on log activity if user is not admin and torrent is hidden + activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, currentUser.Username) } else { - activity.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, username) + activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, username) } //delete reports of torrent - whereParams := serviceBase.CreateWhereParams("torrent_id = ?", id) - reports, _, _ := reportService.GetTorrentReportsOrderBy(&whereParams, "", 0, 0) - for _, report := range reports { - reportService.DeleteTorrentReport(report.ID) + whereParams := structs.CreateWhereParams("torrent_id = ?", id) + torrentReports, _, _ := reports.FindOrderBy(&whereParams, "", 0, 0) + for _, report := range torrentReports { + report.Delete(false) } } c.Redirect(http.StatusSeeOther, "/?deleted") @@ -262,7 +236,7 @@ func DownloadTorrent(c *gin.Context) { FileStat, _ := Openfile.Stat() //Get info from file FileSize := strconv.FormatInt(FileStat.Size(), 10) //Get file size as a string - torrent, err := torrentService.GetRawTorrentByHash(hash) + torrent, err := torrents.FindRawByHash(hash) if err != nil { //File not found, send 404 diff --git a/main.go b/main.go index 78d6043c..ef26bd3c 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "github.com/NyaaPantsu/nyaa/config" "github.com/NyaaPantsu/nyaa/controllers" + "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/utils/cookies" "github.com/NyaaPantsu/nyaa/utils/log" "github.com/NyaaPantsu/nyaa/utils/publicSettings" @@ -83,12 +84,12 @@ func main() { if err != nil { log.CheckError(err) } - db.ORM, err = db.GormInit(conf, db.DefaultLogger) + models.ORM, err = models.GormInit(conf, models.DefaultLogger) if err != nil { log.Fatal(err.Error()) } - db.ElasticSearchClient, _ = db.ElasticSearchInit() - err = publicSettings.InitI18n(conf.I18n, userService.NewCurrentUserRetriever()) + models.ElasticSearchClient, _ = models.ElasticSearchInit() + err = publicSettings.InitI18n(conf.I18n, cookies.NewCurrentUserRetriever()) if err != nil { log.Fatal(err.Error()) } diff --git a/models/activity.go b/models/activity.go index 5f99fb5c..909bb90c 100644 --- a/models/activity.go +++ b/models/activity.go @@ -17,9 +17,6 @@ type Activity struct { User *User } -// TemplateTfunc : Used to prevent cyclic import -type TemplateTfunc func(string, ...interface{}) template.HTML - // NewActivity : Create a new activity log func NewActivity(identifier string, filter string, c ...string) Activity { return Activity{Identifier: identifier, Content: strings.Join(c, ","), Filter: filter} @@ -31,7 +28,7 @@ func (a *Activity) TableName() string { } // ToLocale : Convert list of parameters to message in local language -func (a *Activity) ToLocale(T TemplateTfunc) template.HTML { +func (a *Activity) ToLocale(T func(string, ...interface{}) template.HTML) template.HTML { c := strings.Split(a.Content, ",") d := make([]interface{}, len(c)-1) for i, s := range c[1:] { diff --git a/models/comments/create.go b/models/comments/create.go new file mode 100644 index 00000000..17ffa3c6 --- /dev/null +++ b/models/comments/create.go @@ -0,0 +1,17 @@ +package comments + +import ( + "time" + + "github.com/NyaaPantsu/nyaa/models" +) + +func Create(content string, torrent *models.Torrent, user *models.User) (*models.Comment, error) { + comment := &models.Comment{TorrentID: torrent.ID, UserID: user.ID, Content: content, CreatedAt: time.Now()} + err := models.ORM.Create(comment).Error + if err != nil { + return comment, err + } + comment.Torrent = torrent + return comment, nil +} diff --git a/models/reports/create.go b/models/reports/create.go new file mode 100644 index 00000000..fc1dec68 --- /dev/null +++ b/models/reports/create.go @@ -0,0 +1,21 @@ +package reports + +import ( + "time" + + "github.com/NyaaPantsu/nyaa/models" + "errors" +) + +func Create(desc string, torrent *models.Torrent, user *models.User) (*models.TorrentReport, error) { + report := &models.TorrentReport{ + Description: desc, + TorrentID: torrent.ID, + UserID: user.ID, + CreatedAt: time.Now(), + } + if models.ORM.Create(report).Error != nil { + return report, errors.New("torrent_report_not_created") + } + return report, nil +} diff --git a/models/reports/interaction.go b/models/reports/interaction.go index 0ef948bc..200874ec 100644 --- a/models/reports/interaction.go +++ b/models/reports/interaction.go @@ -3,7 +3,6 @@ package reports import ( "errors" "net/http" - "nyaa-master/db" "strconv" "strings" @@ -11,14 +10,6 @@ import ( "github.com/NyaaPantsu/nyaa/utils/search/structs" ) -// Create : Return torrentReport in case we did modified it (ie: CreatedAt field) -func Create(torrentReport models.TorrentReport) error { - if models.ORM.Create(&torrentReport).Error != nil { - return errors.New("torrent_report_not_created") - } - return nil -} - // Delete : Delete a torrent report by id func Delete(id uint) (*models.TorrentReport, int, error) { return delete(id, false) @@ -57,7 +48,7 @@ func findOrderBy(parameters *structs.WhereParams, orderBy string, limit int, off } conditions := strings.Join(conditionArray, " AND ") if countAll { - err = db.ORM.Model(&torrentReports).Where(conditions, params...).Count(&count).Error + err = models.ORM.Model(&torrentReports).Where(conditions, params...).Count(&count).Error if err != nil { return } @@ -76,7 +67,7 @@ func findOrderBy(parameters *structs.WhereParams, orderBy string, limit int, off if limit != 0 || offset != 0 { // if limits provided dbQuery = dbQuery + " LIMIT " + strconv.Itoa(limit) + " OFFSET " + strconv.Itoa(offset) } - err = db.ORM.Preload("Torrent").Preload("User").Raw(dbQuery, params...).Find(&torrentReports).Error + err = models.ORM.Preload("Torrent").Preload("User").Raw(dbQuery, params...).Find(&torrentReports).Error return } diff --git a/models/torrent.go b/models/torrent.go index 94f1151d..65051970 100644 --- a/models/torrent.go +++ b/models/torrent.go @@ -64,6 +64,7 @@ type Torrent struct { Comments []Comment `gorm:"ForeignKey:torrent_id"` Scrape *Scrape `gorm:"AssociationForeignKey:ID;ForeignKey:torrent_id"` FileList []File `gorm:"ForeignKey:torrent_id"` + Languages []string `gorm:"-"` // This is parsed when retrieved from db } /* We need a JSON object instead of a Gorm structure because magnet URLs are @@ -88,7 +89,7 @@ type TorrentJSON struct { UploaderName template.HTML `json:"uploader_name"` OldUploader template.HTML `json:"uploader_old"` WebsiteLink template.URL `json:"website_link"` - Language string `json:"language"` + Languages []string `json:"languages"` Magnet template.URL `json:"magnet"` TorrentLink template.URL `json:"torrent"` Seeders uint32 `json:"seeders"` @@ -160,7 +161,7 @@ func (t Torrent) AddToESIndex(client *elastic.Client) error { } // DeleteFromESIndex : Removes a torrent from Elastic Search -func (t Torrent) DeleteFromESIndex(client *elastic.Client) error { +func (t *Torrent) DeleteFromESIndex(client *elastic.Client) error { ctx := context.Background() _, err := client.Delete(). Index(config.Conf.Search.ElasticsearchIndex). @@ -195,6 +196,14 @@ func (t *Torrent) ParseTrackers(trackers []string) { t.Trackers = v.Encode() } +func (t *Torrent) ParseLanguages() { + t.Languages = strings.Split(t.Language, ",") +} + +func (t *Torrent) EncodeLanguages() { + t.Language = strings.Join(t.Languages, ",") +} + // GetTrackersArray : Convert trackers string to Array func (t *Torrent) GetTrackersArray() (trackers []string) { v, _ := url.ParseQuery(t.Trackers) @@ -242,10 +251,11 @@ func (t *TorrentJSON) ToTorrent() Torrent { //OldComments: TODO // Comments: TODO // LastScrape not stored in ES, counts won't show without a value however - Scrape: &Scrape{Seeders: t.Seeders, Leechers: t.Leechers, Completed: t.Completed, LastScrape: time.Now()}, - Language: t.Language, + Scrape: &Scrape{Seeders: t.Seeders, Leechers: t.Leechers, Completed: t.Completed, LastScrape: time.Now()}, + Languages: t.Languages, //FileList: TODO } + torrent.EncodeLanguages() return torrent } @@ -310,6 +320,7 @@ func (t *Torrent) ToJSON() TorrentJSON { if t.Scrape != nil { scrape = *t.Scrape } + t.ParseLanguages() res := TorrentJSON{ ID: t.ID, Name: t.Name, @@ -326,7 +337,7 @@ func (t *Torrent) ToJSON() TorrentJSON { UploaderID: uploaderID, UploaderName: sanitize.SafeText(uploader), WebsiteLink: sanitize.Safe(t.WebsiteLink), - Language: t.Language, + Languages: t.Languages, Magnet: template.URL(magnet), TorrentLink: sanitize.Safe(torrentlink), Leechers: scrape.Leechers, @@ -350,23 +361,6 @@ func TorrentsToJSON(t []Torrent) []TorrentJSON { return json } -// APITorrentsToJSON : Map Torrents to TorrentsToJSON for API request without reallocations -func APITorrentsToJSON(t []Torrent) []TorrentJSON { - json := make([]TorrentJSON, len(t)) - for i := range t { - json[i] = t[i].ToJSON() - uploaderID, username := HideTorrentUser(json[i].UploaderID, string(json[i].UploaderName), json[i].Hidden) - json[i].UploaderName = template.HTML(username) - json[i].UploaderID = uploaderID - } - return json -} - -// TorrentsToAPI : Map Torrents for API usage without reallocations -func TorrentsToAPI(t []Torrent) []TorrentJSON { - return APITorrentsToJSON(t) -} - // Update : Update a torrent based on model func (t *Torrent) Update(unscope bool) (int, error) { db := ORM @@ -395,13 +389,29 @@ func (t *Torrent) UpdateUnscope() (int, error) { return t.Update(true) } -// HideTorrentUser : hides a torrent user for hidden torrents -func HideTorrentUser(uploaderID uint, uploaderName string, torrentHidden bool) (uint, string) { - if torrentHidden { - return 0, "れんちょん" +// DeleteTorrent : delete a torrent based on id +func (t *Torrent) Delete(definitely bool) (*Torrent, int, error) { + db := ORM + if definitely { + db = ORM.Unscoped() } - if uploaderID == 0 { - return 0, uploaderName + if db.Delete(t).Error != nil { + return t, http.StatusInternalServerError, errors.New("torrent_not_deleted") } - return uploaderID, uploaderName + + if ElasticSearchClient != nil { + err := t.DeleteFromESIndex(ElasticSearchClient) + if err == nil { + log.Infof("Successfully deleted torrent to ES index.") + } else { + log.Errorf("Unable to delete torrent to ES index: %s", err) + } + } + return t, http.StatusOK, nil +} + +// DefinitelyDelete : deletes definitely a torrent based on id +func (t *Torrent) DefinitelyDelete() (*Torrent, int, error) { + return t.Delete(true) + } diff --git a/models/torrents/delete.go b/models/torrents/delete.go deleted file mode 100644 index e0c88472..00000000 --- a/models/torrents/delete.go +++ /dev/null @@ -1,51 +0,0 @@ -package torrents - -import ( - "errors" - "github.com/NyaaPantsu/nyaa/utils/log" - "net/http" - - "github.com/NyaaPantsu/nyaa/models" -) - -// DeleteTorrent : delete a torrent based on id -func DeleteTorrent(id uint) (*models.Torrent, int, error) { - var torrent models.Torrent - if models.ORM.First(&torrent, id).RecordNotFound() { - return &torrent, http.StatusNotFound, errors.New("Torrent is not found") - } - if models.ORM.Delete(&torrent).Error != nil { - return &torrent, http.StatusInternalServerError, errors.New("Torrent was not deleted") - } - - if models.ElasticSearchClient != nil { - err := torrent.DeleteFromESIndex(models.ElasticSearchClient) - if err == nil { - log.Infof("Successfully deleted torrent to ES index.") - } else { - log.Errorf("Unable to delete torrent to ES index: %s", err) - } - } - return &torrent, http.StatusOK, nil -} - -// DefinitelyDelete : deletes definitely a torrent based on id -func DefinitelyDelete(id uint) (*models.Torrent, int, error) { - var torrent models.Torrent - if models.ORM.Unscoped().Model(&torrent).First(&torrent, id).RecordNotFound() { - return &torrent, http.StatusNotFound, errors.New("Torrent is not found") - } - if models.ORM.Unscoped().Model(&torrent).Delete(&torrent).Error != nil { - return &torrent, http.StatusInternalServerError, errors.New("Torrent was not deleted") - } - - if models.ElasticSearchClient != nil { - err := torrent.DeleteFromESIndex(models.ElasticSearchClient) - if err == nil { - log.Infof("Successfully deleted torrent to ES index.") - } else { - log.Errorf("Unable to delete torrent to ES index: %s", err) - } - } - return &torrent, http.StatusOK, nil -} diff --git a/models/torrents/helpers.go b/models/torrents/helpers.go index 7b711ef5..31a3e3e1 100644 --- a/models/torrents/helpers.go +++ b/models/torrents/helpers.go @@ -5,6 +5,8 @@ import ( "fmt" "strconv" + "html/template" + "github.com/NyaaPantsu/nyaa/config" "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/models/activities" @@ -18,7 +20,7 @@ func ExistOrDelete(hash string, user *models.User) error { models.ORM.Unscoped().Model(&models.Torrent{}).Where("torrent_hash = ?", hash).First(&torrentIndb) if torrentIndb.ID > 0 { if user.CurrentUserIdentical(torrentIndb.UploaderID) && torrentIndb.IsDeleted() && !torrentIndb.IsBlocked() { // if torrent is not locked and is deleted and the user is the actual owner - torrent, _, err := DefinitelyDelete(torrentIndb.ID) + torrent, _, err := torrentIndb.DefinitelyDelete() if err != nil { return err } @@ -47,3 +49,31 @@ func NewTorrentEvent(user *models.User, torrent *models.Torrent) error { } return nil } + +// HideUser : hides a torrent user for hidden torrents +func HideUser(uploaderID uint, uploaderName string, torrentHidden bool) (uint, string) { + if torrentHidden { + return 0, "れんちょん" + } + if uploaderID == 0 { + return 0, uploaderName + } + return uploaderID, uploaderName +} + +// APITorrentsToJSON : Map Torrents to TorrentsToJSON for API request without reallocations +func APITorrentsToJSON(t []models.Torrent) []models.TorrentJSON { + json := make([]models.TorrentJSON, len(t)) + for i := range t { + json[i] = t[i].ToJSON() + uploaderID, username := HideUser(json[i].UploaderID, string(json[i].UploaderName), json[i].Hidden) + json[i].UploaderName = template.HTML(username) + json[i].UploaderID = uploaderID + } + return json +} + +// TorrentsToAPI : Map Torrents for API usage without reallocations +func TorrentsToAPI(t []models.Torrent) []models.TorrentJSON { + return APITorrentsToJSON(t) +} diff --git a/models/user.go b/models/user.go index 6153828e..090ce8e6 100644 --- a/models/user.go +++ b/models/user.go @@ -9,6 +9,8 @@ import ( "net/http" + "errors" + "github.com/NyaaPantsu/nyaa/config" "github.com/NyaaPantsu/nyaa/utils/crypto" ) @@ -312,19 +314,19 @@ func (u *User) ParseSettings() { } // UpdateUserCore updates a user. (Applying the modifed data of user). -func (user *User) Update() (int, error) { - if user.Email == "" { - user.MD5 = "" +func (u *User) Update() (int, error) { + if u.Email == "" { + u.MD5 = "" } else { var err error - user.MD5, err = crypto.GenerateMD5Hash(user.Email) + u.MD5, err = crypto.GenerateMD5Hash(u.Email) if err != nil { return http.StatusInternalServerError, err } } - user.UpdatedAt = time.Now() - err := ORM.Save(user).Error + u.UpdatedAt = time.Now() + err := ORM.Save(u).Error if err != nil { return http.StatusInternalServerError, err } @@ -333,12 +335,24 @@ func (user *User) Update() (int, error) { } // 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 +func (u *User) UpdateRaw() (int, error) { + u.UpdatedAt = time.Now() + err := ORM.Model(u).UpdateColumn(u).Error if err != nil { return http.StatusInternalServerError, err } return http.StatusOK, nil } + +// DeleteUser deletes a user. +func (u *User) Delete(currentUser *User) (int, error) { + if u.ID == 0 { + return http.StatusInternalServerError, errors.New("permission_delete_error") + } + err := ORM.Delete(u).Error + if err != nil { + return http.StatusInternalServerError, errors.New("user_not_deleted") + } + return http.StatusOK, nil +} diff --git a/models/users/create.go b/models/users/create.go index 258c509f..9e27a582 100644 --- a/models/users/create.go +++ b/models/users/create.go @@ -3,21 +3,23 @@ package users import ( "errors" "net/http" - "nyaa-master/util/log" "time" "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/utils/crypto" - "github.com/dorajistyle/goyangi/util/modelHelper" + "github.com/NyaaPantsu/nyaa/utils/log" + msg "github.com/NyaaPantsu/nyaa/utils/messages" + "github.com/NyaaPantsu/nyaa/utils/validator" + "github.com/NyaaPantsu/nyaa/utils/validator/user" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" ) // CreateUserFromForm creates a user from a registration form. -func CreateUserFromRequest(registrationForm *formStruct.RegistrationForm) (*models.User, error) { +func CreateUserFromRequest(registrationForm *userValidator.RegistrationForm) (*models.User, error) { var user = &models.User{} log.Debugf("registrationForm %+v\n", registrationForm) - modelHelper.AssignValue(&user, ®istrationForm) + validator.Bind(&user, ®istrationForm) if user.Email == "" { user.MD5 = "" } else { @@ -37,7 +39,7 @@ func CreateUserFromRequest(registrationForm *formStruct.RegistrationForm) (*mode user.APIToken, _ = crypto.GenerateRandomToken32() user.APITokenExpiry = time.Unix(0, 0) - if ORM.Create(&user).Error != nil { + if models.ORM.Create(&user).Error != nil { return user, errors.New("user not created") } @@ -45,39 +47,31 @@ func CreateUserFromRequest(registrationForm *formStruct.RegistrationForm) (*mode } // CreateUser creates a user. -func CreateUser(c *gin.Context) int { - var user models.User - var registrationForm formStruct.RegistrationForm - var status int +func CreateUser(c *gin.Context) (*models.User, int) { + var user *models.User + var registrationForm userValidator.RegistrationForm var err error messages := msg.GetMessages(c) c.Bind(®istrationForm) usernameCandidate := SuggestUsername(registrationForm.Username) if usernameCandidate != registrationForm.Username { messages.AddErrorTf("username", "username_taken", usernameCandidate) - return http.StatusInternalServerError + return user, http.StatusInternalServerError } if registrationForm.Email != "" && CheckEmail(registrationForm.Email) { messages.AddErrorT("email", "email_in_db") - return http.StatusInternalServerError + return user, http.StatusInternalServerError } password, err := bcrypt.GenerateFromPassword([]byte(registrationForm.Password), 10) if err != nil { - messages.ImportFromError("errors", err) - return http.StatusInternalServerError + messages.Error(err) + return user, http.StatusInternalServerError } registrationForm.Password = string(password) user, err = CreateUserFromRequest(®istrationForm) if err != nil { - messages.ImportFromError("errors", err) - return http.StatusInternalServerError + messages.Error(err) + return user, http.StatusInternalServerError } - if registrationForm.Email != "" { - SendVerificationToUser(user, registrationForm.Email) - } - status, err = cookies.SetLoginCookies(c, user) - if err != nil { - messages.ImportFromError("errors", err) - } - return status + return user, http.StatusOK } diff --git a/models/users/delete.go b/models/users/delete.go deleted file mode 100644 index 63cb5286..00000000 --- a/models/users/delete.go +++ /dev/null @@ -1,22 +0,0 @@ -package users - -// DeleteUser deletes a user. -func DeleteUser(currentUser *models.User, id string) (int, error) { - var user models.User - - if db.ORM.First(&user, id).RecordNotFound() { - return http.StatusNotFound, errors.New("user_not_found") - } - if user.ID == 0 { - return http.StatusInternalServerError, errors.New("permission_delete_error") - } - err := db.ORM.Delete(&user).Error - if err != nil { - return http.StatusInternalServerError, errors.New("user_not_deleted") - } - if user.CurrentUserIdentical(currentUser, user.ID) { - ClearCookie(c) - } - - return http.StatusOK, nil -} \ No newline at end of file diff --git a/models/users/find.go b/models/users/find.go index 897a181b..c75bcf93 100644 --- a/models/users/find.go +++ b/models/users/find.go @@ -3,9 +3,9 @@ package users import ( "errors" "net/http" - "nyaa-master/util/log" "github.com/NyaaPantsu/nyaa/models" + "github.com/NyaaPantsu/nyaa/utils/log" ) // FindOrCreateUser creates a user. @@ -92,9 +92,9 @@ func FindOldUploadsByUsername(username string) ([]uint, error) { } // FindByID retrieves a user by ID. -func FindByID(id string) (*models.User, int, error) { - var user models.User - if models.ORM.Preload("Notifications").Last(&user, id).RecordNotFound() { +func FindByID(id uint) (*models.User, int, error) { + var user = &models.User{} + if models.ORM.Preload("Notifications").Last(user, id).RecordNotFound() { return user, http.StatusNotFound, errors.New("user_not_found") } var liked, likings []models.User @@ -102,11 +102,18 @@ func FindByID(id string) (*models.User, int, error) { models.ORM.Joins("JOIN user_follows on user_follows.following=?", user.ID).Where("users.user_id = user_follows.user_id").Group("users.user_id").Find(&liked) user.Followers = likings user.Likings = liked - return &user, http.StatusOK, nil + return user, http.StatusOK, nil +} + +func SessionByID(id uint) (user *models.User, status int, err error) { + if models.ORM.Preload("Notifications").Where("user_id = ?", id).First(user).RecordNotFound() { // We only load unread notifications + status, err = http.StatusBadRequest, errors.New("user_not_found") + } + return } // FindForAdmin retrieves a user for an administrator, preloads torrents. -func FindForAdmin(id string) (*models.User, int, error) { +func FindForAdmin(id uint) (*models.User, int, error) { var user = &models.User{} if models.ORM.Preload("Notifications").Preload("Torrents").Last(user, id).RecordNotFound() { return user, http.StatusNotFound, errors.New("user_not_found") diff --git a/models/users/helpers.go b/models/users/helpers.go index f34e84cd..fb31b282 100644 --- a/models/users/helpers.go +++ b/models/users/helpers.go @@ -3,12 +3,11 @@ package users import ( "errors" "net/http" - "nyaa-master/db" - "nyaa-master/util/log" "strconv" "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/utils/validator/user" + "github.com/NyaaPantsu/nyaa/utils/log" "golang.org/x/crypto/bcrypt" ) @@ -18,7 +17,7 @@ func CheckEmail(email string) bool { return false } var count int - db.ORM.Model(models.User{}).Where("email = ?", email).Count(&count) + models.ORM.Model(models.User{}).Where("email = ?", email).Count(&count) return count != 0 } @@ -31,12 +30,12 @@ func Exists(email string, pass string) (user *models.User, status int, err error // search by email or username if userValidator.EmailValidation(email) { - if db.ORM.Where("email = ?", email).First(user).RecordNotFound() { + if models.ORM.Where("email = ?", email).First(user).RecordNotFound() { status, err = http.StatusNotFound, errors.New("user_not_found") return } - } else if db.ORM.Where("username = ?", email).First(user).RecordNotFound() { - status, err = ttp.StatusNotFound, errors.New("user_not_found") + } else if models.ORM.Where("username = ?", email).First(user).RecordNotFound() { + status, err = http.StatusNotFound, errors.New("user_not_found") return } err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass)) @@ -53,13 +52,14 @@ func Exists(email string, pass string) (user *models.User, status int, err error return } status, err = http.StatusOK, nil + return } // SuggestUsername suggest user's name if user's name already occupied. func SuggestUsername(username string) string { var count int var usernameCandidate string - db.ORM.Model(models.User{}).Where(&models.User{Username: username}).Count(&count) + models.ORM.Model(models.User{}).Where(&models.User{Username: username}).Count(&count) log.Debugf("count Before : %d", count) if count == 0 { return username @@ -68,7 +68,7 @@ func SuggestUsername(username string) string { for { usernameCandidate = username + strconv.Itoa(postfix) log.Debugf("usernameCandidate: %s\n", usernameCandidate) - db.ORM.Model(models.User{}).Where(&models.User{Username: usernameCandidate}).Count(&count) + models.ORM.Model(models.User{}).Where(&models.User{Username: usernameCandidate}).Count(&count) log.Debugf("count after : %d\n", count) postfix = postfix + 1 if count == 0 { diff --git a/models/users/request.go b/models/users/request.go index 552708d4..9e363b6f 100644 --- a/models/users/request.go +++ b/models/users/request.go @@ -8,89 +8,29 @@ import ( "github.com/NyaaPantsu/nyaa/models" "github.com/NyaaPantsu/nyaa/utils/validator" + "github.com/NyaaPantsu/nyaa/utils/validator/user" "github.com/gin-gonic/gin" - "github.com/gorilla/context" "golang.org/x/crypto/bcrypt" ) -// RetrieveUser retrieves a user. -func RetrieveFromRequest(c *gin.Context, id uint) (*models.User, bool, uint, int, error) { - var user models.User - var currentUserID uint - var isAuthor bool - - if models.ORM.First(&user, id).RecordNotFound() { - return nil, isAuthor, currentUserID, http.StatusNotFound, errors.New("user_not_found") - } - currentUser, err := CurrentUser(c) - if err == nil { - currentUserID = currentUser.ID - isAuthor = currentUser.ID == user.ID - } - - return &user, isAuthor, currentUserID, http.StatusOK, nil -} - -// CurrentUser retrieves a current user. -func CurrentUser(c *gin.Context) (user *models.User, status int, err error) { - encoded := c.Request.Header.Get("X-Auth-Token") - if len(encoded) == 0 { - // check cookie instead - cookie, err = c.Cookie(CookieName) - if err != nil { - status = http.StatusInternalServerError - return - } - encoded = cookie - } - userID, err = DecodeCookie(encoded) - if err != nil { - status = http.StatusInternalServerError - return - } - - userFromContext := getUserFromContext(c) - - if userFromContext.ID > 0 && userID == userFromContext.ID { - user = &userFromContext - } else { - if ORM.Preload("Notifications").Where("user_id = ?", userID).First(user).RecordNotFound() { // We only load unread notifications - return nil, user, errors.New("user_not_found") - } - setUserToContext(c, *user) - } - - if user.IsBanned() { - // recheck as user might've been banned in the meantime - return nil, user, errors.New("account_banned") - } - if err != nil { - status = http.StatusInternalServerError - return - } - status = http.StatusOK - return -} - // UpdateFromRequest updates a user. -func UpdateFromRequest(c *gin.Context, form *formStruct.UserForm, formSet *formStruct.UserSettingsForm, currentUser *models.User, id string) (user *models.User, status int) { - messages := msg.GetMessages(c) +func UpdateFromRequest(c *gin.Context, form *userValidator.UserForm, formSet *userValidator.UserSettingsForm, currentUser *models.User, id uint) (user *models.User, status int, err error) { if models.ORM.First(&user, id).RecordNotFound() { - messages.AddErrorT("errors", "user_not_found") + err = errors.New("user_not_found") status = http.StatusNotFound return } if form.Password != "" { - err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(form.CurrentPassword)) - if err != nil && !currentUser.IsModerator() { - messages.AddErrorT("errors", "incorrect_password") + errBcrypt := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(form.CurrentPassword)) + if errBcrypt != nil && !currentUser.IsModerator() { + err = errors.New("incorrect_password") status = http.StatusInternalServerError return } - newPassword, err := bcrypt.GenerateFromPassword([]byte(form.Password), 10) - if err != nil { - messages.AddErrorT("errors", "error_password_generating") + newPassword, errBcrypt := bcrypt.GenerateFromPassword([]byte(form.Password), 10) + if errBcrypt != nil { + err = errors.New("error_password_generating") status = http.StatusInternalServerError return } @@ -102,13 +42,8 @@ func UpdateFromRequest(c *gin.Context, form *formStruct.UserForm, formSet *formS form.Status = user.Status form.Username = user.Username } - if form.Email != user.Email { - // send verification to new email and keep old - email.SendVerificationToUser(user, form.Email) - form.Email = user.Email - } log.Debugf("form %+v\n", form) - validator.AssignValue(&user, form) + validator.Bind(&user, form) // We set settings here user.ParseSettings() @@ -124,20 +59,6 @@ func UpdateFromRequest(c *gin.Context, form *formStruct.UserForm, formSet *formS user.Settings.Set("followed_email", formSet.FollowedEmail) user.SaveSettings() - status, err = Update(user) - if err != nil { - messages.Error(err) - } + status, err = user.Update() return } - -func getUserFromContext(c *gin.Context) models.User { - if rv := context.Get(c.Request, UserContextKey); rv != nil { - return rv.(models.User) - } - return models.User{} -} - -func setUserToContext(c *gin.Context, val models.User) { - context.Set(c.Request, UserContextKey, val) -} diff --git a/models/users/update.go b/models/users/update.go deleted file mode 100644 index 82abcb9f..00000000 --- a/models/users/update.go +++ /dev/null @@ -1 +0,0 @@ -package users diff --git a/translations/en-us.all.json b/translations/en-us.all.json index da52dbc9..abd92471 100644 --- a/translations/en-us.all.json +++ b/translations/en-us.all.json @@ -983,6 +983,10 @@ "id": "torrent_deleted_definitely", "translation": "Torrent has been erased from the database!" }, + { + "id": "torrent_not_deleted", + "translation": "Torrent was not deleted" + }, { "id": "torrent_unblocked", "translation": "Torrent has been unlocked!" diff --git a/utils/cookies/helpers.go b/utils/cookies/helpers.go index f79ca73d..b335ced4 100644 --- a/utils/cookies/helpers.go +++ b/utils/cookies/helpers.go @@ -1,5 +1,7 @@ package cookies +import "github.com/NyaaPantsu/nyaa/config" + func getDomainName() string { domain := config.Conf.Cookies.DomainName if config.Conf.Environment == "DEVELOPMENT" { diff --git a/utils/cookies/user.go b/utils/cookies/user.go index 0b8592ed..42a37a40 100644 --- a/utils/cookies/user.go +++ b/utils/cookies/user.go @@ -13,6 +13,7 @@ import ( "github.com/NyaaPantsu/nyaa/utils/timeHelper" "github.com/NyaaPantsu/nyaa/utils/validator/user" "github.com/gin-gonic/gin" + "github.com/gorilla/context" "github.com/gorilla/securecookie" ) @@ -24,6 +25,20 @@ const ( UserContextKey = "nyaapantsu.user" ) +// NewCurrentUserRetriever create CurrentUserRetriever Struct for languages +func NewCurrentUserRetriever() *CurrentUserRetriever { + return &CurrentUserRetriever{} +} + +// CurrentUserRetriever struct for languages +type CurrentUserRetriever struct{} + +// RetrieveCurrentUser retrieve current user for languages +func (*CurrentUserRetriever) RetrieveCurrentUser(c *gin.Context) (models.User, error) { + user, _, err := CurrentUser(c) + return *user, err +} + // CreateUserAuthentication creates user authentication. func CreateUserAuthentication(c *gin.Context, form *userValidator.LoginForm) (*models.User, int, error) { username := form.Username @@ -32,7 +47,7 @@ func CreateUserAuthentication(c *gin.Context, form *userValidator.LoginForm) (*m if err != nil { return user, status, err } - status, err = SetLogin(c, *user) + status, err = SetLogin(c, user) return user, status, err } @@ -81,7 +96,7 @@ func Clear(c *gin.Context) { } // SetLogin sets the authentication cookie -func SetLogin(c *gin.Context, user models.User) (int, error) { +func SetLogin(c *gin.Context, user *models.User) (int, error) { maxAge := getMaxAge() validUntil := timeHelper.FewDurationLater(time.Duration(maxAge) * time.Second) encoded, err := Encode(user.ID, validUntil) @@ -94,3 +109,72 @@ func SetLogin(c *gin.Context, user models.User) (int, error) { c.Header("X-Auth-Token", encoded) return http.StatusOK, nil } + +// CurrentUser retrieves a current user. +func CurrentUser(c *gin.Context) (user *models.User, status int, err error) { + encoded := c.Request.Header.Get("X-Auth-Token") + if len(encoded) == 0 { + // check cookie instead + cookie, errCookie := c.Cookie(CookieName) + if errCookie != nil { + err = errCookie + status = http.StatusInternalServerError + return + } + encoded = cookie + } + userID, err := Decode(encoded) + if err != nil { + status = http.StatusInternalServerError + return + } + + userFromContext := getUserFromContext(c) + + if userFromContext.ID > 0 && userID == userFromContext.ID { + user = &userFromContext + } else { + users.SessionByID(userID) + setUserToContext(c, *user) + } + + if user.IsBanned() { + // recheck as user might've been banned in the meantime + status, err = http.StatusUnauthorized, errors.New("account_banned") + return + } + if err != nil { + status = http.StatusInternalServerError + return + } + status = http.StatusOK + return +} +func getUserFromContext(c *gin.Context) models.User { + if rv := context.Get(c.Request, UserContextKey); rv != nil { + return rv.(models.User) + } + return models.User{} +} + +func setUserToContext(c *gin.Context, val models.User) { + context.Set(c.Request, UserContextKey, val) +} + +// RetrieveUser retrieves a user. +func RetrieveUserFromRequest(c *gin.Context, id uint) (*models.User, bool, uint, int, error) { + var user models.User + var currentUserID uint + var isAuthor bool + + if models.ORM.First(&user, id).RecordNotFound() { + return nil, isAuthor, currentUserID, http.StatusNotFound, errors.New("user_not_found") + } + currentUser, _, err := CurrentUser(c) + if err == nil { + currentUserID = currentUser.ID + isAuthor = currentUser.ID == user.ID + } + + return &user, isAuthor, currentUserID, http.StatusOK, nil +} diff --git a/utils/email/email.go b/utils/email/email.go index 1c574041..4035ba35 100644 --- a/utils/email/email.go +++ b/utils/email/email.go @@ -5,7 +5,7 @@ import ( "github.com/NyaaPantsu/nyaa/config" "github.com/NyaaPantsu/nyaa/utils/log" - gomail "gopkg.in/gomail.v2" + "gopkg.in/gomail.v2" ) // Error type diff --git a/utils/email/verification.go b/utils/email/verification.go index 874eca1c..ce1fca71 100644 --- a/utils/email/verification.go +++ b/utils/email/verification.go @@ -1,4 +1,4 @@ -package userService +package email import ( "errors" @@ -8,16 +8,13 @@ import ( "time" "github.com/NyaaPantsu/nyaa/config" - "github.com/NyaaPantsu/nyaa/db" - "github.com/NyaaPantsu/nyaa/model" - "github.com/NyaaPantsu/nyaa/utils" + "github.com/NyaaPantsu/nyaa/models" + "github.com/NyaaPantsu/nyaa/models/users" + "github.com/NyaaPantsu/nyaa/utils/format" "github.com/NyaaPantsu/nyaa/utils/publicSettings" "github.com/NyaaPantsu/nyaa/utils/timeHelper" "github.com/gin-gonic/gin" "github.com/gorilla/securecookie" - "github.com/NyaaPantsu/nyaa/models/users" - "os/user" - "github.com/NyaaPantsu/nyaa/models" ) var verificationHandler = securecookie.New(config.EmailTokenHashKey, nil) @@ -29,12 +26,12 @@ func SendEmailVerification(to string, token string) error { return err } content := T("link") + " : " + config.Conf.WebAddress.Nyaa + "/verify/email/" + token - contentHTML := T("verify_email_content") + "
" + "" + util.GetHostname(config.Conf.WebAddress.Nyaa) + "/verify/email/" + token + "" + contentHTML := T("verify_email_content") + "
" + "" + format.GetHostname(config.Conf.WebAddress.Nyaa) + "/verify/email/" + token + "" return SendEmailFromAdmin(to, T("verify_email_title"), content, contentHTML) } // SendVerificationToUser sends an email verification token to user. -func SendVerificationToUser(user models.User, newEmail string) (int, error) { +func SendVerificationToUser(user *models.User, newEmail string) (int, error) { validUntil := timeHelper.TwentyFourHoursLater() // TODO: longer duration? value := map[string]string{ "t": strconv.FormatInt(validUntil.Unix(), 10), @@ -65,9 +62,10 @@ func EmailVerification(token string, c *gin.Context) (int, error) { return http.StatusForbidden, errors.New("token_expired") } id, _ := strconv.Atoi(value["u"]) - if user, _, err := users.FindByID(uint(id)); err != nil { + user, _, err := users.FindByID(uint(id)) + if err != nil { return http.StatusNotFound, errors.New("user_not_found") } user.Email = value["e"] - return users.Update(user) + return user.Update() } diff --git a/utils/upload/upload.go b/utils/upload/upload.go index f69785ae..15541e3d 100644 --- a/utils/upload/upload.go +++ b/utils/upload/upload.go @@ -190,7 +190,7 @@ func writeTorrentToDisk(file multipart.File, name string, fullpath *string) erro } // NewTorrentRequest : creates a new torrent request struc with some default value -func NewTorrentRequest(params ...string) (torrentRequest torrentValidator.TorrentRequest) { +func NewTorrentRequest(params ...string) (torrentRequest *torrentValidator.TorrentRequest) { if len(params) > 1 { torrentRequest.Category = params[0] } else {