commit 26/08/2025
This commit is contained in:
13
src/Main.kt
13
src/Main.kt
@@ -1,20 +1,31 @@
|
||||
import audio.AudioPlayer
|
||||
import com.sun.jna.Platform
|
||||
import content.ContentCache
|
||||
import database.MariaDB
|
||||
import org.tinylog.Logger
|
||||
import oshi.util.GlobalConfig
|
||||
import web.WebApp
|
||||
|
||||
|
||||
fun main() {
|
||||
if (Platform.isWindows()){
|
||||
// supaya OSHI bisa mendapatkan CPU usage di Windows seperti di Task Manager
|
||||
GlobalConfig.set(GlobalConfig.OSHI_OS_WINDOWS_CPU_UTILITY,true)
|
||||
}
|
||||
Logger.info("Application started" as Any)
|
||||
val audioPlayer = AudioPlayer(44100) // 44100 Hz sampling rate
|
||||
audioPlayer.InitAudio(1)
|
||||
val content = ContentCache()
|
||||
val db = MariaDB()
|
||||
val web = WebApp(3030,
|
||||
listOf(
|
||||
Pair("admin", "password"),
|
||||
Pair("user", "password")
|
||||
)
|
||||
), db
|
||||
)
|
||||
web.Start()
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
package codes
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import oshi.SystemInfo
|
||||
import oshi.hardware.CentralProcessor
|
||||
import oshi.hardware.GlobalMemory
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.function.Consumer
|
||||
|
||||
|
||||
@Suppress("unused")
|
||||
class Somecodes {
|
||||
companion object {
|
||||
|
||||
val current_directory : String = System.getProperty("user.dir")
|
||||
val si = SystemInfo()
|
||||
val processor: CentralProcessor = si.hardware.processor
|
||||
val memory : GlobalMemory = si.hardware.memory
|
||||
|
||||
val datetimeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")
|
||||
|
||||
const val KB_threshold = 1024.0
|
||||
const val MB_threshold = KB_threshold * 1024.0
|
||||
@@ -36,8 +42,59 @@ class Somecodes {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Disk usage using OSHI library.
|
||||
* @param path The path to check disk usage, defaults to the current working directory.
|
||||
* @return A string representing the disk usage of the file system in a human-readable format.
|
||||
*/
|
||||
fun getDiskUsage(path: String = current_directory) : String {
|
||||
return try{
|
||||
val p = Path.of(path).toFile()
|
||||
if (p.exists() && p.isDirectory){
|
||||
val total = p.totalSpace
|
||||
val free = p.freeSpace
|
||||
val used = total - free
|
||||
String.format("Total: %s, Used: %s, Free: %s, Usage: %.2f%%",
|
||||
SizetoHuman(total),
|
||||
SizetoHuman(used),
|
||||
SizetoHuman(free),
|
||||
(used.toDouble() / total * 100)
|
||||
)
|
||||
} else throw Exception()
|
||||
|
||||
} catch (_ : Exception){
|
||||
"N/A"
|
||||
}
|
||||
}
|
||||
|
||||
fun getCPUUsage(cb : Consumer<String>){
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val prev = processor.systemCpuLoadTicks
|
||||
delay(1000)
|
||||
val current = processor.systemCpuLoadTicks
|
||||
|
||||
fun delta(t: CentralProcessor.TickType) = current[t.index] - prev[t.index]
|
||||
|
||||
val idle = delta(CentralProcessor.TickType.IDLE) + delta(CentralProcessor.TickType.IOWAIT)
|
||||
val busy = delta(CentralProcessor.TickType.USER) + delta(CentralProcessor.TickType.SYSTEM) +
|
||||
delta(CentralProcessor.TickType.NICE) + delta(CentralProcessor.TickType.IRQ) +
|
||||
delta(CentralProcessor.TickType.SOFTIRQ)+ delta(CentralProcessor.TickType.STEAL)
|
||||
|
||||
val total = idle + busy
|
||||
val usage = if (total > 0) {
|
||||
(busy.toDouble() / total) * 100
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
cb.accept(String.format("%.2f%%", usage))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get RAM usage using OSHI library.
|
||||
* @return A string representing the total, used, and available memory in a human-readable format.
|
||||
*/
|
||||
fun getMemoryUsage() : String{
|
||||
val totalMemory = memory.total
|
||||
val availableMemory = memory.available
|
||||
|
||||
4
src/database/LanguageLink.kt
Normal file
4
src/database/LanguageLink.kt
Normal file
@@ -0,0 +1,4 @@
|
||||
package database
|
||||
|
||||
@Suppress("unused")
|
||||
data class LanguageLink(val index: UInt, val TAG: String, val Language: String)
|
||||
@@ -1,5 +1,6 @@
|
||||
package database
|
||||
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -29,6 +30,8 @@ class MariaDB (
|
||||
var connected : Boolean = false
|
||||
var SoundbankList : ArrayList<Soundbank> = ArrayList()
|
||||
var MessagebankList : ArrayList<Messagebank> = ArrayList()
|
||||
var LanguageLinkList : ArrayList<LanguageLink> = ArrayList()
|
||||
var SchedulebankList : ArrayList<ScheduleBank> = ArrayList()
|
||||
|
||||
companion object {
|
||||
fun ValidDate(date: String): Boolean {
|
||||
@@ -41,6 +44,22 @@ class MariaDB (
|
||||
val regex = Regex("""^\d{2}:\d{2}:\d{2}$""")
|
||||
return regex.matches(time)
|
||||
}
|
||||
|
||||
private val objectMapper = jacksonObjectMapper()
|
||||
/**
|
||||
* Convert SoundbankList, MessagebankList, LanguageLinkList, or SchedulebankList to a JSON String.
|
||||
* @param list The ArrayList to convert to a String.
|
||||
* @return A JSON String representation of the ArrayList.
|
||||
*/
|
||||
fun <T> ArrayListtoString(list: ArrayList<T>) : String{
|
||||
return try {
|
||||
objectMapper.writeValueAsString(list.toArray())
|
||||
} catch (e: Exception) {
|
||||
Logger.error("Error converting list to JSON: ${e.message}" as Any)
|
||||
"[]"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -52,9 +71,9 @@ class MariaDB (
|
||||
runBlocking {
|
||||
withContext(Dispatchers.IO){
|
||||
Reload_Messagebank()
|
||||
Logger.info { "Messagebank loaded" }
|
||||
Reload_Soundbank()
|
||||
Logger.info { "Soundbank loaded" }
|
||||
Reload_LanguageLink()
|
||||
Reload_Schedulebank()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +82,8 @@ class MariaDB (
|
||||
Logger.info { "Loading MariaDB completed" }
|
||||
Logger.info { "Soundbank count: ${SoundbankList.size}" }
|
||||
Logger.info { "Messagebank count: ${MessagebankList.size}" }
|
||||
Logger.info { "LanguageLink count: ${LanguageLinkList.size}" }
|
||||
Logger.info { "Schedulebank count: ${SchedulebankList.size}" }
|
||||
|
||||
|
||||
} catch (e : Exception) {
|
||||
@@ -165,6 +186,54 @@ class MariaDB (
|
||||
consumer.accept(logList)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the ScheduleBank list from the database.
|
||||
*/
|
||||
private fun Reload_Schedulebank() {
|
||||
SchedulebankList.clear()
|
||||
try {
|
||||
val statement = connection?.createStatement()
|
||||
val resultSet = statement?.executeQuery("SELECT * FROM schedulebank")
|
||||
while (resultSet?.next() == true) {
|
||||
val schedulebank = ScheduleBank(
|
||||
resultSet.getLong("index").toUInt(),
|
||||
resultSet.getString("Description"),
|
||||
resultSet.getString("Day"),
|
||||
resultSet.getString("Time"),
|
||||
resultSet.getString("Soundpath"),
|
||||
resultSet.getInt("Repeat").toUByte(),
|
||||
resultSet.getBoolean("Enable"),
|
||||
resultSet.getString("BroadcastZones"),
|
||||
resultSet.getString("Language")
|
||||
)
|
||||
SchedulebankList.add(schedulebank)
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
Logger.error("Error fetching schedulebanks: ${e.message}" as Any)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the language link list from the database.
|
||||
*/
|
||||
private fun Reload_LanguageLink() {
|
||||
LanguageLinkList.clear()
|
||||
try {
|
||||
val statement = connection?.createStatement()
|
||||
val resultSet = statement?.executeQuery("SELECT * FROM languagelink")
|
||||
while (resultSet?.next() == true) {
|
||||
val languageLink = LanguageLink(
|
||||
resultSet.getLong("index").toUInt(),
|
||||
resultSet.getString("TAG"),
|
||||
resultSet.getString("Language")
|
||||
)
|
||||
LanguageLinkList.add(languageLink)
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
Logger.error("Error fetching language links: ${e.message}" as Any)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the soundbank list from the database.
|
||||
*/
|
||||
|
||||
4
src/database/ScheduleBank.kt
Normal file
4
src/database/ScheduleBank.kt
Normal file
@@ -0,0 +1,4 @@
|
||||
package database
|
||||
|
||||
@Suppress("unused")
|
||||
data class ScheduleBank(val index: UInt, val Description: String, val Day: String, val Time: String, val Soundpath: String, val Repeat: UByte, val Enable: Boolean, val BroadcastZones: String, val Language: String)
|
||||
@@ -2,6 +2,7 @@ package web
|
||||
|
||||
import codes.Somecodes
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import database.MariaDB
|
||||
import io.javalin.Javalin
|
||||
import io.javalin.apibuilder.ApiBuilder.before
|
||||
import io.javalin.apibuilder.ApiBuilder.get
|
||||
@@ -9,13 +10,13 @@ import io.javalin.apibuilder.ApiBuilder.path
|
||||
import io.javalin.apibuilder.ApiBuilder.post
|
||||
import io.javalin.apibuilder.ApiBuilder.ws
|
||||
import io.javalin.http.Context
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Suppress("unused")
|
||||
class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>, val db: MariaDB) {
|
||||
|
||||
var app : Javalin? = null
|
||||
private val objectmapper = jacksonObjectMapper()
|
||||
|
||||
val objectmapper = jacksonObjectMapper()
|
||||
fun Start() {
|
||||
app = Javalin.create {
|
||||
config ->
|
||||
@@ -55,35 +56,58 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
|
||||
path("home.html") {
|
||||
before { CheckUsers(it) }
|
||||
ws("/ws") { it ->
|
||||
ws("/ws") { ws ->
|
||||
// WebSocket endpoint for home
|
||||
it.onClose {
|
||||
ws.onClose { wsCloseContext ->
|
||||
// TODO Handle WebSocket close event
|
||||
println("WebSocket closed: ${it.session.remoteAddress}")
|
||||
println("WebSocket closed: ${wsCloseContext.session.remoteAddress}")
|
||||
}
|
||||
it.onMessage {
|
||||
ws.onMessage { wsMessageContext ->
|
||||
try{
|
||||
val cmd = objectmapper.readValue(it.message(), WebsocketCommand::class.java)
|
||||
val cmd = objectmapper.readValue(wsMessageContext.message(), WebsocketCommand::class.java)
|
||||
when (cmd.command) {
|
||||
"getSystemTime" ->{
|
||||
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, LocalDateTime.now().format(Somecodes.datetimeformat1))))
|
||||
}
|
||||
"getCPUStatus" ->{
|
||||
//TODO Get CPU status
|
||||
|
||||
it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK")))
|
||||
Somecodes.getCPUUsage { vv ->
|
||||
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, vv)))
|
||||
}
|
||||
}
|
||||
"getMemoryStatus" ->{
|
||||
// TODO Get Memory status
|
||||
it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, Somecodes.getMemoryUsage())))
|
||||
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, Somecodes.getMemoryUsage())))
|
||||
}
|
||||
"getDiskStatus" ->{
|
||||
// TODO Get Disk status
|
||||
it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK")))
|
||||
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, Somecodes.getDiskUsage())))
|
||||
}
|
||||
"getNetworkStatus" ->{
|
||||
// TODO Get Network status
|
||||
it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK")))
|
||||
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK")))
|
||||
}
|
||||
"getSoundBankList" ->{
|
||||
println("getSoundBankList command received")
|
||||
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.SoundbankList))))
|
||||
}
|
||||
"getMessageBankList"->{
|
||||
println("getMessageBankList command received")
|
||||
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.MessagebankList))) )
|
||||
}
|
||||
"getLanguageList"->{
|
||||
println("getLanguageList command received")
|
||||
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.LanguageLinkList))))
|
||||
}
|
||||
"getTimerList"->{
|
||||
println("getTimerList command received")
|
||||
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.SchedulebankList))))
|
||||
}
|
||||
"getLog" ->{
|
||||
println("getLog command received")
|
||||
}
|
||||
"getSetting" ->{
|
||||
println("getSetting command received")
|
||||
}
|
||||
else -> {
|
||||
it.send(objectmapper.writeValueAsString(WebsocketReply("error", "Unknown command: ${cmd.command}")))
|
||||
wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply("error", "Unknown command: ${cmd.command}")))
|
||||
}
|
||||
}
|
||||
} catch (e: Exception){
|
||||
@@ -91,30 +115,37 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
}
|
||||
|
||||
}
|
||||
it.onConnect {
|
||||
ws.onConnect { wsConnectContext ->
|
||||
// TODO Handle WebSocket connect event
|
||||
println("WebSocket connected: ${it.session.remoteAddress}")
|
||||
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) }
|
||||
|
||||
}
|
||||
}
|
||||
}.start(listenPort)
|
||||
@@ -122,19 +153,13 @@ class WebApp(val listenPort: Int, val userlist: List<Pair<String, String>>) {
|
||||
}
|
||||
|
||||
fun CheckUsers(ctx: Context){
|
||||
println("Checking user session at ${ctx.req().requestURI}")
|
||||
val user = ctx.sessionAttribute<String?>("user")
|
||||
if (user == null) {
|
||||
println("User not logged in, redirecting to login page")
|
||||
ctx.redirect("login.html")
|
||||
}
|
||||
println("User is logged in: $user")
|
||||
val foundUser = userlist.find { it.first == user }
|
||||
if (foundUser==null) {
|
||||
println("User not found in user list, redirecting to login page")
|
||||
ctx.redirect("login.html")
|
||||
} else {
|
||||
println("User found: $user")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user