feat & fix : intégration IA éditeur + onglet admin IA + corrections CSP (v1.6.24-25)

- #96 : boutons IA sidebar éditeur (analyse critique / réécriture) via Anthropic API
- #97 : onglet admin /admin/ia — provider anthropic/claude_code, modèle, procédure CLI
- #95 : extraction scripts inline vers fichiers JS (comments.js, post_confirm.js, admin.js)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-16 12:18:38 +02:00
parent fabe5a9f53
commit 298f18dabe
16 changed files with 527 additions and 32 deletions
+47 -1
View File
@@ -45,7 +45,7 @@ $action = $_GET['action'] ?? 'list';
$uuid = $_GET['uuid'] ?? '';
$slug = $_GET['slug'] ?? '';
$_noindexActions = ['create', 'edit', 'admin', 'categories', 'diff', 'add_files', 'import_image', 'import_image_step2', 'sources', 'profile', 'delete_file', 'delete_external_link', 'rename_category', 'delete_category', 'toggle_private_category', 'admin_save_site', 'not_found', 'add_feed', 'delete_feed', 'add_link', 'delete_link', 'reorder_links', 'react', 'comment', 'verify_comment', 'comment_moderate', 'comment_delete', 'comment_resend', 'create_tag_type', 'delete_tag_type', 'edit_tags', 'book_save', 'book_delete', 'admin_save_as_groups', 'admin_save_folio_config', 'run_engine_update', 'run_content_migrations', 'admin_delete_feed', 'rate'];
$_noindexActions = ['create', 'edit', 'admin', 'categories', 'diff', 'add_files', 'import_image', 'import_image_step2', 'sources', 'profile', 'delete_file', 'delete_external_link', 'rename_category', 'delete_category', 'toggle_private_category', 'admin_save_site', 'not_found', 'add_feed', 'delete_feed', 'add_link', 'delete_link', 'reorder_links', 'react', 'comment', 'verify_comment', 'comment_moderate', 'comment_delete', 'comment_resend', 'create_tag_type', 'delete_tag_type', 'edit_tags', 'book_save', 'book_delete', 'admin_save_as_groups', 'admin_save_folio_config', 'run_engine_update', 'run_content_migrations', 'admin_delete_feed', 'rate', 'admin_save_ai_config'];
$metaRobots = in_array($action, $_noindexActions, true) ? 'noindex, nofollow' : null;
unset($_noindexActions);
@@ -1688,6 +1688,24 @@ switch ($action) {
echo json_encode(fetchUrlMeta(trim($_GET['url'] ?? '')));
exit;
case 'ai_query':
requireAuth();
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['ok' => false, 'error' => 'Méthode invalide']);
exit;
}
$_aiAction = trim($_POST['action'] ?? '');
$_aiTitle = trim($_POST['title'] ?? '');
$_aiContent = str_replace("\r\n", "\n", trim($_POST['content'] ?? ''));
if (!in_array($_aiAction, ['critique', 'rewrite'], true) || $_aiContent === '') {
echo json_encode(['ok' => false, 'error' => 'Paramètres invalides']);
exit;
}
require_once BASE_PATH . '/src/Service/AiService.php';
echo json_encode((new AiService())->query($_aiAction, $_aiTitle, $_aiContent));
exit;
case 'import_image_step2':
requireAuth();
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
@@ -2765,9 +2783,37 @@ switch ($action) {
}
}
if ($tab === 'ia') {
if (!isAdmin()) { http_response_code(403); exit; }
require_once BASE_PATH . '/src/SiteSettings.php';
require_once BASE_PATH . '/src/Service/AiService.php';
$adminData['ai_provider'] = aiProvider();
$adminData['ai_model'] = aiModel();
$adminData['anthropic_key_set'] = (($_ENV['ANTHROPIC_API_KEY'] ?? getenv('ANTHROPIC_API_KEY') ?: '') !== '');
$adminData['claude_cli_found'] = is_executable('/usr/local/bin/claude');
$adminData['ai_notice'] = $_GET['notice'] ?? '';
}
include BASE_PATH . '/templates/admin.php';
break;
case 'admin_save_ai_config':
requireAuth();
if (!isAdmin() || $_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(403); exit;
}
require_once BASE_PATH . '/src/SiteSettings.php';
$allowedProviders = ['anthropic', 'claude_code'];
$aiProvider = in_array($_POST['ai_provider'] ?? '', $allowedProviders, true)
? $_POST['ai_provider']
: 'anthropic';
$ok = saveSiteSettings([
'ai_provider' => $aiProvider,
'ai_model' => trim($_POST['ai_model'] ?? ''),
]);
header('Location: /admin/ia?notice=' . ($ok ? 'saved' : 'error'));
exit;
case 'admin_smtp_save':
requireAuth();
if (!isAdmin()) {