114 lines
4.1 KiB
JavaScript
114 lines
4.1 KiB
JavaScript
/**
|
|
* 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: [...] }.");
|