import logging import time import jwt as pyjwt from odoo import http, fields from odoo.http import request from odoo.exceptions import AccessDenied from .base import ( _json_response, _error_response, _get_json_body, _get_jwt_secret, validate_token, ) _logger = logging.getLogger(__name__) class EncoachAuthController(http.Controller): # ------------------------------------------------------------------ # POST /api/login # ------------------------------------------------------------------ @http.route('/api/login', type='http', auth='public', methods=['POST'], csrf=False) def login(self, **kw): try: body = _get_json_body() login = (body.get('login') or body.get('email') or '').strip().lower() password = body.get('password', '') if not login or not password: return _error_response('login and password are required', 400) # Odoo 19: session.authenticate(env, credential_dict) credential = { 'type': 'password', 'login': login, 'password': password, } try: request.session.authenticate(request.env, credential) uid = request.session.uid if not uid: return _error_response('Invalid email or password', 401) except AccessDenied: return _error_response('Invalid email or password', 401) except Exception as auth_err: _logger.warning('Auth error for %s: %s', login, auth_err) return _error_response('Invalid email or password', 401) user = request.env['res.users'].sudo().browse(uid) # Generate JWT token secret = _get_jwt_secret() if not secret: return _error_response('JWT not configured on server', 500) token = pyjwt.encode( {'user_id': user.id, 'exp': int(time.time()) + 86400}, secret, algorithm='HS256', ) # Get permissions permissions = [] if hasattr(user, 'get_all_permissions'): permissions = user.get_all_permissions().mapped('code') # Update last login user.write({'last_login': fields.Datetime.now()}) return _json_response({ 'token': token, 'user': self._user_to_dict(user), 'permissions': permissions, }) except Exception as e: _logger.exception('login failed') return _error_response(str(e), 500) # ------------------------------------------------------------------ # GET /api/user (returns current authenticated user) # ------------------------------------------------------------------ @http.route('/api/user', type='http', auth='public', methods=['GET'], csrf=False) def get_current_user(self, **kw): try: user = validate_token() if not user: return _error_response('Authentication required', 401) permissions = [] if hasattr(user, 'get_all_permissions'): permissions = user.get_all_permissions().mapped('code') return _json_response({ 'user': self._user_to_dict(user), 'permissions': permissions, }) except Exception as e: _logger.exception('get_current_user failed') return _error_response(str(e), 500) # ------------------------------------------------------------------ # POST /api/logout # ------------------------------------------------------------------ @http.route('/api/logout', type='http', auth='public', methods=['POST'], csrf=False) def logout(self, **kw): # JWT is stateless — client clears token. Server just returns OK. return _json_response({'ok': True}) # ------------------------------------------------------------------ # helpers # ------------------------------------------------------------------ def _user_to_dict(self, user): """Convert res.users to the dict shape the React frontend expects.""" entities = [] if hasattr(user, 'entity_ids'): entities = [ {'id': e.id, 'name': e.name, 'role': ''} for e in user.entity_ids ] classrooms = [] return { 'id': user.id, 'name': user.name or '', 'email': user.email or '', 'login': user.login or '', 'user_type': getattr(user, '_api_user_type', lambda: user.user_type or 'student')() if callable(getattr(user, '_api_user_type', None)) else (user.user_type or 'student'), 'avatar': bool(getattr(user, 'encoach_avatar', False)), 'phone': user.phone or '', 'country': '', 'timezone': '', 'bio': '', 'gender': getattr(user, 'gender', '') or '', 'student_id': '', 'is_verified': getattr(user, 'is_verified', False), 'entities': entities, 'classrooms': classrooms, 'expiry_date': '', }