Files
T
wmantly f178f1a972
Pull Request Tests / Run Tests (18.x) (push) Failing after 1m43s
Pull Request Tests / Run Tests (20.x) (push) Failing after 30s
Pull Request Tests / Run Tests (22.x) (push) Failing after 31s
Pull Request Tests / Test Summary (push) Failing after 4s
fix: test email/SMS senders, all SMS delivery, join-key install UI (v1.30.1)
Test Email always failed with "Email.send is not a function":
models/email.js exports {Mail}, and the handler required the module and
called .send on it directly. Every other caller destructures it.

Test SMS failed with "Unexpected token '<'": it POSTed to
https://api.voip.ms/v1.0/sms/send with Basic auth, an endpoint that does
not exist. VoIP.ms's REST API is a GET against voip.ms/api/v1/rest.php
with api_username/api_password and method=sendSMS, so the fabricated URL
returned HTML and response.json() threw.

Worse, ALL SMS delivery was broken. models/sms.js called
PluginInstance.find({...}) but the ORM has no find -- the query method is
list({where}) -- so it threw on every send, before it could even fall
back to the direct VoIP.ms path. OTP-by-SMS and notifications were dead.

Both test endpoints now send through the same senders every real message
uses. A test that reimplements delivery proves nothing about whether real
delivery works, which is how two broken paths went unnoticed. Failures
report as 400 with the underlying reason rather than an opaque 500.

Adds a guard suite that fails the build on any call to a non-existent ORM
static, on requiring models/email without destructuring {Mail}, and on
any reference to the bogus api.voip.ms host.

Also: the Install Agent modal now leads with the join-key flow. v1.30.0
shipped join keys in the API and documented the modal as the place to get
one, but the modal still only did the pre-register flow.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 11:51:37 -04:00

63 lines
1.9 KiB
JavaScript

'use strict';
const https = require('https');
const conf = require('@simpleworkjs/conf').voipms;
function toE164Digits(number) {
const digits = String(number).replace(/\D/g, '');
if (digits.length === 10) return '1' + digits;
return digits;
}
async function send(to, message) {
const { PluginInstance } = require('./plugin_instance');
const registry = require('../services/plugin_registry');
const pluginSecrets = require('../utils/plugin_secrets');
// @simpleworkjs/orm has no `find` -- the query method is `list({where})`.
// `PluginInstance.find(...)` threw "is not a function" on EVERY call into
// this sender, so SMS delivery never worked at all: not the test button, not
// OTP-by-SMS, not notifications. It failed before it could even fall back to
// the direct VoIP.ms path below.
const instances = await PluginInstance.list({ where: { category: 'messaging', enabled: true } });
if (instances.length > 0) {
const inst = instances[0];
const manifest = registry.getManifest(inst.pluginType);
if (manifest && manifest.sendMessage) {
const secrets = await pluginSecrets.read(inst.id).catch(() => ({}));
const config = { ...inst.config, ...secrets };
return manifest.sendMessage(config, { to, message });
}
}
const params = new URLSearchParams({
api_username: conf.username,
api_password: conf.password,
method: 'sendSMS',
did: conf.did,
dst: toE164Digits(to),
message,
});
return new Promise((resolve, reject) => {
https.get(`https://voip.ms/api/v1/rest.php?${params}`, res => {
let body = '';
res.on('data', d => body += d);
res.on('end', () => {
try {
const json = JSON.parse(body);
if (json.status !== 'success') {
reject(new Error(`VoIP.ms error: ${json.status}`));
} else {
resolve(json);
}
} catch(e) {
reject(new Error('VoIP.ms returned invalid JSON'));
}
});
}).on('error', reject);
});
}
module.exports = {SMS: {send}};