Refactor authentication and integration handling

- Exclude the 'MeView' endpoint from public API documentation, marking it as an internal authenticated endpoint.
- Enhance error handling in the 'IntegrationSettingsViewSet' to gracefully manage empty request data and improve logging for account and settings lookups.
- Update API key retrieval logic to ensure fallback mechanisms are more robust and informative.
- Refactor user data fetching in the auth store to utilize a unified API system, improving error handling and data consistency.
This commit is contained in:
IGNY8 VPS (Salman)
2025-11-16 09:49:24 +00:00
parent 60ffc12e8c
commit d3ec7cf2e3
3 changed files with 62 additions and 55 deletions

View File

@@ -180,11 +180,7 @@ class ChangePasswordView(APIView):
)
@extend_schema(
tags=['Authentication'],
summary='Get Current User',
description='Get information about the currently authenticated user'
)
@extend_schema(exclude=True) # Exclude from public API documentation - internal authenticated endpoint
class MeView(APIView):
"""Get current user information."""
permission_classes = [permissions.IsAuthenticated]

View File

@@ -90,48 +90,67 @@ class IntegrationSettingsViewSet(viewsets.ViewSet):
)
# Get API key and config from request or saved settings
config = request.data.get('config', {}) if isinstance(request.data.get('config'), dict) else {}
api_key = request.data.get('apiKey') or config.get('apiKey')
# Handle empty request.data gracefully (for API monitor)
try:
request_data = request.data if hasattr(request, 'data') and request.data else {}
if not isinstance(request_data, dict):
request_data = {}
except Exception:
request_data = {}
# Merge request.data with config if config is a dict
config = request_data.get('config', {}) if isinstance(request_data.get('config'), dict) else {}
api_key = request_data.get('apiKey') or config.get('apiKey')
# Ensure config is a dict
if not isinstance(config, dict):
config = {}
if not api_key:
# Try to get from saved settings
account = getattr(request, 'account', None)
logger.info(f"[test_connection] Account from request: {account.id if account else None}")
# Fallback to user's account
if not account:
user = getattr(request, 'user', None)
if user and hasattr(user, 'is_authenticated') and user.is_authenticated:
account = getattr(user, 'account', None)
# Fallback to default account
if not account:
from igny8_core.auth.models import Account
try:
account = Account.objects.first()
except Exception:
pass
if account:
try:
from .models import IntegrationSettings
logger.info(f"[test_connection] Looking for saved settings for account {account.id}")
saved_settings = IntegrationSettings.objects.get(
integration_type=integration_type,
account=account
)
api_key = saved_settings.config.get('apiKey')
logger.info(f"[test_connection] Found saved settings, has_apiKey={bool(api_key)}")
except IntegrationSettings.DoesNotExist:
logger.warning(f"[test_connection] No saved settings found for {integration_type} and account {account.id}")
pass
account = None
try:
account = getattr(request, 'account', None)
if account:
logger.info(f"[test_connection] Account from request: {account.id}")
# Fallback to user's account
if not account:
user = getattr(request, 'user', None)
if user and hasattr(user, 'is_authenticated') and user.is_authenticated:
account = getattr(user, 'account', None)
if account:
logger.info(f"[test_connection] Account from user: {account.id}")
# Fallback to default account (only for API monitor/testing)
if not account:
from igny8_core.auth.models import Account
try:
account = Account.objects.first()
if account:
logger.info(f"[test_connection] Using fallback account: {account.id}")
except Exception as e:
logger.debug(f"[test_connection] Could not get fallback account: {e}")
account = None
if account:
try:
from .models import IntegrationSettings
logger.info(f"[test_connection] Looking for saved settings for account {account.id}")
saved_settings = IntegrationSettings.objects.get(
integration_type=integration_type,
account=account
)
api_key = saved_settings.config.get('apiKey') if saved_settings.config else None
logger.info(f"[test_connection] Found saved settings, has_apiKey={bool(api_key)}")
except IntegrationSettings.DoesNotExist:
logger.debug(f"[test_connection] No saved settings found for {integration_type} and account {account.id if account else 'None'}")
except Exception as e:
logger.debug(f"[test_connection] Error getting saved settings: {e}")
except Exception as e:
logger.debug(f"[test_connection] Error during account/settings lookup: {e}")
if not api_key:
logger.error(f"[test_connection] No API key found in request or saved settings")
logger.info(f"[test_connection] No API key found in request or saved settings - returning 400 (expected for API monitor)")
return error_response(
error='API key is required',
error='API key is required. Please provide an API key in the request or configure it in settings.',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
@@ -210,7 +229,7 @@ class IntegrationSettingsViewSet(viewsets.ViewSet):
total_tokens = usage.get('total_tokens', 0)
# Calculate cost using model rates (reference plugin: line 274-275)
from igny8_core.utils.ai_processor import MODEL_RATES
from igny8_core.ai.constants import MODEL_RATES
rates = MODEL_RATES.get(model, {'input': 2.00, 'output': 8.00})
cost = (input_tokens * rates['input'] + output_tokens * rates['output']) / 1000000

View File

@@ -4,6 +4,7 @@
*/
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { fetchAPI } from '../services/api';
interface User {
id: number;
@@ -192,27 +193,18 @@ export const useAuthStore = create<AuthState>()(
}
try {
const API_BASE_URL = import.meta.env.VITE_BACKEND_URL || 'https://api.igny8.com/api';
const token = state.token || getAuthToken();
const response = await fetch(`${API_BASE_URL}/v1/auth/me/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
...(token ? { 'Authorization': `Bearer ${token}` } : {}),
},
credentials: 'include',
});
// Use unified API system - fetchAPI automatically handles auth token from store
const response = await fetchAPI('/v1/auth/me/');
const data = await response.json();
if (!response.ok || !data.success) {
throw new Error(data.message || 'Failed to refresh user data');
// fetchAPI extracts data from unified format {success: true, data: {user: {...}}}
// So response is {user: {...}}
if (!response || !response.user) {
throw new Error('Invalid user data received');
}
// Update user data with latest from server
// This ensures account/plan changes are reflected immediately
set({ user: data.user });
set({ user: response.user });
} catch (error: any) {
// If refresh fails, don't logout - just log the error
// User might still be authenticated, just couldn't refresh data