Merge remote-tracking branch 'origin/v1-dev' into delete-account

This commit is contained in:
Azalea
2026-08-02 05:31:55 +00:00
33 changed files with 169 additions and 137 deletions

8
.dockerignore Normal file
View File

@@ -0,0 +1,8 @@
*
!gradlew
!gradle/
!gradle/**
!build.gradle.kts
!settings.gradle.kts
!src/
!src/**

View File

@@ -12,9 +12,9 @@ jobs:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '21'
java-version: '25'
distribution: 'temurin'
- name: Build with Gradle

View File

@@ -17,9 +17,9 @@ jobs:
fetch-depth: '10'
- name: Set up JDK
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '17'
java-version: '25'
distribution: 'temurin'
server-id: github

View File

@@ -1,7 +1,6 @@
# Use a multi-stage build to keep the image size small
# Start with a Gradle image for building the project
#FROM gradle:jdk21-alpine as builder
FROM gradle:8.8.0-jdk21 as builder
# Pin the builder so ARM64 deployments cannot reuse an older floating jdk25 image.
FROM gradle:9.6.1-jdk25 AS builder
# Copy the Gradle wrapper and configuration files separately to leverage Docker cache
COPY --chown=gradle:gradle gradlew /home/gradle/
@@ -16,6 +15,7 @@ RUN sed -i 's/\r$//' ./gradlew
# Download dependencies - cached if build.gradle.kts and settings.gradle.kts are unchanged
RUN chmod +x ./gradlew
RUN java -version
RUN ./gradlew dependencies
# Copy the project source, this layer is rebuilt whenever a file has changed
@@ -25,7 +25,7 @@ COPY --chown=gradle:gradle src /home/gradle/src
RUN ./gradlew build -x test
# Start with a fresh image for the runtime
FROM eclipse-temurin:21-jre-alpine
FROM eclipse-temurin:25-jre-alpine
# Set the deployment directory
WORKDIR /app

View File

@@ -2,9 +2,10 @@
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
val ktVer = "2.1.10"
val ktVer = "2.4.0"
java
kotlin("jvm") version ktVer
@@ -13,9 +14,9 @@ plugins {
kotlin("plugin.serialization") version ktVer
kotlin("plugin.allopen") version ktVer
kotlin("kapt") version ktVer
id("org.springframework.boot") version "3.2.3"
id("com.github.ben-manes.versions") version "0.51.0"
id("org.hibernate.orm") version "6.4.4.Final"
id("org.springframework.boot") version "4.1.0"
id("com.github.ben-manes.versions") version "0.54.0"
id("org.hibernate.orm") version "7.4.4.Final"
application
}
@@ -26,6 +27,9 @@ repositories {
mavenCentral()
}
extra["netty.version"] = "4.2.16.Final"
extra["kotlin-coroutines.version"] = "1.11.0"
dependencies {
// Spring boot
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
@@ -34,28 +38,30 @@ dependencies {
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
}
implementation("org.springframework.boot:spring-boot-starter-jetty")
implementation("io.netty:netty-all")
implementation("org.apache.commons:commons-lang3:3.14.0")
implementation("org.apache.httpcomponents.client5:httpclient5")
implementation("org.flywaydb:flyway-core:10.10.0")
implementation("org.flywaydb:flyway-mysql:10.10.0")
implementation(enforcedPlatform("io.netty:netty-bom:4.2.16.Final"))
implementation("io.netty:netty-all:4.2.16.Final")
implementation("org.apache.commons:commons-lang3:3.20.0")
implementation("org.apache.httpcomponents.client5:httpclient5:5.6.2")
implementation("org.springframework.boot:spring-boot-starter-flyway")
implementation("org.flywaydb:flyway-core:12.10.0")
implementation("org.flywaydb:flyway-mysql:12.10.0")
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
testImplementation("org.springframework.security:spring-security-test")
implementation("net.logstash.logback:logstash-logback-encoder:7.4")
implementation("net.logstash.logback:logstash-logback-encoder:9.0")
// Metrics
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("io.micrometer:micrometer-registry-prometheus")
// Database
runtimeOnly("org.mariadb.jdbc:mariadb-java-client:3.3.3")
runtimeOnly("org.xerial:sqlite-jdbc:3.45.2.0")
implementation("org.hibernate.orm:hibernate-core:6.4.4.Final")
implementation("org.hibernate.orm:hibernate-community-dialects:6.4.4.Final")
implementation("io.github.openfeign.querydsl:querydsl-jpa:6.10.1")
kapt("io.github.openfeign.querydsl:querydsl-apt:6.10.1:jpa")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client:3.5.9")
runtimeOnly("org.xerial:sqlite-jdbc:3.53.2.0")
implementation("org.hibernate.orm:hibernate-core:7.4.4.Final")
implementation("org.hibernate.orm:hibernate-community-dialects:7.4.4.Final")
implementation("io.github.openfeign.querydsl:querydsl-jpa:7.4.0")
kapt("io.github.openfeign.querydsl:querydsl-apt:7.4.0:jpa")
// JSR305 for nullable
implementation("com.google.code.findbugs:jsr305:3.0.2")
@@ -65,50 +71,60 @@ dependencies {
// =============================
// Network
implementation("io.ktor:ktor-client-core:3.0.3")
implementation("io.ktor:ktor-client-cio:3.0.3")
implementation("io.ktor:ktor-client-content-negotiation:3.0.3")
implementation("io.ktor:ktor-client-encoding:3.0.3")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.0.3")
implementation("io.ktor:ktor-client-core:3.5.1")
implementation("io.ktor:ktor-client-cio:3.5.1")
implementation("io.ktor:ktor-client-content-negotiation:3.5.1")
implementation("io.ktor:ktor-client-encoding:3.5.1")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.5.1")
implementation("org.jetbrains.kotlin:kotlin-reflect")
// Somehow these are needed for ktor even though they're not in the documentation
runtimeOnly("org.reactivestreams:reactive-streams:1.0.4")
runtimeOnly("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.8.0")
runtimeOnly("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.11.0")
// Email
implementation("org.simplejavamail:simple-java-mail:8.6.3")
implementation("org.simplejavamail:spring-module:8.6.3")
implementation("org.simplejavamail:simple-java-mail:9.0.1")
implementation("org.simplejavamail:spring-module:9.0.1")
// GeoIP
implementation("com.maxmind.geoip2:geoip2:4.2.0")
implementation("com.maxmind.geoip2:geoip2:5.1.0")
// JWT Authentication
implementation("io.jsonwebtoken:jjwt-api:0.12.5")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.5")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.12.5")
implementation("io.jsonwebtoken:jjwt-api:0.13.0")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.13.0")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.13.0")
// Content validation
implementation("org.apache.tika:tika-core:2.9.1")
implementation("org.apache.tika:tika-core:3.3.1")
// Import: DateTime Parsing
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.0")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.22.1")
// Serialization
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.22.1")
// Testing
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.8.1")
testImplementation("io.kotest:kotest-assertions-core")
testImplementation("io.kotest:kotest-runner-junit5-jvm:6.2.2")
testImplementation("io.kotest:kotest-assertions-core:6.2.2")
}
group = "icu.samnya"
version = "1.0.0"
description = "AquaDX Arcade Server"
java.sourceCompatibility = JavaVersion.VERSION_21
java {
sourceCompatibility = JavaVersion.VERSION_25
targetCompatibility = JavaVersion.VERSION_25
toolchain {
languageVersion.set(JavaLanguageVersion.of(25))
}
}
kotlin {
jvmToolchain(21)
jvmToolchain(25)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_25)
}
}
springBoot {
@@ -119,14 +135,6 @@ application {
mainClass = "icu.samnyan.aqua.EntryKt"
}
hibernate {
enhancement {
enableLazyInitialization = true
enableAssociationManagement = false
enableExtendedEnhancement = false
}
}
kapt {
includeCompileClasspath = false
keepJavacAnnotationProcessors = true
@@ -138,29 +146,36 @@ allOpen {
annotation("jakarta.persistence.Embeddable")
}
val buildTime: String by extra(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z").withZone(ZoneId.of("UTC")).format(Instant.now()))
val buildTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z").withZone(ZoneId.of("UTC")).format(Instant.now())
val projectVersion = version.toString()
extra["buildTime"] = buildTime
tasks.processResources {
filesMatching("**/application.properties") {
expand(project.properties)
expand(
mapOf(
"version" to projectVersion,
"ext" to mapOf("buildTime" to buildTime),
)
)
}
}
tasks.test {
enabled = project.hasProperty("runTests")
enabled = providers.gradleProperty("runTests").isPresent
useJUnitPlatform()
jvmArgs("-Dkotest.assertions.collection.print.size=100")
}
tasks.withType<JavaCompile> {
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
}
tasks.withType<Javadoc> {
tasks.withType<Javadoc>().configureEach {
options.encoding = "UTF-8"
}
tasks.getByName<Jar>("jar") {
tasks.named<Jar>("jar") {
enabled = false
}
@@ -170,12 +185,12 @@ sourceSets {
}
}
val copyDependencies by tasks.registering(Copy::class) {
val copyDependencies = tasks.register<Copy>("copyDependencies") {
from(configurations.runtimeClasspath)
into("${layout.buildDirectory.get()}/libs/lib")
}
val packageThin by tasks.registering(Jar::class) {
tasks.register<Jar>("packageThin") {
group = "build"
from(sourceSets.main.get().output)
manifest {

View File

@@ -1,7 +1,7 @@
FROM archlinux:latest
RUN pacman -Syu --noconfirm \
&& pacman -S --noconfirm openssh sudo jdk21-openjdk wget which procps-ng zsh git curlie micro ripgrep python3 exa \
&& pacman -S --noconfirm openssh sudo jdk25-openjdk wget which procps-ng zsh git curlie micro ripgrep python3 exa \
&& rm -rf /var/cache/pacman/pkg/* \
&& mkdir -p /var/run/sshd \
&& chsh -s /bin/zsh root \

View File

@@ -41,10 +41,10 @@ docker compose up
```
### Building
You need to install JDK 21 on your system, then run `./gradlew clean build`. The jar file will be built into the `build/libs` folder.
You need to install JDK 25 on your system, then run `./gradlew clean build`. The jar file will be built into the `build/libs` folder.
## Why drop SQLite support?
If you wonder why I dropped SQLite support, ask SQLite devs why they still haven't supported adding a single constraint to a table without all the hassle of creating a new one and migrating all data over and finally deleting the original.
![](sqlite-sucks.png)
![](sqlite-sucks.png)

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@@ -2,4 +2,8 @@
* This file was generated by the Gradle 'init' task.
*/
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
rootProject.name = "AquaDX"

View File

@@ -277,4 +277,4 @@ fun List<List<Any?>>.numCsv(vararg head: Str) = head.joinToString(",") + "\n" +
joinToString("\n") { it.joinToString(",") }
// DI
inline fun <reified T> ApplicationContext.lazy() = lazy { getBean(T::class.java) }
inline fun <reified T : Any> ApplicationContext.lazy() = kotlin.lazy { getBean(T::class.java) }

View File

@@ -32,7 +32,6 @@ val JSON_DATETIME = SimpleModule().addDeserializer(java.time.LocalDateTime::clas
} }
})
val JACKSON = jacksonObjectMapper().apply {
setSerializationInclusion(JsonInclude.Include.NON_NULL)
setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL)
findAndRegisterModules()
registerModule(JSON_FUZZY_BOOLEAN)

View File

@@ -68,7 +68,7 @@ class CardController(
*/
@API("/link")
@Doc("Bind a card to the user. This action will migrate selected data from the card to the user's ghost card.", "Success message")
suspend fun link(@RP token: Str, @RP cardId: Str, @RP migrate: Str) = jwt.auth(token) { u ->
suspend fun link(@RP token: Str, @RP cardId: Str, @RP migrate: Str): Any = jwt.auth(token) { u ->
// Check if the user's card limit is reached
if (u.cards.size >= props.linkCardLimit) 400 - "Card limit reached"

View File

@@ -154,7 +154,7 @@ class Fedy(
forEach { (k, v) -> v?.let { GAME_OPTIONS_FIELDS[k]?.set(options, it) } }
}
us.userRepo.save(ru)
if (fields.containsKey("pwHash") ?: false) { us.clearAllSessions(ru) }
if (fields.containsKey("pwHash")) { us.clearAllSessions(ru) }
UserUpdateRes(user = ru.fedyBasicInfo())
} caught { UserUpdateRes(error = it) }
}

View File

@@ -1,12 +1,16 @@
package icu.samnyan.aqua.net.components
import jakarta.mail.Message
import ext.Bool
import ext.Str
import ext.logger
import icu.samnyan.aqua.net.db.*
import org.simplejavamail.api.email.Recipient
import org.simplejavamail.api.mailer.Mailer
import org.simplejavamail.email.EmailBuilder
import org.simplejavamail.springsupport.SimpleJavaMailSpringSupport
import org.springframework.beans.factory.ObjectProvider
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.event.ApplicationStartedEvent
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration
@@ -25,15 +29,19 @@ class EmailProperties {
var webHost: Str = "aquadx.net"
}
@Configuration
@ConditionalOnProperty(prefix = "aqua-net.email", name = ["enable"], havingValue = "true")
@Import(SimpleJavaMailSpringSupport::class)
class EmailMailerConfiguration
/**
* Email service. All email related operations should be placed here.
*
* Library Documentation: https://www.simplejavamail.org/
*/
@Service
@Import(SimpleJavaMailSpringSupport::class)
class EmailService(
val mailer: Mailer,
val mailerProvider: ObjectProvider<Mailer>,
val props: EmailProperties,
val confirmationRepo: EmailConfirmationRepo,
val resetPasswordRepo: ResetPasswordRepo,
@@ -44,13 +52,16 @@ class EmailService(
val resetTemplate: Str = this::class.java.getResource("/email/reset.html")?.readText()
?: throw Exception("Password Reset Template Not Found")
private fun mailer() = mailerProvider.getIfAvailable()
?: throw IllegalStateException("Email is enabled, but Simple Java Mail is not configured")
@Async
@EventListener(ApplicationStartedEvent::class)
fun test() {
if (!props.enable) return
try {
mailer.testConnection()
mailer().testConnection()
log.info("Email Service Connected")
} catch (e: Exception) {
log.error("Email Service Connection Failed", e)
@@ -71,9 +82,9 @@ class EmailService(
// Send email
log.info("Sending verification email to ${user.email}")
mailer.sendMail(EmailBuilder.startingBlank()
mailer().sendMail(EmailBuilder.startingBlank()
.from(props.senderName, props.senderAddr)
.to(user.computedName, user.email)
.withRecipients(Recipient(user.computedName, user.email, Message.RecipientType.TO, null))
.withSubject("Verify Your Email Address for AquaNet")
.withHTMLText(confirmTemplate
.replace("{{name}}", user.computedName)
@@ -94,9 +105,9 @@ class EmailService(
// Send email
log.info("Sending reset password email to ${user.email}")
mailer.sendMail(EmailBuilder.startingBlank()
mailer().sendMail(EmailBuilder.startingBlank()
.from(props.senderName, props.senderAddr)
.to(user.computedName, user.email)
.withRecipients(Recipient(user.computedName, user.email, Message.RecipientType.TO, null))
.withSubject("Reset Your Password for AquaNet")
.withHTMLText(resetTemplate
.replace("{{name}}", user.computedName)
@@ -108,9 +119,9 @@ class EmailService(
if (!props.enable) return
log.info("Sending test email to $addr")
mailer.sendMail(EmailBuilder.startingBlank()
mailer().sendMail(EmailBuilder.startingBlank()
.from(props.senderName, props.senderAddr)
.to(name, addr)
.withRecipients(Recipient(name, addr, Message.RecipientType.TO, null))
.withSubject("Test Email")
.withPlainText("This is a test email to check if AquaNet Email Works").buildEmail()).thenRun {
log.info("Test email sent to $addr")

View File

@@ -77,7 +77,7 @@ class GeoIP(
{
return try {
val ipa = InetAddress.getByName(ip)
geoLite.country(ipa)?.country?.isoCode ?: ""
geoLite.country(ipa)?.country()?.isoCode() ?: ""
}
catch (e: AddressNotFoundException) { "" }
catch (e: Exception) {
@@ -85,4 +85,4 @@ class GeoIP(
""
}
}
}
}

View File

@@ -221,21 +221,23 @@ class AquaUserServices(
400 - "User with username `$this` already exists"
}
fun validateEmail(email: Str) = email.apply {
// Check if email is valid
if (!isValidEmail()) 400 - "Invalid email"
// Check if user with the same email exists
if (userRepo.findByEmailIgnoreCase(email) != null)
400 - "User with email `$email` already exists"
}
fun checkPwHash(password: Str) = password.run {
// Validate password
if (length < 8) 400 - "Password must be at least 8 characters"
hasher.encode(this)
}
fun validateEmail(email: Str): Str {
// Check if email is valid
if (!email.isValidEmail()) 400 - "Invalid email"
// Check if user with the same email exists
if (userRepo.findByEmailIgnoreCase(email) != null)
400 - "User with email `$email` already exists"
return email
}
fun checkPwHash(password: Str): Str {
// Validate password
if (password.length < 8) 400 - "Password must be at least 8 characters"
return hasher.encode(password) ?: (500 - "Failed to hash password")
}
fun checkDisplayName(displayName: Str) = displayName.apply {
// Check if display name is valid

View File

@@ -41,7 +41,7 @@ interface IExportClass<UserModel: IUserData> {
}
@NoRepositoryBean
interface IUserRepo<UserModel, ThisModel>: JpaRepository<ThisModel, Long> {
interface IUserRepo<UserModel : IUserData, ThisModel : Any>: JpaRepository<ThisModel, Long> {
fun findByUser(user: UserModel): List<ThisModel>
fun findSingleByUser(user: UserModel): ThisModel?
}

View File

@@ -96,7 +96,6 @@ class AimeDB(
}
}
@Deprecated("Deprecated in Netty 5") // TODO: Move this to ChannelInboundHandler
override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
logger.error("AimeDB: Error", cause)
ctx.close()

View File

@@ -4,7 +4,8 @@ import ext.logger
import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.MultiThreadIoEventLoopGroup
import io.netty.channel.nio.NioIoHandler
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioServerSocketChannel
import io.netty.handler.logging.LogLevel
@@ -38,7 +39,10 @@ class AimeDbServer(
if (!props.enable) return logger.info("Aime DB is disabled.")
val bootstrap = ServerBootstrap()
.group(NioEventLoopGroup(), NioEventLoopGroup())
.group(
MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()),
MultiThreadIoEventLoopGroup(NioIoHandler.newFactory())
)
.handler(LoggingHandler(LogLevel.DEBUG))
.channel(NioServerSocketChannel::class.java)
.childHandler(initializer)

View File

@@ -11,8 +11,8 @@ import org.eclipse.jetty.util.resource.URLResourceFactory
import org.eclipse.jetty.util.ssl.SslContextFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory
import org.springframework.boot.jetty.JettyServerCustomizer
import org.springframework.boot.jetty.servlet.JettyServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

View File

@@ -18,7 +18,7 @@ import icu.samnyan.aqua.sega.util.StaticRepo
@NoRepositoryBean
interface Chu3UserLinked<T> : IUserRepo<Chu3UserData, T> {
interface Chu3UserLinked<T : Any> : IUserRepo<Chu3UserData, T> {
fun findByUser_Card_ExtId(extId: Long): List<T>
fun findSingleByUser_Card_ExtId(extId: Long): T?
fun findByUser_Card_ExtId(extId: Long, pageable: Pageable): Page<T>

View File

@@ -60,9 +60,9 @@ class PsRankingHandler(val db: DivaRepos) {
score1.add(obj.first.maxScore)
score2.add(obj.second.maxScore)
score3.add(obj.third.maxScore)
name1.add(encode(obj.first.pdId?.playerName ?: "xxx"))
name2.add(encode(obj.second.pdId?.playerName ?: "xxx"))
name3.add(encode(obj.third.pdId?.playerName ?: "xxx"))
name1.add(encode(obj.first.pdId.playerName))
name2.add(encode(obj.second.pdId.playerName))
name3.add(encode(obj.third.pdId.playerName))
}
return PsRankingResponse(

View File

@@ -9,7 +9,6 @@ import icu.samnyan.aqua.sega.diva.model.db.userdata.*
import icu.samnyan.aqua.sega.diva.util.DivaCalculator
import org.apache.commons.lang3.StringUtils
import org.springframework.stereotype.Component
import java.lang.String
import java.time.LocalDateTime
import java.util.*
import java.util.function.Supplier
@@ -100,7 +99,7 @@ class StageResultHandler(val db: DivaRepos, val calc: DivaCalculator) {
// Calculate reward
// Contest reward
var contestSpecifier = String.join(",", *request.cr_sp)
var contestSpecifier = request.cr_sp.joinToString(",")
val contestRewardType = arrayOf<kotlin.String?>("-1", "-1", "-1")
val contestRewardValue = arrayOf<kotlin.String?>("-1", "-1", "-1")
val contestRewardString1 = arrayOf<kotlin.String?>("***", "***", "***")
@@ -204,10 +203,10 @@ class StageResultHandler(val db: DivaRepos, val calc: DivaCalculator) {
request.cr_cid,
request.cr_tv,
contestSpecifier,
String.join(",", *contestRewardType),
String.join(",", *contestRewardValue),
String.join(",", *contestRewardString1),
String.join(",", *contestRewardString2),
contestRewardType.joinToString(","),
contestRewardValue.joinToString(","),
contestRewardString1.joinToString(","),
contestRewardString2.joinToString(","),
contestEntryRewardType,
contestEntryRewardValue,
contestEntryRewardString1,
@@ -328,7 +327,7 @@ class StageResultHandler(val db: DivaRepos, val calc: DivaCalculator) {
while (result.size < 60) {
result.add("-1")
}
return String.join(",", result)
return result.joinToString(",")
}
private fun updateReward(

View File

@@ -11,7 +11,6 @@ import icu.samnyan.aqua.sega.diva.model.db.gamedata.Contest
import icu.samnyan.aqua.sega.diva.model.db.userdata.PlayerContest
import icu.samnyan.aqua.sega.diva.util.DivaStringUtils
import org.springframework.stereotype.Component
import java.lang.String
import java.time.LocalDateTime
import java.util.function.Supplier
import kotlin.Any
@@ -47,7 +46,7 @@ class EndHandler(val db: DivaRepos) {
profile.contestNowPlayingId = request.cr_cid
profile.contestNowPlayingResultRank = currentResultRank
profile.contestNowPlayingValue = request.cr_tv
profile.contestNowPlayingSpecifier = String.join(",", *request.cr_sp)
profile.contestNowPlayingSpecifier = request.cr_sp.joinToString(",")
} else {
val contestRecord =
db.contest.findByPdIdAndContestId(profile, request.cr_cid).orElseGet(

View File

@@ -85,7 +85,7 @@ class Contest : Serializable {
}
list += listOf(-1, -2, "7fffffffffffffffffffffffffffffff")
} else {
val groups = pvl!!.split(',').dropLastWhile { it.isEmpty() }.toTypedArray()
val groups = pvl.split(',').dropLastWhile { it.isEmpty() }.toTypedArray()
if (groups.size < i) {
list += listOf(-1, -1, -2, -2, -1, -2, "7fffffffffffffffffffffffffffffff")
} else {
@@ -98,7 +98,7 @@ class Contest : Serializable {
list.add(this.maxComplexity)
list.add(-1)
} else {
val diffList = pvdl!!.split(',').dropLastWhile { it.isEmpty() }.toTypedArray()
val diffList = pvdl.split(',').dropLastWhile { it.isEmpty() }.toTypedArray()
if (diffList.size < i) {
list.add(this.minComplexity)
list.add(this.maxComplexity)

View File

@@ -20,7 +20,7 @@ import icu.samnyan.aqua.sega.util.StaticRepo
import java.util.*
@NoRepositoryBean
interface Mai2UserLinked<T>: JpaRepository<T, Long>, IUserRepo<Mai2UserDetail, T> {
interface Mai2UserLinked<T : Any>: JpaRepository<T, Long>, IUserRepo<Mai2UserDetail, T> {
fun findByUser_Card_ExtId(userId: Long): List<T>
fun findByUser_Card_ExtId(userId: Long, page: Pageable): Page<T>
fun findSingleByUser_Card_ExtId(userId: Long): T?

View File

@@ -18,7 +18,7 @@ import java.util.*
@NoRepositoryBean
interface OngekiUserLinked<T> : IUserRepo<UserData, T> {
interface OngekiUserLinked<T : Any> : IUserRepo<UserData, T> {
fun findByUser_Card_ExtId(extId: Long): List<T>
fun findSingleByUser_Card_ExtId(extId: Long): T?
fun findByUser_Card_ExtId(extId: Long, pageable: Pageable): Page<T>
@@ -221,4 +221,3 @@ class OngekiRepos(
val u: OngekiUserRepos,
val g: OngekiGameRepos,
)

View File

@@ -14,9 +14,9 @@ interface WcUserRepo : JpaRepository<WaccaUser, Long>, GenericUserDataRepo<Wacca
}
@NoRepositoryBean
interface IWaccaUserLinked<T> : JpaRepository<T, Long> {
interface IWaccaUserLinked<T : Any> : JpaRepository<T, Long> {
fun findByUser(user: WaccaUser): List<T>
fun findByUserCardExtId(userId: Long): List<T>
fun findByUserCardExtId(extId: Long): List<T>
@Transactional
fun deleteByUser(user: WaccaUser)
}
@@ -54,4 +54,4 @@ class WaccaRepos(
val bestScore: WcUserBestScoreRepo,
val playLog: WcUserPlayLogRepo,
val stageUp: WcUserStageUpRepo
)
)

View File

@@ -33,7 +33,6 @@ class WaccaUser : BaseEntity(), IUserData {
var titles: MutableList<Int> = mutableListOf(0, 0, 0)
override var playerRating = 0
override var highestRating = 0
@Temporal(TemporalType.TIMESTAMP)
var vipExpireTime: Date = Date(0)
var alwaysVip = false
var loginCount = 0
@@ -50,11 +49,8 @@ class WaccaUser : BaseEntity(), IUserData {
override var lastRomVersion = "1.0.0"
@Convert(converter = IntegerListConverter::class)
var lastSongInfo: MutableList<Int> = mutableListOf(0, 0, 0, 0, 0)
@Temporal(TemporalType.TIMESTAMP)
var lastConsecDate: Date = Date(0)
@Temporal(TemporalType.TIMESTAMP)
override var lastPlayDate: Date = Date()
@Temporal(TemporalType.TIMESTAMP)
override var firstPlayDate: Date = Date()
var gateTutorialFlags: String = "[[1, 0], [2, 0], [3, 0], [4, 0], [5, 0]]"
@Convert(converter = IntegerListConverter::class)
@@ -71,4 +67,4 @@ class WaccaUser : BaseEntity(), IUserData {
else vipExpireTime
val moddedWp get() = if (card?.aquaUser?.gameOptions?.waccaInfiniteWp == true) 999999 else wp
}
}

View File

@@ -59,7 +59,6 @@ class WcUserGate : WaccaUserEntity() {
var progress = 0
var loops = 0
@Temporal(TemporalType.TIMESTAMP)
var lastUsed = Date(0)
var missionFlag = 0
var totalPoints = 0
@@ -77,7 +76,6 @@ class WcUserItem(
var p2: Long = 0L,
var p3: Long = 0L,
@Temporal(TemporalType.TIMESTAMP)
var acquiredDate: Date = Date(),
) : WaccaUserEntity() {
fun ls() = when (type) {
@@ -136,7 +134,6 @@ class WcUserPlayLog : WaccaUserEntity(), IGenericGamePlaylog {
override var beforeRating = 0
override var afterRating = 0
@Temporal(TemporalType.TIMESTAMP)
override var userPlayDate = Date()
fun clears() = ls(1, +isClear, +isFullCombo, +isMissless, +isAllPerfect)

View File

@@ -4,8 +4,8 @@ import icu.samnyan.aqua.sega.aimedb.AimeDbProps
import icu.samnyan.aqua.sega.allnet.AllNetProps
import org.apache.hc.client5.http.impl.classic.HttpClients
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder
import org.apache.hc.client5.http.ssl.TrustAllStrategy
import org.apache.hc.core5.ssl.SSLContextBuilder
import org.springframework.beans.factory.annotation.Value
@@ -71,11 +71,11 @@ class AutoChecker(
val rt = HttpComponentsClientHttpRequestFactory().apply {
httpClient = HttpClients.custom()
.setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(SSLConnectionSocketFactoryBuilder.create()
.setTlsSocketStrategy(ClientTlsStrategyBuilder.create()
.setHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.setSslContext(SSLContextBuilder.create()
.loadTrustMaterial(TrustAllStrategy.INSTANCE)
.build()).build()).build()).build()
.build()).buildClassic()).build()).build()
}.let { RestTemplate(it) }
val url = "https://${aimedb.address}:$BILLING_PORT/sys/test"

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="cmp" converterClass="icu.samnyan.aqua.spring.LoggerComponent" />
<conversionRule conversionWord="cls" converterClass="icu.samnyan.aqua.spring.LoggerClassColor" />
<conversionRule conversionWord="correlationId" converterClass="org.springframework.boot.logging.logback.CorrelationIdConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="clr" class="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="cmp" class="icu.samnyan.aqua.spring.LoggerComponent" />
<conversionRule conversionWord="cls" class="icu.samnyan.aqua.spring.LoggerClassColor" />
<conversionRule conversionWord="correlationId" class="org.springframework.boot.logging.logback.CorrelationIdConverter" />
<conversionRule conversionWord="wex" class="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" class="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- Define the log file name and path -->
<property name="LOG_FILE" value="logs/AquaDX.log"/>