Files
AAS_NewGeneration/src/web/WebApp.kt
2025-09-30 14:44:31 +07:00

1105 lines
69 KiB
Kotlin

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<Pair<String, String>>) {
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<BroadcastZonesHtml> = db.broadcastDB.List.map { xx ->
BroadcastZonesHtml(
xx.index,
xx.description,
xx.SoundChannel,
xx.id,
xx.bp
)
} as ArrayList<BroadcastZonesHtml>
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<String?>("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()
}
}