amauricunha commited on
Commit
4ddaca4
·
verified ·
1 Parent(s): b7c1fc7

Delete admin_module.py

Browse files
Files changed (1) hide show
  1. admin_module.py +0 -562
admin_module.py DELETED
@@ -1,562 +0,0 @@
1
- # admin_module.py - Administrative interface for English Helper
2
- import os
3
- import json
4
- import hashlib
5
- from datetime import datetime, timedelta
6
- from functools import wraps
7
- from flask import session, request, jsonify, redirect, url_for
8
- import sqlite3
9
- from database import get_db_connection
10
- import logging
11
-
12
- logger = logging.getLogger(__name__)
13
-
14
- class AdminManager:
15
- def __init__(self):
16
- self.admin_credentials = self._load_admin_credentials()
17
- self.token_costs = {
18
- 'groq': {'input': 0.00000059, 'output': 0.00000079}, # per token
19
- 'gemini': {'input': 0.00000125, 'output': 0.00000375} # per token
20
- }
21
-
22
- def _load_admin_credentials(self):
23
- """Load admin credentials from environment variables (Hugging Face secrets)"""
24
- try:
25
- # Try to load from Hugging Face secrets format
26
- admin_user = os.environ.get('ADMIN_USERNAME', 'admin')
27
- admin_pass = os.environ.get('ADMIN_PASSWORD', 'admin123')
28
-
29
- # For security, hash the password
30
- admin_pass_hash = hashlib.sha256(admin_pass.encode()).hexdigest()
31
-
32
- return {
33
- 'username': admin_user,
34
- 'password_hash': admin_pass_hash,
35
- 'original_password': admin_pass # Store for initial comparison
36
- }
37
- except Exception as e:
38
- logger.error(f"Error loading admin credentials: {e}")
39
- # Fallback credentials
40
- return {
41
- 'username': 'admin',
42
- 'password_hash': hashlib.sha256('admin123'.encode()).hexdigest(),
43
- 'original_password': 'admin123'
44
- }
45
-
46
- def authenticate_admin(self, username, password):
47
- """Authenticate admin user"""
48
- try:
49
- if username != self.admin_credentials['username']:
50
- return False
51
-
52
- # Check password hash
53
- password_hash = hashlib.sha256(password.encode()).hexdigest()
54
- return password_hash == self.admin_credentials['password_hash']
55
- except Exception as e:
56
- logger.error(f"Admin authentication error: {e}")
57
- return False
58
-
59
- def is_admin_logged_in(self):
60
- """Check if admin is logged in"""
61
- authenticated = session.get('admin_authenticated', False)
62
- username = session.get('admin_username')
63
- login_time = session.get('admin_login_time')
64
-
65
- # Debug logging
66
- logger.info(f"Admin auth check: authenticated={authenticated}, username={username}, login_time={login_time}")
67
-
68
- # Check if session has expired (24 hours)
69
- if authenticated and login_time:
70
- try:
71
- login_datetime = datetime.fromisoformat(login_time)
72
- if datetime.now() - login_datetime > timedelta(hours=24):
73
- logger.info("Admin session expired, logging out")
74
- self.logout_admin()
75
- return False
76
- except Exception as e:
77
- logger.error(f"Error checking session expiry: {e}")
78
-
79
- return authenticated
80
-
81
- def login_admin(self, username, password):
82
- """Admin login"""
83
- if self.authenticate_admin(username, password):
84
- session['admin_authenticated'] = True
85
- session['admin_username'] = username
86
- session['admin_login_time'] = datetime.now().isoformat()
87
- session.permanent = True # Make session permanent
88
-
89
- logger.info(f"Admin login successful: {username}")
90
- return True
91
- else:
92
- logger.warning(f"Admin login failed for username: {username}")
93
- return False
94
-
95
- def logout_admin(self):
96
- """Admin logout"""
97
- session.pop('admin_authenticated', None)
98
- session.pop('admin_username', None)
99
- session.pop('admin_login_time', None)
100
-
101
- def get_system_stats(self):
102
- """Get comprehensive system statistics"""
103
- try:
104
- conn = get_db_connection()
105
- cursor = conn.cursor()
106
-
107
- # User statistics
108
- cursor.execute("SELECT COUNT(*) FROM users")
109
- total_users = cursor.fetchone()[0]
110
-
111
- cursor.execute("SELECT COUNT(*) FROM users WHERE created_at > datetime('now', '-7 days')")
112
- new_users_week = cursor.fetchone()[0]
113
-
114
- cursor.execute("SELECT COUNT(*) FROM users WHERE created_at > datetime('now', '-1 day')")
115
- new_users_today = cursor.fetchone()[0]
116
-
117
- # Activity statistics
118
- cursor.execute("SELECT COUNT(*) FROM study_sessions")
119
- total_sessions = cursor.fetchone()[0]
120
-
121
- cursor.execute("SELECT COUNT(*) FROM flashcards")
122
- total_flashcards = cursor.fetchone()[0]
123
-
124
- cursor.execute("SELECT COUNT(*) FROM user_articles")
125
- total_articles = cursor.fetchone()[0]
126
-
127
- cursor.execute("SELECT COUNT(*) FROM study_plans")
128
- total_study_plans = cursor.fetchone()[0]
129
-
130
- # Token usage statistics
131
- cursor.execute("SELECT SUM(tokens_used), COUNT(*) FROM token_usage")
132
- token_stats = cursor.fetchone()
133
- total_tokens = token_stats[0] if token_stats[0] else 0
134
- total_api_calls = token_stats[1] if token_stats[1] else 0
135
-
136
- # Calculate estimated costs
137
- estimated_cost = self._calculate_estimated_cost(cursor)
138
-
139
- # Recent activity
140
- cursor.execute("""
141
- SELECT u.email, s.created_at, s.activity_type
142
- FROM study_sessions s
143
- JOIN users u ON s.user_id = u.id
144
- ORDER BY s.created_at DESC
145
- LIMIT 10
146
- """)
147
- recent_activity = cursor.fetchall()
148
-
149
- conn.close()
150
-
151
- return {
152
- 'users': {
153
- 'total': total_users,
154
- 'new_week': new_users_week,
155
- 'new_today': new_users_today
156
- },
157
- 'activity': {
158
- 'total_sessions': total_sessions,
159
- 'total_flashcards': total_flashcards,
160
- 'total_articles': total_articles,
161
- 'total_study_plans': total_study_plans
162
- },
163
- 'api_usage': {
164
- 'total_tokens': total_tokens,
165
- 'total_calls': total_api_calls,
166
- 'estimated_cost': estimated_cost
167
- },
168
- 'recent_activity': [
169
- {
170
- 'user': activity[0],
171
- 'timestamp': activity[1],
172
- 'activity': activity[2]
173
- } for activity in recent_activity
174
- ]
175
- }
176
- except Exception as e:
177
- logger.error(f"Error getting system stats: {e}")
178
- return {}
179
-
180
- def _calculate_estimated_cost(self, cursor):
181
- """Calculate estimated API costs"""
182
- try:
183
- cursor.execute("""
184
- SELECT api_provider, SUM(input_tokens), SUM(output_tokens)
185
- FROM token_usage
186
- GROUP BY api_provider
187
- """)
188
- usage_by_provider = cursor.fetchall()
189
-
190
- total_cost = 0
191
- for provider, input_tokens, output_tokens in usage_by_provider:
192
- if provider in self.token_costs:
193
- costs = self.token_costs[provider]
194
- total_cost += (input_tokens * costs['input']) + (output_tokens * costs['output'])
195
-
196
- return round(total_cost, 4)
197
- except:
198
- return 0
199
-
200
- def get_all_users(self, page=1, per_page=20):
201
- """Get paginated list of all users"""
202
- try:
203
- conn = get_db_connection()
204
- cursor = conn.cursor()
205
-
206
- offset = (page - 1) * per_page
207
-
208
- cursor.execute("""
209
- SELECT u.id, u.email, u.created_at, u.email_confirmed, u.last_login,
210
- COUNT(DISTINCT s.id) as session_count,
211
- COUNT(DISTINCT f.id) as flashcard_count,
212
- COUNT(DISTINCT a.id) as article_count
213
- FROM users u
214
- LEFT JOIN study_sessions s ON u.id = s.user_id
215
- LEFT JOIN flashcards f ON u.id = f.user_id
216
- LEFT JOIN user_articles a ON u.id = a.user_id
217
- GROUP BY u.id
218
- ORDER BY u.created_at DESC
219
- LIMIT ? OFFSET ?
220
- """, (per_page, offset))
221
-
222
- users = cursor.fetchall()
223
-
224
- # Get total count
225
- cursor.execute("SELECT COUNT(*) FROM users")
226
- total_users = cursor.fetchone()[0]
227
-
228
- conn.close()
229
-
230
- return {
231
- 'users': [
232
- {
233
- 'id': user[0],
234
- 'email': user[1],
235
- 'created_at': user[2],
236
- 'email_confirmed': bool(user[3]),
237
- 'last_login': user[4],
238
- 'session_count': user[5],
239
- 'flashcard_count': user[6],
240
- 'article_count': user[7]
241
- } for user in users
242
- ],
243
- 'total': total_users,
244
- 'page': page,
245
- 'per_page': per_page,
246
- 'total_pages': (total_users + per_page - 1) // per_page
247
- }
248
- except Exception as e:
249
- logger.error(f"Error getting users: {e}")
250
- return {'users': [], 'total': 0}
251
-
252
- def delete_user(self, user_id):
253
- """Delete a user and all associated data"""
254
- try:
255
- conn = get_db_connection()
256
- cursor = conn.cursor()
257
-
258
- # Delete in order to respect foreign key constraints
259
- tables = [
260
- 'study_plan_activities', 'study_plans', 'user_analytics',
261
- 'content_recommendations', 'user_interests', 'user_articles',
262
- 'study_sessions', 'flashcards', 'user_settings', 'users'
263
- ]
264
-
265
- for table in tables:
266
- cursor.execute(f"DELETE FROM {table} WHERE user_id = ?", (user_id,))
267
-
268
- conn.commit()
269
- conn.close()
270
-
271
- return True
272
- except Exception as e:
273
- logger.error(f"Error deleting user {user_id}: {e}")
274
- return False
275
-
276
- def get_user_details(self, user_id):
277
- """Get detailed information about a specific user"""
278
- try:
279
- conn = get_db_connection()
280
- cursor = conn.cursor()
281
-
282
- # Basic user info
283
- cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
284
- user = cursor.fetchone()
285
-
286
- if not user:
287
- return None
288
-
289
- # User settings
290
- cursor.execute("SELECT * FROM user_settings WHERE user_id = ?", (user_id,))
291
- settings = cursor.fetchone()
292
-
293
- # Recent activity
294
- cursor.execute("""
295
- SELECT activity_type, created_at, duration_minutes
296
- FROM study_sessions
297
- WHERE user_id = ?
298
- ORDER BY created_at DESC
299
- LIMIT 20
300
- """, (user_id,))
301
- recent_sessions = cursor.fetchall()
302
-
303
- # Token usage
304
- cursor.execute("""
305
- SELECT api_provider, SUM(input_tokens), SUM(output_tokens), COUNT(*)
306
- FROM token_usage
307
- WHERE user_id = ?
308
- GROUP BY api_provider
309
- """, (user_id,))
310
- token_usage = cursor.fetchall()
311
-
312
- conn.close()
313
-
314
- return {
315
- 'user': {
316
- 'id': user[0],
317
- 'email': user[1],
318
- 'created_at': user[2],
319
- 'email_confirmed': bool(user[3]),
320
- 'last_login': user[4]
321
- },
322
- 'settings': dict(zip([col[0] for col in cursor.description], settings)) if settings else {},
323
- 'recent_sessions': [
324
- {
325
- 'activity': session[0],
326
- 'timestamp': session[1],
327
- 'duration': session[2]
328
- } for session in recent_sessions
329
- ],
330
- 'token_usage': [
331
- {
332
- 'provider': usage[0],
333
- 'input_tokens': usage[1],
334
- 'output_tokens': usage[2],
335
- 'calls': usage[3]
336
- } for usage in token_usage
337
- ]
338
- }
339
- except Exception as e:
340
- logger.error(f"Error getting user details for {user_id}: {e}")
341
- return None
342
-
343
- def record_token_usage(self, user_id, api_provider, input_tokens, output_tokens, operation_type):
344
- """Record token usage for cost tracking"""
345
- try:
346
- conn = get_db_connection()
347
- cursor = conn.cursor()
348
-
349
- cursor.execute("""
350
- INSERT INTO token_usage
351
- (user_id, api_provider, input_tokens, output_tokens, operation_type, created_at)
352
- VALUES (?, ?, ?, ?, ?, ?)
353
- """, (user_id, api_provider, input_tokens, output_tokens, operation_type, datetime.now().isoformat()))
354
-
355
- conn.commit()
356
- conn.close()
357
-
358
- return True
359
- except Exception as e:
360
- logger.error(f"Error recording token usage: {e}")
361
- return False
362
-
363
- def get_database_schema(self):
364
- """Get database schema information"""
365
- try:
366
- conn = get_db_connection()
367
- cursor = conn.cursor()
368
-
369
- # Get all tables
370
- cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
371
- tables = cursor.fetchall()
372
-
373
- schema_info = {}
374
- for table in tables:
375
- table_name = table[0]
376
-
377
- # Get table info
378
- cursor.execute(f"PRAGMA table_info({table_name})")
379
- columns = cursor.fetchall()
380
-
381
- # Get row count
382
- cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
383
- row_count = cursor.fetchone()[0]
384
-
385
- schema_info[table_name] = {
386
- 'columns': [
387
- {
388
- 'name': col[1],
389
- 'type': col[2],
390
- 'not_null': bool(col[3]),
391
- 'primary_key': bool(col[5])
392
- } for col in columns
393
- ],
394
- 'row_count': row_count
395
- }
396
-
397
- conn.close()
398
- return schema_info
399
- except Exception as e:
400
- logger.error(f"Error getting database schema: {e}")
401
- return {}
402
-
403
- def get_system_health(self):
404
- """Get system health metrics"""
405
- try:
406
- import psutil
407
- import os
408
-
409
- # Memory usage
410
- memory = psutil.virtual_memory()
411
-
412
- # Disk usage
413
- disk = psutil.disk_usage('/')
414
-
415
- # Database size
416
- db_path = 'data/englishhelper.db'
417
- db_size = os.path.getsize(db_path) if os.path.exists(db_path) else 0
418
-
419
- # Recent error logs (would implement proper logging)
420
- recent_errors = self._get_recent_errors()
421
-
422
- return {
423
- 'memory': {
424
- 'total': memory.total,
425
- 'used': memory.used,
426
- 'available': memory.available,
427
- 'percent': memory.percent
428
- },
429
- 'disk': {
430
- 'total': disk.total,
431
- 'used': disk.used,
432
- 'free': disk.free,
433
- 'percent': disk.percent
434
- },
435
- 'database': {
436
- 'size_bytes': db_size,
437
- 'size_mb': round(db_size / 1024 / 1024, 2)
438
- },
439
- 'recent_errors': recent_errors,
440
- 'uptime': self._get_uptime()
441
- }
442
- except Exception as e:
443
- logger.error(f"Error getting system health: {e}")
444
- return {}
445
-
446
- def _get_recent_errors(self):
447
- """Get recent error logs (simplified)"""
448
- try:
449
- # This would typically read from log files
450
- # For now, return sample data
451
- return [
452
- {
453
- 'timestamp': '2024-10-11 14:30:00',
454
- 'level': 'ERROR',
455
- 'message': 'API rate limit exceeded for user 123',
456
- 'module': 'groq_client'
457
- },
458
- {
459
- 'timestamp': '2024-10-11 13:45:00',
460
- 'level': 'WARNING',
461
- 'message': 'High memory usage detected',
462
- 'module': 'system_monitor'
463
- }
464
- ]
465
- except:
466
- return []
467
-
468
- def _get_uptime(self):
469
- """Get system uptime"""
470
- try:
471
- import psutil
472
- boot_time = psutil.boot_time()
473
- uptime_seconds = datetime.now().timestamp() - boot_time
474
-
475
- days = int(uptime_seconds // 86400)
476
- hours = int((uptime_seconds % 86400) // 3600)
477
- minutes = int((uptime_seconds % 3600) // 60)
478
-
479
- return f"{days}d {hours}h {minutes}m"
480
- except:
481
- return "Unknown"
482
-
483
- def check_system_alerts(self):
484
- """Check for system alerts and warnings"""
485
- alerts = []
486
-
487
- try:
488
- # Check token usage limits
489
- conn = get_db_connection()
490
- cursor = conn.cursor()
491
-
492
- # Check daily token usage
493
- cursor.execute("""
494
- SELECT SUM(tokens_used)
495
- FROM token_usage
496
- WHERE date(created_at) = date('now')
497
- """)
498
- daily_tokens = cursor.fetchone()[0] or 0
499
-
500
- if daily_tokens > 100000: # Alert threshold
501
- alerts.append({
502
- 'type': 'warning',
503
- 'message': f'High daily token usage: {daily_tokens:,} tokens',
504
- 'action': 'Monitor API costs'
505
- })
506
-
507
- # Check error rates
508
- cursor.execute("""
509
- SELECT COUNT(*) FROM token_usage
510
- WHERE created_at > datetime('now', '-1 hour')
511
- """)
512
- hourly_requests = cursor.fetchone()[0] or 0
513
-
514
- if hourly_requests > 500: # High load threshold
515
- alerts.append({
516
- 'type': 'info',
517
- 'message': f'High API request rate: {hourly_requests} requests/hour',
518
- 'action': 'Monitor performance'
519
- })
520
-
521
- # Check database size
522
- health = self.get_system_health()
523
- if health.get('database', {}).get('size_mb', 0) > 100: # 100MB threshold
524
- alerts.append({
525
- 'type': 'warning',
526
- 'message': f'Large database size: {health["database"]["size_mb"]}MB',
527
- 'action': 'Consider archiving old data'
528
- })
529
-
530
- conn.close()
531
- return alerts
532
-
533
- except Exception as e:
534
- logger.error(f"Error checking system alerts: {e}")
535
- return []
536
-
537
- # Decorator for admin-only routes
538
- def admin_required(f):
539
- @wraps(f)
540
- def decorated_function(*args, **kwargs):
541
- is_authenticated = admin_manager.is_admin_logged_in()
542
-
543
- # Enhanced logging for debugging
544
- from flask import session, request
545
- logger.info(f"Admin required check for {f.__name__}: authenticated={is_authenticated}")
546
- logger.info(f"Session keys: {list(session.keys())}")
547
- logger.info(f"Request URL: {request.url}")
548
-
549
- if not is_authenticated:
550
- logger.warning(f"Admin authentication failed for {f.__name__}")
551
- return jsonify({
552
- 'error': 'Admin authentication required',
553
- 'authenticated': False,
554
- 'endpoint': f.__name__
555
- }), 401
556
-
557
- logger.info(f"Admin access granted to {f.__name__}")
558
- return f(*args, **kwargs)
559
- return decorated_function
560
-
561
- # Global admin manager instance
562
- admin_manager = AdminManager()