{"id":45778068,"url":"https://github.com/clroot/snaplake","last_synced_at":"2026-03-03T15:01:07.357Z","repository":{"id":340690242,"uuid":"1166321060","full_name":"clroot/snaplake","owner":"clroot","description":"Self-hosted database snapshot management platform — capture, query, and compare PostgreSQL/MySQL snapshots as Parquet files","archived":false,"fork":false,"pushed_at":"2026-02-27T06:03:31.000Z","size":1067,"stargazers_count":9,"open_issues_count":1,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-27T14:37:01.219Z","etag":null,"topics":["database","duckdb","kotlin","mysql","parquet","postgresql","react","self-hosted","snapshot","spring-boot"],"latest_commit_sha":null,"homepage":"https://snaplake.clroot.io/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clroot.png","metadata":{"files":{"readme":"README.ko.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-25T05:14:40.000Z","updated_at":"2026-02-27T13:05:33.000Z","dependencies_parsed_at":"2026-02-27T11:05:06.571Z","dependency_job_id":null,"html_url":"https://github.com/clroot/snaplake","commit_stats":null,"previous_names":["clroot/snaplake"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/clroot/snaplake","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clroot%2Fsnaplake","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clroot%2Fsnaplake/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clroot%2Fsnaplake/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clroot%2Fsnaplake/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clroot","download_url":"https://codeload.github.com/clroot/snaplake/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clroot%2Fsnaplake/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29933021,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T09:58:13.507Z","status":"ssl_error","status_checked_at":"2026-02-28T09:57:57.047Z","response_time":90,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","duckdb","kotlin","mysql","parquet","postgresql","react","self-hosted","snapshot","spring-boot"],"created_at":"2026-02-26T10:46:36.480Z","updated_at":"2026-02-28T12:01:18.295Z","avatar_url":"https://github.com/clroot.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e [English](README.md)\n\n# Snaplake\n\n[![Docker Image Version](https://img.shields.io/docker/v/abcdkh1209/snaplake?sort=semver\u0026label=Docker%20Hub)](https://hub.docker.com/r/abcdkh1209/snaplake)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\n백업 복원 없이 과거 DB 데이터를 SQL로 조회하는 셀프호스팅 도구. PostgreSQL과 MySQL 테이블을 주기적으로 Parquet 파일로 스냅샷하고, DuckDB 기반 SQL로 아무 시점이나 조회할 수 있습니다.\n\n![Dashboard](docs/screenshots/features/dashboard.png)\n\n## 주요 기능\n\n### SQL 쿼리 엔진\n\nDuckDB를 사용하여 모든 스냅샷에 SQL 쿼리를 실행합니다. 조인, 집계, 필터링, CSV/JSON 내보내기를 지원합니다.\n\n![SQL Query](docs/screenshots/features/query.png)\n\n### 스냅샷 비교\n\n두 스냅샷을 나란히 비교하여 행 단위 차이를 확인합니다. 추가, 삭제, 변경된 행을 색상으로 구분합니다.\n\n![Compare Diff](docs/screenshots/features/compare-diff.png)\n\n### 자동 스냅샷\n\nCron 스케줄에 따라 테이블 전체를 Apache Parquet 파일로 캡처합니다. 필터링, 정렬, CSV/JSON 내보내기로 스냅샷 내용을 탐색할 수 있습니다.\n\n![Snapshot Browser](docs/screenshots/features/snapshots.png)\n\n### 기타 기능\n\n- **보존 정책** — 일별/월별 보존 제한으로 스토리지 자동 관리\n- **유연한 스토리지** — 로컬 파일 시스템 또는 S3 호환 오브젝트 스토리지 (AWS S3, MinIO 등)\n- **설정 마법사** — 관리자 계정, 스토리지, 첫 데이터소스 설정을 안내하는 초기 설정 가이드\n- **다크 모드** — 다크/라이트 테마 전체 지원\n\n## 빠른 시작\n\n### Docker (권장)\n\n```bash\ndocker run -d \\\n  --name snaplake \\\n  -p 8080:8080 \\\n  -v snaplake-data:/app/data \\\n  -e SNAPLAKE_JWT_SECRET=your-secret-key \\\n  -e SNAPLAKE_ENCRYPTION_KEY=your-encryption-key \\\n  abcdkh1209/snaplake:latest\n```\n\n\u003e **참고:** `your-secret-key`와 `your-encryption-key`를 안전한 값으로 변경하세요. 각각 JWT 서명과 데이터소스 비밀번호 암호화에 사용됩니다. 생략 시 재시작할 때마다 랜덤 키가 생성되어 기존 세션과 암호화된 데이터가 무효화됩니다.\n\n[http://localhost:8080](http://localhost:8080)을 열고 설정 마법사를 따라 진행하세요.\n\n### 샘플 데이터베이스로 체험\n\n사전 구성된 PostgreSQL 데이터베이스가 포함된 데모 compose 파일이 제공됩니다:\n\n```bash\ndocker compose -f docker-compose.demo.yml up\n```\n\nSnaplake와 함께 샘플 데이터(customers, products, orders)가 로드된 PostgreSQL 인스턴스가 시작됩니다. 설정 시 다음 정보로 연결하세요:\n\n| 항목 | 값 |\n|---|---|\n| Host | `sample-db` |\n| Port | `5432` |\n| Database | `sampledb` |\n| Username | `demo` |\n| Password | `demo1234` |\n\n### 로컬 개발\n\n**사전 요구사항:** Java 21, [Bun](https://bun.sh)\n\n```bash\n# 백엔드 시작 (포트 8080)\n./gradlew bootRun\n\n# 프론트엔드 개발 서버 시작 (포트 5173, 백엔드로 프록시)\ncd frontend \u0026\u0026 bun install \u0026\u0026 bun run dev\n```\n\n## 설정 가이드\n\n첫 실행 시 설정 마법사가 초기 설정을 안내합니다. 자세한 내용은 [설정 가이드](docs/setup-guide.ko.md)를 참조하세요.\n\n## 환경 설정\n\n모든 설정은 환경 변수로 관리합니다:\n\n| 변수 | 기본값 | 설명 |\n|---|---|---|\n| `SNAPLAKE_DATA_DIR` | `./data` | SQLite 메타데이터 DB 및 로컬 스냅샷 디렉토리 |\n| `SNAPLAKE_PORT` | `8080` | 서버 포트 |\n| `SNAPLAKE_JWT_SECRET` | (자동 생성) | JWT 서명 시크릿 |\n| `SNAPLAKE_ENCRYPTION_KEY` | (자동 생성) | 데이터소스 비밀번호 암호화용 AES 키 |\n\n스토리지(Local 또는 S3)는 설정 시 웹 UI에서 구성합니다.\n\n## 동작 방식\n\n```\nPostgreSQL / MySQL\n        |\n   스케줄 또는 수동 트리거\n        |\n   Parquet 파일로 스냅샷 ──→ 로컬 또는 S3 스토리지\n        |\n   DuckDB SQL 엔진 ──→ 조회, 분석, 비교\n```\n\n1. **캡처** — 스케줄 또는 수동 트리거로 테이블을 읽어 Parquet 파일로 저장\n2. **저장** — 스냅샷은 로컬 파일 시스템 또는 S3 호환 스토리지에 보관\n3. **조회** — DuckDB가 Parquet 파일을 직접 읽음 — 임포트도, 복원도 필요 없음\n\n## 아키텍처\n\n관심사의 명확한 분리를 위한 Hexagonal Architecture:\n\n```\nadapter/inbound     (Web, CLI, Scheduler)\n        |\napplication/port    (UseCase interfaces, Port interfaces)\napplication/service (UseCase implementations)\n        |\ndomain/model        (Pure Kotlin domain models)\n        |\nadapter/outbound    (JPA, DuckDB, S3, Local Storage)\n```\n\n의존성 방향: `adapter -\u003e application -\u003e domain`\n\n### 확장 포인트\n\n- **DatabaseDialect** — 새로운 데이터베이스 유형 지원 추가 (현재 PostgreSQL, MySQL)\n- **StorageProvider** — 새로운 스토리지 백엔드 추가 (현재 Local, S3)\n\n## 기술 스택\n\n| 계층 | 기술 |\n|---|---|\n| Backend | Kotlin, Spring Boot 3.4, Java 21 |\n| Metadata DB | SQLite |\n| Query Engine | DuckDB |\n| Snapshot Format | Apache Parquet |\n| Frontend | React 19, TypeScript, Vite |\n| UI Components | Carbon Design System |\n| Routing / State | TanStack Router, TanStack Query |\n| Auth | JWT + Argon2 |\n| Storage | Local filesystem / S3-compatible |\n\n## 빌드\n\n```bash\n# 전체 빌드 (백엔드 + 프론트엔드)\n./gradlew build\n\n# 테스트 실행\n./gradlew test\n\n# 프론트엔드만\ncd frontend \u0026\u0026 bun run build\n\n# 프론트엔드 린트\ncd frontend \u0026\u0026 bun run lint\n```\n\n## 라이선스\n\n이 프로젝트는 [MIT License](LICENSE)에 따라 라이선스가 부여됩니다.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclroot%2Fsnaplake","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclroot%2Fsnaplake","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclroot%2Fsnaplake/lists"}