diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/controller/UserController.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/controller/UserController.java index e3ef697..8c2832d 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/controller/UserController.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/controller/UserController.java @@ -136,4 +136,10 @@ public class UserController { public Page getUserFollowing(@PathVariable String userId, Pageable pageable) { return userService.getFollowing(userId, pageable); } + + @PostMapping(path = "/auth/users/{userId}/reports") + public ResponseEntity reportUser(@PathVariable String userId, @RequestBody UserReportPayload payload) { + userService.reportUser(userId, payload); + return ResponseEntity.ok().build(); + } } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/dao/UserReportRepository.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/dao/UserReportRepository.java new file mode 100644 index 0000000..6a58abb --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/dao/UserReportRepository.java @@ -0,0 +1,9 @@ +package nl.andrewlalis.gymboard_api.domains.auth.dao; + +import nl.andrewlalis.gymboard_api.domains.auth.model.UserReport; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserReportRepository extends JpaRepository { +} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/dto/UserReportPayload.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/dto/UserReportPayload.java new file mode 100644 index 0000000..90cd7eb --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/dto/UserReportPayload.java @@ -0,0 +1,6 @@ +package nl.andrewlalis.gymboard_api.domains.auth.dto; + +public record UserReportPayload( + String reason, + String description +) {} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/service/UserService.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/service/UserService.java index f1da87c..1040177 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/service/UserService.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/service/UserService.java @@ -17,6 +17,7 @@ import org.springframework.http.HttpStatus; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -43,6 +44,7 @@ public class UserService { private final UserFollowingRepository userFollowingRepository; private final UserFollowRequestRepository followRequestRepository; private final UserAccessService userAccessService; + private final UserReportRepository userReportRepository; private final ULID ulid; private final PasswordEncoder passwordEncoder; private final JavaMailSender mailSender; @@ -59,7 +61,7 @@ public class UserService { EmailResetCodeRepository emailResetCodeRepository, UserFollowingRepository userFollowingRepository, UserFollowRequestRepository followRequestRepository, UserAccessService userAccessService, - ULID ulid, + UserReportRepository userReportRepository, ULID ulid, PasswordEncoder passwordEncoder, JavaMailSender mailSender ) { @@ -72,6 +74,7 @@ public class UserService { this.userFollowingRepository = userFollowingRepository; this.followRequestRepository = followRequestRepository; this.userAccessService = userAccessService; + this.userReportRepository = userReportRepository; this.ulid = ulid; this.passwordEncoder = passwordEncoder; this.mailSender = mailSender; @@ -439,4 +442,19 @@ public class UserService { user1FollowedByUser2 ); } + + @Transactional + public void reportUser(String userId, UserReportPayload payload) { + User user = findByIdOrThrow(userId, userRepository); + User reporter = null; + if (SecurityContextHolder.getContext().getAuthentication() instanceof TokenAuthentication t) { + reporter = findByIdOrThrow(t.user().getId(), userRepository); + } + userReportRepository.save(new UserReport( + user, + reporter, + payload.reason(), + payload.description() + )); + } } diff --git a/gymboard-app/src/api/main/auth.ts b/gymboard-app/src/api/main/auth.ts index 1b7773c..296333f 100644 --- a/gymboard-app/src/api/main/auth.ts +++ b/gymboard-app/src/api/main/auth.ts @@ -1,7 +1,7 @@ -import {api} from 'src/api/main/index'; -import {AuthStoreType} from 'stores/auth-store'; +import { api } from 'src/api/main/index'; +import { AuthStoreType } from 'stores/auth-store'; import Timeout = NodeJS.Timeout; -import {WeightUnit} from 'src/api/main/submission'; +import { WeightUnit } from 'src/api/main/submission'; export interface User { id: string; @@ -15,7 +15,7 @@ export interface User { export enum PersonSex { MALE = 'MALE', FEMALE = 'FEMALE', - UNKNOWN = 'UNKNOWN' + UNKNOWN = 'UNKNOWN', } export interface UserPersonalDetails { @@ -104,13 +104,25 @@ class AuthModule { return response.data; } - public async getUser(userId: string, authStore: AuthStoreType): Promise { - const response = await api.get(`/auth/users/${userId}`, authStore.axiosConfig); + public async getUser( + userId: string, + authStore: AuthStoreType + ): Promise { + const response = await api.get( + `/auth/users/${userId}`, + authStore.axiosConfig + ); return response.data; } - public async isUserAccessible(userId: string, authStore: AuthStoreType): Promise { - const response = await api.get(`/auth/users/${userId}/access`, authStore.axiosConfig); + public async isUserAccessible( + userId: string, + authStore: AuthStoreType + ): Promise { + const response = await api.get( + `/auth/users/${userId}/access`, + authStore.axiosConfig + ); return response.data.accessible; } @@ -133,48 +145,131 @@ class AuthModule { }); } - public async getMyPersonalDetails(authStore: AuthStoreType): Promise { - const response = await api.get('/auth/me/personal-details', authStore.axiosConfig); + public async generateEmailResetCode( + newEmail: string, + authStore: AuthStoreType + ) { + await api.post( + '/auth/me/email-reset-code', + { newEmail: newEmail }, + authStore.axiosConfig + ); + } + + public async updateMyEmail(code: string, authStore: AuthStoreType) { + await api.post('/auth/me/email?code=' + code, null, authStore.axiosConfig); + } + + public async getMyPersonalDetails( + authStore: AuthStoreType + ): Promise { + const response = await api.get( + '/auth/me/personal-details', + authStore.axiosConfig + ); return response.data; } - public async updateMyPersonalDetails(authStore: AuthStoreType, newPersonalDetails: UserPersonalDetails): Promise { - const response = await api.post('/auth/me/personal-details', newPersonalDetails, authStore.axiosConfig); + public async updateMyPersonalDetails( + authStore: AuthStoreType, + newPersonalDetails: UserPersonalDetails + ): Promise { + const response = await api.post( + '/auth/me/personal-details', + newPersonalDetails, + authStore.axiosConfig + ); return response.data; } - public async getMyPreferences(authStore: AuthStoreType): Promise { - const response = await api.get('/auth/me/preferences', authStore.axiosConfig); + public async getMyPreferences( + authStore: AuthStoreType + ): Promise { + const response = await api.get( + '/auth/me/preferences', + authStore.axiosConfig + ); return response.data; } - public async updateMyPreferences(authStore: AuthStoreType, newPreferences: UserPreferences): Promise { - const response = await api.post('/auth/me/preferences', newPreferences, authStore.axiosConfig); + public async updateMyPreferences( + authStore: AuthStoreType, + newPreferences: UserPreferences + ): Promise { + const response = await api.post( + '/auth/me/preferences', + newPreferences, + authStore.axiosConfig + ); return response.data; } - public async getFollowers(userId: string, authStore: AuthStoreType, page: number, count: number): Promise { - const response = await api.get(`/auth/users/${userId}/followers?page=${page}&count=${count}`, authStore.axiosConfig); + public async getFollowers( + userId: string, + authStore: AuthStoreType, + page: number, + count: number + ): Promise { + const response = await api.get( + `/auth/users/${userId}/followers?page=${page}&count=${count}`, + authStore.axiosConfig + ); return response.data.content; } - public async getFollowing(userId: string, authStore: AuthStoreType, page: number, count: number): Promise { - const response = await api.get(`/auth/users/${userId}/following?page=${page}&count=${count}`, authStore.axiosConfig); + public async getFollowing( + userId: string, + authStore: AuthStoreType, + page: number, + count: number + ): Promise { + const response = await api.get( + `/auth/users/${userId}/following?page=${page}&count=${count}`, + authStore.axiosConfig + ); return response.data.content; } - public async getRelationshipTo(userId: string, targetUserId: string, authStore: AuthStoreType): Promise { - const response = await api.get(`/auth/users/${userId}/relationship-to/${targetUserId}`, authStore.axiosConfig); + public async getRelationshipTo( + userId: string, + targetUserId: string, + authStore: AuthStoreType + ): Promise { + const response = await api.get( + `/auth/users/${userId}/relationship-to/${targetUserId}`, + authStore.axiosConfig + ); return response.data; } - public async followUser(userId: string, authStore: AuthStoreType) { - await api.post(`/auth/users/${userId}/followers`, undefined, authStore.axiosConfig); + public async followUser( + userId: string, + authStore: AuthStoreType + ): Promise { + const response = await api.post( + `/auth/users/${userId}/followers`, + undefined, + authStore.axiosConfig + ); + return response.data.result; } public async unfollowUser(userId: string, authStore: AuthStoreType) { await api.delete(`/auth/users/${userId}/followers`, authStore.axiosConfig); } + + public async reportUser( + userId: string, + reason: string, + description: string | null, + authStore?: AuthStoreType + ) { + await api.post( + `/auth/users/${userId}/reports`, + { reason, description }, + authStore?.axiosConfig + ); + } } export default AuthModule; diff --git a/gymboard-app/src/pages/gym/GymSubmissionPage.vue b/gymboard-app/src/pages/gym/GymSubmissionPage.vue index 2787dd6..9f2dc42 100644 --- a/gymboard-app/src/pages/gym/GymSubmissionPage.vue +++ b/gymboard-app/src/pages/gym/GymSubmissionPage.vue @@ -83,24 +83,30 @@ A high-level overview of the submission process is as follows:
- Login or register to submit your lift + Login or register to submit your lift
- +