Compare commits
4 Commits
2871697329
...
8ee8f17f96
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ee8f17f96 | |||
| 0bdaa5c94f | |||
| 3746ced387 | |||
| 4d7c46093c |
@@ -8,10 +8,11 @@ object Settings {
|
||||
var port = 9004
|
||||
var connectionTimeout = 30000
|
||||
var jdbcStatsPort = 6001
|
||||
var dataDir = "data"
|
||||
|
||||
var jdbcDriver = "nl.astraeus.jdbc.Driver"
|
||||
val jdbcConnectionUrl
|
||||
get() = "jdbc:stat:webServerPort=$jdbcStatsPort:jdbc:sqlite:data/vst.db"
|
||||
get() = "jdbc:stat:webServerPort=$jdbcStatsPort:jdbc:sqlite:$dataDir/vst.db"
|
||||
var jdbcUser = "sa"
|
||||
var jdbcPassword = ""
|
||||
|
||||
@@ -35,6 +36,8 @@ object Settings {
|
||||
|
||||
port = properties.getProperty("port", port.toString()).toInt()
|
||||
jdbcStatsPort = properties.getProperty("jdbcStatsPort", jdbcStatsPort.toString()).toInt()
|
||||
dataDir = properties.getProperty("dataDir", dataDir)
|
||||
|
||||
connectionTimeout =
|
||||
properties.getProperty("connectionTimeout", connectionTimeout.toString()).toInt()
|
||||
jdbcDriver = properties.getProperty("jdbcDriver", jdbcDriver)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package nl.astraeus.vst.chip.db
|
||||
package nl.astraeus.vst.base.db
|
||||
|
||||
object SampleDao {
|
||||
object BinaryDao {
|
||||
val queryProvider = SampleEntityQueryProvider
|
||||
|
||||
fun getSample(waveHash: String): ByteArray {
|
||||
@@ -1,8 +1,7 @@
|
||||
package nl.astraeus.vst.chip.db
|
||||
package nl.astraeus.vst.base.db
|
||||
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import nl.astraeus.vst.base.db.Entity
|
||||
|
||||
data class SampleEntity(
|
||||
var sha1Hash: String,
|
||||
@@ -1,13 +1,9 @@
|
||||
package nl.astraeus.vst.chip.db
|
||||
package nl.astraeus.vst.base.db
|
||||
|
||||
import nl.astraeus.vst.base.db.QueryProvider
|
||||
import nl.astraeus.vst.base.db.SqlStatement
|
||||
import nl.astraeus.vst.base.db.toDateTimeInstant
|
||||
import nl.astraeus.vst.base.db.toSqlTimestamp
|
||||
import java.sql.ResultSet
|
||||
|
||||
val SAMPLE_CREATE_QUERY = """
|
||||
CREATE TABLE SAMPLES (
|
||||
val BINARY_CREATE_QUERY = """
|
||||
CREATE TABLE BINARIESYSAMPLES (
|
||||
SHA1HASH TEXT,
|
||||
FILENAME TEXT,
|
||||
LENGTH NUMBER,
|
||||
@@ -10,16 +10,6 @@ import io.undertow.server.session.Session
|
||||
import io.undertow.server.session.SessionCookieConfig
|
||||
import io.undertow.server.session.SessionManager
|
||||
import io.undertow.util.Headers
|
||||
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.base.db.Database
|
||||
import nl.astraeus.vst.base.db.PatchDao
|
||||
import nl.astraeus.vst.base.db.PatchEntity
|
||||
import java.nio.file.Paths
|
||||
|
||||
object VstSessionConfig {
|
||||
@@ -30,61 +20,6 @@ object VstSessionConfig {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
Database.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) {
|
||||
Database.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 yet
|
||||
}
|
||||
}
|
||||
|
||||
object WebsocketConnectHandler : HttpHandler {
|
||||
override fun handleRequest(exchange: HttpServerExchange) {
|
||||
|
||||
164
src/jvmMain/kotlin/nl/astraeus/vst/base/web/WebsocketHandler.kt
Normal file
164
src/jvmMain/kotlin/nl/astraeus/vst/base/web/WebsocketHandler.kt
Normal file
@@ -0,0 +1,164 @@
|
||||
package nl.astraeus.vst.base.web
|
||||
|
||||
import io.undertow.server.session.Session
|
||||
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.base.Settings
|
||||
import nl.astraeus.vst.base.db.Database
|
||||
import nl.astraeus.vst.base.db.PatchDao
|
||||
import nl.astraeus.vst.base.db.PatchEntity
|
||||
import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
import java.security.MessageDigest
|
||||
|
||||
class WebsocketHandler(
|
||||
val session: Session?
|
||||
) : AbstractReceiveListener(), WebSocketConnectionCallback {
|
||||
|
||||
companion object {
|
||||
// Ensure the data directory exists
|
||||
private val dataDir = File(Settings.dataDir).apply {
|
||||
if (!exists()) {
|
||||
mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
fun fileExists(hash: String): Boolean {
|
||||
check(hash.length == 64) { "Hash must be 64 characters long" }
|
||||
|
||||
var currentDir = dataDir
|
||||
var remaining = hash
|
||||
while(remaining.length > 8) {
|
||||
val subDir = remaining.substring(0, 8)
|
||||
currentDir = File(currentDir, subDir)
|
||||
if (!currentDir.exists()) {
|
||||
return false
|
||||
}
|
||||
remaining = remaining.substring(8)
|
||||
}
|
||||
|
||||
return File(currentDir, remaining).exists()
|
||||
}
|
||||
|
||||
// Get file from hash, using subdirectories based on hash
|
||||
fun getFileFromHash(hash: String): File {
|
||||
check(hash.length == 64) { "Hash must be 64 characters long" }
|
||||
|
||||
var currentDir = dataDir
|
||||
var remaining = hash
|
||||
while(remaining.length > 8) {
|
||||
val subDir = remaining.substring(0, 8)
|
||||
currentDir = File(currentDir, subDir)
|
||||
if (!currentDir.exists()) {
|
||||
currentDir.mkdirs()
|
||||
}
|
||||
remaining = remaining.substring(8)
|
||||
}
|
||||
|
||||
return File(currentDir, remaining)
|
||||
}
|
||||
|
||||
// Create SHA-1 hash from binary data
|
||||
fun createHashFromBytes(bytes: ByteArray): String {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
val digest = md.digest(bytes)
|
||||
return digest.joinToString("") { "%02x".format(it) }
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
Database.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) {
|
||||
Database.transaction {
|
||||
val patchEntity = PatchDao.findById(patchId)
|
||||
|
||||
if (patchEntity != null) {
|
||||
WebSockets.sendText("LOAD\n${patchEntity.patch}", channel, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"LOAD_BINARY" -> {
|
||||
val hash = value.trim()
|
||||
if (hash.isNotEmpty()) {
|
||||
if (fileExists(hash)) {
|
||||
val file = getFileFromHash(hash)
|
||||
if (file.exists() && file.isFile) {
|
||||
val bytes = file.readBytes()
|
||||
WebSockets.sendBinary(ByteBuffer.wrap(bytes), channel, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFullBinaryMessage(channel: WebSocketChannel?, message: BufferedBinaryMessage?) {
|
||||
// Process binary message: create hash from binary, save file in data/files/ directory,
|
||||
// sub directories are 5 characters of the hash per directory
|
||||
if (channel != null && message != null) {
|
||||
try {
|
||||
// Get the binary data
|
||||
val pooled = message.data
|
||||
val buffer = pooled.resource[0] // Get the first ByteBuffer
|
||||
|
||||
// Convert ByteBuffer to ByteArray
|
||||
val bytes = ByteArray(buffer.remaining())
|
||||
buffer.get(bytes)
|
||||
|
||||
// Free the pooled resource
|
||||
pooled.free()
|
||||
|
||||
// Create hash from binary data
|
||||
val hash = createHashFromBytes(bytes)
|
||||
|
||||
// Save file in data/files/ directory with subdirectories
|
||||
val file = getFileFromHash(hash)
|
||||
file.writeBytes(bytes)
|
||||
|
||||
// Send the hash back to the client
|
||||
WebSockets.sendText("BINARY_SAVED\n$hash", channel, null)
|
||||
} catch (e: Exception) {
|
||||
WebSockets.sendText("ERROR\n${e.message}", channel, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user