Step1
This commit is contained in:
@@ -70,8 +70,8 @@ log-hunter.jar 실행
|
|||||||
|
|
||||||
### Step 1. 프로젝트 초기 설정
|
### Step 1. 프로젝트 초기 설정
|
||||||
|
|
||||||
- [ ] 1-1. Spring Boot 프로젝트 생성
|
- [x] 1-1. Spring Boot 프로젝트 생성
|
||||||
- [ ] 1-2. Vue3 프로젝트 생성 (frontend 폴더)
|
- [x] 1-2. Vue3 프로젝트 생성 (frontend 폴더)
|
||||||
- [ ] 1-3. 빌드 연동 (Vue → static 폴더로)
|
- [ ] 1-3. 빌드 연동 (Vue → static 폴더로)
|
||||||
- [ ] 1-4. SQLite 연동 확인
|
- [ ] 1-4. SQLite 연동 확인
|
||||||
- [ ] 1-5. 앱 실행 시 브라우저 자동 오픈
|
- [ ] 1-5. 앱 실행 시 브라우저 자동 오픈
|
||||||
@@ -158,3 +158,4 @@ Step 1 → Step 2 → Step 3 → Step 6 (공통)
|
|||||||
| 날짜 | 내용 |
|
| 날짜 | 내용 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| 2025-01-06 | 최초 작성, 요구사항 정리 |
|
| 2025-01-06 | 최초 작성, 요구사항 정리 |
|
||||||
|
| 2025-01-06 | Step 1-1, 1-2 완료 (Spring Boot, Vue3 프로젝트 생성) |
|
||||||
|
|||||||
25
backend/.gitignore
vendored
Normal file
25
backend/.gitignore
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Gradle
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Application
|
||||||
|
data/*.db
|
||||||
|
exports/
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# Compiled
|
||||||
|
*.class
|
||||||
|
*.jar
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
51
backend/build.gradle
Normal file
51
backend/build.gradle
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'org.springframework.boot' version '3.2.1'
|
||||||
|
id 'io.spring.dependency-management' version '1.1.4'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.osolit'
|
||||||
|
version = '0.0.1-SNAPSHOT'
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = '17'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Spring Boot
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
|
|
||||||
|
// SQLite
|
||||||
|
implementation 'org.xerial:sqlite-jdbc:3.45.1.0'
|
||||||
|
implementation 'org.hibernate.orm:hibernate-community-dialects:6.4.1.Final'
|
||||||
|
|
||||||
|
// SFTP
|
||||||
|
implementation 'com.jcraft:jsch:0.1.55'
|
||||||
|
|
||||||
|
// Utility
|
||||||
|
implementation 'org.projectlombok:lombok'
|
||||||
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
|
|
||||||
|
// Test
|
||||||
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('test') {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vue3 빌드 결과물을 static 폴더로 복사
|
||||||
|
task copyFrontend(type: Copy) {
|
||||||
|
from '../frontend/dist'
|
||||||
|
into 'src/main/resources/static'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 빌드 전에 frontend 복사
|
||||||
|
bootJar {
|
||||||
|
dependsOn copyFrontend
|
||||||
|
}
|
||||||
1
backend/data/.gitkeep
Normal file
1
backend/data/.gitkeep
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# SQLite DB 파일이 이 폴더에 생성됩니다.
|
||||||
7
backend/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
backend/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
92
backend/gradlew.bat
vendored
Normal file
92
backend/gradlew.bat
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%"=="" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem having the _cmd_ return code (for example, to use in scripts)
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
1
backend/settings.gradle
Normal file
1
backend/settings.gradle
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 'log-hunter'
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.osolit.loghunter;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
|
||||||
|
import java.awt.Desktop;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class LogHunterApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(LogHunterApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
|
public void openBrowser() {
|
||||||
|
String url = "http://localhost:8080";
|
||||||
|
try {
|
||||||
|
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
|
||||||
|
Desktop.getDesktop().browse(new URI(url));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("브라우저를 자동으로 열 수 없습니다. 직접 " + url + " 에 접속해주세요.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
backend/src/main/resources/application.yml
Normal file
26
backend/src/main/resources/application.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
server:
|
||||||
|
port: 8080
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: log-hunter
|
||||||
|
|
||||||
|
datasource:
|
||||||
|
url: jdbc:sqlite:./data/loghunter.db
|
||||||
|
driver-class-name: org.sqlite.JDBC
|
||||||
|
|
||||||
|
jpa:
|
||||||
|
database-platform: org.hibernate.community.dialect.SQLiteDialect
|
||||||
|
hibernate:
|
||||||
|
ddl-auto: update
|
||||||
|
show-sql: false
|
||||||
|
properties:
|
||||||
|
hibernate:
|
||||||
|
format_sql: true
|
||||||
|
|
||||||
|
# 앱 설정
|
||||||
|
app:
|
||||||
|
crypto:
|
||||||
|
key: ${LOGHUNTER_CRYPTO_KEY:LogHunterDefaultKey32Bytes!!}
|
||||||
|
export:
|
||||||
|
path: ./exports
|
||||||
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