refactor: user registrar (#182)

This commit is contained in:
Menci 2025-10-07 03:32:52 +08:00 committed by GitHub
parent 010d4592e4
commit 1cac5e451a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 47 additions and 40 deletions

View File

@ -34,7 +34,6 @@ class UserRegistrar(
val cardService: CardService, val cardService: CardService,
val validator: AquaUserServices, val validator: AquaUserServices,
val emailProps: EmailProperties, val emailProps: EmailProperties,
val sessionRepo: SessionTokenRepo,
final val paths: PathProps final val paths: PathProps
) { ) {
val portraitPath = paths.aquaNetPortrait.path() val portraitPath = paths.aquaNetPortrait.path()
@ -68,29 +67,7 @@ class UserRegistrar(
val country = geoIP.getCountry(ip) val country = geoIP.getCountry(ip)
// Create user // Create user
val u = async { AquaNetUser( val u = async { validator.create(username, email, password, country) }
username = validator.checkUsername(username),
email = validator.checkEmail(email),
pwHash = validator.checkPwHash(password),
regTime = millis(), lastLogin = millis(), country = country,
) }
// Create a ghost card
val card = Card().apply {
extId = cardService.randExtID(cardExtIdStart, cardExtIdEnd)
luid = extId.toString()
registerTime = LocalDateTime.now()
accessTime = registerTime
aquaUser = u
isGhost = true
}
u.ghostCard = card
// Save the user
async {
userRepo.save(u)
cardRepo.save(card)
}
// Send confirmation email // Send confirmation email
emailService.sendConfirmation(u) emailService.sendConfirmation(u)
@ -205,11 +182,6 @@ class UserRegistrar(
// Remove the token from the list // Remove the token from the list
resetPasswordRepo.delete(reset) resetPasswordRepo.delete(reset)
// Clear all sessions
sessionRepo.deleteAll(
sessionRepo.findByAquaNetUserAuId(reset.aquaNetUser.auId)
)
return SUCCESS return SUCCESS
} }
@ -245,21 +217,14 @@ class UserRegistrar(
@API("/setting") @API("/setting")
@Doc("Validate and set a user setting field.", "Success message") @Doc("Validate and set a user setting field.", "Success message")
suspend fun setting(@RP token: Str, @RP key: Str, @RP value: Str) = jwt.auth(token) { u -> suspend fun setting(@RP token: Str, @RP key: Str, @RP value: Str) = jwt.auth(token) { u ->
// Check if the key is a settable field
val field = SETTING_FIELDS.find { it.name == key } ?: (400 - "Invalid setting")
async { async {
// Set the validated field validator.update(u, key, value)
field.setter.call(u, field.checker.call(validator, value))
// Save the user // Save the user
userRepo.save(u) userRepo.save(u)
// Clear all tokens if changing password // Clear all tokens if changing password
if (key == "pwHash") if (key == "pwHash") validator.clearAllSessions(u)
sessionRepo.deleteAll(
sessionRepo.findByAquaNetUserAuId(u.auId)
)
} }
SUCCESS SUCCESS

View File

@ -2,6 +2,8 @@ package icu.samnyan.aqua.net.db
import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonIgnore
import ext.* import ext.*
import icu.samnyan.aqua.net.UserRegistrar.Companion.cardExtIdEnd
import icu.samnyan.aqua.net.UserRegistrar.Companion.cardExtIdStart
import icu.samnyan.aqua.net.components.JWT import icu.samnyan.aqua.net.components.JWT
import icu.samnyan.aqua.sega.allnet.AllNetProps import icu.samnyan.aqua.sega.allnet.AllNetProps
import icu.samnyan.aqua.sega.allnet.KeyChipRepo import icu.samnyan.aqua.sega.allnet.KeyChipRepo
@ -9,11 +11,13 @@ import icu.samnyan.aqua.sega.allnet.KeychipSession
import icu.samnyan.aqua.sega.general.GameMusicPopularity import icu.samnyan.aqua.sega.general.GameMusicPopularity
import icu.samnyan.aqua.sega.general.dao.CardRepository import icu.samnyan.aqua.sega.general.dao.CardRepository
import icu.samnyan.aqua.sega.general.model.Card import icu.samnyan.aqua.sega.general.model.Card
import icu.samnyan.aqua.sega.general.service.CardService
import jakarta.persistence.* import jakarta.persistence.*
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.Serializable import java.io.Serializable
import java.time.LocalDateTime
import kotlin.jvm.optionals.getOrNull import kotlin.jvm.optionals.getOrNull
import kotlin.reflect.KFunction import kotlin.reflect.KFunction
import kotlin.reflect.KMutableProperty import kotlin.reflect.KMutableProperty
@ -124,7 +128,9 @@ class AquaUserServices(
val allNetProps: AllNetProps, val allNetProps: AllNetProps,
val jwt: JWT, val jwt: JWT,
val em: EntityManager, val em: EntityManager,
val pop: GameMusicPopularity val pop: GameMusicPopularity,
val cardService: CardService,
val sessionRepo: SessionTokenRepo,
) { ) {
companion object { companion object {
val SETTING_FIELDS = AquaUserServices::class.functions val SETTING_FIELDS = AquaUserServices::class.functions
@ -136,6 +142,42 @@ class AquaUserServices(
} }
} }
fun create(username: Str, email: Str, password: Str, country: Str): AquaNetUser {
// Create user
val u = AquaNetUser(
username = checkUsername(username),
email = validateEmail(email),
pwHash = checkPwHash(password),
regTime = millis(), lastLogin = millis(), country = country,
)
// Create a ghost card
val card = Card().apply {
extId = cardService.randExtID(cardExtIdStart, cardExtIdEnd)
luid = extId.toString()
registerTime = LocalDateTime.now()
accessTime = registerTime
aquaUser = u
isGhost = true
}
u.ghostCard = card
// Save the user
userRepo.save(u)
cardRepo.save(card)
return u
}
fun update(user: AquaNetUser, key: Str, value: Str) {
// Check if the key is a settable field
val field = SETTING_FIELDS.find { it.name == key } ?: (400 - "Invalid setting")
// Set the validated field
field.setter.call(user, field.checker.call(this, value))
}
fun clearAllSessions(user: AquaNetUser) = sessionRepo.deleteAll(sessionRepo.findByAquaNetUserAuId(user.auId))
suspend fun <T> byName(username: Str, callback: suspend (AquaNetUser) -> T) = suspend fun <T> byName(username: Str, callback: suspend (AquaNetUser) -> T) =
async { userRepo.findByUsernameIgnoreCase(username) }?.let { callback(it) } ?: (404 - "User not found") async { userRepo.findByUsernameIgnoreCase(username) }?.let { callback(it) } ?: (404 - "User not found")
@ -173,7 +215,7 @@ class AquaUserServices(
400 - "User with username `$this` already exists" 400 - "User with username `$this` already exists"
} }
fun checkEmail(email: Str) = email.apply { fun validateEmail(email: Str) = email.apply {
// Check if email is valid // Check if email is valid
if (!isValidEmail()) 400 - "Invalid email" if (!isValidEmail()) 400 - "Invalid email"