last_edit / server /api_new_features.py
Moharek
Deploy Moharek GEO Platform
a74b879
# ═══════════════════════════════════════════════════════════════════════════════
# NEW FEATURES: NOTIFICATIONS, 2FA, SUPPORT
# ═══════════════════════════════════════════════════════════════════════════════
# These endpoints should be added to server/api.py
# ── NOTIFICATIONS ──────────────────────────────────────────────────────────────
from server import notifications as notif_service
class NotificationPrefsRequest(BaseModel):
email_enabled: bool = True
sms_enabled: bool = False
web_enabled: bool = True
crawl_complete: bool = True
analysis_complete: bool = True
alert_triggered: bool = True
weekly_report: bool = True
@app.get('/api/notifications')
async def api_get_notifications(request: Request, unread_only: bool = False):
"""Get user notifications"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
notifs = notif_service.get_notifications(uid, unread_only)
return {'ok': True, 'notifications': notifs, 'count': len(notifs)}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.post('/api/notifications/{notification_id}/read')
async def api_mark_notification_read(notification_id: int, request: Request):
"""Mark notification as read"""
try:
notif_service.mark_as_read(notification_id)
return {'ok': True}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.get('/api/notifications/preferences')
async def api_get_notification_prefs(request: Request):
"""Get notification preferences"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
prefs = notif_service.get_preferences(uid)
return {'ok': True, 'preferences': prefs}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.post('/api/notifications/preferences')
async def api_update_notification_prefs(req: NotificationPrefsRequest, request: Request):
"""Update notification preferences"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
prefs_dict = req.dict()
notif_service.update_preferences(uid, prefs_dict)
return {'ok': True}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
# ── 2FA (TWO-FACTOR AUTHENTICATION) ────────────────────────────────────────────
from server import two_factor_auth as tfa_service
@app.post('/api/2fa/setup')
async def api_setup_2fa(request: Request):
"""Generate 2FA secret and QR code"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
user = user_mgmt.get_user(uid)
secret, qr_code = tfa_service.generate_secret(uid, user['email'])
return {
'ok': True,
'secret': secret,
'qr_code': qr_code,
'backup_codes': tfa_service.get_backup_codes(uid) or []
}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.post('/api/2fa/enable')
async def api_enable_2fa(request: Request):
"""Enable 2FA with verification"""
try:
data = await request.json()
secret = data.get('secret')
token_code = data.get('token')
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
# Verify token before enabling
import pyotp
totp = pyotp.TOTP(secret)
if not totp.verify(token_code):
return JSONResponse({'ok': False, 'error': 'Invalid token'}, status_code=400)
tfa_service.enable_2fa(uid, secret)
backup_codes = tfa_service.get_backup_codes(uid)
return {
'ok': True,
'message': '2FA enabled successfully',
'backup_codes': backup_codes
}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.post('/api/2fa/disable')
async def api_disable_2fa(request: Request):
"""Disable 2FA"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
tfa_service.disable_2fa(uid)
return {'ok': True, 'message': '2FA disabled'}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.get('/api/2fa/status')
async def api_2fa_status(request: Request):
"""Check 2FA status"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
enabled = tfa_service.is_2fa_enabled(uid)
return {'ok': True, 'enabled': enabled}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.get('/api/2fa/login-history')
async def api_login_history(request: Request, days: int = 30):
"""Get login history"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
history = tfa_service.get_login_history(uid, days)
return {'ok': True, 'history': history}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.get('/api/2fa/suspicious-activity')
async def api_check_suspicious_activity(request: Request):
"""Check for suspicious login activity"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
activity = tfa_service.detect_suspicious_activity(uid)
return {'ok': True, **activity}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
# ── SUPPORT SYSTEM ─────────────────────────────────────────────────────────────
from server import support as support_service
class SupportTicketRequest(BaseModel):
subject: str
description: str
category: str
priority: str = 'medium'
class TicketMessageRequest(BaseModel):
message: str
attachment_url: Optional[str] = None
@app.post('/api/support/tickets')
async def api_create_ticket(req: SupportTicketRequest, request: Request):
"""Create support ticket"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
ticket_id = support_service.create_ticket(
uid, req.subject, req.description, req.category, req.priority
)
return {'ok': True, 'ticket_id': ticket_id}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.get('/api/support/tickets')
async def api_list_tickets(request: Request, status: Optional[str] = None):
"""List user tickets"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
tickets = support_service.get_user_tickets(uid, status)
return {'ok': True, 'tickets': tickets, 'count': len(tickets)}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.get('/api/support/tickets/{ticket_id}')
async def api_get_ticket(ticket_id: int, request: Request):
"""Get ticket details"""
try:
ticket = support_service.get_ticket(ticket_id)
if not ticket:
return JSONResponse({'ok': False, 'error': 'Ticket not found'}, status_code=404)
messages = support_service.get_ticket_messages(ticket_id)
return {'ok': True, 'ticket': ticket, 'messages': messages}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.post('/api/support/tickets/{ticket_id}/messages')
async def api_add_ticket_message(ticket_id: int, req: TicketMessageRequest, request: Request):
"""Add message to ticket"""
try:
auth = request.headers.get('authorization', '')
token = auth.split(' ', 1)[1].strip()
uid = user_mgmt.verify_token(token)
if not uid:
return JSONResponse({'ok': False, 'error': 'unauthorized'}, status_code=401)
msg_id = support_service.add_ticket_message(ticket_id, uid, req.message, req.attachment_url)
return {'ok': True, 'message_id': msg_id}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.post('/api/support/tickets/{ticket_id}/status')
async def api_update_ticket_status(ticket_id: int, request: Request):
"""Update ticket status"""
try:
data = await request.json()
status = data.get('status')
support_service.update_ticket_status(ticket_id, status)
return {'ok': True}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.get('/api/support/kb')
async def api_get_kb_articles(category: Optional[str] = None):
"""Get knowledge base articles"""
try:
articles = support_service.get_kb_articles(category)
return {'ok': True, 'articles': articles, 'count': len(articles)}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.get('/api/support/kb/search')
async def api_search_kb(q: str):
"""Search knowledge base"""
try:
results = support_service.search_kb(q)
return {'ok': True, 'results': results, 'count': len(results)}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.get('/api/support/faqs')
async def api_get_faqs(category: Optional[str] = None):
"""Get FAQs"""
try:
faqs = support_service.get_faqs(category)
return {'ok': True, 'faqs': faqs, 'count': len(faqs)}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)
@app.get('/api/support/stats')
async def api_support_stats():
"""Get support statistics"""
try:
stats = support_service.get_ticket_stats()
return {'ok': True, 'stats': stats}
except Exception as e:
return JSONResponse({'ok': False, 'error': str(e)}, status_code=500)