Step1
This commit is contained in:
16
frontend/.gitignore
vendored
Normal file
16
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Build
|
||||
dist/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
23
frontend/index.html
Normal file
23
frontend/index.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!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">
|
||||
<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>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
1560
frontend/package-lock.json
generated
Normal file
1560
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
frontend/package.json
Normal file
21
frontend/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "log-hunter-frontend",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.4.15",
|
||||
"vue-router": "^4.2.5",
|
||||
"axios": "^1.6.5",
|
||||
"pinia": "^2.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"vite": "^5.0.11"
|
||||
}
|
||||
}
|
||||
69
frontend/src/App.vue
Normal file
69
frontend/src/App.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<header class="header">
|
||||
<h1>📊 LogHunter</h1>
|
||||
<nav>
|
||||
<router-link to="/">대시보드</router-link>
|
||||
<router-link to="/errors">에러 이력</router-link>
|
||||
<router-link to="/servers">서버 관리</router-link>
|
||||
<router-link to="/patterns">패턴 관리</router-link>
|
||||
<router-link to="/settings">설정</router-link>
|
||||
</nav>
|
||||
</header>
|
||||
<main class="main">
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
|
||||
.header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
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: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.header nav a:hover {
|
||||
background: #34495e;
|
||||
}
|
||||
|
||||
.header nav a.router-link-active {
|
||||
background: #3498db;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 2rem;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
26
frontend/src/api/index.js
Normal file
26
frontend/src/api/index.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: '/api',
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
// Request interceptor
|
||||
api.interceptors.request.use(
|
||||
config => config,
|
||||
error => Promise.reject(error)
|
||||
)
|
||||
|
||||
// Response interceptor
|
||||
api.interceptors.response.use(
|
||||
response => response.data,
|
||||
error => {
|
||||
console.error('API Error:', error)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default api
|
||||
11
frontend/src/main.js
Normal file
11
frontend/src/main.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
34
frontend/src/router/index.js
Normal file
34
frontend/src/router/index.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'dashboard',
|
||||
component: () => import('@/views/Dashboard.vue')
|
||||
},
|
||||
{
|
||||
path: '/errors',
|
||||
name: 'errors',
|
||||
component: () => import('@/views/ErrorLogs.vue')
|
||||
},
|
||||
{
|
||||
path: '/servers',
|
||||
name: 'servers',
|
||||
component: () => import('@/views/ServerManage.vue')
|
||||
},
|
||||
{
|
||||
path: '/patterns',
|
||||
name: 'patterns',
|
||||
component: () => import('@/views/PatternManage.vue')
|
||||
},
|
||||
{
|
||||
path: '/settings',
|
||||
name: 'settings',
|
||||
component: () => import('@/views/Settings.vue')
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
||||
61
frontend/src/views/Dashboard.vue
Normal file
61
frontend/src/views/Dashboard.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="dashboard">
|
||||
<h2>대시보드</h2>
|
||||
<p>서버 목록과 분석 실행 화면입니다.</p>
|
||||
|
||||
<div class="server-list">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>서버명</th>
|
||||
<th>마지막 분석</th>
|
||||
<th>마지막 에러</th>
|
||||
<th>액션</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="4" class="empty">등록된 서버가 없습니다.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dashboard h2 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.server-list {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #f8f9fa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
92
frontend/src/views/ErrorLogs.vue
Normal file
92
frontend/src/views/ErrorLogs.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="error-logs">
|
||||
<h2>에러 이력</h2>
|
||||
<p>수집된 에러 목록을 조회합니다.</p>
|
||||
|
||||
<div class="filters">
|
||||
<select><option>전체 서버</option></select>
|
||||
<input type="date" placeholder="시작일">
|
||||
<input type="date" placeholder="종료일">
|
||||
<input type="text" placeholder="키워드 검색">
|
||||
<button>검색</button>
|
||||
</div>
|
||||
|
||||
<div class="error-list">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>발생일시</th>
|
||||
<th>서버</th>
|
||||
<th>패턴</th>
|
||||
<th>에러 요약</th>
|
||||
<th>상세</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="5" class="empty">에러 이력이 없습니다.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.error-logs h2 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.filters select,
|
||||
.filters input {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.filters button {
|
||||
padding: 0.5rem 1rem;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.error-list {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
56
frontend/src/views/PatternManage.vue
Normal file
56
frontend/src/views/PatternManage.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="pattern-manage">
|
||||
<h2>패턴 관리</h2>
|
||||
<p>에러 검출 패턴을 관리합니다.</p>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn-primary">+ 패턴 추가</button>
|
||||
</div>
|
||||
|
||||
<div class="pattern-list">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>패턴명</th>
|
||||
<th>정규식</th>
|
||||
<th>심각도</th>
|
||||
<th>컨텍스트</th>
|
||||
<th>활성화</th>
|
||||
<th>액션</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="6" class="empty">등록된 패턴이 없습니다.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pattern-manage h2 { margin-bottom: 1rem; }
|
||||
.actions { margin-bottom: 1rem; }
|
||||
.btn-primary {
|
||||
padding: 0.5rem 1rem;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pattern-list {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #eee; }
|
||||
th { background: #f8f9fa; }
|
||||
.empty { text-align: center; color: #999; }
|
||||
</style>
|
||||
79
frontend/src/views/ServerManage.vue
Normal file
79
frontend/src/views/ServerManage.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="server-manage">
|
||||
<h2>서버 관리</h2>
|
||||
<p>SFTP 서버 접속 정보를 관리합니다.</p>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn-primary">+ 서버 추가</button>
|
||||
</div>
|
||||
|
||||
<div class="server-list">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>서버명</th>
|
||||
<th>호스트</th>
|
||||
<th>포트</th>
|
||||
<th>인증방식</th>
|
||||
<th>활성화</th>
|
||||
<th>액션</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="6" class="empty">등록된 서버가 없습니다.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.server-manage h2 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
padding: 0.5rem 1rem;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.server-list {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
79
frontend/src/views/Settings.vue
Normal file
79
frontend/src/views/Settings.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="settings">
|
||||
<h2>설정</h2>
|
||||
<p>애플리케이션 설정을 관리합니다.</p>
|
||||
|
||||
<div class="settings-form">
|
||||
<div class="form-group">
|
||||
<label>내보내기 경로</label>
|
||||
<input type="text" v-model="form.exportPath" placeholder="./exports">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>로그 보관 기간 (일)</label>
|
||||
<input type="number" v-model="form.retentionDays" min="1">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>웹 서버 포트</label>
|
||||
<input type="number" v-model="form.port" min="1" max="65535">
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn-primary">저장</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const form = ref({
|
||||
exportPath: './exports',
|
||||
retentionDays: 30,
|
||||
port: 8080
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.settings h2 { margin-bottom: 1rem; }
|
||||
|
||||
.settings-form {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
padding: 0.5rem 1rem;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
25
frontend/vite.config.js
Normal file
25
frontend/vite.config.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
emptyOutDir: true
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user