feat: Initial commit for OnEver Drive centralized backup system with Windows PyQt6 agent and Proxmox LXC deployment
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import sqlite3
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any, List
|
||||
from agent.config import AGENT_HOME
|
||||
|
||||
STATE_DB_PATH = AGENT_HOME / "agent_state.db"
|
||||
|
||||
class StateDatabase:
|
||||
"""Local SQLite database for agent offline resiliency, session resume tracking and file caching."""
|
||||
|
||||
def __init__(self, db_path: Path = STATE_DB_PATH):
|
||||
self.db_path = db_path
|
||||
self._init_db()
|
||||
|
||||
def _get_conn(self) -> sqlite3.Connection:
|
||||
return sqlite3.connect(str(self.db_path))
|
||||
|
||||
def _init_db(self):
|
||||
with self._get_conn() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS upload_sessions (
|
||||
filepath TEXT PRIMARY KEY,
|
||||
sha256 TEXT NOT NULL,
|
||||
session_code TEXT NOT NULL,
|
||||
total_chunks INTEGER NOT NULL,
|
||||
chunk_size INTEGER NOT NULL,
|
||||
completed_chunks INTEGER DEFAULT 0,
|
||||
status TEXT NOT NULL,
|
||||
last_updated TIMESTAMP NOT NULL
|
||||
)
|
||||
""")
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS completed_files (
|
||||
filepath TEXT PRIMARY KEY,
|
||||
sha256 TEXT NOT NULL,
|
||||
file_size INTEGER NOT NULL,
|
||||
job_id INTEGER,
|
||||
last_backup_time TIMESTAMP NOT NULL
|
||||
)
|
||||
""")
|
||||
conn.commit()
|
||||
|
||||
def save_session(
|
||||
self,
|
||||
filepath: str,
|
||||
sha256: str,
|
||||
session_code: str,
|
||||
total_chunks: int,
|
||||
chunk_size: int,
|
||||
status: str = "UPLOADING"
|
||||
):
|
||||
with self._get_conn() as conn:
|
||||
cursor = conn.cursor()
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
cursor.execute("""
|
||||
INSERT OR REPLACE INTO upload_sessions
|
||||
(filepath, sha256, session_code, total_chunks, chunk_size, status, last_updated)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""", (filepath, sha256, session_code, total_chunks, chunk_size, status, now))
|
||||
conn.commit()
|
||||
|
||||
def get_session(self, filepath: str) -> Optional[Dict[str, Any]]:
|
||||
with self._get_conn() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT filepath, sha256, session_code, total_chunks, chunk_size, completed_chunks, status FROM upload_sessions WHERE filepath = ?", (filepath,))
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
return {
|
||||
"filepath": row[0],
|
||||
"sha256": row[1],
|
||||
"session_code": row[2],
|
||||
"total_chunks": row[3],
|
||||
"chunk_size": row[4],
|
||||
"completed_chunks": row[5],
|
||||
"status": row[6]
|
||||
}
|
||||
return None
|
||||
|
||||
def mark_session_completed(self, filepath: str, sha256: str, file_size: int, job_id: Optional[int] = None):
|
||||
with self._get_conn() as conn:
|
||||
cursor = conn.cursor()
|
||||
now = datetime.now(timezone.utc).isoformat()
|
||||
cursor.execute("DELETE FROM upload_sessions WHERE filepath = ?", (filepath,))
|
||||
cursor.execute("""
|
||||
INSERT OR REPLACE INTO completed_files (filepath, sha256, file_size, job_id, last_backup_time)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""", (filepath, sha256, file_size, job_id, now))
|
||||
conn.commit()
|
||||
|
||||
def is_file_already_backed_up(self, filepath: str, current_sha256: str) -> bool:
|
||||
with self._get_conn() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT sha256 FROM completed_files WHERE filepath = ?", (filepath,))
|
||||
row = cursor.fetchone()
|
||||
if row and row[0] == current_sha256:
|
||||
return True
|
||||
return False
|
||||
|
||||
state_db = StateDatabase()
|
||||
Reference in New Issue
Block a user