[+] Settings api

pull/23/head
Azalea 2024-03-18 09:32:14 -04:00
parent b3955731c2
commit 5057f6848f
3 changed files with 67 additions and 6 deletions

View File

@ -22,6 +22,9 @@ import java.nio.file.Path
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.full.memberProperties
typealias RP = RequestParam
typealias RB = RequestBody
@ -30,17 +33,22 @@ typealias API = RequestMapping
typealias Str = String
typealias Bool = Boolean
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.PROPERTY_GETTER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Doc(
val desc: String,
val ret: String = ""
)
@Target(AnnotationTarget.VALUE_PARAMETER)
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class SettingField(val name: Str, val desc: Str)
// Reflection
@Suppress("UNCHECKED_CAST")
fun <T : Any> KClass<T>.vars() = memberProperties.mapNotNull { it as? KMutableProperty1<T, Any> }
// Make it easier to throw a ResponseStatusException
operator fun HttpStatus.invoke(message: String? = null): Nothing = throw ApiException(value(), message ?: this.reasonPhrase)
operator fun Int.minus(message: String): Nothing {

View File

@ -0,0 +1,53 @@
package icu.samnyan.aqua.net
import ext.*
import icu.samnyan.aqua.net.db.AquaGameOptions
import icu.samnyan.aqua.net.db.AquaNetUserRepo
import icu.samnyan.aqua.net.db.AquaUserServices
import org.springframework.web.bind.annotation.RestController
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.primaryConstructor
@RestController
@API("/api/v2/settings")
class SettingsApi(
val us: AquaUserServices,
val userRepo: AquaNetUserRepo
) {
// Get all params with SettingField annotation
// Kotlin by default put all the @Annotations in the constructor param
val params = AquaGameOptions::class.primaryConstructor!!.parameters
.mapNotNull { it.findAnnotation<SettingField>()?.let { an -> it to an } }
val fields = AquaGameOptions::class.vars().associateBy { it.name }
val fieldMap = params.associate { (key, _) -> key.name to fields[key.name]!! }
val fieldDesc = params.map { (key, an) -> mapOf(
"key" to key.name,
"name" to an.name,
"desc" to an.desc
) }
@API("get")
@Doc("Get the game options of the logged in user")
fun getSettings(@RP token: String) = us.jwt.auth(token) { u ->
val go = u.gameOptions ?: AquaGameOptions()
fieldDesc.map { it + ("value" to fieldMap[it["key"]]!!.get(go)) }
}
@API("set")
@Doc("Set a field in the game options")
fun setField(@RP token: String, @RP key: String, value: String) = us.jwt.auth(token) { u ->
val field = fieldMap[key] ?: error("Field not found")
val options = u.gameOptions ?: AquaGameOptions().also {
userRepo.save(u.apply { gameOptions = it })
}
// Check field type
val type = field.returnType
val newValue = when {
type == String::class -> value
type == Int::class -> value.toInt()
type == Boolean::class -> value.toBoolean()
else -> error("Unsupported type")
}
field.set(options, newValue)
}
}

View File

@ -16,20 +16,20 @@ class AquaGameOptions(
@SettingField("Unlock All Music",
"Unlock all music and master difficulty in game")
val unlockMusic: Boolean = false,
var unlockMusic: Boolean = false,
@SettingField("Unlock All Chara",
"Unlock all characters and partners in game")
val unlockChara: Boolean = false,
var unlockChara: Boolean = false,
@SettingField("Unlock All Collectables",
"Unlock all collectables (nameplate, title, icon, frame) in game. " +
"This setting is not relevant in chusan because in-game user box is disabled in chusan.")
val unlockCollectables: Boolean = false,
var unlockCollectables: Boolean = false,
@SettingField("Unlock All Tickets" ,
"Unlock all map tickets (the game still limits which tickets can be used)")
val unlockTickets: Boolean = false,
var unlockTickets: Boolean = false,
)
interface AquaGameOptionsRepository : JpaRepository<AquaGameOptions, Long>