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
111 lines
4.5 KiB
Python
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)
|