AdminController.java
package com.ctrlbuy.webshop.controller;
import com.ctrlbuy.webshop.security.entity.User;
import com.ctrlbuy.webshop.security.repository.UserRepository;
import com.ctrlbuy.webshop.service.UserService;
import com.ctrlbuy.webshop.service.EmailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@Controller
@RequestMapping("/admin")
public class AdminController {
@Autowired
private UserService userService;
@Autowired
private EmailService emailService;
@Autowired
private UserRepository userRepository;
/**
* Admin Dashboard - huvudsida efter inloggning
*/
@GetMapping("/dashboard")
public String dashboard(Model model) {
List<User> users = userService.getAllUsers();
// Beräkna statistik
long totalUsers = users.size();
long activeUsers = users.stream()
.filter(User::isActive)
.count();
long inactiveUsers = totalUsers - activeUsers;
long verifiedUsers = users.stream()
.filter(User::isEmailVerified)
.count();
// Lägg till data i model för Thymeleaf
model.addAttribute("users", users);
model.addAttribute("totalUsers", totalUsers);
model.addAttribute("activeUsers", activeUsers);
model.addAttribute("inactiveUsers", inactiveUsers);
model.addAttribute("verifiedUsers", verifiedUsers);
return "admin/dashboard";
}
/**
* Redirect från /admin till dashboard
*/
@GetMapping("")
public String adminHome() {
return "redirect:/admin/dashboard";
}
/**
* ✅ UPPDATERAD: Visa användare med filter för aktiva/inaktiva
*/
@GetMapping("/users")
public String listUsers(@RequestParam(defaultValue = "active") String filter, Model model) {
List<User> users;
String pageTitle;
switch (filter.toLowerCase()) {
case "inactive":
users = userService.getInactiveUsers();
pageTitle = "Inaktiva användare";
break;
case "all":
users = userService.getAllUsers();
pageTitle = "Alla användare";
break;
default: // "active"
users = userService.getActiveUsers();
pageTitle = "Aktiva användare";
break;
}
model.addAttribute("users", users);
model.addAttribute("currentFilter", filter);
model.addAttribute("pageTitle", pageTitle);
model.addAttribute("title", "Kundhantering - CtrlBuy Admin");
// Statistik för filter-knappar
long totalUsers = userService.countAllUsers();
long activeUsers = userService.countActiveUsers();
long inactiveUsers = totalUsers - activeUsers;
long verifiedUsers = users.stream()
.filter(User::isEmailVerified)
.count();
model.addAttribute("totalUsers", totalUsers);
model.addAttribute("activeUsers", activeUsers);
model.addAttribute("inactiveUsers", inactiveUsers);
model.addAttribute("verifiedUsers", verifiedUsers);
return "admin/users";
}
/**
* ✅ UPPDATERAD: Inaktivera användare (mjuk borttagning) med e-postnotifiering
*/
@PostMapping("/users/deactivate/{id}")
@ResponseBody
public ResponseEntity<?> deactivateUser(@PathVariable Long id, @RequestParam(required = false) String reason) {
try {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Användaren finns inte"));
}
String username = user.getUsername();
// Förhindra att admin inaktiverar sig själv
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && username.equals(auth.getName())) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Du kan inte inaktivera dig själv"));
}
// Förhindra inaktivering av huvudadmin "fredrik"
if ("fredrik".equals(username)) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Huvudadmin kan inte inaktiveras"));
}
// Inaktivera användaren (mjuk borttagning)
user.setActive(false);
userService.save(user);
// Skicka e-postnotifiering till användaren
try {
String adminUsername = auth != null ? auth.getName() : "System";
emailService.sendAccountDeactivationNotification(user, adminUsername, reason);
} catch (Exception emailError) {
// Logga men låt inte e-post-fel stoppa inaktiveringen
System.err.println("Kunde inte skicka inaktiveringsnotifiering till " + user.getEmail() + ": " + emailError.getMessage());
}
return ResponseEntity.ok()
.body(Map.of(
"success", true,
"message", "Användare '" + username + "' har inaktiverats (e-post " + user.getEmail() + " är nu blockerad)"
));
} catch (Exception e) {
return ResponseEntity.status(500)
.body(Map.of("success", false, "error", "Fel vid inaktivering: " + e.getMessage()));
}
}
/**
* ✅ UPPDATERAD: Reaktivera användare med e-postnotifiering
*/
@PostMapping("/users/reactivate/{id}")
@ResponseBody
public ResponseEntity<?> reactivateUser(@PathVariable Long id) {
try {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Användaren finns inte"));
}
// Reaktivera användaren
user.setActive(true);
userService.save(user);
// Skicka e-postnotifiering till användaren
try {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String adminUsername = auth != null ? auth.getName() : "System";
emailService.sendAccountReactivationNotification(user, adminUsername);
} catch (Exception emailError) {
// Logga men låt inte e-post-fel stoppa reaktiveringen
System.err.println("Kunde inte skicka reaktiveringsnotifiering till " + user.getEmail() + ": " + emailError.getMessage());
}
return ResponseEntity.ok()
.body(Map.of(
"success", true,
"message", "Användare '" + user.getUsername() + "' har reaktiverats"
));
} catch (Exception e) {
return ResponseEntity.status(500)
.body(Map.of("success", false, "error", "Fel vid reaktivering: " + e.getMessage()));
}
}
/**
* ✅ UPPDATERAD: Snabb inaktivering via e-post med notifiering
*/
@PostMapping("/users/quick-deactivate")
@ResponseBody
public ResponseEntity<?> quickDeactivateByEmail(@RequestParam("email") String email, @RequestParam(required = false) String reason) {
try {
// Hitta användare via e-post
Optional<User> userOpt = userService.findByEmail(email);
if (!userOpt.isPresent()) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Användare med e-post " + email + " hittades inte"));
}
User user = userOpt.get();
String username = user.getUsername();
// Kontrollera att det inte är admin själv
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && username.equals(auth.getName())) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Du kan inte inaktivera dig själv"));
}
// Förhindra inaktivering av huvudadmin "fredrik"
if ("fredrik".equals(username)) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Huvudadmin kan inte inaktiveras"));
}
// Kontrollera om användaren redan är inaktiv
if (!user.isActive()) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Användare '" + username + "' är redan inaktiv"));
}
// Inaktivera användaren
user.setActive(false);
userService.save(user);
// Skicka e-postnotifiering till användaren
try {
String adminUsername = auth != null ? auth.getName() : "System";
emailService.sendAccountDeactivationNotification(user, adminUsername, reason);
} catch (Exception emailError) {
// Logga men låt inte e-post-fel stoppa inaktiveringen
System.err.println("Kunde inte skicka inaktiveringsnotifiering till " + user.getEmail() + ": " + emailError.getMessage());
}
return ResponseEntity.ok()
.body(Map.of(
"success", true,
"message", "Användare '" + username + "' (" + email + ") har inaktiverats"
));
} catch (Exception e) {
return ResponseEntity.status(500)
.body(Map.of("success", false, "error", "Fel vid inaktivering: " + e.getMessage()));
}
}
/**
* ✅ UPPDATERAD: Toggle active (för snabb växling) med e-postnotifiering
*/
@PostMapping("/users/toggle-active/{id}")
@ResponseBody
public ResponseEntity<?> toggleUserActive(@PathVariable Long id) {
try {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Användaren finns inte"));
}
String username = user.getUsername();
boolean wasActive = user.isActive();
// Förhindra att admin togglar sig själv
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && username.equals(auth.getName())) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Du kan inte ändra din egen status"));
}
// Förhindra inaktivering av huvudadmin "fredrik"
if ("fredrik".equals(username) && wasActive) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Huvudadmin kan inte inaktiveras"));
}
// Växla status
userService.toggleUserActive(id);
String action = wasActive ? "inaktiverad" : "aktiverad";
// Skicka e-postnotifiering
try {
String adminUsername = auth != null ? auth.getName() : "System";
if (wasActive) {
// Blev inaktiverad
emailService.sendAccountDeactivationNotification(user, adminUsername, null);
} else {
// Blev aktiverad
emailService.sendAccountReactivationNotification(user, adminUsername);
}
} catch (Exception emailError) {
// Logga men låt inte e-post-fel stoppa statusändringen
System.err.println("Kunde inte skicka statusändringsnotifiering till " + user.getEmail() + ": " + emailError.getMessage());
}
return ResponseEntity.ok()
.body(Map.of(
"success", true,
"message", "Användare '" + username + "' har " + action
));
} catch (Exception e) {
return ResponseEntity.status(500)
.body(Map.of("success", false, "error", "Fel vid statusändring: " + e.getMessage()));
}
}
/**
* ✅ UPPDATERAD: Skicka om verifieringsmail
*/
@PostMapping("/users/{id}/reset-verification")
@ResponseBody
public ResponseEntity<Map<String, Object>> resetUserVerification(@PathVariable Long id) {
Map<String, Object> response = new HashMap<>();
try {
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("Användare hittades inte"));
// Kontrollera om användaren redan är verifierad
if (user.isEmailVerified()) {
response.put("success", false);
response.put("message", "Användaren är redan verifierad");
return ResponseEntity.ok(response);
}
// Generera ny token
String token = UUID.randomUUID().toString();
user.setVerificationToken(token);
user.setVerificationTokenExpiry(LocalDateTime.now().plusHours(24));
userRepository.save(user);
// Skicka mail
boolean emailSent = emailService.sendVerificationEmail(user.getEmail(), token);
if (emailSent) {
response.put("success", true);
response.put("message", "Verifieringsmail har skickats om till " + user.getEmail());
} else {
response.put("success", false);
response.put("message", "Kunde inte skicka verifieringsmail. Kontrollera e-postkonfigurationen.");
}
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("message", "Fel: " + e.getMessage());
return ResponseEntity.status(500).body(response);
}
}
/**
* ✅ UPPDATERAD: Uppdatera email-adress (nu med kontroll av ALLA e-poster)
*/
@PostMapping("/users/update-email/{id}")
@ResponseBody
public ResponseEntity<?> updateUserEmail(@PathVariable Long id,
@RequestParam String email) {
try {
// Validera email format
if (!email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Ogiltigt email-format"));
}
// Uppdatera användare
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Användaren finns inte"));
}
String oldEmail = user.getEmail();
// Kolla om det är samma e-post (tillåt)
if (email.equals(oldEmail)) {
return ResponseEntity.ok()
.body(Map.of(
"success", true,
"message", "E-post är redan " + email + " (ingen ändring)"
));
}
// ✅ UPPDATERAD: Kolla om email redan används (inkluderar inaktiva användare)
if (userService.existsByEmailIncludingInactive(email)) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "error", "Email-adressen används redan (även av inaktiva användare)"));
}
user.setEmail(email);
userService.save(user);
return ResponseEntity.ok()
.body(Map.of(
"success", true,
"message", "Email uppdaterad från " + oldEmail + " till " + email
));
} catch (Exception e) {
return ResponseEntity.status(500)
.body(Map.of("success", false, "error", "Fel vid uppdatering: " + e.getMessage()));
}
}
/**
* ✅ UPPDATERAD: Permanent borttagning av användare (form-baserad) med e-postnotifiering
*/
@PostMapping("/users/{id}/delete-permanently")
@PreAuthorize("hasRole('ADMIN')")
public String deletePermanently(@PathVariable Long id, @RequestParam(required = false) String reason, RedirectAttributes redirectAttributes) {
try {
User user = userService.findById(id);
if (user == null) {
redirectAttributes.addFlashAttribute("error", "Användare hittades inte");
return "redirect:/admin/users?filter=inactive";
}
String userInfo = user.getUsername() + " (" + user.getEmail() + ")";
// Skicka e-postnotifiering INNAN borttagning
try {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String adminUsername = auth != null ? auth.getName() : "System";
emailService.sendAccountDeletionNotification(user, adminUsername, reason);
} catch (Exception emailError) {
// Logga men fortsätt med borttagning
System.err.println("Kunde inte skicka borttagningsnotifiering till " + user.getEmail() + ": " + emailError.getMessage());
}
// Anropa uppdaterade metoden (kastar nu exceptions)
userService.deletePermanently(id);
redirectAttributes.addFlashAttribute("success",
"Användare " + userInfo + " har tagits bort permanent från databasen");
} catch (RuntimeException e) {
// Business logic errors (t.ex. "Admin kan inte tas bort")
redirectAttributes.addFlashAttribute("error", e.getMessage());
} catch (Exception e) {
// Tekniska fel
redirectAttributes.addFlashAttribute("error",
"Tekniskt fel vid permanent borttagning: " + e.getMessage());
}
return "redirect:/admin/users?filter=inactive";
}
/**
* ✅ UPPDATERAD: Permanent borttagning av användare (AJAX-baserad) med e-postnotifiering
* Hanterar nu exceptions från UserService
*/
@PostMapping("/users/{id}/delete-permanently-ajax")
@PreAuthorize("hasRole('ADMIN')")
@ResponseBody
public ResponseEntity<?> deletePermanentlyAjax(@PathVariable Long id, @RequestParam(required = false) String reason) {
try {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.badRequest()
.body(Map.of("success", false, "message", "Användare hittades inte"));
}
String userInfo = user.getUsername() + " (" + user.getEmail() + ")";
// Skicka e-postnotifiering INNAN borttagning
try {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String adminUsername = auth != null ? auth.getName() : "System";
emailService.sendAccountDeletionNotification(user, adminUsername, reason);
} catch (Exception emailError) {
// Logga men fortsätt med borttagning
System.err.println("Kunde inte skicka borttagningsnotifiering till " + user.getEmail() + ": " + emailError.getMessage());
}
// Anropa uppdaterade metoden (kastar nu exceptions istället för att returnera boolean)
userService.deletePermanently(id);
return ResponseEntity.ok(Map.of(
"success", true,
"message", "Användare " + userInfo + " har tagits bort permanent från databasen"
));
} catch (RuntimeException e) {
// Business logic errors (t.ex. "Admin kan inte tas bort", "Endast inaktiva användare")
return ResponseEntity.badRequest()
.body(Map.of("success", false, "message", e.getMessage()));
} catch (Exception e) {
// Tekniska fel (databas, foreign keys, etc.)
return ResponseEntity.status(500)
.body(Map.of("success", false, "message", "Tekniskt fel vid borttagning: " + e.getMessage()));
}
}
}