| import gradio as gr |
| from fastapi import FastAPI, HTTPException, Request |
| from fastapi.responses import StreamingResponse |
| from pathlib import Path |
| import hashlib |
| import os |
| import sqlite3 |
| import tempfile |
| import zipfile |
| from datetime import datetime, timedelta |
| import base64 |
| from cryptography.hazmat.primitives import hashes |
| from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC |
| from cryptography.fernet import Fernet, InvalidToken |
| from apscheduler.schedulers.background import BackgroundScheduler |
|
|
| |
| DATA_DIR = Path("/data") |
| ENCRYPTED_DIR = DATA_DIR / "encrypted" |
| ENCRYPTED_DIR.mkdir(parents=True, exist_ok=True) |
| DB_PATH = DATA_DIR / "files.db" |
|
|
| MAX_STORAGE_TB = 2 |
| DEFAULT_TTL = 30 |
| DEFAULT_DOWNLOAD_LIMIT = 10 |
|
|
| |
| def init_db(): |
| conn = sqlite3.connect(DB_PATH) |
| conn.execute(''' |
| CREATE TABLE IF NOT EXISTS files ( |
| sha TEXT PRIMARY KEY, |
| original_name TEXT, |
| upload_time TEXT, |
| ttl_days INTEGER, |
| download_limit INTEGER, |
| downloads INTEGER DEFAULT 0, |
| is_public INTEGER DEFAULT 0, |
| size INTEGER |
| ) |
| ''') |
| conn.commit() |
| conn.close() |
|
|
| init_db() |
|
|
| def get_db(): |
| conn = sqlite3.connect(DB_PATH) |
| conn.row_factory = sqlite3.Row |
| return conn |
|
|
| |
| def derive_key(password: str, salt: bytes = None): |
| if salt is None: |
| salt = os.urandom(16) |
| kdf = PBKDF2HMAC( |
| algorithm=hashes.SHA256(), |
| length=32, |
| salt=salt, |
| iterations=600_000, |
| ) |
| key = base64.urlsafe_b64encode(kdf.derive(password.encode())) |
| return key, salt |
|
|
| def encrypt_file(input_path: Path, password: str | None, is_public: bool): |
| data = input_path.read_bytes() |
| salt = os.urandom(16) |
| |
| if is_public: |
| key, salt = derive_key("public-default-2026", salt) |
| else: |
| key, salt = derive_key(password, salt) |
| |
| fernet = Fernet(key) |
| encrypted = fernet.encrypt(data) |
| |
| sha = hashlib.sha256(encrypted).hexdigest() |
| enc_path = ENCRYPTED_DIR / f"{sha}.enc" |
| enc_path.write_bytes(salt + encrypted) |
| |
| return enc_path, encrypted |
|
|
| def decrypt_file(sha: str, password: str | None) -> bytes: |
| enc_path = ENCRYPTED_DIR / f"{sha}.enc" |
| if not enc_path.exists(): |
| raise FileNotFoundError("File not found") |
| |
| data = enc_path.read_bytes() |
| salt = data[:16] |
| encrypted = data[16:] |
| |
| if password is None: |
| key, _ = derive_key("public-default-2026", salt) |
| else: |
| key, _ = derive_key(password, salt) |
| |
| fernet = Fernet(key) |
| return fernet.decrypt(encrypted) |
|
|
| |
| def get_usage(): |
| total_size = sum(f.stat().st_size for f in ENCRYPTED_DIR.glob("*.enc") if f.is_file()) |
| file_count = len(list(ENCRYPTED_DIR.glob("*.enc"))) |
| return total_size, file_count |
|
|
| def cleanup_old_files(): |
| now = datetime.now() |
| conn = get_db() |
| rows = conn.execute("SELECT sha, upload_time, ttl_days FROM files").fetchall() |
| for row in rows: |
| expiry = datetime.fromisoformat(row['upload_time']) + timedelta(days=row['ttl_days']) |
| if now > expiry: |
| (ENCRYPTED_DIR / f"{row['sha']}.enc").unlink(missing_ok=True) |
| conn.execute("DELETE FROM files WHERE sha=?", (row['sha'],)) |
| conn.commit() |
| conn.close() |
|
|
| scheduler = BackgroundScheduler() |
| scheduler.add_job(cleanup_old_files, 'interval', hours=4) |
| scheduler.start() |
|
|
| |
| def handle_upload(files, password: str, confirm_pw: str, ttl_days: int, download_limit: int, is_public: bool, progress=gr.Progress()): |
| if not files: |
| return "β No files selected.", None, None |
| |
| if not is_public and (not password or password != confirm_pw): |
| return "β Private files require matching password and confirmation.", None, None |
| |
| progress(0.1, desc="Preparing...") |
| |
| if isinstance(files, list) and len(files) > 1: |
| with tempfile.TemporaryDirectory() as tmpdir: |
| zip_path = Path(tmpdir) / "archive.zip" |
| with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf: |
| for i, f in enumerate(files): |
| progress(0.3 + 0.6 * i / len(files), desc=f"Zipping {Path(f.name).name}") |
| zf.write(f.name, Path(f.name).name) |
| orig_name = "archive.zip" |
| enc_path, _ = encrypt_file(zip_path, password if not is_public else None, is_public) |
| else: |
| f = files[0] if isinstance(files, list) else files |
| orig_name = Path(f.name).name |
| progress(0.4, desc="Encrypting...") |
| enc_path, _ = encrypt_file(Path(f.name), password if not is_public else None, is_public) |
| |
| sha = enc_path.stem |
| size = enc_path.stat().st_size |
| |
| conn = get_db() |
| conn.execute(''' |
| INSERT OR REPLACE INTO files |
| (sha, original_name, upload_time, ttl_days, download_limit, downloads, is_public, size) |
| VALUES (?, ?, ?, ?, ?, 0, ?, ?) |
| ''', (sha, orig_name, datetime.now().isoformat(), ttl_days, download_limit, int(is_public), size)) |
| conn.commit() |
| conn.close() |
| |
| used, count = get_usage() |
| status = f"β
Uploaded!\nSHA: {sha[:16]}...\nStorage: {used/(1024**4):.3f} TB / {MAX_STORAGE_TB} TB" |
| space_id = os.getenv('SPACE_ID', 'your-space') |
| link = f"https://{space_id}.hf.space/dw/{sha}" |
| return status, sha, link |
|
|
| |
| def download_file(sha: str, deck: str = None): |
| conn = get_db() |
| row = conn.execute("SELECT * FROM files WHERE sha=?", (sha,)).fetchone() |
| if not row: |
| raise HTTPException(status_code=404, detail="File not found") |
| |
| if row['downloads'] >= row['download_limit'] and row['download_limit'] > 0: |
| (ENCRYPTED_DIR / f"{sha}.enc").unlink(missing_ok=True) |
| conn.execute("DELETE FROM files WHERE sha=?", (sha,)) |
| conn.commit() |
| raise HTTPException(status_code=410, detail="File expired (download limit reached)") |
| |
| try: |
| decrypted = decrypt_file(sha, None if row['is_public'] else deck) |
| conn.execute("UPDATE files SET downloads = downloads + 1 WHERE sha=?", (sha,)) |
| conn.commit() |
| except InvalidToken: |
| raise HTTPException(status_code=401, detail="Invalid password") |
| except Exception: |
| raise HTTPException(status_code=500, detail="Decryption failed") |
| finally: |
| conn.close() |
| |
| orig_name = row['original_name'] |
| media_type = "application/zip" if orig_name.endswith(".zip") else "application/octet-stream" |
| |
| return StreamingResponse( |
| iter([decrypted]), |
| media_type=media_type, |
| headers={"Content-Disposition": f'attachment; filename="{orig_name}"'} |
| ) |
|
|
| |
| with gr.Blocks(title="Secure Vault") as demo: |
| gr.Markdown("# π Secure Encrypted File Vault\n**2 TB Storage β’ Auto-expiry β’ Passwords never stored**") |
| |
| with gr.Tab("π€ Upload"): |
| with gr.Row(): |
| files_in = gr.File(label="Select one or multiple files", file_count="multiple") |
| |
| with gr.Column(): |
| is_public_in = gr.Checkbox(label="π Public (no password)", value=False) |
| pw_in = gr.Textbox(label="π Encryption Password", type="password") |
| confirm_pw_in = gr.Textbox(label="π Confirm Password", type="password") |
| ttl_in = gr.Slider(1, 30, value=DEFAULT_TTL, step=1, label="β³ TTL (days)") |
| dlimit_in = gr.Slider(1, 50, value=DEFAULT_DOWNLOAD_LIMIT, step=1, label="π’ Max Downloads") |
| |
| upload_btn = gr.Button("π Encrypt & Upload", variant="primary") |
| |
| upload_status = gr.Textbox(label="Status", lines=3) |
| share_link_out = gr.Textbox(label="π Shareable Download Link") |
| |
| def toggle_visibility(is_public): |
| return gr.update(visible=not is_public), gr.update(visible=not is_public) |
| |
| is_public_in.change(toggle_visibility, is_public_in, [pw_in, confirm_pw_in]) |
| |
| upload_btn.click( |
| handle_upload, |
| inputs=[files_in, pw_in, confirm_pw_in, ttl_in, dlimit_in, is_public_in], |
| outputs=[upload_status, gr.State(), share_link_out] |
| ) |
| |
| with gr.Tab("π File Browser"): |
| with gr.Row(): |
| search_in = gr.Textbox(placeholder="Search...", label="Filter") |
| refresh_btn = gr.Button("Refresh") |
| |
| file_table = gr.Dataframe( |
| headers=["SHA", "Name", "Uploaded", "Expires In", "Downloads", "Public"], |
| value=[], |
| interactive=False |
| ) |
| |
| def refresh_browser(search_term=""): |
| conn = get_db() |
| rows = conn.execute("SELECT * FROM files ORDER BY upload_time DESC").fetchall() |
| conn.close() |
| |
| data = [] |
| for r in rows: |
| expiry_days = max(0, (datetime.fromisoformat(r['upload_time']) + timedelta(days=r['ttl_days']) - datetime.now()).days) |
| data.append([ |
| r['sha'][:16] + "...", |
| r['original_name'], |
| r['upload_time'][:10], |
| f"{expiry_days} days", |
| f"{r['downloads']}/{r['download_limit']}", |
| "β
Yes" if r['is_public'] else "π No" |
| ]) |
| return data |
| |
| refresh_btn.click(refresh_browser, inputs=search_in, outputs=file_table) |
| |
| with gr.Tab("π Dashboard"): |
| usage_md = gr.Markdown() |
| |
| def update_usage(): |
| used, count = get_usage() |
| pct = (used / (MAX_STORAGE_TB * 1024**4)) * 100 |
| return f""" |
| **Storage**: {used / (1024**4):.3f} TB / {MAX_STORAGE_TB} TB ({pct:.1f}%) |
| **Files**: {count} |
| **Status**: {'π’ OK' if pct < 85 else 'β οΈ Near limit'} |
| """ |
| |
| demo.load(update_usage, outputs=usage_md) |
| |
| gr.Markdown(""" |
| ### Download Instructions |
| β’ Public: `https://your-space.hf.space/dw/{SHA}` |
| β’ Private: `https://your-space.hf.space/dw/{SHA}?deck=YOUR_PASSWORD` |
| """) |
|
|
| |
| fastapi_app = FastAPI(title="Secure Vault") |
|
|
| @fastapi_app.get("/dw/{sha}") |
| async def dw_route(sha: str, request: Request): |
| deck = request.query_params.get("deck") |
| return download_file(sha, deck) |
|
|
| |
| app = gr.mount_gradio_app(fastapi_app, demo, path="/") |
|
|
| |