diff --git a/ACCOUNT_LEDGER_SNAPSHOT.md b/ACCOUNT_LEDGER_SNAPSHOT.md new file mode 100644 index 0000000..fffed09 --- /dev/null +++ b/ACCOUNT_LEDGER_SNAPSHOT.md @@ -0,0 +1,52 @@ +# WEHAGO 계정별 원장 스냅샷 + +이 저장소에는 한맥기술 WEHAGO 계정별 원장 정보를 공유하기 위한 압축 SQLite 스냅샷이 포함되어 있습니다. + +## 포함 파일 + +```text +snapshots/wehago_account_ledger_snapshot_20260605.sqlite3.gz +``` + +## 포함 데이터 + +- `ledger_rows`: WEHAGO 계정별 원장 행 47,777건 +- `source_files`: 원장 생성에 사용된 원본 파일 메타 136건 +- `snapshot_manifest`: 스냅샷 생성일과 행 수 정보 + +전표 행(`voucher_rows`)과 비교 결과(`comparison_results`)는 이 스냅샷에 포함하지 않았습니다. 계정별 원장 공유 목적에 맞춰 파일 크기와 노출 범위를 줄이기 위해서입니다. + +## 복원 방법 + +저장소를 받은 뒤 아래 명령을 실행합니다. + +```bash +cd WEHAGO_DB +python3 scripts/restore_account_ledger_snapshot.py +``` + +복원 결과: + +```text +data/wehago_account_ledger.sqlite3 +``` + +다른 경로로 복원하려면: + +```bash +python3 scripts/restore_account_ledger_snapshot.py --output /desired/path/wehago_account_ledger.sqlite3 +``` + +## 무결성 + +압축 스냅샷 SHA-256: + +```text +dd1e8792d277427f1c95aa95f8eeefe92b208adc51168161ec3ec5b16f0b526f +``` + +복원 스크립트는 기본적으로 이 체크섬을 검증합니다. + +## 주의 + +이 데이터에는 원장, 거래처, 금액 등 업무 정보가 포함될 수 있습니다. Gitea 저장소 접근 권한은 내부 공유 대상자로 제한해야 합니다. diff --git a/scripts/restore_account_ledger_snapshot.py b/scripts/restore_account_ledger_snapshot.py new file mode 100644 index 0000000..040a177 --- /dev/null +++ b/scripts/restore_account_ledger_snapshot.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +import argparse +import gzip +import hashlib +import shutil +from pathlib import Path + + +DEFAULT_SNAPSHOT = Path(__file__).resolve().parents[1] / "snapshots" / "wehago_account_ledger_snapshot_20260605.sqlite3.gz" +DEFAULT_OUTPUT = Path(__file__).resolve().parents[1] / "data" / "wehago_account_ledger.sqlite3" +EXPECTED_SHA256 = "dd1e8792d277427f1c95aa95f8eeefe92b208adc51168161ec3ec5b16f0b526f" + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def main() -> None: + parser = argparse.ArgumentParser(description="Restore the shared WEHAGO account ledger SQLite snapshot.") + parser.add_argument("--snapshot", type=Path, default=DEFAULT_SNAPSHOT, help="Path to the .sqlite3.gz snapshot.") + parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT, help="Output SQLite DB path.") + parser.add_argument("--skip-checksum", action="store_true", help="Skip snapshot checksum verification.") + args = parser.parse_args() + + snapshot = args.snapshot.expanduser().resolve() + output = args.output.expanduser().resolve() + if not snapshot.exists(): + raise SystemExit(f"Snapshot not found: {snapshot}") + if not args.skip_checksum: + actual = sha256(snapshot) + if actual != EXPECTED_SHA256: + raise SystemExit(f"Checksum mismatch: {actual} != {EXPECTED_SHA256}") + + output.parent.mkdir(parents=True, exist_ok=True) + temp_output = output.with_suffix(output.suffix + ".tmp") + with gzip.open(snapshot, "rb") as source, temp_output.open("wb") as dest: + shutil.copyfileobj(source, dest) + temp_output.replace(output) + print(f"Restored: {output}") + + +if __name__ == "__main__": + main() diff --git a/snapshots/wehago_account_ledger_snapshot_20260605.sqlite3.gz b/snapshots/wehago_account_ledger_snapshot_20260605.sqlite3.gz new file mode 100644 index 0000000..1dc1f8c Binary files /dev/null and b/snapshots/wehago_account_ledger_snapshot_20260605.sqlite3.gz differ