diff --git a/.gitignore b/.gitignore index 3ddbf4c..0ff6dd4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ out/ !**/src/main/**/out/ !**/src/test/**/out/ +logs/ ### Kotlin ### .kotlin diff --git a/FarmToAAS.iml b/FarmToAAS.iml index 2c8a374..ac8e903 100644 --- a/FarmToAAS.iml +++ b/FarmToAAS.iml @@ -8,6 +8,7 @@ + diff --git a/config.properties b/config.properties index 85459ca..0075de8 100644 --- a/config.properties +++ b/config.properties @@ -1,5 +1,5 @@ -#Configuration saved on 2026-01-21T17:17:53.684082100 -#Wed Jan 21 17:17:53 WIB 2026 +#Configuration saved on 2026-01-22T13:49:56.778957100 +#Thu Jan 22 13:49:56 WIB 2026 activemq_brokerurl=tcp\://localhost\:61616 activemq_password=admin activemq_queuename=TEST.QUEUE diff --git a/html/assets/js/log.js b/html/assets/js/log.js index 3dbbff4..fd32f56 100644 --- a/html/assets/js/log.js +++ b/html/assets/js/log.js @@ -1,15 +1,35 @@ $(document).ready(function() { // Your code here console.log("log.js is loaded and ready."); + let $logchooser = $('#logchooser'); + // if logchooser value is null, set to today's date in format mm/dd/yyyy + if ($logchooser.val() === null) { + $logchooser.val(new Date().toISOString().split('T')[0]); + } + $('#logoutbtn').on('click', function() { // Clear session storage on logout fetch('/logout').then(() => { window.location.href = '/login.html'; }); }); + $logchooser.on('change', function() { + const selectedLog = $(this).val(); + GetLog(selectedLog); + }); }); $(window).on('beforeunload', function() { console.log("User is leaving log.html"); // Your cleanup code here -}); \ No newline at end of file +}); + +// function GetLog with date parameter in string, with default value today's date in format dd/mm/yyyy +function GetLog(date = new Date().toISOString().split('T')[0]) { + console.log("Fetching logs for date: " + date); + Get('getLogs?'+new URLSearchParams({date: date}).toString(), function(data){ + console.log(data); + }, function(error){ + alert(error); + }) +} \ No newline at end of file diff --git a/html/log.html b/html/log.html index 5cfa056..2aa284e 100644 --- a/html/log.html +++ b/html/log.html @@ -23,7 +23,7 @@
-
+
diff --git a/src/Log/LogData.kt b/src/Log/LogData.kt new file mode 100644 index 0000000..ae2f159 --- /dev/null +++ b/src/Log/LogData.kt @@ -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) diff --git a/src/Log/LogGetter.kt b/src/Log/LogGetter.kt new file mode 100644 index 0000000..5a012fc --- /dev/null +++ b/src/Log/LogGetter.kt @@ -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 { + // 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() + 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 + } +} \ No newline at end of file diff --git a/src/Main.kt b/src/Main.kt index 8f09496..302150e 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -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(); }) } \ No newline at end of file diff --git a/src/Web/WebUI.kt b/src/Web/WebUI.kt index 2ad259a..83897c2 100644 --- a/src/Web/WebUI.kt +++ b/src/Web/WebUI.kt @@ -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 } - - } } diff --git a/src/tinylog.properties b/src/tinylog.properties new file mode 100644 index 0000000..02167e2 --- /dev/null +++ b/src/tinylog.properties @@ -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 \ No newline at end of file