diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/GenerateId.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/GenerateId.kt deleted file mode 100644 index d1e72a8..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/GenerateId.kt +++ /dev/null @@ -1,16 +0,0 @@ -package nl.astraeus.vst.chip - -import java.security.SecureRandom - -val idChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" -val random = SecureRandom() - -fun generateId(): String { - val id = StringBuilder() - - for (i in 0 until 8) { - id.append(idChars[random.nextInt(idChars.length)]) - } - - return id.toString() -} diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/Settings.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/Settings.kt deleted file mode 100644 index 9a76c92..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/Settings.kt +++ /dev/null @@ -1,50 +0,0 @@ -package nl.astraeus.vst.chip - -import java.io.File -import java.io.FileInputStream -import java.util.* - -object Settings { - var runningAsRoot: Boolean = false - var port = 9000 - var sslPort = 8443 - var connectionTimeout = 30000 - - var jdbcDriver = "nl.astraeus.jdbc.Driver" - var jdbcConnectionUrl = "jdbc:stat:webServerPort=6001:jdbc:sqlite:data/srp.db" - var jdbcUser = "sa" - var jdbcPassword = "" - - var adminUser = "rnentjes" - var adminPassword = "9/SG_Bd}9gWz~?j\\A.U]n9]OO" - - fun getPropertiesFromFile(filename: String): Properties? { - val propertiesFile = File(filename) - return if (propertiesFile.exists()) { - val properties = Properties() - FileInputStream(propertiesFile).use { - properties.load(it) - } - properties - } else { - null - } - } - - fun readProperties(args: Array) { - val filename = if (args.isNotEmpty()) args[0] else "srp.properties" - val properties = getPropertiesFromFile(filename) ?: return // return if properties couldn't be loaded - - runningAsRoot = properties.getProperty("runningAsRoot", runningAsRoot.toString()).toBoolean() - port = properties.getProperty("port", port.toString()).toInt() - sslPort = properties.getProperty("sslPort", sslPort.toString()).toInt() - connectionTimeout = properties.getProperty("connectionTimeout", connectionTimeout.toString()).toInt() - jdbcDriver = properties.getProperty("jdbcDriver", jdbcDriver) - jdbcConnectionUrl = properties.getProperty("jdbcConnectionUrl", jdbcConnectionUrl) - jdbcUser = properties.getProperty("jdbcUser", jdbcUser) - jdbcPassword = properties.getProperty("jdbcPassword", jdbcPassword) - - adminUser = properties.getProperty("adminUser", adminUser) - adminPassword = properties.getProperty("adminPassword", adminPassword) - } -} diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/BaseDao.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/BaseDao.kt deleted file mode 100644 index 4ea410f..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/BaseDao.kt +++ /dev/null @@ -1,170 +0,0 @@ -package nl.astraeus.vst.chip.db - -import kotlinx.datetime.Instant -import nl.astraeus.vst.chip.logger.log -import java.sql.PreparedStatement -import java.sql.ResultSet -import java.sql.Timestamp - -fun Instant.toSqlTimestamp() = Timestamp(this.toEpochMilliseconds()) -fun Timestamp.toDateTimeInstant() = Instant.fromEpochMilliseconds(this.time) - -data class SqlStatement( - val sql: String, - val prepareParameters: T.(PreparedStatement) -> Unit -) - -data class SqlQuery( - val sql: String, - val resultMapper: (ResultSet) -> T -) - -abstract class QueryProvider { - abstract val tableName: String - open val idQuery: String - get() = "SELECT * FROM $tableName WHERE ID = ?" - abstract val resultSetMapper: (ResultSet) -> T - open val find: SqlQuery - get() = SqlQuery( - idQuery, - resultSetMapper - ) - abstract val insert: SqlStatement - abstract val update: SqlStatement - open val delete: SqlStatement - get() = SqlStatement( - "DELETE FROM $tableName WHERE ID = ?" - ) { ps -> - ps.setLong(1, getPK()[0] as Long) - } -} - -abstract class BaseDao { - abstract val queryProvider: QueryProvider - open val autogeneratedPrimaryKey: Boolean = true - - open fun insert(entity: T) { - executeInsert(entity, "insert", queryProvider.insert) - } - - open fun update(entity: T): Int = executeUpdate( - entity, - "update", - queryProvider.update, - true - ) - - open fun upsert(entity: T) { - if ((entity.getPK()[0] as Long) == 0L) { - insert(entity) - } else { - update(entity) - } - } - - open fun delete(entity: T) { - executeUpdate(entity, "delete", queryProvider.delete, true) - } - - open fun find( - id: Long - ): T? { - return executeQuery( - "find", - queryProvider.find - ) { ps -> - ps.setLong(1, id) - }.firstOrNull() - } - - protected fun executeSQLUpdate( - sql: String, - parameterSetter: (PreparedStatement) -> Unit - ): Int { - return transaction { con -> - con.prepareStatement(sql).use { ps -> - parameterSetter(ps) - - ps.executeUpdate() - } - } - } - - protected fun executeQuery( - label: String, - statement: SqlQuery, - prepareParameters: (PreparedStatement) -> Unit, - ): List { - return transaction { con -> - log.debug { "Executing query [$label] - [${statement.sql}]" } - val result = mutableListOf() - - con.prepareStatement(statement.sql)?.use { ps -> - prepareParameters(ps) - - val rs = ps.executeQuery() - - while (rs.next()) { - result.add(statement.resultMapper(rs)) - } - } - - result - } - } - - protected fun executeInsert( - entity: T, - label: String, - statement: SqlStatement, - checkSingleRow: Boolean = false - ) { - transaction { con -> - log.debug { "Executing insert [$label] - [${statement.sql}] - [$entity]" } - con.prepareStatement(statement.sql)?.use { ps -> - statement.prepareParameters(entity, ps) - - val rows = if (checkSingleRow) { - ps.execute() - 1 - } else { - ps.executeUpdate() - } - - if (autogeneratedPrimaryKey) { - val keyResult = ps.generatedKeys - if (keyResult.next()) { - entity.setPK(arrayOf(keyResult.getLong(1))) - } - } - - check(rows == 1) { - "Statement [$label] affected more than 1 row! [${statement.sql}]" - } - } - } - } - - protected fun executeUpdate( - entity: T, - label: String, - statement: SqlStatement, - checkSingleRow: Boolean = false - ): Int = transaction { con -> - var rows = 1 - - log.debug { "Executing update [$label] - [${statement.sql}] - [$entity]" } - con.prepareStatement(statement.sql)?.use { ps -> - statement.prepareParameters(entity, ps) - - rows = ps.executeUpdate() - - check(checkSingleRow || rows == 1) { - "Statement [$label] affected more than 1 row! [${statement.sql}]" - } - } - - rows - } - -} diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/Database.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/Database.kt deleted file mode 100644 index 2276275..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/Database.kt +++ /dev/null @@ -1,99 +0,0 @@ -package nl.astraeus.vst.chip.db - -import com.zaxxer.hikari.HikariConfig -import com.zaxxer.hikari.HikariDataSource -import java.sql.Connection -import java.util.* -import java.util.concurrent.atomic.AtomicBoolean - -enum class TxScope { - REQUIRED, - /* if needed we need to switch db, sqlite only allows one writer/connection */ - //REQUIRES_NEW -} - -private val currentConnection = ThreadLocal() - -fun transaction( - scope: TxScope = TxScope.REQUIRED, - block: (Connection) -> T -): T { - val hasConnection = currentConnection.get() != null - var oldConnection: Connection? = null - - if (!hasConnection) { - currentConnection.set(Database.getConnection()) - /* - } else if (scope == TxScope.REQUIRES_NEW) { - oldConnection = currentConnection.get() - - currentConnection.set(Database.getConnection()) - */ - } - - val connection = currentConnection.get() - - try { - val result = block(connection) - - connection.commit() - - return result - } finally { - if (!hasConnection) { - currentConnection.set(oldConnection) - connection.close() - } - } -} - -object Database { - - var ds: HikariDataSource? = null - - fun initialize(config: HikariConfig) { - val properties = Properties() - properties["journal_mode"] = "WAL" - - - config.dataSourceProperties = properties - config.addDataSourceProperty("cachePrepStmts", "true") - config.addDataSourceProperty("prepStmtCacheSize", "250") - config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048") - - ds = HikariDataSource(config) - Migrations.databaseVersionTableCreated = AtomicBoolean(false) - Migrations.updateDatabaseIfNeeded() - } - - fun getConnection() = ds?.connection ?: error("Database has not been initialized!") - - /* - val ds: HikariDataSource - - init { - val properties = Properties() - properties["journal_mode"] = "WAL" - - val config = HikariConfig().apply { - driverClassName = "nl.astraeus.jdbc.Driver" - jdbcUrl = "jdbc:stat:webServerPort=6001:jdbc:sqlite:data/daw3.db" - username = "sa" - password = "" - maximumPoolSize = 25 - isAutoCommit = false - dataSourceProperties = properties - validate() - } - - config.addDataSourceProperty("cachePrepStmts", "true") - config.addDataSourceProperty("prepStmtCacheSize", "250") - config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048") - - ds = HikariDataSource(config) - } - - fun getConnection() = ds.connection - */ - -} diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/Entity.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/Entity.kt deleted file mode 100644 index 6c82712..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/Entity.kt +++ /dev/null @@ -1,16 +0,0 @@ -package nl.astraeus.vst.chip.db - -interface Entity { - fun getPK(): Array - fun setPK(pks: Array) -} - -interface EntityId : Entity { - var id: Long - - override fun getPK(): Array = arrayOf(id) - - override fun setPK(pks: Array) { - id = pks[0] as Long - } -} diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/Migrations.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/Migrations.kt deleted file mode 100644 index 4207f92..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/Migrations.kt +++ /dev/null @@ -1,106 +0,0 @@ -package nl.astraeus.vst.chip.db - -import nl.astraeus.vst.chip.logger.log -import java.sql.Connection -import java.sql.SQLException -import java.sql.Timestamp -import java.util.concurrent.atomic.AtomicBoolean - -sealed class Migration { - class Query( - val query: String - ) : Migration() { - override fun toString(): String { - return query - } - } - - class Code( - val code: (Connection) -> Unit - ) : Migration() { - override fun toString(): String { - return code.toString() - } - } -} - -val DATABASE_MIGRATIONS = arrayOf( - Migration.Query( - """ - CREATE TABLE DATABASE_VERSION ( - ID INTEGER PRIMARY KEY, - QUERY TEXT, - EXECUTED TIMESTAMP - ) - """.trimIndent() - ), - Migration.Query(PATCH_CREATE_QUERY), -) - -object Migrations { - var databaseVersionTableCreated = AtomicBoolean(false) - - fun updateDatabaseIfNeeded() { - try { - transaction { con -> - con.prepareStatement( - """ - SELECT MAX(ID) FROM DATABASE_VERSION - """.trimIndent() - ).use { ps -> - ps.executeQuery().use { rs -> - databaseVersionTableCreated.compareAndSet(false, true) - - if (rs.next()) { - val maxId = rs.getInt(1) - - for (index in maxId + 1.. - log.debug { - "Executing migration $index - [${DATABASE_MIGRATIONS[index]}]" - } - val description = when ( - val migration = DATABASE_MIGRATIONS[index] - ) { - is Migration.Query -> { - con.prepareStatement(migration.query).use { ps -> - ps.execute() - } - - migration.query - } - - is Migration.Code -> { - migration.code(con) - - migration.code.toString() - } - } - con.prepareStatement("INSERT INTO DATABASE_VERSION VALUES (?, ?, ?)").use { ps -> - ps.setInt(1, index) - ps.setString(2, description) - ps.setTimestamp(3, Timestamp(System.currentTimeMillis())) - - ps.execute() - } - } - } - -} diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/PatchDao.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/PatchDao.kt deleted file mode 100644 index e24d5ba..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/PatchDao.kt +++ /dev/null @@ -1,31 +0,0 @@ -package nl.astraeus.vst.chip.db - -object PatchDao : BaseDao() { - - override val queryProvider: QueryProvider - get() = PatchEntityQueryProvider - - fun create( - patchId: String, - patch: String - ): PatchEntity { - val result = PatchEntity( - 0, - patchId, - patch - ) - - return result - } - - fun findById(patchId: String): PatchEntity? = executeQuery( - "findById", - SqlQuery( - "SELECT * FROM ${queryProvider.tableName} WHERE PATCH_ID = ?", - queryProvider.resultSetMapper - ) - ) { ps -> - ps.setString(1, patchId) - }.firstOrNull() - -} diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/PatchEntity.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/PatchEntity.kt deleted file mode 100644 index 80825d3..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/PatchEntity.kt +++ /dev/null @@ -1,12 +0,0 @@ -package nl.astraeus.vst.chip.db - -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant - -data class PatchEntity( - override var id: Long, - var patchId: String, - var patch: String, - var created: Instant = Clock.System.now(), - var updated: Instant = Clock.System.now(), -) : EntityId \ No newline at end of file diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/PatchEntityQueryProvider.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/PatchEntityQueryProvider.kt deleted file mode 100644 index cc3650c..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/db/PatchEntityQueryProvider.kt +++ /dev/null @@ -1,64 +0,0 @@ -package nl.astraeus.vst.chip.db - -import java.sql.ResultSet -import java.sql.Types - -val PATCH_CREATE_QUERY = """ - CREATE TABLE INSTRUMENTS ( - ID INTEGER PRIMARY KEY AUTOINCREMENT, - PATCH_ID TEXT, - PATCH TEXT, - CREATED TIMESTAMP, - UPDATED TIMESTAMP - ) - """.trimIndent() - -object PatchEntityQueryProvider : QueryProvider() { - override val tableName: String - get() = "INSTRUMENTS" - override val resultSetMapper: (ResultSet) -> PatchEntity - get() = { rs -> - PatchEntity( - rs.getLong(1), - rs.getString(2), - rs.getString(3), - rs.getTimestamp(4).toDateTimeInstant(), - rs.getTimestamp(5).toDateTimeInstant() - ) - } - override val insert: SqlStatement - get() = SqlStatement( - """ - INSERT INTO $tableName ( - ID, - PATCH_ID, - PATCH, - CREATED, - UPDATED - ) VALUES ( - ?,?,?,?,? - ) - """.trimIndent() - ) { ps -> - ps.setNull(1, Types.BIGINT) - ps.setString(2, patchId) - ps.setString(3, patch) - ps.setTimestamp(4, created.toSqlTimestamp()) - ps.setTimestamp(5, updated.toSqlTimestamp()) - } - override val update: SqlStatement - get() = SqlStatement( - """ - UPDATE $tableName - SET PATCH_ID = ?, - PATCH = ?, - UPDATED = ? - WHERE ID = ? - """.trimIndent() - ) { ps -> - ps.setString(1, patchId) - ps.setString(2, patch) - ps.setTimestamp(3, updated.toSqlTimestamp()) - ps.setLong(4, id) - } -} diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/web/Index.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/web/Index.kt deleted file mode 100644 index 25cda93..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/web/Index.kt +++ /dev/null @@ -1,42 +0,0 @@ -package nl.astraeus.vst.chip.web - -import kotlinx.html.body -import kotlinx.html.head -import kotlinx.html.html -import kotlinx.html.meta -import kotlinx.html.script -import kotlinx.html.stream.appendHTML -import kotlinx.html.title - -fun generateIndex(patch: String?): String { - val result = StringBuilder(); - - if (patch == null) { - result.appendHTML(true).html { - head { - title { +"VST Chip" } - } - body { - script { - type = "application/javascript" - src = "/vst-chip-worklet-ui.js" - } - } - } - } else { - result.appendHTML(true).html { - head { - title { +"VST Chip" } - meta { - httpEquiv = "refresh" - content = "0; url=/patch/$patch" - } - } - body { - +"Redirecting to patch $patch..." - } - } - } - - return result.toString() -} diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/web/RequestHandler.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/web/RequestHandler.kt deleted file mode 100644 index c36c8e1..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/web/RequestHandler.kt +++ /dev/null @@ -1,128 +0,0 @@ -package nl.astraeus.vst.chip.web - -import io.undertow.Handlers.websocket -import io.undertow.server.HttpHandler -import io.undertow.server.HttpServerExchange -import io.undertow.server.handlers.PathHandler -import io.undertow.server.handlers.resource.PathResourceManager -import io.undertow.server.handlers.resource.ResourceHandler -import io.undertow.server.session.Session -import io.undertow.server.session.SessionConfig -import io.undertow.server.session.SessionManager -import io.undertow.websockets.WebSocketConnectionCallback -import io.undertow.websockets.core.AbstractReceiveListener -import io.undertow.websockets.core.BufferedBinaryMessage -import io.undertow.websockets.core.BufferedTextMessage -import io.undertow.websockets.core.WebSocketChannel -import io.undertow.websockets.core.WebSockets -import io.undertow.websockets.spi.WebSocketHttpExchange -import nl.astraeus.vst.chip.db.PatchDao -import nl.astraeus.vst.chip.db.PatchEntity -import nl.astraeus.vst.chip.db.transaction -import nl.astraeus.vst.chip.generateId -import java.nio.file.Paths - -class WebsocketHandler( - val session: Session? -) : AbstractReceiveListener(), WebSocketConnectionCallback { - - override fun onConnect(exchange: WebSocketHttpExchange, channel: WebSocketChannel) { - channel.receiveSetter.set(this) - channel.resumeReceives() - } - - override fun onFullTextMessage(channel: WebSocketChannel, message: BufferedTextMessage) { - val vstSession = session?.getAttribute("html-session") as? VstSession - - val data = message.data - val commandLength = data.indexOf('\n') - if (commandLength > 0) { - val command = data.substring(0, commandLength) - val value = data.substring(commandLength + 1) - - when (command) { - "SAVE" -> { - val patchId = vstSession?.patchId - if (patchId != null) { - transaction { - val patchEntity = PatchDao.findById(patchId) - - if (patchEntity != null) { - PatchDao.update(patchEntity.copy(patch = value)) - } else { - PatchDao.insert(PatchEntity(0, patchId, value)) - } - } - WebSockets.sendText("SAVED\n$patchId", channel, null) - } - } - - "LOAD" -> { - val patchId = vstSession?.patchId - if (patchId != null) { - transaction { - val patchEntity = PatchDao.findById(patchId) - - if (patchEntity != null) { - WebSockets.sendText("LOAD\n${patchEntity.patch}", channel, null) - } - } - } - } - } - } - } - - override fun onFullBinaryMessage(channel: WebSocketChannel?, message: BufferedBinaryMessage?) { - // do nothing - } -} - -object WebsocketConnectHandler : HttpHandler { - override fun handleRequest(exchange: HttpServerExchange) { - val sessionManager = exchange.getAttachment(SessionManager.ATTACHMENT_KEY) - val sessionConfig = exchange.getAttachment(SessionConfig.ATTACHMENT_KEY) - - val httpSession: Session? = sessionManager.getSession(exchange, sessionConfig) - - websocket(WebsocketHandler(httpSession)).handleRequest(exchange) - } -} - -object PatchHandler : HttpHandler { - override fun handleRequest(exchange: HttpServerExchange) { - if (exchange.requestPath.startsWith("/patch/")) { - val patchId = exchange.requestPath.substring(7) - val sessionManager = exchange.getAttachment(SessionManager.ATTACHMENT_KEY) - val sessionConfig = exchange.getAttachment(SessionConfig.ATTACHMENT_KEY) - var httpSession: Session? = sessionManager.getSession(exchange, sessionConfig) - - if (httpSession == null) { - httpSession = sessionManager.createSession(exchange, sessionConfig) - } - httpSession?.setAttribute("html-session", VstSession(patchId)) - - exchange.responseSender.send(generateIndex(null)) - } else { - val patchId = generateId() - - exchange.responseSender.send(generateIndex(patchId)) - } - } -} - -object RequestHandler : HttpHandler { - val resourceHandler = ResourceHandler(PathResourceManager(Paths.get("web"))) - val pathHandler = PathHandler(resourceHandler) - - init { - pathHandler.addExactPath("/", PatchHandler) - pathHandler.addExactPath("/index.html", PatchHandler) - pathHandler.addPrefixPath("/patch", PatchHandler) - pathHandler.addExactPath("/ws", WebsocketConnectHandler) - } - - override fun handleRequest(exchange: HttpServerExchange) { - pathHandler.handleRequest(exchange) - } -} diff --git a/src/jvmMain/kotlin/nl/astraeus/vst/chip/web/Session.kt b/src/jvmMain/kotlin/nl/astraeus/vst/chip/web/Session.kt deleted file mode 100644 index eb0c91b..0000000 --- a/src/jvmMain/kotlin/nl/astraeus/vst/chip/web/Session.kt +++ /dev/null @@ -1,5 +0,0 @@ -package nl.astraeus.vst.chip.web - -class VstSession( - val patchId: String -) \ No newline at end of file