/** * ai-seed-provider.js — Configure un fournisseur IA en ligne de commande, en * attendant le back-office admin (Task future : CRUD ai_providers dans * Admin.jsx). Usage (depuis le dossier backend/) : * * node scripts/ai-seed-provider.js --email vous@exemple.fr --type anthropic \ * --model claude-sonnet-4-5 --key sk-ant-xxxxx [--nom "Anthropic (test)"] * * Effets : * - crée/met à jour un fournisseur ai_providers (nom sert de clé d'upsert), * le marque is_default=1 (et retire ce flag des autres fournisseurs) * - active ai_config.actif (interrupteur global) — sauf --no-activate * - active users.ai_enabled pour l'email donné — sauf --no-user-enable * * Nécessite AI_ENC_KEY dans .env (voir .env.example) — sinon échoue avec un * message explicite (chiffrement de la clé API du fournisseur). */ import 'dotenv/config'; import db from '../src/db/index.js'; import { encryptSecret } from '../src/ai/crypto.js'; function parseArgs(argv) { const args = {}; for (let i = 0; i < argv.length; i++) { if (argv[i].startsWith('--')) { const key = argv[i].slice(2); const next = argv[i + 1]; if (next === undefined || next.startsWith('--')) { args[key] = true; // flag booléen (ex. --no-activate) } else { args[key] = next; i++; } } } return args; } const args = parseArgs(process.argv.slice(2)); const email = args.email; const key = args.key; const model = args.model; const type = args.type || 'anthropic'; const nom = args.nom || (type === 'anthropic' ? 'Anthropic' : `${type} (via seed)`); const baseUrl = args['base-url'] || null; const activate = !args['no-activate']; const enableUser = !args['no-user-enable']; function fail(msg) { console.error(`✗ ${msg}`); process.exit(1); } if (!email) fail('--email requis (email du compte utilisateur à autoriser)'); if (!key) fail('--key requis (clé API du fournisseur, en clair — sera chiffrée avant stockage)'); if (!model) fail("--model requis (ex. claude-sonnet-4-5 pour l'adaptateur anthropic)"); if (!['anthropic', 'openai_compatible'].includes(type)) { fail(`--type invalide (${type}) — attendu "anthropic" ou "openai_compatible"`); } if (type === 'openai_compatible' && !baseUrl) { fail('--base-url requis pour --type openai_compatible (ex. https://openrouter.ai/api/v1)'); } const user = db.prepare('SELECT id, email FROM users WHERE email = ?').get(email); if (!user) fail(`Aucun utilisateur avec l'email "${email}"`); let apiKeyChiffree; try { apiKeyChiffree = encryptSecret(key); } catch (e) { fail(e.message); } const tx = db.transaction(() => { db.exec('UPDATE ai_providers SET is_default = 0'); const existing = db.prepare('SELECT id FROM ai_providers WHERE nom = ?').get(nom); let providerId; if (existing) { db.prepare(` UPDATE ai_providers SET type = ?, base_url = ?, modele = ?, api_key_chiffree = ?, actif = 1, is_default = 1, updated_at = datetime('now') WHERE id = ? `).run(type, baseUrl, model, apiKeyChiffree, existing.id); providerId = existing.id; console.log(`✓ Fournisseur "${nom}" mis à jour (id=${providerId})`); } else { const info = db.prepare(` INSERT INTO ai_providers (nom, type, base_url, modele, api_key_chiffree, actif, is_default) VALUES (?, ?, ?, ?, ?, 1, 1) `).run(nom, type, baseUrl, model, apiKeyChiffree); providerId = info.lastInsertRowid; console.log(`✓ Fournisseur "${nom}" créé (id=${providerId})`); } if (activate) { db.prepare(` UPDATE ai_config SET actif = 1, provider_defaut_id = ?, updated_at = datetime('now') WHERE id = 1 `).run(providerId); console.log('✓ Interrupteur global IA activé (ai_config.actif = 1)'); } if (enableUser) { db.prepare('UPDATE users SET ai_enabled = 1 WHERE id = ?').run(user.id); console.log(`✓ Accès IA activé pour ${user.email}`); } }); tx(); console.log('\nPrêt à tester : GET /api/ai/status devrait répondre { enabled: true } pour cet utilisateur,'); console.log("puis POST /api/ai/chat avec un en-tête X-Investisseur-Id valide et un body { messages: [...] }.");