Files
encoach_be_odoo19/encoach_api/controllers/auth.py
Talal Sharabi f5b627256f EnCoach Odoo 19 custom modules
Full backend implementation with custom Odoo modules:
- encoach_api: Core API, user management, JWT auth
- encoach_exam: Exam generation (reading, writing, listening, speaking)
- encoach_evaluate: AI-powered evaluation (writing, speaking)
- encoach_training: Training tips and walkthrough
- encoach_storage: File storage management
- encoach_payment: Stripe, PayPal, Paymob integration
- encoach_mail: Email notifications

Made-with: Cursor
2026-03-14 16:46:46 +04:00

111 lines
4.5 KiB
Python

import logging
from datetime import datetime, timedelta
import jwt as pyjwt
from odoo import http
from odoo.exceptions import AccessDenied
from odoo.http import request
from .base import EncoachMixin
_logger = logging.getLogger(__name__)
class AuthController(EncoachMixin, http.Controller):
def _create_jwt(self, user, secret, expires_hours=72):
payload = {
'user_id': user.id,
'email': user.login,
'iat': datetime.utcnow(),
'exp': datetime.utcnow() + timedelta(hours=expires_hours),
}
return pyjwt.encode(payload, secret, algorithm='HS256')
@http.route('/api/login', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
def login(self, **kwargs):
try:
body = self._get_json_body()
email = (body.get('credential') or body.get('email') or '').strip()
password = body.get('password', '')
if not email or not password:
return self._error_response('Email and password are required', 400)
user = request.env['res.users'].sudo().search([
('login', '=', email),
], limit=1)
if not user:
return self._error_response('Invalid email or password', 401)
try:
credential = {'type': 'password', 'login': email, 'password': password}
request.env['res.users'].sudo().authenticate(
credential, {'interactive': False},
)
except AccessDenied:
return self._error_response('Invalid email or password', 401)
secret = request.env['ir.config_parameter'].sudo().get_param('encoach.jwt_secret')
if not secret:
return self._error_response('Server configuration error', 500)
token = self._create_jwt(user, secret)
return self._json_response({
'user': self._serialize(user),
'token': token,
})
except Exception:
_logger.exception("Login error")
return self._error_response('Internal server error', 500)
@http.route('/api/logout', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
def logout(self, **kwargs):
try:
return self._json_response({'ok': True})
except Exception:
_logger.exception("Logout error")
return self._error_response('Internal server error', 500)
@http.route('/api/reset', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
def reset_password(self, **kwargs):
try:
body = self._get_json_body()
email = body.get('email', '').strip()
new_password = body.get('password', '')
token = body.get('token', '')
if not email:
return self._error_response('Email is required', 400)
user = request.env['res.users'].sudo().search([('login', '=', email)], limit=1)
if not user:
return self._json_response({'ok': True})
if token and new_password:
secret = request.env['ir.config_parameter'].sudo().get_param('encoach.jwt_secret')
try:
payload = pyjwt.decode(token, secret, algorithms=['HS256'])
except pyjwt.InvalidTokenError:
return self._error_response('Invalid or expired reset token', 400)
if payload.get('user_id') != user.id or payload.get('purpose') != 'password_reset':
return self._error_response('Invalid reset token', 400)
user.sudo().write({'password': new_password})
return self._json_response({'ok': True})
return self._json_response({'ok': True})
except Exception:
_logger.exception("Password reset error")
return self._error_response('Internal server error', 500)
@http.route('/api/reset/sendVerification', type='http', auth='public', methods=['POST', 'OPTIONS'], csrf=False, cors='*')
def send_verification(self, **kwargs):
try:
body = self._get_json_body()
email = body.get('email', '').strip()
if not email:
return self._error_response('Email is required', 400)
return self._json_response({'ok': True})
except Exception:
_logger.exception("Send verification error")
return self._error_response('Internal server error', 500)