diff --git a/lib/routes/api/accounts.js b/lib/routes/api/accounts.js index 1345232..bba6dd3 100644 --- a/lib/routes/api/accounts.js +++ b/lib/routes/api/accounts.js @@ -519,7 +519,7 @@ router.delete('/:sid', async(req, res) => { await validateDelete(req, sid); const [account] = await promisePool.query('SELECT * FROM accounts WHERE account_sid = ?', sid); - const {sip_realm, stripe_customer_id} = account[0]; + const {sip_realm, stripe_customer_id, registration_hook_sid} = account[0]; /* remove dns records */ if (process.env.NODE_ENV !== 'test' || process.env.DME_API_KEY) { @@ -560,6 +560,15 @@ account_subscriptions WHERE account_sid = ?) await promisePool.execute('DELETE from applications where account_sid = ?', [sid]); await promisePool.execute('DELETE from accounts where account_sid = ?', [sid]); + if (registration_hook_sid) { + /* remove registration hook if only used by this account */ + const sql = 'SELECT COUNT(*) as count FROM accounts WHERE registration_hook_sid = ?'; + const [r] = await promisePool.query(sql, registration_hook_sid); + if (r[0]?.count === 0) { + await promisePool.execute('DELETE from webhooks where webhook_sid = ?', [registration_hook_sid]); + } + } + if (stripe_customer_id) { const response = await deleteCustomer(logger, stripe_customer_id); logger.info({response}, `deleted stripe customer_id ${stripe_customer_id} for account_si ${sid}`); diff --git a/lib/routes/api/applications.js b/lib/routes/api/applications.js index 99a0b06..6203fd1 100644 --- a/lib/routes/api/applications.js +++ b/lib/routes/api/applications.js @@ -3,12 +3,12 @@ const {DbErrorBadRequest, DbErrorUnprocessableRequest} = require('../../utils/er const Application = require('../../models/application'); const Account = require('../../models/account'); const Webhook = require('../../models/webhook'); +const {promisePool} = require('../../db'); const decorate = require('./decorate'); const sysError = require('../error'); const preconditions = { 'add': validateAdd, - 'update': validateUpdate, - 'delete': validateDelete + 'update': validateUpdate }; /* only user-level tokens can add applications */ @@ -59,7 +59,7 @@ async function validateDelete(req, sid) { if (assignedPhoneNumbers > 0) throw new DbErrorUnprocessableRequest('cannot delete application with phone numbers'); } -decorate(router, Application, ['delete'], preconditions); +decorate(router, Application, [], preconditions); /* add */ router.post('/', async(req, res) => { @@ -111,6 +111,47 @@ router.get('/:sid', async(req, res) => { } }); +/* delete */ +router.delete('/:sid', async(req, res) => { + const sid = req.params.sid; + const logger = req.app.locals.logger; + try { + await validateDelete(req, sid); + + const [application] = await promisePool.query('SELECT * FROM applications WHERE application_sid = ?', sid); + const {call_hook_sid, call_status_hook_sid, messaging_hook_sid} = application[0]; + logger.info({call_hook_sid, call_status_hook_sid, messaging_hook_sid, sid}, 'deleting application'); + await promisePool.execute('DELETE from applications where application_sid = ?', [sid]); + + if (call_hook_sid) { + /* remove call hook if only used by this app */ + const sql = 'SELECT COUNT(*) as count FROM applications WHERE call_hook_sid = ?'; + const [r] = await promisePool.query(sql, call_hook_sid); + if (r[0]?.count === 0) { + await promisePool.execute('DELETE from webhooks where webhook_sid = ?', [call_hook_sid]); + } + } + if (call_status_hook_sid) { + const sql = 'SELECT COUNT(*) as count FROM applications WHERE call_status_hook_sid = ?'; + const [r] = await promisePool.query(sql, call_status_hook_sid); + if (r[0]?.count === 0) { + await promisePool.execute('DELETE from webhooks where webhook_sid = ?', [call_status_hook_sid]); + } + } + if (messaging_hook_sid) { + const sql = 'SELECT COUNT(*) as count FROM applications WHERE messaging_hook_sid = ?'; + const [r] = await promisePool.query(sql, messaging_hook_sid); + if (r[0]?.count === 0) { + await promisePool.execute('DELETE from webhooks where webhook_sid = ?', [messaging_hook_sid]); + } + } + + res.status(204).end(); + } catch (err) { + sysError(logger, res, err); + } +}); + /* update */ router.put('/:sid', async(req, res) => { const sid = req.params.sid; diff --git a/lib/routes/api/index.js b/lib/routes/api/index.js index 64a71f5..f46ed6c 100644 --- a/lib/routes/api/index.js +++ b/lib/routes/api/index.js @@ -7,9 +7,16 @@ const isAdminScope = (req, res, next) => { message: 'insufficient privileges' }); }; +const isAdminOrSPScope = (req, res, next) => { + if (req.user.hasScope('admin') || req.user.hasScope('service_provider')) return next(); + res.status(403).json({ + status: 'fail', + message: 'insufficient privileges' + }); +}; api.use('/BetaInviteCodes', isAdminScope, require('./beta-invite-codes')); -api.use('/ServiceProviders', isAdminScope, require('./service-providers')); +api.use('/ServiceProviders', isAdminOrSPScope, require('./service-providers')); api.use('/VoipCarriers', require('./voip-carriers')); api.use('/Webhooks', require('./webhooks')); api.use('/SipGateways', require('./sip-gateways'));