No description
  • JavaScript 86.8%
  • CSS 10%
  • HTML 3%
  • Dockerfile 0.2%
Find a file
2026-08-17 12:55:03 +02:00
__tests__ Uniformisation des tags 2026-06-05 00:33:48 +02:00
js Uniformisation des tags 2026-06-05 00:33:48 +02:00
.dockerignore feat: dockerisation v1.1.0 (Dockerfile, compose, CI multi-push) 2026-06-05 00:48:34 +02:00
.env.example Augmentation tu timeout + retry 2026-06-04 23:58:07 +02:00
.gitignore Ajout de la configuration de l'API key du provider depuis l'application 2026-06-04 22:50:31 +02:00
.gitlab-ci.yml chore: bump to 1.2.5 + dynamic CI version 2026-06-05 13:59:02 +02:00
.prettierrc refactoring complet + début d'application 2026-05-13 13:09:30 +02:00
ai.js modif nombre tags 2026-06-05 13:52:54 +02:00
aiConfig.js ajout du choix du model 2026-06-04 23:41:32 +02:00
aiModels.js Uniformisation des tags 2026-06-05 00:33:48 +02:00
DEPLOY_COOLIFY.md chore: bump to 1.2.5 + dynamic CI version 2026-06-05 13:59:02 +02:00
docker-compose.yml chore: bump to 1.2.5 + dynamic CI version 2026-06-05 13:59:02 +02:00
docker-entrypoint.sh fix: run entrypoint as root, drop to node via gosu 2026-06-05 09:50:44 +02:00
Dockerfile fix: run entrypoint as root, drop to node via gosu 2026-06-05 09:50:44 +02:00
eslint.config.mjs Suppression d'un tool 2026-05-13 14:33:17 +02:00
index.html Uniformisation des tags 2026-06-05 00:33:48 +02:00
LICENSE Add license 2026-05-15 09:33:01 +00:00
package-lock.json Ajout de test + maj du readme 2026-05-15 11:01:41 +02:00
package.json chore: bump to 1.2.5 + dynamic CI version 2026-06-05 13:59:02 +02:00
README.md chore: bump to 1.2.5 + dynamic CI version 2026-06-05 13:59:02 +02:00
RELEASE.md Workflow de release 2026-08-17 12:55:03 +02:00
save.json Uniformisation des tags 2026-06-05 00:33:48 +02:00
server.js Uniformisation des tags 2026-06-05 00:33:48 +02:00
style.css Uniformisation des tags 2026-06-05 00:33:48 +02:00
urlFetcher.js ajout du choix du model 2026-06-04 23:41:32 +02:00
vitest.config.js Ajout de test + maj du readme 2026-05-15 11:01:41 +02:00

Neuron

Gestionnaire de bookmarks personnel — une interface web simple pour organiser et retrouver vos outils et liens utiles.

Fonctionnalités

  • Affichage des outils sous forme de cartes stylisées avec tri par tags automatique
  • Ajout, modification et suppression d'outils (nom, URL, tags, description)
  • Recherche textuelle par nom ou tags (debounced 250ms)
  • Filtrage par tags (clic dans la sidebar, combinaison AND)
  • Filtre "Favoris uniquement"
  • Mode sélection multiple avec suppression groupée et annulation (undo)
  • Pagination (20 outils par page)
  • Thème clair / sombre (suit la préférence OS au premier chargement)
  • Persistance atomique des données dans un fichier JSON (écriture temp + rename)
  • Timeout automatique sur les requêtes réseau (5s)
  • Mise en file d'attente des sauvegardes (évite les races conditions)
  • Suggestion de tags par IA (optionnelle, via Ollama Cloud ou OpenRouter) avec détection des doublons
  • Uniformisation des tags : modale de nettoyage pour fusionner les tags similaires déjà présents
  • Icônes Lucide

Docker

L'image est publiée automatiquement à chaque release taguée vX.Y.Z sur deux registries :

Registry Image
GitLab Container Registry registry.gitlab.com/cortex-website/neuron-tool:X.Y.Z
Docker Hub primorion/neuron:X.Y.Z

Les tags latest sont également poussés sur les deux registries.

Avec docker-compose (recommandé)

Un docker-compose.yml est fourni à la racine. Pour lancer l'application :

docker compose up -d

Ouvrez http://localhost:3000 dans votre navigateur.

Les données (outils + configuration IA) sont persistées dans le volume nommé neuron-data.

Pour arrêter :

docker compose down

Avec docker run

Pull de l'image (exemple avec Docker Hub) :

docker pull primorion/neuron:latest

Puis :

docker run -d -p 3000:3000 -v neuron-data:/app/data --name neuron primorion/neuron:latest

Build local

docker build -t neuron:1.2.5 .
docker run -d -p 3000:3000 -v neuron-data:/app/data --name neuron neuron:1.2.5

Variables d'environnement

Voir la section Configuration par variables d'environnement pour la liste complète. En Docker, vous pouvez :

  • les passer via environment: dans docker-compose.yml
  • ou créer un fichier .env à côté du docker-compose.yml (lu automatiquement par Docker)

Les données utilisateur (clés IA saisies via l'interface, save.json) sont stockées dans /app/data à l'intérieur du conteneur — Montez un volume sur ce dossier pour les persister entre les redémarrages (le docker-compose.yml fourni le fait pour vous).

Prérequis

  • Node.js (version 18 ou supérieure)

Installation

git clone https://gitlab.com/cortex-website/neuron-tool.git
cd neuron-tool
npm install
npm start

Ouvrez http://localhost:3000 dans votre navigateur.

Suggestion de tags par IA (optionnel)

Dans la modale d'ajout/édition, un bouton Suggérer apparaît à côté du champ Tags dès que le nom de l'outil est rempli. Au clic, Neuron envoie le nom, l'URL et la description de l'outil à un fournisseur LLM, qui renvoie 3 à 6 tags. Les tags sont fusionnés (sans doublon) avec ceux déjà saisis.

Pour éviter la création de doublons (par exemple vps à côté de vps-hosting), Neuron envoie la liste des tags déjà utilisés dans le prompt système et applique un post-traitement qui remappe toute suggestion proche d'un tag existant vers ce dernier.

Fournisseurs supportés

Fournisseur Variable d'env Modèle par défaut Où obtenir la clé
Ollama Cloud OLLAMA_API_KEY gpt-oss:120b-cloud ollama.com/settings/keys
OpenRouter OPENROUTER_API_KEY openrouter/free openrouter.ai/settings/keys

Si les deux clés sont définies, Ollama Cloud est utilisé. Vous pouvez forcer un fournisseur via AI_PROVIDER=openrouter (ou ollama), et override le modèle via AI_MODEL=….

Configuration depuis l'interface (recommandé)

Cliquez sur l'icône ⚙️ en haut à droite. La modale Paramètres IA vous permet de :

  • Choisir le fournisseur (Ollama Cloud ou OpenRouter)
  • Choisir le modèle dans la liste déroulante (optionnel — "Défaut" utilise le modèle recommandé par le fournisseur ; la liste est récupérée dynamiquement depuis l'API du provider, mise en cache 5 min côté serveur)
  • Saisir votre clé d'API (8 caractères minimum, masquée par défaut, révélable au clic)
  • Tester immédiatement la connexion avec un appel réel au fournisseur
  • Supprimer la clé enregistrée

La clé est stockée dans ai-config.json à la racine du projet (même répertoire que save.json). Elle n'est jamais envoyée au navigateur — seul le booléen hasKey remonte via l'API. Vous pouvez ignorer ce fichier dans votre .gitignore.

Si une clé est définie à la fois via .env (ou variables d'environnement) et via l'interface, la clé saisie via l'interface prend toujours priorité. Pour revenir à la variable d'environnement, supprimez la clé dans l'interface ou effacez ai-config.json.

Configuration par variables d'environnement (avancé)

Au démarrage, server.js charge automatiquement un fichier .env à la racine s'il existe (uniquement pour les variables pas déjà définies dans l'environnement). Pour configurer :

cp .env.example .env
# Puis éditez .env

Les variables déjà présentes dans l'environnement (shell, env_file Docker, etc.) restent prioritaires sur le fichier .env. En Docker, vous pouvez donc utiliser indifféremment :

  • env_file: .env (équivalent, le fichier est lu par Docker avant le démarrage)
  • -e OPENROUTER_API_KEY=… (variable shell, priorité sur .env)
  • un .env à la racine monté dans le conteneur (lu par server.js au démarrage)

Toutes les variables :

Variable Description
OLLAMA_API_KEY Clé d'API Ollama Cloud
OPENROUTER_API_KEY Clé d'API OpenRouter
AI_PROVIDER Forcer le fournisseur (ollama ou openrouter)
AI_MODEL Override du modèle par défaut
AI_BASE_URL Override de l'URL de base (utile derrière un proxy OpenAI-compat. personnel)
AI_OLLAMA_MODELS_URL Override de l'endpoint de listing des modèles Ollama (défaut : https://ollama.com/api/tags)
AI_OPENROUTER_MODELS_URL Override de l'endpoint de listing des modèles OpenRouter (défaut : https://openrouter.ai/api/v1/models)
AI_CONFIG_PATH Chemin du fichier de config IA (défaut : ai-config.json à la racine)

Confidentialité : la clé d'API est lue uniquement côté serveur Node. Le navigateur ne reçoit jamais la clé ni l'URL du fournisseur. En revanche, chaque clic envoie le nom, l'URL et la description de l'outil au fournisseur choisi — ne l'activez pas pour des outils confidentiels.

Si aucune clé n'est définie (ni via .env, ni via l'interface), le bouton est toujours visible mais renvoie une erreur claire au clic ; l'application reste 100 % fonctionnelle sans IA.

Uniformisation des tags

Le bouton Uniformiser dans la sidebar (à côté de Réinitialiser) ouvre une modale qui :

  1. Charge la liste des tags via GET /api/tags/duplicates (ne nécessite aucune clé IA).
  2. Détecte les groupes de tags similaires selon trois heuristiques :
    • Préfixe / inclusion : vpsvps-hosting → groupe ["vps", "vps-hosting"]
    • Levenshtein ≤ 2 : vps-hostngvps-hosting (distance 1) → groupe
    • Exclusion : les tags de moins de 3 caractères (js) ne sont pas fusionnés entre eux pour éviter les faux positifs (js vs json)
  3. Pour chaque groupe, suggère automatiquement le tag canonique (le plus utilisé, ou le plus long en cas d'égalité) et laisse l'utilisateur surcharger via un menu déroulant.
  4. Au clic sur Fusionner, envoie { mapping: { ancien: nouveau, ... } } à POST /api/tags/normalize, qui réécrit tous les outils de manière atomique (save.json.tmp + rename) et déduplique les tags après fusion.

Scripts

npm start          # Démarrer le serveur
npm run dev        # Démarrer avec --watch (redémarrage auto)
npm run lint       # ESLint
npm run format     # Prettier
npm test           # Tests unitaires + intégration
npm run test:watch # Tests en mode watch

Structure

├── index.html          # Page principale
├── style.css           # Styles (thème, cartes, modale, responsive)
├── server.js           # Serveur Node.js (fichiers statiques + API REST)
├── ai.js               # Abstraction LLM (Ollama Cloud / OpenRouter)
├── aiConfig.js         # Persistance de la config IA (ai-config.json)
├── urlFetcher.js       # Fetch HTML côté serveur avec garde-fou SSRF
├── save.json           # Stockage persistant des outils
├── vitest.config.js    # Configuration des tests
├── .env.example        # Variables d'env documentées (copier en .env)
├── js/
│   ├── app.js          # Point d'entrée frontend
│   ├── store.js        # Gestion des données (CRUD, rollback, tags)
│   ├── api.js          # Client HTTP (appels fetch vers l'API)
│   ├── toast.js        # Notifications toast
│   ├── utils.js        # Utilitaires (validation, DOM, formatage)
│   └── components/
│       ├── ToolCard.js           # Composant carte outil
│       ├── ToolModal.js          # Composant modale d'ajout/édition
│       ├── SettingsModal.js      # Composant modale paramètres IA
│       └── UniformizeTagsModal.js # Composant modale d'uniformisation des tags
├── js/__tests__/          # Tests frontend (vitest + happy-dom)
│   ├── utils.test.js        # 19 tests — validation, formatage, DOM
│   ├── store.test.js        # 30 tests — CRUD, rollback, filtrage, tags, normalisation
│   ├── api.test.js          # 17 tests — appels HTTP mockés (CRUD + IA + tags)
│   ├── server.test.js       # 14 tests — serveur forké, CRUD complet
│   ├── ToolCard.test.js     # 9 tests — rendu, clics, sélection
│   ├── ToolModal.test.js    # 11 tests — ouverture, validation, soumission, suggestion IA
│   ├── SettingsModal.test.js # 35 tests — ouverture, save, clear, test, statut
│   ├── UniformizeTagsModal.test.js # 11 tests — modale d'uniformisation
│   └── toast.test.js        # 5 tests — affichage, action
└── __tests__/             # Tests serveur (vitest + node)
    ├── ai.test.js            # 49 tests — provider, prompt, normalisation, similarité
    ├── aiConfig.test.js      # 24 tests — load/save/clear, normalisation, statuts
    ├── urlFetcher.test.js    # 26 tests — extraction HTML, SSRF, limites
    ├── server-ai.test.js     # 17 tests — route /api/ai/suggest-tags (avec vocabulaire)
    ├── server-ai-config.test.js # 19 tests — routes /api/ai/config GET/PUT/DELETE
    └── server-tags.test.js   # 18 tests — /api/tags/duplicates et /api/tags/normalize

API REST

Méthode Route Description
GET /api/tools Récupérer tous les outils
POST /api/tools Créer un nouvel outil
PUT /api/tools Remplacer tous les outils (bulk)
PUT /api/tools/:id Mettre à jour un outil
DELETE /api/tools/:id Supprimer un outil
POST /api/ai/suggest-tags Suggérer 3-6 tags via LLM (optionnel) — utilise la liste de tags existants pour éviter les doublons
GET /api/ai/config Lire l'état de la config IA (source = ui, env, none) — la clé n'est jamais renvoyée
PUT /api/ai/config Enregistrer une clé ({ provider, apiKey, model? })
DELETE /api/ai/config Supprimer la clé UI (revient à l'env var ou "non configuré")
GET /api/ai/models?provider=… Lister les modèles disponibles pour un provider (utilisé par le dropdown de la modale ; cache 5 min côté serveur)
GET /api/tags/duplicates Détecter les groupes de tags similaires (préfixe, inclusion, Levenshtein ≤ 2)
POST /api/tags/normalize Fusionner des tags : body { "mapping": { "vps": "vps-hosting", ... } }. Réécrit tous les outils concernés de manière atomique.

Note sur la sécurité : L'API REST n'est pas authentifiée. Neuron est conçu pour un usage local uniquement. Ne l'exposez pas sur un réseau public. La route /api/ai/suggest-tags sort de ce cadre : si vous la configurez, elle envoie le nom/URL/description de l'outil à un fournisseur LLM tiers.

Détails techniques de l'API

  • Validation : Content-Type vérifié sur toutes les requêtes entrantes, taille du corps limitée à 1 Mo
  • Erreurs : Toutes les erreurs HTTP utilisent une classe RequestError avec un message et un code status cohérents
  • Écriture atomique : Les données sont d'abord écrites dans un fichier temporaire (save.json.tmp) puis déplacées, évitant la corruption

Thème clair/sombre

Le projet utilise la fonction CSS light-dark() pour définir les couleurs des deux thèmes dans une seule propriété. Les navigateurs qui ne supportent pas light-dark() (Safari) reçoivent un fallback explicite avec les valeurs du thème sombre.

Le thème initial respecte la préférence OS (prefers-color-scheme) via un script DOMContentLoaded qui coche la checkbox de thème avant le premier paint. Le changement de thème est géré par le sélecteur CSS :has() sans JavaScript.

Technologies

  • JavaScript vanilla (ES6 modules)
  • Node.js (module http natif)
  • CSS custom properties + light-dark() + :has()
  • Lucide (icônes)
  • vitest + happy-dom (tests)

Licence

MIT