package web import StreamerOutputs import codes.Somecodes import codes.Somecodes.Companion.ListAudioFiles import codes.Somecodes.Companion.ValiDateForLogHtml import codes.Somecodes.Companion.ValidFile import codes.Somecodes.Companion.ValidIPV4 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.ScheduleDay import content.VoiceType import database.BroadcastZones import database.BroadcastZonesHtml import database.LanguageLink import database.MariaDB import database.Messagebank import database.SoundChannel import database.Soundbank import db import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.before import io.javalin.apibuilder.ApiBuilder.delete import io.javalin.apibuilder.ApiBuilder.get import io.javalin.apibuilder.ApiBuilder.patch import io.javalin.apibuilder.ApiBuilder.path import io.javalin.apibuilder.ApiBuilder.post 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>) { var app: Javalin? = null val objectmapper = jacksonObjectMapper() private fun SendReply(context: WsMessageContext, command: String, value: String) { try { if (context.session.isOpen) { context.send(objectmapper.writeValueAsString(WebsocketReply(command, value))) } } catch (_: Exception) { } } fun Start() { app = Javalin.create { config -> config.useVirtualThreads = true config.staticFiles.add("/webpage") config.jsonMapper(JavalinJackson(jacksonObjectMapper())) config.router.apiBuilder { path("/") { get { ctx -> // Serve the main page ctx.sessionAttribute("user", null) // Clear user session ctx.redirect("login.html") } } path("login.html") { post { it -> // get username and password from form val username = it.formParam("username") val password = it.formParam("password") if (username == null || password == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Username and password are required"))) return@post } // Check if user exists in userlist val user = userlist.find { it.first == username && it.second == password } if (user == null) { it.status(401).result(objectmapper.writeValueAsString(resultMessage("Invalid username or password"))) return@post } // Set user session it.sessionAttribute("user", user.first) println("User ${user.first} logged in") // Redirect to home page it.redirect("home.html") } } path("home.html") { before { CheckUsers(it) } ws("/ws") { ws -> // WebSocket endpoint for home ws.onClose { wsCloseContext -> // TODO Handle WebSocket close event println("WebSocket closed: ${wsCloseContext.session.remoteAddress}") } ws.onMessage { wsMessageContext -> try { val cmd = objectmapper.readValue(wsMessageContext.message(), WebsocketCommand::class.java) when (cmd.command) { "getSystemTime" -> { SendReply( wsMessageContext, cmd.command, LocalDateTime.now().format(Somecodes.datetimeformat1) ) } "getCPUStatus" -> { Somecodes.getCPUUsage { vv -> SendReply(wsMessageContext, cmd.command, vv) } } "getMemoryStatus" -> { SendReply(wsMessageContext, cmd.command, Somecodes.getMemoryUsage()) } "getDiskStatus" -> { SendReply(wsMessageContext, cmd.command, Somecodes.getDiskUsage()) } "getNetworkStatus" -> { // TODO Get Network status SendReply(wsMessageContext, cmd.command, "OK") } "getPagingQueue" ->{ SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(db.queuepagingDB.List)) } "getAASQueue" ->{ SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(db.queuetableDB.List)) } "getStreamerOutputs" -> { SendReply(wsMessageContext, cmd.command, objectmapper.writeValueAsString(StreamerOutputs.values.toList())) } else -> { SendReply(wsMessageContext, cmd.command, "Unknown command") } } } catch (e: Exception) { println("Error processing WebSocket message: ${e.message}") } } ws.onConnect { wsConnectContext -> // TODO Handle WebSocket connect event println("WebSocket connected: ${wsConnectContext.session.remoteAddress}") } } } path("soundbank.html") { before { CheckUsers(it) } } path("messagebank.html") { before { CheckUsers(it) } } path("language.html") { before { CheckUsers(it) } } path("log.html") { before { CheckUsers(it) } } path("setting.html") { before { CheckUsers(it) } } path("timer.html") { before { CheckUsers(it) } } path("api") { path("VoiceType"){ get{ it.result(objectmapper.writeValueAsString(VoiceType.entries.map { vt -> vt.name })) } } path("Category") { get { it.result(objectmapper.writeValueAsString(Category.entries.map { cat -> cat.name}) ) } } path("Language") { get { it.result(objectmapper.writeValueAsString(Language.entries.map { lang -> lang.name }) ) } } path("ScheduleDay") { get { it.result(objectmapper.writeValueAsString(ScheduleDay.entries.map { day -> day.toString() })) } } path("SoundBank") { get("List") { it.result(MariaDB.ArrayListtoString(db.soundDB.List)) } get("ListFiles"){ it.result(objectmapper.writeValueAsString(ListAudioFiles("C:\\soundbank"))) } 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)) { // check apakah TAG sudah ada untuk language dan category yang sama val exists = db.soundDB.List.any { sb -> sb.TAG == addvalue.TAG && sb.Language == addvalue.Language && sb.Category == addvalue.Category } if (!exists) { if (ValidFile(addvalue.Path)) { if (db.soundDB.Add(addvalue)) { db.soundDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else it.status(500) .result(objectmapper.writeValueAsString(resultMessage("Failed to add soundbank to database"))) } else it.status(400) .result(objectmapper.writeValueAsString(resultMessage("Invalid Path, file does not exist"))) } else it.status(400) .result(objectmapper.writeValueAsString(resultMessage("TAG=${addvalue.TAG} already exists for the same Language=${addvalue.Language} and Category=${addvalue.Category}"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Path"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Language"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Category"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid TAG"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Description"))) } catch (_: Exception) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid request body"))) } } delete("List") { // truncate soundbank table if (db.soundDB.Clear()) { db.soundDB.Get() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate soundbank table"))) } } delete("DeleteByIndex/{index}") { // delete by index val index = it.pathParam("index").toUIntOrNull() if (index == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { if (db.soundDB.DeleteByIndex(index.toInt())) { db.soundDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete soundbank with index $index"))) } } } patch("UpdateByIndex/{index}") { // update by index val index = it.pathParam("index").toUIntOrNull() if (index == null) { // tidak ada path param index it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { val sb = db.soundDB.List.find { xx -> xx.index == index } if (sb == null) { // soundbank dengan index tersebut tidak ditemukan it.status(404).result(objectmapper.writeValueAsString(resultMessage("Soundbank with index $index not found"))) } else { // soundbank dengan index tersebut ditemukan, sekarang update val json: JsonNode = objectmapper.readTree(it.body()) if (json.isEmpty) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("UpdateByIndex with index=$index has empty body"))) } else { val _description = json.get("Description").asText("") val _tag = json.get("TAG").asText("") val _category = json.get("Category").asText("") val _language = json.get("Language").asText("") val _path = json.get("Path").asText("") var changed = false if (ValidString(_description) && _description != sb.Description) { sb.Description = _description changed = true } 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 }) { sb.Category = _category changed = true } else { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Category"))) return@patch } } if (ValidString(_language) && _language != sb.Language) { sb.Language = _language changed = true } if (ValidString(_path) && _path != sb.Path) { if (ValidFile(_path)) { sb.Path = _path changed = true } else { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Path, file does not exist"))) return@patch } } if (changed) { if (db.soundDB.UpdateByIndex(index.toInt(), sb)) { db.soundDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to update soundbank with index $index"))) } else it.status(400) .result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for soundbank with index $index"))) } } } } get("ExportXLSX") { val xlsxdata = db.soundDB.Export_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(objectmapper.writeValueAsString(resultMessage("Failed to export soundbank to XLSX"))) } } post("ImportXLSX") { val uploaded = it.uploadedFile("file") if (uploaded == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("No file uploaded"))) return@post } try { val xlsx = XSSFWorkbook(uploaded.content()) if (db.soundDB.Import_XLSX(xlsx)) { db.soundDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to import soundbank from XLSX"))) } } catch (e: Exception) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file"))) } } } path("MessageBank") { get("List") { // get messagebank list it.result(MariaDB.ArrayListtoString(db.messageDB.List)) } post("Add"){ val json : JsonNode = objectmapper.readTree(it.body()) val description = json.get("Description")?.asText("") ?: "" val language = json.get("Language")?.asText("") ?: "" val ann_id = json.get("ANN_ID")?.asInt()?.toUInt() ?: 0u val voice_type = json.get("Voice_Type")?.asText("") ?: "" val message_detail = json.get("Message_Detail")?.asText("") ?: "" val message_tags = json.get("Message_TAGS")?.asText("") ?: "" if (description.isNotEmpty()){ if (language.isNotEmpty() && Language.entries.any{ lang -> lang.name == language }){ if (ann_id>0u){ if (voice_type.isNotEmpty() && VoiceType.entries.any{ vt -> vt.name == voice_type }){ if (message_detail.isNotEmpty()){ if (message_tags.isNotEmpty()){ val mb = Messagebank(0u, description, language, ann_id, voice_type, message_detail, message_tags) if (db.messageDB.Add(mb)){ db.messageDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add messagebank to database"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Message_TAGS"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Message_Detail"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Voice_Type"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid ANN_ID"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Language"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Description"))) } delete("List") { // truncate messagebank table if (db.messageDB.Clear()) { db.messageDB.Get() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate messagebank table"))) } } delete("DeleteByIndex/{index}") { // delete by index val index = it.pathParam("index").toUIntOrNull() if (index == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { if (db.messageDB.DeleteByIndex(index.toInt())) { db.messageDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("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(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { val mb = db.messageDB.List.find { xx -> xx.index == index } if (mb == null) { it.status(404).result(objectmapper.writeValueAsString(resultMessage("Messagebank with index $index not found"))) } else { val json: JsonNode = objectmapper.readTree(it.body()) if (json.isEmpty) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("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(objectmapper.writeValueAsString(resultMessage("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.messageDB.UpdateByIndex(index.toInt(), mb)) { db.messageDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else it.status(500) .result(objectmapper.writeValueAsString(resultMessage("Failed to update messagebank with index $index"))) } else it.status(400) .result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for messagebank with index $index"))) } } } } get("ExportXLSX") { val xlsxdata = db.messageDB.Export_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(objectmapper.writeValueAsString(resultMessage("Failed to export messagebank to XLSX"))) } } post("ImportXLSX") { val uploaded = it.uploadedFile("file") if (uploaded == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("No file uploaded"))) return@post } try { val xlsx = XSSFWorkbook(uploaded.content()) if (db.messageDB.Import_XLSX(xlsx)) { db.messageDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to import messagebank from XLSX"))) } } catch (e: Exception) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file"))) } } } path("LanguageLink") { get("List") { // get language link list it.result(MariaDB.ArrayListtoString(db.languageDB.List)) } post("Add"){ // Parse JSON from request body val json: JsonNode = objectmapper.readTree(it.body()) val tag = json.get("tag").asText("") val languages = json.get("language").asText("").split(";") println("Add Language Link, tag=$tag, languages=$languages") if (ValidString(tag)){ if (languages.all { xx -> Language.entries.any { yy -> yy.name.equals(xx,true)} }){ if (!db.languageDB.List.any { ll -> ll.TAG.equals(tag,true) }) { val newvalue = LanguageLink(0u, tag, languages.joinToString(";")) if (db.languageDB.Add(newvalue)){ db.languageDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add language link to database"))) println("Failed to add language link to database") } } else { it.status(400).result(objectmapper.writeValueAsString(resultMessage("TAG=$tag already exists"))) println("TAG=$tag already exists") } } else { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Contains unsupported language"))) println("Contains unsupported language") } } else { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid tag or language"))) println("Invalid tag") } } delete("List") { // truncate language link table if (db.languageDB.Clear()) { db.languageDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate language link table"))) } } delete("DeleteByIndex/{index}") { // delete by index val index = it.pathParam("index").toUIntOrNull() if (index == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { if (db.languageDB.DeleteByIndex(index.toInt())) { db.languageDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("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(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { val ll = db.languageDB.List.find { xx -> xx.index == index } if (ll == null) { it.status(404).result(objectmapper.writeValueAsString(resultMessage("Language link with index $index not found"))) } else { val json: JsonNode = objectmapper.readTree(it.body()) if (json.isEmpty) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("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.languageDB.UpdateByIndex(index.toInt(), ll)) { db.languageDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else it.status(500) .result(objectmapper.writeValueAsString(resultMessage("Failed to update language link with index $index"))) } else it.status(400) .result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for language link with index $index"))) } } } } get("ExportXLSX") { val xlsxdata = db.languageDB.Export_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(objectmapper.writeValueAsString(resultMessage("Failed to export language link to XLSX"))) } } post("ImportXLSX") { val uploaded = it.uploadedFile("file") if (uploaded == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("No file uploaded"))) return@post } try { val xlsx = XSSFWorkbook(uploaded.content()) if (db.languageDB.Import_XLSX(xlsx)) { db.languageDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to import language link from XLSX"))) } } catch (e: Exception) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file"))) } } } path("ScheduleBank") { get("List") { // get timer list it.result(MariaDB.ArrayListtoString(db.scheduleDB.List)) } delete("List") { // truncate timer table if (db.scheduleDB.Clear()) { db.scheduleDB.Get() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate schedulebank table"))) } } delete("DeleteByIndex/{index}") { // delete by index val index = it.pathParam("index").toUIntOrNull() if (index == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { if (db.scheduleDB.DeleteByIndex(index.toInt())) { db.scheduleDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("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(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { val sb = db.scheduleDB.List.find { xx -> xx.index == index } if (sb == null) { it.status(404).result(objectmapper.writeValueAsString(resultMessage("Schedule with index $index not found"))) } else { val json: JsonNode = objectmapper.readTree(it.body()) if (json.isEmpty) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("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(objectmapper.writeValueAsString(resultMessage("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(objectmapper.writeValueAsString(resultMessage("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(objectmapper.writeValueAsString(resultMessage("Invalid Language"))) return@patch } } if (changed) { if (db.scheduleDB.UpdateByIndex(index.toInt(), sb)) { db.scheduleDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else it.status(500) .result(objectmapper.writeValueAsString(resultMessage("Failed to update schedule with index $index"))) } else it.status(400) .result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for schedule with index $index"))) } } } } get("ExportXLSX") { val xlsxdata = db.scheduleDB.Export_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(objectmapper.writeValueAsString(resultMessage("Failed to export schedulebank to XLSX"))) } } post("ImportXLSX") { val uploaded = it.uploadedFile("file") if (uploaded == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("No file uploaded"))) return@post } try { val xlsx = XSSFWorkbook(uploaded.content()) if (db.scheduleDB.Import_XLSX(xlsx)) { db.scheduleDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to import schedulebank from XLSX"))) } } catch (e: Exception) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file"))) } } } path("Log") { get("List") { get1 -> val logdate = get1.queryParam("date") ?: "" val logfilter = get1.queryParam("filter") ?: "" if (ValiDateForLogHtml(logdate)) { if (ValidString(logfilter)) { // ada log filter db.GetLogForHtml(logdate, logfilter) { get1.result(MariaDB.ArrayListtoString(it)) } } else { db.GetLogForHtml(logdate) { get1.result(MariaDB.ArrayListtoString(it)) } } } else { println("Invalid logdate=$logdate") get1.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid logdate"))) } } get("ExportXLSX") { get1 -> val logdate = get1.queryParam("date") ?: "" val logfilter = get1.queryParam("filter") ?: "" println("Export log to XLSX, date=$logdate, filter=$logfilter") if (ValiDateForLogHtml(logdate)) { val xlsxdata = if (ValidString(logfilter)) { db.Export_Log_XLSX(logdate.replace('-','/'), logfilter) } else { db.Export_Log_XLSX(logdate.replace('-','/'), "") } if (xlsxdata != null) { get1.header( "Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) get1.header("Content-Disposition", "attachment; filename=\"log_$logdate.xlsx\"") get1.outputStream().use { out -> xlsxdata.write(out) } } else { get1.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to export log to XLSX"))) } } else get1.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid logdate"))) } } path("BroadcastZones"){ get("List") { // TODO : temporary, convert BroadcastZones to BroadcastZonesHtml, karena harus revisi javascript di Bootstrap Studio val newlist: ArrayList = db.broadcastDB.List.map { xx -> BroadcastZonesHtml( xx.index, xx.description, xx.SoundChannel, xx.id, xx.bp ) } as ArrayList it.result(MariaDB.ArrayListtoString(newlist)) } delete("List"){ // truncate broadcast zones table if (db.broadcastDB.Clear()) { db.broadcastDB.Get() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate broadcast zones table"))) } } post("Add") { val json : JsonNode = objectmapper.readTree(it.body()) val _description = json.get("description").asText("") val _soundchannel = json.get("SoundChannel").asText("") val _box = json.get("Box").asText("") val _relay = json.get("Relay").asText("") if (ValidString(_description)){ if (ValidString(_soundchannel)){ if (ValidString(_box)){ if (ValidString(_relay)){ val newbp = BroadcastZones(0u,_description,_soundchannel,_box,_relay) if (db.broadcastDB.Add(newbp)){ db.broadcastDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add broadcast zone to database"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Relay"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Box"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid SoundChannel"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid description"))) } delete("DeleteByIndex/{index}") { // delete by index val index = it.pathParam("index").toUIntOrNull() if (index == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { if (db.broadcastDB.DeleteByIndex(index.toInt())) { db.broadcastDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete broadcast zone with index $index"))) } } } patch("UpdateByIndex/{index}") { // update by index val index = it.pathParam("index").toUIntOrNull() if (index == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { val bz = db.broadcastDB.List.find { xx -> xx.index == index } if (bz == null) { it.status(404).result(objectmapper.writeValueAsString(resultMessage("Broadcast zone with index $index not found"))) } else { val json: JsonNode = objectmapper.readTree(it.body()) if (json.isEmpty) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("UpdateByIndex with index=$index has empty body"))) } else { val _description = json.get("description").asText("") val _soundchannel = json.get("SoundChannel").asText("") val _box = json.get("Box").asText("") val _relay = json.get("Relay").asText("") var changed = false if (ValidString(_description) && _description != bz.description) { bz.description = _description changed = true } if (ValidString(_soundchannel) && _soundchannel != bz.SoundChannel) { bz.SoundChannel = _soundchannel changed = true } if (ValidString(_box) && _box != bz.id) { bz.id = _box changed = true } if (ValidString(_relay) && _relay != bz.bp) { bz.bp = _relay changed = true } if (changed) { if (db.broadcastDB.UpdateByIndex(index.toInt(), bz)) { db.broadcastDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else it.status(500) .result(objectmapper.writeValueAsString(resultMessage("Failed to update broadcast zone with index $index"))) } else it.status(400) .result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for broadcast zone with index $index"))) } } } } get("ExportXLSX") { val xlsxdata = db.broadcastDB.Export_XLSX() if (xlsxdata != null) { it.header( "Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) it.header("Content-Disposition", "attachment; filename=\"broadcastzones.xlsx\"") it.outputStream().use { out -> xlsxdata.write(out) } } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to export broadcast zones to XLSX"))) } } post("ImportXLSX") { val uploaded = it.uploadedFile("file") if (uploaded == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("No file uploaded"))) return@post } try { val xlsx = XSSFWorkbook(uploaded.content()) if (db.broadcastDB.Import_XLSX(xlsx)) { db.broadcastDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to import broadcast zones from XLSX"))) } } catch (e: Exception) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file"))) } } } path("SoundChannel"){ get("List"){ it.result(MariaDB.ArrayListtoString(db.soundchannelDB.List)) } delete("List"){ // truncate sound channel table if (db.soundchannelDB.Clear()) { db.soundchannelDB.Get() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate sound channel table"))) } } patch("UpdateByIndex/{index}"){ val index = it.pathParam("index").toUIntOrNull() if (index == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid index"))) } else { val sc = db.soundchannelDB.List.find { xx -> xx.index == index } if (sc == null) { println("Sound channel with index $index not found") it.status(404).result(objectmapper.writeValueAsString(resultMessage("Sound channel with index $index not found"))) } else { val json: JsonNode = objectmapper.readTree(it.body()) println("Received JSON: $json") if (json.isEmpty) { println("UpdateByIndex with index=$index has empty body") it.status(400).result(objectmapper.writeValueAsString(resultMessage("UpdateByIndex with index=$index has empty body"))) } else { val _channel = json.get("description").asText("") val _ip = json.get("ip").asText("") println("Update sound channel with index $index, channel=$_channel, ip=$_ip") if (ValidString(_channel)){ if (ValidIPV4(_ip)){ if (_channel.equals(sc.channel) && _ip.equals(sc.ip)) { println("Nothing has changed for sound channel with index $index") it.status(400).result(objectmapper.writeValueAsString(resultMessage("Nothing has changed for sound channel with index $index"))) return@patch } else { // cek apakah ada soundchannel lain yang pakai ip dan channel yang sama if (db.soundchannelDB.List.any { xx -> xx.index != index && _ip.equals(xx.ip) && _channel.equals(xx.channel, true) }) { println("Another sound channel already uses IP $_ip and channel $_channel") it.status(400).result(objectmapper.writeValueAsString(resultMessage("Another sound channel already uses IP $_ip and channel $_channel"))) return@patch } // ada sesuatu yang ganti val newsc = SoundChannel(0u, _channel, _ip) if (db.soundchannelDB.UpdateByIndex(index.toInt(),newsc)){ println("Updated sound channel with index $index") db.soundchannelDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { println("Failed to update sound channel with index $index") it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to update sound channel with index $index"))) return@patch } } } else { println("Invalid IP address") it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid IP address"))) } } else { println("Invalid channel") it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid channel"))) } } } } } get("ExportXLSX"){ val xlsxdata = db.soundchannelDB.Export_XLSX() if (xlsxdata != null) { it.header( "Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) it.header("Content-Disposition", "attachment; filename=\"soundchannel.xlsx\"") it.outputStream().use { out -> xlsxdata.write(out) } } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to export sound channel to XLSX"))) } } post("ImportXLSX"){ val uploaded = it.uploadedFile("file") if (uploaded == null) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("No file uploaded"))) return@post } try { val xlsx = XSSFWorkbook(uploaded.content()) if (db.soundchannelDB.Import_XLSX(xlsx)) { db.soundchannelDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to import sound channel from XLSX"))) } } catch (e: Exception) { it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid XLSX file"))) } } } } } }.start(listenPort) } 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) { ctx.redirect("login.html") } } fun Stop() { app?.stop() } }