feat(gowa): device id kosong ikut device terhubung + auto webhook secret
All checks were successful
CI / test (push) Successful in 1m45s

- hapus auto-pin gowa_device_id pada QR/status
- hint device id + secret otomatis
- ensureGowaWebhookSecret saat boot & simpan pengaturan
- versi 0.5.18
This commit is contained in:
oxpoe
2026-11-06 03:50:06 +07:00
parent fadbf0084b
commit ad62aab07d
8 changed files with 37 additions and 13 deletions

View File

@@ -2,6 +2,12 @@
Format mengikuti [Keep a Changelog](https://keepachangelog.com/) dan [Semantic Versioning](https://semver.org/).
## [0.5.18] — 2026-11-06 — "gowa-autodevice"
### Diperbaiki
- **Device ID kosong** = otomatis mengikuti device yang terhubung lewat QR dashboard (tidak lagi dipin/disimpan otomatis). Isi Device ID hanya untuk menunjuk device GOWA tertentu.
- **Webhook secret GOWA** dibuat otomatis (32 byte hex) bila kosong atau masih default `secret`, lalu ditampilkan di pengaturan untuk disamakan dengan `WHATSAPP_WEBHOOK_SECRET` di GOWA.
## [0.5.17] — 2026-11-06 — "gowa-connected"
### Diperbaiki

View File

@@ -11,7 +11,7 @@
<p align="center">
<img src="https://img.shields.io/badge/lisensi-ISC-ADFF2F" alt="Lisensi: ISC">
<img src="https://img.shields.io/badge/versi-0.5.17_gowa--connected-ADFF2F" alt="Versi 0.5.17 (gowa-connected)">
<img src="https://img.shields.io/badge/versi-0.5.18_gowa--autodevice-ADFF2F" alt="Versi 0.5.18 (gowa-autodevice)">
<img src="https://img.shields.io/badge/Node.js-%E2%89%A520-339933" alt="Node.js >= 20">
<img src="https://img.shields.io/badge/docker-ready-2496ED?logo=docker&logoColor=white" alt="Docker">
<a href="https://demo-openisp.oxpoe.xyz"><img src="https://img.shields.io/badge/demo-live-ADFF2F?logo=googlechrome&logoColor=black" alt="Demo"></a>

View File

@@ -60,6 +60,7 @@ const { SUPPORTED_LANGS, FALLBACK_LANG, normalizeLang, t } = require('./config/i
// Pastikan semua default settings ada (untuk migrasi/update dari Gitea)
if (typeof ensureDefaultSettings === 'function') {
ensureDefaultSettings();
try { require('./config/settingsManager').ensureGowaWebhookSecret(); } catch (e) {}
}
// Inisialisasi aplikasi Express

View File

@@ -278,6 +278,18 @@ function formatTimeLocal(date) {
* Memastikan semua setting wajib ada di settings.json dengan default value.
* Dijalankan saat startup aplikasi untuk migrasi/inisialisasi setting baru.
*/
/**
* Pastikan gowa_webhook_secret tidak memakai default 'secret'.
* Dibuat otomatis (32 byte hex) bila kosong/default/terlalu pendek.
*/
function ensureGowaWebhookSecret(force) {
const current = String(getSetting('gowa_webhook_secret', '') || '').trim();
if (!force && current && current !== 'secret' && current.length >= 24) return current;
const secret = crypto.randomBytes(32).toString('hex');
saveSettings({ gowa_webhook_secret: secret });
return secret;
}
function ensureDefaultSettings() {
try {
const currentSettings = getSettings();
@@ -361,5 +373,6 @@ module.exports = {
getNowLocalISO,
parseDateInTimezone,
startSettingsWatcher,
ensureDefaultSettings
ensureDefaultSettings,
ensureGowaWebhookSecret
};

View File

@@ -1,6 +1,6 @@
{
"name": "open-isp",
"version": "0.5.17",
"version": "0.5.18",
"description": "OPEN-ISP \u2014 sistem billing & manajemen ISP open-source (PWA pelanggan, multi-OLT, RADIUS, WhatsApp, QRIS)",
"main": "app-customer.js",
"scripts": {

View File

@@ -6738,7 +6738,7 @@ router.get('/whatsapp/gowa/qr', requireAdminSession, async (req, res) => {
try {
const gowa = require('../services/gowaWhatsappService');
const dev = await gowa.ensureDevice();
if (dev && dev !== getSetting('gowa_device_id', '')) saveSettings({ gowa_device_id: dev });
// Jangan simpan otomatis: device id kosong = ikuti device yang terhubung via QR.
const r = await gowa.getLoginQr(dev);
if (!r.ok) {
const detail = r.message || r.code || 'pastikan GOWA berjalan & username/password Basic Auth benar';
@@ -6782,13 +6782,15 @@ router.get('/whatsapp', requireAdminSession, requireSidebarMenuAccess('whatsapp'
fonnte_country_code: getSetting('fonnte_country_code', '62')
};
const gowaBaseUrl = getSetting('gowa_base_url', 'http://127.0.0.1:3000');
let gowaWebhookSecret = getSetting('gowa_webhook_secret', '');
try { gowaWebhookSecret = require('../config/settingsManager').ensureGowaWebhookSecret(); } catch (e) {}
const gowaSettings = {
gowa_mode: getSetting('gowa_mode', /^https:/i.test(String(gowaBaseUrl)) ? 'cloud' : 'local'),
gowa_base_url: gowaBaseUrl,
gowa_username: getSetting('gowa_username', ''),
gowa_password: getSetting('gowa_password', ''),
gowa_device_id: getSetting('gowa_device_id', ''),
gowa_webhook_secret: getSetting('gowa_webhook_secret', 'secret')
gowa_webhook_secret: gowaWebhookSecret
};
const gowaWebhookUrl = String(getSetting('app_url', '') || ((req.headers['x-forwarded-proto'] || req.protocol) + '://' + req.get('host'))).replace(/\/+$/, '') + '/api/webhook/gowa';
const gowaUiHost = String(req.get('host') || '').split(':')[0];
@@ -6844,7 +6846,10 @@ router.post('/whatsapp/gateway-settings', requireAdminSession, express.urlencode
gowa_username: String(gowa_username || '').trim(),
gowa_password: String(gowa_password || '').trim(),
gowa_device_id: String(gowa_device_id || '').trim(),
gowa_webhook_secret: String(gowa_webhook_secret || 'secret').trim()
gowa_webhook_secret: (function () {
const v = String(gowa_webhook_secret || '').trim();
return (v && v !== 'secret' && v.length >= 24) ? v : crypto.randomBytes(32).toString('hex');
})()
});
if ((wa_gateway_type || 'baileys') === 'baileys') {
@@ -7400,10 +7405,7 @@ router.get('/api/whatsapp/status', requireAdmin, async (req, res) => {
const isConn = !!(pick.is_connected || pick.connected || pick.state === 'connected');
const conn = isConn ? 'open' : (loggedIn ? 'connecting' : 'close');
const deviceId = String(pick.device_id || pick.id || '').trim();
// Simpan otomatis device id agar status berikutnya memakai /devices/:id/status.
if (deviceId && !String(getSetting('gowa_device_id', '') || '').trim()) {
try { saveSettings({ gowa_device_id: deviceId }); } catch (e) {}
}
// device id kosong = selalu ikuti device yang terhubung (tidak dipin otomatis)
res.json({
connection: conn, gateway: 'gowa', loggedIn: loggedIn, deviceId: deviceId,
user: { id: deviceId || 'gowa_gateway', name: loggedIn ? 'GOWA (WhatsApp)' : 'GOWA Gateway' },

View File

@@ -1 +1 @@
0.5.17
0.5.18

View File

@@ -181,7 +181,8 @@
</div>
<div class="fg" style="flex:1;">
<label class="fl"><%= t('admin.wa.ph_deviceid','Device ID (opsional)') %></label>
<input class="fc" name="gowa_device_id" value="<%= gowaSettings.gowa_device_id %>" placeholder="multi-device (mis. device1)">
<input class="fc" name="gowa_device_id" value="<%= gowaSettings.gowa_device_id %>" placeholder="kosongkan = ikuti device yang terhubung">
<div style="font-size:11px;color:var(--muted);margin-top:4px">Kosongkan untuk otomatis mengikuti device yang login via QR. Isi hanya bila ingin menunjuk device GOWA tertentu.</div>
</div>
</div>
<div class="form-row mb12">
@@ -219,7 +220,8 @@
</div>
<div class="fg" style="flex:1;">
<label class="fl">Webhook Secret (HMAC)</label>
<input class="fc" name="gowa_webhook_secret" value="<%= gowaSettings.gowa_webhook_secret %>" placeholder="secret">
<input class="fc" name="gowa_webhook_secret" value="<%= gowaSettings.gowa_webhook_secret %>" placeholder="otomatis dibuat">
<div style="font-size:11px;color:var(--muted);margin-top:4px">Dibuat otomatis (32 byte). Samakan dengan <code>WHATSAPP_WEBHOOK_SECRET</code> di GOWA.</div>
</div>
</div>
</div>