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:
@@ -180,11 +180,7 @@ class ChangePasswordView(APIView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@extend_schema(
|
@extend_schema(exclude=True) # Exclude from public API documentation - internal authenticated endpoint
|
||||||
tags=['Authentication'],
|
|
||||||
summary='Get Current User',
|
|
||||||
description='Get information about the currently authenticated user'
|
|
||||||
)
|
|
||||||
class MeView(APIView):
|
class MeView(APIView):
|
||||||
"""Get current user information."""
|
"""Get current user information."""
|
||||||
permission_classes = [permissions.IsAuthenticated]
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|||||||
@@ -90,48 +90,67 @@ class IntegrationSettingsViewSet(viewsets.ViewSet):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Get API key and config from request or saved settings
|
# Get API key and config from request or saved settings
|
||||||
config = request.data.get('config', {}) if isinstance(request.data.get('config'), dict) else {}
|
# Handle empty request.data gracefully (for API monitor)
|
||||||
api_key = request.data.get('apiKey') or config.get('apiKey')
|
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):
|
if not isinstance(config, dict):
|
||||||
config = {}
|
config = {}
|
||||||
|
|
||||||
if not api_key:
|
if not api_key:
|
||||||
# Try to get from saved settings
|
# Try to get from saved settings
|
||||||
account = getattr(request, 'account', None)
|
account = None
|
||||||
logger.info(f"[test_connection] Account from request: {account.id if account else None}")
|
try:
|
||||||
# Fallback to user's account
|
account = getattr(request, 'account', None)
|
||||||
if not account:
|
if account:
|
||||||
user = getattr(request, 'user', None)
|
logger.info(f"[test_connection] Account from request: {account.id}")
|
||||||
if user and hasattr(user, 'is_authenticated') and user.is_authenticated:
|
# Fallback to user's account
|
||||||
account = getattr(user, 'account', None)
|
if not account:
|
||||||
# Fallback to default account
|
user = getattr(request, 'user', None)
|
||||||
if not account:
|
if user and hasattr(user, 'is_authenticated') and user.is_authenticated:
|
||||||
from igny8_core.auth.models import Account
|
account = getattr(user, 'account', None)
|
||||||
try:
|
if account:
|
||||||
account = Account.objects.first()
|
logger.info(f"[test_connection] Account from user: {account.id}")
|
||||||
except Exception:
|
# Fallback to default account (only for API monitor/testing)
|
||||||
pass
|
if not account:
|
||||||
|
from igny8_core.auth.models import Account
|
||||||
if account:
|
try:
|
||||||
try:
|
account = Account.objects.first()
|
||||||
from .models import IntegrationSettings
|
if account:
|
||||||
logger.info(f"[test_connection] Looking for saved settings for account {account.id}")
|
logger.info(f"[test_connection] Using fallback account: {account.id}")
|
||||||
saved_settings = IntegrationSettings.objects.get(
|
except Exception as e:
|
||||||
integration_type=integration_type,
|
logger.debug(f"[test_connection] Could not get fallback account: {e}")
|
||||||
account=account
|
account = None
|
||||||
)
|
|
||||||
api_key = saved_settings.config.get('apiKey')
|
if account:
|
||||||
logger.info(f"[test_connection] Found saved settings, has_apiKey={bool(api_key)}")
|
try:
|
||||||
except IntegrationSettings.DoesNotExist:
|
from .models import IntegrationSettings
|
||||||
logger.warning(f"[test_connection] No saved settings found for {integration_type} and account {account.id}")
|
logger.info(f"[test_connection] Looking for saved settings for account {account.id}")
|
||||||
pass
|
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:
|
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(
|
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,
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
request=request
|
request=request
|
||||||
)
|
)
|
||||||
@@ -210,7 +229,7 @@ class IntegrationSettingsViewSet(viewsets.ViewSet):
|
|||||||
total_tokens = usage.get('total_tokens', 0)
|
total_tokens = usage.get('total_tokens', 0)
|
||||||
|
|
||||||
# Calculate cost using model rates (reference plugin: line 274-275)
|
# 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})
|
rates = MODEL_RATES.get(model, {'input': 2.00, 'output': 8.00})
|
||||||
cost = (input_tokens * rates['input'] + output_tokens * rates['output']) / 1000000
|
cost = (input_tokens * rates['input'] + output_tokens * rates['output']) / 1000000
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
|
import { fetchAPI } from '../services/api';
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -192,27 +193,18 @@ export const useAuthStore = create<AuthState>()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const API_BASE_URL = import.meta.env.VITE_BACKEND_URL || 'https://api.igny8.com/api';
|
// Use unified API system - fetchAPI automatically handles auth token from store
|
||||||
const token = state.token || getAuthToken();
|
const response = await fetchAPI('/v1/auth/me/');
|
||||||
|
|
||||||
const response = await fetch(`${API_BASE_URL}/v1/auth/me/`, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
...(token ? { 'Authorization': `Bearer ${token}` } : {}),
|
|
||||||
},
|
|
||||||
credentials: 'include',
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
// fetchAPI extracts data from unified format {success: true, data: {user: {...}}}
|
||||||
|
// So response is {user: {...}}
|
||||||
if (!response.ok || !data.success) {
|
if (!response || !response.user) {
|
||||||
throw new Error(data.message || 'Failed to refresh user data');
|
throw new Error('Invalid user data received');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update user data with latest from server
|
// Update user data with latest from server
|
||||||
// This ensures account/plan changes are reflected immediately
|
// This ensures account/plan changes are reflected immediately
|
||||||
set({ user: data.user });
|
set({ user: response.user });
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// If refresh fails, don't logout - just log the error
|
// If refresh fails, don't logout - just log the error
|
||||||
// User might still be authenticated, just couldn't refresh data
|
// User might still be authenticated, just couldn't refresh data
|
||||||
|
|||||||
Reference in New Issue
Block a user