From ff4f0fd7427d829f816d9c353647c4db57479071 Mon Sep 17 00:00:00 2001 From: rdkartono Date: Wed, 3 Sep 2025 13:51:38 +0700 Subject: [PATCH] commit 03/09/2025 --- src/codes/Somecodes.kt | 48 ++++ src/content/ScheduleDay.kt | 13 + src/database/MariaDB.kt | 4 +- src/database/ScheduleBank.kt | 11 +- src/web/WebApp.kt | 537 ++++++++++++++++++++++++++++------- 5 files changed, 508 insertions(+), 105 deletions(-) create mode 100644 src/content/ScheduleDay.kt diff --git a/src/codes/Somecodes.kt b/src/codes/Somecodes.kt index 6701d05..7c80ef5 100644 --- a/src/codes/Somecodes.kt +++ b/src/codes/Somecodes.kt @@ -1,5 +1,6 @@ package codes +import content.ScheduleDay import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -23,6 +24,7 @@ class Somecodes { val datetimeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss") val dateformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy") val timeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm:ss") + val timeformat2: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm") const val KB_threshold = 1024.0 const val MB_threshold = KB_threshold * 1024.0 const val GB_threshold = MB_threshold * 1024.0 @@ -165,6 +167,52 @@ class Somecodes { false } } + + /** + * Check if a string is a valid schedule time in the format "HH:mm". + * @param value The string to check. + * @return True if the string is a valid schedule time, false otherwise. + */ + fun ValidScheduleTime(value: String): Boolean{ + // format HH:mm + try { + if (ValidString(value)){ + timeformat2.parse(value) + return true + } + } catch (_ : Exception){ + + } + return false + } + + /** + * Find a schedule day by its name. + * @param value The name of the schedule day to find. + * @return The name of the schedule day if found, null otherwise. + */ + fun FindScheduleDay(value: String) : String? { + val sd = ScheduleDay.entries.find { sd -> sd.name == value } + return sd?.name + } + + /** + * Check if a string is a valid schedule day or a valid date. + * A valid schedule day is either one of the ScheduleDay enum names or a date in the format "dd/MM/yyyy". + * @param value The string to check. + * @return True if the string is a valid schedule day or date, false otherwise. + */ + fun ValidScheduleDay(value: String) : Boolean { + if (ValidString(value)){ + // check if value is one of ScheduleDay enum name + if (FindScheduleDay(value) != null){ + return true + } + // check if value is in format dd/MM/yyyy + return ValidDate(value) + } + return false + } } diff --git a/src/content/ScheduleDay.kt b/src/content/ScheduleDay.kt new file mode 100644 index 0000000..600ad5d --- /dev/null +++ b/src/content/ScheduleDay.kt @@ -0,0 +1,13 @@ +package content + +@Suppress("unused") +enum class ScheduleDay(val day: String) { + Sunday("Sunday"), + Monday("Monday"), + Tuesday("Tuesday"), + Wednesday("Wednesday"), + Thursday("Thursday"), + Friday("Friday"), + Saturday("Saturday"), + Everyday("Everyday") +} \ No newline at end of file diff --git a/src/database/MariaDB.kt b/src/database/MariaDB.kt index 4152509..cbf040f 100644 --- a/src/database/MariaDB.kt +++ b/src/database/MariaDB.kt @@ -1157,7 +1157,7 @@ class MariaDB( * Exports the messagebank table to an XLSX workbook. * @return An XSSFWorkbook containing the messagebank data. */ - fun Export_Messagebank_XLSX(): XSSFWorkbook { + fun Export_Messagebank_XLSX(): XSSFWorkbook? { try { val statement = connection?.createStatement() val resultSet = statement?.executeQuery("SELECT * FROM messagebank") @@ -1188,7 +1188,7 @@ class MariaDB( } catch (e: Exception) { Logger.error { "Error exporting Messagebank, Msg: ${e.message}" } } - return XSSFWorkbook() + return null } fun Import_Messagebank_XLSX(workbook: XSSFWorkbook): Boolean { diff --git a/src/database/ScheduleBank.kt b/src/database/ScheduleBank.kt index 01b795e..300d89e 100644 --- a/src/database/ScheduleBank.kt +++ b/src/database/ScheduleBank.kt @@ -1,4 +1,13 @@ package database @Suppress("unused") -data class ScheduleBank(var index: UInt, var Description: String, var Day: String, var Time: String, var Soundpath: String, var Repeat: UByte, var Enable: Boolean, var BroadcastZones: String, var Language: String) +data class ScheduleBank( + var index: UInt, + var Description: String, + var Day: String, + var Time: String, + var Soundpath: String, + var Repeat: UByte, + var Enable: Boolean, + var BroadcastZones: String, + var Language: String) diff --git a/src/web/WebApp.kt b/src/web/WebApp.kt index 541c1d0..be6193f 100644 --- a/src/web/WebApp.kt +++ b/src/web/WebApp.kt @@ -3,10 +3,14 @@ package web import codes.Somecodes import codes.Somecodes.Companion.ValidDate import codes.Somecodes.Companion.ValidFile +import codes.Somecodes.Companion.ValidScheduleDay +import codes.Somecodes.Companion.ValidScheduleTime import codes.Somecodes.Companion.ValidString import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import content.Category +import content.Language +import content.VoiceType import database.MariaDB import database.Soundbank import io.javalin.Javalin @@ -20,12 +24,13 @@ import io.javalin.apibuilder.ApiBuilder.ws import io.javalin.http.Context import io.javalin.json.JavalinJackson import io.javalin.websocket.WsMessageContext +import org.apache.poi.xssf.usermodel.XSSFWorkbook import java.time.LocalDateTime @Suppress("unused") class WebApp(val listenPort: Int, val userlist: List>, val db: MariaDB) { - var app : Javalin? = null + var app: Javalin? = null val objectmapper = jacksonObjectMapper() private fun SendReply(context: WsMessageContext, command: String, value: String) { @@ -38,13 +43,12 @@ class WebApp(val listenPort: Int, val userlist: List>, val } fun Start() { - app = Javalin.create { - config -> + app = Javalin.create { config -> config.useVirtualThreads = true config.staticFiles.add("/webpage") config.jsonMapper(JavalinJackson(jacksonObjectMapper())) config.router.apiBuilder { - path("/"){ + path("/") { get { ctx -> // Serve the main page ctx.sessionAttribute("user", null) // Clear user session @@ -52,8 +56,8 @@ class WebApp(val listenPort: Int, val userlist: List>, val } } - path("login.html"){ - post{ it -> + path("login.html") { + post { it -> // get username and password from form val username = it.formParam("username") val password = it.formParam("password") @@ -84,49 +88,78 @@ class WebApp(val listenPort: Int, val userlist: List>, val println("WebSocket closed: ${wsCloseContext.session.remoteAddress}") } ws.onMessage { wsMessageContext -> - try{ - val cmd = objectmapper.readValue(wsMessageContext.message(), WebsocketCommand::class.java) + try { + val cmd = + objectmapper.readValue(wsMessageContext.message(), WebsocketCommand::class.java) when (cmd.command) { - "getSystemTime" ->{ - SendReply(wsMessageContext, cmd.command, LocalDateTime.now().format(Somecodes.datetimeformat1)) + "getSystemTime" -> { + SendReply( + wsMessageContext, + cmd.command, + LocalDateTime.now().format(Somecodes.datetimeformat1) + ) } - "getCPUStatus" ->{ + + "getCPUStatus" -> { Somecodes.getCPUUsage { vv -> - SendReply(wsMessageContext, cmd.command, vv ) + SendReply(wsMessageContext, cmd.command, vv) } } - "getMemoryStatus" ->{ + + "getMemoryStatus" -> { SendReply(wsMessageContext, cmd.command, Somecodes.getMemoryUsage()) } - "getDiskStatus" ->{ + + "getDiskStatus" -> { SendReply(wsMessageContext, cmd.command, Somecodes.getDiskUsage()) } - "getNetworkStatus" ->{ + + "getNetworkStatus" -> { // TODO Get Network status SendReply(wsMessageContext, cmd.command, "OK") } - "getSoundBankList" ->{ + + "getSoundBankList" -> { println("getSoundBankList command received") - SendReply(wsMessageContext, cmd.command, MariaDB.ArrayListtoString(db.SoundbankList)) + SendReply( + wsMessageContext, + cmd.command, + MariaDB.ArrayListtoString(db.SoundbankList) + ) } - "getMessageBankList"->{ + + "getMessageBankList" -> { println("getMessageBankList command received") - SendReply(wsMessageContext, cmd.command, MariaDB.ArrayListtoString(db.MessagebankList)) + SendReply( + wsMessageContext, + cmd.command, + MariaDB.ArrayListtoString(db.MessagebankList) + ) } - "getLanguageList"->{ + + "getLanguageList" -> { println("getLanguageList command received") - SendReply(wsMessageContext, cmd.command, MariaDB.ArrayListtoString(db.LanguageLinkList)) + SendReply( + wsMessageContext, + cmd.command, + MariaDB.ArrayListtoString(db.LanguageLinkList) + ) } - "getTimerList"->{ + + "getTimerList" -> { println("getTimerList command received") - SendReply(wsMessageContext, cmd.command, MariaDB.ArrayListtoString(db.SchedulebankList)) + SendReply( + wsMessageContext, + cmd.command, + MariaDB.ArrayListtoString(db.SchedulebankList) + ) } else -> { SendReply(wsMessageContext, cmd.command, "Unknown command") } } - } catch (e: Exception){ + } catch (e: Exception) { println("Error processing WebSocket message: ${e.message}") } @@ -139,7 +172,7 @@ class WebApp(val listenPort: Int, val userlist: List>, val } } path("soundbank.html") { - before {CheckUsers(it)} + before { CheckUsers(it) } } path("messagebank.html") { before { CheckUsers(it) } @@ -156,76 +189,79 @@ class WebApp(val listenPort: Int, val userlist: List>, val path("timer.html") { before { CheckUsers(it) } } - path("api"){ - path("SoundBank"){ - get("List"){ + path("api") { + path("SoundBank") { + get("List") { // get soundbank list it.result(MariaDB.ArrayListtoString(db.SoundbankList)) } - post("Add"){ + post("Add") { try { val addvalue = objectmapper.readValue(it.body(), Soundbank::class.java) - if (ValidString(addvalue.Description)){ - if (ValidString(addvalue.TAG)){ - if (ValidString(addvalue.Category)){ - if (ValidString(addvalue.Language)){ - if (ValidString(addvalue.Path)){ + if (ValidString(addvalue.Description)) { + if (ValidString(addvalue.TAG)) { + if (ValidString(addvalue.Category)) { + if (ValidString(addvalue.Language)) { + if (ValidString(addvalue.Path)) { // check apakah TAG sudah ada untuk language dan category yang sama val exists = db.SoundbankList.any { sb -> sb.TAG == addvalue.TAG && sb.Language == addvalue.Language && sb.Category == addvalue.Category } - if (!exists){ - if (ValidFile(addvalue.Path)){ - if (db.Add_Soundbank(addvalue)){ + if (!exists) { + if (ValidFile(addvalue.Path)) { + if (db.Add_Soundbank(addvalue)) { it.result("OK") - } else it.status(500).result("Failed to add soundbank to database") - } else it.status(400).result("Invalid Path, file does not exist") - } else it.status(400).result("TAG=${addvalue.TAG} already exists for the same Language=${addvalue.Language} and Category=${addvalue.Category}") + } else it.status(500) + .result("Failed to add soundbank to database") + } else it.status(400) + .result("Invalid Path, file does not exist") + } else it.status(400) + .result("TAG=${addvalue.TAG} already exists for the same Language=${addvalue.Language} and Category=${addvalue.Category}") } else it.status(400).result("Invalid Path") } else it.status(400).result("Invalid Language") } else it.status(400).result("Invalid Category") } else it.status(400).result("Invalid TAG") } else it.status(400).result("Invalid Description") - } catch (_: Exception){ + } catch (_: Exception) { it.status(400).result("Invalid request body") } } - delete("List"){ + delete("List") { // truncate soundbank table - if (db.Clear_Soundbank()){ + if (db.Clear_Soundbank()) { it.result("OK") } else { it.status(500).result("Failed to truncate soundbank table") } } - delete("DeleteByIndex/{index}"){ + delete("DeleteByIndex/{index}") { // delete by index val index = it.pathParam("index").toUIntOrNull() - if (index == null){ + if (index == null) { it.status(400).result("Invalid index") - } else{ - if (db.Delete_Soundbank_by_index(index)){ + } else { + if (db.Delete_Soundbank_by_index(index)) { it.result("OK") } else { it.status(500).result("Failed to delete soundbank with index $index") } } } - patch("UpdateByIndex/{index}"){ + patch("UpdateByIndex/{index}") { // update by index val index = it.pathParam("index").toUIntOrNull() - if (index == null){ + if (index == null) { // tidak ada path param index it.status(400).result("Invalid index") } else { - val sb = db.SoundbankList.find{ xx -> xx.index == index } - if (sb == null){ + val sb = db.SoundbankList.find { xx -> xx.index == index } + if (sb == null) { // soundbank dengan index tersebut tidak ditemukan it.status(404).result("Soundbank with index $index not found") } else { // soundbank dengan index tersebut ditemukan, sekarang update - val json : JsonNode = objectmapper.readTree(it.body()) - if (json.isEmpty){ + val json: JsonNode = objectmapper.readTree(it.body()) + if (json.isEmpty) { it.status(400).result("UpdateByIndex with index=$index has empty body") } else { val _description = json.get("Description").asText() @@ -234,17 +270,17 @@ class WebApp(val listenPort: Int, val userlist: List>, val val _language = json.get("Language").asText() val _path = json.get("Path").asText() var changed = false - if (ValidString(_description) && _description!=sb.Description){ + if (ValidString(_description) && _description != sb.Description) { sb.Description = _description changed = true } - if (ValidString(_tag) && _tag!=sb.TAG){ + if (ValidString(_tag) && _tag != sb.TAG) { sb.TAG = _tag changed = true } - if (ValidString(_category) && _category!=sb.Category){ - if (Category.entries.any { - cat -> cat.name == _category + if (ValidString(_category) && _category != sb.Category) { + if (Category.entries.any { cat -> + cat.name == _category }) { sb.Category = _category changed = true @@ -253,12 +289,12 @@ class WebApp(val listenPort: Int, val userlist: List>, val return@patch } } - if (ValidString(_language) && _language!=sb.Language){ + if (ValidString(_language) && _language != sb.Language) { sb.Language = _language changed = true } - if (ValidString(_path) && _path!=sb.Path){ - if (ValidFile(_path)){ + if (ValidString(_path) && _path != sb.Path) { + if (ValidFile(_path)) { sb.Path = _path changed = true } else { @@ -266,119 +302,417 @@ class WebApp(val listenPort: Int, val userlist: List>, val return@patch } } - if (changed){ - if (db.Update_Soundbank_by_index(index, sb)){ + if (changed) { + if (db.Update_Soundbank_by_index(index, sb)) { it.result("OK") } else it.status(500).result("Failed to update soundbank with index $index") - } else it.status(400).result("Nothing has changed for soundbank with index $index") + } else it.status(400) + .result("Nothing has changed for soundbank with index $index") } } } } - get("ExportXLSX"){ - + get("ExportXLSX") { + val xlsxdata = db.Export_Soundbank_XLSX() + if (xlsxdata != null) { + it.header( + "Content-Type", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) + it.header("Content-Disposition", "attachment; filename=\"soundbank.xlsx\"") + it.outputStream().use { out -> + xlsxdata.write(out) + } + } else { + it.status(500).result("Failed to export soundbank to XLSX") + } } - post("ImportXLSX"){ + post("ImportXLSX") { val uploaded = it.uploadedFile("file") - if (uploaded==null){ + if (uploaded == null) { it.status(400).result("No file uploaded") return@post } + try { + val xlsx = XSSFWorkbook(uploaded.content()) + if (db.Import_Soundbank_XLSX(xlsx)) { + it.result("OK") + } else { + it.status(500).result("Failed to import soundbank from XLSX") + } + } catch (e: Exception) { + it.status(400).result("Invalid XLSX file") + } } } - path("MessageBank"){ - get("List"){ + path("MessageBank") { + get("List") { // get messagebank list it.result(MariaDB.ArrayListtoString(db.MessagebankList)) } - delete("List"){ + delete("List") { // truncate messagebank table - if (db.Clear_Messagebank()){ + if (db.Clear_Messagebank()) { it.result("OK") } else { it.status(500).result("Failed to truncate messagebank table") } } - delete("DeleteByIndex/{index}"){ + delete("DeleteByIndex/{index}") { // delete by index val index = it.pathParam("index").toUIntOrNull() - if (index == null){ + if (index == null) { it.status(400).result("Invalid index") - } else{ - if (db.Delete_Messagebank_by_index(index)){ + } else { + if (db.Delete_Messagebank_by_index(index)) { it.result("OK") } else { it.status(500).result("Failed to delete messagebank with index $index") } } } + patch("UpdateByIndex/{index}") { + // update messagebank by index + val index = it.pathParam("index").toUIntOrNull() + if (index == null) { + it.status(400).result("Invalid index") + } else { + val mb = db.MessagebankList.find { xx -> xx.index == index } + if (mb == null) { + it.status(404).result("Messagebank with index $index not found") + } else { + val json: JsonNode = objectmapper.readTree(it.body()) + if (json.isEmpty) { + it.status(400).result("UpdateByIndex with index=$index has empty body") + } else { + val _description = json.get("Description").asText() + val _language = json.get("Language").asText() + val _ann_id = json.get("ANN_ID").asInt().toUInt() + val _voice_type = json.get("Voice_Type").asText() + val _message_detail = json.get("Message_Detail").asText() + val _message_tags = json.get("Message_TAGS").asText() + + var changed = false + if (ValidString(_description) && _description != mb.Description) { + mb.Description = _description + changed = true + } + if (ValidString(_language) && _language != mb.Language) { + mb.Language = _language + changed = true + } + if (_ann_id > 0u && _ann_id != mb.ANN_ID) { + mb.ANN_ID = _ann_id + changed = true + } + if (ValidString(_voice_type) && _voice_type != mb.Voice_Type) { + if (VoiceType.entries.any { vt -> vt.name == _voice_type }) { + mb.Voice_Type = _voice_type + changed = true + } else { + it.status(400).result("Invalid Voice_Type") + return@patch + } + } + if (ValidString(_message_detail) && _message_detail != mb.Message_Detail) { + mb.Message_Detail = _message_detail + changed = true + } + if (ValidString(_message_tags) && _message_tags != mb.Message_TAGS) { + mb.Message_TAGS = _message_tags + changed = true + } + if (changed) { + if (db.Update_Messagebank_by_index(index, mb)) { + it.result("OK") + } else it.status(500) + .result("Failed to update messagebank with index $index") + } else it.status(400) + .result("Nothing has changed for messagebank with index $index") + } + } + } + } + get("ExportXLSX") { + val xlsxdata = db.Export_Messagebank_XLSX() + if (xlsxdata != null) { + it.header( + "Content-Type", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) + it.header("Content-Disposition", "attachment; filename=\"messagebank.xlsx\"") + it.outputStream().use { out -> + xlsxdata.write(out) + } + } else { + it.status(500).result("Failed to export messagebank to XLSX") + } + } + post("ImportXLSX") { + val uploaded = it.uploadedFile("file") + if (uploaded == null) { + it.status(400).result("No file uploaded") + return@post + } + try { + val xlsx = XSSFWorkbook(uploaded.content()) + if (db.Import_Messagebank_XLSX(xlsx)) { + it.result("OK") + } else { + it.status(500).result("Failed to import messagebank from XLSX") + } + } catch (e: Exception) { + it.status(400).result("Invalid XLSX file") + } + } } - path("LanguageLink"){ - get("List"){ + path("LanguageLink") { + get("List") { // get language link list it.result(MariaDB.ArrayListtoString(db.LanguageLinkList)) } - delete("List"){ + delete("List") { // truncate language link table - if (db.Clear_LanguageLink()){ + if (db.Clear_LanguageLink()) { it.result("OK") } else { it.status(500).result("Failed to truncate language link table") } } - delete("DeleteByIndex/{index}"){ + delete("DeleteByIndex/{index}") { // delete by index val index = it.pathParam("index").toUIntOrNull() - if (index == null){ + if (index == null) { it.status(400).result("Invalid index") - } else{ - if (db.Delete_LanguageLink_by_index(index)){ + } else { + if (db.Delete_LanguageLink_by_index(index)) { it.result("OK") } else { it.status(500).result("Failed to delete language link with index $index") } } } + patch("UpdateByIndex/{index}") { + // update by index + val index = it.pathParam("index").toUIntOrNull() + if (index == null) { + it.status(400).result("Invalid index") + } else { + val ll = db.LanguageLinkList.find { xx -> xx.index == index } + if (ll == null) { + it.status(404).result("Language link with index $index not found") + } else { + val json: JsonNode = objectmapper.readTree(it.body()) + if (json.isEmpty) { + it.status(400).result("UpdateByIndex with index=$index has empty body") + } else { + val _tag = json.get("TAG").asText() + val _language = json.get("Language").asText() + var changed = false + if (ValidString(_language) && _language != ll.Language) { + ll.Language = _language + changed = true + } + if (ValidString(_tag) && _tag != ll.TAG) { + ll.TAG = _tag + changed = true + } + if (changed) { + if (db.Update_LanguageLink_by_index(index, ll)) { + it.result("OK") + } else it.status(500) + .result("Failed to update language link with index $index") + } else it.status(400) + .result("Nothing has changed for language link with index $index") + } + } + } + } + get("ExportXLSX") { + val xlsxdata = db.Export_LanguageLink_XLSX() + if (xlsxdata != null) { + it.header( + "Content-Type", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) + it.header("Content-Disposition", "attachment; filename=\"languagelink.xlsx\"") + it.outputStream().use { out -> + xlsxdata.write(out) + } + } else { + it.status(500).result("Failed to export language link to XLSX") + } + } + post("ImportXLSX") { + val uploaded = it.uploadedFile("file") + if (uploaded == null) { + it.status(400).result("No file uploaded") + return@post + } + try { + val xlsx = XSSFWorkbook(uploaded.content()) + if (db.Import_LanguageLink_XLSX(xlsx)) { + it.result("OK") + } else { + it.status(500).result("Failed to import language link from XLSX") + } + } catch (e: Exception) { + it.status(400).result("Invalid XLSX file") + } + } } - path("ScheduleBank"){ - get("List"){ + path("ScheduleBank") { + get("List") { // get timer list it.result(MariaDB.ArrayListtoString(db.SchedulebankList)) } - delete("List"){ + delete("List") { // truncate timer table - if (db.Clear_Schedulebank()){ + if (db.Clear_Schedulebank()) { it.result("OK") } else { it.status(500).result("Failed to truncate schedulebank table") } } - delete("DeleteByIndex/{index}"){ + delete("DeleteByIndex/{index}") { // delete by index val index = it.pathParam("index").toUIntOrNull() - if (index == null){ + if (index == null) { it.status(400).result("Invalid index") - } else{ - if (db.Delete_Schedulebank_by_index(index)){ + } else { + if (db.Delete_Schedulebank_by_index(index)) { it.result("OK") } else { it.status(500).result("Failed to delete schedule with index $index") } } } + patch("UpdateByIndex/{index}") { + // update by index + val index = it.pathParam("index").toUIntOrNull() + if (index == null) { + it.status(400).result("Invalid index") + } else { + val sb = db.SchedulebankList.find { xx -> xx.index == index } + if (sb == null) { + it.status(404).result("Schedule with index $index not found") + } else { + val json: JsonNode = objectmapper.readTree(it.body()) + if (json.isEmpty) { + it.status(400).result("UpdateByIndex with index=$index has empty body") + } else { + val _description = json.get("Description").asText() + val _time = json.get("Time").asText() + val _day = json.get("Day").asText() + val _soundpath = json.get("Soundpath").asText() + val _repeat = json.get("Repeat").asInt().toUByte() + val _enable = json.get("Enable").asBoolean() + val _broadcast_zones = json.get("BroadcastZones").asText() + val _language = json.get("Language").asText() + var changed = false + if (ValidString(_description) && _description != sb.Description) { + sb.Description = _description + changed = true + } + if (ValidString(_time) && _time != sb.Time) { + if (ValidScheduleTime(_time)) { + sb.Time = _time + changed = true + } else { + it.status(400).result("Invalid Time format, must be HH:mm") + return@patch + } + } + if (ValidString(_day) && _day != sb.Day) { + if (ValidScheduleDay(_day)) { + sb.Day = _day + changed = true + } else { + it.status(400).result("Invalid Day format") + return@patch + } + } + if (ValidString(_soundpath) && _soundpath != sb.Soundpath) { + sb.Soundpath = _soundpath + changed = true + } + if (_repeat != sb.Repeat) { + sb.Repeat = _repeat + changed = true + } + if (_enable != sb.Enable) { + sb.Enable = _enable + changed = true + } + if (ValidString(_broadcast_zones) && _broadcast_zones != sb.BroadcastZones) { + sb.BroadcastZones = _broadcast_zones + changed = true + } + if (ValidString(_language) && _language != sb.Language) { + if (Language.entries.any{ lang -> lang.name == _language }) { + sb.Language = _language + changed = true + } else { + it.status(400).result("Invalid Language") + return@patch + } + } + if (changed) { + if (db.Update_Schedulebank_by_index(index, sb)) { + it.result("OK") + } else it.status(500) + .result("Failed to update schedule with index $index") + } else it.status(400) + .result("Nothing has changed for schedule with index $index") + } + } + } + } + get("ExportXLSX") { + val xlsxdata = db.Export_Schedulebank_XLSX() + if (xlsxdata != null) { + it.header( + "Content-Type", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + ) + it.header("Content-Disposition", "attachment; filename=\"schedulebank.xlsx\"") + it.outputStream().use { out -> + xlsxdata.write(out) + } + } else { + it.status(500).result("Failed to export schedulebank to XLSX") + } + } + post("ImportXLSX") { + val uploaded = it.uploadedFile("file") + if (uploaded == null) { + it.status(400).result("No file uploaded") + return@post + } + try { + val xlsx = XSSFWorkbook(uploaded.content()) + if (db.Import_Schedulebank_XLSX(xlsx)) { + it.result("OK") + } else { + it.status(500).result("Failed to import schedulebank from XLSX") + } + } catch (e: Exception) { + it.status(400).result("Invalid XLSX file") + } + } } - path("Log"){ - get("List//"){ get1 -> + path("Log") { + get("List//") { get1 -> val logdate = get1.pathParam("logdate") val logfilter = get1.pathParam("logfilter") - if (ValidDate(logdate)){ - if (ValidString(logfilter)){ + if (ValidDate(logdate)) { + if (ValidString(logfilter)) { // ada log filter - db.GetLog(logdate, logfilter){ + db.GetLog(logdate, logfilter) { get1.result(MariaDB.ArrayListtoString(it)) } } else { - db.GetLog(logdate){ + db.GetLog(logdate) { get1.result(MariaDB.ArrayListtoString(it)) } } @@ -392,19 +726,18 @@ class WebApp(val listenPort: Int, val userlist: List>, val } - - fun CheckUsers(ctx: Context){ + fun CheckUsers(ctx: Context) { val user = ctx.sessionAttribute("user") if (user == null) { ctx.redirect("login.html") } val foundUser = userlist.find { it.first == user } - if (foundUser==null) { + if (foundUser == null) { ctx.redirect("login.html") } } - fun Stop(){ + fun Stop() { app?.stop() }