feat(invite): add GET /v1/invites/{code} + rework POST accept as signup+join
- V027 migration: add invited_by FK column on household_invite
- HouseholdInvite entity: add invitedBy field, set on createInvite
- New DTOs: InviteInfoResponse, AcceptInviteRequest
- HouseholdService: add getInviteInfo(), rewrite acceptInvite(code, name, email, password) — creates UserAccount + joins household in one transaction
- HouseholdController: GET /v1/invites/{code} (unauthenticated), POST /v1/invites/{code}/accept creates session after join
- SecurityConfig: permitAll() for /v1/invites/*, sessionFixation().changeSessionId()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,17 @@
|
||||
package com.recipeapp.household;
|
||||
|
||||
import com.recipeapp.auth.entity.UserAccount;
|
||||
import com.recipeapp.common.ApiResponse;
|
||||
import com.recipeapp.household.dto.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.security.Principal;
|
||||
@@ -71,11 +78,34 @@ public class HouseholdController {
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
@PostMapping("/invites/{code}/accept")
|
||||
public ResponseEntity<ApiResponse<AcceptInviteResponse>> acceptInvite(
|
||||
Principal principal,
|
||||
@PathVariable String code) {
|
||||
AcceptInviteResponse response = householdService.acceptInvite(principal.getName(), code);
|
||||
@GetMapping("/invites/{code}")
|
||||
public ResponseEntity<ApiResponse<InviteInfoResponse>> getInviteInfo(@PathVariable String code) {
|
||||
InviteInfoResponse response = householdService.getInviteInfo(code);
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
@PostMapping("/invites/{code}/accept")
|
||||
public ResponseEntity<ApiResponse<AcceptInviteResponse>> acceptInvite(
|
||||
@PathVariable String code,
|
||||
@Valid @RequestBody AcceptInviteRequest request,
|
||||
HttpServletRequest httpRequest) {
|
||||
AcceptInviteResponse response = householdService.acceptInvite(
|
||||
code, request.name(), request.email(), request.password());
|
||||
authenticateInSession(request.email(), httpRequest);
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
private void authenticateInSession(String email, HttpServletRequest request) {
|
||||
var oldSession = request.getSession(false);
|
||||
if (oldSession != null) {
|
||||
oldSession.invalidate();
|
||||
}
|
||||
var auth = UsernamePasswordAuthenticationToken.authenticated(
|
||||
email, null, List.of(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
context.setAuthentication(auth);
|
||||
SecurityContextHolder.setContext(context);
|
||||
request.getSession(true).setAttribute(
|
||||
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user