File size: 6,893 Bytes
8d54fe3
 
 
83e9a1d
8d54fe3
 
83e9a1d
8d54fe3
 
83e9a1d
 
 
 
8d54fe3
 
e6094bb
8d54fe3
22a1b7e
 
 
 
 
 
 
 
 
8d54fe3
83e9a1d
22a1b7e
 
 
 
 
 
83e9a1d
22a1b7e
 
 
 
 
 
8d54fe3
 
83e9a1d
e6094bb
 
 
 
 
 
 
 
cfa6ae4
83e9a1d
 
 
 
7874b25
e6094bb
 
 
cfa6ae4
83e9a1d
cfa6ae4
83e9a1d
cfa6ae4
e6094bb
83e9a1d
 
 
22a1b7e
e6094bb
83e9a1d
22a1b7e
 
 
83e9a1d
 
e6094bb
83e9a1d
e6094bb
8d54fe3
 
83e9a1d
 
 
e6094bb
 
 
 
8d54fe3
 
83e9a1d
 
 
 
 
22a1b7e
 
 
 
 
 
 
 
 
 
 
 
 
 
83e9a1d
8d54fe3
e6094bb
 
 
8d54fe3
e6094bb
8d54fe3
 
e6094bb
8d54fe3
 
 
 
 
 
83e9a1d
 
 
 
 
 
e6094bb
 
 
 
8d54fe3
e6094bb
 
 
 
 
 
 
 
 
 
83e9a1d
e6094bb
 
8d54fe3
 
 
 
83e9a1d
 
e6094bb
 
 
8d54fe3
e6094bb
8d54fe3
22a1b7e
 
e6094bb
 
8d54fe3
 
 
 
 
e6094bb
8d54fe3
 
 
 
 
e6094bb
83e9a1d
 
e6094bb
 
 
 
 
 
 
 
 
 
 
 
 
8d54fe3
e6094bb
8d54fe3
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
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)