update 22

This commit is contained in:
2026-01-07 01:14:51 +09:00
parent 57c3eea429
commit 66e8e21302
220 changed files with 2911 additions and 700 deletions

View File

@@ -4,6 +4,9 @@
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>LogHunter</title>
<style>
* {

View File

@@ -1,16 +1,19 @@
{
"name": "log-hunter-frontend",
"version": "0.0.1",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "log-hunter-frontend",
"version": "0.0.1",
"version": "1.0.0",
"dependencies": {
"axios": "^1.6.5",
"chart.js": "^4.5.1",
"chartjs-plugin-datalabels": "^2.2.0",
"pinia": "^2.1.7",
"vue": "^3.4.15",
"vue-chartjs": "^5.3.3",
"vue-router": "^4.2.5"
},
"devDependencies": {
@@ -65,9 +68,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
"integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
"cpu": [
"ppc64"
],
@@ -82,9 +85,9 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
"integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
"cpu": [
"arm"
],
@@ -99,9 +102,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
"integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
"cpu": [
"arm64"
],
@@ -116,9 +119,9 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
"integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
"cpu": [
"x64"
],
@@ -133,9 +136,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
"integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
"cpu": [
"arm64"
],
@@ -150,9 +153,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
"integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
"cpu": [
"x64"
],
@@ -167,9 +170,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
"integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
"cpu": [
"arm64"
],
@@ -184,9 +187,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
"integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
"cpu": [
"x64"
],
@@ -201,9 +204,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
"integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
"cpu": [
"arm"
],
@@ -218,9 +221,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
"integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
"cpu": [
"arm64"
],
@@ -235,9 +238,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
"integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
"cpu": [
"ia32"
],
@@ -252,9 +255,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
"integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
"cpu": [
"loong64"
],
@@ -269,9 +272,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
"integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
"cpu": [
"mips64el"
],
@@ -286,9 +289,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
"integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
"cpu": [
"ppc64"
],
@@ -303,9 +306,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
"integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
"cpu": [
"riscv64"
],
@@ -320,9 +323,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
"integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
"cpu": [
"s390x"
],
@@ -337,9 +340,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
"integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
"cpu": [
"x64"
],
@@ -354,9 +357,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
"integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
"cpu": [
"x64"
],
@@ -371,9 +374,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
"integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
"cpu": [
"x64"
],
@@ -388,9 +391,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
"integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
"cpu": [
"x64"
],
@@ -405,9 +408,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
"integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
"cpu": [
"arm64"
],
@@ -422,9 +425,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
"integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
"cpu": [
"ia32"
],
@@ -439,9 +442,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
"integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
"cpu": [
"x64"
],
@@ -461,6 +464,12 @@
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"license": "MIT"
},
"node_modules/@kurkle/color": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.55.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz",
@@ -819,67 +828,67 @@
"license": "MIT"
},
"node_modules/@vitejs/plugin-vue": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz",
"integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==",
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
"integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"peerDependencies": {
"vite": "^5.0.0",
"vite": "^5.0.0 || ^6.0.0",
"vue": "^3.2.25"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz",
"integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==",
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz",
"integrity": "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.23.6",
"@vue/shared": "3.4.15",
"entities": "^4.5.0",
"@babel/parser": "^7.28.5",
"@vue/shared": "3.5.26",
"entities": "^7.0.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz",
"integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==",
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.26.tgz",
"integrity": "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==",
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.4.15",
"@vue/shared": "3.4.15"
"@vue/compiler-core": "3.5.26",
"@vue/shared": "3.5.26"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz",
"integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==",
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz",
"integrity": "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.23.6",
"@vue/compiler-core": "3.4.15",
"@vue/compiler-dom": "3.4.15",
"@vue/compiler-ssr": "3.4.15",
"@vue/shared": "3.4.15",
"@babel/parser": "^7.28.5",
"@vue/compiler-core": "3.5.26",
"@vue/compiler-dom": "3.5.26",
"@vue/compiler-ssr": "3.5.26",
"@vue/shared": "3.5.26",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5",
"postcss": "^8.4.33",
"source-map-js": "^1.0.2"
"magic-string": "^0.30.21",
"postcss": "^8.5.6",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz",
"integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==",
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz",
"integrity": "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.4.15",
"@vue/shared": "3.4.15"
"@vue/compiler-dom": "3.5.26",
"@vue/shared": "3.5.26"
}
},
"node_modules/@vue/devtools-api": {
@@ -889,52 +898,53 @@
"license": "MIT"
},
"node_modules/@vue/reactivity": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz",
"integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==",
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.26.tgz",
"integrity": "sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==",
"license": "MIT",
"dependencies": {
"@vue/shared": "3.4.15"
"@vue/shared": "3.5.26"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz",
"integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==",
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.26.tgz",
"integrity": "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.4.15",
"@vue/shared": "3.4.15"
"@vue/reactivity": "3.5.26",
"@vue/shared": "3.5.26"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz",
"integrity": "sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==",
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.26.tgz",
"integrity": "sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ==",
"license": "MIT",
"dependencies": {
"@vue/runtime-core": "3.4.15",
"@vue/shared": "3.4.15",
"csstype": "^3.1.3"
"@vue/reactivity": "3.5.26",
"@vue/runtime-core": "3.5.26",
"@vue/shared": "3.5.26",
"csstype": "^3.2.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz",
"integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==",
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.26.tgz",
"integrity": "sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==",
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.4.15",
"@vue/shared": "3.4.15"
"@vue/compiler-ssr": "3.5.26",
"@vue/shared": "3.5.26"
},
"peerDependencies": {
"vue": "3.4.15"
"vue": "3.5.26"
}
},
"node_modules/@vue/shared": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz",
"integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==",
"license": "MIT"
},
"node_modules/asynckit": {
@@ -944,13 +954,13 @@
"license": "MIT"
},
"node_modules/axios": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
"integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
@@ -967,6 +977,27 @@
"node": ">= 0.4"
}
},
"node_modules/chart.js": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
"license": "MIT",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=8"
}
},
"node_modules/chartjs-plugin-datalabels": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz",
"integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==",
"license": "MIT",
"peerDependencies": {
"chart.js": ">=3.0.0"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -1009,9 +1040,9 @@
}
},
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz",
"integrity": "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -1066,9 +1097,9 @@
}
},
"node_modules/esbuild": {
"version": "0.19.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
"integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1079,29 +1110,29 @@
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.19.12",
"@esbuild/android-arm": "0.19.12",
"@esbuild/android-arm64": "0.19.12",
"@esbuild/android-x64": "0.19.12",
"@esbuild/darwin-arm64": "0.19.12",
"@esbuild/darwin-x64": "0.19.12",
"@esbuild/freebsd-arm64": "0.19.12",
"@esbuild/freebsd-x64": "0.19.12",
"@esbuild/linux-arm": "0.19.12",
"@esbuild/linux-arm64": "0.19.12",
"@esbuild/linux-ia32": "0.19.12",
"@esbuild/linux-loong64": "0.19.12",
"@esbuild/linux-mips64el": "0.19.12",
"@esbuild/linux-ppc64": "0.19.12",
"@esbuild/linux-riscv64": "0.19.12",
"@esbuild/linux-s390x": "0.19.12",
"@esbuild/linux-x64": "0.19.12",
"@esbuild/netbsd-x64": "0.19.12",
"@esbuild/openbsd-x64": "0.19.12",
"@esbuild/sunos-x64": "0.19.12",
"@esbuild/win32-arm64": "0.19.12",
"@esbuild/win32-ia32": "0.19.12",
"@esbuild/win32-x64": "0.19.12"
"@esbuild/aix-ppc64": "0.21.5",
"@esbuild/android-arm": "0.21.5",
"@esbuild/android-arm64": "0.21.5",
"@esbuild/android-x64": "0.21.5",
"@esbuild/darwin-arm64": "0.21.5",
"@esbuild/darwin-x64": "0.21.5",
"@esbuild/freebsd-arm64": "0.21.5",
"@esbuild/freebsd-x64": "0.21.5",
"@esbuild/linux-arm": "0.21.5",
"@esbuild/linux-arm64": "0.21.5",
"@esbuild/linux-ia32": "0.21.5",
"@esbuild/linux-loong64": "0.21.5",
"@esbuild/linux-mips64el": "0.21.5",
"@esbuild/linux-ppc64": "0.21.5",
"@esbuild/linux-riscv64": "0.21.5",
"@esbuild/linux-s390x": "0.21.5",
"@esbuild/linux-x64": "0.21.5",
"@esbuild/netbsd-x64": "0.21.5",
"@esbuild/openbsd-x64": "0.21.5",
"@esbuild/sunos-x64": "0.21.5",
"@esbuild/win32-arm64": "0.21.5",
"@esbuild/win32-ia32": "0.21.5",
"@esbuild/win32-x64": "0.21.5"
}
},
"node_modules/estree-walker": {
@@ -1322,57 +1353,27 @@
"license": "ISC"
},
"node_modules/pinia": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz",
"integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz",
"integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^6.5.0",
"vue-demi": ">=0.14.5"
"@vue/devtools-api": "^6.6.3",
"vue-demi": "^0.14.10"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"@vue/composition-api": "^1.4.0",
"typescript": ">=4.4.4",
"vue": "^2.6.14 || ^3.3.0"
"vue": "^2.7.0 || ^3.5.11"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/pinia/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
@@ -1462,15 +1463,15 @@
}
},
"node_modules/vite": {
"version": "5.0.11",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.0.11.tgz",
"integrity": "sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==",
"version": "5.4.21",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.19.3",
"postcss": "^8.4.32",
"rollup": "^4.2.0"
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
"rollup": "^4.20.0"
},
"bin": {
"vite": "bin/vite.js"
@@ -1489,6 +1490,7 @@
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
@@ -1506,6 +1508,9 @@
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
@@ -1518,16 +1523,16 @@
}
},
"node_modules/vue": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz",
"integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==",
"version": "3.5.26",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz",
"integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.4.15",
"@vue/compiler-sfc": "3.4.15",
"@vue/runtime-dom": "3.4.15",
"@vue/server-renderer": "3.4.15",
"@vue/shared": "3.4.15"
"@vue/compiler-dom": "3.5.26",
"@vue/compiler-sfc": "3.5.26",
"@vue/runtime-dom": "3.5.26",
"@vue/server-renderer": "3.5.26",
"@vue/shared": "3.5.26"
},
"peerDependencies": {
"typescript": "*"
@@ -1538,19 +1543,55 @@
}
}
},
"node_modules/vue-chartjs": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.3.tgz",
"integrity": "sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA==",
"license": "MIT",
"peerDependencies": {
"chart.js": "^4.1.1",
"vue": "^3.0.0-0 || ^2.7.0"
}
},
"node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/vue-router": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz",
"integrity": "sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==",
"version": "4.6.4",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
"integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^6.5.0"
"@vue/devtools-api": "^6.6.4"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"vue": "^3.2.0"
"vue": "^3.5.0"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "log-hunter-frontend",
"version": "0.0.1",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
@@ -10,8 +10,11 @@
},
"dependencies": {
"axios": "^1.6.5",
"chart.js": "^4.5.1",
"chartjs-plugin-datalabels": "^2.2.0",
"pinia": "^2.1.7",
"vue": "^3.4.15",
"vue-chartjs": "^5.3.3",
"vue-router": "^4.2.5"
},
"devDependencies": {

View File

@@ -1,10 +1,14 @@
<template>
<div id="app">
<header class="header">
<h1>📊 LogHunter</h1>
<router-link to="/" class="logo">
<h1>📊 LogHunter</h1>
</router-link>
<nav>
<router-link to="/">대시보드</router-link>
<router-link to="/errors">에러 이력</router-link>
<router-link to="/stats/monthly">월별현황</router-link>
<router-link to="/stats/daily">일별현황</router-link>
<router-link to="/servers">서버 관리</router-link>
<router-link to="/patterns">패턴 관리</router-link>
<router-link to="/settings">설정</router-link>
@@ -48,6 +52,11 @@ body {
margin: 0;
}
.header .logo {
text-decoration: none;
color: white;
}
.header nav {
display: flex;
gap: 1rem;
@@ -70,8 +79,6 @@ body {
}
.main {
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
padding: 1.5rem;
}
</style>

View File

@@ -132,14 +132,36 @@ export const scanApi = {
getProgress: (serverId) => api.get(`/scan/progress/${serverId}`),
// 스캔 이력 조회
getHistory: (serverId) => api.get(`/scan/history/${serverId}`)
getHistory: (serverId) => api.get(`/scan/history/${serverId}`),
// 분석 결과 초기화
reset: (serverId) => api.delete(`/scan/reset/${serverId}`),
resetAll: () => api.delete('/scan/reset-all'),
// 에러 통계
getStatsByFile: (serverId) => api.get('/scan/stats/by-file', { params: { serverId } }),
getStatsByServer: () => api.get('/scan/stats/by-server'),
getStatsByPattern: (serverId) => api.get('/scan/stats/by-pattern', { params: { serverId } }),
// 대시보드용: 서버별 최근 N일 일별 통계
getDailyStatsByServer: (days = 30) => api.get('/scan/stats/daily-by-server', { params: { days } }),
// 월별현황용: 서버별 해당 월 일별 통계
getMonthlyStatsByServer: (year, month) => api.get('/scan/stats/monthly-by-server', { params: { year, month } }),
// 일별현황용: 서버별 해당 날짜 5분 단위 통계
getTimeStatsByServer: (date, intervalMinutes = 5) => api.get('/scan/stats/time-by-server', { params: { date, intervalMinutes } })
}
// ErrorLog API
export const errorLogApi = {
search: (params) => api.get('/error-logs', { params }),
getById: (id) => api.get(`/error-logs/${id}`),
getByServer: (serverId, params) => api.get(`/error-logs/server/${serverId}`, { params })
getByServer: (serverId, params) => api.get(`/error-logs/server/${serverId}`, { params }),
getTree: () => api.get('/error-logs/tree'),
getFiles: (serverId) => api.get('/error-logs/files', { params: { serverId } }),
deleteByIds: (ids) => api.delete('/error-logs/batch', { data: ids }),
deleteByFile: (serverId, filePath) => api.delete('/error-logs/by-file', { params: { serverId, filePath } })
}
// Export API (Step 5에서 구현 예정)

View File

@@ -1,5 +1,6 @@
<template>
<button
ref="buttonRef"
:type="type"
:class="['btn', `btn-${variant}`, { 'btn-sm': size === 'sm', 'btn-lg': size === 'lg' }]"
:disabled="disabled || loading"
@@ -11,7 +12,9 @@
</template>
<script setup>
defineProps({
import { ref } from 'vue'
const props = defineProps({
type: {
type: String,
default: 'button'
@@ -31,6 +34,14 @@ defineProps({
})
defineEmits(['click'])
const buttonRef = ref(null)
const focus = () => {
buttonRef.value?.focus()
}
defineExpose({ focus })
</script>
<style scoped>

View File

@@ -18,6 +18,16 @@ const router = createRouter({
name: 'servers',
component: () => import('@/views/ServerManage.vue')
},
{
path: '/stats/monthly',
name: 'monthly-stats',
component: () => import('@/views/MonthlyStats.vue')
},
{
path: '/stats/daily',
name: 'daily-stats',
component: () => import('@/views/DailyStats.vue')
},
{
path: '/patterns',
name: 'patterns',

View File

@@ -0,0 +1,289 @@
<template>
<div class="daily-stats">
<div class="page-header">
<h2>일별 에러현황</h2>
<div class="filter-section">
<button class="nav-btn" @click="prevDay"> 이전</button>
<input type="date" v-model="selectedDate" @change="loadStats" />
<button class="nav-btn" @click="nextDay">다음 </button>
</div>
</div>
<div v-if="loading" class="loading">
<p>로딩중...</p>
</div>
<div v-else-if="stats.length === 0" class="no-data">
<p>{{ formatDate(selectedDate) }} 분석된 에러 데이터가 없습니다.</p>
</div>
<div v-else class="server-charts">
<Card v-for="server in stats" :key="server.serverId" class="server-chart-card">
<template #header>
<div class="chart-header">
<h3>🖥 {{ server.serverName }}</h3>
<span class="chart-subtitle">{{ formatDate(selectedDate) }} 15 단위 에러 ({{ getTotalCount(server) }})</span>
</div>
</template>
<div class="chart-wrapper">
<div class="chart-container">
<Bar :data="getChartData(server)" :options="chartOptions" />
</div>
</div>
</Card>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { Card } from '@/components'
import { scanApi } from '@/api'
import { Bar } from 'vue-chartjs'
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
} from 'chart.js'
import ChartDataLabels from 'chartjs-plugin-datalabels'
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, ChartDataLabels)
// State
const loading = ref(false)
const stats = ref([])
const selectedDate = ref(getCurrentDate())
function getCurrentDate() {
const now = new Date()
return now.toISOString().split('T')[0]
}
function prevDay() {
const date = new Date(selectedDate.value)
date.setDate(date.getDate() - 1)
selectedDate.value = date.toISOString().split('T')[0]
loadStats()
}
function nextDay() {
const date = new Date(selectedDate.value)
date.setDate(date.getDate() + 1)
selectedDate.value = date.toISOString().split('T')[0]
loadStats()
}
// Chart Options (10분 단위 144개 막대)
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
},
tooltip: {
mode: 'index',
intersect: false
},
datalabels: {
display: (context) => {
if (context.datasetIndex !== 2) return false
const datasets = context.chart.data.datasets
const index = context.dataIndex
const total = datasets.reduce((sum, ds) => sum + (ds.data[index] || 0), 0)
return total > 0
},
anchor: 'end',
align: 'end',
offset: 0,
font: {
size: 9
},
color: '#666',
formatter: (value, context) => {
const datasets = context.chart.data.datasets
const index = context.dataIndex
return datasets.reduce((sum, ds) => sum + (ds.data[index] || 0), 0)
}
}
},
scales: {
x: {
stacked: true,
ticks: {
maxRotation: 0,
autoSkip: true,
maxTicksLimit: 24,
callback: function(value, index) {
const label = this.getLabelForValue(value)
if (label && label.endsWith(':00')) {
return label
}
return ''
}
},
grid: {
display: false
}
},
y: {
stacked: true,
beginAtZero: true
}
},
barPercentage: 0.8,
categoryPercentage: 0.9
}
// Chart Data 생성
const getChartData = (server) => {
const labels = server.timeStats.map(s => s.time)
return {
labels,
datasets: [
{
label: 'CRITICAL',
data: server.timeStats.map(s => s.critical),
backgroundColor: '#9b59b6',
borderWidth: 0
},
{
label: 'ERROR',
data: server.timeStats.map(s => s.error),
backgroundColor: '#e74c3c',
borderWidth: 0
},
{
label: 'WARN',
data: server.timeStats.map(s => s.warn),
backgroundColor: '#f39c12',
borderWidth: 0
}
]
}
}
// Load data (10분 단위)
const loadStats = async () => {
loading.value = true
try {
stats.value = await scanApi.getTimeStatsByServer(selectedDate.value, 15)
} catch (e) {
console.error('Failed to load stats:', e)
stats.value = []
} finally {
loading.value = false
}
}
// Utils
const formatDate = (dateStr) => {
const date = new Date(dateStr)
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}`
}
const getTotalCount = (server) => {
return server.timeStats.reduce((sum, s) => sum + s.total, 0)
}
onMounted(() => {
loadStats()
})
</script>
<style scoped>
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-header h2 {
margin: 0;
}
.filter-section {
display: flex;
align-items: center;
gap: 8px;
}
.filter-section input[type="date"] {
padding: 10px 16px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
}
.filter-section input[type="date"]:focus {
outline: none;
border-color: #3498db;
}
.nav-btn {
padding: 10px 16px;
border: 1px solid #ddd;
border-radius: 6px;
background: white;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.nav-btn:hover {
background: #f0f0f0;
border-color: #3498db;
}
.loading,
.no-data {
text-align: center;
padding: 60px;
color: #666;
background: white;
border-radius: 8px;
}
.server-charts {
display: flex;
flex-direction: column;
gap: 20px;
}
.server-chart-card {
width: 100%;
}
.chart-header {
display: flex;
align-items: center;
gap: 12px;
}
.chart-header h3 {
margin: 0;
font-size: 16px;
}
.chart-subtitle {
font-size: 13px;
color: #888;
}
.chart-wrapper {
overflow-x: auto;
}
.chart-container {
height: 220px;
min-width: 100%;
padding: 8px 0;
}
</style>

View File

@@ -84,102 +84,140 @@
</div>
</Card>
<!-- 최근 에러 -->
<Card v-if="recentErrors.length > 0" class="recent-errors">
<template #header>
<div class="section-header">
<h3>최근 에러</h3>
<Button size="sm" variant="secondary" @click="$router.push('/errors')">전체보기</Button>
</div>
</template>
<table class="error-table">
<thead>
<tr>
<th>시간</th>
<th>서버</th>
<th>심각도</th>
<th>요약</th>
</tr>
</thead>
<tbody>
<tr v-for="error in recentErrors" :key="error.id" @click="showErrorDetail(error)">
<td>{{ formatDate(error.occurredAt) }}</td>
<td>{{ error.serverName }}</td>
<td>
<Badge :variant="getSeverityVariant(error.severity)">{{ error.severity }}</Badge>
</td>
<td class="summary-cell">{{ truncate(error.summary, 80) }}</td>
</tr>
</tbody>
</table>
</Card>
<!-- 에러 상세 모달 -->
<Modal v-model="showErrorModal" title="에러 상세" width="800px">
<div v-if="selectedError" class="error-detail">
<div class="detail-grid">
<div class="detail-item">
<label>서버</label>
<span>{{ selectedError.serverName }}</span>
<!-- 서버별 30일 에러 차트 -->
<div v-if="dailyStats.length > 0" class="daily-charts">
<h3>최근 30 에러 추이</h3>
<div class="chart-list">
<Card v-for="server in dailyStats" :key="server.serverId" class="chart-card">
<template #header>
<div class="chart-header">
<span>🖥 {{ server.serverName }}</span>
<span class="chart-total"> {{ getTotalCount(server) }}</span>
</div>
</template>
<div class="chart-container">
<Bar :data="getChartData(server)" :options="chartOptions" />
</div>
<div class="detail-item">
<label>심각도</label>
<Badge :variant="getSeverityVariant(selectedError.severity)">{{ selectedError.severity }}</Badge>
</div>
<div class="detail-item">
<label>파일</label>
<span>{{ selectedError.filePath }}</span>
</div>
<div class="detail-item">
<label>라인</label>
<span>{{ selectedError.lineNumber }}</span>
</div>
<div class="detail-item">
<label>발생시간</label>
<span>{{ formatDate(selectedError.occurredAt) }}</span>
</div>
<div class="detail-item">
<label>패턴</label>
<span>{{ selectedError.patternName }}</span>
</div>
</div>
<div class="detail-section">
<label>요약</label>
<div class="summary-box">{{ selectedError.summary }}</div>
</div>
<div class="detail-section">
<label>컨텍스트</label>
<pre class="context-box">{{ selectedError.context }}</pre>
</div>
</Card>
</div>
<template #footer>
<Button variant="secondary" @click="showErrorModal = false">닫기</Button>
</template>
</Modal>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { Card, Button, Badge, Modal } from '@/components'
import { serverApi, scanApi, errorLogApi } from '@/api'
import { Card, Button, Badge } from '@/components'
import { serverApi, scanApi } from '@/api'
import { Bar } from 'vue-chartjs'
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
} from 'chart.js'
import ChartDataLabels from 'chartjs-plugin-datalabels'
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, ChartDataLabels)
// State
const servers = ref([])
const dailyStats = ref([])
const loading = ref(false)
const scanningServerId = ref(null)
const scanningAll = ref(false)
const progressMap = ref({})
const recentErrors = ref([])
// Error detail
const showErrorModal = ref(false)
const selectedError = ref(null)
// Computed
const activeServers = computed(() => servers.value.filter(s => s.active))
// Chart options
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
tooltip: {
mode: 'index',
intersect: false
},
datalabels: {
display: (context) => {
if (context.datasetIndex !== 2) return false
const datasets = context.chart.data.datasets
const index = context.dataIndex
const total = datasets.reduce((sum, ds) => sum + (ds.data[index] || 0), 0)
return total > 0
},
anchor: 'end',
align: 'end',
offset: 2,
font: {
size: 10,
weight: 'bold'
},
color: '#666',
formatter: (value, context) => {
const datasets = context.chart.data.datasets
const index = context.dataIndex
return datasets.reduce((sum, ds) => sum + (ds.data[index] || 0), 0)
}
}
},
scales: {
x: {
stacked: true,
grid: { display: false }
},
y: {
stacked: true,
beginAtZero: true,
grid: { color: '#f0f0f0' }
}
}
}
// Chart data 생성
const getChartData = (server) => {
const labels = server.dailyStats.map(s => {
const date = new Date(s.date)
return `${date.getMonth() + 1}/${date.getDate()}`
})
return {
labels,
datasets: [
{
label: 'CRITICAL',
data: server.dailyStats.map(s => s.critical),
backgroundColor: '#9b59b6',
borderRadius: 2
},
{
label: 'ERROR',
data: server.dailyStats.map(s => s.error),
backgroundColor: '#e74c3c',
borderRadius: 2
},
{
label: 'WARN',
data: server.dailyStats.map(s => s.warn),
backgroundColor: '#f39c12',
borderRadius: 2
}
]
}
}
// 총 에러 수 계산
const getTotalCount = (server) => {
return server.dailyStats.reduce((sum, s) => sum + s.total, 0)
}
// Load data
const loadServers = async () => {
loading.value = true
@@ -192,12 +230,11 @@ const loadServers = async () => {
}
}
const loadRecentErrors = async () => {
const loadDailyStats = async () => {
try {
const result = await errorLogApi.search({ page: 0, size: 10 })
recentErrors.value = result.content || []
dailyStats.value = await scanApi.getDailyStatsByServer(30)
} catch (e) {
console.error('Failed to load recent errors:', e)
console.error('Failed to load daily stats:', e)
}
}
@@ -234,8 +271,7 @@ const scanServer = (server) => {
}
}
loadServers()
loadRecentErrors()
// 3초 후 진행상황 제거
loadDailyStats()
setTimeout(() => {
delete progressMap.value[server.id]
}, 5000)
@@ -262,8 +298,7 @@ const scanAllServers = () => {
(results) => {
scanningAll.value = false
loadServers()
loadRecentErrors()
// 3초 후 진행상황 제거
loadDailyStats()
setTimeout(() => {
progressMap.value = {}
}, 5000)
@@ -275,23 +310,12 @@ const scanAllServers = () => {
)
}
// Error detail
const showErrorDetail = (error) => {
selectedError.value = error
showErrorModal.value = true
}
// Utils
const formatDate = (dateStr) => {
if (!dateStr) return '-'
return new Date(dateStr).toLocaleString('ko-KR')
}
const truncate = (str, len) => {
if (!str) return ''
return str.length > len ? str.substring(0, len) + '...' : str
}
const getStatusText = (progress) => {
if (progress.status === 'SUCCESS') return progress.message || '완료'
if (progress.status === 'FAILED') return progress.message || '실패'
@@ -305,14 +329,9 @@ const getProgressPercent = (progress) => {
return Math.round((progress.scannedFiles / progress.totalFiles) * 100)
}
const getSeverityVariant = (severity) => {
const map = { 'CRITICAL': 'critical', 'ERROR': 'error', 'WARN': 'warn' }
return map[severity] || 'default'
}
onMounted(() => {
loadServers()
loadRecentErrors()
loadDailyStats()
})
</script>
@@ -441,105 +460,39 @@ onMounted(() => {
color: #666;
}
.recent-errors {
margin-top: 24px;
/* 차트 섹션 */
.daily-charts {
margin-top: 32px;
}
.section-header {
.daily-charts h3 {
margin-bottom: 16px;
font-size: 18px;
}
.chart-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.chart-card {
width: 100%;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.section-header h3 {
margin: 0;
}
.error-table {
width: 100%;
border-collapse: collapse;
}
.error-table th,
.error-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #eee;
}
.error-table th {
background: #f8f9fa;
font-weight: 600;
.chart-total {
font-size: 13px;
color: #888;
}
.error-table tbody tr {
cursor: pointer;
transition: background 0.2s;
}
.error-table tbody tr:hover {
background: #f8f9fa;
}
.summary-cell {
max-width: 400px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Error Detail Modal */
.error-detail {
max-height: 60vh;
overflow-y: auto;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 20px;
}
.detail-item {
display: flex;
flex-direction: column;
gap: 4px;
}
.detail-item label {
font-size: 12px;
color: #666;
}
.detail-section {
margin-bottom: 16px;
}
.detail-section label {
display: block;
font-size: 12px;
color: #666;
margin-bottom: 8px;
}
.summary-box {
padding: 12px;
background: #f8f9fa;
border-radius: 4px;
font-size: 14px;
}
.context-box {
padding: 12px;
background: #2d2d2d;
color: #f8f8f2;
border-radius: 4px;
font-size: 12px;
line-height: 1.5;
overflow-x: auto;
white-space: pre;
margin: 0;
.chart-container {
height: 200px;
padding: 8px 0;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,270 @@
<template>
<div class="monthly-stats">
<div class="page-header">
<h2>월별 에러현황</h2>
<div class="filter-section">
<button class="nav-btn" @click="prevMonth"> 이전</button>
<input type="month" v-model="selectedMonth" @change="loadStats" />
<button class="nav-btn" @click="nextMonth">다음 </button>
</div>
</div>
<div v-if="loading" class="loading">
<p>로딩중...</p>
</div>
<div v-else-if="stats.length === 0" class="no-data">
<p>{{ formatMonth(selectedMonth) }} 분석된 에러 데이터가 없습니다.</p>
</div>
<div v-else class="server-charts">
<Card v-for="server in stats" :key="server.serverId" class="server-chart-card">
<template #header>
<div class="chart-header">
<h3>🖥 {{ server.serverName }}</h3>
<span class="chart-subtitle">{{ formatMonth(selectedMonth) }} 일별 에러 ({{ getTotalCount(server) }})</span>
</div>
</template>
<div class="chart-container">
<Bar :data="getChartData(server)" :options="chartOptions" />
</div>
</Card>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { Card } from '@/components'
import { scanApi } from '@/api'
import { Bar } from 'vue-chartjs'
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend
} from 'chart.js'
import ChartDataLabels from 'chartjs-plugin-datalabels'
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, ChartDataLabels)
// State
const loading = ref(false)
const stats = ref([])
const selectedMonth = ref(getCurrentMonth())
function getCurrentMonth() {
const now = new Date()
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
}
function prevMonth() {
const [year, month] = selectedMonth.value.split('-').map(Number)
const date = new Date(year, month - 2, 1)
selectedMonth.value = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`
loadStats()
}
function nextMonth() {
const [year, month] = selectedMonth.value.split('-').map(Number)
const date = new Date(year, month, 1)
selectedMonth.value = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`
loadStats()
}
// Chart Options
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
},
tooltip: {
mode: 'index',
intersect: false
},
datalabels: {
display: (context) => {
if (context.datasetIndex !== 2) return false
const datasets = context.chart.data.datasets
const index = context.dataIndex
const total = datasets.reduce((sum, ds) => sum + (ds.data[index] || 0), 0)
return total > 0
},
anchor: 'end',
align: 'end',
offset: 2,
font: {
size: 10,
weight: 'bold'
},
color: '#666',
formatter: (value, context) => {
const datasets = context.chart.data.datasets
const index = context.dataIndex
return datasets.reduce((sum, ds) => sum + (ds.data[index] || 0), 0)
}
}
},
scales: {
x: {
stacked: true
},
y: {
stacked: true,
beginAtZero: true
}
}
}
// Chart Data 생성
const getChartData = (server) => {
const labels = server.dailyStats.map(s => {
const date = new Date(s.date)
return `${date.getDate()}`
})
return {
labels,
datasets: [
{
label: 'CRITICAL',
data: server.dailyStats.map(s => s.critical),
backgroundColor: '#9b59b6',
borderRadius: 2
},
{
label: 'ERROR',
data: server.dailyStats.map(s => s.error),
backgroundColor: '#e74c3c',
borderRadius: 2
},
{
label: 'WARN',
data: server.dailyStats.map(s => s.warn),
backgroundColor: '#f39c12',
borderRadius: 2
}
]
}
}
// Load data
const loadStats = async () => {
loading.value = true
try {
const [year, month] = selectedMonth.value.split('-').map(Number)
stats.value = await scanApi.getMonthlyStatsByServer(year, month)
} catch (e) {
console.error('Failed to load stats:', e)
stats.value = []
} finally {
loading.value = false
}
}
// Utils
const formatMonth = (monthStr) => {
const [year, month] = monthStr.split('-')
return `${year}${parseInt(month)}`
}
const getTotalCount = (server) => {
return server.dailyStats.reduce((sum, s) => sum + s.total, 0)
}
onMounted(() => {
loadStats()
})
</script>
<style scoped>
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-header h2 {
margin: 0;
}
.filter-section {
display: flex;
align-items: center;
gap: 8px;
}
.filter-section input[type="month"] {
padding: 10px 16px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
}
.filter-section input[type="month"]:focus {
outline: none;
border-color: #3498db;
}
.nav-btn {
padding: 10px 16px;
border: 1px solid #ddd;
border-radius: 6px;
background: white;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.nav-btn:hover {
background: #f0f0f0;
border-color: #3498db;
}
.loading,
.no-data {
text-align: center;
padding: 60px;
color: #666;
background: white;
border-radius: 8px;
}
.server-charts {
display: flex;
flex-direction: column;
gap: 20px;
}
.server-chart-card {
width: 100%;
}
.chart-header {
display: flex;
align-items: center;
gap: 12px;
}
.chart-header h3 {
margin: 0;
font-size: 16px;
}
.chart-subtitle {
font-size: 13px;
color: #888;
}
.chart-container {
height: 220px;
padding: 8px 0;
}
</style>

View File

@@ -53,6 +53,14 @@
required
hint="Java 정규식 문법을 사용합니다."
/>
<FormInput
v-model="form.excludeRegex"
label="제외 정규식"
type="textarea"
:rows="2"
placeholder="예: throws\\s+(Exception|java\\.lang\\.Exception)"
hint="이 패턴에 매칭되면 에러에서 제외됩니다. (선택)"
/>
<FormInput
v-model="form.severity"
label="심각도"
@@ -177,6 +185,7 @@ const editId = ref(null)
const form = ref({
name: '',
regex: '',
excludeRegex: '',
severity: 'ERROR',
contextLines: 5,
description: '',
@@ -213,6 +222,7 @@ const openAddModal = () => {
form.value = {
name: '',
regex: '',
excludeRegex: '',
severity: 'ERROR',
contextLines: 5,
description: '',
@@ -228,6 +238,7 @@ const openEditModal = (pattern) => {
form.value = {
name: pattern.name,
regex: pattern.regex,
excludeRegex: pattern.excludeRegex || '',
severity: pattern.severity,
contextLines: pattern.contextLines,
description: pattern.description || '',

View File

@@ -0,0 +1 @@
import{_ as r,d as m,a as d,e as c,b as t,t as u,p as n,j as f,l as p,T as _,h as a,i as h,n as y}from"./index-D2VmGkBi.js";import"./index-Bx7gSOle.js";const v={class:"modal-header"},b={class:"modal-body"},g={key:0,class:"modal-footer"},B={__name:"Modal",props:{modelValue:{type:Boolean,default:!1},title:{type:String,default:""},width:{type:String,default:"500px"}},emits:["update:modelValue","close"],setup(e,{emit:s}){const o=s,i=()=>{o("update:modelValue",!1),o("close")};return(l,S)=>(a(),m(_,{to:"body"},[e.modelValue?(a(),d("div",{key:0,class:"modal-overlay",onClick:p(i,["self"])},[t("div",{class:"modal",style:f({width:e.width})},[t("div",v,[t("h3",null,u(e.title),1),t("button",{class:"close-btn",onClick:i},"×")]),t("div",b,[n(l.$slots,"default",{},void 0)]),l.$slots.footer?(a(),d("div",g,[n(l.$slots,"footer",{},void 0)])):c("",!0)],4)])):c("",!0)]))}},w=r(B,[["__scopeId","data-v-90993dd3"]]),k={__name:"Badge",props:{text:String,variant:{type:String,default:"default"}},setup(e){return(s,o)=>(a(),d("span",{class:y(["badge",`badge-${e.variant}`])},[n(s.$slots,"default",{},()=>[h(u(e.text),1)])],2))}},x=r(k,[["__scopeId","data-v-b7bd2350"]]);export{x as B,w as M};

View File

@@ -0,0 +1 @@
.dashboard-header[data-v-af1336d8]{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}.dashboard-header h2[data-v-af1336d8]{margin:0}.header-actions[data-v-af1336d8]{display:flex;gap:8px}.server-grid[data-v-af1336d8]{display:grid;grid-template-columns:repeat(auto-fill,minmax(350px,1fr));gap:20px;margin-bottom:24px}.server-card[data-v-af1336d8]{transition:box-shadow .2s}.server-card[data-v-af1336d8]:hover{box-shadow:0 4px 12px #00000026}.server-header[data-v-af1336d8]{display:flex;justify-content:space-between;align-items:center}.server-title[data-v-af1336d8]{display:flex;align-items:center;gap:10px}.server-title h4[data-v-af1336d8]{margin:0;font-size:16px}.server-info[data-v-af1336d8]{margin-bottom:12px}.info-row[data-v-af1336d8]{display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid #f0f0f0}.info-row[data-v-af1336d8]:last-child{border-bottom:none}.info-row .label[data-v-af1336d8]{color:#666;font-size:13px}.info-row .value[data-v-af1336d8]{font-weight:500}.info-row .value.has-error[data-v-af1336d8]{color:#e74c3c}.progress-section[data-v-af1336d8]{padding:12px;background:#f8f9fa;border-radius:8px;margin-top:12px}.progress-header[data-v-af1336d8]{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}.status-text[data-v-af1336d8]{font-size:13px;color:#333}.progress-bar-container[data-v-af1336d8]{height:8px;background:#e0e0e0;border-radius:4px;overflow:hidden;margin-bottom:8px}.progress-bar[data-v-af1336d8]{height:100%;background:#3498db;transition:width .3s}.progress-details[data-v-af1336d8]{display:flex;justify-content:space-between;font-size:12px;color:#666}.empty-card[data-v-af1336d8]{text-align:center}.empty-content[data-v-af1336d8]{padding:40px 20px}.empty-content p[data-v-af1336d8]{margin-bottom:16px;color:#666}.stats-section[data-v-af1336d8]{margin-bottom:24px}.stats-grid[data-v-af1336d8]{display:grid;grid-template-columns:repeat(auto-fit,minmax(400px,1fr));gap:20px}.stats-card h3[data-v-af1336d8]{margin:0;font-size:16px}.stats-table-wrapper[data-v-af1336d8]{max-height:300px;overflow-y:auto}.stats-table[data-v-af1336d8]{width:100%;border-collapse:collapse;font-size:13px}.stats-table th[data-v-af1336d8],.stats-table td[data-v-af1336d8]{padding:10px 8px;text-align:left;border-bottom:1px solid #eee}.stats-table th[data-v-af1336d8]{background:#f8f9fa;font-weight:600;position:sticky;top:0}.file-path-cell[data-v-af1336d8]{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:monospace;font-size:12px}.count-col[data-v-af1336d8]{width:70px;text-align:center!important}.count-col.critical[data-v-af1336d8]{color:#9b59b6;font-weight:600}.count-col.error[data-v-af1336d8]{color:#e74c3c;font-weight:600}.count-col.warn[data-v-af1336d8]{color:#f39c12;font-weight:600}.count-col.total[data-v-af1336d8]{font-weight:700;color:#333}.recent-errors[data-v-af1336d8]{margin-top:24px}.section-header[data-v-af1336d8]{display:flex;justify-content:space-between;align-items:center}.section-header h3[data-v-af1336d8]{margin:0}.error-table[data-v-af1336d8]{width:100%;border-collapse:collapse}.error-table th[data-v-af1336d8],.error-table td[data-v-af1336d8]{padding:12px;text-align:left;border-bottom:1px solid #eee}.error-table th[data-v-af1336d8]{background:#f8f9fa;font-weight:600;font-size:13px}.error-table tbody tr[data-v-af1336d8]{cursor:pointer;transition:background .2s}.error-table tbody tr[data-v-af1336d8]:hover{background:#f8f9fa}.summary-cell[data-v-af1336d8]{max-width:400px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.warning-text[data-v-af1336d8]{color:#e74c3c;font-size:13px}.error-detail[data-v-af1336d8]{max-height:60vh;overflow-y:auto}.detail-grid[data-v-af1336d8]{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:20px}.detail-item[data-v-af1336d8]{display:flex;flex-direction:column;gap:4px}.detail-item label[data-v-af1336d8]{font-size:12px;color:#666}.detail-section[data-v-af1336d8]{margin-bottom:16px}.detail-section label[data-v-af1336d8]{display:block;font-size:12px;color:#666;margin-bottom:8px}.summary-box[data-v-af1336d8]{padding:12px;background:#f8f9fa;border-radius:4px;font-size:14px}.context-box[data-v-af1336d8]{padding:12px;background:#2d2d2d;color:#f8f8f2;border-radius:4px;font-size:12px;line-height:1.5;overflow-x:auto;white-space:pre;margin:0}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
import"./index-Bx7gSOle.js";import{_ as h,a as e,h as a,b as o,e as u,F as d,g as i,j as f,t as c,p as y,i as g}from"./index-D2VmGkBi.js";const b={class:"data-table-wrapper"},p={class:"data-table"},$={key:0,class:"actions-col"},_={key:0},D=["colspan"],S={key:1},T=["colspan"],B=["onClick"],N={key:0,class:"actions-col"},V={__name:"DataTable",props:{columns:{type:Array,required:!0},data:{type:Array,default:()=>[]},loading:{type:Boolean,default:!1},emptyText:{type:String,default:"데이터가 없습니다."}},emits:["row-click"],setup(n){const m=(t,r)=>t==null?"-":r.type==="date"&&t?new Date(t).toLocaleString("ko-KR"):r.type==="boolean"?t?"Y":"N":t;return(t,r)=>(a(),e("div",b,[o("table",p,[o("thead",null,[o("tr",null,[(a(!0),e(d,null,i(n.columns,s=>(a(),e("th",{key:s.key,style:f({width:s.width})},c(s.label),5))),128)),t.$slots.actions?(a(),e("th",$,"작업")):u("",!0)])]),o("tbody",null,[n.loading?(a(),e("tr",_,[o("td",{colspan:n.columns.length+(t.$slots.actions?1:0),class:"loading-cell"}," 로딩 중... ",8,D)])):!n.data||n.data.length===0?(a(),e("tr",S,[o("td",{colspan:n.columns.length+(t.$slots.actions?1:0),class:"empty-cell"},c(n.emptyText),9,T)])):(a(!0),e(d,{key:2},i(n.data,(s,k)=>(a(),e("tr",{key:s.id||k,onClick:l=>t.$emit("row-click",s)},[(a(!0),e(d,null,i(n.columns,l=>(a(),e("td",{key:l.key},[y(t.$slots,l.key,{row:s,value:s[l.key]},()=>[g(c(m(s[l.key],l)),1)])]))),128)),t.$slots.actions?(a(),e("td",N,[y(t.$slots,"actions",{row:s},void 0)])):u("",!0)],8,B))),128))])])]))}},A=h(V,[["__scopeId","data-v-db5e24a9"]]);export{A as D};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.layout-container[data-v-138c0e4a]{display:flex;gap:20px;height:calc(100vh - 120px)}.tree-panel[data-v-138c0e4a]{width:320px;min-width:280px;background:#fff;border-radius:8px;box-shadow:0 1px 3px #0000001a;display:flex;flex-direction:column}.tree-header[data-v-138c0e4a]{display:flex;justify-content:space-between;align-items:center;padding:16px;border-bottom:1px solid #eee}.tree-header h4[data-v-138c0e4a]{margin:0;font-size:15px}.tree-content[data-v-138c0e4a]{flex:1;overflow-y:auto;padding:8px}.tree-loading[data-v-138c0e4a],.tree-empty[data-v-138c0e4a]{padding:20px;text-align:center;color:#666;font-size:13px}.tree-item[data-v-138c0e4a]{display:flex;align-items:center;padding:8px 10px;border-radius:6px;cursor:pointer;font-size:13px;transition:background .15s}.tree-item[data-v-138c0e4a]:hover{background:#f5f5f5}.tree-item.active[data-v-138c0e4a]{background:#e3f2fd;font-weight:500}.tree-toggle[data-v-138c0e4a]{width:16px;font-size:10px;color:#999;margin-right:4px}.tree-icon[data-v-138c0e4a]{margin-right:8px;font-size:14px}.tree-label[data-v-138c0e4a]{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-count[data-v-138c0e4a]{font-size:12px;color:#666;background:#f0f0f0;padding:2px 8px;border-radius:10px;margin-left:8px}.tree-count-detail[data-v-138c0e4a]{display:flex;gap:4px;margin-left:8px}.tree-count-detail span[data-v-138c0e4a]{font-size:11px;padding:2px 6px;border-radius:8px;font-weight:500}.tree-count-detail .critical[data-v-138c0e4a]{background:#f3e5f5;color:#9b59b6}.tree-count-detail .error[data-v-138c0e4a]{background:#ffebee;color:#e74c3c}.tree-count-detail .warn[data-v-138c0e4a]{background:#fff8e1;color:#f39c12}.tree-delete[data-v-138c0e4a]{opacity:0;background:none;border:none;cursor:pointer;padding:4px;font-size:12px;transition:opacity .15s}.tree-item:hover .tree-delete[data-v-138c0e4a]{opacity:1}.tree-delete[data-v-138c0e4a]:hover{transform:scale(1.1)}.tree-all[data-v-138c0e4a]{margin-bottom:8px;border-bottom:1px solid #eee;padding-bottom:12px}.tree-server[data-v-138c0e4a]{margin-bottom:4px}.tree-paths[data-v-138c0e4a],.tree-files[data-v-138c0e4a]{margin-left:20px}.tree-file-item[data-v-138c0e4a]{padding-left:26px}.list-panel[data-v-138c0e4a]{flex:1;min-width:0}.list-panel[data-v-138c0e4a] .card{height:100%;display:flex;flex-direction:column}.card-header-content[data-v-138c0e4a]{display:flex;justify-content:space-between;align-items:center}.card-header-content h3[data-v-138c0e4a]{margin:0;font-size:16px}.header-actions[data-v-138c0e4a]{display:flex;gap:8px}.filters[data-v-138c0e4a]{padding:16px;background:#f8f9fa;border-radius:8px;margin-bottom:16px}.filter-row[data-v-138c0e4a]{display:grid;grid-template-columns:1fr 1fr 2fr auto;gap:16px;align-items:end}.filter-actions[data-v-138c0e4a]{display:flex;gap:8px;padding-bottom:4px}.results-section[data-v-138c0e4a]{flex:1;display:flex;flex-direction:column;min-height:0}.results-header[data-v-138c0e4a]{margin-bottom:12px;color:#666;font-size:14px}.table-wrapper[data-v-138c0e4a]{flex:1;overflow:auto}.error-table[data-v-138c0e4a]{width:100%;border-collapse:collapse}.error-table th[data-v-138c0e4a],.error-table td[data-v-138c0e4a]{padding:10px 8px;text-align:left;border-bottom:1px solid #eee}.error-table th[data-v-138c0e4a]{background:#f8f9fa;font-weight:600;font-size:13px;position:sticky;top:0}.error-table tbody tr[data-v-138c0e4a]:hover{background:#fafafa}.col-time[data-v-138c0e4a]{width:150px;white-space:nowrap}.col-severity[data-v-138c0e4a]{width:80px}.col-pattern[data-v-138c0e4a]{width:100px}.col-action[data-v-138c0e4a]{width:70px;text-align:center}.empty-result[data-v-138c0e4a],.loading-result[data-v-138c0e4a]{padding:40px;text-align:center;color:#666}.pagination[data-v-138c0e4a]{display:flex;justify-content:center;align-items:center;gap:16px;padding:16px 0;border-top:1px solid #eee;margin-top:auto}.page-info[data-v-138c0e4a]{font-size:14px;color:#666}.warning-text[data-v-138c0e4a]{color:#e74c3c;font-size:13px;margin-top:8px}.error-detail[data-v-138c0e4a]{max-height:60vh;overflow-y:auto}.detail-grid[data-v-138c0e4a]{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:20px}.detail-item[data-v-138c0e4a]{display:flex;flex-direction:column;gap:4px}.detail-item label[data-v-138c0e4a]{font-size:12px;color:#666}.file-path[data-v-138c0e4a]{word-break:break-all;font-family:monospace;font-size:12px}.detail-section[data-v-138c0e4a]{margin-bottom:16px}.detail-section label[data-v-138c0e4a]{display:block;font-size:12px;color:#666;margin-bottom:8px}.summary-box[data-v-138c0e4a]{padding:12px;background:#f8f9fa;border-radius:4px;font-size:14px}.context-box[data-v-138c0e4a]{padding:12px;background:#1e1e1e;color:#d4d4d4;border-radius:4px;font-size:12px;line-height:1.6;overflow-x:auto;white-space:pre;margin:0;max-height:250px}

View File

@@ -0,0 +1 @@
import"./index-Bx7gSOle.js";import{_ as i,c as s,a as t,h as l,e as o,i as c,t as n,F as m,g as y}from"./index-D2VmGkBi.js";const h={class:"form-group"},v=["for"],b={key:0,class:"required"},g=["id","type","value","placeholder","disabled","readonly"],f=["id","value","placeholder","disabled","readonly","rows"],k=["id","value","disabled"],V={key:0,value:""},S=["value"],x={key:4,class:"error-text"},I={key:5,class:"hint-text"},B={__name:"FormInput",props:{modelValue:{type:[String,Number],default:""},label:String,type:{type:String,default:"text"},placeholder:String,required:Boolean,disabled:Boolean,readonly:Boolean,error:String,hint:String,rows:{type:Number,default:3},options:{type:Array,default:()=>[]}},emits:["update:modelValue"],setup(e){const r=s(()=>`input-${Math.random().toString(36).slice(2,9)}`);return(u,d)=>(l(),t("div",h,[e.label?(l(),t("label",{key:0,for:r.value},[c(n(e.label)+" ",1),e.required?(l(),t("span",b,"*")):o("",!0)],8,v)):o("",!0),e.type!=="textarea"&&e.type!=="select"?(l(),t("input",{key:1,id:r.value,type:e.type,value:e.modelValue,placeholder:e.placeholder,disabled:e.disabled,readonly:e.readonly,class:"form-input",onInput:d[0]||(d[0]=a=>u.$emit("update:modelValue",a.target.value))},null,40,g)):e.type==="textarea"?(l(),t("textarea",{key:2,id:r.value,value:e.modelValue,placeholder:e.placeholder,disabled:e.disabled,readonly:e.readonly,rows:e.rows,class:"form-input",onInput:d[1]||(d[1]=a=>u.$emit("update:modelValue",a.target.value))},null,40,f)):e.type==="select"?(l(),t("select",{key:3,id:r.value,value:e.modelValue,disabled:e.disabled,class:"form-input",onChange:d[2]||(d[2]=a=>u.$emit("update:modelValue",a.target.value))},[e.placeholder?(l(),t("option",V,n(e.placeholder),1)):o("",!0),(l(!0),t(m,null,y(e.options,a=>(l(),t("option",{key:a.value,value:a.value},n(a.label),9,S))),128))],40,k)):o("",!0),e.error?(l(),t("span",x,n(e.error),1)):o("",!0),e.hint?(l(),t("span",I,n(e.hint),1)):o("",!0)]))}},N=i(B,[["__scopeId","data-v-45f49038"]]);export{N as F};

View File

@@ -0,0 +1 @@
.card-header-content[data-v-fc6fccb5]{display:flex;justify-content:space-between;align-items:center}.card-header-content h3[data-v-fc6fccb5]{margin:0}.action-buttons[data-v-fc6fccb5]{display:flex;gap:4px}.regex-code[data-v-fc6fccb5]{font-family:monospace;background:#f1f3f4;padding:2px 6px;border-radius:3px;font-size:12px}.form-group[data-v-fc6fccb5]{margin-bottom:16px}.form-group label[data-v-fc6fccb5]{display:flex;align-items:center;gap:8px;cursor:pointer}.test-section[data-v-fc6fccb5]{display:flex;flex-direction:column;gap:16px}.test-pattern label[data-v-fc6fccb5]{display:block;font-weight:500;margin-bottom:6px}.regex-display[data-v-fc6fccb5]{display:block;font-family:monospace;background:#f8f9fa;padding:12px;border-radius:4px;font-size:13px;word-break:break-all}.test-result[data-v-fc6fccb5]{padding:16px;border-radius:8px;margin-top:8px}.test-result.success[data-v-fc6fccb5]{background:#d4edda;border:1px solid #c3e6cb}.test-result.fail[data-v-fc6fccb5]{background:#f8d7da;border:1px solid #f5c6cb}.test-result h4[data-v-fc6fccb5]{margin:0 0 12px}.test-result p[data-v-fc6fccb5]{margin:0}.match-info[data-v-fc6fccb5]{margin-top:8px}.match-info label[data-v-fc6fccb5]{font-weight:500;margin-right:8px}.match-info code[data-v-fc6fccb5]{background:#0000001a;padding:2px 6px;border-radius:3px}.error-msg[data-v-fc6fccb5]{color:#721c24}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.card-header-content[data-v-6f59a208]{display:flex;justify-content:space-between;align-items:center}.card-header-content h3[data-v-6f59a208]{margin:0}.action-buttons[data-v-6f59a208]{display:flex;gap:4px}.form-group[data-v-6f59a208]{margin-bottom:16px}.form-group label[data-v-6f59a208]{display:flex;align-items:center;gap:8px;cursor:pointer}.log-path-section h4[data-v-6f59a208]{margin:0 0 12px;font-size:14px;color:#333}.log-path-form[data-v-6f59a208]{padding:16px;background:#f8f9fa;border-radius:8px;margin-bottom:20px}.log-path-inputs[data-v-6f59a208]{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-bottom:12px}.log-path-list table[data-v-6f59a208]{width:100%;border-collapse:collapse}.log-path-list th[data-v-6f59a208],.log-path-list td[data-v-6f59a208]{padding:10px 12px;text-align:left;border-bottom:1px solid #eee}.log-path-list th[data-v-6f59a208]{background:#f8f9fa;font-weight:600;font-size:13px}.empty-text[data-v-6f59a208]{color:#6c757d;text-align:center;padding:20px}.warning-text[data-v-6f59a208]{color:#e74c3c;font-size:14px}.test-result[data-v-6f59a208]{padding:16px;border-radius:8px}.test-result.success[data-v-6f59a208]{background:#d4edda;border:1px solid #c3e6cb}.test-result.fail[data-v-6f59a208]{background:#f8d7da;border:1px solid #f5c6cb}.test-result p[data-v-6f59a208]{margin:0}

View File

@@ -0,0 +1 @@
.settings[data-v-fdca948e]{max-width:800px}.card-header-content h3[data-v-fdca948e]{margin:0}.loading[data-v-fdca948e]{text-align:center;padding:40px;color:#666}.settings-form[data-v-fdca948e]{padding:10px 0}.setting-section[data-v-fdca948e]{margin-bottom:30px;padding-bottom:20px;border-bottom:1px solid #eee}.setting-section[data-v-fdca948e]:last-of-type{border-bottom:none}.setting-section h4[data-v-fdca948e]{margin:0 0 20px;font-size:16px;color:#2c3e50}.form-actions[data-v-fdca948e]{display:flex;justify-content:flex-end;gap:12px;margin-top:20px;padding-top:20px;border-top:1px solid #eee}.app-info[data-v-fdca948e]{margin-top:20px}.app-info h3[data-v-fdca948e]{margin:0}.info-list[data-v-fdca948e]{display:flex;flex-direction:column;gap:12px}.info-item[data-v-fdca948e]{display:flex;justify-content:space-between;padding:12px 0;border-bottom:1px solid #f0f0f0}.info-item[data-v-fdca948e]:last-child{border-bottom:none}.info-item .label[data-v-fdca948e]{color:#666}.info-item .value[data-v-fdca948e]{font-weight:500}

View File

@@ -0,0 +1 @@
import{_ as S,r as v,o as F,a as p,f as a,w as i,u as n,h as m,l as B,b as t,i as c}from"./index-D2VmGkBi.js";import{C as V,b,B as y}from"./index-Bx7gSOle.js";import{F as u}from"./FormInput-DHZMRclc.js";const k={class:"settings"},w={key:0,class:"loading"},C={class:"setting-section"},U={class:"setting-section"},h={class:"setting-section"},M={class:"setting-section"},z={class:"form-actions"},A={__name:"Settings",setup(N){const d=v(!1),r=v(!1),l=v({}),f={"server.port":"8080","export.path":"./exports","retention.days":"90","scan.timeout":"30","scan.maxFileSize":"100"},g=async()=>{d.value=!0;try{const o=await b.getAllAsMap();l.value={...f,...o}}catch(o){console.error("Failed to load settings:",o),l.value={...f}}finally{d.value=!1}},x=async()=>{r.value=!0;try{for(const[o,e]of Object.entries(l.value))await b.save({key:o,value:String(e)});alert("설정이 저장되었습니다.")}catch(o){console.error("Failed to save settings:",o),alert("설정 저장에 실패했습니다.")}finally{r.value=!1}};return F(()=>{g()}),(o,e)=>(m(),p("div",k,[a(n(V),null,{header:i(()=>[...e[5]||(e[5]=[t("div",{class:"card-header-content"},[t("h3",null,"설정")],-1)])]),default:i(()=>[d.value?(m(),p("div",w,"로딩중...")):(m(),p("form",{key:1,onSubmit:B(x,["prevent"]),class:"settings-form"},[t("div",C,[e[6]||(e[6]=t("h4",null,"일반 설정",-1)),a(n(u),{modelValue:l.value["server.port"],"onUpdate:modelValue":e[0]||(e[0]=s=>l.value["server.port"]=s),label:"서버 포트",type:"number",hint:"애플리케이션이 실행될 포트 번호 (기본: 8080)"},null,8,["modelValue"])]),t("div",U,[e[7]||(e[7]=t("h4",null,"내보내기 설정",-1)),a(n(u),{modelValue:l.value["export.path"],"onUpdate:modelValue":e[1]||(e[1]=s=>l.value["export.path"]=s),label:"내보내기 경로",placeholder:"예: C:\\LogHunter\\exports",hint:"리포트 파일이 저장될 기본 경로"},null,8,["modelValue"])]),t("div",h,[e[8]||(e[8]=t("h4",null,"데이터 관리",-1)),a(n(u),{modelValue:l.value["retention.days"],"onUpdate:modelValue":e[2]||(e[2]=s=>l.value["retention.days"]=s),label:"로그 보관 기간 (일)",type:"number",hint:"에러 로그 데이터 보관 기간 (0 = 무제한)"},null,8,["modelValue"])]),t("div",M,[e[9]||(e[9]=t("h4",null,"스캔 설정",-1)),a(n(u),{modelValue:l.value["scan.timeout"],"onUpdate:modelValue":e[3]||(e[3]=s=>l.value["scan.timeout"]=s),label:"스캔 타임아웃 (초)",type:"number",hint:"SFTP 연결 및 파일 다운로드 타임아웃"},null,8,["modelValue"]),a(n(u),{modelValue:l.value["scan.maxFileSize"],"onUpdate:modelValue":e[4]||(e[4]=s=>l.value["scan.maxFileSize"]=s),label:"최대 파일 크기 (MB)",type:"number",hint:"분석할 로그 파일의 최대 크기"},null,8,["modelValue"])]),t("div",z,[a(n(y),{onClick:g,variant:"secondary"},{default:i(()=>[...e[10]||(e[10]=[c("초기화",-1)])]),_:1}),a(n(y),{type:"submit",loading:r.value},{default:i(()=>[...e[11]||(e[11]=[c("저장",-1)])]),_:1},8,["loading"])])],32))]),_:1}),a(n(V),{class:"app-info"},{header:i(()=>[...e[12]||(e[12]=[t("h3",null,"애플리케이션 정보",-1)])]),default:i(()=>[e[13]||(e[13]=t("div",{class:"info-list"},[t("div",{class:"info-item"},[t("span",{class:"label"},"버전"),t("span",{class:"value"},"1.0.0")]),t("div",{class:"info-item"},[t("span",{class:"label"},"프레임워크"),t("span",{class:"value"},"Spring Boot 3.2 + Vue 3")]),t("div",{class:"info-item"},[t("span",{class:"label"},"데이터베이스"),t("span",{class:"value"},"SQLite (./data/loghunter.db)")])],-1))]),_:1})]))}},T=S(A,[["__scopeId","data-v-fdca948e"]]);export{T as default};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.data-table-wrapper[data-v-db5e24a9]{overflow-x:auto}.data-table[data-v-db5e24a9]{width:100%;border-collapse:collapse;background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 1px 3px #0000001a}.data-table th[data-v-db5e24a9],.data-table td[data-v-db5e24a9]{padding:12px 16px;text-align:left;border-bottom:1px solid #eee}.data-table th[data-v-db5e24a9]{background:#f8f9fa;font-weight:600;color:#495057}.data-table tbody tr[data-v-db5e24a9]:hover{background:#f8f9fa}.data-table tbody tr:last-child td[data-v-db5e24a9]{border-bottom:none}.actions-col[data-v-db5e24a9]{width:120px;text-align:center}.loading-cell[data-v-db5e24a9],.empty-cell[data-v-db5e24a9]{text-align:center;color:#6c757d;padding:40px!important}.modal-overlay[data-v-90993dd3]{position:fixed;top:0;left:0;right:0;bottom:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1000}.modal[data-v-90993dd3]{background:#fff;border-radius:8px;max-height:90vh;overflow:hidden;display:flex;flex-direction:column;box-shadow:0 4px 20px #00000026}.modal-header[data-v-90993dd3]{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #eee}.modal-header h3[data-v-90993dd3]{margin:0;font-size:1.1rem}.close-btn[data-v-90993dd3]{background:none;border:none;font-size:1.5rem;cursor:pointer;color:#666;padding:0;line-height:1}.close-btn[data-v-90993dd3]:hover{color:#333}.modal-body[data-v-90993dd3]{padding:20px;overflow-y:auto;flex:1}.modal-footer[data-v-90993dd3]{padding:16px 20px;border-top:1px solid #eee;display:flex;justify-content:flex-end;gap:8px}.form-group[data-v-45f49038]{margin-bottom:16px}label[data-v-45f49038]{display:block;margin-bottom:6px;font-weight:500;color:#333}.required[data-v-45f49038]{color:#e74c3c}.form-input[data-v-45f49038]{width:100%;padding:10px 12px;border:1px solid #ddd;border-radius:4px;font-size:14px;transition:border-color .2s}.form-input[data-v-45f49038]:focus{outline:none;border-color:#3498db}.form-input[data-v-45f49038]:disabled{background:#f5f5f5;cursor:not-allowed}textarea.form-input[data-v-45f49038]{resize:vertical}select.form-input[data-v-45f49038]{cursor:pointer}.error-text[data-v-45f49038]{display:block;margin-top:4px;font-size:12px;color:#e74c3c}.hint-text[data-v-45f49038]{display:block;margin-top:4px;font-size:12px;color:#6c757d}.btn[data-v-e5da414f]{display:inline-flex;align-items:center;justify-content:center;gap:6px;padding:10px 16px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s}.btn[data-v-e5da414f]:disabled{opacity:.6;cursor:not-allowed}.btn-sm[data-v-e5da414f]{padding:6px 12px;font-size:12px}.btn-lg[data-v-e5da414f]{padding:12px 24px;font-size:16px}.btn-primary[data-v-e5da414f]{background:#3498db;color:#fff}.btn-primary[data-v-e5da414f]:hover:not(:disabled){background:#2980b9}.btn-secondary[data-v-e5da414f]{background:#6c757d;color:#fff}.btn-secondary[data-v-e5da414f]:hover:not(:disabled){background:#5a6268}.btn-danger[data-v-e5da414f]{background:#e74c3c;color:#fff}.btn-danger[data-v-e5da414f]:hover:not(:disabled){background:#c0392b}.btn-success[data-v-e5da414f]{background:#27ae60;color:#fff}.btn-success[data-v-e5da414f]:hover:not(:disabled){background:#1e8449}.btn-warning[data-v-e5da414f]{background:#f39c12;color:#fff}.btn-warning[data-v-e5da414f]:hover:not(:disabled){background:#d68910}.spinner[data-v-e5da414f]{width:14px;height:14px;border:2px solid transparent;border-top-color:currentColor;border-radius:50%;animation:spin-e5da414f .8s linear infinite}@keyframes spin-e5da414f{to{transform:rotate(360deg)}}.badge[data-v-b7bd2350]{display:inline-block;padding:4px 8px;font-size:12px;font-weight:500;border-radius:4px}.badge-default[data-v-b7bd2350]{background:#e9ecef;color:#495057}.badge-critical[data-v-b7bd2350],.badge-error[data-v-b7bd2350]{background:#e74c3c;color:#fff}.badge-warn[data-v-b7bd2350]{background:#f39c12;color:#fff}.badge-success[data-v-b7bd2350]{background:#27ae60;color:#fff}.badge-info[data-v-b7bd2350]{background:#3498db;color:#fff}.card[data-v-2f260fa2]{background:#fff;border-radius:8px;box-shadow:0 1px 3px #0000001a;overflow:hidden}.card-header[data-v-2f260fa2]{padding:16px 20px;border-bottom:1px solid #eee}.card-header h3[data-v-2f260fa2]{margin:0;font-size:1.1rem;color:#333}.card-body[data-v-2f260fa2]{padding:20px}.card-footer[data-v-2f260fa2]{padding:16px 20px;border-top:1px solid #eee;background:#f8f9fa}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
*{box-sizing:border-box}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}#app{min-height:100vh;background:#f5f5f5}.header{background:#2c3e50;color:#fff;padding:1rem 2rem;display:flex;align-items:center;gap:2rem}.header h1{font-size:1.5rem;margin:0}.header nav{display:flex;gap:1rem}.header nav a{color:#ecf0f1;text-decoration:none;padding:.5rem 1rem;border-radius:4px;transition:background .2s}.header nav a:hover{background:#34495e}.header nav a.router-link-active{background:#3498db}.main{padding:2rem;max-width:1400px;margin:0 auto}

View File

@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>LogHunter</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
</style>
<script type="module" crossorigin src="/assets/index-D2VmGkBi.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-GLdO36rE.css">
</head>
<body>
<div id="app"></div>
</body>
</html>