Hamza4100 commited on
Commit
c6eda4d
·
verified ·
1 Parent(s): d255786

Update user_management.py

Browse files
Files changed (1) hide show
  1. user_management.py +348 -326
user_management.py CHANGED
@@ -1,326 +1,348 @@
1
- """
2
- User Management Module
3
- ======================
4
- Handles user database storage in Hugging Face repository instead of local JSON.
5
- Stores users_db.json in: Hamza4100/multi-pdf-storage/users_db.json
6
- """
7
-
8
- import os
9
- import json
10
- import hashlib
11
- import tempfile
12
- from typing import Optional, Dict, Any
13
- from datetime import datetime
14
- from huggingface_hub import HfApi, hf_hub_download, login
15
-
16
-
17
- class HFUserManager:
18
- """Manages user database stored in Hugging Face repository."""
19
-
20
- def __init__(self, hf_token: Optional[str], hf_repo: str):
21
- """
22
- Initialize HF User Manager.
23
-
24
- Args:
25
- hf_token: Hugging Face API token with write access
26
- hf_repo: HF repository ID (e.g., "Hamza4100/multi-pdf-storage")
27
- """
28
- self.hf_token = hf_token
29
- self.hf_repo = hf_repo
30
- self.enabled = bool(hf_token and hf_repo)
31
- self.api = None
32
- self.users_db_file = "users_db.json" # File stored at repo root
33
- self.users_data: Dict[str, Any] = {}
34
-
35
- if self.enabled:
36
- try:
37
- login(token=hf_token, add_to_git_credential=True)
38
- self.api = HfApi()
39
- print(f"✅ HF User Manager initialized: {hf_repo}")
40
- # Load users database on init
41
- self._load_users_from_hf()
42
- except Exception as e:
43
- print(f"⚠️ HF User Manager initialization failed: {e}")
44
- self.enabled = False
45
- else:
46
- print("⚠️ HF User Manager disabled (HF_TOKEN or HF_REPO not set)")
47
-
48
- def _load_users_from_hf(self) -> bool:
49
- """
50
- Load users_db.json from HF repository.
51
-
52
- Returns:
53
- bool: True if loaded successfully, False otherwise
54
- """
55
- if not self.enabled:
56
- return False
57
-
58
- try:
59
- print(f"📥 Loading users database from {self.hf_repo}/{self.users_db_file}")
60
-
61
- # Download users_db.json from HF repo
62
- downloaded_path = hf_hub_download(
63
- repo_id=self.hf_repo,
64
- filename=self.users_db_file,
65
- token=self.hf_token,
66
- repo_type="model",
67
- local_dir_use_symlinks=False
68
- )
69
-
70
- # Read the file
71
- with open(downloaded_path, 'r') as f:
72
- self.users_data = json.load(f)
73
-
74
- print(f"✅ Loaded {len(self.users_data)} user(s) from HF repo")
75
- return True
76
-
77
- except Exception as e:
78
- # File might not exist yet (first run is okay)
79
- print(f"⚠️ Could not load users database from HF: {str(e)[:100]}")
80
- print(" Starting with empty database (will be created on first signup)")
81
- self.users_data = {}
82
- return False
83
-
84
- def _save_users_to_hf(self, commit_message: str = "Update users database") -> bool:
85
- """
86
- Save users_db.json to HF repository.
87
-
88
- Args:
89
- commit_message: Commit message for the upload
90
-
91
- Returns:
92
- bool: True if saved successfully, False otherwise
93
- """
94
- if not self.enabled:
95
- print("⚠️ HF User Manager disabled, cannot save to HF")
96
- return False
97
-
98
- try:
99
- print(f"📤 Saving users database to {self.hf_repo}/{self.users_db_file}")
100
-
101
- # Create a temporary file with the users data
102
- with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as tmp:
103
- json.dump(self.users_data, tmp, indent=2)
104
- tmp_path = tmp.name
105
-
106
- try:
107
- # Upload to HF repo
108
- self.api.upload_file(
109
- path_or_fileobj=tmp_path,
110
- path_in_repo=self.users_db_file,
111
- repo_id=self.hf_repo,
112
- token=self.hf_token,
113
- repo_type="model",
114
- commit_message=commit_message
115
- )
116
-
117
- print(f"✅ Users database saved to HF repo")
118
- return True
119
-
120
- finally:
121
- # Clean up temporary file
122
- if os.path.exists(tmp_path):
123
- os.remove(tmp_path)
124
-
125
- except Exception as e:
126
- print(f"❌ Failed to save users database to HF: {e}")
127
- return False
128
-
129
- def get_user(self, username: str) -> Optional[Dict[str, Any]]:
130
- """
131
- Get user by username.
132
-
133
- Args:
134
- username: Username to retrieve
135
-
136
- Returns:
137
- User data dict or None if not found
138
- """
139
- return self.users_data.get(username)
140
-
141
- def user_exists(self, username: str) -> bool:
142
- """
143
- Check if user exists.
144
-
145
- Args:
146
- username: Username to check
147
-
148
- Returns:
149
- True if user exists, False otherwise
150
- """
151
- return username in self.users_data
152
-
153
- def create_user(
154
- self,
155
- username: str,
156
- password_hash: str,
157
- email: str,
158
- api_key: str
159
- ) -> bool:
160
- """
161
- Create a new user account.
162
-
163
- Args:
164
- username: Username
165
- password_hash: Hashed password
166
- email: User email
167
- api_key: API key for this user
168
-
169
- Returns:
170
- bool: True if user created, False if user already exists
171
- """
172
- if self.user_exists(username):
173
- return False
174
-
175
- self.users_data[username] = {
176
- "password_hash": password_hash,
177
- "email": email,
178
- "api_key": api_key,
179
- "created_at": datetime.now().isoformat()
180
- }
181
-
182
- # Save to HF
183
- success = self._save_users_to_hf(
184
- commit_message=f"Add new user: {username}"
185
- )
186
-
187
- if success:
188
- print(f"✅ User '{username}' created and saved to HF")
189
-
190
- return success
191
-
192
- def update_user(self, username: str, updates: Dict[str, Any]) -> bool:
193
- """
194
- Update user data.
195
-
196
- Args:
197
- username: Username to update
198
- updates: Dictionary with updates
199
-
200
- Returns:
201
- bool: True if updated successfully, False if user not found
202
- """
203
- if not self.user_exists(username):
204
- return False
205
-
206
- self.users_data[username].update(updates)
207
-
208
- # Save to HF
209
- success = self._save_users_to_hf(
210
- commit_message=f"Update user: {username}"
211
- )
212
-
213
- if success:
214
- print(f"✅ User '{username}' updated in HF")
215
-
216
- return success
217
-
218
- def delete_user(self, username: str) -> bool:
219
- """
220
- Delete a user account.
221
-
222
- Args:
223
- username: Username to delete
224
-
225
- Returns:
226
- bool: True if deleted successfully, False if user not found
227
- """
228
- if not self.user_exists(username):
229
- return False
230
-
231
- del self.users_data[username]
232
-
233
- # Save to HF
234
- success = self._save_users_to_hf(
235
- commit_message=f"Delete user: {username}"
236
- )
237
-
238
- if success:
239
- print(f"✅ User '{username}' deleted from HF")
240
-
241
- return success
242
-
243
- def get_all_users(self) -> Dict[str, Any]:
244
- """
245
- Get all users data.
246
-
247
- Returns:
248
- Dictionary of all users
249
- """
250
- return self.users_data.copy()
251
-
252
- def get_user_by_api_key(self, api_key: str) -> Optional[tuple]:
253
- """
254
- Find user by API key.
255
-
256
- Args:
257
- api_key: API key to search for
258
-
259
- Returns:
260
- Tuple of (username, user_data) or None if not found
261
- """
262
- for username, user_data in self.users_data.items():
263
- if user_data.get("api_key") == api_key:
264
- return (username, user_data)
265
-
266
- return None
267
-
268
- def get_user_by_email(self, email: str) -> Optional[tuple]:
269
- """
270
- Find user by email.
271
-
272
- Args:
273
- email: Email to search for
274
-
275
- Returns:
276
- Tuple of (username, user_data) or None if not found
277
- """
278
- for username, user_data in self.users_data.items():
279
- if user_data.get("email") == email:
280
- return (username, user_data)
281
-
282
- return None
283
-
284
- def verify_password(self, username: str, password_hash: str) -> bool:
285
- """
286
- Verify user password hash.
287
-
288
- Args:
289
- username: Username
290
- password_hash: Password hash to verify
291
-
292
- Returns:
293
- bool: True if password matches, False otherwise
294
- """
295
- user = self.get_user(username)
296
- if not user:
297
- return False
298
-
299
- return user.get("password_hash") == password_hash
300
-
301
-
302
- # ============================================
303
- # CONVENIENCE FUNCTIONS
304
- # ============================================
305
-
306
- def create_hf_user_manager(
307
- hf_token: Optional[str] = None,
308
- hf_repo: Optional[str] = None
309
- ) -> HFUserManager:
310
- """
311
- Create and return an HF User Manager instance.
312
-
313
- Args:
314
- hf_token: HF token (reads from env if not provided)
315
- hf_repo: HF repo ID (reads from env if not provided)
316
-
317
- Returns:
318
- HFUserManager instance
319
- """
320
- if hf_token is None:
321
- hf_token = os.environ.get("HF_TOKEN")
322
-
323
- if hf_repo is None:
324
- hf_repo = os.environ.get("HF_REPO", "Hamza4100/multi-pdf-storage")
325
-
326
- return HFUserManager(hf_token=hf_token, hf_repo=hf_repo)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ User Management Module
3
+ ======================
4
+ Handles user database storage in Hugging Face repository instead of local JSON.
5
+ Stores users_db.json in: Hamza4100/multi-pdf-storage/users_db.json
6
+ """
7
+
8
+ import os
9
+ import json
10
+ import hashlib
11
+ import tempfile
12
+ from typing import Optional, Dict, Any
13
+ from datetime import datetime
14
+ from huggingface_hub import HfApi, hf_hub_download, login
15
+
16
+
17
+ class HFUserManager:
18
+ """Manages user database stored in Hugging Face repository."""
19
+
20
+ def __init__(self, hf_token: Optional[str], hf_repo: str):
21
+ """
22
+ Initialize HF User Manager.
23
+
24
+ Args:
25
+ hf_token: Hugging Face API token with write access
26
+ hf_repo: HF repository ID (e.g., "Hamza4100/multi-pdf-storage")
27
+ """
28
+ self.hf_token = hf_token
29
+ self.hf_repo = hf_repo
30
+ self.enabled = bool(hf_token and hf_repo)
31
+ self.api = None
32
+ self.users_db_file = "users_db.json" # File stored at repo root
33
+ self.users_data: Dict[str, Any] = {}
34
+
35
+ if self.enabled:
36
+ try:
37
+ login(token=hf_token, add_to_git_credential=True)
38
+ self.api = HfApi()
39
+ print(f"✅ HF User Manager initialized: {hf_repo}")
40
+ # Load users database on init
41
+ self._load_users_from_hf()
42
+ except Exception as e:
43
+ print(f"⚠️ HF User Manager initialization failed: {e}")
44
+ self.enabled = False
45
+ else:
46
+ print("⚠️ HF User Manager disabled (HF_TOKEN or HF_REPO not set)")
47
+
48
+ def _load_users_from_hf(self) -> bool:
49
+ """
50
+ Load users_db.json from HF repository.
51
+
52
+ Returns:
53
+ bool: True if loaded successfully, False otherwise
54
+ """
55
+ if not self.enabled:
56
+ return False
57
+
58
+ try:
59
+ print(f"📥 Loading users database from {self.hf_repo}/{self.users_db_file}")
60
+
61
+ # Download users_db.json from HF repo
62
+ downloaded_path = hf_hub_download(
63
+ repo_id=self.hf_repo,
64
+ filename=self.users_db_file,
65
+ token=self.hf_token,
66
+ repo_type="model",
67
+ local_dir_use_symlinks=False
68
+ )
69
+
70
+ # Read the file
71
+ with open(downloaded_path, 'r') as f:
72
+ self.users_data = json.load(f)
73
+
74
+ print(f"✅ Loaded {len(self.users_data)} user(s) from HF repo")
75
+ # Backfill missing user_id fields (derive from api_key) and save
76
+ modified = False
77
+ for username, udata in list(self.users_data.items()):
78
+ if 'user_id' not in udata and udata.get('api_key'):
79
+ try:
80
+ udata['user_id'] = hashlib.sha256(udata['api_key'].encode()).hexdigest()[:12]
81
+ self.users_data[username] = udata
82
+ modified = True
83
+ except Exception:
84
+ continue
85
+
86
+ if modified:
87
+ # Save back to HF to persist user_id fields
88
+ self._save_users_to_hf(commit_message="Backfill user_id for existing users")
89
+
90
+ return True
91
+
92
+ except Exception as e:
93
+ # File might not exist yet (first run is okay)
94
+ print(f"⚠️ Could not load users database from HF: {str(e)[:100]}")
95
+ print(" Starting with empty database (will be created on first signup)")
96
+ self.users_data = {}
97
+ return False
98
+
99
+ def _save_users_to_hf(self, commit_message: str = "Update users database") -> bool:
100
+ """
101
+ Save users_db.json to HF repository.
102
+
103
+ Args:
104
+ commit_message: Commit message for the upload
105
+
106
+ Returns:
107
+ bool: True if saved successfully, False otherwise
108
+ """
109
+ if not self.enabled:
110
+ print("⚠️ HF User Manager disabled, cannot save to HF")
111
+ return False
112
+
113
+ try:
114
+ print(f"📤 Saving users database to {self.hf_repo}/{self.users_db_file}")
115
+
116
+ # Create a temporary file with the users data
117
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as tmp:
118
+ json.dump(self.users_data, tmp, indent=2)
119
+ tmp_path = tmp.name
120
+
121
+ try:
122
+ # Upload to HF repo
123
+ self.api.upload_file(
124
+ path_or_fileobj=tmp_path,
125
+ path_in_repo=self.users_db_file,
126
+ repo_id=self.hf_repo,
127
+ token=self.hf_token,
128
+ repo_type="model",
129
+ commit_message=commit_message
130
+ )
131
+
132
+ print(f"✅ Users database saved to HF repo")
133
+ return True
134
+
135
+ finally:
136
+ # Clean up temporary file
137
+ if os.path.exists(tmp_path):
138
+ os.remove(tmp_path)
139
+
140
+ except Exception as e:
141
+ print(f"❌ Failed to save users database to HF: {e}")
142
+ return False
143
+
144
+ def get_user(self, username: str) -> Optional[Dict[str, Any]]:
145
+ """
146
+ Get user by username.
147
+
148
+ Args:
149
+ username: Username to retrieve
150
+
151
+ Returns:
152
+ User data dict or None if not found
153
+ """
154
+ return self.users_data.get(username)
155
+
156
+ def user_exists(self, username: str) -> bool:
157
+ """
158
+ Check if user exists.
159
+
160
+ Args:
161
+ username: Username to check
162
+
163
+ Returns:
164
+ True if user exists, False otherwise
165
+ """
166
+ return username in self.users_data
167
+
168
+ def create_user(
169
+ self,
170
+ username: str,
171
+ password_hash: str,
172
+ email: str,
173
+ api_key: str
174
+ ) -> bool:
175
+ """
176
+ Create a new user account.
177
+
178
+ Args:
179
+ username: Username
180
+ password_hash: Hashed password
181
+ email: User email
182
+ api_key: API key for this user
183
+
184
+ Returns:
185
+ bool: True if user created, False if user already exists
186
+ """
187
+ if self.user_exists(username):
188
+ return False
189
+
190
+ # Derive stable user_id from API key for storage isolation
191
+ try:
192
+ user_id = hashlib.sha256(api_key.encode()).hexdigest()[:12]
193
+ except Exception:
194
+ user_id = None
195
+
196
+ self.users_data[username] = {
197
+ "password_hash": password_hash,
198
+ "email": email,
199
+ "api_key": api_key,
200
+ "user_id": user_id,
201
+ "created_at": datetime.now().isoformat()
202
+ }
203
+
204
+ # Save to HF
205
+ success = self._save_users_to_hf(
206
+ commit_message=f"Add new user: {username}"
207
+ )
208
+
209
+ if success:
210
+ print(f" User '{username}' created and saved to HF")
211
+
212
+ return success
213
+
214
+ def update_user(self, username: str, updates: Dict[str, Any]) -> bool:
215
+ """
216
+ Update user data.
217
+
218
+ Args:
219
+ username: Username to update
220
+ updates: Dictionary with updates
221
+
222
+ Returns:
223
+ bool: True if updated successfully, False if user not found
224
+ """
225
+ if not self.user_exists(username):
226
+ return False
227
+
228
+ self.users_data[username].update(updates)
229
+
230
+ # Save to HF
231
+ success = self._save_users_to_hf(
232
+ commit_message=f"Update user: {username}"
233
+ )
234
+
235
+ if success:
236
+ print(f"✅ User '{username}' updated in HF")
237
+
238
+ return success
239
+
240
+ def delete_user(self, username: str) -> bool:
241
+ """
242
+ Delete a user account.
243
+
244
+ Args:
245
+ username: Username to delete
246
+
247
+ Returns:
248
+ bool: True if deleted successfully, False if user not found
249
+ """
250
+ if not self.user_exists(username):
251
+ return False
252
+
253
+ del self.users_data[username]
254
+
255
+ # Save to HF
256
+ success = self._save_users_to_hf(
257
+ commit_message=f"Delete user: {username}"
258
+ )
259
+
260
+ if success:
261
+ print(f"✅ User '{username}' deleted from HF")
262
+
263
+ return success
264
+
265
+ def get_all_users(self) -> Dict[str, Any]:
266
+ """
267
+ Get all users data.
268
+
269
+ Returns:
270
+ Dictionary of all users
271
+ """
272
+ return self.users_data.copy()
273
+
274
+ def get_user_by_api_key(self, api_key: str) -> Optional[tuple]:
275
+ """
276
+ Find user by API key.
277
+
278
+ Args:
279
+ api_key: API key to search for
280
+
281
+ Returns:
282
+ Tuple of (username, user_data) or None if not found
283
+ """
284
+ for username, user_data in self.users_data.items():
285
+ if user_data.get("api_key") == api_key:
286
+ return (username, user_data)
287
+
288
+ return None
289
+
290
+ def get_user_by_email(self, email: str) -> Optional[tuple]:
291
+ """
292
+ Find user by email.
293
+
294
+ Args:
295
+ email: Email to search for
296
+
297
+ Returns:
298
+ Tuple of (username, user_data) or None if not found
299
+ """
300
+ for username, user_data in self.users_data.items():
301
+ if user_data.get("email") == email:
302
+ return (username, user_data)
303
+
304
+ return None
305
+
306
+ def verify_password(self, username: str, password_hash: str) -> bool:
307
+ """
308
+ Verify user password hash.
309
+
310
+ Args:
311
+ username: Username
312
+ password_hash: Password hash to verify
313
+
314
+ Returns:
315
+ bool: True if password matches, False otherwise
316
+ """
317
+ user = self.get_user(username)
318
+ if not user:
319
+ return False
320
+
321
+ return user.get("password_hash") == password_hash
322
+
323
+
324
+ # ============================================
325
+ # CONVENIENCE FUNCTIONS
326
+ # ============================================
327
+
328
+ def create_hf_user_manager(
329
+ hf_token: Optional[str] = None,
330
+ hf_repo: Optional[str] = None
331
+ ) -> HFUserManager:
332
+ """
333
+ Create and return an HF User Manager instance.
334
+
335
+ Args:
336
+ hf_token: HF token (reads from env if not provided)
337
+ hf_repo: HF repo ID (reads from env if not provided)
338
+
339
+ Returns:
340
+ HFUserManager instance
341
+ """
342
+ if hf_token is None:
343
+ hf_token = os.environ.get("HF_TOKEN")
344
+
345
+ if hf_repo is None:
346
+ hf_repo = os.environ.get("HF_REPO", "Hamza4100/multi-pdf-storage")
347
+
348
+ return HFUserManager(hf_token=hf_token, hf_repo=hf_repo)