From 8544803e62ed95c46defb376400de5144b8ffa1f Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:25:32 +0800 Subject: [PATCH] feat: shorten invite links --- .../java/com/openisle/model/InviteToken.java | 7 ++++ .../repository/InviteTokenRepository.java | 4 +++ .../com/openisle/service/InviteService.java | 32 +++++++++++++++---- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/com/openisle/model/InviteToken.java b/backend/src/main/java/com/openisle/model/InviteToken.java index 7db547bcc..4a1180dc8 100644 --- a/backend/src/main/java/com/openisle/model/InviteToken.java +++ b/backend/src/main/java/com/openisle/model/InviteToken.java @@ -14,6 +14,13 @@ public class InviteToken { @Id private String token; + /** + * Short token used in invite links. Existing records may have this field null + * and fall back to {@link #token} for backward compatibility. + */ + @Column(unique = true) + private String shortToken; + @ManyToOne private User inviter; diff --git a/backend/src/main/java/com/openisle/repository/InviteTokenRepository.java b/backend/src/main/java/com/openisle/repository/InviteTokenRepository.java index 3257124d8..8e6d45d94 100644 --- a/backend/src/main/java/com/openisle/repository/InviteTokenRepository.java +++ b/backend/src/main/java/com/openisle/repository/InviteTokenRepository.java @@ -9,4 +9,8 @@ import java.util.Optional; public interface InviteTokenRepository extends JpaRepository { Optional findByInviterAndCreatedDate(User inviter, LocalDate createdDate); + + Optional findByShortToken(String shortToken); + + boolean existsByShortToken(String shortToken); } diff --git a/backend/src/main/java/com/openisle/service/InviteService.java b/backend/src/main/java/com/openisle/service/InviteService.java index 23ca58bd1..94e5a38f4 100644 --- a/backend/src/main/java/com/openisle/service/InviteService.java +++ b/backend/src/main/java/com/openisle/service/InviteService.java @@ -30,33 +30,53 @@ public class InviteService { LocalDate today = LocalDate.now(); Optional existing = inviteTokenRepository.findByInviterAndCreatedDate(inviter, today); if (existing.isPresent()) { - return existing.get().getToken(); + InviteToken inviteToken = existing.get(); + return inviteToken.getShortToken() != null ? inviteToken.getShortToken() : inviteToken.getToken(); } + String token = jwtService.generateInviteToken(username); + String shortToken; + do { + shortToken = java.util.UUID.randomUUID().toString().replace("-", "").substring(0, 8); + } while (inviteTokenRepository.existsByShortToken(shortToken)); + InviteToken inviteToken = new InviteToken(); inviteToken.setToken(token); + inviteToken.setShortToken(shortToken); inviteToken.setInviter(inviter); inviteToken.setCreatedDate(today); inviteToken.setUsageCount(0); inviteTokenRepository.save(inviteToken); - return token; + return shortToken; } public InviteValidateResult validate(String token) { if (token == null || token.isEmpty()) { return new InviteValidateResult(null, false); } + + InviteToken invite = inviteTokenRepository.findById(token).orElse(null); + String realToken = token; + if (invite == null) { + invite = inviteTokenRepository.findByShortToken(token).orElse(null); + if (invite == null) { + return new InviteValidateResult(null, false); + } + realToken = invite.getToken(); + } + try { - jwtService.validateAndGetSubjectForInvite(token); + jwtService.validateAndGetSubjectForInvite(realToken); } catch (Exception e) { return new InviteValidateResult(null, false); } - InviteToken invite = inviteTokenRepository.findById(token).orElse(null); - return new InviteValidateResult(invite, invite != null && invite.getUsageCount() < 3); + + return new InviteValidateResult(invite, invite.getUsageCount() < 3); } public void consume(String token, String newUserName) { - InviteToken invite = inviteTokenRepository.findById(token).orElseThrow(); + InviteToken invite = inviteTokenRepository.findById(token) + .orElseGet(() -> inviteTokenRepository.findByShortToken(token).orElseThrow()); invite.setUsageCount(invite.getUsageCount() + 1); inviteTokenRepository.save(invite); pointService.awardForInvite(invite.getInviter().getUsername(), newUserName);