package admin import ( "fmt" "net/http" "strconv" "strings" "code.nonshy.com/nonshy/website/pkg/config" "code.nonshy.com/nonshy/website/pkg/models" "code.nonshy.com/nonshy/website/pkg/models/deletion" "code.nonshy.com/nonshy/website/pkg/session" "code.nonshy.com/nonshy/website/pkg/templates" ) // Mark a user photo as Explicit for them. func MarkPhotoExplicit() http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var ( photoID uint64 next = r.FormValue("next") ) if !strings.HasPrefix(next, "/") { next = "/" } // Get current user. currentUser, err := session.CurrentUser(r) if err != nil { session.FlashError(w, r, "Failed to get current user: %s", err) templates.Redirect(w, "/") return } if idInt, err := strconv.Atoi(r.FormValue("photo_id")); err == nil { photoID = uint64(idInt) } else { session.FlashError(w, r, "Invalid or missing photo_id parameter: %s", err) templates.Redirect(w, next) return } // Get this photo. photo, err := models.GetPhoto(photoID) if err != nil { session.FlashError(w, r, "Didn't find photo ID in database: %s", err) templates.Redirect(w, next) return } photo.Explicit = true if err := photo.Save(); err != nil { session.FlashError(w, r, "Couldn't save photo: %s", err) } else { session.Flash(w, r, "Marked photo as Explicit!") } // Log the change. models.LogUpdated(&models.User{ID: photo.UserID}, currentUser, "photos", photo.ID, "Marked explicit by admin action.", []models.FieldDiff{ models.NewFieldDiff("Explicit", false, true), }) templates.Redirect(w, next) }) } // Admin actions against a user account. func UserActions() http.HandlerFunc { tmpl := templates.Must("admin/user_actions.html") return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var ( intent = r.FormValue("intent") confirm = r.Method == http.MethodPost reason = r.FormValue("reason") // for impersonation userId uint64 ) // Get current user. currentUser, err := session.CurrentUser(r) if err != nil { session.FlashError(w, r, "Failed to get current user: %s", err) templates.Redirect(w, "/") return } if idInt, err := strconv.Atoi(r.FormValue("user_id")); err == nil { userId = uint64(idInt) } else { session.FlashError(w, r, "Invalid or missing user_id parameter: %s", err) templates.Redirect(w, "/admin") return } // Get this user. user, err := models.GetUser(userId) if err != nil { session.FlashError(w, r, "Didn't find user ID in database: %s", err) templates.Redirect(w, "/admin") return } // Template variables. var vars = map[string]interface{}{ "Intent": intent, "User": user, } switch intent { case "insights": // Admin insights (peek at block lists, etc.) if !currentUser.HasAdminScope(config.ScopeUserInsight) { session.FlashError(w, r, "Missing admin scope: %s", config.ScopeUserInsight) templates.Redirect(w, "/admin") return } insights, err := models.GetBlocklistInsights(user) if err != nil { session.FlashError(w, r, "Error getting blocklist insights: %s", err) } vars["BlocklistInsights"] = insights case "impersonate": // Scope check. if !currentUser.HasAdminScope(config.ScopeUserImpersonate) { session.FlashError(w, r, "Missing admin scope: %s", config.ScopeUserImpersonate) templates.Redirect(w, "/admin") return } if confirm { if err := session.ImpersonateUser(w, r, user, currentUser, reason); err != nil { session.FlashError(w, r, "Failed to impersonate user: %s", err) } else { session.Flash(w, r, "You are now impersonating %s", user.Username) templates.Redirect(w, "/me") return } } case "ban": // Scope check. if !currentUser.HasAdminScope(config.ScopeUserBan) { session.FlashError(w, r, "Missing admin scope: %s", config.ScopeUserBan) templates.Redirect(w, "/admin") return } if confirm { status := r.PostFormValue("status") if status == "active" { user.Status = models.UserStatusActive } else if status == "banned" { user.Status = models.UserStatusBanned } user.Save() session.Flash(w, r, "User ban status updated!") templates.Redirect(w, "/u/"+user.Username) // Log the change. models.LogEvent(user, currentUser, models.ChangeLogBanned, "users", currentUser.ID, fmt.Sprintf("User ban status updated to: %s", status)) return } case "promote": // Scope check. if !currentUser.HasAdminScope(config.ScopeUserPromote) { session.FlashError(w, r, "Missing admin scope: %s", config.ScopeUserPromote) templates.Redirect(w, "/admin") return } if confirm { action := r.PostFormValue("action") user.IsAdmin = action == "promote" user.Save() session.Flash(w, r, "User admin status updated!") templates.Redirect(w, "/u/"+user.Username) // Log the change. models.LogEvent(user, currentUser, models.ChangeLogAdmin, "users", currentUser.ID, fmt.Sprintf("User admin status updated to: %s", action)) return } case "delete": // Scope check. if !currentUser.HasAdminScope(config.ScopeUserDelete) { session.FlashError(w, r, "Missing admin scope: %s", config.ScopeUserDelete) templates.Redirect(w, "/admin") return } if confirm { if err := deletion.DeleteUser(user); err != nil { session.FlashError(w, r, "Failed when deleting the user: %s", err) } else { session.Flash(w, r, "User has been deleted!") } templates.Redirect(w, "/admin") // Log the change. models.LogDeleted(nil, currentUser, "users", user.ID, fmt.Sprintf("Username %s has been deleted by an admin.", user.Username), nil) return } default: session.FlashError(w, r, "Unsupported admin user intent: %s", intent) templates.Redirect(w, "/admin") return } if err := tmpl.Execute(w, r, vars); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }) } // Un-impersonate a user account. func Unimpersonate() http.HandlerFunc { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sess := session.Get(r) if sess.Impersonator > 0 { user, err := models.GetUser(sess.Impersonator) if err != nil { session.FlashError(w, r, "Couldn't unimpersonate: impersonator (%d) is not an admin!", user.ID) templates.Redirect(w, "/") return } session.LoginUser(w, r, user) session.Flash(w, r, "No longer impersonating.") templates.Redirect(w, "/") } templates.Redirect(w, "/") }) }