From ca28d32aa96a45a295170f2a7199deaaeeb559e2 Mon Sep 17 00:00:00 2001 From: William Brawner Date: Mon, 16 Aug 2021 15:25:32 -0600 Subject: [PATCH] Minor fixes for web and mobile --- .../kotlin/com/wbrawner/twigs/ApiUtils.kt | 2 +- .../kotlin/com/wbrawner/twigs/BudgetRoutes.kt | 2 +- .../com/wbrawner/twigs/CategoryRoutes.kt | 2 +- .../com/wbrawner/twigs/TransactionRoutes.kt | 2 +- .../main/kotlin/com/wbrawner/twigs/UserApi.kt | 2 +- .../kotlin/com/wbrawner/twigs/UserRoutes.kt | 2 +- app/build.gradle.kts | 51 +++++++++++++++++++ .../com/wbrawner/twigs/server/Application.kt | 22 +++++--- app/src/main/resources/application.conf | 6 +++ .../com/wbrawner/twigs/model}/Session.kt | 2 +- .../com/wbrawner/twigs/db/DatabaseMetadata.kt | 4 +- .../twigs/db/JdbcCategoryRepository.kt | 1 + .../twigs/db/JdbcSessionRepository.kt | 2 +- .../twigs/storage/SessionRepository.kt | 2 + web/build.gradle.kts | 36 +++++++++++++ 15 files changed, 121 insertions(+), 17 deletions(-) rename {storage/src/main/kotlin/com/wbrawner/twigs/storage => core/src/main/kotlin/com/wbrawner/twigs/model}/Session.kt (91%) diff --git a/api/src/main/kotlin/com/wbrawner/twigs/ApiUtils.kt b/api/src/main/kotlin/com/wbrawner/twigs/ApiUtils.kt index ef3ecbf..a364dd4 100644 --- a/api/src/main/kotlin/com/wbrawner/twigs/ApiUtils.kt +++ b/api/src/main/kotlin/com/wbrawner/twigs/ApiUtils.kt @@ -2,9 +2,9 @@ package com.wbrawner.twigs import com.wbrawner.twigs.model.Budget import com.wbrawner.twigs.model.Permission +import com.wbrawner.twigs.model.Session import com.wbrawner.twigs.storage.BudgetRepository import com.wbrawner.twigs.storage.PermissionRepository -import com.wbrawner.twigs.storage.Session import io.ktor.application.* import io.ktor.auth.* import io.ktor.http.* diff --git a/api/src/main/kotlin/com/wbrawner/twigs/BudgetRoutes.kt b/api/src/main/kotlin/com/wbrawner/twigs/BudgetRoutes.kt index 16f0db3..277b943 100644 --- a/api/src/main/kotlin/com/wbrawner/twigs/BudgetRoutes.kt +++ b/api/src/main/kotlin/com/wbrawner/twigs/BudgetRoutes.kt @@ -2,10 +2,10 @@ package com.wbrawner.twigs import com.wbrawner.twigs.model.Budget import com.wbrawner.twigs.model.Permission +import com.wbrawner.twigs.model.Session import com.wbrawner.twigs.model.UserPermission import com.wbrawner.twigs.storage.BudgetRepository import com.wbrawner.twigs.storage.PermissionRepository -import com.wbrawner.twigs.storage.Session import io.ktor.application.* import io.ktor.auth.* import io.ktor.http.* diff --git a/api/src/main/kotlin/com/wbrawner/twigs/CategoryRoutes.kt b/api/src/main/kotlin/com/wbrawner/twigs/CategoryRoutes.kt index 8318714..e37e469 100644 --- a/api/src/main/kotlin/com/wbrawner/twigs/CategoryRoutes.kt +++ b/api/src/main/kotlin/com/wbrawner/twigs/CategoryRoutes.kt @@ -2,9 +2,9 @@ package com.wbrawner.twigs import com.wbrawner.twigs.model.Category import com.wbrawner.twigs.model.Permission +import com.wbrawner.twigs.model.Session import com.wbrawner.twigs.storage.CategoryRepository import com.wbrawner.twigs.storage.PermissionRepository -import com.wbrawner.twigs.storage.Session import io.ktor.application.* import io.ktor.auth.* import io.ktor.http.* diff --git a/api/src/main/kotlin/com/wbrawner/twigs/TransactionRoutes.kt b/api/src/main/kotlin/com/wbrawner/twigs/TransactionRoutes.kt index d9e6c78..4d1cb66 100644 --- a/api/src/main/kotlin/com/wbrawner/twigs/TransactionRoutes.kt +++ b/api/src/main/kotlin/com/wbrawner/twigs/TransactionRoutes.kt @@ -1,9 +1,9 @@ package com.wbrawner.twigs import com.wbrawner.twigs.model.Permission +import com.wbrawner.twigs.model.Session import com.wbrawner.twigs.model.Transaction import com.wbrawner.twigs.storage.PermissionRepository -import com.wbrawner.twigs.storage.Session import com.wbrawner.twigs.storage.TransactionRepository import io.ktor.application.* import io.ktor.auth.* diff --git a/api/src/main/kotlin/com/wbrawner/twigs/UserApi.kt b/api/src/main/kotlin/com/wbrawner/twigs/UserApi.kt index 4c6f8c1..872f272 100644 --- a/api/src/main/kotlin/com/wbrawner/twigs/UserApi.kt +++ b/api/src/main/kotlin/com/wbrawner/twigs/UserApi.kt @@ -1,8 +1,8 @@ package com.wbrawner.twigs import com.wbrawner.twigs.model.Permission +import com.wbrawner.twigs.model.Session import com.wbrawner.twigs.model.User -import com.wbrawner.twigs.storage.Session import kotlinx.serialization.Serializable import java.util.* diff --git a/api/src/main/kotlin/com/wbrawner/twigs/UserRoutes.kt b/api/src/main/kotlin/com/wbrawner/twigs/UserRoutes.kt index 31c01f8..9e53aa9 100644 --- a/api/src/main/kotlin/com/wbrawner/twigs/UserRoutes.kt +++ b/api/src/main/kotlin/com/wbrawner/twigs/UserRoutes.kt @@ -1,8 +1,8 @@ package com.wbrawner.twigs +import com.wbrawner.twigs.model.Session import com.wbrawner.twigs.model.User import com.wbrawner.twigs.storage.PermissionRepository -import com.wbrawner.twigs.storage.Session import com.wbrawner.twigs.storage.SessionRepository import com.wbrawner.twigs.storage.UserRepository import io.ktor.application.* diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 64a137b..9cab751 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,4 +1,5 @@ import java.net.URI +import java.util.* plugins { java @@ -48,3 +49,53 @@ tasks.shadowJar { archiveVersion.set("") } } + +val captainDefinition = File(project.buildDir, "captain-definition") +val tarFile = File(project.buildDir, "twigs.tar") + +tasks.register("package") { + dependsOn(":app:shadowJar") + doLast { + captainDefinition.createNewFile() + captainDefinition.outputStream().writer().use { + it.appendLine( + """ + { + "schemaVersion": 2, + "dockerfileLines": [ + "FROM adoptopenjdk:openj9", + "COPY libs/twigs.jar twigs.jar", + "CMD /opt/java/openjdk/bin/java ${'$'}JVM_ARGS -jar /twigs.jar" + ] + } + """.trimIndent() + ) + } + exec { + commandLine( + "tar", + "-C", + project.buildDir.absolutePath, + "-cf", + project.buildDir.name + File.separator + tarFile.name, + captainDefinition.name, + "libs/twigs.jar" + ) + } + } +} + +tasks.register("publish") { + dependsOn(":app:package") + doLast { + var command = listOf("caprover", "deploy", "-t", "build/${tarFile.name}", "-n", "wbrawner", "-a", "twigs") + command = if (System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("windows")) { + listOf("powershell", "-Command") + command + } else { + listOf("bash", "-c", "\"${command.joinToString(" ")}\"") + } + exec { + commandLine(command) + } + } +} diff --git a/app/src/main/kotlin/com/wbrawner/twigs/server/Application.kt b/app/src/main/kotlin/com/wbrawner/twigs/server/Application.kt index 66a0ca7..28b9107 100644 --- a/app/src/main/kotlin/com/wbrawner/twigs/server/Application.kt +++ b/app/src/main/kotlin/com/wbrawner/twigs/server/Application.kt @@ -2,6 +2,7 @@ package com.wbrawner.twigs.server import com.wbrawner.twigs.* import com.wbrawner.twigs.db.* +import com.wbrawner.twigs.model.Session import com.wbrawner.twigs.storage.* import com.wbrawner.twigs.web.webRoutes import com.zaxxer.hikari.HikariConfig @@ -27,11 +28,11 @@ private const val DATABASE_VERSION = 1 @ExperimentalTime fun Application.module() { - val dbHost = environment.config.propertyOrNull("ktor.database.host")?.getString() ?: "localhost" - val dbPort = environment.config.propertyOrNull("ktor.database.port")?.getString() ?: "5432" - val dbName = environment.config.propertyOrNull("ktor.database.name")?.getString() ?: "twigs" - val dbUser = environment.config.propertyOrNull("ktor.database.user")?.getString() ?: "twigs" - val dbPass = environment.config.propertyOrNull("ktor.database.password")?.getString() ?: "twigs" + val dbHost = environment.config.propertyOrNull("twigs.database.host")?.getString() ?: "localhost" + val dbPort = environment.config.propertyOrNull("twigs.database.port")?.getString() ?: "5432" + val dbName = environment.config.propertyOrNull("twigs.database.name")?.getString() ?: "twigs" + val dbUser = environment.config.propertyOrNull("twigs.database.user")?.getString() ?: "twigs" + val dbPass = environment.config.propertyOrNull("twigs.database.password")?.getString() ?: "twigs" val jdbcUrl = "jdbc:postgresql://$dbHost:$dbPort/$dbName?stringtype=unspecified" HikariDataSource(HikariConfig().apply { setJdbcUrl(jdbcUrl) @@ -119,7 +120,16 @@ fun Application.moduleWithDependencies( metadataRepository.runMigration(version) metadataRepository.save(metadata.copy(version = version)) } - salt = metadata.salt + salt = metadata.salt.ifEmpty { + metadataRepository.save( + metadata.copy( + salt = environment.config + .propertyOrNull("twigs.password.salt") + ?.getString() + ?: randomString(16) + ) + ).salt + } while (currentCoroutineContext().isActive) { delay(Duration.hours(24)) sessionRepository.deleteExpired() diff --git a/app/src/main/resources/application.conf b/app/src/main/resources/application.conf index 10358bc..9007c0f 100644 --- a/app/src/main/resources/application.conf +++ b/app/src/main/resources/application.conf @@ -6,6 +6,9 @@ ktor { application { modules = [ com.wbrawner.twigs.server.ApplicationKt.module ] } +} + +twigs { database { host = localhost host = ${?TWIGS_DB_HOST} @@ -18,4 +21,7 @@ ktor { password = twigs password = ${?TWIGS_DB_PASS} } + password { + salt = ${?TWIGS_PW_SALT} + } } diff --git a/storage/src/main/kotlin/com/wbrawner/twigs/storage/Session.kt b/core/src/main/kotlin/com/wbrawner/twigs/model/Session.kt similarity index 91% rename from storage/src/main/kotlin/com/wbrawner/twigs/storage/Session.kt rename to core/src/main/kotlin/com/wbrawner/twigs/model/Session.kt index d73b905..6e4ea97 100644 --- a/storage/src/main/kotlin/com/wbrawner/twigs/storage/Session.kt +++ b/core/src/main/kotlin/com/wbrawner/twigs/model/Session.kt @@ -1,4 +1,4 @@ -package com.wbrawner.twigs.storage +package com.wbrawner.twigs.model import com.wbrawner.twigs.Identifiable import com.wbrawner.twigs.randomString diff --git a/db/src/main/kotlin/com/wbrawner/twigs/db/DatabaseMetadata.kt b/db/src/main/kotlin/com/wbrawner/twigs/db/DatabaseMetadata.kt index f7f798f..6dc26b6 100644 --- a/db/src/main/kotlin/com/wbrawner/twigs/db/DatabaseMetadata.kt +++ b/db/src/main/kotlin/com/wbrawner/twigs/db/DatabaseMetadata.kt @@ -1,8 +1,6 @@ package com.wbrawner.twigs.db -import com.wbrawner.twigs.randomString - data class DatabaseMetadata( val version: Int = 0, - val salt: String = randomString(16) + val salt: String = "" ) \ No newline at end of file diff --git a/db/src/main/kotlin/com/wbrawner/twigs/db/JdbcCategoryRepository.kt b/db/src/main/kotlin/com/wbrawner/twigs/db/JdbcCategoryRepository.kt index ee18e5a..8ddae96 100644 --- a/db/src/main/kotlin/com/wbrawner/twigs/db/JdbcCategoryRepository.kt +++ b/db/src/main/kotlin/com/wbrawner/twigs/db/JdbcCategoryRepository.kt @@ -36,6 +36,7 @@ class JdbcCategoryRepository(dataSource: DataSource) : sql.append(" AND ${Fields.ARCHIVED.name.lowercase()} = ?") params.add(it) } + sql.append(" ORDER BY ${Fields.TITLE.name.lowercase()} ASC") conn.executeQuery(sql.toString(), params) } diff --git a/db/src/main/kotlin/com/wbrawner/twigs/db/JdbcSessionRepository.kt b/db/src/main/kotlin/com/wbrawner/twigs/db/JdbcSessionRepository.kt index 6b69725..ebe61b9 100644 --- a/db/src/main/kotlin/com/wbrawner/twigs/db/JdbcSessionRepository.kt +++ b/db/src/main/kotlin/com/wbrawner/twigs/db/JdbcSessionRepository.kt @@ -1,6 +1,6 @@ package com.wbrawner.twigs.db -import com.wbrawner.twigs.storage.Session +import com.wbrawner.twigs.model.Session import com.wbrawner.twigs.storage.SessionRepository import java.sql.ResultSet import java.time.Instant diff --git a/storage/src/main/kotlin/com/wbrawner/twigs/storage/SessionRepository.kt b/storage/src/main/kotlin/com/wbrawner/twigs/storage/SessionRepository.kt index c7656e2..de6f763 100644 --- a/storage/src/main/kotlin/com/wbrawner/twigs/storage/SessionRepository.kt +++ b/storage/src/main/kotlin/com/wbrawner/twigs/storage/SessionRepository.kt @@ -1,5 +1,7 @@ package com.wbrawner.twigs.storage +import com.wbrawner.twigs.model.Session + interface SessionRepository : Repository { fun findAll( token: String diff --git a/web/build.gradle.kts b/web/build.gradle.kts index 158a8a6..5c30962 100644 --- a/web/build.gradle.kts +++ b/web/build.gradle.kts @@ -1,3 +1,5 @@ +import java.util.* + plugins { kotlin("jvm") `java-library` @@ -15,3 +17,37 @@ dependencies { tasks.getByName("test") { useJUnitPlatform() } + +// TODO: Replace this hack with either a git submodule or an internal Kotlin-based UI +tasks.register("package") { + doLast { + val built = File(rootProject.rootDir.parent, "twigs-web/dist/twigs") + if (built.exists()) { + built.deleteRecursively() + } + val dest = File(project.projectDir, "src/main/resources/twigs") + if (dest.exists()) { + dest.deleteRecursively() + } + var command = listOf( + "cd", "../../twigs-web", ";", + "npm", "i", ";", + "npm", "run", "package" + ) + command = if (System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("windows")) { + listOf("powershell", "-Command") + command + } else { + listOf("bash", "-c", "\"${command.joinToString(" ")}\"") + } + exec { + commandLine(command) + } + if (!built.copyRecursively(dest, true) || !dest.isDirectory) { + throw GradleException("Failed to copy files from ${built.absolutePath} to ${dest.absolutePath}") + } + } +} + +tasks.getByName("processResources") { + dependsOn.add("package") +}