Multi-tenant (p.1)
This commit is contained in:
@@ -133,12 +133,25 @@ Le cron tourne chaque minute et envoie une notification Discord à l'heure exact
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🔄 Mises à jour & Migrations SQL (Multi-Tenant)
|
||||||
|
|
||||||
|
L'architecture de HouseHub isole chaque foyer dans sa propre base de données (`househub_f1`, `househub_f2`, etc.). Lors de l'ajout de nouvelles fonctionnalités impliquant des changements de structure SQL, il faut suivre une double procédure :
|
||||||
|
|
||||||
|
1. **Pour les futures familles (Initialisation) :**
|
||||||
|
Mettre à jour le fichier `docker/schema_family.sql`. Ce fichier est lu par `register.php` à la création d'un nouvel espace.
|
||||||
|
2. **Pour les familles existantes (Migration) :**
|
||||||
|
Créer ou mettre à jour le script `migrate.php` à la racine du projet. Ce script boucle sur toutes les bases existantes référencées dans `househub_meta.families` pour y injecter les requêtes `ALTER TABLE` ou `CREATE TABLE`.
|
||||||
|
- **Exécution** : Accéder à `https://votre-domaine.com/migrate.php` avec un navigateur.
|
||||||
|
- ⚠️ **Sécurité** : Pensez à supprimer ou sécuriser l'accès à ce fichier après exécution en production.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 🗺️ Roadmap de Variabilisation (Migration Multi-Tenant)
|
## 🗺️ Roadmap de Variabilisation (Migration Multi-Tenant)
|
||||||
|
|
||||||
Afin de transformer complètement l'application héritée de Pacha Family en un produit générique et autonome pour chaque espace familial, les chantiers suivants doivent être menés.
|
Afin de transformer complètement l'application héritée de Pacha Family en un produit générique et autonome pour chaque espace familial, les chantiers suivants doivent être menés.
|
||||||
|
|
||||||
🟢 Niveau 1 : Facile (Constantes et configurations globales)
|
🟢 Niveau 1 : Facile (Constantes et configurations globales)
|
||||||
[ ] Configuration de base : Extraire la devise (CURRENCY) et la zone de vacances scolaires (ZONE_SCOLAIRE) du fichier config.php pour les stocker dans les paramètres d'espace en BDD.
|
[x] Configuration de base : Extraire la devise (CURRENCY) et la zone de vacances scolaires (ZONE_SCOLAIRE) du fichier config.php pour les stocker dans les paramètres d'espace en BDD.
|
||||||
[ ] Filtres API Éducation Nationale : Rendre dynamique la zone de l'API (zones LIKE '%Zone C%' dans family-calendar.js) pour s'adapter à la région de chaque foyer.
|
[ ] Filtres API Éducation Nationale : Rendre dynamique la zone de l'API (zones LIKE '%Zone C%' dans family-calendar.js) pour s'adapter à la région de chaque foyer.
|
||||||
[ ] Identifiant Planka : Variabiliser le project_id Planka par défaut écrit en dur dans planka/api.php.
|
[ ] Identifiant Planka : Variabiliser le project_id Planka par défaut écrit en dur dans planka/api.php.
|
||||||
[ ] Suppression des IDs statiques : Supprimer les IDs parents fixes (ID_ALEX = 2, ID_LAIA = 3) dans config.php au profit d'une lecture dynamique basée sur les utilisateurs réels de la famille connectée.
|
[ ] Suppression des IDs statiques : Supprimer les IDs parents fixes (ID_ALEX = 2, ID_LAIA = 3) dans config.php au profit d'une lecture dynamique basée sur les utilisateurs réels de la famille connectée.
|
||||||
@@ -163,6 +176,7 @@ Afin de transformer complètement l'application héritée de Pacha Family en un
|
|||||||
|
|
||||||
| Version | Changements |
|
| Version | Changements |
|
||||||
| ------- | ------------------------------------------------------------------------------------------ |
|
| ------- | ------------------------------------------------------------------------------------------ |
|
||||||
|
| v2.2.0 | Multi-tenant (p.1) : Paramètres dynamiques du foyer (€, Zone) via BDD + script migration |
|
||||||
| v2.1.0 | Module Planka : Kanban natif connecté à Planka via API proxy PHP |
|
| v2.1.0 | Module Planka : Kanban natif connecté à Planka via API proxy PHP |
|
||||||
| v2.0.0 | Module PrintVault : viewer 3D STL/3MF/GCode, stockage local, trajectoires GCode illimitées |
|
| v2.0.0 | Module PrintVault : viewer 3D STL/3MF/GCode, stockage local, trajectoires GCode illimitées |
|
||||||
| v1.5.0 | Modules Todo (rappels Discord), Mémo, Courses (liste partagée) |
|
| v1.5.0 | Modules Todo (rappels Discord), Mémo, Courses (liste partagée) |
|
||||||
|
|||||||
@@ -12,6 +12,17 @@ CREATE TABLE IF NOT EXISTS pf_users (
|
|||||||
display_name VARCHAR(100) NOT NULL
|
display_name VARCHAR(100) NOT NULL
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
-- ─── Familles ─────────────────────────────────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS pf_foyer_settings (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
currency VARCHAR(10) NOT NULL DEFAULT '€',
|
||||||
|
zone_scolaire VARCHAR(5) NOT NULL DEFAULT 'C',
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
-- Initialisation de la ligne unique par défaut
|
||||||
|
INSERT IGNORE INTO pf_foyer_settings (id, currency, zone_scolaire) VALUES (1, '€', 'C');
|
||||||
|
|
||||||
-- ─── Personnes ────────────────────────────────────────────────────────────────
|
-- ─── Personnes ────────────────────────────────────────────────────────────────
|
||||||
CREATE TABLE IF NOT EXISTS pf_people (
|
CREATE TABLE IF NOT EXISTS pf_people (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
|||||||
+2
-3
@@ -1,8 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
// HouseHub Config - Version sans erreur
|
// includes/config.php
|
||||||
|
// HouseHub Config - Version dynamique
|
||||||
if (!defined('ID_ALEX')) {
|
if (!defined('ID_ALEX')) {
|
||||||
define('ID_ALEX', 2);
|
define('ID_ALEX', 2);
|
||||||
define('ID_LAIA', 3);
|
define('ID_LAIA', 3);
|
||||||
define('CURRENCY', '€');
|
|
||||||
define('ZONE_SCOLAIRE', 'C');
|
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,21 @@ try {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
$pdo->exec("SET collation_connection = utf8mb4_general_ci");
|
$pdo->exec("SET collation_connection = utf8mb4_general_ci");
|
||||||
|
|
||||||
|
try {
|
||||||
|
$foyer = $pdo->query("SELECT currency, zone_scolaire FROM pf_foyer_settings WHERE id = 1")->fetch();
|
||||||
|
if (!defined('CURRENCY')) {
|
||||||
|
define('CURRENCY', $foyer['currency'] ?? '€');
|
||||||
|
}
|
||||||
|
if (!defined('ZONE_SCOLAIRE')) {
|
||||||
|
define('ZONE_SCOLAIRE', $foyer['zone_scolaire'] ?? 'C');
|
||||||
|
}
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
if (!defined('CURRENCY')) define('CURRENCY', '€');
|
||||||
|
if (!defined('ZONE_SCOLAIRE')) define('ZONE_SCOLAIRE', 'C');
|
||||||
|
}
|
||||||
|
// ------------------------------------------------
|
||||||
|
|
||||||
} catch (\PDOException $e) {
|
} catch (\PDOException $e) {
|
||||||
if (!headers_sent()) { header('Content-Type: application/json'); http_response_code(500); }
|
if (!headers_sent()) { header('Content-Type: application/json'); http_response_code(500); }
|
||||||
die(json_encode(['ok' => false, 'error' => 'Erreur BDD : ' . $e->getMessage()]));
|
die(json_encode(['ok' => false, 'error' => 'Erreur BDD : ' . $e->getMessage()]));
|
||||||
|
|||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
// Script de migration pour mettre à jour TOUTES les bases de données familiales
|
||||||
|
require_once __DIR__ . '/includes/meta_db.php';
|
||||||
|
|
||||||
|
echo "<h1>🚀 Début de la migration...</h1>";
|
||||||
|
|
||||||
|
// 1. Récupérer toutes les bases de données des familles
|
||||||
|
$stmt = $meta_pdo->query("SELECT name, db_name FROM families WHERE db_name != ''");
|
||||||
|
$families = $stmt->fetchAll();
|
||||||
|
|
||||||
|
$host = getenv('DB_HOST') ?: 'househub-db';
|
||||||
|
$user = getenv('DB_USER') ?: 'househub';
|
||||||
|
$pass = getenv('DB_PASS') ?: 'changeme';
|
||||||
|
|
||||||
|
// 2. Boucler sur chaque famille
|
||||||
|
foreach ($families as $f) {
|
||||||
|
$db_name = $f['db_name'];
|
||||||
|
echo "Mise à jour de la famille <strong>{$f['name']}</strong> (Base : $db_name) ... ";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$fam_pdo = new PDO("mysql:host=$host;dbname=$db_name;charset=utf8mb4", $user, $pass, [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 3. Injecter la nouvelle table
|
||||||
|
$sql = "
|
||||||
|
CREATE TABLE IF NOT EXISTS pf_foyer_settings (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
currency VARCHAR(10) NOT NULL DEFAULT '€',
|
||||||
|
zone_scolaire VARCHAR(5) NOT NULL DEFAULT 'C',
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
INSERT IGNORE INTO pf_foyer_settings (id, currency, zone_scolaire) VALUES (1, '€', 'C');
|
||||||
|
";
|
||||||
|
|
||||||
|
$fam_pdo->exec($sql);
|
||||||
|
echo "<span style='color:green'>✅ OK</span><br>";
|
||||||
|
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
echo "<span style='color:red'>❌ Erreur : " . $e->getMessage() . "</span><br>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "<h2>🎉 Migration terminée ! Tu peux supprimer ce fichier.</h2>";
|
||||||
|
?>
|
||||||
@@ -35,6 +35,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
} else {
|
} else {
|
||||||
$action = $_POST['action'] ?? '';
|
$action = $_POST['action'] ?? '';
|
||||||
|
|
||||||
|
if ($action === 'update_family_info') {
|
||||||
|
require_once __DIR__ . '/includes/db.php';
|
||||||
|
$currency = trim($_POST['currency'] ?? '€');
|
||||||
|
$zone = trim($_POST['zone_scolaire'] ?? 'C');
|
||||||
|
|
||||||
|
$stmtSave = $pdo->prepare("UPDATE pf_foyer_settings SET currency = ?, zone_scolaire = ? WHERE id = 1");
|
||||||
|
$stmtSave->execute([$currency, $zone]);
|
||||||
|
$success = "Paramètres du foyer mis à jour avec succès.";
|
||||||
|
}
|
||||||
|
|
||||||
if ($action === 'set_modules' && $family_id) {
|
if ($action === 'set_modules' && $family_id) {
|
||||||
$all = ['calendar', 'budget', 'holidays', 'gifts', 'garage', 'memo', 'todo', 'groceries', 'calendar_ios', 'printvault', 'planka'];
|
$all = ['calendar', 'budget', 'holidays', 'gifts', 'garage', 'memo', 'todo', 'groceries', 'calendar_ios', 'printvault', 'planka'];
|
||||||
$enabled = array_values(array_filter($all, fn($m) => isset($_POST['mod_' . $m])));
|
$enabled = array_values(array_filter($all, fn($m) => isset($_POST['mod_' . $m])));
|
||||||
@@ -440,6 +450,41 @@ require __DIR__ . '/header.php';
|
|||||||
</section>
|
</section>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ── Famille ──────────────────────────────────────────────────────────── -->
|
||||||
|
<?php
|
||||||
|
$currentCurrency = defined('CURRENCY') ? CURRENCY : '€';
|
||||||
|
$currentZone = defined('ZONE_SCOLAIRE') ? ZONE_SCOLAIRE : 'C';
|
||||||
|
?>
|
||||||
|
<section class="pf-panel-card">
|
||||||
|
<h2 class="pf-card-h2">👪 Configuration du foyer</h2>
|
||||||
|
<p class="pf-muted-note">Configurez les informations partagées par l'ensemble de votre foyer.</p>
|
||||||
|
|
||||||
|
<form method="post" class="pf-stack-md">
|
||||||
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars(csrf_token()) ?>">
|
||||||
|
<input type="hidden" name="action" value="update_family_info">
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="pf-label">Devise monétaire</label>
|
||||||
|
<input type="text" name="currency" class="pf-input" value="<?= htmlspecialchars($currentCurrency) ?>" placeholder="ex: €, $, CHF" required maxlength="10">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="pf-label">Zone de vacances scolaires (France)</label>
|
||||||
|
<select name="zone_scolaire" class="pf-input" style="background:var(--bg-panel);">
|
||||||
|
<option value="A" <?= $currentZone === 'A' ? 'selected' : '' ?>>Zone A</option>
|
||||||
|
<option value="B" <?= $currentZone === 'B' ? 'selected' : '' ?>>Zone B</option>
|
||||||
|
<option value="C" <?= $currentZone === 'C' ? 'selected' : '' ?>>Zone C</option>
|
||||||
|
<option value="Autre" <?= $currentZone === 'Autre' ? 'selected' : '' ?>>Hors France / Autre</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="pf-btn">Enregistrer</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
<!-- ── Profil ──────────────────────────────────────────────────────────── -->
|
<!-- ── Profil ──────────────────────────────────────────────────────────── -->
|
||||||
<section class="pf-panel-card">
|
<section class="pf-panel-card">
|
||||||
<h2 class="pf-card-h2">👤 Mon profil</h2>
|
<h2 class="pf-card-h2">👤 Mon profil</h2>
|
||||||
|
|||||||
Reference in New Issue
Block a user