Files
AAS_NewGeneration/src/web/WebApp.kt
2025-10-08 14:32:09 +07:00

1200 lines
76 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.*
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
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<BroadcastZonesHtml> = db.broadcastDB.List.map { xx ->
// BroadcastZonesHtml(
// xx.index,
// xx.description,
// xx.SoundChannel,
// xx.id,
// xx.bp
// )
// } as ArrayList<BroadcastZonesHtml>
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<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()
}
}