diff --git a/src/main/java/icu/samnyan/aqua/net/UserRegistrar.kt b/src/main/java/icu/samnyan/aqua/net/UserRegistrar.kt index dcf16ca5..e9bc5b08 100644 --- a/src/main/java/icu/samnyan/aqua/net/UserRegistrar.kt +++ b/src/main/java/icu/samnyan/aqua/net/UserRegistrar.kt @@ -4,17 +4,14 @@ import ext.* import icu.samnyan.aqua.net.components.* import icu.samnyan.aqua.net.db.AquaNetUser import icu.samnyan.aqua.net.db.AquaNetUserRepo -import icu.samnyan.aqua.net.db.AquaGameOptionsRepo import icu.samnyan.aqua.net.db.AquaUserServices import icu.samnyan.aqua.net.db.EmailConfirmationRepo import icu.samnyan.aqua.net.db.ResetPasswordRepo -import icu.samnyan.aqua.net.db.SessionTokenRepo import icu.samnyan.aqua.net.utils.AquaNetProps import icu.samnyan.aqua.net.utils.PathProps import icu.samnyan.aqua.net.utils.SUCCESS import icu.samnyan.aqua.sega.allnet.UserKeychip import icu.samnyan.aqua.sega.allnet.UserKeychipRepo -import icu.samnyan.aqua.sega.allnet.KeychipSessionRepo import icu.samnyan.aqua.sega.chusan.model.Chu3Repos import icu.samnyan.aqua.sega.diva.DivaRepos import icu.samnyan.aqua.sega.diva.model.db.userdata.PlayerProfile @@ -376,12 +373,6 @@ data class AccountFileCleanup( class AccountDeletionService( val userRepo: AquaNetUserRepo, val cardRepo: CardRepository, - val gameOptionsRepo: AquaGameOptionsRepo, - val sessionRepo: SessionTokenRepo, - val confirmationRepo: EmailConfirmationRepo, - val resetPasswordRepo: ResetPasswordRepo, - val userKeychipRepo: UserKeychipRepo, - val keychipSessionRepo: KeychipSessionRepo, val mai2: Mai2Repos, val chu3: Chu3Repos, val ongeki: OngekiUserRepos, @@ -405,28 +396,10 @@ class AccountDeletionService( wacca.user.findByCard(ghostCard)?.let { wacca.user.delete(it) } chu3.userLoginBonus.deleteAll(chu3.userLoginBonus.findByUser(ghostCard.extId.toInt())) - sessionRepo.deleteAll(sessionRepo.findByAquaNetUserAuId(auId)) - confirmationRepo.deleteAll(confirmationRepo.findByAquaNetUserAuId(auId)) - resetPasswordRepo.deleteAll(resetPasswordRepo.findByAquaNetUserAuId(auId)) - userKeychipRepo.deleteAll(userKeychipRepo.findAllByUserAuId(auId)) - keychipSessionRepo.deleteAll(keychipSessionRepo.findAllByUserAuId(auId)) - - val linkedCards = (cardRepo.findAllByAquaUserAuId(auId) + ghostCard).distinctBy { it.id } - linkedCards.forEach { it.aquaUser = null } - cardRepo.saveAll(linkedCards) - cardRepo.flush() - - user.cards.clear() - user.keychips.clear() - user.keychipSessions.clear() - val gameOptions = user.gameOptions - user.gameOptions = null - userRepo.delete(user) userRepo.flush() cardRepo.delete(ghostCard) cardRepo.flush() - gameOptions?.let { gameOptionsRepo.delete(it) } log.info("Deleted account and game data for user $auId") return AccountFileCleanup(user.profilePicture, ghostCard.extId, divaScreenshots) diff --git a/src/main/java/icu/samnyan/aqua/net/db/AquaEmailConfirmation.kt b/src/main/java/icu/samnyan/aqua/net/db/AquaEmailConfirmation.kt index 9a324647..ec347b8d 100644 --- a/src/main/java/icu/samnyan/aqua/net/db/AquaEmailConfirmation.kt +++ b/src/main/java/icu/samnyan/aqua/net/db/AquaEmailConfirmation.kt @@ -1,7 +1,9 @@ package icu.samnyan.aqua.net.db import jakarta.persistence.* -import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.JpaRepository +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction import org.springframework.stereotype.Repository import java.io.Serializable import java.time.Instant @@ -20,9 +22,10 @@ class EmailConfirmation( @Column(nullable = false) var createdAt: Instant = Instant.now(), - // Linking to the AquaNetUser - @ManyToOne - @JoinColumn(name = "auId", referencedColumnName = "auId") + // Linking to the AquaNetUser + @ManyToOne + @OnDelete(action = OnDeleteAction.CASCADE) + @JoinColumn(name = "auId", referencedColumnName = "auId") var aquaNetUser: AquaNetUser = AquaNetUser() ) : Serializable @@ -30,4 +33,4 @@ class EmailConfirmation( interface EmailConfirmationRepo : JpaRepository { fun findByToken(token: String): EmailConfirmation? fun findByAquaNetUserAuId(auId: Long): List -} \ No newline at end of file +} diff --git a/src/main/java/icu/samnyan/aqua/net/db/AquaEmailResetPassword.kt b/src/main/java/icu/samnyan/aqua/net/db/AquaEmailResetPassword.kt index 98a0281c..729c24aa 100644 --- a/src/main/java/icu/samnyan/aqua/net/db/AquaEmailResetPassword.kt +++ b/src/main/java/icu/samnyan/aqua/net/db/AquaEmailResetPassword.kt @@ -2,6 +2,8 @@ package icu.samnyan.aqua.net.db import jakarta.persistence.* import org.springframework.data.jpa.repository.JpaRepository +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction import org.springframework.stereotype.Repository import java.io.Serializable import java.time.Instant @@ -22,6 +24,7 @@ class ResetPassword( // Linking to the AquaNetUser @ManyToOne + @OnDelete(action = OnDeleteAction.CASCADE) @JoinColumn(name = "auId", referencedColumnName = "auId") var aquaNetUser: AquaNetUser = AquaNetUser() ) : Serializable @@ -30,4 +33,4 @@ class ResetPassword( interface ResetPasswordRepo : JpaRepository { fun findByToken(token: String): ResetPassword? fun findByAquaNetUserAuId(auId: Long): List -} \ No newline at end of file +} diff --git a/src/main/java/icu/samnyan/aqua/net/db/AquaNetSession.kt b/src/main/java/icu/samnyan/aqua/net/db/AquaNetSession.kt index f3f24d52..0b7220a7 100644 --- a/src/main/java/icu/samnyan/aqua/net/db/AquaNetSession.kt +++ b/src/main/java/icu/samnyan/aqua/net/db/AquaNetSession.kt @@ -2,6 +2,8 @@ package icu.samnyan.aqua.net.db import jakarta.persistence.* import org.springframework.data.jpa.repository.JpaRepository +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction import org.springframework.stereotype.Repository import java.io.Serializable import java.time.Instant @@ -22,6 +24,7 @@ class SessionToken( // Linking to the AquaNetUser @ManyToOne + @OnDelete(action = OnDeleteAction.CASCADE) @JoinColumn(name = "auId", referencedColumnName = "auId") var aquaNetUser: AquaNetUser = AquaNetUser() ) : Serializable diff --git a/src/main/java/icu/samnyan/aqua/net/db/AquaNetUser.kt b/src/main/java/icu/samnyan/aqua/net/db/AquaNetUser.kt index 620bcbc8..f9f3bd23 100644 --- a/src/main/java/icu/samnyan/aqua/net/db/AquaNetUser.kt +++ b/src/main/java/icu/samnyan/aqua/net/db/AquaNetUser.kt @@ -79,13 +79,13 @@ class AquaNetUser( // Each user can have multiple keychips (if the user owns cabinets) @JsonIgnore - @OneToMany(mappedBy = "user", cascade = [CascadeType.ALL]) - var keychips: MutableList = mutableListOf(), + @OneToMany(mappedBy = "user", cascade = [CascadeType.PERSIST, CascadeType.MERGE]) + var keychips: MutableList = mutableListOf(), // Each user's keychip can have multiple sessions @JsonIgnore - @OneToMany(mappedBy = "user", cascade = [CascadeType.ALL]) - var keychipSessions: MutableList = mutableListOf(), + @OneToMany(mappedBy = "user", cascade = [CascadeType.PERSIST, CascadeType.MERGE]) + var keychipSessions: MutableList = mutableListOf(), @Column(nullable = false) var canModifyKeychips: Boolean = false, diff --git a/src/main/java/icu/samnyan/aqua/sega/allnet/KeychipSession.kt b/src/main/java/icu/samnyan/aqua/sega/allnet/KeychipSession.kt index 62cb6646..f53e014f 100644 --- a/src/main/java/icu/samnyan/aqua/sega/allnet/KeychipSession.kt +++ b/src/main/java/icu/samnyan/aqua/sega/allnet/KeychipSession.kt @@ -5,7 +5,9 @@ import icu.samnyan.aqua.net.db.AquaNetUser import jakarta.persistence.* import jakarta.transaction.Transactional import org.slf4j.LoggerFactory -import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.JpaRepository +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Repository import org.springframework.stereotype.Service @@ -18,9 +20,10 @@ import java.security.SecureRandom @Table(name = "allnet_keychip_sessions", indexes = [ Index(name = "idx_last_use", columnList = "lastUse") ]) -class KeychipSession( - @ManyToOne - @JoinColumn(name = "au_id") +class KeychipSession( + @ManyToOne + @OnDelete(action = OnDeleteAction.CASCADE) + @JoinColumn(name = "au_id") var user: AquaNetUser? = null, @Column(length = 4) @@ -47,7 +50,6 @@ fun genUrlSafeToken(length: Int): String { @Repository("KeychipSessionRepo") interface KeychipSessionRepo : JpaRepository { fun findByToken(token: String): KeychipSession? - fun findAllByUserAuId(auId: Long): List @Transactional fun deleteAllByLastUseBefore(expire: Long) diff --git a/src/main/java/icu/samnyan/aqua/sega/allnet/UserKeychip.kt b/src/main/java/icu/samnyan/aqua/sega/allnet/UserKeychip.kt index 49e70c80..5c73a69a 100644 --- a/src/main/java/icu/samnyan/aqua/sega/allnet/UserKeychip.kt +++ b/src/main/java/icu/samnyan/aqua/sega/allnet/UserKeychip.kt @@ -4,6 +4,8 @@ import icu.samnyan.aqua.net.db.AquaNetUser import jakarta.persistence.* import jakarta.transaction.Transactional import org.springframework.data.jpa.repository.JpaRepository +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction import org.springframework.stereotype.Repository @Entity @@ -13,6 +15,7 @@ class UserKeychip( val id: Long = 0, @ManyToOne + @OnDelete(action = OnDeleteAction.CASCADE) @JoinColumn(name = "au_id", nullable = false) var user: AquaNetUser, diff --git a/src/main/java/icu/samnyan/aqua/sega/general/dao/CardRepository.kt b/src/main/java/icu/samnyan/aqua/sega/general/dao/CardRepository.kt index 3307f061..538afe78 100644 --- a/src/main/java/icu/samnyan/aqua/sega/general/dao/CardRepository.kt +++ b/src/main/java/icu/samnyan/aqua/sega/general/dao/CardRepository.kt @@ -11,5 +11,4 @@ import org.springframework.stereotype.Repository interface CardRepository : JpaRepository { fun findByExtId(extId: Long): Card? fun findByLuid(luid: String): Card? - fun findAllByAquaUserAuId(auId: Long): List } diff --git a/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt b/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt index fcd0183f..9c50ee08 100644 --- a/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt +++ b/src/main/java/icu/samnyan/aqua/sega/general/model/Card.kt @@ -3,6 +3,8 @@ package icu.samnyan.aqua.sega.general.model import com.fasterxml.jackson.annotation.JsonIgnore import icu.samnyan.aqua.net.db.AquaNetUser import jakarta.persistence.* +import org.hibernate.annotations.OnDelete +import org.hibernate.annotations.OnDeleteAction import java.time.LocalDateTime enum class CardStatus { @@ -59,6 +61,7 @@ class Card( // Defines the AquaNet user that this card is bound to @ManyToOne + @OnDelete(action = OnDeleteAction.SET_NULL) @JoinColumn(name = "net_user_id") @JsonIgnore var aquaUser: AquaNetUser? = null, diff --git a/src/main/resources/db/120/V1000_84__account_owned_cascade.sql b/src/main/resources/db/120/V1000_84__account_owned_cascade.sql new file mode 100644 index 00000000..32add6df --- /dev/null +++ b/src/main/resources/db/120/V1000_84__account_owned_cascade.sql @@ -0,0 +1,42 @@ +-- Account-owned rows disappear with the account. Physical cards are only unlinked. +ALTER TABLE aqua_net_session + DROP FOREIGN KEY IF EXISTS FK_SESSION; +ALTER TABLE aqua_net_session + ADD CONSTRAINT FK_SESSION + FOREIGN KEY (au_id) REFERENCES aqua_net_user (au_id) + ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE aqua_net_email_confirmation + DROP FOREIGN KEY IF EXISTS FK_EMAIL_CONFIRMATION_ON_AQUA_USER; +ALTER TABLE aqua_net_email_confirmation + ADD CONSTRAINT FK_EMAIL_CONFIRMATION_ON_AQUA_USER + FOREIGN KEY (au_id) REFERENCES aqua_net_user (au_id) + ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE aqua_net_email_reset_password + DROP FOREIGN KEY IF EXISTS FK_EMAIL_RESET_PASSWORD_ON_AQUA_USER; +ALTER TABLE aqua_net_email_reset_password + ADD CONSTRAINT FK_EMAIL_RESET_PASSWORD_ON_AQUA_USER + FOREIGN KEY (au_id) REFERENCES aqua_net_user (au_id) + ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE allnet_keychip_sessions + DROP FOREIGN KEY IF EXISTS FK_ALLNET_KEYCHIP_SESSIONS_ON_AU; +ALTER TABLE allnet_keychip_sessions + ADD CONSTRAINT FK_ALLNET_KEYCHIP_SESSIONS_ON_AU + FOREIGN KEY (au_id) REFERENCES aqua_net_user (au_id) + ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE user_keychip + DROP FOREIGN KEY IF EXISTS fk_user_keychip_on_au; +ALTER TABLE user_keychip + ADD CONSTRAINT fk_user_keychip_on_au + FOREIGN KEY (au_id) REFERENCES aqua_net_user (au_id) + ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE sega_card + DROP FOREIGN KEY IF EXISTS FK_SEGA_CARD_ON_NET_USER; +ALTER TABLE sega_card + ADD CONSTRAINT FK_SEGA_CARD_ON_NET_USER + FOREIGN KEY (net_user_id) REFERENCES aqua_net_user (au_id) + ON DELETE SET NULL ON UPDATE CASCADE;