diff --git a/.gradle/8.5/checksums/checksums.lock b/.gradle/8.5/checksums/checksums.lock new file mode 100644 index 0000000..18479a0 Binary files /dev/null and b/.gradle/8.5/checksums/checksums.lock differ diff --git a/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock b/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock new file mode 100644 index 0000000..b7ece63 Binary files /dev/null and b/.gradle/8.5/dependencies-accessors/dependencies-accessors.lock differ diff --git a/.gradle/8.5/dependencies-accessors/gc.properties b/.gradle/8.5/dependencies-accessors/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.gradle/8.5/executionHistory/executionHistory.lock b/.gradle/8.5/executionHistory/executionHistory.lock new file mode 100644 index 0000000..c668c5e Binary files /dev/null and b/.gradle/8.5/executionHistory/executionHistory.lock differ diff --git a/.gradle/8.5/fileChanges/last-build.bin b/.gradle/8.5/fileChanges/last-build.bin new file mode 100644 index 0000000..f76dd23 Binary files /dev/null and b/.gradle/8.5/fileChanges/last-build.bin differ diff --git a/.gradle/8.5/fileHashes/fileHashes.lock b/.gradle/8.5/fileHashes/fileHashes.lock new file mode 100644 index 0000000..d526a1d Binary files /dev/null and b/.gradle/8.5/fileHashes/fileHashes.lock differ diff --git a/.gradle/8.5/gc.properties b/.gradle/8.5/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000..17d6bca Binary files /dev/null and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..635b4fc --- /dev/null +++ b/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Tue Jan 06 21:43:32 KST 2026 +gradle.version=8.5 diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/HELP.md b/HELP.md new file mode 100644 index 0000000..51e689f --- /dev/null +++ b/HELP.md @@ -0,0 +1,25 @@ +# Getting Started + +### Reference Documentation + +For further reference, please consider the following sections: + +* [Official Gradle documentation](https://docs.gradle.org) +* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/4.0.1/gradle-plugin) +* [Create an OCI image](https://docs.spring.io/spring-boot/4.0.1/gradle-plugin/packaging-oci-image.html) +* [Spring Web](https://docs.spring.io/spring-boot/4.0.1/reference/web/servlet.html) + +### Guides + +The following guides illustrate how to use some features concretely: + +* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) +* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) +* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) + +### Additional Links + +These additional references should also help you: + +* [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle) + diff --git a/README.md b/README.md index 873aee4..ef7fb6f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ SFTP를 통해 원격 서버의 로그 파일을 수집하고, 등록된 패턴 | 구분 | 기술 | |------|------| -| Backend | Spring Boot | +| Backend | Spring Boot 3.2 | | Frontend | Vue3 (SPA, 빌드 후 static 폴더에 포함) | | Database | SQLite (파일 DB) | | SFTP | JSch | @@ -37,19 +37,22 @@ SFTP를 통해 원격 서버의 로그 파일을 수집하고, 등록된 패턴 | 4 | 패턴 관리 | 에러 패턴 등록/수정/삭제, 패턴 테스트 | | 5 | 설정 | 내보내기 경로, 로그 보관 기간, 포트 설정 | -## 폴더 구조 (예정) +## 폴더 구조 ``` log-hunter/ -├── backend/ # Spring Boot -│ └── src/main/resources/ -│ └── static/ # Vue3 빌드 결과물 +├── src/ # Spring Boot 소스 +│ └── main/ +│ ├── java/ # Java 소스 +│ └── resources/ +│ └── static/ # Vue3 빌드 결과물 ├── frontend/ # Vue3 (개발용) │ └── src/ │ └── views/ ├── data/ # SQLite DB ├── exports/ # HTML/TXT 내보내기 -└── config/ # 설정 파일 +├── build.gradle +└── settings.gradle ``` ## 실행 시 동작 @@ -72,57 +75,57 @@ log-hunter.jar 실행 - [x] 1-1. Spring Boot 프로젝트 생성 - [x] 1-2. Vue3 프로젝트 생성 (frontend 폴더) -- [ ] 1-3. 빌드 연동 (Vue → static 폴더로) -- [ ] 1-4. SQLite 연동 확인 -- [ ] 1-5. 앱 실행 시 브라우저 자동 오픈 +- [x] 1-3. 빌드 연동 (Vue → static 폴더로) +- [x] 1-4. SQLite 연동 확인 +- [x] 1-5. 앱 실행 시 브라우저 자동 오픈 ### Step 2. DB 스키마 설계 -- [ ] 2-1. servers 테이블 (SFTP 서버 정보) -- [ ] 2-2. server_log_paths 테이블 (서버별 로그 경로, 1:N) -- [ ] 2-3. patterns 테이블 (에러 패턴 정의) -- [ ] 2-4. scan_history 테이블 (분석 실행 이력) -- [ ] 2-5. error_logs 테이블 (검출된 에러) -- [ ] 2-6. settings 테이블 (전역 설정, key-value) +- [x] 2-1. servers 테이블 (SFTP 서버 정보) +- [x] 2-2. server_log_paths 테이블 (서버별 로그 경로, 1:N) +- [x] 2-3. patterns 테이블 (에러 패턴 정의) +- [x] 2-4. scan_history 테이블 (분석 실행 이력) +- [x] 2-5. error_logs 테이블 (검출된 에러) +- [x] 2-6. settings 테이블 (전역 설정, key-value) ### Step 3. Backend - 기본 CRUD API -- [ ] 3-1. 서버 관리 API (CRUD) -- [ ] 3-2. 로그 경로 관리 API -- [ ] 3-3. 패턴 관리 API (CRUD) -- [ ] 3-4. 설정 API -- [ ] 3-5. 비밀번호/passphrase 암호화 유틸 +- [x] 3-1. 서버 관리 API (CRUD) +- [x] 3-2. 로그 경로 관리 API +- [x] 3-3. 패턴 관리 API (CRUD) +- [x] 3-4. 설정 API +- [x] 3-5. 비밀번호/passphrase 암호화 유틸 ### Step 4. Backend - 핵심 기능 -- [ ] 4-1. SFTP 연결 (비밀번호 / 키 파일) -- [ ] 4-2. 연결 테스트 API -- [ ] 4-3. 파일 목록 조회 (마지막 분석 이후 파일) -- [ ] 4-4. 파일 다운로드 -- [ ] 4-5. 로그 파싱 + 패턴 매칭 엔진 -- [ ] 4-6. 에러 저장 (컨텍스트 포함) -- [ ] 4-7. 분석 실행 API (진행상황 SSE 또는 WebSocket) +- [x] 4-1. SFTP 연결 (비밀번호 / 키 파일) +- [x] 4-2. 연결 테스트 API +- [x] 4-3. 파일 목록 조회 (마지막 분석 이후 파일) +- [x] 4-4. 파일 다운로드 +- [x] 4-5. 로그 파싱 + 패턴 매칭 엔진 +- [x] 4-6. 에러 저장 (컨텍스트 포함) +- [x] 4-7. 분석 실행 API (진행상황 SSE 또는 WebSocket) ### Step 5. Backend - 내보내기 -- [ ] 5-1. HTML 리포트 생성 -- [ ] 5-2. 에러 상세 TXT 파일 생성 -- [ ] 5-3. 내보내기 API +- [x] 5-1. HTML 리포트 생성 +- [x] 5-2. TXT 리포트 생성 +- [x] 5-3. 내보내기 API ### Step 6. Frontend - 공통 -- [ ] 6-1. 레이아웃 (헤더, 사이드메뉴) -- [ ] 6-2. 라우터 설정 -- [ ] 6-3. API 클라이언트 (axios) -- [ ] 6-4. 공통 컴포넌트 (테이블, 모달, 폼) +- [x] 6-1. 레이아웃 (헤더, 사이드메뉴) +- [x] 6-2. 라우터 설정 +- [x] 6-3. API 클라이언트 (axios) +- [x] 6-4. 공통 컴포넌트 (테이블, 모달, 폼) ### Step 7. Frontend - 화면 개발 -- [ ] 7-1. 대시보드 (서버 목록 + 실행 + 진행상황) -- [ ] 7-2. 에러 이력 (필터 + 목록 + 상세보기) -- [ ] 7-3. 서버 관리 (목록 + 추가/수정 폼) -- [ ] 7-4. 패턴 관리 (목록 + 추가/수정 + 테스트) -- [ ] 7-5. 설정 +- [x] 7-1. 대시보드 (서버 목록 + 실행 + 진행상황) +- [x] 7-2. 에러 이력 (필터 + 목록 + 상세보기) +- [x] 7-3. 서버 관리 (목록 + 추가/수정 폼) +- [x] 7-4. 패턴 관리 (목록 + 추가/수정 + 테스트) +- [x] 7-5. 설정 ### Step 8. 테스트 및 마무리 @@ -155,7 +158,16 @@ Step 1 → Step 2 → Step 3 → Step 6 (공통) ## 변경 이력 -| 날짜 | 내용 | +| 일시 | 내용 | |------|------| | 2025-01-06 | 최초 작성, 요구사항 정리 | -| 2025-01-06 | Step 1-1, 1-2 완료 (Spring Boot, Vue3 프로젝트 생성) | +| 2025-01-06 | Step 1 완료 (Spring Boot, Vue3, SQLite, 빌드 연동, 브라우저 자동 오픈) | +| 2025-01-06 | Step 2 완료 (DB 스키마 설계 - 6개 Entity 생성) | +| 2025-01-06 | Step 3 완료 (Backend CRUD API - Repository, Service, Controller) | +| 2025-01-06 | Step 6 완료 (Frontend 공통 - 레이아웃, 라우터, API, 공통컴포넌트) | +| 2025-01-06 | Step 7-3, 7-4 완료 (서버 관리, 패턴 관리 화면) | +| 2025-01-06 | Step 4 완료 (Backend 핵심기능 - SFTP, 스캔엔진, 에러로그) | +| 2025-01-06 | Step 7-1, 7-2 완료 (대시보드, 에러 이력 화면) | +| 2025-01-06 | Step 5, 7-5 완료 (내보내기 HTML/TXT, 설정 화면) | +| 2025-01-06 19:05:00 | TEST_GUIDE.md 작성 (테스트 가이드) | +| 2025-01-06 19:23:00 | Frontend 빌드 + Backend 실행 테스트 성공 | diff --git a/TEST_GUIDE.md b/TEST_GUIDE.md new file mode 100644 index 0000000..dc355ab --- /dev/null +++ b/TEST_GUIDE.md @@ -0,0 +1,256 @@ +# LogHunter 테스트 가이드 + +## 1. 사전 요구사항 + +| 항목 | 버전 | 확인 명령어 | +|------|------|-------------| +| Java | 17 이상 | `java -version` | +| Node.js | 18 이상 | `node -v` | +| npm | 9 이상 | `npm -v` | + +--- + +## 2. 프로젝트 실행 + +### 2-1. Frontend 빌드 + +```bash +cd /Users/coziny/devs/osolit-research/workspace/log-hunter/frontend + +# 의존성 설치 (최초 1회) +npm install + +# 빌드 (결과물이 ../src/main/resources/static 으로 복사됨) +npm run build +``` + +### 2-2. Backend 실행 + +```bash +cd /Users/coziny/devs/osolit-research/workspace/log-hunter + +# Gradle Wrapper로 실행 +./gradlew bootRun +``` + +### 2-3. 접속 + +- 실행 시 브라우저가 자동으로 열림 +- 수동 접속: http://localhost:8080 + +--- + +## 3. 테스트 시나리오 + +### 3-1. 패턴 등록 (먼저!) + +1. **패턴 관리** 메뉴 클릭 +2. **+ 패턴 추가** 클릭 +3. 샘플 패턴 입력: + +| 패턴명 | 정규식 | 심각도 | 컨텍스트 | +|--------|--------|--------|----------| +| Exception | `Exception\|Error` | ERROR | 5 | +| NullPointer | `NullPointerException` | CRITICAL | 5 | +| WARN 로그 | `\[WARN\]` | WARN | 3 | +| Tomcat 에러 | `SEVERE\|FATAL` | CRITICAL | 5 | + +4. **테스트** 버튼으로 정규식 검증 가능 + +### 3-2. 서버 등록 + +1. **서버 관리** 메뉴 클릭 +2. **+ 서버 추가** 클릭 +3. 서버 정보 입력: + +**비밀번호 인증 예시:** +``` +서버명: 운영서버1 +호스트: 192.168.1.100 +포트: 22 +사용자명: username +인증방식: 비밀번호 +비밀번호: ******** +``` + +**키파일 인증 예시:** +``` +서버명: 개발서버 +호스트: 10.0.0.50 +포트: 22 +사용자명: deploy +인증방식: 키 파일 +키 파일 경로: C:\Users\사용자\.ssh\id_rsa +Passphrase: (없으면 비워둠) +``` + +4. **테스트** 버튼으로 연결 확인 + +### 3-3. 로그 경로 등록 + +1. 서버 목록에서 **경로** 버튼 클릭 +2. 로그 경로 추가: + +| 경로 | 파일 패턴 | 설명 | +|------|-----------|------| +| `/var/log/tomcat/` | `catalina.*.log` | Tomcat 로그 | +| `/var/log/` | `*.log` | 시스템 로그 | +| `/app/logs/` | `application-*.log` | 애플리케이션 로그 | + +### 3-4. 분석 실행 + +1. **대시보드** 메뉴 클릭 +2. 개별 서버 **분석 실행** 또는 **전체 분석 실행** +3. 진행상황 확인 (프로그레스 바) +4. 완료 후 **에러 이력**에서 결과 확인 + +### 3-5. 결과 확인 및 내보내기 + +1. **에러 이력** 메뉴 클릭 +2. 필터로 검색 (서버, 심각도, 기간 등) +3. **상세** 버튼으로 컨텍스트 확인 +4. **HTML 내보내기** 또는 **TXT 내보내기** + +--- + +## 4. 단일 JAR 빌드 + +### 4-1. 전체 빌드 (Frontend + Backend) + +```bash +cd /Users/coziny/devs/osolit-research/workspace/log-hunter + +# Frontend 빌드 +cd frontend && npm run build && cd .. + +# JAR 빌드 +./gradlew clean bootJar +``` + +### 4-2. 빌드 결과 + +``` +build/libs/log-hunter-0.0.1-SNAPSHOT.jar +``` + +### 4-3. JAR 실행 + +```bash +java -jar build/libs/log-hunter-0.0.1-SNAPSHOT.jar +``` + +--- + +## 5. Windows 실행 스크립트 + +### run.bat 생성 + +```batch +@echo off +title LogHunter +echo LogHunter 시작 중... +java -jar log-hunter.jar +pause +``` + +### 배포 폴더 구조 + +``` +LogHunter/ +├── log-hunter.jar +├── run.bat +└── data/ <- 자동 생성됨 (SQLite DB) +``` + +--- + +## 6. 트러블슈팅 + +### 포트 충돌 (8080 사용 중) + +```bash +# application.yml에서 포트 변경 +server: + port: 9090 +``` + +또는 실행 시 지정: +```bash +java -jar log-hunter.jar --server.port=9090 +``` + +### SFTP 연결 실패 + +- 호스트/포트 확인 +- 방화벽 확인 (22번 포트) +- 사용자명/비밀번호 확인 +- 키파일 경로 확인 (Windows: `C:\Users\...`, 절대경로) + +### 한글 깨짐 + +- 로그 파일 인코딩이 UTF-8인지 확인 +- 필요시 서버에서 `file -i 파일명`으로 인코딩 확인 + +### DB 초기화 (데이터 삭제) + +```bash +# data 폴더의 DB 파일 삭제 +rm -rf data/loghunter.db +``` + +--- + +## 7. 개발 모드 (Hot Reload) + +### Frontend 개발 서버 + +```bash +cd frontend +npm run dev +``` +- http://localhost:5173 에서 실행 +- API 프록시 설정으로 백엔드(8080)와 연동됨 + +### Backend만 재시작 + +```bash +./gradlew bootRun +``` + +--- + +## 8. API 테스트 (curl 예시) + +```bash +# 서버 목록 조회 +curl http://localhost:8080/api/servers + +# 패턴 목록 조회 +curl http://localhost:8080/api/patterns + +# 연결 테스트 +curl -X POST http://localhost:8080/api/servers/1/test-connection + +# 스캔 실행 (동기) +curl -X POST http://localhost:8080/api/scan/execute/1 + +# 에러 로그 검색 +curl "http://localhost:8080/api/error-logs?page=0&size=10" +``` + +--- + +## 체크리스트 + +- [x] Java 17+ 설치 확인 +- [x] Node.js 18+ 설치 확인 +- [x] Frontend 빌드 완료 (2025-01-06 19:23 확인) +- [x] Backend 실행 성공 (2025-01-06 19:23 확인) +- [x] 브라우저 접속 확인 (localhost:8080) +- [ ] 패턴 등록 +- [ ] 서버 등록 +- [ ] 연결 테스트 성공 +- [ ] 로그 경로 등록 +- [ ] 분석 실행 +- [ ] 에러 검출 확인 +- [ ] 내보내기 테스트 diff --git a/backend/.gitignore b/backend/.gitignore deleted file mode 100644 index e1eecbf..0000000 --- a/backend/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# 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 diff --git a/backend/build.gradle b/build.gradle similarity index 75% rename from backend/build.gradle rename to build.gradle index 93cf3c7..5235875 100644 --- a/backend/build.gradle +++ b/build.gradle @@ -4,11 +4,14 @@ plugins { id 'io.spring.dependency-management' version '1.1.4' } -group = 'com.osolit' +group = 'research' version = '0.0.1-SNAPSHOT' +description = 'log-hunter' java { - sourceCompatibility = '17' + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } repositories { @@ -33,19 +36,9 @@ dependencies { // Test testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } tasks.named('test') { useJUnitPlatform() } - -// Vue3 빌드 결과물을 static 폴더로 복사 -task copyFrontend(type: Copy) { - from '../frontend/dist' - into 'src/main/resources/static' -} - -// 빌드 전에 frontend 복사 -bootJar { - dependsOn copyFrontend -} diff --git a/backend/data/.gitkeep b/data/.gitkeep similarity index 100% rename from backend/data/.gitkeep rename to data/.gitkeep diff --git a/data/loghunter.db b/data/loghunter.db new file mode 100644 index 0000000..dd72f35 Binary files /dev/null and b/data/loghunter.db differ diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 97dcc40..8e0e83a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -65,9 +65,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", "cpu": [ "ppc64" ], @@ -82,9 +82,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", "cpu": [ "arm" ], @@ -99,9 +99,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "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==", + "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==", "cpu": [ "arm64" ], @@ -116,9 +116,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", "cpu": [ "x64" ], @@ -133,9 +133,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", "cpu": [ "arm64" ], @@ -150,9 +150,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", "cpu": [ "x64" ], @@ -167,9 +167,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "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==", "cpu": [ "arm64" ], @@ -184,9 +184,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "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==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", "cpu": [ "x64" ], @@ -201,9 +201,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "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==", "cpu": [ "arm" ], @@ -218,9 +218,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", "cpu": [ "arm64" ], @@ -235,9 +235,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", "cpu": [ "ia32" ], @@ -252,9 +252,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "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==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", "cpu": [ "loong64" ], @@ -269,9 +269,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", "cpu": [ "mips64el" ], @@ -286,9 +286,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "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==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", "cpu": [ "ppc64" ], @@ -303,9 +303,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", "cpu": [ "riscv64" ], @@ -320,9 +320,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", "cpu": [ "s390x" ], @@ -337,9 +337,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "cpu": [ "x64" ], @@ -354,9 +354,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", "cpu": [ "x64" ], @@ -371,9 +371,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", "cpu": [ "x64" ], @@ -388,9 +388,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "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==", + "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==", "cpu": [ "x64" ], @@ -405,9 +405,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", "cpu": [ "arm64" ], @@ -422,9 +422,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "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==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", "cpu": [ "ia32" ], @@ -439,9 +439,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "cpu": [ "x64" ], @@ -819,67 +819,67 @@ "license": "MIT" }, "node_modules/@vitejs/plugin-vue": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", - "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz", + "integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==", "dev": true, "license": "MIT", "engines": { "node": "^18.0.0 || >=20.0.0" }, "peerDependencies": { - "vite": "^5.0.0 || ^6.0.0", + "vite": "^5.0.0", "vue": "^3.2.25" } }, "node_modules/@vue/compiler-core": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz", - "integrity": "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz", + "integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@vue/shared": "3.5.26", - "entities": "^7.0.0", + "@babel/parser": "^7.23.6", + "@vue/shared": "3.4.15", + "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.2.1" + "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "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==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz", + "integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.26", - "@vue/shared": "3.5.26" + "@vue/compiler-core": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz", - "integrity": "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz", + "integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==", "license": "MIT", "dependencies": { - "@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", + "@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", "estree-walker": "^2.0.2", - "magic-string": "^0.30.21", - "postcss": "^8.5.6", - "source-map-js": "^1.2.1" + "magic-string": "^0.30.5", + "postcss": "^8.4.33", + "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz", - "integrity": "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz", + "integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.26", - "@vue/shared": "3.5.26" + "@vue/compiler-dom": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/devtools-api": { @@ -889,53 +889,52 @@ "license": "MIT" }, "node_modules/@vue/reactivity": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.26.tgz", - "integrity": "sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz", + "integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.26" + "@vue/shared": "3.4.15" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.26.tgz", - "integrity": "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz", + "integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.26", - "@vue/shared": "3.5.26" + "@vue/reactivity": "3.4.15", + "@vue/shared": "3.4.15" } }, "node_modules/@vue/runtime-dom": { - "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==", + "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==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.26", - "@vue/runtime-core": "3.5.26", - "@vue/shared": "3.5.26", - "csstype": "^3.2.3" + "@vue/runtime-core": "3.4.15", + "@vue/shared": "3.4.15", + "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.26.tgz", - "integrity": "sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz", + "integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.26", - "@vue/shared": "3.5.26" + "@vue/compiler-ssr": "3.4.15", + "@vue/shared": "3.4.15" }, "peerDependencies": { - "vue": "3.5.26" + "vue": "3.4.15" } }, "node_modules/@vue/shared": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz", - "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz", + "integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==", "license": "MIT" }, "node_modules/asynckit": { @@ -945,13 +944,13 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", + "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, @@ -1010,9 +1009,9 @@ } }, "node_modules/entities": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz", - "integrity": "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -1067,9 +1066,9 @@ } }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1080,29 +1079,29 @@ "node": ">=12" }, "optionalDependencies": { - "@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" + "@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" } }, "node_modules/estree-walker": { @@ -1323,27 +1322,57 @@ "license": "ISC" }, "node_modules/pinia": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", - "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz", + "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", "license": "MIT", "dependencies": { - "@vue/devtools-api": "^6.6.3", - "vue-demi": "^0.14.10" + "@vue/devtools-api": "^6.5.0", + "vue-demi": ">=0.14.5" }, "funding": { "url": "https://github.com/sponsors/posva" }, "peerDependencies": { + "@vue/composition-api": "^1.4.0", "typescript": ">=4.4.4", - "vue": "^2.7.0 || ^3.5.11" + "vue": "^2.6.14 || ^3.3.0" }, "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", @@ -1433,15 +1462,15 @@ } }, "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.11.tgz", + "integrity": "sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.19.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" }, "bin": { "vite": "bin/vite.js" @@ -1460,7 +1489,6 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", - "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -1478,9 +1506,6 @@ "sass": { "optional": true }, - "sass-embedded": { - "optional": true - }, "stylus": { "optional": true }, @@ -1493,16 +1518,16 @@ } }, "node_modules/vue": { - "version": "3.5.26", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz", - "integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz", + "integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==", "license": "MIT", "dependencies": { - "@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" + "@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" }, "peerDependencies": { "typescript": "*" @@ -1513,45 +1538,19 @@ } } }, - "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.6.4", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", - "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz", + "integrity": "sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==", "license": "MIT", "dependencies": { - "@vue/devtools-api": "^6.6.4" + "@vue/devtools-api": "^6.5.0" }, "funding": { "url": "https://github.com/sponsors/posva" }, "peerDependencies": { - "vue": "^3.5.0" + "vue": "^3.2.0" } } } diff --git a/frontend/package.json b/frontend/package.json index b2dc500..bfe4ada 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,10 +9,10 @@ "preview": "vite preview" }, "dependencies": { - "vue": "^3.4.15", - "vue-router": "^4.2.5", "axios": "^1.6.5", - "pinia": "^2.1.7" + "pinia": "^2.1.7", + "vue": "^3.4.15", + "vue-router": "^4.2.5" }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.3", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 54e3baa..f6cdda8 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -20,11 +20,19 @@ .header { background: #2c3e50; diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index cf8562b..ec2292d 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -23,4 +23,129 @@ api.interceptors.response.use( } ) +// Server API +export const serverApi = { + getAll: () => api.get('/servers'), + getAllActive: () => api.get('/servers/active'), + getById: (id) => api.get(`/servers/${id}`), + create: (data) => api.post('/servers', data), + update: (id, data) => api.put(`/servers/${id}`, data), + delete: (id) => api.delete(`/servers/${id}`), + testConnection: (id) => api.post(`/servers/${id}/test-connection`) +} + +// LogPath API +export const logPathApi = { + getByServerId: (serverId) => api.get(`/log-paths/server/${serverId}`), + getActiveByServerId: (serverId) => api.get(`/log-paths/server/${serverId}/active`), + getById: (id) => api.get(`/log-paths/${id}`), + create: (data) => api.post('/log-paths', data), + update: (id, data) => api.put(`/log-paths/${id}`, data), + delete: (id) => api.delete(`/log-paths/${id}`) +} + +// Pattern API +export const patternApi = { + getAll: () => api.get('/patterns'), + getAllActive: () => api.get('/patterns/active'), + getById: (id) => api.get(`/patterns/${id}`), + create: (data) => api.post('/patterns', data), + update: (id, data) => api.put(`/patterns/${id}`, data), + delete: (id) => api.delete(`/patterns/${id}`), + test: (regex, sampleText) => api.post('/patterns/test', null, { params: { regex, sampleText } }) +} + +// Setting API +export const settingApi = { + getAll: () => api.get('/settings'), + getAllAsMap: () => api.get('/settings/map'), + getValue: (key) => api.get(`/settings/${key}`), + save: (data) => api.post('/settings', data), + saveAll: (settings) => api.put('/settings', settings), + delete: (key) => api.delete(`/settings/${key}`) +} + +// Scan API +export const scanApi = { + // SSE 기반 스캔 시작 (진행상황 실시간 수신) + startWithProgress: (serverId, onProgress, onComplete, onError) => { + const eventSource = new EventSource(`/api/scan/start/${serverId}`) + + eventSource.addEventListener('progress', (event) => { + const progress = JSON.parse(event.data) + onProgress && onProgress(progress) + }) + + eventSource.addEventListener('complete', (event) => { + const result = JSON.parse(event.data) + onComplete && onComplete(result) + eventSource.close() + }) + + eventSource.addEventListener('error', (event) => { + if (event.data) { + onError && onError(event.data) + } + eventSource.close() + }) + + eventSource.onerror = () => { + eventSource.close() + } + + return eventSource + }, + + // SSE 기반 전체 서버 스캔 + startAllWithProgress: (onProgress, onComplete, onError) => { + const eventSource = new EventSource('/api/scan/start-all') + + eventSource.addEventListener('progress', (event) => { + const progress = JSON.parse(event.data) + onProgress && onProgress(progress) + }) + + eventSource.addEventListener('complete', (event) => { + const results = JSON.parse(event.data) + onComplete && onComplete(results) + eventSource.close() + }) + + eventSource.addEventListener('error', (event) => { + if (event.data) { + onError && onError(event.data) + } + eventSource.close() + }) + + eventSource.onerror = () => { + eventSource.close() + } + + return eventSource + }, + + // 동기 방식 스캔 (간단 실행) + execute: (serverId) => api.post(`/scan/execute/${serverId}`), + + // 진행 상황 조회 + getProgress: (serverId) => api.get(`/scan/progress/${serverId}`), + + // 스캔 이력 조회 + getHistory: (serverId) => api.get(`/scan/history/${serverId}`) +} + +// 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 }) +} + +// Export API (Step 5에서 구현 예정) +export const exportApi = { + exportHtml: (params) => api.post('/export/html', params, { responseType: 'blob' }), + exportTxt: (params) => api.post('/export/txt', params, { responseType: 'blob' }) +} + export default api diff --git a/frontend/src/components/Badge.vue b/frontend/src/components/Badge.vue new file mode 100644 index 0000000..75d4027 --- /dev/null +++ b/frontend/src/components/Badge.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/frontend/src/components/Button.vue b/frontend/src/components/Button.vue new file mode 100644 index 0000000..bfdac8f --- /dev/null +++ b/frontend/src/components/Button.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/frontend/src/components/Card.vue b/frontend/src/components/Card.vue new file mode 100644 index 0000000..934881c --- /dev/null +++ b/frontend/src/components/Card.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/frontend/src/components/DataTable.vue b/frontend/src/components/DataTable.vue new file mode 100644 index 0000000..db98e0b --- /dev/null +++ b/frontend/src/components/DataTable.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/frontend/src/components/FormInput.vue b/frontend/src/components/FormInput.vue new file mode 100644 index 0000000..75ee385 --- /dev/null +++ b/frontend/src/components/FormInput.vue @@ -0,0 +1,138 @@ +