Обновление админки и представлений для интеграций
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from .models import RecommerceIntegration, WooCommerceIntegration, IntegrationCategoryMapping
|
||||
from .models import RecommerceIntegration, WooCommerceIntegration, IntegrationCategoryMapping, GLMIntegration, OpenRouterIntegration
|
||||
|
||||
|
||||
@admin.register(RecommerceIntegration)
|
||||
@@ -46,3 +46,31 @@ class IntegrationCategoryMappingAdmin(admin.ModelAdmin):
|
||||
('Внешняя категория', {'fields': ('external_category_sku', 'external_category_name')}),
|
||||
('Служебное', {'fields': ('created_at', 'updated_at'), 'classes': ('collapse',)}),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(GLMIntegration)
|
||||
class GLMIntegrationAdmin(admin.ModelAdmin):
|
||||
"""Админка для GLM интеграции"""
|
||||
list_display = ['__str__', 'is_active', 'is_configured', 'model_name', 'updated_at']
|
||||
list_filter = ['is_active', 'is_coding_endpoint']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
fieldsets = (
|
||||
('Основное', {'fields': ('name', 'is_active')}),
|
||||
('API настройки', {'fields': ('api_key', 'api_url', 'model_name', 'temperature', 'is_coding_endpoint')}),
|
||||
('Служебное', {'fields': ('created_at', 'updated_at'), 'classes': ('collapse',)}),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(OpenRouterIntegration)
|
||||
class OpenRouterIntegrationAdmin(admin.ModelAdmin):
|
||||
"""Админка для OpenRouter интеграции"""
|
||||
list_display = ['__str__', 'is_active', 'is_configured', 'model_name', 'updated_at']
|
||||
list_filter = ['is_active']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
|
||||
fieldsets = (
|
||||
('Основное', {'fields': ('name', 'is_active')}),
|
||||
('API настройки', {'fields': ('api_key', 'api_url', 'model_name', 'temperature', 'max_tokens')}),
|
||||
('Служебное', {'fields': ('created_at', 'updated_at'), 'classes': ('collapse',)}),
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ from django.http import JsonResponse
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from user_roles.mixins import OwnerRequiredMixin
|
||||
from .models import RecommerceIntegration, WooCommerceIntegration
|
||||
from .models import RecommerceIntegration, WooCommerceIntegration, GLMIntegration, OpenRouterIntegration
|
||||
from integrations.recommerce.tasks import sync_products_batch_task
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ from integrations.recommerce.tasks import sync_products_batch_task
|
||||
INTEGRATION_REGISTRY = {
|
||||
'recommerce': (RecommerceIntegration, 'Recommerce', 'Маркетплейс'),
|
||||
'woocommerce': (WooCommerceIntegration, 'WooCommerce', 'Маркетплейс'),
|
||||
'glm': (GLMIntegration, 'GLM от Z.AI', 'Сервис ИИ'),
|
||||
'openrouter': (OpenRouterIntegration, 'OpenRouter.ai', 'Сервис ИИ'),
|
||||
# Добавлять новые интеграции здесь:
|
||||
# 'shopify': (ShopifyIntegration, 'Shopify', 'Маркетплейс'),
|
||||
}
|
||||
@@ -110,27 +112,51 @@ def test_integration_connection(request, integration_id: str):
|
||||
Тестировать соединение с интеграцией.
|
||||
POST /integrations/test/<integration_id>/
|
||||
"""
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
logger.info(f"=== Начало тестирования интеграции: {integration_id} ===")
|
||||
|
||||
if not hasattr(request, 'user') or not request.user.is_authenticated:
|
||||
logger.error("Пользователь не авторизован")
|
||||
return JsonResponse({'error': 'Unauthorized'}, status=401)
|
||||
|
||||
model = get_integration_model(integration_id)
|
||||
if not model:
|
||||
logger.error(f"Неизвестная интеграция: {integration_id}")
|
||||
return JsonResponse({'error': f'Unknown integration: {integration_id}'}, status=404)
|
||||
|
||||
instance = model.objects.first()
|
||||
if not instance:
|
||||
logger.error(f"Интеграция {integration_id} не настроена (нет записи в БД)")
|
||||
return JsonResponse({'error': 'Интеграция не настроена'}, status=400)
|
||||
|
||||
logger.info(f"Найдена интеграция: {instance.name}, is_active={instance.is_active}, is_configured={instance.is_configured}")
|
||||
|
||||
if not instance.is_configured:
|
||||
logger.error(f"Интеграция {integration_id} не сконфигурирована")
|
||||
return JsonResponse({'error': 'Заполните настройки интеграции'}, status=400)
|
||||
|
||||
# Получить сервис для интеграции
|
||||
service = get_integration_service(integration_id, instance)
|
||||
if not service:
|
||||
return JsonResponse({'error': 'Сервис не реализован'}, status=501)
|
||||
try:
|
||||
logger.info(f"Попытка получить сервис для {integration_id}...")
|
||||
service = get_integration_service(integration_id, instance)
|
||||
if not service:
|
||||
logger.error(f"Сервис для {integration_id} не реализован")
|
||||
return JsonResponse({'error': 'Сервис не реализован'}, status=501)
|
||||
logger.info(f"Сервис успешно создан: {service.__class__.__name__}")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при создании сервиса для {integration_id}: {str(e)}", exc_info=True)
|
||||
return JsonResponse({'error': f'Ошибка при создании сервиса: {str(e)}'}, status=500)
|
||||
|
||||
# Выполнить тест
|
||||
success, message = service.test_connection()
|
||||
try:
|
||||
logger.info(f"Выполнение теста соединения...")
|
||||
success, message = service.test_connection()
|
||||
logger.info(f"Результат теста: success={success}, message={message}")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при выполнении теста соединения: {str(e)}", exc_info=True)
|
||||
return JsonResponse({'error': f'Ошибка при тестировании: {str(e)}'}, status=500)
|
||||
|
||||
return JsonResponse({
|
||||
'success': success,
|
||||
@@ -146,6 +172,12 @@ def get_integration_service(integration_id: str, instance):
|
||||
elif integration_id == 'woocommerce':
|
||||
# TODO: WooCommerceService
|
||||
return None
|
||||
elif integration_id == 'glm':
|
||||
from .services.ai_services.glm_service import GLMIntegrationService
|
||||
return GLMIntegrationService(instance)
|
||||
elif integration_id == 'openrouter':
|
||||
from .services.ai_services.openrouter_service import OpenRouterIntegrationService
|
||||
return OpenRouterIntegrationService(instance)
|
||||
return None
|
||||
|
||||
|
||||
@@ -285,18 +317,89 @@ def get_integration_form_data(request, integration_id: str):
|
||||
def get_form_fields_meta(model):
|
||||
"""Получить метаданные полей для построения формы на фронте"""
|
||||
fields = []
|
||||
for field_name in get_editable_fields(model):
|
||||
field = model._meta.get_field(field_name)
|
||||
field_info = {
|
||||
'name': field_name,
|
||||
'label': getattr(field, 'verbose_name', field_name),
|
||||
'help_text': getattr(field, 'help_text', ''),
|
||||
'required': not getattr(field, 'blank', True),
|
||||
'type': 'text', # default
|
||||
}
|
||||
editable_fields = get_editable_fields(model)
|
||||
|
||||
# Определить тип поля
|
||||
if isinstance(field, model._meta.get_field(field_name).__class__):
|
||||
# Для GLM отображаем только имя и API ключ
|
||||
if model.__name__ == 'GLMIntegration':
|
||||
for field_name in editable_fields:
|
||||
if field_name in ['name', 'api_key']:
|
||||
field = model._meta.get_field(field_name)
|
||||
field_info = {
|
||||
'name': field_name,
|
||||
'label': getattr(field, 'verbose_name', field_name),
|
||||
'help_text': getattr(field, 'help_text', ''),
|
||||
'required': not getattr(field, 'blank', True),
|
||||
'type': 'text', # default
|
||||
}
|
||||
|
||||
# Определить тип поля
|
||||
if 'BooleanField' in field.__class__.__name__:
|
||||
field_info['type'] = 'checkbox'
|
||||
elif 'URLField' in field.__class__.__name__:
|
||||
field_info['type'] = 'url'
|
||||
elif 'secret' in field_name.lower() or 'token' in field_name.lower() or 'key' in field_name.lower():
|
||||
field_info['type'] = 'password'
|
||||
|
||||
fields.append(field_info)
|
||||
elif field_name == 'model_name':
|
||||
field = model._meta.get_field(field_name)
|
||||
field_info = {
|
||||
'name': field_name,
|
||||
'label': getattr(field, 'verbose_name', field_name),
|
||||
'help_text': getattr(field, 'help_text', ''),
|
||||
'required': not getattr(field, 'blank', True),
|
||||
'type': 'select', # dropdown
|
||||
'choices': getattr(field, 'choices', [])
|
||||
}
|
||||
|
||||
fields.append(field_info)
|
||||
# Для OpenRouter отображаем только имя, API ключ, модель и температуру
|
||||
elif model.__name__ == 'OpenRouterIntegration':
|
||||
for field_name in editable_fields:
|
||||
if field_name in ['name', 'api_key']:
|
||||
field = model._meta.get_field(field_name)
|
||||
field_info = {
|
||||
'name': field_name,
|
||||
'label': getattr(field, 'verbose_name', field_name),
|
||||
'help_text': getattr(field, 'help_text', ''),
|
||||
'required': not getattr(field, 'blank', True),
|
||||
'type': 'text', # default
|
||||
}
|
||||
|
||||
# Определить тип поля
|
||||
if 'BooleanField' in field.__class__.__name__:
|
||||
field_info['type'] = 'checkbox'
|
||||
elif 'URLField' in field.__class__.__name__:
|
||||
field_info['type'] = 'url'
|
||||
elif 'secret' in field_name.lower() or 'token' in field_name.lower() or 'key' in field_name.lower():
|
||||
field_info['type'] = 'password'
|
||||
|
||||
fields.append(field_info)
|
||||
elif field_name in ['model_name', 'temperature']:
|
||||
field = model._meta.get_field(field_name)
|
||||
field_info = {
|
||||
'name': field_name,
|
||||
'label': getattr(field, 'verbose_name', field_name),
|
||||
'help_text': getattr(field, 'help_text', ''),
|
||||
'required': not getattr(field, 'blank', True),
|
||||
'type': 'select', # dropdown
|
||||
'choices': getattr(field, 'choices', [])
|
||||
}
|
||||
|
||||
fields.append(field_info)
|
||||
else:
|
||||
# Для других интеграций - все редактируемые поля
|
||||
for field_name in editable_fields:
|
||||
field = model._meta.get_field(field_name)
|
||||
field_info = {
|
||||
'name': field_name,
|
||||
'label': getattr(field, 'verbose_name', field_name),
|
||||
'help_text': getattr(field, 'help_text', ''),
|
||||
'required': not getattr(field, 'blank', True),
|
||||
'type': 'text', # default
|
||||
}
|
||||
|
||||
# Определить тип поля
|
||||
if 'BooleanField' in field.__class__.__name__:
|
||||
field_info['type'] = 'checkbox'
|
||||
elif 'URLField' in field.__class__.__name__:
|
||||
@@ -304,5 +407,6 @@ def get_form_fields_meta(model):
|
||||
elif 'secret' in field_name.lower() or 'token' in field_name.lower() or 'key' in field_name.lower():
|
||||
field_info['type'] = 'password'
|
||||
|
||||
fields.append(field_info)
|
||||
fields.append(field_info)
|
||||
|
||||
return fields
|
||||
|
||||
@@ -189,6 +189,23 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
<label class="form-check-label" for="field-${field.name}">${field.label}</label>
|
||||
${field.help_text ? `<div class="form-text">${field.help_text}</div>` : ''}
|
||||
`;
|
||||
} else if (field.type === 'select') {
|
||||
div.innerHTML = `
|
||||
<label class="form-label" for="field-${field.name}">
|
||||
${field.label}
|
||||
${field.required ? '<span class="text-danger">*</span>' : ''}
|
||||
</label>
|
||||
<select class="form-select" id="field-${field.name}"
|
||||
name="${field.name}"
|
||||
${field.required ? 'required' : ''}>
|
||||
${field.choices.map(choice => `
|
||||
<option value="${choice[0]}" ${data[field.name] === choice[0] ? 'selected' : ''}>
|
||||
${choice[1]}
|
||||
</option>
|
||||
`).join('')}
|
||||
</select>
|
||||
${field.help_text ? `<div class="form-text">${field.help_text}</div>` : ''}
|
||||
`;
|
||||
} else {
|
||||
const inputType = field.type === 'password' ? 'password' : (field.type === 'url' ? 'url' : 'text');
|
||||
const value = data[field.name] || '';
|
||||
|
||||
Reference in New Issue
Block a user