#!/usr/bin/env python3 """ Database for tracking file expiry in Sharey Simple SQLite database to store file paths and their expiry times """ import sqlite3 import os from datetime import datetime from pathlib import Path class ExpiryDatabase: """Manages file expiry tracking database""" def __init__(self, db_path="expiry.db"): self.db_path = db_path self.init_database() def init_database(self): """Initialize the expiry database with required tables""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() # Create expiry table cursor.execute(''' CREATE TABLE IF NOT EXISTS file_expiry ( id INTEGER PRIMARY KEY AUTOINCREMENT, file_path TEXT UNIQUE NOT NULL, expires_at TEXT NOT NULL, created_at TEXT NOT NULL, storage_backend TEXT NOT NULL ) ''') # Create URL shortener table cursor.execute(''' CREATE TABLE IF NOT EXISTS url_redirects ( id INTEGER PRIMARY KEY AUTOINCREMENT, short_code TEXT UNIQUE NOT NULL, target_url TEXT NOT NULL, created_at TEXT NOT NULL, expires_at TEXT, click_count INTEGER DEFAULT 0, is_active INTEGER DEFAULT 1, created_by_ip TEXT, notes TEXT ) ''') conn.commit() conn.close() print(f"✅ Expiry database initialized: {self.db_path}") def add_file(self, file_path: str, expires_at: str, storage_backend: str = "b2"): """Add a file with expiry time to the database""" try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() created_at = datetime.utcnow().isoformat() + 'Z' cursor.execute(''' INSERT OR REPLACE INTO file_expiry (file_path, expires_at, created_at, storage_backend) VALUES (?, ?, ?, ?) ''', (file_path, expires_at, created_at, storage_backend)) conn.commit() conn.close() print(f"📝 Added expiry tracking: {file_path} expires at {expires_at}") return True except Exception as e: print(f"❌ Failed to add expiry tracking for {file_path}: {e}") return False def get_expired_files(self) -> list: """Get list of files that have expired""" try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() current_time = datetime.utcnow().isoformat() + 'Z' cursor.execute(''' SELECT file_path, expires_at, storage_backend FROM file_expiry WHERE expires_at <= ? ORDER BY expires_at ASC ''', (current_time,)) expired_files = cursor.fetchall() conn.close() return [ { 'file_path': row[0], 'expires_at': row[1], 'storage_backend': row[2] } for row in expired_files ] except Exception as e: print(f"❌ Failed to get expired files: {e}") return [] def remove_file(self, file_path: str): """Remove a file from expiry tracking (after deletion)""" try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() cursor.execute('DELETE FROM file_expiry WHERE file_path = ?', (file_path,)) conn.commit() conn.close() print(f"🗑️ Removed from expiry tracking: {file_path}") return True except Exception as e: print(f"❌ Failed to remove expiry tracking for {file_path}: {e}") return False def get_all_files(self) -> list: """Get all files in expiry tracking""" try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() cursor.execute(''' SELECT file_path, expires_at, created_at, storage_backend FROM file_expiry ORDER BY expires_at ASC ''') all_files = cursor.fetchall() conn.close() return [ { 'file_path': row[0], 'expires_at': row[1], 'created_at': row[2], 'storage_backend': row[3] } for row in all_files ] except Exception as e: print(f"❌ Failed to get all files: {e}") return [] def get_stats(self) -> dict: """Get database statistics""" try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() # Total files cursor.execute('SELECT COUNT(*) FROM file_expiry') total_files = cursor.fetchone()[0] # Expired files current_time = datetime.utcnow().isoformat() + 'Z' cursor.execute('SELECT COUNT(*) FROM file_expiry WHERE expires_at <= ?', (current_time,)) expired_files = cursor.fetchone()[0] # Files by storage backend cursor.execute('SELECT storage_backend, COUNT(*) FROM file_expiry GROUP BY storage_backend') by_backend = dict(cursor.fetchall()) conn.close() return { 'total_files': total_files, 'expired_files': expired_files, 'active_files': total_files - expired_files, 'by_backend': by_backend } except Exception as e: print(f"❌ Failed to get stats: {e}") return {} # URL Shortener Methods def add_redirect(self, short_code, target_url, expires_at=None, created_by_ip=None, notes=None): """Add a URL redirect""" try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() created_at = datetime.utcnow().isoformat() + 'Z' cursor.execute(''' INSERT INTO url_redirects (short_code, target_url, created_at, expires_at, created_by_ip, notes) VALUES (?, ?, ?, ?, ?, ?) ''', (short_code, target_url, created_at, expires_at, created_by_ip, notes)) conn.commit() conn.close() print(f"✅ Added redirect: {short_code} -> {target_url}") return True except sqlite3.IntegrityError: print(f"❌ Short code already exists: {short_code}") return False except Exception as e: print(f"❌ Failed to add redirect: {e}") return False def get_redirect(self, short_code): """Get redirect target URL and increment click count""" try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() # Check if redirect exists and is active cursor.execute(''' SELECT target_url, expires_at, is_active FROM url_redirects WHERE short_code = ? AND is_active = 1 ''', (short_code,)) result = cursor.fetchone() if not result: conn.close() return None target_url, expires_at, is_active = result # Check if expired if expires_at: current_time = datetime.utcnow().isoformat() + 'Z' if expires_at <= current_time: conn.close() return None # Increment click count cursor.execute(''' UPDATE url_redirects SET click_count = click_count + 1 WHERE short_code = ? ''', (short_code,)) conn.commit() conn.close() return target_url except Exception as e: print(f"❌ Failed to get redirect: {e}") return None def disable_redirect(self, short_code): """Disable a redirect (for abuse/takedown)""" try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() cursor.execute(''' UPDATE url_redirects SET is_active = 0 WHERE short_code = ? ''', (short_code,)) rows_affected = cursor.rowcount conn.commit() conn.close() if rows_affected > 0: print(f"✅ Disabled redirect: {short_code}") return True else: print(f"❌ Redirect not found: {short_code}") return False except Exception as e: print(f"❌ Failed to disable redirect: {e}") return False def list_redirects(self, limit=100): """List recent redirects for admin purposes""" try: conn = sqlite3.connect(self.db_path) cursor = conn.cursor() cursor.execute(''' SELECT short_code, target_url, created_at, expires_at, click_count, is_active, created_by_ip FROM url_redirects ORDER BY created_at DESC LIMIT ? ''', (limit,)) redirects = [] for row in cursor.fetchall(): redirects.append({ 'short_code': row[0], 'target_url': row[1], 'created_at': row[2], 'expires_at': row[3], 'click_count': row[4], 'is_active': bool(row[5]), 'created_by_ip': row[6] }) conn.close() return redirects except Exception as e: print(f"❌ Failed to list redirects: {e}") return []