feat: migrate WAHA configuration from system-wide to per-user settings with API key support
This commit is contained in:
206
views/admin.php
206
views/admin.php
@@ -3,13 +3,15 @@ if(empty($_SESSION['is_admin'])) die("Access Denied");
|
||||
|
||||
// ── DB Migrations ─────────────────────────────────────────────────────────────
|
||||
try { $pdo->exec("ALTER TABLE users ADD COLUMN profile_pic TEXT DEFAULT NULL"); } catch(Exception $e) {}
|
||||
try { $pdo->exec("ALTER TABLE users ADD COLUMN waha_endpoint TEXT DEFAULT NULL"); } catch(Exception $e) {}
|
||||
try { $pdo->exec("ALTER TABLE users ADD COLUMN waha_chat_id TEXT DEFAULT NULL"); } catch(Exception $e) {}
|
||||
try { $pdo->exec("ALTER TABLE users ADD COLUMN waha_template TEXT DEFAULT NULL"); } catch(Exception $e) {}
|
||||
try { $pdo->exec("ALTER TABLE users ADD COLUMN waha_session TEXT DEFAULT 'default'"); } catch(Exception $e) {}
|
||||
try { $pdo->exec("ALTER TABLE users ADD COLUMN waha_api_key TEXT DEFAULT NULL"); } catch(Exception $e) {}
|
||||
try {
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS system_config (k TEXT PRIMARY KEY, v TEXT)");
|
||||
$pdo->exec("INSERT OR IGNORE INTO system_config (k, v) VALUES ('completed_days', '7')");
|
||||
$pdo->exec("INSERT OR IGNORE INTO system_config (k, v) VALUES ('archived_days', '90')");
|
||||
$pdo->exec("INSERT OR IGNORE INTO system_config (k, v) VALUES ('waha_endpoint', '')");
|
||||
$pdo->exec("INSERT OR IGNORE INTO system_config (k, v) VALUES ('waha_chat_id', '')");
|
||||
$pdo->exec("INSERT OR IGNORE INTO system_config (k, v) VALUES ('waha_template', '')");
|
||||
} catch(Exception $e) {}
|
||||
|
||||
// ── Handle POST ───────────────────────────────────────────────────────────────
|
||||
@@ -79,8 +81,13 @@ if($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if(isset($_POST['update_user_channels'])) {
|
||||
$ch = $_POST['channels'] ?? [];
|
||||
$ch_str = is_array($ch) ? implode(',', $ch) : '';
|
||||
$stmt = $pdo->prepare("UPDATE users SET notify_channels=?, telegram_id=?, email=?, whatsapp_number=? WHERE id=?");
|
||||
$stmt->execute([$ch_str, $_POST['telegram_id'], $_POST['email'], $_POST['whatsapp_number'], $_POST['user_id']]);
|
||||
$waha_ep = trim($_POST['waha_endpoint'] ?? '');
|
||||
$waha_cid = trim($_POST['waha_chat_id'] ?? '');
|
||||
$waha_tpl = trim($_POST['waha_template'] ?? '');
|
||||
$waha_ses = trim($_POST['waha_session'] ?? '') ?: 'default';
|
||||
$waha_key = trim($_POST['waha_api_key'] ?? '');
|
||||
$stmt = $pdo->prepare("UPDATE users SET notify_channels=?, telegram_id=?, email=?, whatsapp_number=?, waha_endpoint=?, waha_chat_id=?, waha_template=?, waha_session=?, waha_api_key=? WHERE id=?");
|
||||
$stmt->execute([$ch_str, $_POST['telegram_id'], $_POST['email'], $_POST['whatsapp_number'], $waha_ep ?: null, $waha_cid ?: null, $waha_tpl ?: null, $waha_ses, $waha_key ?: null, $_POST['user_id']]);
|
||||
$success_msg = "User notification preferences updated!";
|
||||
}
|
||||
|
||||
@@ -88,15 +95,12 @@ if($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$stmt = $pdo->prepare("UPDATE system_config SET v=? WHERE k=?");
|
||||
$stmt->execute([$_POST['completed_days'] ?? 7, 'completed_days']);
|
||||
$stmt->execute([$_POST['archived_days'] ?? 90, 'archived_days']);
|
||||
$stmt->execute([$_POST['waha_endpoint'] ?? '', 'waha_endpoint']);
|
||||
$stmt->execute([$_POST['waha_chat_id'] ?? '', 'waha_chat_id']);
|
||||
$stmt->execute([$_POST['waha_template'] ?? '', 'waha_template']);
|
||||
$success_msg = "System configuration saved!";
|
||||
}
|
||||
}
|
||||
|
||||
// ── Fetch Data ────────────────────────────────────────────────────────────────
|
||||
$users = query("SELECT id, username, is_admin, notify_channels, telegram_id, email, whatsapp_number, profile_pic FROM users ORDER BY is_admin DESC, username ASC")->fetchAll();
|
||||
$users = query("SELECT id, username, is_admin, notify_channels, telegram_id, email, whatsapp_number, profile_pic, waha_endpoint, waha_chat_id, waha_template, waha_session, waha_api_key FROM users ORDER BY is_admin DESC, username ASC")->fetchAll();
|
||||
$all_groups = query("SELECT * FROM groups ORDER BY name")->fetchAll();
|
||||
$conf_comp = query("SELECT v FROM system_config WHERE k='completed_days'")->fetchColumn() ?: 7;
|
||||
$conf_arch = query("SELECT v FROM system_config WHERE k='archived_days'")->fetchColumn() ?: 90;
|
||||
@@ -368,60 +372,100 @@ $active_tab = $_GET['tab'] ?? 'overview';
|
||||
<div class="admin-tab-pane <?= $active_tab==='channels'?'active':'' ?>" id="tab-channels">
|
||||
<div class="card">
|
||||
<h3 style="font-size:1rem; font-weight:700; margin-bottom:0.5rem;"><i class="fa fa-bell"></i> User Notification Channels</h3>
|
||||
<p style="font-size:0.85rem; color:var(--text-light); margin-bottom:1.25rem;">Configure notification channels (Native, Telegram, Email, WhatsApp) per user.</p>
|
||||
<div style="overflow-x:auto;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<th>Role</th>
|
||||
<th>Channels</th>
|
||||
<th>Telegram ID</th>
|
||||
<th>Email</th>
|
||||
<th>WhatsApp</th>
|
||||
<th>Save</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach($users as $u):
|
||||
$chs = explode(',', $u['notify_channels'] ?? '');
|
||||
?>
|
||||
<tr>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="user_id" value="<?= $u['id'] ?>">
|
||||
<td>
|
||||
<div style="display:flex; align-items:center; gap:0.5rem;">
|
||||
<div style="width:28px;height:28px;border-radius:50%;background:var(--primary);color:#fff;display:flex;align-items:center;justify-content:center;font-size:0.75rem;font-weight:700;flex-shrink:0;overflow:hidden;">
|
||||
<?php if(($u['profile_pic']??null) && file_exists($u['profile_pic'])): ?>
|
||||
<img src="<?= htmlspecialchars($u['profile_pic']) ?>" style="width:100%;height:100%;object-fit:cover;">
|
||||
<?php else: ?>
|
||||
<?= strtoupper(mb_substr($u['username'],0,1)) ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<strong><?= htmlspecialchars($u['username']) ?></strong>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="<?= $u['is_admin']?'admin-badge-admin':'admin-badge-user' ?>"><?= $u['is_admin']?'Admin':'User' ?></span></td>
|
||||
<td>
|
||||
<div style="display:flex; gap:0.5rem; flex-wrap:wrap;">
|
||||
<?php foreach(['native','email','telegram','whatsapp'] as $ch): ?>
|
||||
<label style="display:flex; align-items:center; gap:3px; font-size:0.8rem; cursor:pointer; white-space:nowrap;">
|
||||
<input type="checkbox" name="channels[]" value="<?= $ch ?>" <?= in_array($ch,$chs)?'checked':'' ?>>
|
||||
<?= ucfirst($ch) ?>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</td>
|
||||
<td><input type="text" name="telegram_id" value="<?= htmlspecialchars($u['telegram_id']??'') ?>" class="form-control" style="width:100px; padding:0.35rem;"></td>
|
||||
<td><input type="email" name="email" value="<?= htmlspecialchars($u['email']??'') ?>" class="form-control" style="width:160px; padding:0.35rem;"></td>
|
||||
<td><input type="text" name="whatsapp_number" value="<?= htmlspecialchars($u['whatsapp_number']??'') ?>" class="form-control" style="width:120px; padding:0.35rem;"></td>
|
||||
<td><button type="submit" name="update_user_channels" class="btn btn-sm"><i class="fa fa-save"></i></button></td>
|
||||
</form>
|
||||
</tr>
|
||||
<p style="font-size:0.85rem; color:var(--text-light); margin-bottom:1.25rem;">Configure notification channels per user. WAHA settings appear when <strong>WhatsApp</strong> is enabled.</p>
|
||||
|
||||
<?php foreach($users as $u):
|
||||
$chs = explode(',', $u['notify_channels'] ?? '');
|
||||
$uid = $u['id'];
|
||||
$hasWA = in_array('whatsapp', $chs);
|
||||
?>
|
||||
<div class="card" style="margin-bottom:1rem; padding:1rem; border:1px solid var(--border);">
|
||||
<form method="POST">
|
||||
<input type="hidden" name="user_id" value="<?= $uid ?>">
|
||||
|
||||
<!-- User header row -->
|
||||
<div style="display:flex; align-items:center; gap:0.75rem; flex-wrap:wrap;">
|
||||
<!-- Avatar -->
|
||||
<div style="width:36px;height:36px;border-radius:50%;background:var(--primary);color:#fff;display:flex;align-items:center;justify-content:center;font-size:0.85rem;font-weight:700;flex-shrink:0;overflow:hidden;">
|
||||
<?php if(($u['profile_pic']??null) && file_exists($u['profile_pic'])): ?>
|
||||
<img src="<?= htmlspecialchars($u['profile_pic']) ?>" style="width:100%;height:100%;object-fit:cover;">
|
||||
<?php else: ?>
|
||||
<?= strtoupper(mb_substr($u['username'],0,1)) ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Name & role -->
|
||||
<div style="flex:1;">
|
||||
<div style="font-weight:700; font-size:0.95rem;"><?= htmlspecialchars($u['username']) ?></div>
|
||||
<div><span class="<?= $u['is_admin']?'admin-badge-admin':'admin-badge-user' ?>" style="font-size:0.7rem;"><?= $u['is_admin']?'Admin':'User' ?></span></div>
|
||||
</div>
|
||||
|
||||
<!-- Save button -->
|
||||
<button type="submit" name="update_user_channels" class="btn btn-sm"><i class="fa fa-save"></i> Save</button>
|
||||
</div>
|
||||
|
||||
<!-- Channel checkboxes -->
|
||||
<div style="display:flex; gap:1rem; flex-wrap:wrap; margin-top:0.85rem; padding-top:0.75rem; border-top:1px dashed var(--border);">
|
||||
<?php foreach(['native','email','telegram','whatsapp'] as $ch): ?>
|
||||
<label style="display:flex; align-items:center; gap:5px; font-size:0.85rem; cursor:pointer; font-weight:500;">
|
||||
<input type="checkbox" name="channels[]" value="<?= $ch ?>"
|
||||
<?= in_array($ch,$chs)?'checked':'' ?>
|
||||
<?= $ch==='whatsapp' ? "onchange=\"toggleWAHA({$uid}, this.checked)\"" : '' ?>>
|
||||
<?php
|
||||
$icons = ['native'=>'🔔','email'=>'📧','telegram'=>'✈️','whatsapp'=>'📱'];
|
||||
echo $icons[$ch] . ' ' . ucfirst($ch);
|
||||
?>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Extra fields row -->
|
||||
<div style="display:flex; gap:1rem; flex-wrap:wrap; margin-top:0.75rem; align-items:flex-end;">
|
||||
<div class="form-group" style="margin:0; min-width:130px;">
|
||||
<label style="font-size:0.75rem;">Telegram ID</label>
|
||||
<input type="text" name="telegram_id" value="<?= htmlspecialchars($u['telegram_id']??'') ?>" class="form-control" style="padding:0.35rem;" placeholder="@username or ID">
|
||||
</div>
|
||||
<div class="form-group" style="margin:0; min-width:180px;">
|
||||
<label style="font-size:0.75rem;">Email</label>
|
||||
<input type="email" name="email" value="<?= htmlspecialchars($u['email']??'') ?>" class="form-control" style="padding:0.35rem;" placeholder="user@example.com">
|
||||
</div>
|
||||
<div class="form-group" style="margin:0; min-width:140px;">
|
||||
<label style="font-size:0.75rem;">WhatsApp Number</label>
|
||||
<input type="text" name="whatsapp_number" value="<?= htmlspecialchars($u['whatsapp_number']??'') ?>" class="form-control" style="padding:0.35rem;" placeholder="6281234567890">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- WAHA expandable section -->
|
||||
<div id="waha-section-<?= $uid ?>" style="<?= $hasWA ? '' : 'display:none;' ?> margin-top:0.75rem; padding:0.85rem; background:rgba(37,211,102,0.06); border:1px solid rgba(37,211,102,0.25); border-radius:8px;">
|
||||
<div style="font-size:0.78rem; font-weight:700; color:#25D366; margin-bottom:0.65rem;"><i class="fab fa-whatsapp"></i> WAHA WhatsApp API Settings</div>
|
||||
<div style="display:flex; gap:1rem; flex-wrap:wrap; align-items:flex-end;">
|
||||
<div class="form-group" style="margin:0; flex:2; min-width:200px;">
|
||||
<label style="font-size:0.75rem;">WAHA Endpoint URL</label>
|
||||
<input type="text" name="waha_endpoint" value="<?= htmlspecialchars($u['waha_endpoint']??'') ?>" class="form-control" style="padding:0.35rem;" placeholder="http://localhost:3000/api/sendText">
|
||||
</div>
|
||||
<div class="form-group" style="margin:0; flex:1; min-width:120px;">
|
||||
<label style="font-size:0.75rem;">Session Name</label>
|
||||
<input type="text" name="waha_session" value="<?= htmlspecialchars($u['waha_session'] ?? 'default') ?>" class="form-control" style="padding:0.35rem;" placeholder="default">
|
||||
</div>
|
||||
<div class="form-group" style="margin:0; flex:1; min-width:160px;">
|
||||
<label style="font-size:0.75rem;">Chat ID</label>
|
||||
<input type="text" name="waha_chat_id" value="<?= htmlspecialchars($u['waha_chat_id']??'') ?>" class="form-control" style="padding:0.35rem;" placeholder="6281234567890@c.us">
|
||||
</div>
|
||||
<div class="form-group" style="margin:0; flex:1; min-width:160px;">
|
||||
<label style="font-size:0.75rem;">API Key <span style="font-weight:400; color:var(--text-light);">(X-Api-Key)</span></label>
|
||||
<input type="password" name="waha_api_key" value="<?= htmlspecialchars($u['waha_api_key']??'') ?>" class="form-control" style="padding:0.35rem; font-family:monospace;" placeholder="Leave blank if not required">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin-top:0.6rem;">
|
||||
<label style="font-size:0.75rem;">Message Template</label>
|
||||
<textarea name="waha_template" class="form-control" rows="2" style="padding:0.35rem; font-size:0.8rem;" placeholder="*Alert:* {action} on {module} '{item}' by {user}"><?= htmlspecialchars($u['waha_template']??'') ?></textarea>
|
||||
<div style="font-size:0.72rem; color:var(--text-light); margin-top:3px;">Variables: <code>{action}</code> <code>{module}</code> <code>{item}</code> <code>{user}</code></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -445,31 +489,6 @@ $active_tab = $_GET['tab'] ?? 'overview';
|
||||
</div>
|
||||
<button type="submit" name="update_sys_config" class="btn" style="margin-top:1.25rem;"><i class="fa fa-save"></i> Save Configuration</button>
|
||||
</form>
|
||||
<h3 style="font-size:1rem; font-weight:700; margin-bottom:0.5rem; margin-top:2rem;"><i class="fab fa-whatsapp"></i> WAHA WhatsApp Notifications</h3>
|
||||
<p style="font-size:0.85rem; color:var(--text-light); margin-bottom:1.5rem;">Configure centralized WhatsApp notifications globally via WAHA (WhatsApp HTTP API).</p>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="completed_days" value="<?= htmlspecialchars($conf_comp) ?>">
|
||||
<input type="hidden" name="archived_days" value="<?= htmlspecialchars($conf_arch) ?>">
|
||||
|
||||
<div style="display:grid; grid-template-columns:1fr; gap:1.5rem; max-width:600px;">
|
||||
<div class="form-group" style="margin:0;">
|
||||
<label>WAHA Endpoint URL</label>
|
||||
<input type="text" name="waha_endpoint" value="<?= htmlspecialchars($waha_endpoint) ?>" class="form-control" placeholder="http://localhost:3000/api/sendText">
|
||||
<div style="font-size:0.75rem; color:var(--text-light); margin-top:4px;">The full /api/sendText HTTP API endpoint where WAHA is hosted. Leave blank to disable WhatsApp Notification feature.</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin:0;">
|
||||
<label>Default Chat ID</label>
|
||||
<input type="text" name="waha_chat_id" value="<?= htmlspecialchars($waha_chat_id) ?>" class="form-control" placeholder="1234567890@c.us or group-id@g.us">
|
||||
<div style="font-size:0.75rem; color:var(--text-light); margin-top:4px;">The centralized group or destination ID.</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin:0;">
|
||||
<label>Message Template</label>
|
||||
<textarea name="waha_template" class="form-control" rows="3" placeholder="*System Alert:* {action} on {module} '{item}' by {user}"><?= htmlspecialchars($waha_template) ?></textarea>
|
||||
<div style="font-size:0.75rem; color:var(--text-light); margin-top:4px;">Available Variables: <code>{action}</code>, <code>{module}</code>, <code>{item}</code>, <code>{user}</code></div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" name="update_sys_config" class="btn" style="margin-top:1.25rem;"><i class="fa fa-save"></i> Save WAHA Configuration</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Danger Zone -->
|
||||
@@ -493,9 +512,22 @@ function switchTab(name) {
|
||||
document.querySelectorAll('.admin-tab-pane').forEach(p => p.classList.remove('active'));
|
||||
document.querySelector(`[onclick="switchTab('${name}')"]`).classList.add('active');
|
||||
document.getElementById('tab-'+name).classList.add('active');
|
||||
// Update URL without reload
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.set('tab', name);
|
||||
history.replaceState({}, '', url);
|
||||
}
|
||||
|
||||
function toggleWAHA(uid, enabled) {
|
||||
const section = document.getElementById('waha-section-' + uid);
|
||||
if (!section) return;
|
||||
if (enabled) {
|
||||
section.style.display = 'block';
|
||||
section.style.animation = 'fadeIn 0.25s ease';
|
||||
} else {
|
||||
section.style.display = 'none';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@keyframes fadeIn { from { opacity:0; transform:translateY(-6px); } to { opacity:1; transform:none; } }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user