diff --git a/src/main/angular/src/app/api/User/UserPrivate.ts b/src/main/angular/src/app/api/User/UserPrivate.ts index 33cc9f7..6322a82 100644 --- a/src/main/angular/src/app/api/User/UserPrivate.ts +++ b/src/main/angular/src/app/api/User/UserPrivate.ts @@ -7,6 +7,7 @@ export class UserPrivate extends UserPublic { readonly privateUuid: string, publicUuid: string, readonly created: Date, + readonly lastAccess: Date, name: string, readonly password: boolean, readonly email: string, @@ -21,6 +22,7 @@ export class UserPrivate extends UserPublic { validateString(json['privateUuid']), validateString(json['publicUuid']), validateDate(json['created']), + validateDate(json['lastAccess']), validateString(json['name']), validateBoolean(json['password']), validateString(json['email']), diff --git a/src/main/angular/src/app/api/User/user.service.ts b/src/main/angular/src/app/api/User/user.service.ts index ad75509..5b1087b 100644 --- a/src/main/angular/src/app/api/User/user.service.ts +++ b/src/main/angular/src/app/api/User/user.service.ts @@ -13,6 +13,7 @@ import {GroupLeftEvent} from "../group/events/GroupLeftEvent"; import {UserLogoutEvent} from "./events/UserLogoutEvent"; import {validateBoolean} from "../common/validators"; import {UserLoginRequest} from "./requests/UserLoginRequest"; +import {Page} from "../common/Page"; function userPushMessageFromJson(json: any): object { const type = json['_type_']; @@ -143,4 +144,14 @@ export class UserService { this.router.navigate(['Profile']); } + adminPage(pageIndex: number, pageSize: number, next: Next>) { + this.api.postPage(['User', 'Admin', 'page'], {pageIndex: pageIndex, pageSize: pageSize, orders: [{property: 'lastAccess', direction: 'ASC'}]}, UserPrivate.fromJson, next); + } + + adminDelete(user: UserPrivate, next?: Next) { + if (confirm("Benutzer \"" + user.name + "\" wirklich löschen?")) { + this.api.postNone(['User', 'Admin', 'delete'], user.privateUuid, next); + } + } + } diff --git a/src/main/angular/src/app/app.routes.ts b/src/main/angular/src/app/app.routes.ts index 16d5ff8..e35d65a 100644 --- a/src/main/angular/src/app/app.routes.ts +++ b/src/main/angular/src/app/app.routes.ts @@ -9,6 +9,7 @@ import {GroupComponent} from "./pages/group/group/group.component"; import {NumbersComponent} from "./pages/tools/numbers/numbers.component"; import {EmailConfirmationComponent} from "./pages/email-confirmation/email-confirmation.component"; import {LoginComponent} from "./pages/login/login.component"; +import {AdminComponent} from "./pages/admin/admin.component"; export const routes: Routes = [ {path: 'SolarSystemPrintout', component: SolarSystemPrintoutComponent}, @@ -27,6 +28,8 @@ export const routes: Routes = [ {path: 'Profile', component: ProfileComponent}, {path: 'emailConfirmation/:emailConfirmation', component: EmailConfirmationComponent}, + {path: 'Admin', component: AdminComponent}, + // fallback {path: '**', redirectTo: '/SolarSystem'}, ]; diff --git a/src/main/angular/src/app/pages/admin/admin.component.html b/src/main/angular/src/app/pages/admin/admin.component.html new file mode 100644 index 0000000..f8a28b2 --- /dev/null +++ b/src/main/angular/src/app/pages/admin/admin.component.html @@ -0,0 +1,27 @@ +
+
+
+
+ Benutzer +
+
+ + + + + + + + + + + + +
BenutzernameZuletztAktion
{{ user.name }}{{ user.lastAccess | relative:now }} +
Löschen
+
+ +
+
+
+
diff --git a/src/main/angular/src/app/pages/admin/admin.component.less b/src/main/angular/src/app/pages/admin/admin.component.less new file mode 100644 index 0000000..e69de29 diff --git a/src/main/angular/src/app/pages/admin/admin.component.ts b/src/main/angular/src/app/pages/admin/admin.component.ts new file mode 100644 index 0000000..dcdca7e --- /dev/null +++ b/src/main/angular/src/app/pages/admin/admin.component.ts @@ -0,0 +1,42 @@ +import {Component, OnInit} from '@angular/core'; +import {NgForOf} from "@angular/common"; +import {Page} from "../../api/common/Page"; +import {PaginationComponent} from "../../shared/pagination/pagination.component"; +import {UserService} from "../../api/User/user.service"; +import {UserPrivate} from "../../api/User/UserPrivate"; +import {RelativePipe} from "../../shared/relative.pipe"; + +@Component({ + selector: 'app-admin', + standalone: true, + imports: [ + NgForOf, + PaginationComponent, + RelativePipe + ], + templateUrl: './admin.component.html', + styleUrl: './admin.component.less' +}) +export class AdminComponent implements OnInit { + + private readonly PER_PAGE: number = 20; + + protected page: Page = Page.EMPTY; + + protected now: Date = new Date(); + + constructor( + protected readonly userService: UserService, + ) { + // - + } + + ngOnInit(): void { + this.goto(0); + } + + goto(pageIndex: number) { + this.userService.adminPage(pageIndex, this.PER_PAGE, page => this.page = page); + } + +} diff --git a/src/main/angular/src/app/shared/pagination/pagination.component.html b/src/main/angular/src/app/shared/pagination/pagination.component.html new file mode 100644 index 0000000..9806bed --- /dev/null +++ b/src/main/angular/src/app/shared/pagination/pagination.component.html @@ -0,0 +1,5 @@ + diff --git a/src/main/angular/src/app/shared/pagination/pagination.component.less b/src/main/angular/src/app/shared/pagination/pagination.component.less new file mode 100644 index 0000000..3f718a2 --- /dev/null +++ b/src/main/angular/src/app/shared/pagination/pagination.component.less @@ -0,0 +1,16 @@ +@import "../../../styles/config"; + +.pagination { + + .number { + float: left; + padding: @halfSpace; + margin: @halfSpace; + text-align: center; + } + + .currentPage { + color: dodgerblue; + } + +} diff --git a/src/main/angular/src/app/shared/pagination/pagination.component.ts b/src/main/angular/src/app/shared/pagination/pagination.component.ts new file mode 100644 index 0000000..294505b --- /dev/null +++ b/src/main/angular/src/app/shared/pagination/pagination.component.ts @@ -0,0 +1,34 @@ +import {Component, EventEmitter, Input, Output} from '@angular/core'; +import {Page} from '../../api/common/Page'; +import {NgForOf, NgIf} from "@angular/common"; + +@Component({ + selector: 'app-pagination', + standalone: true, + imports: [ + NgForOf, + NgIf + ], + templateUrl: './pagination.component.html', + styleUrl: './pagination.component.less' +}) +export class PaginationComponent { + + _page: Page = Page.EMPTY; + + protected numbers: number[] = []; + + @Output() + readonly goto: EventEmitter = new EventEmitter(); + + @Input() + set page(page: Page) { + this._page = page; + this.numbers = [...Array(this.page.totalPages).keys()]; + } + + get page(): Page { + return this._page; + } + +} diff --git a/src/main/java/de/ph87/tools/email/EmailRepository.java b/src/main/java/de/ph87/tools/email/EmailRepository.java index 3d2c708..292c504 100644 --- a/src/main/java/de/ph87/tools/email/EmailRepository.java +++ b/src/main/java/de/ph87/tools/email/EmailRepository.java @@ -1,5 +1,6 @@ package de.ph87.tools.email; +import de.ph87.tools.user.User; import lombok.NonNull; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -8,6 +9,7 @@ import org.springframework.data.repository.ListCrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; import java.time.ZonedDateTime; +import java.util.List; public interface EmailRepository extends ListCrudRepository, PagingAndSortingRepository { @@ -15,4 +17,7 @@ public interface EmailRepository extends ListCrudRepository, Pagi @Query("select e from Email e where e.sent is null and (e.tried is null or e.tried <= :earliest) and e.tries < 5") Page findNextToSend(@NonNull final ZonedDateTime earliest, @NonNull Pageable pageable); + @NonNull + List findAllByUser(@NonNull User user); + } diff --git a/src/main/java/de/ph87/tools/email/EmailService.java b/src/main/java/de/ph87/tools/email/EmailService.java index 2cfd12a..0263ea7 100644 --- a/src/main/java/de/ph87/tools/email/EmailService.java +++ b/src/main/java/de/ph87/tools/email/EmailService.java @@ -80,4 +80,11 @@ public class EmailService { .replaceAll("%EMAIL_CONFIRMATION%", user.getEmailConfirmation()); } + public void deleteAllByUser(@NonNull final User user) { + emailRepository.findAllByUser(user).forEach(email -> { + emailRepository.delete(email); + log.info("Email deleted: {}", email); + }); + } + } diff --git a/src/main/java/de/ph87/tools/group/Group.java b/src/main/java/de/ph87/tools/group/Group.java index 25c34d1..92cfaf7 100644 --- a/src/main/java/de/ph87/tools/group/Group.java +++ b/src/main/java/de/ph87/tools/group/Group.java @@ -27,6 +27,7 @@ public class Group extends GroupAbstract implements IWebSocketMessage { @Transient @ToString.Exclude + @Getter(AccessLevel.NONE) private GroupUuid _uuid; @NonNull diff --git a/src/main/java/de/ph87/tools/group/GroupMapper.java b/src/main/java/de/ph87/tools/group/GroupMapper.java index 4626368..ab4c2df 100644 --- a/src/main/java/de/ph87/tools/group/GroupMapper.java +++ b/src/main/java/de/ph87/tools/group/GroupMapper.java @@ -1,6 +1,7 @@ package de.ph87.tools.group; import de.ph87.tools.group.dto.GroupDto; +import de.ph87.tools.user.User; import de.ph87.tools.user.UserPublicDto; import de.ph87.tools.user.push.UserPushService; import lombok.NonNull; @@ -20,6 +21,8 @@ public class GroupMapper { private final UserPushService userPushService; + private final GroupRepository groupRepository; + @NonNull public GroupDto toDto(@NonNull final Group group) { final UserPublicDto owner = new UserPublicDto(group.getOwner()); @@ -35,4 +38,8 @@ public class GroupMapper { return dto; } + public void pushAllByUser(@NonNull final User user) { + groupRepository.findAllByUsersContains(user).forEach(this::push); + } + } diff --git a/src/main/java/de/ph87/tools/group/GroupRepository.java b/src/main/java/de/ph87/tools/group/GroupRepository.java index 39c6ce0..f3752e0 100644 --- a/src/main/java/de/ph87/tools/group/GroupRepository.java +++ b/src/main/java/de/ph87/tools/group/GroupRepository.java @@ -4,6 +4,7 @@ import de.ph87.tools.user.User; import lombok.NonNull; import org.springframework.data.repository.ListCrudRepository; +import java.util.List; import java.util.Optional; import java.util.Set; @@ -12,6 +13,9 @@ public interface GroupRepository extends ListCrudRepository { @NonNull Optional findByUuid(@NonNull String uuid); + @NonNull + List findAllByOwner(@NonNull User user); + @NonNull Set findAllByUsersContains(@NonNull User user); diff --git a/src/main/java/de/ph87/tools/group/member/GroupMemberService.java b/src/main/java/de/ph87/tools/group/member/GroupMemberService.java index c742f63..1201435 100644 --- a/src/main/java/de/ph87/tools/group/member/GroupMemberService.java +++ b/src/main/java/de/ph87/tools/group/member/GroupMemberService.java @@ -22,8 +22,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ResponseStatusException; -import java.util.function.BiConsumer; - @Slf4j @Service @Transactional @@ -62,7 +60,7 @@ public class GroupMemberService { // owner cannot remove itself from group throw new ResponseStatusException(HttpStatus.BAD_REQUEST); } - _remove_user_from_group_unchecked(group, user, (g, u) -> log.info("User left Group: user={}, group={}", u, g)); + _remove_user_from_group_unchecked(group, user); } @NonNull @@ -74,11 +72,11 @@ public class GroupMemberService { return groupMapper.push(group); } - public void _remove_user_from_group_unchecked(@NonNull final Group group, @NonNull final User user, @NonNull final BiConsumer beforePush) { + public void _remove_user_from_group_unchecked(@NonNull final Group group, @NonNull final User user) { group.getUsers().remove(user); group.touch(); user.touch(); - beforePush.accept(group, user); + log.info("User left Group: user={}, group={}", user, group); userPushService.push(user, new GroupLeftEvent(group)); groupMapper.push(group); } diff --git a/src/main/java/de/ph87/tools/group/owner/GroupOwnerService.java b/src/main/java/de/ph87/tools/group/owner/GroupOwnerService.java index 88e0c84..6303f99 100644 --- a/src/main/java/de/ph87/tools/group/owner/GroupOwnerService.java +++ b/src/main/java/de/ph87/tools/group/owner/GroupOwnerService.java @@ -29,8 +29,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ResponseStatusException; -import java.util.function.BiConsumer; - @Slf4j @Service @Transactional @@ -69,12 +67,12 @@ public class GroupOwnerService { } public void kick(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUserRequest request) { - final OwnerRemoveResult result = _removeUser(privateUuid, request, (group, user) -> log.info("User kicked out of group: user={}, group={}", user, group)); + final OwnerRemoveResult result = _removeUser(privateUuid, request); groupMapper.push(result.group); } public void ban(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUserRequest request) { - final OwnerRemoveResult result = _removeUser(privateUuid, request, (group, user) -> log.info("User banned from group: user={}, group={}", user, group)); + final OwnerRemoveResult result = _removeUser(privateUuid, request); result.group.getBanned().add(result.kicked); groupMapper.push(result.group); } @@ -114,14 +112,14 @@ public class GroupOwnerService { } @NonNull - private OwnerRemoveResult _removeUser(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUserRequest request, @NonNull final BiConsumer beforePush) { + private OwnerRemoveResult _removeUser(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUserRequest request) { final GroupAccess access = groupAccessService.ownerAccess(privateUuid, request.groupUuid); final User user = access.group.getUsers().stream().filter(u -> u.getPublicUuid().equals(request.userPublicUuid)).findFirst().orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); if (user.equals(access.principal)) { // owner cannot kick itself from group throw new ResponseStatusException(HttpStatus.BAD_REQUEST); } - groupMemberService._remove_user_from_group_unchecked(access.group, user, beforePush); + groupMemberService._remove_user_from_group_unchecked(access.group, user); return new OwnerRemoveResult(access, user); } diff --git a/src/main/java/de/ph87/tools/tools/numbers/Numbers.java b/src/main/java/de/ph87/tools/tools/numbers/Numbers.java index 59a48ac..b6b5536 100644 --- a/src/main/java/de/ph87/tools/tools/numbers/Numbers.java +++ b/src/main/java/de/ph87/tools/tools/numbers/Numbers.java @@ -25,6 +25,7 @@ public class Numbers extends NumbersAbstract { @Transient @ToString.Exclude + @Getter(AccessLevel.NONE) private NumbersUuid _uuid; @NonNull diff --git a/src/main/java/de/ph87/tools/tools/numbers/NumbersRepository.java b/src/main/java/de/ph87/tools/tools/numbers/NumbersRepository.java index 06ec9df..0b45da9 100644 --- a/src/main/java/de/ph87/tools/tools/numbers/NumbersRepository.java +++ b/src/main/java/de/ph87/tools/tools/numbers/NumbersRepository.java @@ -1,11 +1,14 @@ package de.ph87.tools.tools.numbers; import de.ph87.tools.group.Group; +import de.ph87.tools.user.User; import lombok.NonNull; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.ListCrudRepository; +import java.util.List; + public interface NumbersRepository extends ListCrudRepository { @NonNull @@ -13,4 +16,7 @@ public interface NumbersRepository extends ListCrudRepository { void deleteAllByGroup(@NonNull Group group); + @NonNull + List findAllByLotsUser(@NonNull User user); + } diff --git a/src/main/java/de/ph87/tools/user/User.java b/src/main/java/de/ph87/tools/user/User.java index c9a431f..8be1ba8 100644 --- a/src/main/java/de/ph87/tools/user/User.java +++ b/src/main/java/de/ph87/tools/user/User.java @@ -24,6 +24,7 @@ public class User extends UserPublicAbstract { @Transient @ToString.Exclude + @Getter(AccessLevel.NONE) private UserPublicUuid _publicUuid; @NonNull @@ -41,6 +42,7 @@ public class User extends UserPublicAbstract { @Transient @ToString.Exclude + @Getter(AccessLevel.NONE) private UserPrivateUuid _privateUuid; @NonNull diff --git a/src/main/java/de/ph87/tools/user/UserPrivateDto.java b/src/main/java/de/ph87/tools/user/UserPrivateDto.java index e2b5b12..1f053af 100644 --- a/src/main/java/de/ph87/tools/user/UserPrivateDto.java +++ b/src/main/java/de/ph87/tools/user/UserPrivateDto.java @@ -35,6 +35,9 @@ public class UserPrivateDto extends UserPublicAbstract { @NonNull public final ZonedDateTime created; + @NonNull + public final ZonedDateTime lastAccess; + private final boolean password; private final String email; @@ -48,6 +51,7 @@ public class UserPrivateDto extends UserPublicAbstract { this.privateUuid = user.getPrivateUuid(); this.name = user.getName(); this.created = user.getCreated(); + this.lastAccess = user.getLastAccess(); this.password = !user.getPassword().isEmpty(); this.email = obfuscateEmail(user.getEmail()); this.emailConfirmed = user.isEmailConfirmed(); diff --git a/src/main/java/de/ph87/tools/user/UserRepository.java b/src/main/java/de/ph87/tools/user/UserRepository.java index 05ff2ff..f99b6c2 100644 --- a/src/main/java/de/ph87/tools/user/UserRepository.java +++ b/src/main/java/de/ph87/tools/user/UserRepository.java @@ -2,10 +2,11 @@ package de.ph87.tools.user; import lombok.NonNull; import org.springframework.data.repository.ListCrudRepository; +import org.springframework.data.repository.PagingAndSortingRepository; import java.util.Optional; -public interface UserRepository extends ListCrudRepository { +public interface UserRepository extends ListCrudRepository, PagingAndSortingRepository { boolean existsByName(@NonNull String name); diff --git a/src/main/java/de/ph87/tools/user/UserService.java b/src/main/java/de/ph87/tools/user/UserService.java index 8c4708b..1df09da 100644 --- a/src/main/java/de/ph87/tools/user/UserService.java +++ b/src/main/java/de/ph87/tools/user/UserService.java @@ -3,7 +3,6 @@ package de.ph87.tools.user; import de.ph87.tools.common.uuid.AbstractUuid; import de.ph87.tools.email.EmailService; import de.ph87.tools.group.GroupMapper; -import de.ph87.tools.group.GroupRepository; import de.ph87.tools.user.push.UserPushService; import de.ph87.tools.user.requests.UserLoginRequest; import de.ph87.tools.user.uuid.UserPrivateUuid; @@ -50,8 +49,6 @@ public class UserService { private final PasswordEncoder passwordEncoder; - private final GroupRepository groupRepository; - private final UserAccessService userAccessService; private final GroupMapper groupMapper; @@ -133,7 +130,7 @@ public class UserService { } user.setName(name); log.info("User name changed: user={}", user); - groupRepository.findAllByUsersContains(user).forEach(groupMapper::push); + groupMapper.pushAllByUser(user); }); } diff --git a/src/main/java/de/ph87/tools/user/admin/UserAdminController.java b/src/main/java/de/ph87/tools/user/admin/UserAdminController.java new file mode 100644 index 0000000..91a74ed --- /dev/null +++ b/src/main/java/de/ph87/tools/user/admin/UserAdminController.java @@ -0,0 +1,31 @@ +package de.ph87.tools.user.admin; + +import de.ph87.tools.user.UserPrivateDto; +import de.ph87.tools.user.uuid.UserPrivateUuid; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@CrossOrigin +@RestController +@RequiredArgsConstructor +@RequestMapping("User/Admin") +public class UserAdminController { + + private final UserAdminService userAdminService; + + @NonNull + @PostMapping("page") + public Page page(@NonNull final UserPrivateUuid privateUuid, @NonNull @RequestBody final UserAdminPageRequest request) { + return userAdminService.page(privateUuid, request); + } + + @PostMapping("delete") + public void delete(@NonNull final UserPrivateUuid privateUuid, @RequestBody @NonNull final String deletePrivateUuid) { // TODO we cannot use UserPrivateUuid for 'deletePrivateUuid' because it would be filled with cookie's UserPrivateUuid + userAdminService.delete(privateUuid, new UserPrivateUuid(deletePrivateUuid)); + } + +} diff --git a/src/main/java/de/ph87/tools/user/admin/UserAdminPageRequest.java b/src/main/java/de/ph87/tools/user/admin/UserAdminPageRequest.java new file mode 100644 index 0000000..76504d9 --- /dev/null +++ b/src/main/java/de/ph87/tools/user/admin/UserAdminPageRequest.java @@ -0,0 +1,48 @@ +package de.ph87.tools.user.admin; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +import java.util.List; + +@Getter +@ToString +@AllArgsConstructor +public class UserAdminPageRequest { + + public final int pageIndex; + + public final int pageSize; + + @NonNull + public final List orders; + + @NonNull + public Pageable getPageable() { + return PageRequest.of(pageIndex, pageSize, Sort.by(orders.stream().map(Order::toSortOrder).toList())); + } + + @Getter + @ToString + @AllArgsConstructor + public static class Order { + + @NonNull + public final String property; + + @NonNull + public final Sort.Direction direction; + + @NonNull + public Sort.Order toSortOrder() { + return new Sort.Order(direction, property); + } + + } + +} diff --git a/src/main/java/de/ph87/tools/user/admin/UserAdminService.java b/src/main/java/de/ph87/tools/user/admin/UserAdminService.java new file mode 100644 index 0000000..4395e40 --- /dev/null +++ b/src/main/java/de/ph87/tools/user/admin/UserAdminService.java @@ -0,0 +1,75 @@ +package de.ph87.tools.user.admin; + +import de.ph87.tools.email.EmailService; +import de.ph87.tools.group.GroupRepository; +import de.ph87.tools.group.member.GroupMemberService; +import de.ph87.tools.group.owner.GroupOwnerService; +import de.ph87.tools.tools.numbers.NumbersRepository; +import de.ph87.tools.user.User; +import de.ph87.tools.user.UserAccessService; +import de.ph87.tools.user.UserPrivateDto; +import de.ph87.tools.user.UserRepository; +import de.ph87.tools.user.uuid.UserPrivateUuid; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.server.ResponseStatusException; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class UserAdminService { + + private final UserRepository userRepository; + + private final UserAccessService userAccessService; + + private final GroupRepository groupRepository; + + private final GroupMemberService groupMemberService; + + private final NumbersRepository numbersRepository; + + private final GroupOwnerService groupOwnerService; + + private final EmailService emailService; + + @NonNull + public Page page(@NonNull final UserPrivateUuid privateUuid, @NonNull final UserAdminPageRequest request) { + adminAccess(privateUuid); + return userRepository.findAll(request.getPageable()).map(UserPrivateDto::new); + } + + public void delete(@NonNull final UserPrivateUuid privateUuid, @NonNull final UserPrivateUuid deletePrivateUuid) { + adminAccess(privateUuid); + final User user = userAccessService.access(deletePrivateUuid); + groupRepository.findAllByOwner(user).forEach(group -> groupOwnerService.delete(group.getOwner().getPrivateUuid(), group.getUuid())); + groupRepository.findAllByUsersContains(user).forEach(group -> groupMemberService._remove_user_from_group_unchecked(group, user)); + numbersRepository.findAllByLotsUser(user).forEach(numbers -> numbers.getLots().removeIf(lot -> { + if (lot.getUser().equals(user)) { + log.info("Deleting NumbersLot: {}", lot); + return true; + } + return false; + })); + emailService.deleteAllByUser(user); + userRepository.delete(user); + log.info("User deleted: {}", user); + } + + @NonNull + @SuppressWarnings("UnusedReturnValue") + private User adminAccess(@NonNull final UserPrivateUuid privateUuid) { + final User user = userAccessService.access(privateUuid); + if (!user.isAdmin()) { + throw new ResponseStatusException(HttpStatus.FORBIDDEN); + } + return user; + } + +}