feat: Implement comprehensive database schema with new models, CRUD operations, and documentation for host metrics, Docker management, and terminal sessions, while removing old test files.
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled

This commit is contained in:
2026-03-05 10:16:13 -05:00
parent 121aab9d42
commit 984d06a223
72 changed files with 3781 additions and 501 deletions
+716
View File
@@ -0,0 +1,716 @@
# Documentation du Modèle de Données (Base de Données)
Cette documentation a été générée automatiquement à partir des modèles SQLAlchemy.
## 1. Diagramme Entité-Association (ERD)
Le schéma ci-dessous montre l'architecture des tables et leurs relations :
```mermaid
erDiagram
alerts {
INTEGER id PK
INTEGER user_id FK "nullable"
VARCHAR category
VARCHAR level "nullable"
VARCHAR title "nullable"
TEXT message
VARCHAR source "nullable"
JSON details "nullable"
DATETIME read_at "nullable"
DATETIME created_at
}
app_settings {
VARCHAR key PK
TEXT value "nullable"
DATETIME created_at
DATETIME updated_at
}
bootstrap_status {
INTEGER id PK
VARCHAR host_id FK
VARCHAR status
VARCHAR automation_user "nullable"
DATETIME last_attempt "nullable"
TEXT error_message "nullable"
DATETIME created_at
}
container_customizations {
INTEGER id PK
INTEGER user_id FK "nullable"
VARCHAR host_id FK
VARCHAR container_id
VARCHAR icon_key "nullable"
VARCHAR icon_color "nullable"
VARCHAR bg_color "nullable"
DATETIME created_at
DATETIME updated_at
}
docker_alerts {
INTEGER id PK
VARCHAR host_id FK
VARCHAR container_name
VARCHAR severity
VARCHAR state
TEXT message "nullable"
DATETIME opened_at
DATETIME closed_at "nullable"
DATETIME acknowledged_at "nullable"
VARCHAR acknowledged_by "nullable"
DATETIME last_notified_at "nullable"
}
docker_containers {
INTEGER id PK
VARCHAR host_id FK
VARCHAR container_id
VARCHAR name
VARCHAR image "nullable"
VARCHAR state
VARCHAR status "nullable"
VARCHAR health "nullable"
DATETIME created_at "nullable"
JSON ports "nullable"
JSON labels "nullable"
VARCHAR compose_project "nullable"
DATETIME last_update_at
}
docker_images {
INTEGER id PK
VARCHAR host_id FK
VARCHAR image_id
JSON repo_tags "nullable"
BIGINT size "nullable"
DATETIME created "nullable"
DATETIME last_update_at
}
docker_volumes {
INTEGER id PK
VARCHAR host_id FK
VARCHAR name
VARCHAR driver "nullable"
TEXT mountpoint "nullable"
VARCHAR scope "nullable"
DATETIME last_update_at
}
favorite_containers {
INTEGER id PK
INTEGER user_id FK "nullable"
INTEGER docker_container_id FK
INTEGER group_id FK "nullable"
DATETIME created_at
}
favorite_groups {
INTEGER id PK
INTEGER user_id FK "nullable"
VARCHAR name
INTEGER sort_order
VARCHAR color "nullable"
VARCHAR icon_key "nullable"
DATETIME created_at
DATETIME updated_at
}
host_metrics {
INTEGER id PK
VARCHAR host_id FK
VARCHAR metric_type
INTEGER cpu_count "nullable"
VARCHAR cpu_model "nullable"
INTEGER cpu_cores "nullable"
INTEGER cpu_threads "nullable"
INTEGER cpu_threads_per_core "nullable"
INTEGER cpu_sockets "nullable"
FLOAT cpu_mhz "nullable"
FLOAT cpu_max_mhz "nullable"
FLOAT cpu_min_mhz "nullable"
FLOAT cpu_load_1m "nullable"
FLOAT cpu_load_5m "nullable"
FLOAT cpu_load_15m "nullable"
FLOAT cpu_usage_percent "nullable"
FLOAT cpu_temperature "nullable"
INTEGER memory_total_mb "nullable"
INTEGER memory_used_mb "nullable"
INTEGER memory_free_mb "nullable"
FLOAT memory_usage_percent "nullable"
INTEGER swap_total_mb "nullable"
INTEGER swap_used_mb "nullable"
FLOAT swap_usage_percent "nullable"
JSON disk_info "nullable"
JSON disk_devices
FLOAT disk_root_total_gb "nullable"
FLOAT disk_root_used_gb "nullable"
FLOAT disk_root_usage_percent "nullable"
JSON lvm_info "nullable"
JSON zfs_info "nullable"
JSON storage_details "nullable"
VARCHAR os_name "nullable"
VARCHAR os_version "nullable"
VARCHAR kernel_version "nullable"
VARCHAR hostname "nullable"
INTEGER uptime_seconds "nullable"
VARCHAR uptime_human "nullable"
JSON network_info "nullable"
JSON raw_data "nullable"
VARCHAR collection_source "nullable"
INTEGER collection_duration_ms "nullable"
TEXT error_message "nullable"
DATETIME collected_at
DATETIME created_at
}
hosts {
VARCHAR id PK
VARCHAR name
VARCHAR ip_address
VARCHAR status
VARCHAR ansible_group "nullable"
DATETIME last_seen "nullable"
BOOLEAN reachable
DATETIME created_at
DATETIME updated_at
DATETIME deleted_at "nullable"
BOOLEAN docker_enabled
VARCHAR docker_version "nullable"
VARCHAR docker_status "nullable"
DATETIME docker_last_collect_at "nullable"
}
logs {
INTEGER id PK
VARCHAR level
VARCHAR source "nullable"
TEXT message
JSON details "nullable"
VARCHAR host_id "nullable"
VARCHAR task_id "nullable"
VARCHAR schedule_id "nullable"
DATETIME created_at
}
playbook_lint_results {
INTEGER id PK
VARCHAR filename
INTEGER quality_score
INTEGER total_issues
INTEGER errors_count
INTEGER warnings_count
INTEGER execution_time_ms
TEXT issues_json "nullable"
TEXT raw_output "nullable"
DATETIME created_at
DATETIME updated_at
}
schedule_runs {
INTEGER id PK
VARCHAR schedule_id FK
VARCHAR task_id FK "nullable"
VARCHAR status
DATETIME started_at
DATETIME completed_at "nullable"
FLOAT duration "nullable"
INTEGER hosts_impacted "nullable"
TEXT error_message "nullable"
TEXT output "nullable"
DATETIME created_at
}
schedules {
VARCHAR id PK
VARCHAR name
TEXT description "nullable"
VARCHAR playbook
VARCHAR target_type "nullable"
VARCHAR target
JSON extra_vars "nullable"
VARCHAR schedule_type
DATETIME schedule_time "nullable"
VARCHAR recurrence_type "nullable"
VARCHAR recurrence_time "nullable"
TEXT recurrence_days "nullable"
VARCHAR cron_expression "nullable"
VARCHAR timezone "nullable"
DATETIME start_at "nullable"
DATETIME end_at "nullable"
BOOLEAN enabled
TEXT tags "nullable"
DATETIME next_run "nullable"
DATETIME last_run "nullable"
VARCHAR last_status "nullable"
INTEGER retry_on_failure "nullable"
INTEGER timeout "nullable"
VARCHAR notification_type "nullable"
INTEGER run_count "nullable"
INTEGER success_count "nullable"
INTEGER failure_count "nullable"
DATETIME created_at
DATETIME updated_at
DATETIME deleted_at "nullable"
}
tasks {
VARCHAR id PK
VARCHAR action
VARCHAR target
VARCHAR status
VARCHAR playbook "nullable"
DATETIME started_at "nullable"
DATETIME completed_at "nullable"
TEXT error_message "nullable"
JSON result_data "nullable"
DATETIME created_at
}
terminal_command_logs {
INTEGER id PK
DATETIME created_at
VARCHAR host_id FK
INTEGER user_id FK "nullable"
VARCHAR terminal_session_id "nullable"
TEXT command
VARCHAR command_hash
VARCHAR source
BOOLEAN is_pinned
BOOLEAN is_blocked
VARCHAR blocked_reason "nullable"
VARCHAR username "nullable"
VARCHAR host_name "nullable"
}
terminal_sessions {
VARCHAR id PK
VARCHAR host_id
VARCHAR host_name
VARCHAR host_ip
INTEGER user_id "nullable"
VARCHAR username "nullable"
VARCHAR token_hash
INTEGER ttyd_port
INTEGER ttyd_pid "nullable"
VARCHAR mode
VARCHAR status
VARCHAR reason_closed "nullable"
DATETIME created_at
DATETIME last_seen_at
DATETIME expires_at
DATETIME closed_at "nullable"
}
users {
INTEGER id PK
VARCHAR username
VARCHAR email "nullable"
VARCHAR hashed_password
VARCHAR role
BOOLEAN is_active
BOOLEAN is_superuser
VARCHAR display_name "nullable"
DATETIME created_at
DATETIME updated_at
DATETIME last_login "nullable"
DATETIME password_changed_at "nullable"
DATETIME deleted_at "nullable"
}
users ||--o{ alerts : "user_id"
hosts ||--o{ bootstrap_status : "host_id"
users ||--o{ container_customizations : "user_id"
hosts ||--o{ container_customizations : "host_id"
hosts ||--o{ docker_alerts : "host_id"
hosts ||--o{ docker_containers : "host_id"
hosts ||--o{ docker_images : "host_id"
hosts ||--o{ docker_volumes : "host_id"
users ||--o{ favorite_containers : "user_id"
docker_containers ||--o{ favorite_containers : "docker_container_id"
favorite_groups ||--o{ favorite_containers : "group_id"
users ||--o{ favorite_groups : "user_id"
hosts ||--o{ host_metrics : "host_id"
schedules ||--o{ schedule_runs : "schedule_id"
tasks ||--o{ schedule_runs : "task_id"
hosts ||--o{ terminal_command_logs : "host_id"
users ||--o{ terminal_command_logs : "user_id"
```
## 2. Dictionnaire de Données (Tables)
### Table : `alerts`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `user_id` | `INTEGER` | - | 🔗 `users.id` | Oui | - |
| `category` | `VARCHAR(50)` | - | - | **Non** | - |
| `level` | `VARCHAR(20)` | - | - | Oui | - |
| `title` | `VARCHAR(255)` | - | - | Oui | - |
| `message` | `TEXT` | - | - | **Non** | - |
| `source` | `VARCHAR(50)` | - | - | Oui | - |
| `details` | `JSON` | - | - | Oui | - |
| `read_at` | `DATETIME` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `app_settings`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `key` | `VARCHAR(100)` | ✅ Oui | - | **Non** | - |
| `value` | `TEXT` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
| `updated_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `bootstrap_status`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `host_id` | `VARCHAR(50)` | - | 🔗 `hosts.id` | **Non** | - |
| `status` | `VARCHAR(50)` | - | - | **Non** | - |
| `automation_user` | `VARCHAR(50)` | - | - | Oui | - |
| `last_attempt` | `DATETIME` | - | - | Oui | - |
| `error_message` | `TEXT` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `container_customizations`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `user_id` | `INTEGER` | - | 🔗 `users.id` | Oui | - |
| `host_id` | `VARCHAR(50)` | - | 🔗 `hosts.id` | **Non** | - |
| `container_id` | `VARCHAR(64)` | - | - | **Non** | - |
| `icon_key` | `VARCHAR(100)` | - | - | Oui | - |
| `icon_color` | `VARCHAR(20)` | - | - | Oui | - |
| `bg_color` | `VARCHAR(20)` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
| `updated_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `docker_alerts`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `host_id` | `VARCHAR(50)` | - | 🔗 `hosts.id` | **Non** | - |
| `container_name` | `VARCHAR(255)` | - | - | **Non** | - |
| `severity` | `VARCHAR(20)` | - | - | **Non** | `warning` |
| `state` | `VARCHAR(20)` | - | - | **Non** | `open` |
| `message` | `TEXT` | - | - | Oui | - |
| `opened_at` | `DATETIME` | - | - | **Non** | `now()` |
| `closed_at` | `DATETIME` | - | - | Oui | - |
| `acknowledged_at` | `DATETIME` | - | - | Oui | - |
| `acknowledged_by` | `VARCHAR(100)` | - | - | Oui | - |
| `last_notified_at` | `DATETIME` | - | - | Oui | - |
### Table : `docker_containers`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `host_id` | `VARCHAR(50)` | - | 🔗 `hosts.id` | **Non** | - |
| `container_id` | `VARCHAR(64)` | - | - | **Non** | - |
| `name` | `VARCHAR(255)` | - | - | **Non** | - |
| `image` | `VARCHAR(255)` | - | - | Oui | - |
| `state` | `VARCHAR(20)` | - | - | **Non** | `unknown` |
| `status` | `VARCHAR(255)` | - | - | Oui | - |
| `health` | `VARCHAR(20)` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | Oui | - |
| `ports` | `JSON` | - | - | Oui | - |
| `labels` | `JSON` | - | - | Oui | - |
| `compose_project` | `VARCHAR(255)` | - | - | Oui | - |
| `last_update_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `docker_images`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `host_id` | `VARCHAR(50)` | - | 🔗 `hosts.id` | **Non** | - |
| `image_id` | `VARCHAR(64)` | - | - | **Non** | - |
| `repo_tags` | `JSON` | - | - | Oui | - |
| `size` | `BIGINT` | - | - | Oui | - |
| `created` | `DATETIME` | - | - | Oui | - |
| `last_update_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `docker_volumes`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `host_id` | `VARCHAR(50)` | - | 🔗 `hosts.id` | **Non** | - |
| `name` | `VARCHAR(255)` | - | - | **Non** | - |
| `driver` | `VARCHAR(50)` | - | - | Oui | - |
| `mountpoint` | `TEXT` | - | - | Oui | - |
| `scope` | `VARCHAR(20)` | - | - | Oui | - |
| `last_update_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `favorite_containers`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `user_id` | `INTEGER` | - | 🔗 `users.id` | Oui | - |
| `docker_container_id` | `INTEGER` | - | 🔗 `docker_containers.id` | **Non** | - |
| `group_id` | `INTEGER` | - | 🔗 `favorite_groups.id` | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `favorite_groups`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `user_id` | `INTEGER` | - | 🔗 `users.id` | Oui | - |
| `name` | `VARCHAR(100)` | - | - | **Non** | - |
| `sort_order` | `INTEGER` | - | - | **Non** | `0` |
| `color` | `VARCHAR(20)` | - | - | Oui | - |
| `icon_key` | `VARCHAR(100)` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
| `updated_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `host_metrics`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `host_id` | `VARCHAR(50)` | - | 🔗 `hosts.id` | **Non** | - |
| `metric_type` | `VARCHAR(50)` | - | - | **Non** | - |
| `cpu_count` | `INTEGER` | - | - | Oui | - |
| `cpu_model` | `VARCHAR(200)` | - | - | Oui | - |
| `cpu_cores` | `INTEGER` | - | - | Oui | - |
| `cpu_threads` | `INTEGER` | - | - | Oui | - |
| `cpu_threads_per_core` | `INTEGER` | - | - | Oui | - |
| `cpu_sockets` | `INTEGER` | - | - | Oui | - |
| `cpu_mhz` | `FLOAT` | - | - | Oui | - |
| `cpu_max_mhz` | `FLOAT` | - | - | Oui | - |
| `cpu_min_mhz` | `FLOAT` | - | - | Oui | - |
| `cpu_load_1m` | `FLOAT` | - | - | Oui | - |
| `cpu_load_5m` | `FLOAT` | - | - | Oui | - |
| `cpu_load_15m` | `FLOAT` | - | - | Oui | - |
| `cpu_usage_percent` | `FLOAT` | - | - | Oui | - |
| `cpu_temperature` | `FLOAT` | - | - | Oui | - |
| `memory_total_mb` | `INTEGER` | - | - | Oui | - |
| `memory_used_mb` | `INTEGER` | - | - | Oui | - |
| `memory_free_mb` | `INTEGER` | - | - | Oui | - |
| `memory_usage_percent` | `FLOAT` | - | - | Oui | - |
| `swap_total_mb` | `INTEGER` | - | - | Oui | - |
| `swap_used_mb` | `INTEGER` | - | - | Oui | - |
| `swap_usage_percent` | `FLOAT` | - | - | Oui | - |
| `disk_info` | `JSON` | - | - | Oui | - |
| `disk_devices` | `JSON` | - | - | **Non** | - |
| `disk_root_total_gb` | `FLOAT` | - | - | Oui | - |
| `disk_root_used_gb` | `FLOAT` | - | - | Oui | - |
| `disk_root_usage_percent` | `FLOAT` | - | - | Oui | - |
| `lvm_info` | `JSON` | - | - | Oui | - |
| `zfs_info` | `JSON` | - | - | Oui | - |
| `storage_details` | `JSON` | - | - | Oui | - |
| `os_name` | `VARCHAR(100)` | - | - | Oui | - |
| `os_version` | `VARCHAR(100)` | - | - | Oui | - |
| `kernel_version` | `VARCHAR(100)` | - | - | Oui | - |
| `hostname` | `VARCHAR(200)` | - | - | Oui | - |
| `uptime_seconds` | `INTEGER` | - | - | Oui | - |
| `uptime_human` | `VARCHAR(100)` | - | - | Oui | - |
| `network_info` | `JSON` | - | - | Oui | - |
| `raw_data` | `JSON` | - | - | Oui | - |
| `collection_source` | `VARCHAR(100)` | - | - | Oui | - |
| `collection_duration_ms` | `INTEGER` | - | - | Oui | - |
| `error_message` | `TEXT` | - | - | Oui | - |
| `collected_at` | `DATETIME` | - | - | **Non** | `now()` |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `hosts`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `VARCHAR(50)` | ✅ Oui | - | **Non** | - |
| `name` | `VARCHAR(255)` | - | - | **Non** | - |
| `ip_address` | `VARCHAR(50)` | - | - | **Non** | - |
| `status` | `VARCHAR(50)` | - | - | **Non** | `'unknown'` |
| `ansible_group` | `VARCHAR(50)` | - | - | Oui | - |
| `last_seen` | `DATETIME` | - | - | Oui | - |
| `reachable` | `BOOLEAN` | - | - | **Non** | `0` |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
| `updated_at` | `DATETIME` | - | - | **Non** | `now()` |
| `deleted_at` | `DATETIME` | - | - | Oui | - |
| `docker_enabled` | `BOOLEAN` | - | - | **Non** | `0` |
| `docker_version` | `VARCHAR(50)` | - | - | Oui | - |
| `docker_status` | `VARCHAR(20)` | - | - | Oui | - |
| `docker_last_collect_at` | `DATETIME` | - | - | Oui | - |
### Table : `logs`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `level` | `VARCHAR(50)` | - | - | **Non** | - |
| `source` | `VARCHAR(255)` | - | - | Oui | - |
| `message` | `TEXT` | - | - | **Non** | - |
| `details` | `JSON` | - | - | Oui | - |
| `host_id` | `VARCHAR(50)` | - | - | Oui | - |
| `task_id` | `VARCHAR(50)` | - | - | Oui | - |
| `schedule_id` | `VARCHAR(50)` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `playbook_lint_results`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `filename` | `VARCHAR(255)` | - | - | **Non** | - |
| `quality_score` | `INTEGER` | - | - | **Non** | `100` |
| `total_issues` | `INTEGER` | - | - | **Non** | `0` |
| `errors_count` | `INTEGER` | - | - | **Non** | `0` |
| `warnings_count` | `INTEGER` | - | - | **Non** | `0` |
| `execution_time_ms` | `INTEGER` | - | - | **Non** | `0` |
| `issues_json` | `TEXT` | - | - | Oui | - |
| `raw_output` | `TEXT` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | *(auto generate)* |
| `updated_at` | `DATETIME` | - | - | **Non** | *(auto generate)* |
### Table : `schedule_runs`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `schedule_id` | `VARCHAR(50)` | - | 🔗 `schedules.id` | **Non** | - |
| `task_id` | `VARCHAR(50)` | - | 🔗 `tasks.id` | Oui | - |
| `status` | `VARCHAR(50)` | - | - | **Non** | - |
| `started_at` | `DATETIME` | - | - | **Non** | - |
| `completed_at` | `DATETIME` | - | - | Oui | - |
| `duration` | `FLOAT` | - | - | Oui | - |
| `hosts_impacted` | `INTEGER` | - | - | Oui | `0` |
| `error_message` | `TEXT` | - | - | Oui | - |
| `output` | `TEXT` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `schedules`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `VARCHAR(50)` | ✅ Oui | - | **Non** | - |
| `name` | `VARCHAR(255)` | - | - | **Non** | - |
| `description` | `TEXT` | - | - | Oui | - |
| `playbook` | `VARCHAR(255)` | - | - | **Non** | - |
| `target_type` | `VARCHAR(50)` | - | - | Oui | `group` |
| `target` | `VARCHAR(255)` | - | - | **Non** | - |
| `extra_vars` | `JSON` | - | - | Oui | - |
| `schedule_type` | `VARCHAR(50)` | - | - | **Non** | - |
| `schedule_time` | `DATETIME` | - | - | Oui | - |
| `recurrence_type` | `VARCHAR(50)` | - | - | Oui | - |
| `recurrence_time` | `VARCHAR(50)` | - | - | Oui | - |
| `recurrence_days` | `TEXT` | - | - | Oui | - |
| `cron_expression` | `VARCHAR(100)` | - | - | Oui | - |
| `timezone` | `VARCHAR(100)` | - | - | Oui | `America/Montreal` |
| `start_at` | `DATETIME` | - | - | Oui | - |
| `end_at` | `DATETIME` | - | - | Oui | - |
| `enabled` | `BOOLEAN` | - | - | **Non** | `True` |
| `tags` | `TEXT` | - | - | Oui | - |
| `next_run` | `DATETIME` | - | - | Oui | - |
| `last_run` | `DATETIME` | - | - | Oui | - |
| `last_status` | `VARCHAR(50)` | - | - | Oui | `never` |
| `retry_on_failure` | `INTEGER` | - | - | Oui | `0` |
| `timeout` | `INTEGER` | - | - | Oui | `3600` |
| `notification_type` | `VARCHAR(50)` | - | - | Oui | `all` |
| `run_count` | `INTEGER` | - | - | Oui | `0` |
| `success_count` | `INTEGER` | - | - | Oui | `0` |
| `failure_count` | `INTEGER` | - | - | Oui | `0` |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
| `updated_at` | `DATETIME` | - | - | **Non** | `now()` |
| `deleted_at` | `DATETIME` | - | - | Oui | - |
### Table : `tasks`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `VARCHAR(50)` | ✅ Oui | - | **Non** | - |
| `action` | `VARCHAR(100)` | - | - | **Non** | - |
| `target` | `VARCHAR(255)` | - | - | **Non** | - |
| `status` | `VARCHAR(50)` | - | - | **Non** | `'pending'` |
| `playbook` | `VARCHAR(255)` | - | - | Oui | - |
| `started_at` | `DATETIME` | - | - | Oui | - |
| `completed_at` | `DATETIME` | - | - | Oui | - |
| `error_message` | `TEXT` | - | - | Oui | - |
| `result_data` | `JSON` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
### Table : `terminal_command_logs`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
| `host_id` | `VARCHAR(50)` | - | 🔗 `hosts.id` | **Non** | - |
| `user_id` | `INTEGER` | - | 🔗 `users.id` | Oui | - |
| `terminal_session_id` | `VARCHAR(64)` | - | - | Oui | - |
| `command` | `TEXT` | - | - | **Non** | - |
| `command_hash` | `VARCHAR(64)` | - | - | **Non** | - |
| `source` | `VARCHAR(20)` | - | - | **Non** | `'terminal'` |
| `is_pinned` | `BOOLEAN` | - | - | **Non** | `0` |
| `is_blocked` | `BOOLEAN` | - | - | **Non** | `0` |
| `blocked_reason` | `VARCHAR(255)` | - | - | Oui | - |
| `username` | `VARCHAR(100)` | - | - | Oui | - |
| `host_name` | `VARCHAR(100)` | - | - | Oui | - |
### Table : `terminal_sessions`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `VARCHAR(50)` | ✅ Oui | - | **Non** | - |
| `host_id` | `VARCHAR(50)` | - | - | **Non** | - |
| `host_name` | `VARCHAR(255)` | - | - | **Non** | - |
| `host_ip` | `VARCHAR(50)` | - | - | **Non** | - |
| `user_id` | `INTEGER` | - | - | Oui | - |
| `username` | `VARCHAR(50)` | - | - | Oui | - |
| `token_hash` | `VARCHAR(128)` | - | - | **Non** | - |
| `ttyd_port` | `INTEGER` | - | - | **Non** | - |
| `ttyd_pid` | `INTEGER` | - | - | Oui | - |
| `mode` | `VARCHAR(20)` | - | - | **Non** | `'embedded'` |
| `status` | `VARCHAR(20)` | - | - | **Non** | `'active'` |
| `reason_closed` | `VARCHAR(30)` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
| `last_seen_at` | `DATETIME` | - | - | **Non** | `now()` |
| `expires_at` | `DATETIME` | - | - | **Non** | - |
| `closed_at` | `DATETIME` | - | - | Oui | - |
### Table : `users`
| Colonne | Type | Clé Primaire | Clé Étrangère | Nullable | Défaut |
|---------|------|--------------|---------------|----------|--------|
| `id` | `INTEGER` | ✅ Oui | - | **Non** | - |
| `username` | `VARCHAR(50)` | - | - | **Non** | - |
| `email` | `VARCHAR(255)` | - | - | Oui | - |
| `hashed_password` | `VARCHAR(255)` | - | - | **Non** | - |
| `role` | `VARCHAR(20)` | - | - | **Non** | `'admin'` |
| `is_active` | `BOOLEAN` | - | - | **Non** | `1` |
| `is_superuser` | `BOOLEAN` | - | - | **Non** | `0` |
| `display_name` | `VARCHAR(100)` | - | - | Oui | - |
| `created_at` | `DATETIME` | - | - | **Non** | `now()` |
| `updated_at` | `DATETIME` | - | - | **Non** | `now()` |
| `last_login` | `DATETIME` | - | - | Oui | - |
| `password_changed_at` | `DATETIME` | - | - | Oui | - |
| `deleted_at` | `DATETIME` | - | - | Oui | - |
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 676 KiB

+655
View File
@@ -0,0 +1,655 @@
import React, { useState, useMemo } from "react";
const SCHEMA = {
alerts: {
group: "Notifications",
description: "Alertes système générées pour les utilisateurs",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "user_id", type: "INTEGER", pk: false, fk: "users.id", nullable: true, default: null },
{ name: "category", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: null },
{ name: "level", type: "VARCHAR(20)", pk: false, fk: null, nullable: true, default: null },
{ name: "title", type: "VARCHAR(255)", pk: false, fk: null, nullable: true, default: null },
{ name: "message", type: "TEXT", pk: false, fk: null, nullable: false, default: null },
{ name: "source", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "details", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "read_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
app_settings: {
group: "Système",
description: "Paramètres globaux de l'application",
columns: [
{ name: "key", type: "VARCHAR(100)", pk: true, fk: null, nullable: false, default: null },
{ name: "value", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "updated_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
bootstrap_status: {
group: "Hôtes",
description: "État du bootstrapping Ansible par hôte",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "host_id", type: "VARCHAR(50)", pk: false, fk: "hosts.id", nullable: false, default: null },
{ name: "status", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: null },
{ name: "automation_user", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "last_attempt", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "error_message", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
container_customizations: {
group: "Docker",
description: "Personnalisations visuelles de conteneurs par utilisateur",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "user_id", type: "INTEGER", pk: false, fk: "users.id", nullable: true, default: null },
{ name: "host_id", type: "VARCHAR(50)", pk: false, fk: "hosts.id", nullable: false, default: null },
{ name: "container_id", type: "VARCHAR(64)", pk: false, fk: null, nullable: false, default: null },
{ name: "icon_key", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "icon_color", type: "VARCHAR(20)", pk: false, fk: null, nullable: true, default: null },
{ name: "bg_color", type: "VARCHAR(20)", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "updated_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
docker_alerts: {
group: "Docker",
description: "Alertes liées aux conteneurs Docker",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "host_id", type: "VARCHAR(50)", pk: false, fk: "hosts.id", nullable: false, default: null },
{ name: "container_name", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "severity", type: "VARCHAR(20)", pk: false, fk: null, nullable: false, default: "warning" },
{ name: "state", type: "VARCHAR(20)", pk: false, fk: null, nullable: false, default: "open" },
{ name: "message", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "opened_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "closed_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "acknowledged_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "acknowledged_by", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "last_notified_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
]
},
docker_containers: {
group: "Docker",
description: "Inventaire des conteneurs Docker par hôte",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "host_id", type: "VARCHAR(50)", pk: false, fk: "hosts.id", nullable: false, default: null },
{ name: "container_id", type: "VARCHAR(64)", pk: false, fk: null, nullable: false, default: null },
{ name: "name", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "image", type: "VARCHAR(255)", pk: false, fk: null, nullable: true, default: null },
{ name: "state", type: "VARCHAR(20)", pk: false, fk: null, nullable: false, default: "unknown" },
{ name: "status", type: "VARCHAR(255)", pk: false, fk: null, nullable: true, default: null },
{ name: "health", type: "VARCHAR(20)", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "ports", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "labels", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "compose_project", type: "VARCHAR(255)", pk: false, fk: null, nullable: true, default: null },
{ name: "last_update_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
docker_images: {
group: "Docker",
description: "Images Docker disponibles sur les hôtes",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "host_id", type: "VARCHAR(50)", pk: false, fk: "hosts.id", nullable: false, default: null },
{ name: "image_id", type: "VARCHAR(64)", pk: false, fk: null, nullable: false, default: null },
{ name: "repo_tags", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "size", type: "BIGINT", pk: false, fk: null, nullable: true, default: null },
{ name: "created", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "last_update_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
docker_volumes: {
group: "Docker",
description: "Volumes Docker par hôte",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "host_id", type: "VARCHAR(50)", pk: false, fk: "hosts.id", nullable: false, default: null },
{ name: "name", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "driver", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "mountpoint", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "scope", type: "VARCHAR(20)", pk: false, fk: null, nullable: true, default: null },
{ name: "last_update_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
favorite_containers: {
group: "Utilisateurs",
description: "Conteneurs favoris par utilisateur et groupe",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "user_id", type: "INTEGER", pk: false, fk: "users.id", nullable: true, default: null },
{ name: "docker_container_id", type: "INTEGER", pk: false, fk: "docker_containers.id", nullable: false, default: null },
{ name: "group_id", type: "INTEGER", pk: false, fk: "favorite_groups.id", nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
favorite_groups: {
group: "Utilisateurs",
description: "Groupes de favoris définis par l'utilisateur",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "user_id", type: "INTEGER", pk: false, fk: "users.id", nullable: true, default: null },
{ name: "name", type: "VARCHAR(100)", pk: false, fk: null, nullable: false, default: null },
{ name: "sort_order", type: "INTEGER", pk: false, fk: null, nullable: false, default: "0" },
{ name: "color", type: "VARCHAR(20)", pk: false, fk: null, nullable: true, default: null },
{ name: "icon_key", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "updated_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
host_metrics: {
group: "Hôtes",
description: "Métriques système collectées par hôte (CPU, RAM, disque, réseau…)",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "host_id", type: "VARCHAR(50)", pk: false, fk: "hosts.id", nullable: false, default: null },
{ name: "metric_type", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: null },
{ name: "cpu_count", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_model", type: "VARCHAR(200)", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_cores", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_threads", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_threads_per_core", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_sockets", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_mhz", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_max_mhz", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_min_mhz", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_load_1m", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_load_5m", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_load_15m", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_usage_percent", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "cpu_temperature", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "memory_total_mb", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "memory_used_mb", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "memory_free_mb", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "memory_usage_percent", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "swap_total_mb", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "swap_used_mb", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "swap_usage_percent", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "disk_info", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "disk_devices", type: "JSON", pk: false, fk: null, nullable: false, default: null },
{ name: "disk_root_total_gb", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "disk_root_used_gb", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "disk_root_usage_percent", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "lvm_info", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "zfs_info", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "storage_details", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "os_name", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "os_version", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "kernel_version", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "hostname", type: "VARCHAR(200)", pk: false, fk: null, nullable: true, default: null },
{ name: "uptime_seconds", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "uptime_human", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "network_info", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "raw_data", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "collection_source", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "collection_duration_ms", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "error_message", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "collected_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
hosts: {
group: "Hôtes",
description: "Inventaire des serveurs/hôtes gérés",
columns: [
{ name: "id", type: "VARCHAR(50)", pk: true, fk: null, nullable: false, default: null },
{ name: "name", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "ip_address", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: null },
{ name: "status", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: "'unknown'" },
{ name: "ansible_group", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "last_seen", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "reachable", type: "BOOLEAN", pk: false, fk: null, nullable: false, default: "0" },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "updated_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "deleted_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "docker_enabled", type: "BOOLEAN", pk: false, fk: null, nullable: false, default: "0" },
{ name: "docker_version", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "docker_status", type: "VARCHAR(20)", pk: false, fk: null, nullable: true, default: null },
{ name: "docker_last_collect_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
]
},
logs: {
group: "Système",
description: "Journal applicatif général",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "level", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: null },
{ name: "source", type: "VARCHAR(255)", pk: false, fk: null, nullable: true, default: null },
{ name: "message", type: "TEXT", pk: false, fk: null, nullable: false, default: null },
{ name: "details", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "host_id", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "task_id", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "schedule_id", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
playbook_lint_results: {
group: "Ansible",
description: "Résultats d'analyse de qualité des playbooks Ansible",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "filename", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "quality_score", type: "INTEGER", pk: false, fk: null, nullable: false, default: "100" },
{ name: "total_issues", type: "INTEGER", pk: false, fk: null, nullable: false, default: "0" },
{ name: "errors_count", type: "INTEGER", pk: false, fk: null, nullable: false, default: "0" },
{ name: "warnings_count", type: "INTEGER", pk: false, fk: null, nullable: false, default: "0" },
{ name: "execution_time_ms", type: "INTEGER", pk: false, fk: null, nullable: false, default: "0" },
{ name: "issues_json", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "raw_output", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "auto" },
{ name: "updated_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "auto" },
]
},
schedule_runs: {
group: "Ansible",
description: "Historique des exécutions de planifications",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "schedule_id", type: "VARCHAR(50)", pk: false, fk: "schedules.id", nullable: false, default: null },
{ name: "task_id", type: "VARCHAR(50)", pk: false, fk: "tasks.id", nullable: true, default: null },
{ name: "status", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: null },
{ name: "started_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: null },
{ name: "completed_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "duration", type: "FLOAT", pk: false, fk: null, nullable: true, default: null },
{ name: "hosts_impacted", type: "INTEGER", pk: false, fk: null, nullable: true, default: "0" },
{ name: "error_message", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "output", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
schedules: {
group: "Ansible",
description: "Planifications d'exécution de playbooks",
columns: [
{ name: "id", type: "VARCHAR(50)", pk: true, fk: null, nullable: false, default: null },
{ name: "name", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "description", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "playbook", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "target_type", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: "group" },
{ name: "target", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "extra_vars", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "schedule_type", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: null },
{ name: "schedule_time", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "recurrence_type", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "recurrence_time", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "recurrence_days", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "cron_expression", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "timezone", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: "America/Montreal" },
{ name: "start_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "end_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "enabled", type: "BOOLEAN", pk: false, fk: null, nullable: false, default: "True" },
{ name: "tags", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "next_run", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "last_run", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "last_status", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: "never" },
{ name: "retry_on_failure", type: "INTEGER", pk: false, fk: null, nullable: true, default: "0" },
{ name: "timeout", type: "INTEGER", pk: false, fk: null, nullable: true, default: "3600" },
{ name: "notification_type", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: "all" },
{ name: "run_count", type: "INTEGER", pk: false, fk: null, nullable: true, default: "0" },
{ name: "success_count", type: "INTEGER", pk: false, fk: null, nullable: true, default: "0" },
{ name: "failure_count", type: "INTEGER", pk: false, fk: null, nullable: true, default: "0" },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "updated_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "deleted_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
]
},
tasks: {
group: "Ansible",
description: "Tâches Ansible déclenchées manuellement ou via planification",
columns: [
{ name: "id", type: "VARCHAR(50)", pk: true, fk: null, nullable: false, default: null },
{ name: "action", type: "VARCHAR(100)", pk: false, fk: null, nullable: false, default: null },
{ name: "target", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "status", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: "'pending'" },
{ name: "playbook", type: "VARCHAR(255)", pk: false, fk: null, nullable: true, default: null },
{ name: "started_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "completed_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "error_message", type: "TEXT", pk: false, fk: null, nullable: true, default: null },
{ name: "result_data", type: "JSON", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
]
},
terminal_command_logs: {
group: "Terminal",
description: "Journal des commandes exécutées dans le terminal",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "host_id", type: "VARCHAR(50)", pk: false, fk: "hosts.id", nullable: false, default: null },
{ name: "user_id", type: "INTEGER", pk: false, fk: "users.id", nullable: true, default: null },
{ name: "terminal_session_id", type: "VARCHAR(64)", pk: false, fk: null, nullable: true, default: null },
{ name: "command", type: "TEXT", pk: false, fk: null, nullable: false, default: null },
{ name: "command_hash", type: "VARCHAR(64)", pk: false, fk: null, nullable: false, default: null },
{ name: "source", type: "VARCHAR(20)", pk: false, fk: null, nullable: false, default: "'terminal'" },
{ name: "is_pinned", type: "BOOLEAN", pk: false, fk: null, nullable: false, default: "0" },
{ name: "is_blocked", type: "BOOLEAN", pk: false, fk: null, nullable: false, default: "0" },
{ name: "blocked_reason", type: "VARCHAR(255)", pk: false, fk: null, nullable: true, default: null },
{ name: "username", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "host_name", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
]
},
terminal_sessions: {
group: "Terminal",
description: "Sessions de terminal actives ou fermées",
columns: [
{ name: "id", type: "VARCHAR(50)", pk: true, fk: null, nullable: false, default: null },
{ name: "host_id", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: null },
{ name: "host_name", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "host_ip", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: null },
{ name: "user_id", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "username", type: "VARCHAR(50)", pk: false, fk: null, nullable: true, default: null },
{ name: "token_hash", type: "VARCHAR(128)", pk: false, fk: null, nullable: false, default: null },
{ name: "ttyd_port", type: "INTEGER", pk: false, fk: null, nullable: false, default: null },
{ name: "ttyd_pid", type: "INTEGER", pk: false, fk: null, nullable: true, default: null },
{ name: "mode", type: "VARCHAR(20)", pk: false, fk: null, nullable: false, default: "'embedded'" },
{ name: "status", type: "VARCHAR(20)", pk: false, fk: null, nullable: false, default: "'active'" },
{ name: "reason_closed", type: "VARCHAR(30)", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "last_seen_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "expires_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: null },
{ name: "closed_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
]
},
users: {
group: "Utilisateurs",
description: "Comptes utilisateurs de l'application",
columns: [
{ name: "id", type: "INTEGER", pk: true, fk: null, nullable: false, default: null },
{ name: "username", type: "VARCHAR(50)", pk: false, fk: null, nullable: false, default: null },
{ name: "email", type: "VARCHAR(255)", pk: false, fk: null, nullable: true, default: null },
{ name: "hashed_password", type: "VARCHAR(255)", pk: false, fk: null, nullable: false, default: null },
{ name: "role", type: "VARCHAR(20)", pk: false, fk: null, nullable: false, default: "'admin'" },
{ name: "is_active", type: "BOOLEAN", pk: false, fk: null, nullable: false, default: "1" },
{ name: "is_superuser", type: "BOOLEAN", pk: false, fk: null, nullable: false, default: "0" },
{ name: "display_name", type: "VARCHAR(100)", pk: false, fk: null, nullable: true, default: null },
{ name: "created_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "updated_at", type: "DATETIME", pk: false, fk: null, nullable: false, default: "now()" },
{ name: "last_login", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "password_changed_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
{ name: "deleted_at", type: "DATETIME", pk: false, fk: null, nullable: true, default: null },
]
},
};
const GROUP_COLORS = {
"Hôtes": { bg: "#0f172a", accent: "#38bdf8", badge: "#0369a1", badgeText: "#e0f2fe" },
"Docker": { bg: "#0a1628", accent: "#22d3ee", badge: "#0e7490", badgeText: "#cffafe" },
"Ansible": { bg: "#1a0a2e", accent: "#a78bfa", badge: "#6d28d9", badgeText: "#ede9fe" },
"Utilisateurs": { bg: "#0f1f0f", accent: "#4ade80", badge: "#15803d", badgeText: "#dcfce7" },
"Terminal": { bg: "#1a1400", accent: "#fbbf24", badge: "#b45309", badgeText: "#fef3c7" },
"Notifications": { bg: "#1a0a0a", accent: "#f87171", badge: "#b91c1c", badgeText: "#fee2e2" },
"Système": { bg: "#111827", accent: "#94a3b8", badge: "#475569", badgeText: "#f1f5f9" },
};
const TYPE_COLOR = (t) => {
if (t.startsWith("VARCHAR") || t === "TEXT") return "#7dd3fc";
if (t === "INTEGER" || t === "BIGINT" || t === "FLOAT") return "#86efac";
if (t === "DATETIME") return "#fcd34d";
if (t === "BOOLEAN") return "#f9a8d4";
if (t === "JSON") return "#c4b5fd";
return "#e2e8f0";
};
const GROUPS = [...new Set(Object.values(SCHEMA).map(t => t.group))];
export default function App() {
const [search, setSearch] = useState("");
const [activeGroup, setActiveGroup] = useState("Tous");
const [selectedTable, setSelectedTable] = useState(null);
const [colSearch, setColSearch] = useState("");
const tableNames = useMemo(() => Object.keys(SCHEMA).sort(), []);
const filtered = useMemo(() => {
return tableNames.filter(name => {
const matchGroup = activeGroup === "Tous" || SCHEMA[name].group === activeGroup;
const matchSearch = name.toLowerCase().includes(search.toLowerCase()) ||
SCHEMA[name].description.toLowerCase().includes(search.toLowerCase());
return matchGroup && matchSearch;
});
}, [tableNames, search, activeGroup]);
const tableData = selectedTable ? SCHEMA[selectedTable] : null;
const groupColor = tableData ? GROUP_COLORS[tableData.group] : null;
const filteredCols = useMemo(() => {
if (!tableData) return [];
return tableData.columns.filter(c =>
c.name.toLowerCase().includes(colSearch.toLowerCase()) ||
c.type.toLowerCase().includes(colSearch.toLowerCase())
);
}, [tableData, colSearch]);
const grouped = useMemo(() => {
const g = {};
GROUPS.forEach(grp => {
const tables = filtered.filter(n => SCHEMA[n].group === grp);
if (tables.length) g[grp] = tables;
});
return g;
}, [filtered]);
return (
<div style={{ display: "flex", height: "100vh", background: "#0d1117", fontFamily: "'JetBrains Mono', 'Fira Code', monospace", color: "#c9d1d9", overflow: "hidden" }}>
<style>{`
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;600;700&display=swap');
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: #161b22; }
::-webkit-scrollbar-thumb { background: #30363d; border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: #484f58; }
.table-item { transition: background 0.15s, border-color 0.15s; }
.table-item:hover { background: #21262d !important; }
.col-row:hover { background: #1c2128 !important; }
.group-btn { transition: all 0.15s; }
.group-btn:hover { filter: brightness(1.2); }
`}</style>
{/* SIDEBAR */}
<div style={{ width: 280, background: "#161b22", borderRight: "1px solid #21262d", display: "flex", flexDirection: "column", flexShrink: 0 }}>
{/* Header */}
<div style={{ padding: "20px 16px 16px", borderBottom: "1px solid #21262d" }}>
<div style={{ fontSize: 11, color: "#58a6ff", fontWeight: 700, letterSpacing: 3, marginBottom: 6, textTransform: "uppercase" }}>Schema Explorer</div>
<div style={{ fontSize: 12, color: "#484f58" }}>{tableNames.length} tables · SQLAlchemy</div>
</div>
{/* Search */}
<div style={{ padding: "12px 12px 8px" }}>
<div style={{ position: "relative" }}>
<span style={{ position: "absolute", left: 10, top: "50%", transform: "translateY(-50%)", color: "#484f58", fontSize: 13 }}>⌕</span>
<input
value={search}
onChange={e => setSearch(e.target.value)}
placeholder="Rechercher une table…"
style={{ width: "100%", background: "#0d1117", border: "1px solid #30363d", borderRadius: 6, padding: "7px 10px 7px 30px", color: "#c9d1d9", fontSize: 12, outline: "none", boxSizing: "border-box" }}
/>
</div>
</div>
{/* Groups filter */}
<div style={{ padding: "0 12px 10px", display: "flex", flexWrap: "wrap", gap: 5 }}>
{["Tous", ...GROUPS].map(g => {
const isActive = activeGroup === g;
const gc = GROUP_COLORS[g];
return (
<button key={g} className="group-btn" onClick={() => setActiveGroup(g)} style={{
fontSize: 10, padding: "3px 8px", borderRadius: 4, cursor: "pointer", border: "none", fontFamily: "inherit", fontWeight: 600,
background: isActive ? (gc ? gc.badge : "#21262d") : "#0d1117",
color: isActive ? (gc ? gc.badgeText : "#c9d1d9") : "#58616a",
letterSpacing: 0.5
}}>
{g}
</button>
);
})}
</div>
{/* Table list */}
<div style={{ flex: 1, overflowY: "auto", padding: "0 8px 16px" }}>
{Object.keys(grouped).map(grp => {
const gc = GROUP_COLORS[grp];
return (
<div key={grp} style={{ marginBottom: 8 }}>
<div style={{ fontSize: 10, color: gc.accent, fontWeight: 700, letterSpacing: 2, textTransform: "uppercase", padding: "8px 8px 4px" }}>
{grp}
</div>
{grouped[grp].map(name => {
const isSelected = selectedTable === name;
return (
<div key={name} className="table-item" onClick={() => { setSelectedTable(name); setColSearch(""); }} style={{
padding: "8px 10px", borderRadius: 6, cursor: "pointer", marginBottom: 2,
background: isSelected ? "#1f2d3d" : "transparent",
border: isSelected ? `1px solid ${gc.accent}44` : "1px solid transparent",
}}>
<div style={{ display: "flex", alignItems: "center", gap: 7 }}>
<span style={{ color: isSelected ? gc.accent : "#484f58", fontSize: 12 }}>⬡</span>
<span style={{ fontSize: 12, color: isSelected ? "#e6edf3" : "#8b949e", fontWeight: isSelected ? 600 : 400 }}>{name}</span>
<span style={{ marginLeft: "auto", fontSize: 10, color: "#484f58" }}>{SCHEMA[name].columns.length}</span>
</div>
</div>
);
})}
</div>
);
})}
{filtered.length === 0 && (
<div style={{ padding: 20, color: "#484f58", fontSize: 12, textAlign: "center" }}>Aucune table trouvée</div>
)}
</div>
</div>
{/* MAIN CONTENT */}
<div style={{ flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }}>
{selectedTable && tableData ? (
<>
{/* Table header */}
<div style={{ padding: "20px 28px 16px", borderBottom: "1px solid #21262d", background: "#161b22" }}>
<div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 6 }}>
<span style={{ fontSize: 22, color: groupColor.accent }}>⬡</span>
<h1 style={{ margin: 0, fontSize: 22, fontWeight: 700, color: "#e6edf3", letterSpacing: 1 }}>{selectedTable}</h1>
<span style={{ fontSize: 11, padding: "2px 10px", borderRadius: 4, fontWeight: 700, background: groupColor.badge, color: groupColor.badgeText }}>
{tableData.group}
</span>
</div>
<p style={{ margin: 0, fontSize: 13, color: "#8b949e" }}>{tableData.description}</p>
{/* Stats row */}
<div style={{ display: "flex", gap: 20, marginTop: 12 }}>
{[
{ label: "Colonnes", val: tableData.columns.length },
{ label: "Clés primaires", val: tableData.columns.filter(c => c.pk).length },
{ label: "Clés étrangères", val: tableData.columns.filter(c => c.fk).length },
{ label: "Nullable", val: tableData.columns.filter(c => c.nullable).length },
].map(s => (
<div key={s.label} style={{ background: "#0d1117", border: "1px solid #21262d", borderRadius: 8, padding: "6px 14px", textAlign: "center" }}>
<div style={{ fontSize: 18, fontWeight: 700, color: groupColor.accent }}>{s.val}</div>
<div style={{ fontSize: 10, color: "#484f58", letterSpacing: 1 }}>{s.label}</div>
</div>
))}
</div>
</div>
{/* Column search */}
<div style={{ padding: "12px 28px 0", background: "#0d1117" }}>
<div style={{ position: "relative", maxWidth: 340 }}>
<span style={{ position: "absolute", left: 10, top: "50%", transform: "translateY(-50%)", color: "#484f58", fontSize: 13 }}>⌕</span>
<input
value={colSearch}
onChange={e => setColSearch(e.target.value)}
placeholder="Filtrer les colonnes…"
style={{ width: "100%", background: "#161b22", border: "1px solid #30363d", borderRadius: 6, padding: "7px 10px 7px 30px", color: "#c9d1d9", fontSize: 12, outline: "none", boxSizing: "border-box" }}
/>
</div>
</div>
{/* Columns table */}
<div style={{ flex: 1, overflowY: "auto", padding: "12px 28px 28px", background: "#0d1117" }}>
<table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12 }}>
<thead>
<tr style={{ borderBottom: "2px solid #21262d" }}>
{["Colonne", "Type", "Flags", "Défaut"].map(h => (
<th key={h} style={{ textAlign: "left", padding: "8px 12px", color: "#484f58", fontWeight: 700, fontSize: 10, letterSpacing: 2, textTransform: "uppercase" }}>{h}</th>
))}
</tr>
</thead>
<tbody>
{filteredCols.map((col, i) => (
<tr key={col.name} className="col-row" style={{ borderBottom: "1px solid #161b22", background: i % 2 === 0 ? "transparent" : "#0a0f16" }}>
<td style={{ padding: "10px 12px", fontWeight: col.pk ? 700 : 400 }}>
<div style={{ display: "flex", alignItems: "center", gap: 7 }}>
{col.pk && <span title="Clé primaire" style={{ fontSize: 11, color: "#f59e0b" }}>🔑</span>}
{col.fk && !col.pk && <span title={`Clé étrangère → ${col.fk}`} style={{ fontSize: 11 }}>🔗</span>}
{!col.pk && !col.fk && <span style={{ fontSize: 11, color: "#30363d" }}>·</span>}
<span style={{ color: col.pk ? "#fbbf24" : col.fk ? "#60a5fa" : "#e6edf3" }}>{col.name}</span>
</div>
{col.fk && (
<div style={{ fontSize: 10, color: "#484f58", marginLeft: 22, marginTop: 2 }}>→ {col.fk}</div>
)}
</td>
<td style={{ padding: "10px 12px" }}>
<span style={{ color: TYPE_COLOR(col.type), fontWeight: 600 }}>{col.type}</span>
</td>
<td style={{ padding: "10px 12px" }}>
<div style={{ display: "flex", gap: 5, flexWrap: "wrap" }}>
{!col.nullable && (
<span style={{ fontSize: 10, padding: "1px 7px", borderRadius: 3, background: "#1c2e1c", color: "#4ade80", fontWeight: 600 }}>NOT NULL</span>
)}
{col.nullable && (
<span style={{ fontSize: 10, padding: "1px 7px", borderRadius: 3, background: "#1c1c1c", color: "#484f58" }}>NULL</span>
)}
</div>
</td>
<td style={{ padding: "10px 12px" }}>
{col.default ? (
<code style={{ fontSize: 11, color: "#c4b5fd", background: "#1e1a2e", padding: "2px 7px", borderRadius: 3 }}>{col.default}</code>
) : (
<span style={{ color: "#30363d" }}>—</span>
)}
</td>
</tr>
))}
</tbody>
</table>
{filteredCols.length === 0 && (
<div style={{ padding: 40, textAlign: "center", color: "#484f58", fontSize: 12 }}>Aucune colonne trouvée</div>
)}
</div>
</>
) : (
/* Welcome / overview */
<div style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: 40, gap: 32 }}>
<div style={{ textAlign: "center" }}>
<div style={{ fontSize: 48, marginBottom: 12 }}>⬡</div>
<h2 style={{ margin: 0, fontSize: 24, color: "#e6edf3", fontWeight: 700 }}>Modèle de Données</h2>
<p style={{ color: "#484f58", fontSize: 13, marginTop: 8 }}>Sélectionnez une table dans le panneau de gauche</p>
</div>
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))", gap: 12, maxWidth: 700, width: "100%" }}>
{GROUPS.map(grp => {
const gc = GROUP_COLORS[grp];
const count = tableNames.filter(n => SCHEMA[n].group === grp).length;
const firstTable = tableNames.find(n => SCHEMA[n].group === grp);
return (
<div key={grp} onClick={() => { setActiveGroup(grp); setSelectedTable(firstTable); setColSearch(""); }} style={{
background: "#161b22", border: `1px solid ${gc.accent}33`, borderRadius: 10, padding: "16px 18px", cursor: "pointer",
transition: "all 0.15s"
}}
onMouseEnter={e => e.currentTarget.style.borderColor = gc.accent}
onMouseLeave={e => e.currentTarget.style.borderColor = gc.accent + "33"}
>
<div style={{ fontSize: 20, marginBottom: 8 }}>⬡</div>
<div style={{ fontSize: 13, fontWeight: 700, color: gc.accent, marginBottom: 4 }}>{grp}</div>
<div style={{ fontSize: 11, color: "#484f58" }}>{count} table{count > 1 ? "s" : ""}</div>
</div>
);
})}
</div>
</div>
)}
</div>
</div>
);
}
+311
View File
@@ -0,0 +1,311 @@
erDiagram
alerts {
INTEGER id PK
INTEGER user_id FK "nullable"
VARCHAR category
VARCHAR level "nullable"
VARCHAR title "nullable"
TEXT message
VARCHAR source "nullable"
JSON details "nullable"
DATETIME read_at "nullable"
DATETIME created_at
}
app_settings {
VARCHAR key PK
TEXT value "nullable"
DATETIME created_at
DATETIME updated_at
}
bootstrap_status {
INTEGER id PK
VARCHAR host_id FK
VARCHAR status
VARCHAR automation_user "nullable"
DATETIME last_attempt "nullable"
TEXT error_message "nullable"
DATETIME created_at
}
container_customizations {
INTEGER id PK
INTEGER user_id FK "nullable"
VARCHAR host_id FK
VARCHAR container_id
VARCHAR icon_key "nullable"
VARCHAR icon_color "nullable"
VARCHAR bg_color "nullable"
DATETIME created_at
DATETIME updated_at
}
docker_alerts {
INTEGER id PK
VARCHAR host_id FK
VARCHAR container_name
VARCHAR severity
VARCHAR state
TEXT message "nullable"
DATETIME opened_at
DATETIME closed_at "nullable"
DATETIME acknowledged_at "nullable"
VARCHAR acknowledged_by "nullable"
DATETIME last_notified_at "nullable"
}
docker_containers {
INTEGER id PK
VARCHAR host_id FK
VARCHAR container_id
VARCHAR name
VARCHAR image "nullable"
VARCHAR state
VARCHAR status "nullable"
VARCHAR health "nullable"
DATETIME created_at "nullable"
JSON ports "nullable"
JSON labels "nullable"
VARCHAR compose_project "nullable"
DATETIME last_update_at
}
docker_images {
INTEGER id PK
VARCHAR host_id FK
VARCHAR image_id
JSON repo_tags "nullable"
BIGINT size "nullable"
DATETIME created "nullable"
DATETIME last_update_at
}
docker_volumes {
INTEGER id PK
VARCHAR host_id FK
VARCHAR name
VARCHAR driver "nullable"
TEXT mountpoint "nullable"
VARCHAR scope "nullable"
DATETIME last_update_at
}
favorite_containers {
INTEGER id PK
INTEGER user_id FK "nullable"
INTEGER docker_container_id FK
INTEGER group_id FK "nullable"
DATETIME created_at
}
favorite_groups {
INTEGER id PK
INTEGER user_id FK "nullable"
VARCHAR name
INTEGER sort_order
VARCHAR color "nullable"
VARCHAR icon_key "nullable"
DATETIME created_at
DATETIME updated_at
}
host_metrics {
INTEGER id PK
VARCHAR host_id FK
VARCHAR metric_type
INTEGER cpu_count "nullable"
VARCHAR cpu_model "nullable"
INTEGER cpu_cores "nullable"
INTEGER cpu_threads "nullable"
INTEGER cpu_threads_per_core "nullable"
INTEGER cpu_sockets "nullable"
FLOAT cpu_mhz "nullable"
FLOAT cpu_max_mhz "nullable"
FLOAT cpu_min_mhz "nullable"
FLOAT cpu_load_1m "nullable"
FLOAT cpu_load_5m "nullable"
FLOAT cpu_load_15m "nullable"
FLOAT cpu_usage_percent "nullable"
FLOAT cpu_temperature "nullable"
INTEGER memory_total_mb "nullable"
INTEGER memory_used_mb "nullable"
INTEGER memory_free_mb "nullable"
FLOAT memory_usage_percent "nullable"
INTEGER swap_total_mb "nullable"
INTEGER swap_used_mb "nullable"
FLOAT swap_usage_percent "nullable"
JSON disk_info "nullable"
JSON disk_devices
FLOAT disk_root_total_gb "nullable"
FLOAT disk_root_used_gb "nullable"
FLOAT disk_root_usage_percent "nullable"
JSON lvm_info "nullable"
JSON zfs_info "nullable"
JSON storage_details "nullable"
VARCHAR os_name "nullable"
VARCHAR os_version "nullable"
VARCHAR kernel_version "nullable"
VARCHAR hostname "nullable"
INTEGER uptime_seconds "nullable"
VARCHAR uptime_human "nullable"
JSON network_info "nullable"
JSON raw_data "nullable"
VARCHAR collection_source "nullable"
INTEGER collection_duration_ms "nullable"
TEXT error_message "nullable"
DATETIME collected_at
DATETIME created_at
}
hosts {
VARCHAR id PK
VARCHAR name
VARCHAR ip_address
VARCHAR status
VARCHAR ansible_group "nullable"
DATETIME last_seen "nullable"
BOOLEAN reachable
DATETIME created_at
DATETIME updated_at
DATETIME deleted_at "nullable"
BOOLEAN docker_enabled
VARCHAR docker_version "nullable"
VARCHAR docker_status "nullable"
DATETIME docker_last_collect_at "nullable"
}
logs {
INTEGER id PK
VARCHAR level
VARCHAR source "nullable"
TEXT message
JSON details "nullable"
VARCHAR host_id "nullable"
VARCHAR task_id "nullable"
VARCHAR schedule_id "nullable"
DATETIME created_at
}
playbook_lint_results {
INTEGER id PK
VARCHAR filename
INTEGER quality_score
INTEGER total_issues
INTEGER errors_count
INTEGER warnings_count
INTEGER execution_time_ms
TEXT issues_json "nullable"
TEXT raw_output "nullable"
DATETIME created_at
DATETIME updated_at
}
schedule_runs {
INTEGER id PK
VARCHAR schedule_id FK
VARCHAR task_id FK "nullable"
VARCHAR status
DATETIME started_at
DATETIME completed_at "nullable"
FLOAT duration "nullable"
INTEGER hosts_impacted "nullable"
TEXT error_message "nullable"
TEXT output "nullable"
DATETIME created_at
}
schedules {
VARCHAR id PK
VARCHAR name
TEXT description "nullable"
VARCHAR playbook
VARCHAR target_type "nullable"
VARCHAR target
JSON extra_vars "nullable"
VARCHAR schedule_type
DATETIME schedule_time "nullable"
VARCHAR recurrence_type "nullable"
VARCHAR recurrence_time "nullable"
TEXT recurrence_days "nullable"
VARCHAR cron_expression "nullable"
VARCHAR timezone "nullable"
DATETIME start_at "nullable"
DATETIME end_at "nullable"
BOOLEAN enabled
TEXT tags "nullable"
DATETIME next_run "nullable"
DATETIME last_run "nullable"
VARCHAR last_status "nullable"
INTEGER retry_on_failure "nullable"
INTEGER timeout "nullable"
VARCHAR notification_type "nullable"
INTEGER run_count "nullable"
INTEGER success_count "nullable"
INTEGER failure_count "nullable"
DATETIME created_at
DATETIME updated_at
DATETIME deleted_at "nullable"
}
tasks {
VARCHAR id PK
VARCHAR action
VARCHAR target
VARCHAR status
VARCHAR playbook "nullable"
DATETIME started_at "nullable"
DATETIME completed_at "nullable"
TEXT error_message "nullable"
JSON result_data "nullable"
DATETIME created_at
}
terminal_command_logs {
INTEGER id PK
DATETIME created_at
VARCHAR host_id FK
INTEGER user_id FK "nullable"
VARCHAR terminal_session_id "nullable"
TEXT command
VARCHAR command_hash
VARCHAR source
BOOLEAN is_pinned
BOOLEAN is_blocked
VARCHAR blocked_reason "nullable"
VARCHAR username "nullable"
VARCHAR host_name "nullable"
}
terminal_sessions {
VARCHAR id PK
VARCHAR host_id
VARCHAR host_name
VARCHAR host_ip
INTEGER user_id "nullable"
VARCHAR username "nullable"
VARCHAR token_hash
INTEGER ttyd_port
INTEGER ttyd_pid "nullable"
VARCHAR mode
VARCHAR status
VARCHAR reason_closed "nullable"
DATETIME created_at
DATETIME last_seen_at
DATETIME expires_at
DATETIME closed_at "nullable"
}
users {
INTEGER id PK
VARCHAR username
VARCHAR email "nullable"
VARCHAR hashed_password
VARCHAR role
BOOLEAN is_active
BOOLEAN is_superuser
VARCHAR display_name "nullable"
DATETIME created_at
DATETIME updated_at
DATETIME last_login "nullable"
DATETIME password_changed_at "nullable"
DATETIME deleted_at "nullable"
}
users ||--o{ alerts : "user_id"
hosts ||--o{ bootstrap_status : "host_id"
users ||--o{ container_customizations : "user_id"
hosts ||--o{ container_customizations : "host_id"
hosts ||--o{ docker_alerts : "host_id"
hosts ||--o{ docker_containers : "host_id"
hosts ||--o{ docker_images : "host_id"
hosts ||--o{ docker_volumes : "host_id"
users ||--o{ favorite_containers : "user_id"
docker_containers ||--o{ favorite_containers : "docker_container_id"
favorite_groups ||--o{ favorite_containers : "group_id"
users ||--o{ favorite_groups : "user_id"
hosts ||--o{ host_metrics : "host_id"
schedules ||--o{ schedule_runs : "schedule_id"
tasks ||--o{ schedule_runs : "task_id"
hosts ||--o{ terminal_command_logs : "host_id"
users ||--o{ terminal_command_logs : "user_id"
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

+51
View File
@@ -0,0 +1,51 @@
# 📊 Explorateur de Schéma Interactive
Ce répertoire contient un outil de documentation interactif permettant de visualiser et de naviguer dans le schéma de la base de données de l'application.
## 🔍 À propos de l'outil
Le fichier `database_schema_explorer.jsx` est une application React autonome qui :
- Listes toutes les tables par catégorie (Docker, Ansible, Système, etc.).
- Affiche les détails complets des colonnes (types, pk, fk, nullable, valeurs par défaut).
- Propose une interface moderne avec recherche temps réel et filtrage par groupes.
## 🚀 Comment l'exécuter
### Méthode 1 : Zéro-Installation (La plus rapide pour consulter)
Si vous ne souhaitez pas installer de nouvelles dépendances Node.js, ouvrez simplement le fichier suivant dans votre navigateur :
📄 `docs/database/standalone.html`
*Note : Cette méthode utilise des bibliothèques via CDN (React + Babel Standalone) pour transpiler le JSX à la volée directement dans le navigateur. C'est idéal pour une consultation ponctuelle.*
### Méthode 2 : Méthode Professionnelle (Vite)
C'est la méthode recommandée pour un développement fluide avec Hot Module Replacement (HMR).
1. **Installer les dépendances** :
```powershell
npm install react react-dom
npm install -D vite @vitejs/plugin-react
```
2. **Lancer l'explorateur** :
```powershell
npx vite docs/database
```
👉 L'application sera disponible sur `http://localhost:5173`.
## 🛠️ Configuration d'entrée
Un fichier `index.html` est présent dans ce répertoire pour servir de point d'entrée. Il charge dynamiquement le composant React et le monte dans la page.
## 💡 Astuce de Productivité
Vous pouvez ajouter un raccourci dans votre `package.json` pour un accès rapide :
```json
"scripts": {
"docs:db": "vite docs/database"
}
```
Cela vous permettra de lancer l'outil simplement avec : `npm run docs:db`.
+22
View File
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📊</text></svg>" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Base de données - Explorateur de Schéma</title>
</head>
<body style="margin: 0; background: #0d1117;">
<div id="root"></div>
<script type="module">
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './database_schema_explorer.jsx';
// Rendu du composant principal
ReactDOM.createRoot(document.getElementById('root')).render(
React.createElement(React.StrictMode, null, React.createElement(App))
);
</script>
</body>
</html>
+39
View File
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📊</text></svg>" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Base de données - Explorateur de Schéma (CDN Mode)</title>
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@18",
"react-dom": "https://esm.sh/react-dom@18",
"react-dom/client": "https://esm.sh/react-dom@18/client"
}
}
</script>
<!-- Babel Standalone (Transpilation JSX) -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body style="margin: 0; background: #0d1117;">
<div id="root"></div>
<!-- Script principal utilisant Babel Standalone -->
<script type="text/babel" data-type="module">
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './database_schema_explorer.jsx';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
</script>
</body>
</html>