lot of messs

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-23 14:35:41 +00:00
parent edb64824be
commit 38bc015d96
17 changed files with 2448 additions and 303 deletions

View File

@@ -47,11 +47,7 @@ const GSCIcon = () => (
interface IntegrationConfig {
id: string;
enabled: boolean;
apiKey?: string;
clientId?: string;
clientSecret?: string;
authBaseUri?: string;
appName?: string;
// Note: API keys are configured platform-wide in GlobalIntegrationSettings (not user-editable)
model?: string;
// Image generation service settings (separate from API integrations)
service?: string; // 'openai' or 'runware'
@@ -74,13 +70,12 @@ export default function Integration() {
openai: {
id: 'openai',
enabled: false,
apiKey: '',
model: 'gpt-4.1',
model: 'gpt-4o-mini',
},
runware: {
id: 'runware',
enabled: false,
apiKey: '',
model: 'runware:97@1',
},
image_generation: {
id: 'image_generation',
@@ -105,6 +100,17 @@ export default function Integration() {
const [isSaving, setIsSaving] = useState(false);
const [isTesting, setIsTesting] = useState(false);
// Available models from AIModelConfig
const [availableModels, setAvailableModels] = useState<{
openai_text: Array<{ value: string; label: string }>;
openai_image: Array<{ value: string; label: string }>;
runware_image: Array<{ value: string; label: string }>;
}>({
openai_text: [],
openai_image: [],
runware_image: [],
});
// Validation status for each integration: 'not_configured' | 'pending' | 'success' | 'error'
const [validationStatuses, setValidationStatuses] = useState<Record<string, 'not_configured' | 'pending' | 'success' | 'error'>>({
openai: 'not_configured',
@@ -124,16 +130,22 @@ export default function Integration() {
) => {
const API_BASE_URL = import.meta.env.VITE_BACKEND_URL || 'https://api.igny8.com/api';
// Only validate OpenAI and Runware (GSC doesn't have validation endpoint)
// Image generation doesn't have a test endpoint - just set status based on enabled
if (integrationId === 'image_generation') {
setValidationStatuses(prev => ({
...prev,
[integrationId]: enabled ? 'success' : 'not_configured',
}));
return;
}
// Only validate OpenAI and Runware (they have test endpoints)
if (!['openai', 'runware'].includes(integrationId)) {
return;
}
// Check if integration is enabled and has API key configured
const hasApiKey = apiKey && apiKey.trim() !== '';
if (!hasApiKey || !enabled) {
// Not configured or disabled - set status accordingly
// If disabled, mark as not_configured (not error!)
if (!enabled) {
setValidationStatuses(prev => ({
...prev,
[integrationId]: 'not_configured',
@@ -141,40 +153,29 @@ export default function Integration() {
return;
}
// Set pending status
// Integration is enabled - test the connection
// Set pending status while testing
setValidationStatuses(prev => ({
...prev,
[integrationId]: 'pending',
}));
// Test connection asynchronously
// Test connection asynchronously - send empty body, backend will use global settings
try {
// Build request body based on integration type
const requestBody: any = {
apiKey: apiKey,
};
// OpenAI needs model in config, Runware doesn't
if (integrationId === 'openai') {
requestBody.config = {
model: model || 'gpt-4.1',
with_response: false, // Simple connection test for status validation
};
}
const data = await fetchAPI(`/v1/system/settings/integrations/${integrationId}/test/`, {
method: 'POST',
body: JSON.stringify(requestBody),
body: JSON.stringify({}),
});
// fetchAPI extracts the data field and throws on error
// If we get here without error, validation was successful
console.log(`✅ Validation successful for ${integrationId}`);
setValidationStatuses(prev => ({
...prev,
[integrationId]: 'success',
}));
} catch (error: any) {
console.error(`Error validating ${integrationId}:`, error);
console.error(`❌ Validation failed for ${integrationId}:`, error);
setValidationStatuses(prev => ({
...prev,
[integrationId]: 'error',
@@ -189,17 +190,16 @@ export default function Integration() {
const validateEnabledIntegrations = useCallback(async () => {
// Use functional update to read latest state without adding dependencies
setIntegrations((currentIntegrations) => {
// Validate each integration
['openai', 'runware'].forEach((id) => {
// Validate each integration (including image_generation)
['openai', 'runware', 'image_generation'].forEach((id) => {
const integration = currentIntegrations[id];
if (!integration) return;
const enabled = integration.enabled === true;
const apiKey = integration.apiKey;
const model = integration.model;
// Validate with current state (fire and forget - don't await)
validateIntegration(id, enabled, apiKey, model);
validateIntegration(id, enabled, undefined, model);
});
// Return unchanged - we're just reading state
@@ -207,16 +207,30 @@ export default function Integration() {
});
}, [validateIntegration]);
// Load integration settings on mount
// Load available models from backend
const loadAvailableModels = async () => {
try {
const data = await fetchAPI('/v1/system/settings/integrations/available-models/');
if (data) {
setAvailableModels(data);
}
} catch (error) {
console.error('Error loading available models:', error);
// Keep default empty arrays
}
};
// Load integration settings and available models on mount
useEffect(() => {
loadIntegrationSettings();
loadAvailableModels();
}, []);
// Validate integrations after settings are loaded or changed (debounced to prevent excessive validation)
useEffect(() => {
// Only validate if integrations have been loaded (not initial empty state)
const hasLoadedData = Object.values(integrations).some(integ =>
integ.apiKey !== undefined || integ.enabled !== undefined
integ.enabled !== undefined
);
if (!hasLoadedData) return;
@@ -227,7 +241,7 @@ export default function Integration() {
return () => clearTimeout(timeoutId);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [integrations.openai.enabled, integrations.runware.enabled, integrations.openai.apiKey, integrations.runware.apiKey]);
}, [integrations.openai.enabled, integrations.runware.enabled, integrations.openai.model, integrations.runware.model]);
const loadIntegrationSettings = async () => {
try {
@@ -294,12 +308,6 @@ export default function Integration() {
}
const config = integrations[selectedIntegration];
const apiKey = config.apiKey;
if (!apiKey) {
toast.error('Please enter an API key first');
return;
}
setIsTesting(true);
@@ -423,13 +431,12 @@ export default function Integration() {
if (integrationId === 'openai') {
return [
{ label: 'App Name', value: 'OpenAI API' },
{ label: 'API Key', value: config.apiKey ? `${config.apiKey.substring(0, 20)}...` : 'Not configured' },
{ label: 'Model', value: config.model || 'Not set' },
];
} else if (integrationId === 'runware') {
return [
{ label: 'App Name', value: 'Runware API' },
{ label: 'API Key', value: config.apiKey ? `${config.apiKey.substring(0, 20)}...` : 'Not configured' },
{ label: 'Model', value: config.model || 'Not set' },
];
} else if (integrationId === 'image_generation') {
const service = config.service || 'openai';
@@ -477,55 +484,48 @@ export default function Integration() {
if (integrationId === 'openai') {
return [
{
key: 'apiKey',
label: 'OpenAI API Key',
type: 'password',
value: config.apiKey || '',
onChange: (value) => {
setIntegrations({
...integrations,
[integrationId]: { ...config, apiKey: value },
});
},
placeholder: 'Enter your OpenAI API key',
required: true,
},
{
key: 'model',
label: 'AI Model',
type: 'select',
value: config.model || 'gpt-4.1',
value: config.model || 'gpt-4o-mini',
onChange: (value) => {
setIntegrations({
...integrations,
[integrationId]: { ...config, model: value },
});
},
options: [
{ value: 'gpt-4.1', label: 'GPT-4.1 - $2.00 / $8.00 per 1M tokens' },
{ value: 'gpt-4o-mini', label: 'GPT-4o mini - $0.15 / $0.60 per 1M tokens' },
{ value: 'gpt-4o', label: 'GPT-4o - $2.50 / $10.00 per 1M tokens' },
{ value: 'gpt-5.1', label: 'GPT-5.1 - $1.25 / $10.00 per 1M tokens (16K)' },
{ value: 'gpt-5.2', label: 'GPT-5.2 - $1.75 / $14.00 per 1M tokens (16K)' },
],
options: availableModels?.openai_text?.length > 0
? availableModels.openai_text
: [
{ value: 'gpt-4.1', label: 'GPT-4.1 - $2.00 / $8.00 per 1M tokens' },
{ value: 'gpt-4o-mini', label: 'GPT-4o mini - $0.15 / $0.60 per 1M tokens' },
{ value: 'gpt-4o', label: 'GPT-4o - $2.50 / $10.00 per 1M tokens' },
{ value: 'gpt-5.1', label: 'GPT-5.1 - $1.25 / $10.00 per 1M tokens (16K)' },
{ value: 'gpt-5.2', label: 'GPT-5.2 - $1.75 / $14.00 per 1M tokens (16K)' },
],
},
];
} else if (integrationId === 'runware') {
return [
{
key: 'apiKey',
label: 'Runware API Key',
type: 'password',
value: config.apiKey || '',
key: 'model',
label: 'Runware Model',
type: 'select',
value: config.model || 'runware:97@1',
onChange: (value) => {
setIntegrations({
...integrations,
[integrationId]: { ...config, apiKey: value },
[integrationId]: { ...config, model: value },
});
},
placeholder: 'Enter your Runware API key',
required: true,
options: availableModels?.runware_image?.length > 0
? availableModels.runware_image
: [
{ value: 'runware:97@1', label: 'Runware 97@1 - Versatile Model' },
{ value: 'runware:100@1', label: 'Runware 100@1 - High Quality' },
{ value: 'runware:101@1', label: 'Runware 101@1 - Fast Generation' },
],
},
];
} else if (integrationId === 'image_generation') {
@@ -569,13 +569,12 @@ export default function Integration() {
[integrationId]: { ...config, model: value },
});
},
options: [
{ value: 'dall-e-3', label: 'DALL·E 3 - $0.040 per image' },
{ value: 'dall-e-2', label: 'DALL·E 2 - $0.020 per image' },
// Note: gpt-image-1 and gpt-image-1-mini are not valid for OpenAI's /v1/images/generations endpoint
// They are not currently supported by OpenAI's image generation API
// Only dall-e-3 and dall-e-2 are supported
],
options: availableModels?.openai_image?.length > 0
? availableModels.openai_image
: [
{ value: 'dall-e-3', label: 'DALL·E 3 - $0.040 per image' },
{ value: 'dall-e-2', label: 'DALL·E 2 - $0.020 per image' },
],
});
} else if (service === 'runware') {
fields.push({
@@ -589,11 +588,13 @@ export default function Integration() {
[integrationId]: { ...config, runwareModel: value },
});
},
options: [
{ value: 'runware:97@1', label: 'HiDream-I1 Full - $0.009 per image' },
{ value: 'runware:gen3a_turbo', label: 'Gen3a Turbo - $0.009 per image' },
{ value: 'runware:gen3a', label: 'Gen3a - $0.009 per image' },
],
options: availableModels?.runware_image?.length > 0
? availableModels.runware_image
: [
{ value: 'runware:97@1', label: 'HiDream-I1 Full - $0.009 per image' },
{ value: 'runware:100@1', label: 'Runware 100@1 - High Quality' },
{ value: 'runware:101@1', label: 'Runware 101@1 - Fast Generation' },
],
});
}
@@ -905,7 +906,7 @@ export default function Integration() {
console.error('Error rendering image generation form:', error);
return <div className="text-error-500">Error loading form. Please refresh the page.</div>;
}
}, [selectedIntegration, integrations, showSettingsModal, getSettingsFields]);
}, [selectedIntegration, integrations, showSettingsModal, availableModels]);
return (
<>
@@ -951,15 +952,15 @@ export default function Integration() {
validationStatus={validationStatuses.runware}
integrationId="runware"
modelName={
integrations.image_generation?.service === 'runware' && integrations.image_generation.runwareModel
integrations.runware?.enabled && integrations.runware?.model
? (() => {
// Map model ID to display name
const modelDisplayNames: Record<string, string> = {
'runware:97@1': 'HiDream-I1 Full',
'runware:gen3a_turbo': 'Gen3a Turbo',
'runware:gen3a': 'Gen3a',
'runware:100@1': 'Runware 100@1',
'runware:101@1': 'Runware 101@1',
};
return modelDisplayNames[integrations.image_generation.runwareModel] || integrations.image_generation.runwareModel;
return modelDisplayNames[integrations.runware.model] || integrations.runware.model;
})()
: undefined
}
@@ -996,6 +997,12 @@ export default function Integration() {
title="Image Generation Service"
description="Default image generation service and model selection for app-wide use"
validationStatus={validationStatuses.image_generation}
onToggleSuccess={(enabled, data) => {
// Validate when toggle changes - same pattern as openai/runware
const provider = data?.provider || data?.service || 'openai';
const model = data?.model || (provider === 'openai' ? 'dall-e-3' : 'runware:97@1');
validateIntegration('image_generation', enabled, null, model);
}}
onSettings={() => handleSettings('image_generation')}
onDetails={() => handleDetails('image_generation')}
/>