# -*- coding: utf-8 -*- import json import logging from odoo import http, fields from odoo.http import request from odoo.service import db from odoo.tools import config _logger = logging.getLogger(__name__) class SaasApiController(http.Controller): def _authenticate(self): # Authenticate using a SaaS Token defined in odoo.conf (e.g. saas_api_token = abcxyz123) expected_token = config.get('saas_api_token', 'METATRON_DINE360_SECRET_SaaS_2026') auth_header = request.httprequest.headers.get('Authorization') if not auth_header or not auth_header.startswith('Bearer '): return False token = auth_header.split(' ')[1] return token == expected_token def _json_response(self, data, status=200): return request.make_response( json.dumps(data), headers=[('Content-Type', 'application/json')], status=status ) @http.route('/api/v1/saas/create_restaurant', type='json', auth='none', methods=['POST'], csrf=False) def api_create_restaurant(self): if not self._authenticate(): return self._json_response({'error': 'Unauthorized'}, status=401) try: data = request.get_json_data() name = data.get('name') owner_name = data.get('owner_name') email = data.get('email') plan_name = data.get('plan_name', 'Starter') billing_cycle = data.get('billing_cycle', 'monthly') expiry_date_str = data.get('expiry_date') # Format YYYY-MM-DD if not all([name, owner_name, email, expiry_date_str]): return self._json_response({'error': 'Missing required fields'}, status=400) # Find subscription plan plan = request.env['saas.plan'].sudo().search([('name', '=', plan_name)], limit=1) if not plan: return self._json_response({'error': f"Plan '{plan_name}' not found"}, status=404) expiry_date = fields.Date.from_string(expiry_date_str) # Create Restaurant record inside the master DB env restaurant = request.env['saas.restaurant'].sudo().create({ 'name': name, 'owner_name': owner_name, 'email': email, 'phone': data.get('phone', ''), 'street': data.get('street', ''), 'city': data.get('city', ''), 'plan_id': plan.id, 'billing_cycle': billing_cycle, 'expiry_date': expiry_date, 'currency_id': request.env['res.currency'].sudo().search([('name', '=', data.get('currency', 'USD'))], limit=1).id, 'timezone': data.get('timezone', 'America/New_York') }) # Programmatically trigger database creation in Odoo restaurant.action_create_database() return self._json_response({ 'success': True, 'restaurant_id': restaurant.id, 'database_name': restaurant.database_name, 'subdomain': restaurant.subdomain, 'status': restaurant.status }) except Exception as e: _logger.error(f"SaaS API Create Restaurant Error: {str(e)}") return self._json_response({'error': str(e)}, status=500) @http.route('/api/v1/saas/suspend_restaurant', type='json', auth='none', methods=['POST'], csrf=False) def api_suspend_restaurant(self): if not self._authenticate(): return self._json_response({'error': 'Unauthorized'}, status=401) data = request.get_json_data() db_name = data.get('database_name') restaurant = request.env['saas.restaurant'].sudo().search([('database_name', '=', db_name)], limit=1) if not restaurant: return self._json_response({'error': 'Restaurant database not found'}, status=404) try: restaurant.action_suspend() return self._json_response({'success': True, 'status': restaurant.status}) except Exception as e: return self._json_response({'error': str(e)}, status=500) @http.route('/api/v1/saas/renew_subscription', type='json', auth='none', methods=['POST'], csrf=False) def api_renew_subscription(self): if not self._authenticate(): return self._json_response({'error': 'Unauthorized'}, status=401) data = request.get_json_data() db_name = data.get('database_name') new_expiry_str = data.get('expiry_date') restaurant = request.env['saas.restaurant'].sudo().search([('database_name', '=', db_name)], limit=1) if not restaurant: return self._json_response({'error': 'Restaurant database not found'}, status=404) try: new_expiry = fields.Date.from_string(new_expiry_str) restaurant.write({'expiry_date': new_expiry}) if restaurant.status in ['suspended', 'expired']: restaurant.action_activate() return self._json_response({ 'success': True, 'status': restaurant.status, 'expiry_date': fields.Date.to_string(restaurant.expiry_date) }) except Exception as e: return self._json_response({'error': str(e)}, status=500) @http.route('/api/v1/saas/delete_restaurant', type='json', auth='none', methods=['POST'], csrf=False) def api_delete_restaurant(self): if not self._authenticate(): return self._json_response({'error': 'Unauthorized'}, status=401) data = request.get_json_data() db_name = data.get('database_name') restaurant = request.env['saas.restaurant'].sudo().search([('database_name', '=', db_name)], limit=1) if not restaurant: return self._json_response({'error': 'Restaurant database not found'}, status=404) try: master_pwd = config.get('admin_passwd', 'admin') # 1. Drop database in PostgreSQL via Odoo service API db.exp_drop(master_pwd, db_name) # 2. Delete database registry entry in master restaurant.unlink() return self._json_response({'success': True}) except Exception as e: return self._json_response({'error': str(e)}, status=500) @http.route('/api/v1/saas/backup_restaurant', type='json', auth='none', methods=['POST'], csrf=False) def api_backup_restaurant(self): if not self._authenticate(): return self._json_response({'error': 'Unauthorized'}, status=401) data = request.get_json_data() db_name = data.get('database_name') storage_type = data.get('storage_type', 'local') restaurant = request.env['saas.restaurant'].sudo().search([('database_name', '=', db_name)], limit=1) if not restaurant: return self._json_response({'error': 'Restaurant database not found'}, status=404) try: backup = request.env['saas.backup'].sudo().create({ 'restaurant_id': restaurant.id, 'storage_type': storage_type }) backup.action_backup_database() return self._json_response({ 'success': True, 'backup_file': backup.backup_file, 's3_url': backup.s3_url }) except Exception as e: return self._json_response({'error': str(e)}, status=500)