Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import time | |
| import pandas as pd # Included as requested | |
| from flask import Flask, render_template, request, redirect, url_for, session, flash | |
| from huggingface_hub import HfApi, hf_hub_download | |
| from huggingface_hub.utils import EntryNotFoundError, RepositoryNotFoundError, RevisionNotFoundError | |
| from werkzeug.security import generate_password_hash, check_password_hash | |
| from threading import Lock | |
| from dotenv import load_dotenv | |
| # Load local .env file if present (Good for local testing) | |
| load_dotenv() | |
| app = Flask(__name__) | |
| app.secret_key = os.environ.get("SECRET_KEY", "super_secret_key_123") | |
| # --- CONFIGURATION (WITH AUTO-FIX) --- | |
| # We use .strip() here to automatically remove invisible spaces | |
| # that often happen when pasting into Space Settings. | |
| token_env = os.environ.get("HF_TOKEN") | |
| HF_TOKEN = token_env.strip() if token_env else None | |
| repo_env = os.environ.get("DB_REPO") | |
| DB_REPO = repo_env.strip() if repo_env else None | |
| DB_FILE = "db.json" | |
| # Print to logs so you can verify exactly what the app sees | |
| print(f"--- SYSTEM CONFIG ---") | |
| print(f"Target Repo: '{DB_REPO}'") | |
| print(f"Token Found: {'Yes' if HF_TOKEN else 'NO (Check Secrets!)'}") | |
| print(f"---------------------") | |
| # Initialize API | |
| if HF_TOKEN: | |
| api = HfApi(token=HF_TOKEN) | |
| else: | |
| api = None | |
| print("WARNING: No HF_TOKEN found. App is strictly Read-Only and may crash on save.") | |
| db_lock = Lock() | |
| # --- STARTER DATA --- | |
| DEFAULT_PINS = [ | |
| {"id": 1, "url": "https://images.unsplash.com/photo-1541963463532-d68292c34b19", "caption": "Nature Vibes", "author": "System"}, | |
| {"id": 2, "url": "https://images.unsplash.com/photo-1493246507139-91e8fad9978e", "caption": "Alpine Lake", "author": "System"}, | |
| {"id": 3, "url": "https://images.unsplash.com/photo-1511497584788-876760111969", "caption": "Forest Mist", "author": "System"}, | |
| {"id": 4, "url": "https://images.unsplash.com/photo-1682687982501-1e58ab814714", "caption": "Desert Life", "author": "System"}, | |
| {"id": 5, "url": "https://images.unsplash.com/photo-1472214103451-9374bd1c798e", "caption": "Green Valley", "author": "System"} | |
| ] | |
| def load_db(): | |
| """ | |
| Tries to download the DB. If it fails because the file doesn't exist yet, | |
| it returns the default data so the app can start (and save it later). | |
| """ | |
| if not HF_TOKEN or not DB_REPO: | |
| print("WARNING: HF_TOKEN or DB_REPO secrets are missing. Site is Read-Only.") | |
| return {"users": {}, "pins": DEFAULT_PINS} | |
| try: | |
| print(f"Attempting to download {DB_FILE} from {DB_REPO}...") | |
| path = hf_hub_download(repo_id=DB_REPO, filename=DB_FILE, repo_type="dataset", token=HF_TOKEN) | |
| with open(path, 'r') as f: | |
| data = json.load(f) | |
| # Ensure structure exists | |
| if 'pins' not in data: data['pins'] = DEFAULT_PINS | |
| if 'users' not in data: data['users'] = {} | |
| print("Database loaded successfully.") | |
| return data | |
| except (EntryNotFoundError, RepositoryNotFoundError, RevisionNotFoundError) as e: | |
| print(f"NOTE: Could not load {DB_FILE}. Reason: {e}") | |
| print("Initializing with default data (Normal for first run).") | |
| return {"users": {}, "pins": DEFAULT_PINS} | |
| except Exception as e: | |
| print(f"CRITICAL: Unknown DB Load Error: {e}") | |
| return {"users": {}, "pins": DEFAULT_PINS} | |
| def save_db(data): | |
| """ | |
| Saves the DB locally and then pushes to Hugging Face. | |
| """ | |
| if not HF_TOKEN or not DB_REPO: | |
| flash("Error: Cannot save. Secrets (HF_TOKEN/DB_REPO) are missing!") | |
| return False | |
| with db_lock: | |
| try: | |
| # 1. Save locally | |
| with open(DB_FILE, 'w') as f: | |
| json.dump(data, f, indent=2) | |
| # 2. Push to Hugging Face Dataset | |
| if api: | |
| print("Syncing to Hugging Face Dataset...") | |
| api.upload_file( | |
| path_or_fileobj=DB_FILE, | |
| path_in_repo=DB_FILE, | |
| repo_id=DB_REPO, | |
| repo_type="dataset", | |
| commit_message="Sync DB via App" | |
| ) | |
| print("Sync successful!") | |
| return True | |
| else: | |
| flash("Error: API not initialized (Missing Token)") | |
| return False | |
| except Exception as e: | |
| print(f"Sync Error: {e}") | |
| flash(f"Database Sync Failed: {str(e)}") | |
| return False | |
| # Load data on startup | |
| DATA_CACHE = load_db() | |
| # --- ROUTES --- | |
| def index(): | |
| return render_template('index.html', pins=DATA_CACHE.get('pins', []), user=session.get('user')) | |
| def signup(): | |
| username = request.form.get('username') | |
| password = request.form.get('password') | |
| if not username or not password: | |
| flash("Username and password required") | |
| return redirect(url_for('index')) | |
| # 1. Check if user exists | |
| if username in DATA_CACHE.get('users', {}): | |
| flash("User already exists!") | |
| return redirect(url_for('index')) | |
| # 2. Add user | |
| DATA_CACHE.setdefault('users', {})[username] = generate_password_hash(password) | |
| # 3. Save to DB | |
| success = save_db(DATA_CACHE) | |
| if success: | |
| session['user'] = username | |
| flash("Account created successfully!") | |
| else: | |
| # Revert change if save failed | |
| del DATA_CACHE['users'][username] | |
| return redirect(url_for('index')) | |
| def login(): | |
| username = request.form.get('username') | |
| password = request.form.get('password') | |
| users = DATA_CACHE.get('users', {}) | |
| if username in users and check_password_hash(users[username], password): | |
| session['user'] = username | |
| flash("Logged in!") | |
| else: | |
| # Debug helper: Print what we checked against | |
| print(f"Login Failed for {username}. Available users: {list(users.keys())}") | |
| flash("Invalid username or password") | |
| return redirect(url_for('index')) | |
| def logout(): | |
| session.pop('user', None) | |
| flash("Logged out") | |
| return redirect(url_for('index')) | |
| def add_pin(): | |
| if 'user' not in session: return redirect(url_for('index')) | |
| img_url = request.form.get('img_url') | |
| caption = request.form.get('caption') | |
| if not img_url: | |
| flash("Image URL is required") | |
| return redirect(url_for('index')) | |
| new_pin = { | |
| "id": int(time.time()), | |
| "url": img_url, | |
| "caption": caption, | |
| "author": session['user'] | |
| } | |
| DATA_CACHE.setdefault('pins', []).insert(0, new_pin) | |
| save_db(DATA_CACHE) | |
| flash("Pin added!") | |
| return redirect(url_for('index')) | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=7860) | |