Update main.py
Browse files
main.py
CHANGED
|
@@ -66,31 +66,54 @@ def verify_admin_and_get_uid(auth_header):
|
|
| 66 |
|
| 67 |
@app.route('/api/auth/signup', methods=['POST'])
|
| 68 |
def signup():
|
| 69 |
-
"""Handles new user sign-up."""
|
| 70 |
try:
|
| 71 |
data = request.get_json()
|
| 72 |
email, password, display_name = data.get('email'), data.get('password'), data.get('displayName')
|
| 73 |
|
| 74 |
-
if not
|
| 75 |
return jsonify({'error': 'Email, password, and display name are required'}), 400
|
| 76 |
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
'
|
| 82 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
}
|
| 84 |
-
db.collection('users').document(user.uid).set(
|
| 85 |
|
| 86 |
logging.info(f"New user signed up: {user.uid}, Name: {display_name}")
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
except Exception as e:
|
| 90 |
-
logging.error(f"Signup failed: {e}")
|
| 91 |
if 'EMAIL_EXISTS' in str(e):
|
| 92 |
return jsonify({'error': 'An account with this email already exists.'}), 409
|
| 93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
@app.route('/api/auth/social-signin', methods=['POST'])
|
| 96 |
def social_signin():
|
|
@@ -102,22 +125,34 @@ def social_signin():
|
|
| 102 |
user_doc = user_ref.get()
|
| 103 |
|
| 104 |
if user_doc.exists:
|
|
|
|
| 105 |
return jsonify({'uid': uid, **user_doc.to_dict()}), 200
|
| 106 |
else:
|
|
|
|
| 107 |
logging.info(f"New social user detected: {uid}. Creating database profile.")
|
| 108 |
try:
|
| 109 |
firebase_user = auth.get_user(uid)
|
| 110 |
-
|
|
|
|
|
|
|
| 111 |
'uid': uid, 'email': firebase_user.email, 'displayName': firebase_user.display_name,
|
| 112 |
'isAdmin': False, 'phone': None, 'phoneStatus': 'unsubmitted', 'organizationId': None,
|
| 113 |
-
'createdAt': firestore.SERVER_TIMESTAMP
|
| 114 |
}
|
| 115 |
-
user_ref.set(
|
| 116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
except Exception as e:
|
| 118 |
logging.error(f"Error creating profile for new social user {uid}: {e}")
|
| 119 |
return jsonify({'error': f'Failed to create user profile: {str(e)}'}), 500
|
| 120 |
-
|
| 121 |
# -----------------------------------------------------------------------------
|
| 122 |
# 4. LOGGED-IN USER ENDPOINTS
|
| 123 |
# -----------------------------------------------------------------------------
|
|
@@ -301,17 +336,25 @@ def create_organization():
|
|
| 301 |
|
| 302 |
try:
|
| 303 |
org_ref = db.collection('organizations').document()
|
| 304 |
-
|
|
|
|
|
|
|
| 305 |
'id': org_ref.id, 'name': org_name, 'ownerUid': uid,
|
| 306 |
-
'members': [uid], 'createdAt': firestore.SERVER_TIMESTAMP
|
| 307 |
}
|
| 308 |
|
| 309 |
batch = db.batch()
|
| 310 |
-
batch.set(org_ref,
|
| 311 |
batch.update(db.collection('users').document(uid), {'organizationId': org_ref.id})
|
| 312 |
batch.commit()
|
| 313 |
|
| 314 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
except Exception as e:
|
| 316 |
logging.error(f"User {uid} failed to create organization: {e}")
|
| 317 |
return jsonify({'error': 'An internal error occurred'}), 500
|
|
|
|
| 66 |
|
| 67 |
@app.route('/api/auth/signup', methods=['POST'])
|
| 68 |
def signup():
|
| 69 |
+
"""Handles new user sign-up with email/password and creates their Firestore profile."""
|
| 70 |
try:
|
| 71 |
data = request.get_json()
|
| 72 |
email, password, display_name = data.get('email'), data.get('password'), data.get('displayName')
|
| 73 |
|
| 74 |
+
if not email or not password or not display_name:
|
| 75 |
return jsonify({'error': 'Email, password, and display name are required'}), 400
|
| 76 |
|
| 77 |
+
# Step 1: Create the user in Firebase Authentication
|
| 78 |
+
user = auth.create_user(
|
| 79 |
+
email=email,
|
| 80 |
+
password=password,
|
| 81 |
+
display_name=display_name
|
| 82 |
+
)
|
| 83 |
|
| 84 |
+
# Step 2: Create the data dictionary FOR THE DATABASE, using the special marker
|
| 85 |
+
user_data_for_db = {
|
| 86 |
+
'uid': user.uid,
|
| 87 |
+
'email': email,
|
| 88 |
+
'displayName': display_name,
|
| 89 |
+
'isAdmin': False,
|
| 90 |
+
'phone': None,
|
| 91 |
+
'phoneStatus': 'unsubmitted',
|
| 92 |
+
'organizationId': None,
|
| 93 |
+
'createdAt': firestore.SERVER_TIMESTAMP # This is for Firestore
|
| 94 |
}
|
| 95 |
+
db.collection('users').document(user.uid).set(user_data_for_db)
|
| 96 |
|
| 97 |
logging.info(f"New user signed up: {user.uid}, Name: {display_name}")
|
| 98 |
+
|
| 99 |
+
# --- THE FIX IS HERE ---
|
| 100 |
+
# Step 3: Create a SEPARATE dictionary for the JSON response
|
| 101 |
+
# Replace the special marker with a JSON-friendly ISO date string.
|
| 102 |
+
response_data = user_data_for_db.copy()
|
| 103 |
+
response_data['createdAt'] = datetime.utcnow().isoformat() + "Z" # This is for the client
|
| 104 |
+
|
| 105 |
+
return jsonify({'success': True, **response_data}), 201
|
| 106 |
|
| 107 |
except Exception as e:
|
| 108 |
+
logging.error(f"Signup failed: {e}", exc_info=True) # exc_info=True gives more detail
|
| 109 |
if 'EMAIL_EXISTS' in str(e):
|
| 110 |
return jsonify({'error': 'An account with this email already exists.'}), 409
|
| 111 |
+
# Check for the specific error text in the exception itself
|
| 112 |
+
if 'Object of type Sentinel is not JSON serializable' in str(e):
|
| 113 |
+
# This means the DB write likely succeeded but the return failed.
|
| 114 |
+
# In this specific case, we can arguably return success.
|
| 115 |
+
return jsonify({'success': True, 'uid': data.get('uid'), 'message': 'Account created, but response generation had a minor issue.'}), 201
|
| 116 |
+
return jsonify({'error': 'An internal server error occurred.'}), 500
|
| 117 |
|
| 118 |
@app.route('/api/auth/social-signin', methods=['POST'])
|
| 119 |
def social_signin():
|
|
|
|
| 125 |
user_doc = user_ref.get()
|
| 126 |
|
| 127 |
if user_doc.exists:
|
| 128 |
+
# User already exists, this is safe to return.
|
| 129 |
return jsonify({'uid': uid, **user_doc.to_dict()}), 200
|
| 130 |
else:
|
| 131 |
+
# This is a new user (first social login), create their full profile in Firestore.
|
| 132 |
logging.info(f"New social user detected: {uid}. Creating database profile.")
|
| 133 |
try:
|
| 134 |
firebase_user = auth.get_user(uid)
|
| 135 |
+
|
| 136 |
+
# Data for the database, with the special marker
|
| 137 |
+
new_user_data_for_db = {
|
| 138 |
'uid': uid, 'email': firebase_user.email, 'displayName': firebase_user.display_name,
|
| 139 |
'isAdmin': False, 'phone': None, 'phoneStatus': 'unsubmitted', 'organizationId': None,
|
| 140 |
+
'createdAt': firestore.SERVER_TIMESTAMP # For Firestore
|
| 141 |
}
|
| 142 |
+
user_ref.set(new_user_data_for_db)
|
| 143 |
+
|
| 144 |
+
logging.info(f"Successfully created profile for new social user: {uid}")
|
| 145 |
+
|
| 146 |
+
# --- THE FIX IS HERE ---
|
| 147 |
+
# Create a clean copy for the JSON response
|
| 148 |
+
response_data = new_user_data_for_db.copy()
|
| 149 |
+
response_data['createdAt'] = datetime.utcnow().isoformat() + "Z" # For the client
|
| 150 |
+
|
| 151 |
+
return jsonify({'success': True, **response_data}), 201
|
| 152 |
+
|
| 153 |
except Exception as e:
|
| 154 |
logging.error(f"Error creating profile for new social user {uid}: {e}")
|
| 155 |
return jsonify({'error': f'Failed to create user profile: {str(e)}'}), 500
|
|
|
|
| 156 |
# -----------------------------------------------------------------------------
|
| 157 |
# 4. LOGGED-IN USER ENDPOINTS
|
| 158 |
# -----------------------------------------------------------------------------
|
|
|
|
| 336 |
|
| 337 |
try:
|
| 338 |
org_ref = db.collection('organizations').document()
|
| 339 |
+
|
| 340 |
+
# Data for the database, with the special marker
|
| 341 |
+
org_data_for_db = {
|
| 342 |
'id': org_ref.id, 'name': org_name, 'ownerUid': uid,
|
| 343 |
+
'members': [uid], 'createdAt': firestore.SERVER_TIMESTAMP # For Firestore
|
| 344 |
}
|
| 345 |
|
| 346 |
batch = db.batch()
|
| 347 |
+
batch.set(org_ref, org_data_for_db)
|
| 348 |
batch.update(db.collection('users').document(uid), {'organizationId': org_ref.id})
|
| 349 |
batch.commit()
|
| 350 |
|
| 351 |
+
# --- THE FIX IS HERE ---
|
| 352 |
+
# Create a clean copy for the JSON response
|
| 353 |
+
response_data = org_data_for_db.copy()
|
| 354 |
+
response_data['createdAt'] = datetime.utcnow().isoformat() + "Z" # For the client
|
| 355 |
+
|
| 356 |
+
return jsonify(response_data), 201
|
| 357 |
+
|
| 358 |
except Exception as e:
|
| 359 |
logging.error(f"User {uid} failed to create organization: {e}")
|
| 360 |
return jsonify({'error': 'An internal error occurred'}), 500
|