commit 22/01/2026

This commit is contained in:
2026-01-22 13:57:34 +07:00
parent 34cf8c97df
commit ffd41ee225
10 changed files with 163 additions and 72 deletions

4
src/Log/LogData.kt Normal file
View File

@@ -0,0 +1,4 @@
package Log
@Suppress("UNUSED")
data class LogData(val index: Int, val date: String, val time: String, val function: String, val message: String)

59
src/Log/LogGetter.kt Normal file
View File

@@ -0,0 +1,59 @@
package Log
import org.tinylog.Logger
import java.nio.file.Files
import java.nio.file.Paths
/**
* Class to get log data from log files
*/
@Suppress("UNUSED")
class LogGetter {
// valid date format either dd-MM-yyyy or dd/MM/yyyy
private val dateRegex = """(\d{2})[-/](\d{2})[-/](\d{4})""".toRegex()
// each logline is in format dd/MM/yyyy HH:mm:ss Function: Message
private val logLineRegex = """(\d{2}/\d{2}/\d{4}) (\d{2}:\d{2}:\d{2}) (.+?): (.+)""".toRegex()
// log folder is current working directory + /logs
private val logFolder: String = System.getProperty("user.dir") + "/logs"
/**
* Get log data for a specific date
* @param date Date in format dd-MM-yyyy or dd/MM/yyyy
* @return List of LogData for the specified date or empty list if date is invalid or no logs found
*/
fun getLog(date: String) : ArrayList<LogData> {
// check date with dateRegex and find dd MM yyyy
val x = dateRegex.find(date)
if (x==null) {
Logger.error { "Invalid date format: $date" }
return arrayListOf()
}
val (day, month, year) = x.destructured
// logFilePath is logFolder/year/month/day.log
val p = Paths.get(logFolder, year, month, "${day}.log")
// check if file exists
if (!Files.exists(p)) {
Logger.error { "Log file does not exist: $p" }
return arrayListOf()
}
val logDataList = ArrayList<LogData>()
Files.newBufferedReader(p).useLines { lines ->
lines.forEachIndexed { index, string ->
val ll = logLineRegex.find(string)
if (ll != null && ll.groupValues.size == 5) {
val logData = LogData(
index ,
ll.groupValues[1],
ll.groupValues[2],
ll.groupValues[3],
ll.groupValues[4]
)
logDataList.add(logData)
}
}
}
return logDataList
}
}

View File

@@ -2,6 +2,7 @@ import ActiveMQ.ActiveMQClient
import Other.Config
import Web.WebUI
import database.MySQLInjector
import org.tinylog.provider.ProviderRegistry
import java.util.function.Consumer
lateinit var config : Config
@@ -23,5 +24,7 @@ fun main() {
webUI.Stop()
activeclient.Stop()
mysql.Stop()
// shutdown tinylog properly
ProviderRegistry.getLoggingProvider().shutdown();
})
}

View File

@@ -1,12 +1,11 @@
package Web
import Log.LogGetter
import config
import io.javalin.Javalin
import io.javalin.apibuilder.ApiBuilder.before
import io.javalin.apibuilder.ApiBuilder.get
import io.javalin.apibuilder.ApiBuilder.path
import io.javalin.apibuilder.ApiBuilder.post
import io.javalin.apibuilder.ApiBuilder.ws
import org.tinylog.Logger
@Suppress("unused")
@@ -14,41 +13,57 @@ import org.tinylog.Logger
* Start WebUI Server
*/
class WebUI{
// regex untuk date dengan format YYYY-MM-DD
private val dateRegex1 = """\d{4}-\d{2}-\d{2}""".toRegex()
private var app : Javalin = Javalin.create { cfg ->
cfg.staticFiles.add("/")
cfg.router.apiBuilder {
path("/"){
get {
if (config.WebUsername==it.cookie("username")){
Logger.info{"${it.ip()} logged in as ${it.cookie("username")}, forward to home.html"}
it.redirect("home.html")
} else{
Logger.info{"${it.ip()} have not logged in, forward to login.html"}
it.redirect("login.html")
}
}
}
path("login.html"){
post{
val username = it.formParam("username")
val password = it.formParam("password")
if (config.WebUsername==username && config.WebPassword==password) {
Logger.info { "${it.ip()} login successful for user $username" }
it.cookie("username", username)
it.redirect("home.html")
} else {
Logger.info { "${it.ip()} Login failed for user $username" }
it.redirect("/login.html?error=1")
}
}
}
path("logout"){
get {
Logger.info{"${it.ip()} User ${it.cookie("username")} logged out"}
it.removeCookie("username")
get("/"){
if (config.WebUsername==it.cookie("username")){
Logger.info{"${it.ip()} logged in as ${it.cookie("username")}, forward to home.html"}
it.redirect("home.html")
} else{
Logger.info{"${it.ip()} have not logged in, forward to login.html"}
it.redirect("login.html")
}
}
post("login.html"){
val username = it.formParam("username")
val password = it.formParam("password")
if (config.WebUsername==username && config.WebPassword==password) {
Logger.info { "${it.ip()} login successful for user $username" }
it.cookie("username", username)
it.redirect("home.html")
} else {
Logger.info { "${it.ip()} Login failed for user $username" }
it.redirect("/login.html?error=1")
}
}
get("getLogs"){
var date = it.queryParam("date")
Logger.info{"${it.ip()} User ${it.cookie("username")} requested logs for date: $date"}
if (date!=null){
if (date.isNotEmpty()){
if (dateRegex1.matches(date)){
// ketemu format YYYY-MM-DD, convert format ke dd/MM/yyyy
val parts = date.split("-")
date = "${parts[2]}/${parts[1]}/${parts[0]}"
}
val logs = LogGetter().getLog(date)
if (logs.isNotEmpty()){
it.json(logs)
} else it.status(404).json(webReply("No logs found for the specified date"))
} else it.status(400).json(webReply("Date parameter is empty"))
} else it.status(400).json(webReply("Please provide a date"))
}
get("logout"){
Logger.info{"${it.ip()} User ${it.cookie("username")} logged out"}
it.removeCookie("username")
it.redirect("login.html")
}
get("getSetting"){
val fd = farmData(config.ActiveMQ_BrokerURL, config.ActiveMQ_Username, config.ActiveMQ_Password, config.ActiveMQ_QueueName)
@@ -97,48 +112,26 @@ class WebUI{
}
}
path("home.html"){
before{
if (config.WebUsername!=it.cookie("username")){
Logger.info {"${it.ip()} Have not logged in, forward to login.html"}
it.redirect("login.html")
return@before
}
before("home.html"){
if (config.WebUsername!=it.cookie("username")){
Logger.info {"${it.ip()} Have not logged in, forward to login.html"}
it.redirect("login.html")
return@before
}
ws("/ws"){ ws ->
ws.onConnect { wsconnectcontext -> Logger.info { "WebSocket connected: ${wsconnectcontext.sessionId()}"; wsconnectcontext.enableAutomaticPings() } }
ws.onClose { wsclosecontext -> Logger.info { "WebSocket closed: ${wsclosecontext.sessionId()}" } }
ws.onError { wserrorcontext -> Logger.error { "WebSocket error in session ${wserrorcontext.sessionId()}: ${wserrorcontext.error()?.message}" } }
ws.onMessage { wsMessageContext ->
// TODO handle incoming messages
}
}
}
path("log.html"){
before{
if (config.WebUsername!=it.cookie("username")){
Logger.info{"${it.ip()} Have not logged in, forward to login.html"}
it.redirect("login.html")
return@before
}
before("log.html"){
if (config.WebUsername!=it.cookie("username")){
Logger.info{"${it.ip()} Have not logged in, forward to login.html"}
it.redirect("login.html")
return@before
}
}
path("setting.html"){
before{
if (config.WebUsername!=it.cookie("username")){
Logger.info{"${it.ip()} Have not logged in, forward to login.html"}
it.redirect("login.html")
return@before
}
before("setting.html"){
if (config.WebUsername!=it.cookie("username")){
Logger.info{"${it.ip()} Have not logged in, forward to login.html"}
it.redirect("login.html")
return@before
}
}
}

10
src/tinylog.properties Normal file
View File

@@ -0,0 +1,10 @@
writer1 = console
writer1.level = info
writer1.format = {date:dd/MM/yyyy HH:mm:ss} {class}.{method}(): {message}
writer2 = rolling file
writer2.format = {date:dd/MM/yyyy HH:mm:ss} {class}.{method}(): {message}
writer2.file = logs/{date:yyyy}/{date:MM}/{date:dd}.log
writer2.level = info
autoshutdown = false # optional, default: true