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.* 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 Somecodes.GetNetworkStatus("") 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"))) } } post("Add"){ // TODO add new schedule // recheck lagi tambahan steph val json: JsonNode = objectmapper.readTree(it.body()) val description = json.get("Description")?.asText("") ?: "" val day = json.get("Day")?.asText("") ?: "" val time = json.get("Time")?.asText("") ?: "" val soundpath = json.get("Soundpath")?.asText("") ?: "" val repeat = json.get("Repeat")?.asInt()?.toUByte() ?: 0u val enable = json.get("Enable")?.asBoolean() ?: false val broadcast_zones = json.get("BroadcastZones")?.asText("") ?: "" val language = json.get("Language")?.asText("") ?: "" if (ValidString(description)){ if (ValidString(day) && ValidScheduleDay(day)){ if (ValidString(time) && ValidScheduleTime(time)){ if (ValidString(soundpath) && ValidFile(soundpath)){ if (repeat in 0u..127u){ if (ValidString(broadcast_zones)){ val zones = broadcast_zones.split(";") if (zones.all { zz -> db.broadcastDB.List.any { xx -> xx.description.equals(zz,true) } }){ if (ValidString(language) && Language.entries.any{ lang -> lang.name == language }){ val newvalue = ScheduleBank(0u, description, day, time, soundpath, repeat, enable, broadcast_zones, language) if (db.scheduleDB.Add(newvalue)){ db.scheduleDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to add schedule to database"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Language"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Contains unsupported BroadcastZones"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid BroadcastZones"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Repeat, must be between 0-127"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Soundpath"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Time format, must be HH:mm"))) } else it.status(400).result(objectmapper.writeValueAsString(resultMessage("Invalid Day format"))) } 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.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(db.broadcastDB.List)) } 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"))) } } } // Steph : coba tambah untuk QueuePaging Table. Belum ada di JS file(?) path("QueuePaging"){ get("List"){ it.result(MariaDB.ArrayListtoString(db.queuepagingDB.List)) } delete("List"){ // truncate queue paging table if (db.queuepagingDB.Clear()) { db.queuepagingDB.Get() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate queue paging 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.queuepagingDB.DeleteByIndex(index.toInt())) { db.queuepagingDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete queue paging with index $index"))) } } } } // Steph : coba tambah untuk QueueTable Table. Belum ada di JS file(?) path("QueueTable"){ get("List"){ it.result(MariaDB.ArrayListtoString(db.queuetableDB.List)) } delete("List"){ // truncate queue table if (db.queuetableDB.Clear()) { db.queuetableDB.Get() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to truncate queue sound 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.queuetableDB.DeleteByIndex(index.toInt())) { db.queuetableDB.Resort() it.result(objectmapper.writeValueAsString(resultMessage("OK"))) } else { it.status(500).result(objectmapper.writeValueAsString(resultMessage("Failed to delete queue sound with index $index"))) } } } } } } }.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() } }