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