chatwoot admin panel fixes
All checks were successful
Deploy to VPS / deploy (push) Successful in 1m16s
All checks were successful
Deploy to VPS / deploy (push) Successful in 1m16s
This commit is contained in:
@@ -104,13 +104,13 @@ public class AdminSecurityConfig {
|
||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/api/admin/login", "/api/admin/chatwoot-session").permitAll()
|
||||
.requestMatchers("/api/admin/users/**").hasAnyRole("ADMIN", "GAME_ADMIN", "TICKETS_SUPPORT")
|
||||
.requestMatchers("/api/admin/payments/**").hasAnyRole("ADMIN", "GAME_ADMIN")
|
||||
.requestMatchers("/api/admin/payouts/**").hasAnyRole("ADMIN", "PAYOUT_SUPPORT", "GAME_ADMIN")
|
||||
.requestMatchers("/api/admin/rooms/**").hasAnyRole("ADMIN", "GAME_ADMIN")
|
||||
.requestMatchers("/api/admin/configurations/**").hasAnyRole("ADMIN", "GAME_ADMIN")
|
||||
.requestMatchers("/api/admin/tickets/**").hasAnyRole("ADMIN", "TICKETS_SUPPORT", "GAME_ADMIN")
|
||||
.requestMatchers("/api/admin/quick-answers/**").hasAnyRole("ADMIN", "TICKETS_SUPPORT", "GAME_ADMIN")
|
||||
.requestMatchers("/api/admin/users/**").hasAnyRole("ADMIN", "SUPPORT")
|
||||
.requestMatchers("/api/admin/payments/**").hasAnyRole("ADMIN", "SUPPORT")
|
||||
.requestMatchers("/api/admin/payouts/**").hasAnyRole("ADMIN", "SUPPORT")
|
||||
.requestMatchers("/api/admin/rooms/**").hasAnyRole("ADMIN")
|
||||
.requestMatchers("/api/admin/configurations/**").hasAnyRole("ADMIN")
|
||||
.requestMatchers("/api/admin/tickets/**").hasAnyRole("ADMIN")
|
||||
.requestMatchers("/api/admin/quick-answers/**").hasAnyRole("ADMIN")
|
||||
.requestMatchers("/api/admin/**").hasRole("ADMIN")
|
||||
.anyRequest().denyAll()
|
||||
)
|
||||
|
||||
@@ -57,7 +57,7 @@ public class AdminLoginController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchanges a Chatwoot integration API key for an admin JWT with ROLE_TICKETS_SUPPORT.
|
||||
* Exchanges a Chatwoot integration API key for an admin JWT with ROLE_SUPPORT.
|
||||
* Used when the admin panel is embedded in Chatwoot as a Dashboard App iframe.
|
||||
* API key must match CHATWOOT_INTEGRATION_SECRET on the server.
|
||||
*/
|
||||
@@ -73,11 +73,11 @@ public class AdminLoginController {
|
||||
if (!chatwootIntegrationSecret.equals(request.getApiKey().trim())) {
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid apiKey");
|
||||
}
|
||||
String token = jwtUtil.generateTokenWithRole("__chatwoot__", "ROLE_TICKETS_SUPPORT");
|
||||
String token = jwtUtil.generateTokenWithRole("__chatwoot__", "ROLE_SUPPORT");
|
||||
return ResponseEntity.ok(new AdminLoginResponse(
|
||||
token,
|
||||
"__chatwoot__",
|
||||
"ROLE_TICKETS_SUPPORT"
|
||||
"ROLE_SUPPORT"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import com.honey.honey.repository.PaymentRepository;
|
||||
import com.honey.honey.repository.UserARepository;
|
||||
import com.honey.honey.repository.UserDRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -16,6 +14,8 @@ import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
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.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
@@ -31,18 +31,18 @@ import java.util.stream.Collectors;
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/payments")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public class AdminPaymentController {
|
||||
|
||||
private final PaymentRepository paymentRepository;
|
||||
private final UserARepository userARepository;
|
||||
private final UserDRepository userDRepository;
|
||||
|
||||
private boolean isGameAdmin() {
|
||||
private static boolean isSupport() {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (auth == null || auth.getAuthorities() == null) return false;
|
||||
return auth.getAuthorities().stream()
|
||||
.anyMatch(a -> "ROLE_GAME_ADMIN".equals(a.getAuthority()));
|
||||
.anyMatch(a -> "ROLE_SUPPORT".equals(a.getAuthority()));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@@ -70,7 +70,7 @@ public class AdminPaymentController {
|
||||
|
||||
Pageable pageable = PageRequest.of(page, size, sort);
|
||||
|
||||
List<Integer> masterIds = isGameAdmin() ? userDRepository.findMasterUserIds() : List.of();
|
||||
List<Integer> masterIds = isSupport() ? userDRepository.findMasterUserIds() : List.of();
|
||||
|
||||
// Build specification
|
||||
Specification<Payment> spec = (root, query, cb) -> {
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.util.stream.Collectors;
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/payouts")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'PAYOUT_SUPPORT', 'GAME_ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public class AdminPayoutController {
|
||||
|
||||
private final PayoutRepository payoutRepository;
|
||||
@@ -45,11 +45,11 @@ public class AdminPayoutController {
|
||||
private final PayoutService payoutService;
|
||||
private final LocalizationService localizationService;
|
||||
|
||||
private boolean isGameAdmin() {
|
||||
private static boolean isSupport() {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (auth == null || auth.getAuthorities() == null) return false;
|
||||
return auth.getAuthorities().stream()
|
||||
.anyMatch(a -> "ROLE_GAME_ADMIN".equals(a.getAuthority()));
|
||||
.anyMatch(a -> "ROLE_SUPPORT".equals(a.getAuthority()));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@@ -74,7 +74,7 @@ public class AdminPayoutController {
|
||||
|
||||
Pageable pageable = PageRequest.of(page, size, sort);
|
||||
|
||||
List<Integer> masterIds = isGameAdmin() ? userDRepository.findMasterUserIds() : List.of();
|
||||
List<Integer> masterIds = isSupport() ? userDRepository.findMasterUserIds() : List.of();
|
||||
|
||||
// Build specification
|
||||
Specification<Payout> spec = (root, query, cb) -> {
|
||||
@@ -156,7 +156,7 @@ public class AdminPayoutController {
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/complete")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'PAYOUT_SUPPORT')")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<?> completePayout(@PathVariable Long id) {
|
||||
try {
|
||||
Optional<Payout> payoutOpt = payoutRepository.findById(id);
|
||||
@@ -179,7 +179,7 @@ public class AdminPayoutController {
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/cancel")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'PAYOUT_SUPPORT')")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
public ResponseEntity<?> cancelPayout(@PathVariable Long id) {
|
||||
try {
|
||||
Optional<Payout> payoutOpt = payoutRepository.findById(id);
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.util.stream.Collectors;
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/tickets")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'TICKETS_SUPPORT', 'GAME_ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN')")
|
||||
public class AdminSupportTicketController {
|
||||
|
||||
private final SupportTicketRepository supportTicketRepository;
|
||||
|
||||
@@ -25,6 +25,13 @@ import java.util.Set;
|
||||
@RequiredArgsConstructor
|
||||
public class AdminUserController {
|
||||
|
||||
private static boolean isSupport() {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (auth == null || auth.getAuthorities() == null) return false;
|
||||
return auth.getAuthorities().stream()
|
||||
.anyMatch(a -> "ROLE_SUPPORT".equals(a.getAuthority()));
|
||||
}
|
||||
|
||||
/** Sortable fields: UserA properties plus UserB/UserD (handled via custom query in service). */
|
||||
private static final Set<String> SORTABLE_FIELDS = Set.of(
|
||||
"id", "screenName", "telegramId", "telegramName", "isPremium",
|
||||
@@ -37,15 +44,8 @@ public class AdminUserController {
|
||||
private final AdminUserService adminUserService;
|
||||
private final UserService userService;
|
||||
|
||||
private boolean isGameAdmin() {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (auth == null || auth.getAuthorities() == null) return false;
|
||||
return auth.getAuthorities().stream()
|
||||
.anyMatch(a -> "ROLE_GAME_ADMIN".equals(a.getAuthority()));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public ResponseEntity<Map<String, Object>> getUsers(
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@RequestParam(defaultValue = "50") int size,
|
||||
@@ -85,7 +85,7 @@ public class AdminUserController {
|
||||
Long balanceMinBigint = balanceMin != null ? balanceMin * 1000000L : null;
|
||||
Long balanceMaxBigint = balanceMax != null ? balanceMax * 1000000L : null;
|
||||
|
||||
boolean excludeMasters = isGameAdmin();
|
||||
boolean excludeMasters = isSupport();
|
||||
Page<AdminUserDto> dtoPage = adminUserService.getUsers(
|
||||
pageable,
|
||||
search,
|
||||
@@ -124,7 +124,7 @@ public class AdminUserController {
|
||||
* Chatwoot (Telegram channel) often uses Telegram user ID as contact identifier.
|
||||
*/
|
||||
@GetMapping("/by-telegram-id")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN', 'TICKETS_SUPPORT')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public ResponseEntity<Map<String, Integer>> getUserByTelegramId(@RequestParam("telegram_id") Long telegramId) {
|
||||
if (telegramId == null) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
@@ -135,9 +135,9 @@ public class AdminUserController {
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN', 'TICKETS_SUPPORT')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public ResponseEntity<AdminUserDetailDto> getUserDetail(@PathVariable Integer id) {
|
||||
AdminUserDetailDto userDetail = adminUserService.getUserDetail(id, isGameAdmin());
|
||||
AdminUserDetailDto userDetail = adminUserService.getUserDetail(id, isSupport());
|
||||
if (userDetail == null) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
@@ -145,7 +145,7 @@ public class AdminUserController {
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/transactions")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public ResponseEntity<Map<String, Object>> getUserTransactions(
|
||||
@PathVariable Integer id,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@@ -167,7 +167,7 @@ public class AdminUserController {
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/payments")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public ResponseEntity<Map<String, Object>> getUserPayments(
|
||||
@PathVariable Integer id,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@@ -190,7 +190,7 @@ public class AdminUserController {
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/payouts")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public ResponseEntity<Map<String, Object>> getUserPayouts(
|
||||
@PathVariable Integer id,
|
||||
@RequestParam(defaultValue = "0") int page,
|
||||
@@ -213,14 +213,14 @@ public class AdminUserController {
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/tasks")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public ResponseEntity<Map<String, Object>> getUserTasks(@PathVariable Integer id) {
|
||||
Map<String, Object> tasks = adminUserService.getUserTasks(id);
|
||||
return ResponseEntity.ok(tasks);
|
||||
}
|
||||
|
||||
@PatchMapping("/{id}/ban")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public ResponseEntity<?> setUserBanned(
|
||||
@PathVariable Integer id,
|
||||
@RequestBody Map<String, Boolean> body) {
|
||||
@@ -237,7 +237,7 @@ public class AdminUserController {
|
||||
}
|
||||
|
||||
@PatchMapping("/{id}/withdrawals-enabled")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'GAME_ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'SUPPORT')")
|
||||
public ResponseEntity<?> setWithdrawalsEnabled(
|
||||
@PathVariable Integer id,
|
||||
@RequestBody Map<String, Boolean> body) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.stream.Collectors;
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/quick-answers")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasAnyRole('ADMIN', 'TICKETS_SUPPORT', 'GAME_ADMIN')")
|
||||
@PreAuthorize("hasAnyRole('ADMIN')")
|
||||
public class QuickAnswerController {
|
||||
|
||||
private final QuickAnswerRepository quickAnswerRepository;
|
||||
|
||||
@@ -88,7 +88,7 @@ public interface UserDRepository extends JpaRepository<UserD, Integer> {
|
||||
List<UserD> findAllMasters();
|
||||
|
||||
/**
|
||||
* IDs of users who are Masters (id = master_id and master_id > 0). Used to exclude them from GAME_ADMIN views.
|
||||
* IDs of users who are Masters (id = master_id and master_id > 0).
|
||||
*/
|
||||
@Query("SELECT d.id FROM UserD d WHERE d.id = d.masterId AND d.masterId > 0")
|
||||
List<Integer> findMasterUserIds();
|
||||
|
||||
Reference in New Issue
Block a user