diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..759858e6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +* +!gradlew +!gradle/ +!gradle/** +!build.gradle.kts +!settings.gradle.kts +!src/ +!src/** diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 395ca244..0b23cb47 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -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 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index ac5312aa..f2490b0a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -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 diff --git a/Dockerfile b/Dockerfile index 19e44a3b..2713f370 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/build.gradle.kts b/build.gradle.kts index 46e9a85c..5cd073fd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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 { +tasks.withType().configureEach { options.encoding = "UTF-8" } -tasks.withType { +tasks.withType().configureEach { options.encoding = "UTF-8" } -tasks.getByName("jar") { +tasks.named("jar") { enabled = false } @@ -170,12 +185,12 @@ sourceSets { } } -val copyDependencies by tasks.registering(Copy::class) { +val copyDependencies = tasks.register("copyDependencies") { from(configurations.runtimeClasspath) into("${layout.buildDirectory.get()}/libs/lib") } -val packageThin by tasks.registering(Jar::class) { +tasks.register("packageThin") { group = "build" from(sourceSets.main.get().output) manifest { diff --git a/config/devtainer/Dockerfile b/config/devtainer/Dockerfile index 7458b109..294bddf2 100644 --- a/config/devtainer/Dockerfile +++ b/config/devtainer/Dockerfile @@ -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 \ diff --git a/docs/self-hosting.md b/docs/self-hosting.md index 057eec5c..bf713176 100644 --- a/docs/self-hosting.md +++ b/docs/self-hosting.md @@ -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) \ No newline at end of file +![](sqlite-sucks.png) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b95..1b33c55b 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a793..a351597e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 diff --git a/settings.gradle.kts b/settings.gradle.kts index 5b0f8437..9b19339f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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" diff --git a/src/main/java/ext/Ext.kt b/src/main/java/ext/Ext.kt index 920c475b..39ab99a8 100644 --- a/src/main/java/ext/Ext.kt +++ b/src/main/java/ext/Ext.kt @@ -277,4 +277,4 @@ fun List>.numCsv(vararg head: Str) = head.joinToString(",") + "\n" + joinToString("\n") { it.joinToString(",") } // DI -inline fun ApplicationContext.lazy() = lazy { getBean(T::class.java) } +inline fun ApplicationContext.lazy() = kotlin.lazy { getBean(T::class.java) } diff --git a/src/main/java/ext/Json.kt b/src/main/java/ext/Json.kt index f7acf3ac..78ceb88e 100644 --- a/src/main/java/ext/Json.kt +++ b/src/main/java/ext/Json.kt @@ -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) diff --git a/src/main/java/icu/samnyan/aqua/net/CardController.kt b/src/main/java/icu/samnyan/aqua/net/CardController.kt index e015eb39..5f50fcb8 100644 --- a/src/main/java/icu/samnyan/aqua/net/CardController.kt +++ b/src/main/java/icu/samnyan/aqua/net/CardController.kt @@ -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" diff --git a/src/main/java/icu/samnyan/aqua/net/Fedy.kt b/src/main/java/icu/samnyan/aqua/net/Fedy.kt index c2f6359a..c865316f 100644 --- a/src/main/java/icu/samnyan/aqua/net/Fedy.kt +++ b/src/main/java/icu/samnyan/aqua/net/Fedy.kt @@ -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) } } diff --git a/src/main/java/icu/samnyan/aqua/net/components/Email.kt b/src/main/java/icu/samnyan/aqua/net/components/Email.kt index ed4b5916..527c95af 100644 --- a/src/main/java/icu/samnyan/aqua/net/components/Email.kt +++ b/src/main/java/icu/samnyan/aqua/net/components/Email.kt @@ -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, 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") diff --git a/src/main/java/icu/samnyan/aqua/net/components/GeoIP.kt b/src/main/java/icu/samnyan/aqua/net/components/GeoIP.kt index c926b14a..133bf5e2 100644 --- a/src/main/java/icu/samnyan/aqua/net/components/GeoIP.kt +++ b/src/main/java/icu/samnyan/aqua/net/components/GeoIP.kt @@ -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( "" } } -} \ No newline at end of file +} 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 f9f3bd23..c80ba967 100644 --- a/src/main/java/icu/samnyan/aqua/net/db/AquaNetUser.kt +++ b/src/main/java/icu/samnyan/aqua/net/db/AquaNetUser.kt @@ -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 diff --git a/src/main/java/icu/samnyan/aqua/net/games/ImportController.kt b/src/main/java/icu/samnyan/aqua/net/games/ImportController.kt index f9163d53..fa5b6aee 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/ImportController.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/ImportController.kt @@ -41,7 +41,7 @@ interface IExportClass { } @NoRepositoryBean -interface IUserRepo: JpaRepository { +interface IUserRepo: JpaRepository { fun findByUser(user: UserModel): List fun findSingleByUser(user: UserModel): ThisModel? } diff --git a/src/main/java/icu/samnyan/aqua/sega/aimedb/AimeDB.kt b/src/main/java/icu/samnyan/aqua/sega/aimedb/AimeDB.kt index 330325c3..f86a131d 100644 --- a/src/main/java/icu/samnyan/aqua/sega/aimedb/AimeDB.kt +++ b/src/main/java/icu/samnyan/aqua/sega/aimedb/AimeDB.kt @@ -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() diff --git a/src/main/java/icu/samnyan/aqua/sega/aimedb/AimeDbServer.kt b/src/main/java/icu/samnyan/aqua/sega/aimedb/AimeDbServer.kt index 9e19ec41..f30a6289 100644 --- a/src/main/java/icu/samnyan/aqua/sega/aimedb/AimeDbServer.kt +++ b/src/main/java/icu/samnyan/aqua/sega/aimedb/AimeDbServer.kt @@ -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) diff --git a/src/main/java/icu/samnyan/aqua/sega/billing/Billing.kt b/src/main/java/icu/samnyan/aqua/sega/billing/Billing.kt index bf12e1cc..a39db6e5 100644 --- a/src/main/java/icu/samnyan/aqua/sega/billing/Billing.kt +++ b/src/main/java/icu/samnyan/aqua/sega/billing/Billing.kt @@ -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 diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/model/Chu3Repos.kt b/src/main/java/icu/samnyan/aqua/sega/chusan/model/Chu3Repos.kt index 8bc14641..cb95b51e 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chusan/model/Chu3Repos.kt +++ b/src/main/java/icu/samnyan/aqua/sega/chusan/model/Chu3Repos.kt @@ -18,7 +18,7 @@ import icu.samnyan.aqua.sega.util.StaticRepo @NoRepositoryBean -interface Chu3UserLinked : IUserRepo { +interface Chu3UserLinked : IUserRepo { fun findByUser_Card_ExtId(extId: Long): List fun findSingleByUser_Card_ExtId(extId: Long): T? fun findByUser_Card_ExtId(extId: Long, pageable: Pageable): Page diff --git a/src/main/java/icu/samnyan/aqua/sega/diva/handler/databank/PsRankingHandler.kt b/src/main/java/icu/samnyan/aqua/sega/diva/handler/databank/PsRankingHandler.kt index 066e63e6..b37f1283 100644 --- a/src/main/java/icu/samnyan/aqua/sega/diva/handler/databank/PsRankingHandler.kt +++ b/src/main/java/icu/samnyan/aqua/sega/diva/handler/databank/PsRankingHandler.kt @@ -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( diff --git a/src/main/java/icu/samnyan/aqua/sega/diva/handler/ingame/StageResultHandler.kt b/src/main/java/icu/samnyan/aqua/sega/diva/handler/ingame/StageResultHandler.kt index 5f79830d..e4c98ba3 100644 --- a/src/main/java/icu/samnyan/aqua/sega/diva/handler/ingame/StageResultHandler.kt +++ b/src/main/java/icu/samnyan/aqua/sega/diva/handler/ingame/StageResultHandler.kt @@ -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("-1", "-1", "-1") val contestRewardValue = arrayOf("-1", "-1", "-1") val contestRewardString1 = arrayOf("***", "***", "***") @@ -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( diff --git a/src/main/java/icu/samnyan/aqua/sega/diva/handler/user/EndHandler.kt b/src/main/java/icu/samnyan/aqua/sega/diva/handler/user/EndHandler.kt index 14d45e86..a4afba57 100644 --- a/src/main/java/icu/samnyan/aqua/sega/diva/handler/user/EndHandler.kt +++ b/src/main/java/icu/samnyan/aqua/sega/diva/handler/user/EndHandler.kt @@ -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( diff --git a/src/main/java/icu/samnyan/aqua/sega/diva/model/db/gamedata/Contest.kt b/src/main/java/icu/samnyan/aqua/sega/diva/model/db/gamedata/Contest.kt index 60e3c306..473cefdd 100644 --- a/src/main/java/icu/samnyan/aqua/sega/diva/model/db/gamedata/Contest.kt +++ b/src/main/java/icu/samnyan/aqua/sega/diva/model/db/gamedata/Contest.kt @@ -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) diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/model/Repos.kt b/src/main/java/icu/samnyan/aqua/sega/maimai2/model/Repos.kt index c278b030..326067a8 100644 --- a/src/main/java/icu/samnyan/aqua/sega/maimai2/model/Repos.kt +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/model/Repos.kt @@ -20,7 +20,7 @@ import icu.samnyan.aqua.sega.util.StaticRepo import java.util.* @NoRepositoryBean -interface Mai2UserLinked: JpaRepository, IUserRepo { +interface Mai2UserLinked: JpaRepository, IUserRepo { fun findByUser_Card_ExtId(userId: Long): List fun findByUser_Card_ExtId(userId: Long, page: Pageable): Page fun findSingleByUser_Card_ExtId(userId: Long): T? diff --git a/src/main/java/icu/samnyan/aqua/sega/ongeki/OngekiRepos.kt b/src/main/java/icu/samnyan/aqua/sega/ongeki/OngekiRepos.kt index e1714871..cdd81796 100644 --- a/src/main/java/icu/samnyan/aqua/sega/ongeki/OngekiRepos.kt +++ b/src/main/java/icu/samnyan/aqua/sega/ongeki/OngekiRepos.kt @@ -18,7 +18,7 @@ import java.util.* @NoRepositoryBean -interface OngekiUserLinked : IUserRepo { +interface OngekiUserLinked : IUserRepo { fun findByUser_Card_ExtId(extId: Long): List fun findSingleByUser_Card_ExtId(extId: Long): T? fun findByUser_Card_ExtId(extId: Long, pageable: Pageable): Page @@ -221,4 +221,3 @@ class OngekiRepos( val u: OngekiUserRepos, val g: OngekiGameRepos, ) - diff --git a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt index ca599394..f6125337 100644 --- a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt +++ b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt @@ -14,9 +14,9 @@ interface WcUserRepo : JpaRepository, GenericUserDataRepo : JpaRepository { +interface IWaccaUserLinked : JpaRepository { fun findByUser(user: WaccaUser): List - fun findByUserCardExtId(userId: Long): List + fun findByUserCardExtId(extId: Long): List @Transactional fun deleteByUser(user: WaccaUser) } @@ -54,4 +54,4 @@ class WaccaRepos( val bestScore: WcUserBestScoreRepo, val playLog: WcUserPlayLogRepo, val stageUp: WcUserStageUpRepo -) \ No newline at end of file +) diff --git a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt index c20e60e6..2003e110 100644 --- a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt +++ b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt @@ -33,7 +33,6 @@ class WaccaUser : BaseEntity(), IUserData { var titles: MutableList = 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 = 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 -} \ No newline at end of file +} diff --git a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt index aa2b5968..e26e3339 100644 --- a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt +++ b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt @@ -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) diff --git a/src/main/java/icu/samnyan/aqua/spring/AutoChecker.kt b/src/main/java/icu/samnyan/aqua/spring/AutoChecker.kt index 45a0757d..7ffd0b90 100644 --- a/src/main/java/icu/samnyan/aqua/spring/AutoChecker.kt +++ b/src/main/java/icu/samnyan/aqua/spring/AutoChecker.kt @@ -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" diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index f908dbba..521ee7e4 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -1,11 +1,11 @@ - - - - - - + + + + + +