from flask import Flask, render_template, request, jsonify, session, redirect, url_for from pymongo import MongoClient from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime, timedelta import os from functools import wraps import uuid import base64 import random from dotenv import load_dotenv load_dotenv() app = Flask(__name__) app.secret_key = os.urandom(24) # Configure session to be permanent and last for 30 days app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=30) app.config['SESSION_COOKIE_SECURE'] = False # Set to True in production with HTTPS app.config['SESSION_COOKIE_HTTPONLY'] = True app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # MongoDB connection MONGO_URI = os.getenv('MONGO_URI') client = MongoClient(MONGO_URI) db = client.startpage # Collections users = db.users user_bookmarks = db.bookmarks user_notes = db.notes site_stats = db.site_stats vault_passwords = db.vault_passwords def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): if 'user_id' not in session: return jsonify({'error': 'Authentication required'}), 401 return f(*args, **kwargs) return decorated_function @app.route('/') def index(): # Track page visit today = datetime.now().strftime('%Y-%m-%d') site_stats.update_one( {'date': today}, {'$inc': {'page_visits': 1}}, upsert=True ) # Check if user is logged in if 'user_id' not in session: return render_template('auth.html') return render_template('index.html', username=session.get('username', 'User')) @app.route('/register', methods=['POST']) def register(): data = request.json username = data.get('username') password = data.get('password') if not username or not password: return jsonify({'error': 'Username and password required'}), 400 if users.find_one({'username': username}): return jsonify({'error': 'Username already exists'}), 400 user_id = str(uuid.uuid4()) hashed_password = generate_password_hash(password) users.insert_one({ 'user_id': user_id, 'username': username, 'password': hashed_password, 'created_at': datetime.now() }) # Add default bookmarks for new user default_bookmarks = [ { 'name': 'YouTube', 'url': 'https://youtube.com', 'icon': 'play_circle', 'user_id': user_id, 'created_at': datetime.now() }, { 'name': 'GitHub', 'url': 'https://github.com', 'icon': 'code', 'user_id': user_id, 'created_at': datetime.now() }, { 'name': 'Gmail', 'url': 'https://gmail.com', 'icon': 'mail', 'user_id': user_id, 'created_at': datetime.now() }, { 'name': 'Google Drive', 'url': 'https://drive.google.com', 'icon': 'cloud', 'user_id': user_id, 'created_at': datetime.now() }, { 'name': 'Netflix', 'url': 'https://netflix.com', 'icon': 'movie', 'user_id': user_id, 'created_at': datetime.now() }, { 'name': 'Reddit', 'url': 'https://reddit.com', 'icon': 'forum', 'user_id': user_id, 'created_at': datetime.now() }, { 'name': 'Twitter', 'url': 'https://twitter.com', 'icon': 'alternate_email', 'user_id': user_id, 'created_at': datetime.now() }, { 'name': 'LinkedIn', 'url': 'https://linkedin.com', 'icon': 'work', 'user_id': user_id, 'created_at': datetime.now() } ] user_bookmarks.insert_many(default_bookmarks) # Make session permanent so it persists across browser sessions session.permanent = True session['user_id'] = user_id session['username'] = username return jsonify({'message': 'Registration successful'}) @app.route('/login', methods=['POST']) def login(): data = request.json username = data.get('username') password = data.get('password') remember_me = data.get('rememberMe', True) # Default to True for backward compatibility if not username or not password: return jsonify({'error': 'Username and password required'}), 400 user = users.find_one({'username': username}) if not user or not check_password_hash(user['password'], password): return jsonify({'error': 'Invalid credentials'}), 401 # Set session permanence based on user choice session.permanent = remember_me session['user_id'] = user['user_id'] session['username'] = username session['remember_me'] = remember_me return jsonify({'message': 'Login successful'}) @app.route('/logout', methods=['POST']) def logout(): session.clear() return jsonify({'message': 'Logout successful'}) @app.route('/api/bookmarks', methods=['GET']) @login_required def get_bookmarks(): user_id = session['user_id'] bookmarks = list(user_bookmarks.find({'user_id': user_id}, {'_id': 0})) return jsonify(bookmarks) @app.route('/api/bookmarks', methods=['POST']) @login_required def save_bookmarks(): user_id = session['user_id'] data = request.json user_bookmarks.delete_many({'user_id': user_id}) for bookmark in data.get('bookmarks', []): bookmark['user_id'] = user_id bookmark['created_at'] = datetime.now() user_bookmarks.insert_one(bookmark) return jsonify({'message': 'Bookmarks saved'}) @app.route('/api/notes', methods=['GET']) @login_required def get_notes(): user_id = session['user_id'] notes = list(user_notes.find({'user_id': user_id}, {'_id': 0})) return jsonify(notes) @app.route('/api/notes', methods=['POST']) @login_required def save_notes(): user_id = session['user_id'] data = request.json user_notes.delete_many({'user_id': user_id}) for note in data.get('notes', []): note['user_id'] = user_id note['updated_at'] = datetime.now() user_notes.insert_one(note) return jsonify({'message': 'Notes saved'}) @app.route('/api/search', methods=['POST']) @login_required def track_search(): today = datetime.now().strftime('%Y-%m-%d') site_stats.update_one( {'date': today}, {'$inc': {'search_count': 1}}, upsert=True ) return jsonify({'message': 'Search tracked'}) @app.route('/api/stats', methods=['GET']) def get_stats(): today = datetime.now().strftime('%Y-%m-%d') stats = site_stats.find_one({'date': today}) if not stats: return jsonify({'page_visits': 0, 'search_count': 0}) return jsonify({ 'page_visits': stats.get('page_visits', 0), 'search_count': stats.get('search_count', 0) }) @app.route('/api/change-password', methods=['POST']) @login_required def change_password(): data = request.json current_password = data.get('currentPassword') new_password = data.get('newPassword') if not current_password or not new_password: return jsonify({'error': 'Current password and new password required'}), 400 user_id = session['user_id'] user = users.find_one({'user_id': user_id}) if not user or not check_password_hash(user['password'], current_password): return jsonify({'error': 'Current password is incorrect'}), 400 if len(new_password) < 6: return jsonify({'error': 'Password must be at least 6 characters long'}), 400 # Update password hashed_password = generate_password_hash(new_password) users.update_one( {'user_id': user_id}, {'$set': {'password': hashed_password, 'updated_at': datetime.now()}} ) return jsonify({'message': 'Password changed successfully'}) @app.route('/api/developer-stats', methods=['GET']) @login_required def get_developer_stats(): # Total users count total_users = users.count_documents({}) # Total visits and searches across all time all_stats = list(site_stats.find({})) total_visits = sum(stat.get('page_visits', 0) for stat in all_stats) total_searches = sum(stat.get('search_count', 0) for stat in all_stats) # Monthly data (last 12 months) monthly_data = [] current_date = datetime.now() for i in range(12): # Calculate the first day of each month target_date = datetime(current_date.year, current_date.month - i, 1) if current_date.month - i > 0 else datetime(current_date.year - 1, current_date.month - i + 12, 1) # Find all stats for that month start_of_month = target_date if target_date.month == 12: end_of_month = datetime(target_date.year + 1, 1, 1) else: end_of_month = datetime(target_date.year, target_date.month + 1, 1) # Query stats for the month month_stats = site_stats.aggregate([ { '$match': { 'date': { '$gte': start_of_month.strftime('%Y-%m-%d'), '$lt': end_of_month.strftime('%Y-%m-%d') } } }, { '$group': { '_id': None, 'total_visits': {'$sum': '$page_visits'}, 'total_searches': {'$sum': '$search_count'} } } ]) month_result = list(month_stats) month_name = target_date.strftime('%b') if month_result: monthly_data.append({ 'month': month_name, 'visits': month_result[0]['total_visits'], 'searches': month_result[0]['total_searches'] }) else: monthly_data.append({ 'month': month_name, 'visits': 0, 'searches': 0 }) # Reverse to get chronological order monthly_data.reverse() return jsonify({ 'totalUsers': total_users, 'totalVisits': total_visits, 'totalSearches': total_searches, 'monthlyData': monthly_data }) # Vault API endpoints @app.route('/api/vault/authenticate', methods=['POST']) @login_required def authenticate_vault(): data = request.json password = data.get('password') if not password: return jsonify({'error': 'Password required'}), 400 user_id = session['user_id'] user = users.find_one({'user_id': user_id}) if not user or not check_password_hash(user['password'], password): return jsonify({'error': 'Invalid password'}), 401 return jsonify({'message': 'Authentication successful'}) @app.route('/api/vault/passwords', methods=['GET']) @login_required def get_vault_passwords(): user_id = session['user_id'] passwords = list(vault_passwords.find({'user_id': user_id}, {'_id': 0})) # Decode passwords for display (simple base64 encoding for demo) for password in passwords: try: password['password'] = base64.b64decode(password['password'].encode()).decode() except: pass # Keep original if decoding fails return jsonify(passwords) @app.route('/api/vault/passwords', methods=['POST']) @login_required def save_vault_password(): user_id = session['user_id'] data = request.json # Validate required fields if not data.get('title') or not data.get('password'): return jsonify({'error': 'Title and password are required'}), 400 # Simple base64 encoding for password storage (demo purposes) encoded_password = base64.b64encode(data['password'].encode()).decode() password_entry = { 'user_id': user_id, 'title': data.get('title'), 'username': data.get('username', ''), 'password': encoded_password, 'website': data.get('website', ''), 'notes': data.get('notes', ''), 'created_at': datetime.now(), 'updated_at': datetime.now() } # Check if updating existing entry if data.get('id'): password_entry['id'] = data['id'] vault_passwords.update_one( {'user_id': user_id, 'id': data['id']}, {'$set': password_entry} ) else: password_entry['id'] = str(uuid.uuid4()) vault_passwords.insert_one(password_entry) return jsonify({'message': 'Password saved successfully'}) @app.route('/api/vault/passwords/', methods=['DELETE']) @login_required def delete_vault_password(password_id): user_id = session['user_id'] result = vault_passwords.delete_one({ 'user_id': user_id, 'id': password_id }) if result.deleted_count > 0: return jsonify({'message': 'Password deleted successfully'}) else: return jsonify({'error': 'Password not found'}), 404 if __name__ == '__main__': app.run(debug=True, port=7860)