Public Access
fix(sync): stop the nightly sync when the Steam ID is gone
After an unlink in Auth, or when a verified link took the ID away, the nightly job kept syncing the old Steam ID for up to 14 days. A games-count request without a steamId claim now clears user_seen_at. steam_id stays, so a later link with another ID still soft-deletes the old games.
This commit is contained in:
1 parent
393cdddf06
commit
a389bdd0c7
5 files changed
+83
-1
No files matched your search
@@ -70,6 +70,8 @@ public class LibraryController {
|
||||
Long userId = principal.getIdAsLong();
|
||||
if (principal.steamId() != null) {
|
||||
syncService.syncIfStale(userId, String.valueOf(principal.steamId()));
|
||||
} else {
|
||||
syncService.stopNightlySync(userId);
|
||||
}
|
||||
long count = queryService.countOwnedGames(userId);
|
||||
return ResponseEntity.ok(Map.of("count", count, "hasSteamId", principal.steamId() != null));
|
||||
|
||||
@@ -52,6 +52,15 @@ public interface UserSteamSyncRepository extends JpaRepository<UserSteamSync, Lo
|
||||
@Query(value = "UPDATE user_steam_sync SET user_seen_at = :now WHERE user_id = :userId", nativeQuery = true)
|
||||
void touchUserSeen(@Param("userId") Long userId, @Param("now") Instant now);
|
||||
|
||||
/**
|
||||
* Takes the user out of the nightly sync. steam_id stays, so a later sync with another ID still sees the change.
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query(value = "UPDATE user_steam_sync SET user_seen_at = NULL WHERE user_id = :userId AND user_seen_at IS NOT NULL",
|
||||
nativeQuery = true)
|
||||
void clearUserSeen(@Param("userId") Long userId);
|
||||
|
||||
List<UserSteamSync> findByUserSeenAtAfter(Instant since);
|
||||
}
|
||||
|
||||
@@ -65,6 +65,14 @@ public class LibrarySyncService {
|
||||
accounts.forEach(sync -> worker.runSyncNow(sync.getUserId(), sync.getSteamId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* For a user whose token has no Steam ID any more (unlinked, or taken by a verified link in Auth): the nightly
|
||||
* job would otherwise keep syncing the old ID until the user is 14 days inactive.
|
||||
*/
|
||||
public void stopNightlySync(Long userId) {
|
||||
userSteamSyncRepository.clearUserSeen(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* User-triggered sync. Throws SyncCooldownException if recently synced or running.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package core_service.modules.library.api;
|
||||
|
||||
import core_service.modules.library.repo.UserSteamSyncRepository;
|
||||
import core_service.shared.test.AbstractIntegrationTest;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
* A user who unlinked the Steam ID, or lost it to a verified link, must drop out of the nightly sync.
|
||||
*/
|
||||
@AutoConfigureMockMvc
|
||||
class LibraryControllerSteamIdGoneTest extends AbstractIntegrationTest {
|
||||
|
||||
private static final long USER_ID = 77L;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
@Autowired
|
||||
private JdbcTemplate jdbc;
|
||||
@Autowired
|
||||
private UserSteamSyncRepository userSteamSyncRepository;
|
||||
|
||||
@BeforeEach
|
||||
void seedSyncedUser() {
|
||||
jdbc.update("DELETE FROM user_steam_sync WHERE user_id = ?", USER_ID);
|
||||
jdbc.update("INSERT INTO user_steam_sync (user_id, steam_id, last_synced_at, last_sync_status, user_seen_at) "
|
||||
+ "VALUES (?, '76561198000000001', now(), 'OK', now())", USER_ID);
|
||||
}
|
||||
|
||||
private static Cookie loginWithoutSteamId() {
|
||||
return new Cookie("tbp_access", Jwts.builder()
|
||||
.subject("unlinked@example.com")
|
||||
.claim("userId", String.valueOf(USER_ID))
|
||||
.expiration(new Date(System.currentTimeMillis() + 60_000))
|
||||
.signWith(TEST_KEYS.getPrivate(), Jwts.SIG.EdDSA)
|
||||
.compact());
|
||||
}
|
||||
|
||||
@Test
|
||||
void gamesCount_WithoutSteamIdClaim_StopsTheNightlySyncOfTheOldId() throws Exception {
|
||||
mockMvc.perform(get("/api/library/games-count").cookie(loginWithoutSteamId())).andExpect(status().isOk());
|
||||
|
||||
assertTrue(userSteamSyncRepository.findByUserSeenAtAfter(Instant.now().minus(Duration.ofDays(14))).stream()
|
||||
.noneMatch(sync -> sync.getUserId() == USER_ID));
|
||||
// The old ID stays: a later link with another ID must still soft-delete the old games.
|
||||
assertTrue(userSteamSyncRepository.findById(USER_ID).orElseThrow().getSteamId().equals("76561198000000001"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user