Megararedo / app.py
Shinhati2023's picture
Update app.py
22a1b7e verified
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 ---
@app.route('/')
def index():
return render_template('index.html', pins=DATA_CACHE.get('pins', []), user=session.get('user'))
@app.route('/signup', methods=['POST'])
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'))
@app.route('/login', methods=['POST'])
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'))
@app.route('/logout')
def logout():
session.pop('user', None)
flash("Logged out")
return redirect(url_for('index'))
@app.route('/add_pin', methods=['POST'])
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)