Proposition : Connectors Hub - Actions Phase 1
Date : 28/01/2026 01:22
Status : IMPLEMENTEE
Priorité : HAUTE
Projet : connectors-api + connectors-front
Estimation : Phase 1 complète
Objectif
Implémenter le système Actions pour Connectors Hub :
- Actions = unités atomiques réutilisables (SSH ou API)
- Exécution manuelle avec paramètres
- Historique des exécutions
1. Schéma Base de Données
Table connectors.actions
CREATE TABLE connectors.actions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id),
-- Identification
name VARCHAR(100) NOT NULL,
description TEXT,
-- Type d'action
action_type VARCHAR(20) NOT NULL CHECK (action_type IN ('ssh', 'api')),
-- Référence connecteur/instance
connector_id UUID REFERENCES connectors.config(id), -- Pour API
instance_id UUID REFERENCES connectors.user_connectors(id), -- Pour SSH ou instance spécifique
-- Configuration (JSON selon type)
config JSONB NOT NULL,
-- Schémas I/O
input_schema JSONB, -- JSON Schema des paramètres attendus
output_schema JSONB, -- JSON Schema de la sortie
-- Metadata
is_public BOOLEAN DEFAULT false, -- Partageable avec autres users
tags TEXT[] DEFAULT '{}', -- Pour filtrage
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now(),
CONSTRAINT actions_user_name_unique UNIQUE (user_id, name)
);
-- Index pour recherche rapide
CREATE INDEX idx_actions_user ON connectors.actions(user_id);
CREATE INDEX idx_actions_type ON connectors.actions(action_type);
CREATE INDEX idx_actions_connector ON connectors.actions(connector_id);
CREATE INDEX idx_actions_instance ON connectors.actions(instance_id);
CREATE INDEX idx_actions_public ON connectors.actions(is_public) WHERE is_public = true;
Table connectors.action_executions
CREATE TABLE connectors.action_executions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
action_id UUID REFERENCES connectors.actions(id) ON DELETE CASCADE,
user_id UUID REFERENCES auth.users(id),
-- Paramètres d'entrée
input JSONB,
-- Résultat
output JSONB,
status VARCHAR(20) NOT NULL CHECK (status IN ('pending', 'running', 'success', 'error')),
error_message TEXT,
-- Timing
started_at TIMESTAMPTZ DEFAULT now(),
completed_at TIMESTAMPTZ,
duration_ms INTEGER,
-- Context
trigger_type VARCHAR(20) DEFAULT 'manual' CHECK (trigger_type IN ('manual', 'routine', 'webhook', 'cron')),
routine_execution_id UUID, -- Si déclenché par routine (Phase 2)
created_at TIMESTAMPTZ DEFAULT now()
);
-- Index pour historique
CREATE INDEX idx_action_exec_action ON connectors.action_executions(action_id, created_at DESC);
CREATE INDEX idx_action_exec_user ON connectors.action_executions(user_id, created_at DESC);
CREATE INDEX idx_action_exec_status ON connectors.action_executions(status);
Types de Configuration (JSONB)
Config SSH (action_type = 'ssh')
interface SSHActionConfig {
type: 'command' | 'script';
// Commande ou script
command?: string; // "df -h", "docker ps --format json"
script?: string; // Contenu script bash multi-lignes
// Options
sudo?: boolean;
timeout?: number; // ms, default 30000
working_dir?: string; // cd avant exécution
// Parsing output
output_parser: 'raw' | 'json' | 'lines' | 'table' | 'regex';
parser_config?: {
// Pour 'table'
columns?: string[]; // ["filesystem", "size", "used", "avail", "use%", "mount"]
separator?: string; // Default: whitespace
skip_header?: number; // Lignes à ignorer
// Pour 'regex'
pattern?: string; // Regex avec groupes nommés
// Pour 'json'
json_path?: string; // jq-like: ".data[].name"
};
}
Config API (action_type = 'api')
interface APIActionConfig {
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
path: string; // "/api/nodes/{node}/status"
// Paramètres avec placeholders {{param}}
path_params?: Record<string, string>; // Valeurs par défaut
query_params?: Record<string, any>;
body?: any;
headers?: Record<string, string>;
// Extraction output
output_path?: string; // "data.status" → extrait ce champ du JSON
}
2. Endpoints API
Service src/services/actions.ts
// CRUD Actions
list(userId: string, filters?: ActionFilters): Promise<Action[]>
get(actionId: string, userId: string): Promise<Action | null>
create(data: CreateActionInput, userId: string): Promise<Action>
update(actionId: string, data: UpdateActionInput, userId: string): Promise<Action>
delete(actionId: string, userId: string): Promise<void>
// Exécution
execute(actionId: string, input: Record<string, any>, userId: string): Promise<ActionExecution>
getHistory(actionId: string, userId: string, limit?: number): Promise<ActionExecution[]>
// Helpers
validateConfig(actionType: string, config: any): ValidationResult
parseOutput(output: string, parser: string, config: any): any
Routes src/index.ts
// Liste actions (user + public)
GET /api/actions
Query: ?type=ssh|api &connector_id=xxx &tags=tag1,tag2 &include_public=true
Response: Action[]
// Détails action
GET /api/actions/:id
Response: Action (avec config complète)
// Créer action
POST /api/actions
Body: {
name: string,
description?: string,
action_type: 'ssh' | 'api',
connector_id?: string, // Pour API
instance_id?: string, // Pour SSH ou instance spécifique
config: SSHActionConfig | APIActionConfig,
input_schema?: JSONSchema,
output_schema?: JSONSchema,
is_public?: boolean,
tags?: string[]
}
Response: Action
// Modifier action
PUT /api/actions/:id
Body: Partial<CreateActionInput>
Response: Action
// Supprimer action
DELETE /api/actions/:id
Response: { success: true }
// Exécuter action
POST /api/actions/:id/execute
Body: {
input?: Record<string, any> // Paramètres selon input_schema
}
Response: ActionExecution
// Historique exécutions
GET /api/actions/:id/history
Query: ?limit=20 &status=success|error
Response: ActionExecution[]
// Actions prédéfinies (templates)
GET /api/actions/templates
Response: ActionTemplate[] // disk-usage, docker-ps, etc.
// Créer depuis template
POST /api/actions/from-template
Body: {
template_id: string,
instance_id: string, // SSH instance à utiliser
name?: string // Override nom
}
Response: Action
3. Actions Prédéfinies (Templates)
SSH Templates
| Template ID |
Nom |
Commande |
Parser |
ssh-disk-usage |
Disk Usage |
df -h |
table (filesystem, size, used, avail, use%, mount) |
ssh-docker-ps |
Docker Containers |
docker ps --format json |
json |
ssh-docker-stats |
Docker Stats |
docker stats --no-stream --format json |
json |
ssh-top-snapshot |
System Load |
top -bn1 \| head -20 |
raw |
ssh-memory |
Memory Usage |
free -h |
table |
ssh-uptime |
Uptime |
uptime -p |
raw |
ssh-service-status |
Service Status |
systemctl status {{service}} --no-pager |
raw |
ssh-tail-logs |
Tail Logs |
tail -n {{lines}} {{path}} |
lines |
ssh-apt-update |
APT Update |
sudo apt update && sudo apt upgrade -y |
raw |
API Templates (par connecteur)
| Connecteur |
Template ID |
Path |
Method |
| Proxmox |
proxmox-node-status |
/api2/json/nodes/{node}/status |
GET |
| Proxmox |
proxmox-list-vms |
/api2/json/nodes/{node}/qemu |
GET |
| Jellyfin |
jellyfin-sessions |
/Sessions |
GET |
| Jellyfin |
jellyfin-scan-library |
/Library/Refresh |
POST |
| GitHub |
github-repos |
/user/repos |
GET |
| Portainer |
portainer-containers |
/endpoints/{id}/docker/containers/json |
GET |
4. Frontend - Routes & Pages
Structure
src/routes/actions/
├── index.tsx # Liste actions
├── new/index.tsx # Créer action
├── templates/index.tsx # Choisir template
└── [id]/
├── index.tsx # Détails + exécuter
├── edit/index.tsx # Modifier
└── history/index.tsx # Historique
Page Liste (/actions)
┌─────────────────────────────────────────────────────────────┐
│ Actions [+ Nouvelle] │
├─────────────────────────────────────────────────────────────┤
│ Filtres: [Type ▼] [Connecteur ▼] [Tags] [□ Inclure public] │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 🖥️ Disk Usage (prod-portainer) [▶ Run] │ │
│ │ SSH • df -h • Dernière exec: il y a 2h │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 🐳 Docker Containers (prod-portainer) [▶ Run] │ │
│ │ SSH • docker ps --format json • Jamais exécuté │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 📊 Proxmox Node Status (pve) [▶ Run] │ │
│ │ API • GET /api2/json/nodes/pve/status │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Page Créer (/actions/new)
┌─────────────────────────────────────────────────────────────┐
│ Nouvelle Action │
├─────────────────────────────────────────────────────────────┤
│ │
│ Nom* [_________________________________] │
│ Description [_________________________________] │
│ │
│ Type* (•) SSH ( ) API │
│ │
│ ─── Configuration SSH ─────────────────────────────── │
│ │
│ Instance* [prod-portainer-SSH ▼] │
│ │
│ Type (•) Commande ( ) Script │
│ │
│ Commande* [df -h_____________________________] │
│ │
│ [ ] Sudo │
│ Timeout [30000] ms │
│ │
│ Parser [table ▼] │
│ Colonnes [filesystem, size, used, avail, use%, mount] │
│ │
│ ─── Paramètres (optionnel) ────────────────────────── │
│ │
│ La commande peut contenir {{param}} pour paramétrage │
│ Ex: tail -n {{lines}} {{path}} │
│ │
│ [+ Ajouter paramètre] │
│ │
│ | Nom | Type | Défaut | Requis | │
│ |--------|--------|--------|--------| │
│ | lines | number | 100 | [ ] | │
│ | path | string | - | [x] | │
│ │
│ [Annuler] [Créer Action] │
└─────────────────────────────────────────────────────────────┘
Page Détails + Exécution (/actions/[id])
┌─────────────────────────────────────────────────────────────┐
│ 🖥️ Disk Usage [Modifier] [Supprimer]│
│ SSH sur prod-portainer │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Configuration │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ Commande: df -h │ │
│ │ Parser: table │ │
│ │ Colonnes: filesystem, size, used, avail, use%, mount │ │
│ │ Timeout: 30s │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Exécuter [▶ Run] │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ (Pas de paramètres requis) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Dernier résultat (il y a 5 min) ✓ │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ ┌────────────┬──────┬──────┬───────┬─────┬───────┐ │ │
│ │ │ Filesystem │ Size │ Used │ Avail │ Use%│ Mount │ │ │
│ │ ├────────────┼──────┼──────┼───────┼─────┼───────┤ │ │
│ │ │ /dev/sda1 │ 50G │ 32G │ 18G │ 64% │ / │ │ │
│ │ │ /dev/sdb1 │ 500G │ 320G │ 180G │ 64% │ /data │ │ │
│ │ └────────────┴──────┴──────┴───────┴─────┴───────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Historique [Voir tout →] │ │
│ ├─────────────────────────────────────────────────────┤ │
│ │ • il y a 5 min ✓ success 42ms │ │
│ │ • il y a 2h ✓ success 38ms │ │
│ │ • hier 14:30 ✗ error timeout │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
5. Composants Réutilisables
| Composant |
Usage |
ActionCard |
Card dans liste (nom, type, instance, bouton run) |
ActionForm |
Formulaire création/édition |
ActionExecutor |
Zone exécution avec params + bouton |
ActionResult |
Affichage résultat (table, json, raw) |
ActionHistory |
Liste exécutions avec status |
ParserConfigEditor |
Config du parser (colonnes, regex, etc.) |
InputSchemaEditor |
Définition paramètres dynamiques |
6. Plan d'Implémentation
Étape 1 : Base de données
- [ ] Migration SQL pour
actions et action_executions
- [ ] Tester création/lecture via Supabase Studio
Étape 2 : Service Backend
- [ ] Créer
src/services/actions.ts
- [ ] Implémenter CRUD (list, get, create, update, delete)
- [ ] Implémenter execute() avec support SSH et API
- [ ] Implémenter parsers (raw, json, lines, table, regex)
Étape 3 : Routes API
- [ ] Ajouter routes dans
src/index.ts
- [ ] Tester avec curl/Swagger
Étape 4 : Templates
- [ ] Créer
src/data/action-templates.ts
- [ ] Endpoint GET /api/actions/templates
- [ ] Endpoint POST /api/actions/from-template
Étape 5 : Frontend - Liste
- [ ] Route
/actions
- [ ] Composant
ActionCard
- [ ] Filtres (type, connecteur, tags)
Étape 6 : Frontend - Création
- [ ] Route
/actions/new
- [ ] Composant
ActionForm
- [ ] Support SSH + API configs
- [ ]
InputSchemaEditor pour paramètres
Étape 7 : Frontend - Détails & Exécution
- [ ] Route
/actions/[id]
- [ ] Composant
ActionExecutor
- [ ] Composant
ActionResult (parsers visuels)
- [ ] Composant
ActionHistory
Étape 8 : Frontend - Templates
- [ ] Route
/actions/templates
- [ ] Sélection template + instance
7. Sécurité
Validation commandes SSH
// Liste noire de commandes dangereuses
const FORBIDDEN_PATTERNS = [
/rm\s+(-rf?|--recursive)\s+\//, // rm -rf /
/mkfs/,
/dd\s+.*of=\/dev/,
/>\s*\/dev\/sd/,
/shutdown|reboot|halt|poweroff/,
/:(){ :|:& };:/, // Fork bomb
];
// Validation avant exécution
function validateCommand(cmd: string): boolean {
return !FORBIDDEN_PATTERNS.some(p => p.test(cmd));
}
Permissions
- Actions liées à
user_id (isolation)
- Actions publiques en lecture seule pour autres users
- Seul le créateur peut modifier/supprimer
- Exécution = vérifie accès à l'instance
8. Fichiers à Créer/Modifier
Backend (connectors-api)
| Fichier |
Action |
migrations/004_actions.sql |
Créer |
src/services/actions.ts |
Créer |
src/services/parsers.ts |
Créer |
src/data/action-templates.ts |
Créer |
src/types/action.ts |
Créer |
src/index.ts |
Modifier (ajouter routes) |
Frontend (connectors-front)
| Fichier |
Action |
src/routes/actions/index.tsx |
Créer |
src/routes/actions/new/index.tsx |
Créer |
src/routes/actions/templates/index.tsx |
Créer |
src/routes/actions/[id]/index.tsx |
Créer |
src/routes/actions/[id]/edit/index.tsx |
Créer |
src/routes/actions/[id]/history/index.tsx |
Créer |
src/components/actions/ActionCard.tsx |
Créer |
src/components/actions/ActionForm.tsx |
Créer |
src/components/actions/ActionExecutor.tsx |
Créer |
src/components/actions/ActionResult.tsx |
Créer |
src/components/actions/ActionHistory.tsx |
Créer |
src/lib/api.ts |
Modifier (ajouter endpoints) |
Validation
- [ ] Schéma DB validé
- [ ] Endpoints API validés
- [ ] Maquettes UI validées
- [ ] Plan d'implémentation validé