✨ Abuse report system
This commit is contained in:
		@@ -27,6 +27,7 @@ var AutoMaintainRange = []any{
 | 
				
			|||||||
	&models.ApiKey{},
 | 
						&models.ApiKey{},
 | 
				
			||||||
	&models.SignRecord{},
 | 
						&models.SignRecord{},
 | 
				
			||||||
	&models.PreferenceNotification{},
 | 
						&models.PreferenceNotification{},
 | 
				
			||||||
 | 
						&models.AbuseReport{},
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func RunMigration(source *gorm.DB) error {
 | 
					func RunMigration(source *gorm.DB) error {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										19
									
								
								pkg/internal/models/reports.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								pkg/internal/models/reports.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						ReportStatusPending   = "pending"
 | 
				
			||||||
 | 
						ReportStatusReviewing = "reviewing"
 | 
				
			||||||
 | 
						ReportStatusConfirmed = "confirmed"
 | 
				
			||||||
 | 
						ReportStatusRejected  = "rejected"
 | 
				
			||||||
 | 
						ReportStatusProcessed = "processed"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AbuseReport struct {
 | 
				
			||||||
 | 
						BaseModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Resource  string  `json:"resource"`
 | 
				
			||||||
 | 
						Reason    string  `json:"reason"`
 | 
				
			||||||
 | 
						Status    string  `json:"status"`
 | 
				
			||||||
 | 
						AccountID uint    `json:"account_id"`
 | 
				
			||||||
 | 
						Account   Account `json:"account"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -30,6 +30,17 @@ func MapAPIs(app *fiber.App, baseURL string) {
 | 
				
			|||||||
			preferences.Put("/notifications", updateNotificationPreference)
 | 
								preferences.Put("/notifications", updateNotificationPreference)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							reports := api.Group("/reports").Name("Reports API")
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								abuse := reports.Group("/abuse").Name("Abuse Reports")
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									abuse.Get("/", listAbuseReports)
 | 
				
			||||||
 | 
									abuse.Get("/:id", getAbuseReport)
 | 
				
			||||||
 | 
									abuse.Put("/:id/status", updateAbuseReportStatus)
 | 
				
			||||||
 | 
									abuse.Post("/", createAbuseReport)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		api.Get("/users/lookup", lookupAccount)
 | 
							api.Get("/users/lookup", lookupAccount)
 | 
				
			||||||
		api.Get("/users/search", searchAccount)
 | 
							api.Get("/users/search", searchAccount)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										77
									
								
								pkg/internal/server/api/reports.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								pkg/internal/server/api/reports.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
 | 
				
			||||||
 | 
						"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts"
 | 
				
			||||||
 | 
						"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
 | 
				
			||||||
 | 
						"github.com/gofiber/fiber/v2"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func listAbuseReports(c *fiber.Ctx) error {
 | 
				
			||||||
 | 
						if err := exts.EnsureAuthenticated(c); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						user := c.Locals("user").(models.Account)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reports, err := services.ListAbuseReport(user)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fiber.NewError(fiber.StatusBadRequest, err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return c.JSON(reports)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getAbuseReport(c *fiber.Ctx) error {
 | 
				
			||||||
 | 
						id, _ := c.ParamsInt("id")
 | 
				
			||||||
 | 
						report, err := services.GetAbuseReport(uint(id))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fiber.NewError(fiber.StatusNotFound, err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return c.JSON(report)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func updateAbuseReportStatus(c *fiber.Ctx) error {
 | 
				
			||||||
 | 
						if err := exts.EnsureGrantedPerm(c, "DealAbuseReport", true); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var data struct {
 | 
				
			||||||
 | 
							Status string `json:"status" validate:"required"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := exts.BindAndValidate(c, &data); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						id, _ := c.ParamsInt("id")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := services.UpdateAbuseReportStatus(uint(id), data.Status); err != nil {
 | 
				
			||||||
 | 
							return fiber.NewError(fiber.StatusBadRequest, err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return c.SendStatus(fiber.StatusOK)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func createAbuseReport(c *fiber.Ctx) error {
 | 
				
			||||||
 | 
						if err := exts.EnsureAuthenticated(c); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						user := c.Locals("user").(models.Account)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var data struct {
 | 
				
			||||||
 | 
							Resource string `json:"resource" validate:"required"`
 | 
				
			||||||
 | 
							Reason   string `json:"reason" validate:"required,min=16,max=4096"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := exts.BindAndValidate(c, &data); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						report, err := services.NewAbuseReport(data.Resource, data.Reason, user)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fiber.NewError(fiber.StatusBadRequest, err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return c.JSON(report)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										75
									
								
								pkg/internal/services/reports.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								pkg/internal/services/reports.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					package services
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
 | 
				
			||||||
 | 
						"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ListAbuseReport(account models.Account) ([]models.AbuseReport, error) {
 | 
				
			||||||
 | 
						var reports []models.AbuseReport
 | 
				
			||||||
 | 
						err := database.C.
 | 
				
			||||||
 | 
							Where("account_id = ?", account.ID).
 | 
				
			||||||
 | 
							Find(&reports).Error
 | 
				
			||||||
 | 
						return reports, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetAbuseReport(id uint) (models.AbuseReport, error) {
 | 
				
			||||||
 | 
						var report models.AbuseReport
 | 
				
			||||||
 | 
						err := database.C.
 | 
				
			||||||
 | 
							Where("id = ?", id).
 | 
				
			||||||
 | 
							First(&report).Error
 | 
				
			||||||
 | 
						return report, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func UpdateAbuseReportStatus(id uint, status string) error {
 | 
				
			||||||
 | 
						var report models.AbuseReport
 | 
				
			||||||
 | 
						err := database.C.
 | 
				
			||||||
 | 
							Where("id = ?", id).
 | 
				
			||||||
 | 
							Preload("Account").
 | 
				
			||||||
 | 
							First(&report).Error
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						report.Status = status
 | 
				
			||||||
 | 
						account := report.Account
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = database.C.Save(&report).Error
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						NewNotification(models.Notification{
 | 
				
			||||||
 | 
							Topic:     "reports.feedback",
 | 
				
			||||||
 | 
							Title:     "Abuse report status has been changed.",
 | 
				
			||||||
 | 
							Body:      fmt.Sprintf("The report created by you with ID #%d's status has been changed to %s", id, status),
 | 
				
			||||||
 | 
							Account:   account,
 | 
				
			||||||
 | 
							AccountID: account.ID,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewAbuseReport(resource string, reason string, account models.Account) (models.AbuseReport, error) {
 | 
				
			||||||
 | 
						var report models.AbuseReport
 | 
				
			||||||
 | 
						if err := database.C.
 | 
				
			||||||
 | 
							Where(
 | 
				
			||||||
 | 
								"resource = ? AND account_id = ? AND status IN ?",
 | 
				
			||||||
 | 
								resource,
 | 
				
			||||||
 | 
								account.ID,
 | 
				
			||||||
 | 
								[]string{models.ReportStatusPending, models.ReportStatusReviewing},
 | 
				
			||||||
 | 
							).First(&report).Error; err == nil {
 | 
				
			||||||
 | 
							return report, fmt.Errorf("you already reported this resource and it still in process")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						report = models.AbuseReport{
 | 
				
			||||||
 | 
							Resource:  resource,
 | 
				
			||||||
 | 
							Reason:    reason,
 | 
				
			||||||
 | 
							AccountID: account.ID,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := database.C.Create(&report).Error
 | 
				
			||||||
 | 
						return report, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user