164 lines
5.4 KiB
TypeScript
164 lines
5.4 KiB
TypeScript
#!/usr/bin/env tsx
|
||
|
||
import { SkillsService } from "../services/skills-service";
|
||
import { loadSkillCategoriesFromFiles } from "../lib/skill-file-loader";
|
||
import { SkillCategory, Skill } from "../lib/types";
|
||
|
||
interface SyncStats {
|
||
categoriesProcessed: number;
|
||
skillsProcessed: number;
|
||
newSkills: number;
|
||
updatedSkills: number;
|
||
skippedSkills: number;
|
||
}
|
||
|
||
/**
|
||
* Synchronise les skills depuis les fichiers JSON vers la base de données
|
||
* - Préserve les skills existantes (ne modifie que description/image)
|
||
* - Ajoute uniquement les nouvelles skills
|
||
* - Ne supprime jamais de skills
|
||
*/
|
||
async function syncSkillsToDatabase(): Promise<void> {
|
||
try {
|
||
console.log("🚀 Démarrage de la synchronisation des skills...");
|
||
|
||
// Charger les skills depuis les fichiers JSON
|
||
const skillCategoriesFromFiles = loadSkillCategoriesFromFiles();
|
||
console.log(
|
||
`📁 ${skillCategoriesFromFiles.length} catégories trouvées dans les fichiers`
|
||
);
|
||
|
||
// Récupérer les skills existantes de la base de données
|
||
const existingCategories = await SkillsService.getSkillCategories();
|
||
console.log(
|
||
`💾 ${existingCategories.length} catégories existantes en base`
|
||
);
|
||
|
||
// Créer un map des skills existantes pour une recherche rapide
|
||
const existingSkillsMap = new Map<string, Skill>();
|
||
existingCategories.forEach((category) => {
|
||
category.skills.forEach((skill) => {
|
||
existingSkillsMap.set(skill.id, skill);
|
||
});
|
||
});
|
||
|
||
console.log(`🔍 ${existingSkillsMap.size} skills existantes détectées`);
|
||
|
||
const stats: SyncStats = {
|
||
categoriesProcessed: 0,
|
||
skillsProcessed: 0,
|
||
newSkills: 0,
|
||
updatedSkills: 0,
|
||
skippedSkills: 0,
|
||
};
|
||
|
||
// Synchroniser chaque catégorie
|
||
for (const categoryFromFile of skillCategoriesFromFiles) {
|
||
console.log(
|
||
`\n📂 Traitement de la catégorie: ${categoryFromFile.category}`
|
||
);
|
||
|
||
const categoryId = categoryFromFile.category.toLowerCase();
|
||
|
||
// Vérifier si la catégorie existe, sinon la créer
|
||
const existingCategory = existingCategories.find(
|
||
(cat) =>
|
||
cat.category.toLowerCase() === categoryFromFile.category.toLowerCase()
|
||
);
|
||
|
||
if (!existingCategory) {
|
||
console.log(
|
||
` ➕ Création de la nouvelle catégorie: ${categoryFromFile.category}`
|
||
);
|
||
await SkillsService.createSkillCategory({
|
||
id: categoryId,
|
||
name: categoryFromFile.category,
|
||
icon: categoryFromFile.icon,
|
||
});
|
||
}
|
||
|
||
// Synchroniser les skills de cette catégorie
|
||
for (const skillFromFile of categoryFromFile.skills) {
|
||
const existingSkill = existingSkillsMap.get(skillFromFile.id);
|
||
|
||
// Utiliser bulkInsertSkillsFromJSON qui gère les conflits automatiquement
|
||
const needsUpdate = existingSkill
|
||
? existingSkill.description !== skillFromFile.description ||
|
||
existingSkill.icon !== skillFromFile.icon ||
|
||
existingSkill.importance !== skillFromFile.importance ||
|
||
JSON.stringify(existingSkill.links?.sort()) !==
|
||
JSON.stringify(skillFromFile.links?.sort())
|
||
: true;
|
||
|
||
if (!existingSkill) {
|
||
console.log(
|
||
` ➕ Nouvelle skill: ${skillFromFile.name} (${skillFromFile.id})`
|
||
);
|
||
stats.newSkills++;
|
||
} else if (needsUpdate) {
|
||
console.log(
|
||
` 🔄 Mise à jour de la skill: ${skillFromFile.name} (${skillFromFile.id})`
|
||
);
|
||
stats.updatedSkills++;
|
||
} else {
|
||
console.log(
|
||
` ✅ Skill inchangée: ${skillFromFile.name} (${skillFromFile.id})`
|
||
);
|
||
stats.skippedSkills++;
|
||
}
|
||
|
||
// Insérer/mettre à jour la skill (bulkInsert gère les conflits)
|
||
await SkillsService.bulkInsertSkillsFromJSON([
|
||
{
|
||
id: categoryFromFile.category.toLowerCase(),
|
||
name: categoryFromFile.category,
|
||
category: categoryFromFile.category,
|
||
icon: categoryFromFile.icon,
|
||
skills: [skillFromFile],
|
||
},
|
||
]);
|
||
|
||
stats.skillsProcessed++;
|
||
}
|
||
|
||
stats.categoriesProcessed++;
|
||
}
|
||
|
||
// Afficher les statistiques finales
|
||
console.log("\n📊 Statistiques de synchronisation:");
|
||
console.log(` • Catégories traitées: ${stats.categoriesProcessed}`);
|
||
console.log(` • Skills traitées: ${stats.skillsProcessed}`);
|
||
console.log(` • Nouvelles skills: ${stats.newSkills}`);
|
||
console.log(` • Skills mises à jour: ${stats.updatedSkills}`);
|
||
console.log(` • Skills inchangées: ${stats.skippedSkills}`);
|
||
|
||
// Vérification finale
|
||
const finalCategories = await SkillsService.getSkillCategories();
|
||
const totalSkillsInDb = finalCategories.reduce(
|
||
(sum, cat) => sum + cat.skills.length,
|
||
0
|
||
);
|
||
|
||
console.log(`\n✅ Synchronisation terminée avec succès!`);
|
||
console.log(`💾 Total skills en base: ${totalSkillsInDb}`);
|
||
} catch (error) {
|
||
console.error("❌ Erreur lors de la synchronisation:", error);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
// Exécuter le script si appelé directement
|
||
if (require.main === module) {
|
||
syncSkillsToDatabase()
|
||
.then(() => {
|
||
console.log("🎉 Script terminé avec succès");
|
||
process.exit(0);
|
||
})
|
||
.catch((error) => {
|
||
console.error("💥 Erreur fatale:", error);
|
||
process.exit(1);
|
||
});
|
||
}
|
||
|
||
export { syncSkillsToDatabase };
|