Other files
This commit is contained in:
@@ -1139,3 +1139,155 @@ def get_user_stores(user_id):
|
||||
'location': {'zip_code': user['zip_code'], 'radius_miles': user.get('radius_miles', 25)},
|
||||
'stores': result
|
||||
})
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Checkout Profile Routes
|
||||
# =============================================================================
|
||||
|
||||
@api_bp.route('/users/<int:user_id>/profile/data', methods=['GET'])
|
||||
def get_profile_data(user_id):
|
||||
"""
|
||||
Return non-sensitive profile fields for pre-filling the edit form.
|
||||
Only works if the profile is already unlocked (in memory).
|
||||
Card number is returned masked. Passwords are never returned.
|
||||
"""
|
||||
from src.profile_store import get_unlocked_profile, masked_card
|
||||
db = get_database()
|
||||
if not db.get_user(user_id):
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
profile = get_unlocked_profile(user_id)
|
||||
if not profile:
|
||||
return jsonify({'error': 'Profile not unlocked'}), 403
|
||||
|
||||
shipping = profile.get('shipping', {})
|
||||
creds = profile.get('site_credentials', {})
|
||||
card = masked_card(user_id)
|
||||
|
||||
return jsonify({
|
||||
'shipping': shipping,
|
||||
'masked_card': card,
|
||||
'site_credentials': {
|
||||
'target_email': creds.get('target_email', ''),
|
||||
'bestbuy_email': creds.get('bestbuy_email', ''),
|
||||
'gamestop_email': creds.get('gamestop_email', ''),
|
||||
# passwords intentionally omitted
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@api_bp.route('/users/<int:user_id>/profile', methods=['GET'])
|
||||
def get_profile_status(user_id):
|
||||
"""
|
||||
Return profile metadata — never returns card data or decrypted fields.
|
||||
Frontend uses this to show lock/unlock state and masked card info.
|
||||
"""
|
||||
from src.profile_store import is_unlocked, masked_card
|
||||
db = get_database()
|
||||
if not db.get_user(user_id):
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
has_profile = db.user_has_profile(user_id)
|
||||
unlocked = is_unlocked(user_id)
|
||||
|
||||
return jsonify({
|
||||
'has_profile': has_profile,
|
||||
'is_unlocked': unlocked,
|
||||
'masked_card': masked_card(user_id) if unlocked else None,
|
||||
})
|
||||
|
||||
|
||||
@api_bp.route('/users/<int:user_id>/profile', methods=['POST'])
|
||||
def save_profile(user_id):
|
||||
"""
|
||||
Save (or replace) an encrypted checkout profile.
|
||||
Expects JSON: { password, shipping: {...}, payment: {...} }
|
||||
Card data is encrypted immediately — never stored in plaintext.
|
||||
"""
|
||||
from src.profile_store import save_profile as _save
|
||||
db = get_database()
|
||||
if not db.get_user(user_id):
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'error': 'No data provided'}), 400
|
||||
|
||||
password = data.get('password', '').strip()
|
||||
if len(password) < 8:
|
||||
return jsonify({'error': 'Password must be at least 8 characters'}), 400
|
||||
|
||||
shipping = data.get('shipping', {})
|
||||
payment = data.get('payment', {})
|
||||
site_credentials = data.get('site_credentials', {})
|
||||
|
||||
required_shipping = ['first_name', 'last_name', 'address1', 'city', 'state', 'zip', 'email']
|
||||
missing = [f for f in required_shipping if not shipping.get(f)]
|
||||
if missing:
|
||||
return jsonify({'error': f'Missing shipping fields: {", ".join(missing)}'}), 400
|
||||
|
||||
# If card fields are blank and a profile already exists, keep the existing card data
|
||||
if not payment.get('card_number') and db.user_has_profile(user_id):
|
||||
from src.profile_store import get_unlocked_profile
|
||||
existing = get_unlocked_profile(user_id)
|
||||
if existing:
|
||||
for k in ['card_number', 'expiry_month', 'expiry_year', 'cvv', 'name_on_card']:
|
||||
if not payment.get(k):
|
||||
payment[k] = existing.get('payment', {}).get(k, '')
|
||||
|
||||
required_payment = ['card_number', 'expiry_month', 'expiry_year', 'cvv']
|
||||
missing = [f for f in required_payment if not payment.get(f)]
|
||||
if missing:
|
||||
return jsonify({'error': f'Missing payment fields: {", ".join(missing)}'}), 400
|
||||
|
||||
try:
|
||||
ok = _save(user_id, password, shipping, payment, db, site_credentials=site_credentials)
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
if not ok:
|
||||
return jsonify({'error': 'Encryption failed — is the cryptography package installed? Run: pip install cryptography'}), 500
|
||||
|
||||
return jsonify({'success': True, 'is_unlocked': True})
|
||||
|
||||
|
||||
@api_bp.route('/users/<int:user_id>/profile/unlock', methods=['POST'])
|
||||
def unlock_profile(user_id):
|
||||
"""Decrypt profile into memory. Expects JSON: { password }"""
|
||||
from src.profile_store import unlock_profile as _unlock
|
||||
db = get_database()
|
||||
if not db.get_user(user_id):
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
|
||||
if not db.user_has_profile(user_id):
|
||||
return jsonify({'error': 'No profile saved for this user'}), 404
|
||||
|
||||
data = request.get_json()
|
||||
password = (data or {}).get('password', '')
|
||||
|
||||
if not _unlock(user_id, password, db):
|
||||
return jsonify({'error': 'Incorrect password'}), 401
|
||||
|
||||
from src.profile_store import masked_card
|
||||
return jsonify({'success': True, 'masked_card': masked_card(user_id)})
|
||||
|
||||
|
||||
@api_bp.route('/users/<int:user_id>/profile/lock', methods=['POST'])
|
||||
def lock_profile(user_id):
|
||||
"""Clear profile from memory."""
|
||||
from src.profile_store import lock_profile as _lock
|
||||
_lock(user_id)
|
||||
return jsonify({'success': True})
|
||||
|
||||
|
||||
@api_bp.route('/users/<int:user_id>/profile', methods=['DELETE'])
|
||||
def delete_profile(user_id):
|
||||
"""Permanently delete a user's saved profile."""
|
||||
from src.profile_store import lock_profile as _lock
|
||||
db = get_database()
|
||||
if not db.get_user(user_id):
|
||||
return jsonify({'error': 'User not found'}), 404
|
||||
_lock(user_id)
|
||||
db.delete_user_profile(user_id)
|
||||
return jsonify({'success': True})
|
||||
|
||||
Reference in New Issue
Block a user