commit 22/01/2026
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
|||||||
out/
|
out/
|
||||||
!**/src/main/**/out/
|
!**/src/main/**/out/
|
||||||
!**/src/test/**/out/
|
!**/src/test/**/out/
|
||||||
|
logs/
|
||||||
|
|
||||||
### Kotlin ###
|
### Kotlin ###
|
||||||
.kotlin
|
.kotlin
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/testResources" type="java-test-resource" />
|
<sourceFolder url="file://$MODULE_DIR$/testResources" type="java-test-resource" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/logs" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Configuration saved on 2026-01-21T17:17:53.684082100
|
#Configuration saved on 2026-01-22T13:49:56.778957100
|
||||||
#Wed Jan 21 17:17:53 WIB 2026
|
#Thu Jan 22 13:49:56 WIB 2026
|
||||||
activemq_brokerurl=tcp\://localhost\:61616
|
activemq_brokerurl=tcp\://localhost\:61616
|
||||||
activemq_password=admin
|
activemq_password=admin
|
||||||
activemq_queuename=TEST.QUEUE
|
activemq_queuename=TEST.QUEUE
|
||||||
|
|||||||
@@ -1,15 +1,35 @@
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// Your code here
|
// Your code here
|
||||||
console.log("log.js is loaded and ready.");
|
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() {
|
$('#logoutbtn').on('click', function() {
|
||||||
// Clear session storage on logout
|
// Clear session storage on logout
|
||||||
fetch('/logout').then(() => {
|
fetch('/logout').then(() => {
|
||||||
window.location.href = '/login.html';
|
window.location.href = '/login.html';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
$logchooser.on('change', function() {
|
||||||
|
const selectedLog = $(this).val();
|
||||||
|
GetLog(selectedLog);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(window).on('beforeunload', function() {
|
$(window).on('beforeunload', function() {
|
||||||
console.log("User is leaving log.html");
|
console.log("User is leaving log.html");
|
||||||
// Your cleanup code here
|
// Your cleanup code here
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
<div class="col-2"><label class="col-form-label w-100 h-100 ms-1">Log Chooser</label></div>
|
<div class="col-2"><label class="col-form-label w-100 h-100 ms-1">Log Chooser</label></div>
|
||||||
<div class="col"><input class="form-control-lg" type="date"></div>
|
<div class="col"><input class="form-control-lg" id="logchooser" type="date"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
|
|||||||
4
src/Log/LogData.kt
Normal file
4
src/Log/LogData.kt
Normal 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
59
src/Log/LogGetter.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import ActiveMQ.ActiveMQClient
|
|||||||
import Other.Config
|
import Other.Config
|
||||||
import Web.WebUI
|
import Web.WebUI
|
||||||
import database.MySQLInjector
|
import database.MySQLInjector
|
||||||
|
import org.tinylog.provider.ProviderRegistry
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
|
||||||
lateinit var config : Config
|
lateinit var config : Config
|
||||||
@@ -23,5 +24,7 @@ fun main() {
|
|||||||
webUI.Stop()
|
webUI.Stop()
|
||||||
activeclient.Stop()
|
activeclient.Stop()
|
||||||
mysql.Stop()
|
mysql.Stop()
|
||||||
|
// shutdown tinylog properly
|
||||||
|
ProviderRegistry.getLoggingProvider().shutdown();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
129
src/Web/WebUI.kt
129
src/Web/WebUI.kt
@@ -1,12 +1,11 @@
|
|||||||
package Web
|
package Web
|
||||||
|
|
||||||
|
import Log.LogGetter
|
||||||
import config
|
import config
|
||||||
import io.javalin.Javalin
|
import io.javalin.Javalin
|
||||||
import io.javalin.apibuilder.ApiBuilder.before
|
import io.javalin.apibuilder.ApiBuilder.before
|
||||||
import io.javalin.apibuilder.ApiBuilder.get
|
import io.javalin.apibuilder.ApiBuilder.get
|
||||||
import io.javalin.apibuilder.ApiBuilder.path
|
|
||||||
import io.javalin.apibuilder.ApiBuilder.post
|
import io.javalin.apibuilder.ApiBuilder.post
|
||||||
import io.javalin.apibuilder.ApiBuilder.ws
|
|
||||||
import org.tinylog.Logger
|
import org.tinylog.Logger
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@@ -14,41 +13,57 @@ import org.tinylog.Logger
|
|||||||
* Start WebUI Server
|
* Start WebUI Server
|
||||||
*/
|
*/
|
||||||
class WebUI{
|
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 ->
|
private var app : Javalin = Javalin.create { cfg ->
|
||||||
cfg.staticFiles.add("/")
|
cfg.staticFiles.add("/")
|
||||||
cfg.router.apiBuilder {
|
cfg.router.apiBuilder {
|
||||||
path("/"){
|
get("/"){
|
||||||
get {
|
if (config.WebUsername==it.cookie("username")){
|
||||||
if (config.WebUsername==it.cookie("username")){
|
Logger.info{"${it.ip()} logged in as ${it.cookie("username")}, forward to home.html"}
|
||||||
Logger.info{"${it.ip()} logged in as ${it.cookie("username")}, forward to home.html"}
|
it.redirect("home.html")
|
||||||
it.redirect("home.html")
|
} else{
|
||||||
} else{
|
Logger.info{"${it.ip()} have not logged in, forward to login.html"}
|
||||||
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")
|
|
||||||
it.redirect("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"){
|
get("getSetting"){
|
||||||
val fd = farmData(config.ActiveMQ_BrokerURL, config.ActiveMQ_Username, config.ActiveMQ_Password, config.ActiveMQ_QueueName)
|
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("home.html"){
|
||||||
before{
|
if (config.WebUsername!=it.cookie("username")){
|
||||||
if (config.WebUsername!=it.cookie("username")){
|
Logger.info {"${it.ip()} Have not logged in, forward to login.html"}
|
||||||
Logger.info {"${it.ip()} Have not logged in, forward to login.html"}
|
it.redirect("login.html")
|
||||||
it.redirect("login.html")
|
return@before
|
||||||
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("log.html"){
|
||||||
before{
|
if (config.WebUsername!=it.cookie("username")){
|
||||||
if (config.WebUsername!=it.cookie("username")){
|
Logger.info{"${it.ip()} Have not logged in, forward to login.html"}
|
||||||
Logger.info{"${it.ip()} Have not logged in, forward to login.html"}
|
it.redirect("login.html")
|
||||||
it.redirect("login.html")
|
return@before
|
||||||
return@before
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
path("setting.html"){
|
before("setting.html"){
|
||||||
before{
|
if (config.WebUsername!=it.cookie("username")){
|
||||||
if (config.WebUsername!=it.cookie("username")){
|
Logger.info{"${it.ip()} Have not logged in, forward to login.html"}
|
||||||
Logger.info{"${it.ip()} Have not logged in, forward to login.html"}
|
it.redirect("login.html")
|
||||||
it.redirect("login.html")
|
return@before
|
||||||
return@before
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
10
src/tinylog.properties
Normal file
10
src/tinylog.properties
Normal 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
|
||||||
Reference in New Issue
Block a user