diff --git a/dashboard/src/models/MailModel.js b/dashboard/src/models/MailModel.js index 07d1d103d..8488d57f7 100644 --- a/dashboard/src/models/MailModel.js +++ b/dashboard/src/models/MailModel.js @@ -39,16 +39,16 @@ function create() { if (result.status !== 200) return [result]; return [null, result.body]; }, - async mailboxCount(domain) { + async stats(domain) { let result; try { - result = await fetcher.get(`${API_ORIGIN}/api/v1/mail/${domain}/mailbox_count`, { access_token: accessToken }); + result = await fetcher.get(`${API_ORIGIN}/api/v1/mail/${domain}/stats`, { access_token: accessToken }); } catch (e) { return [e]; } if (result.status !== 200) return [result]; - return [null, result.body.count]; + return [null, result.body]; }, async setCatchallAddresses(domain, addresses) { let result; diff --git a/dashboard/src/views/EmailDomainsView.vue b/dashboard/src/views/EmailDomainsView.vue index ba7b8b7b5..8d16a1b82 100644 --- a/dashboard/src/views/EmailDomainsView.vue +++ b/dashboard/src/views/EmailDomainsView.vue @@ -57,10 +57,10 @@ async function refreshUsage() { domain.relayProvider = result.relay ? result.relay.provider : 'unset'; // do this even if no outbound since people forget to remove mailboxes - [error, result] = await mailModel.mailboxCount(domain.domain); + [error, result] = await mailModel.stats(domain.domain); if (error) console.error(error); - domain.mailboxCount = result; + domain.mailboxCount = result.mailboxCount; // this may temporarily fail while the mail server is restarting while (true) { diff --git a/src/appstore.js b/src/appstore.js index 2ea936b7d..3ca8102c8 100644 --- a/src/appstore.js +++ b/src/appstore.js @@ -99,78 +99,29 @@ function getFeatures() { } async function getState() { + const mailDomains = await mail.listDomains(); + const mailStats = await Promise.all(mailDomains.map(d => mail.getStats(d.domain))); + const state = { - userCount: -1, - groupCount: -1, - domainCount: -1, - incomingEmailDomainCount: -1, - mailboxCount: -1, - mailinglistCount: -1, - apps: null, // array of appstore IDs - dockerRegistryCount: -1, - backupSiteCount: -1, - externalLdapEnabled: null, // boolean - volumeCount: -1, + userCount: (await users.list()).length, + groupCount: (await groups.list()).length, + domains: (await domains.list()).map(d => d.provider), + mail: { + incomingCount: mailDomains.filter(md => md.enabled).length, + catchAllCount: mailDomains.filter(md => md.catchAll.length).length, + bannerCount: mailDomains.filter(md => md.banner.text || md.banner.html).length, + mailboxCount: mailStats.reduce((acc, cur) => acc + cur.mailboxCount, 0), + mailingListCount: mailStats.reduce((acc, cur) => acc + cur.mailingListCount, 0), + pop3Count: mailStats.reduce((acc, cur) => acc + cur.pop3Count, 0), + aliasCount: mailStats.reduce((acc, cur) => acc + cur.aliasCount, 0) + }, + apps: (await apps.list()).map(a => a.manifest.id), // array of appstore IDs + dockerRegistries: (await dockerRegistries.list()).map(r => r.provider), + backupSites: (await backupSites.list()).map(s => { return { provider: s.provider, format: s.format, encryption: !!s.encryption }; }), + externalLdap: (await externalLdap.getConfig()).provider, + volumes: (await volumes.list()).map(v => v.mountType), }; - let [error, result] = await safe(users.list()); - if (error) debug('getState: Failed to list users:', error); - else state.userCount = result.length; - - [error, result] = await safe(groups.list()); - if (error) debug('getState: Failed to list groups:', error); - else state.groupCount = result.length; - - [error, result] = await safe(domains.list()); - if (error) debug('getState: Failed to list domains:', error); - else state.domainCount = result.length; - - for (const domain of result) { - let [error, result] = await safe(mail.getDomain(domain.domain)); - if (error) { - debug('getState: Failed to get mail domain:', error); - } else { - if (state.incomingEmailDomainCount === -1) state.incomingEmailDomainCount = 0; - if (result.enabled) state.incomingEmailDomainCount += 1; - } - - [error, result] = await safe(mail.getMailboxCount(domain.domain)); - if (error) { - debug('getState: Failed to list mailboxes:', error); - } else { - if (state.mailboxCount === -1) state.mailboxCount = 0; - state.mailboxCount += result; - } - - [error, result] = await safe(mail.getListCount(domain.domain)); - if (error) { - debug('getState: Failed to list mailinglists:', error); - } else { - if (state.mailinglistCount === -1) state.mailinglistCount = 0; - state.mailinglistCount += result; - } - } - - [error, result] = await safe(apps.list()); - if (error) debug('getState: Failed to list apps:', error); - else state.apps = result.map(a => a.manifest.id); - - [error, result] = await safe(dockerRegistries.list()); - if (error) debug('getState: Failed to list docker registries:', error); - else state.dockerRegistryCount = result.length; - - [error, result] = await safe(backupSites.list()); - if (error) debug('getState: Failed to list backup sites:', error); - else state.backupSiteCount = result.length; - - [error, result] = await safe(externalLdap.getConfig()); - if (error) debug('getState: Failed to get external ldap config:', error); - else state.externalLdapEnabled = result.provider !== 'noop'; - - [error, result] = await safe(volumes.list()); - if (error) debug('getState: Failed to list volumes:', error); - else state.volumeCount = result.length; - return state; } @@ -195,7 +146,8 @@ async function getSubscription() { const token = await settings.get(settings.APPSTORE_API_TOKEN_KEY); if (!token) throw new BoxError(BoxError.LICENSE_ERROR, 'Missing token'); - const state = await getState(); + const [stateError, state] = await safe(getState()); + if (stateError) debug('getSubscription: error getting current state', stateError); const [error, response] = await safe(superagent.post(`${await getApiServerOrigin()}/api/v1/subscription3`) .query({ accessToken: token }) diff --git a/src/ldapserver.js b/src/ldapserver.js index 18b95bab8..3406d1650 100644 --- a/src/ldapserver.js +++ b/src/ldapserver.js @@ -305,7 +305,7 @@ async function mailboxSearch(req, res, next) { } } else { // new sogo and dovecot listing (doveadm -A) // TODO figure out how proper pagination here could work - const [error, mailboxes] = await safe(mail.listAllMailboxes(1, 100000)); + const [error, mailboxes] = await safe(mail.listMailboxes(1, 100000)); if (error) return next(new ldap.OperationsError(error.message)); const results = []; @@ -398,7 +398,7 @@ async function mailingListSearch(req, res, next) { if (parts.length !== 2) return next(new ldap.NoSuchObjectError('Invalid CN')); const name = parts[0], domain = parts[1]; - const [error, result] = await safe(mail.resolveList(parts[0], parts[1])); + const [error, result] = await safe(mail.resolveMailingList(parts[0], parts[1])); if (error && error.reason === BoxError.NOT_FOUND) return next(new ldap.NoSuchObjectError('No such list')); if (error) return next(new ldap.OperationsError(error.message)); diff --git a/src/mail.js b/src/mail.js index 319f6001b..ed64d162f 100644 --- a/src/mail.js +++ b/src/mail.js @@ -25,9 +25,8 @@ exports = module.exports = { sendTestMail, - getMailboxCount, + listMailboxesByDomain, listMailboxes, - listAllMailboxes, getMailbox, addMailbox, updateMailbox, @@ -38,13 +37,14 @@ exports = module.exports = { setAliases, searchAlias, - getListCount, - getLists, - getList, - addList, - updateList, - delList, - resolveList, + listMailingListsByDomain, + getMailingList, + addMailingList, + updateMailingList, + delMailingList, + resolveMailingList, + + getStats, checkStatus, @@ -70,7 +70,6 @@ const assert = require('node:assert'), eventlog = require('./eventlog.js'), mailer = require('./mailer.js'), mailServer = require('./mailserver.js'), - mysql = require('mysql2'), net = require('node:net'), network = require('./network.js'), nodemailer = require('nodemailer'), @@ -87,7 +86,7 @@ const assert = require('node:assert'), const DNS_OPTIONS = { timeout: 20000, tries: 4 }; const REMOVE_MAILBOX_CMD = path.join(__dirname, 'scripts/rmmailbox.sh'); -// if you add a field here, listMailboxes has to be updated +// if you add a field here, listMailboxes* has to be updated const MAILBOX_FIELDS = [ 'name', 'type', 'ownerId', 'ownerType', 'aliasName', 'aliasDomain', 'creationTime', 'membersJson', 'membersOnly', 'domain', 'active', 'enablePop3', 'storageQuota', 'messagesQuota' ].join(','); const MAILDB_FIELDS = [ 'domain', 'enabled', 'mailFromValidation', 'catchAllJson', 'relayJson', 'dkimKeyJson', 'dkimSelector', 'bannerJson' ].join(','); @@ -837,14 +836,13 @@ async function sendTestMail(domain, to) { await mailer.sendTestMail(result.domain, to); } -async function listMailboxes(domain, search, page, perPage) { +async function listMailboxesByDomain(domain, page, perPage) { assert.strictEqual(typeof domain, 'string'); - assert(typeof search === 'string' || search === null); assert.strictEqual(typeof page, 'number'); assert.strictEqual(typeof perPage, 'number'); - const escapedSearch = mysql.escape('%' + search + '%'); // this also quotes the string - const searchQuery = search ? ` HAVING (name LIKE ${escapedSearch} OR aliasNames LIKE ${escapedSearch} OR aliasDomains LIKE ${escapedSearch})` : ''; // having instead of where because of aggregated columns use + // const escapedSearch = mysql.escape('%' + search + '%'); // this also quotes the string + // const searchQuery = search ? ` HAVING (name LIKE ${escapedSearch} OR aliasNames LIKE ${escapedSearch} OR aliasDomains LIKE ${escapedSearch})` : ''; // having instead of where because of aggregated columns use const query = 'SELECT m1.name AS name, m1.domain AS domain, m1.ownerId AS ownerId, m1.ownerType as ownerType, m1.active as active, JSON_ARRAYAGG(m2.name) AS aliasNames, JSON_ARRAYAGG(m2.domain) AS aliasDomains, m1.enablePop3 AS enablePop3, m1.storageQuota AS storageQuota, m1.messagesQuota AS messagesQuota ' + ` FROM (SELECT * FROM mailboxes WHERE type='${exports.TYPE_MAILBOX}') AS m1` @@ -852,7 +850,7 @@ async function listMailboxes(domain, search, page, perPage) { + ' ON m1.name=m2.aliasName AND m1.domain=m2.aliasDomain AND m1.ownerId=m2.ownerId' + ' WHERE m1.domain = ?' + ' GROUP BY m1.name, m1.domain, m1.ownerId' - + searchQuery + // + searchQuery + ' ORDER BY name LIMIT ?,?'; const results = await database.query(query, [ domain, (page-1)*perPage, perPage ]); @@ -863,7 +861,7 @@ async function listMailboxes(domain, search, page, perPage) { return results; } -async function listAllMailboxes(page, perPage) { +async function listMailboxes(page, perPage) { assert.strictEqual(typeof page, 'number'); assert.strictEqual(typeof perPage, 'number'); @@ -882,12 +880,18 @@ async function listAllMailboxes(page, perPage) { return results; } -async function getMailboxCount(domain) { +async function getStats(domain) { assert.strictEqual(typeof domain, 'string'); - const results = await database.query('SELECT COUNT(*) AS total FROM mailboxes WHERE type = ? AND domain = ?', [ exports.TYPE_MAILBOX, domain ]); + const mailboxes = await listMailboxesByDomain(domain, 1, 10000); + const mailingLists = await listMailingListsByDomain(domain, 1, 10000); - return results[0].total; + return { + mailboxCount: mailboxes.length, + pop3Count: mailboxes.filter(mb => mb.enablePop3).length, + aliasCount: mailboxes.map(mb => mb.aliases.length).reduce((a, b) => a + b, 0), + mailingListCount: mailingLists.length + }; } async function delByDomain(domain) { @@ -900,7 +904,7 @@ async function get(name, domain) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - const results = await database.query('SELECT ' + MAILBOX_FIELDS + ' FROM mailboxes WHERE name = ? AND domain = ?', [ name, domain ]); + const results = await database.query(`SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE name = ? AND domain = ?`, [ name, domain ]); if (results.length === 0) return null; return postProcessMailbox(results[0]); @@ -910,7 +914,7 @@ async function getMailbox(name, domain) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); - const results = await database.query('SELECT ' + MAILBOX_FIELDS + ' FROM mailboxes WHERE name = ? AND type = ? AND domain = ?', [ name, exports.TYPE_MAILBOX, domain ]); + const results = await database.query(`SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE name = ? AND type = ? AND domain = ?`, [ name, exports.TYPE_MAILBOX, domain ]); if (results.length === 0) return null; return postProcessMailbox(results[0]); } @@ -1089,22 +1093,13 @@ async function setAliases(name, domain, aliases, auditSource) { await eventlog.add(eventlog.ACTION_MAIL_MAILBOX_UPDATE, auditSource, { name, domain, aliases }); } -async function getListCount(domain) { +async function listMailingListsByDomain(domain, page, perPage) { assert.strictEqual(typeof domain, 'string'); - - const results = await database.query('SELECT COUNT(*) AS total FROM mailboxes WHERE type = ? AND domain = ?', [ exports.TYPE_LIST, domain ]); - - return results[0].total; -} - -async function getLists(domain, search, page, perPage) { - assert.strictEqual(typeof domain, 'string'); - assert(typeof search === 'string' || search === null); assert.strictEqual(typeof page, 'number'); assert.strictEqual(typeof perPage, 'number'); let query = `SELECT ${MAILBOX_FIELDS} FROM mailboxes WHERE type = ? AND domain = ?`; - if (search) query += ' AND (name LIKE ' + mysql.escape('%' + search + '%') + ' OR membersJson LIKE ' + mysql.escape('%' + search + '%') + ')'; + // if (search) query += ' AND (name LIKE ' + mysql.escape('%' + search + '%') + ' OR membersJson LIKE ' + mysql.escape('%' + search + '%') + ')'; query += 'ORDER BY name LIMIT ?,?'; @@ -1115,7 +1110,7 @@ async function getLists(domain, search, page, perPage) { return results; } -async function getList(name, domain) { +async function getMailingList(name, domain) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); @@ -1125,7 +1120,7 @@ async function getList(name, domain) { return postProcessMailbox(results[0]); } -async function addList(name, domain, data, auditSource) { +async function addMailingList(name, domain, data, auditSource) { assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof data, 'object'); @@ -1152,7 +1147,7 @@ async function addList(name, domain, data, auditSource) { await eventlog.add(eventlog.ACTION_MAIL_LIST_ADD, auditSource, { name, domain, members, membersOnly, active }); } -async function updateList(name, domain, data, auditSource) { +async function updateMailingList(name, domain, data, auditSource) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof data, 'object'); @@ -1179,7 +1174,7 @@ async function updateList(name, domain, data, auditSource) { await eventlog.add(eventlog.ACTION_MAIL_LIST_UPDATE, auditSource, { name, domain, oldMembers: result.members, members, membersOnly, active }); } -async function delList(name, domain, auditSource) { +async function delMailingList(name, domain, auditSource) { assert.strictEqual(typeof name, 'string'); assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof auditSource, 'object'); @@ -1192,14 +1187,14 @@ async function delList(name, domain, auditSource) { } // resolves the members of a list. i.e the lists and aliases -async function resolveList(listName, listDomain) { +async function resolveMailingList(listName, listDomain) { assert.strictEqual(typeof listName, 'string'); assert.strictEqual(typeof listDomain, 'string'); const mailDomains = await listDomains(); const mailInDomains = mailDomains.filter(function (d) { return d.enabled; }).map(function (d) { return d.domain; }).join(','); - const list = await getList(listName, listDomain); + const list = await getMailingList(listName, listDomain); if (!list) throw new BoxError(BoxError.NOT_FOUND, 'List not found'); const resolvedMembers = [], visited = []; // slice creates a copy of array @@ -1217,7 +1212,7 @@ async function resolveList(listName, listDomain) { const member =`${memberName}@${memberDomain}`; // cleaned up without any '+' subaddress if (visited.includes(member)) { - debug(`resolveList: list ${listName}@${listDomain} has a recursion at member ${member}`); + debug(`resolveMailingList: list ${listName}@${listDomain} has a recursion at member ${member}`); continue; } visited.push(member); diff --git a/src/routes/mail.js b/src/routes/mail.js index c8b210aba..7b1f0a1ab 100644 --- a/src/routes/mail.js +++ b/src/routes/mail.js @@ -14,7 +14,7 @@ exports = module.exports = { sendTestMail, - listMailboxes, + listMailboxesByDomain, getMailbox, addMailbox, updateMailbox, @@ -23,13 +23,13 @@ exports = module.exports = { getAliases, setAliases, - getLists, - getList, - addList, - updateList, - delList, + listMailingListsByDomain, + getMailingList, + addMailingList, + updateMailingList, + delMailingList, - getMailboxCount + getStats }; const assert = require('node:assert'), @@ -143,7 +143,7 @@ async function sendTestMail(req, res, next) { next(new HttpSuccess(202)); } -async function listMailboxes(req, res, next) { +async function listMailboxesByDomain(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); const page = typeof req.query.page === 'string' ? parseInt(req.query.page) : 1; @@ -152,21 +152,19 @@ async function listMailboxes(req, res, next) { const perPage = typeof req.query.per_page === 'string'? parseInt(req.query.per_page) : 25; if (!perPage || perPage < 0) return next(new HttpError(400, 'per_page query param has to be a positive number')); - if (req.query.search && typeof req.query.search !== 'string') return next(new HttpError(400, 'search must be a string')); - - const [error, mailboxes] = await safe(mail.listMailboxes(req.params.domain, req.query.search || null, page, perPage)); + const [error, mailboxes] = await safe(mail.listMailboxesByDomain(req.params.domain, page, perPage)); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(200, { mailboxes })); } -async function getMailboxCount(req, res, next) { +async function getStats(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); - const [error, count] = await safe(mail.getMailboxCount(req.params.domain)); + const [error, stats] = await safe(mail.getStats(req.params.domain)); if (error) return next(BoxError.toHttpError(error)); - next(new HttpSuccess(200, { count })); + next(new HttpSuccess(200, stats)); } async function getMailbox(req, res, next) { @@ -274,7 +272,7 @@ async function setBanner(req, res, next) { next(new HttpSuccess(202)); } -async function getLists(req, res, next) { +async function listMailingListsByDomain(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); const page = typeof req.query.page === 'string' ? parseInt(req.query.page) : 1; @@ -283,26 +281,24 @@ async function getLists(req, res, next) { const perPage = typeof req.query.per_page === 'string'? parseInt(req.query.per_page) : 25; if (!perPage || perPage < 0) return next(new HttpError(400, 'per_page query param has to be a positive number')); - if (req.query.search && typeof req.query.search !== 'string') return next(new HttpError(400, 'search must be a string')); - - const [error, lists] = await safe(mail.getLists(req.params.domain, req.query.search || null, page, perPage)); + const [error, lists] = await safe(mail.listMailingListsByDomain(req.params.domain, page, perPage)); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(200, { lists })); } -async function getList(req, res, next) { +async function getMailingList(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); assert.strictEqual(typeof req.params.name, 'string'); - const [error, result] = await safe(mail.getList(req.params.name, req.params.domain)); + const [error, result] = await safe(mail.getMailingList(req.params.name, req.params.domain)); if (error) return next(BoxError.toHttpError(error)); if (!result) return next(new HttpError(404, 'List not found')); next(new HttpSuccess(200, { list: result })); } -async function addList(req, res, next) { +async function addMailingList(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); assert.strictEqual(typeof req.body, 'object'); @@ -316,13 +312,13 @@ async function addList(req, res, next) { if (typeof req.body.membersOnly !== 'boolean') return next(new HttpError(400, 'membersOnly must be a boolean')); if (typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean')); - const [error] = await safe(mail.addList(req.body.name, req.params.domain, req.body, AuditSource.fromRequest(req))); + const [error] = await safe(mail.addMailingList(req.body.name, req.params.domain, req.body, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(201, {})); } -async function updateList(req, res, next) { +async function updateMailingList(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); assert.strictEqual(typeof req.params.name, 'string'); @@ -335,17 +331,17 @@ async function updateList(req, res, next) { if (typeof req.body.membersOnly !== 'boolean') return next(new HttpError(400, 'membersOnly must be a boolean')); if (typeof req.body.active !== 'boolean') return next(new HttpError(400, 'active must be a boolean')); - const [error] = await safe(mail.updateList(req.params.name, req.params.domain, req.body, AuditSource.fromRequest(req))); + const [error] = await safe(mail.updateMailingList(req.params.name, req.params.domain, req.body, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(204)); } -async function delList(req, res, next) { +async function delMailingList(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); assert.strictEqual(typeof req.params.name, 'string'); - const [error] = await safe(mail.delList(req.params.name, req.params.domain, AuditSource.fromRequest(req))); + const [error] = await safe(mail.delMailingList(req.params.name, req.params.domain, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(204)); diff --git a/src/server.js b/src/server.js index 526e1fd9d..29cd5f245 100644 --- a/src/server.js +++ b/src/server.js @@ -404,19 +404,19 @@ async function initializeExpressSync() { router.post('/api/v1/mail/:domain/relay', json, token, authorizeAdmin, routes.mail.setMailRelay); router.post('/api/v1/mail/:domain/banner', json, token, authorizeMailManager, routes.mail.setBanner); router.post('/api/v1/mail/:domain/send_test_mail', json, token, authorizeMailManager, routes.mail.sendTestMail); - router.get ('/api/v1/mail/:domain/mailbox_count', token, authorizeMailManager, routes.mail.getMailboxCount); - router.get ('/api/v1/mail/:domain/mailboxes', token, authorizeMailManager, routes.mail.listMailboxes); + router.get ('/api/v1/mail/:domain/stats', token, authorizeMailManager, routes.mail.getStats); + router.get ('/api/v1/mail/:domain/mailboxes', token, authorizeMailManager, routes.mail.listMailboxesByDomain); router.get ('/api/v1/mail/:domain/mailboxes/:name', token, authorizeMailManager, routes.mail.getMailbox); router.post('/api/v1/mail/:domain/mailboxes', json, token, authorizeMailManager, routes.mail.addMailbox); router.post('/api/v1/mail/:domain/mailboxes/:name', json, token, authorizeMailManager, routes.mail.updateMailbox); router.del ('/api/v1/mail/:domain/mailboxes/:name', json, token, authorizeMailManager, routes.mail.delMailbox); router.get ('/api/v1/mail/:domain/mailboxes/:name/aliases', token, authorizeMailManager, routes.mail.getAliases); router.put ('/api/v1/mail/:domain/mailboxes/:name/aliases', json, token, authorizeMailManager, routes.mail.setAliases); - router.get ('/api/v1/mail/:domain/lists', token, authorizeMailManager, routes.mail.getLists); - router.post('/api/v1/mail/:domain/lists', json, token, authorizeMailManager, routes.mail.addList); - router.get ('/api/v1/mail/:domain/lists/:name', token, authorizeMailManager, routes.mail.getList); - router.post('/api/v1/mail/:domain/lists/:name', json, token, authorizeMailManager, routes.mail.updateList); - router.del ('/api/v1/mail/:domain/lists/:name', token, authorizeMailManager, routes.mail.delList); + router.get ('/api/v1/mail/:domain/lists', token, authorizeMailManager, routes.mail.listMailingListsByDomain); + router.post('/api/v1/mail/:domain/lists', json, token, authorizeMailManager, routes.mail.addMailingList); + router.get ('/api/v1/mail/:domain/lists/:name', token, authorizeMailManager, routes.mail.getMailingList); + router.post('/api/v1/mail/:domain/lists/:name', json, token, authorizeMailManager, routes.mail.updateMailingList); + router.del ('/api/v1/mail/:domain/lists/:name', token, authorizeMailManager, routes.mail.delMailingList); // domain routes router.post('/api/v1/domains', json, token, authorizeAdmin, routes.domains.add); diff --git a/src/test/ldapserver-test.js b/src/test/ldapserver-test.js index 11516c186..971b67ca1 100644 --- a/src/test/ldapserver-test.js +++ b/src/test/ldapserver-test.js @@ -360,7 +360,7 @@ describe('Ldap Server', function () { const LIST_NAME = 'devs', LIST = `devs@${domain.domain}`; before(async function () { - await mail.addList(LIST_NAME, domain.domain, { members: [ mailbox , 'outsider@external.com' ], membersOnly: false, active: true }, auditSource); + await mail.addMailingList(LIST_NAME, domain.domain, { members: [ mailbox , 'outsider@external.com' ], membersOnly: false, active: true }, auditSource); }); it('get specific list', async function () { @@ -376,7 +376,7 @@ describe('Ldap Server', function () { }); it('inactive list', async function () { - await mail.updateList(LIST_NAME, domain.domain, { members: [ mailbox , 'outsider@external.com' ], membersOnly: false, active: false }, auditSource); + await mail.updateMailingList(LIST_NAME, domain.domain, { members: [ mailbox , 'outsider@external.com' ], membersOnly: false, active: false }, auditSource); const [error] = await safe(ldapSearch('cn=devs@example.com,ou=mailinglists,dc=cloudron', 'objectclass=mailGroup')); expect(error).to.be.a(ldap.NoSuchObjectError); }); diff --git a/src/test/mail-test.js b/src/test/mail-test.js index e53009541..55114e0c3 100644 --- a/src/test/mail-test.js +++ b/src/test/mail-test.js @@ -156,15 +156,15 @@ describe('Mail', function () { expect(mailbox.enablePop3).to.be(true); }); - it('list mailboxes succeeds', async function () { - const mailboxes = await mail.listMailboxes(domain.domain, null /* search */, 1, 10); + it('list mailboxes by domain succeeds', async function () { + const mailboxes = await mail.listMailboxesByDomain(domain.domain, 1, 10); expect(mailboxes.length).to.be(2); expect(mailboxes[0].name).to.be('girish'); expect(mailboxes[1].name).to.be('support'); }); it('list all mailboxes succeeds', async function () { - const mailboxes = await mail.listAllMailboxes(1, 10); + const mailboxes = await mail.listMailboxes(1, 10); expect(mailboxes.length).to.be(2); expect(mailboxes[0].name).to.be('girish'); expect(mailboxes[0].domain).to.be(domain.domain); @@ -173,8 +173,8 @@ describe('Mail', function () { }); it('mailbox count succeeds', async function () { - const count = await mail.getMailboxCount(domain.domain); - expect(count).to.be(2); + const stats = await mail.getStats(domain.domain); + expect(stats.mailboxCount).to.be(2); }); it('can set alias', async function () { @@ -211,27 +211,27 @@ describe('Mail', function () { }); it('add list succeeds', async function () { - await mail.addList('people', domain.domain, { members: [ 'test@cloudron.io' ], membersOnly: false, active: true }, auditSource); + await mail.addMailingList('people', domain.domain, { members: [ 'test@cloudron.io' ], membersOnly: false, active: true }, auditSource); }); it('cannot add dup list', async function () { - const [error] = await safe(mail.addList('people', domain.domain, { members: [ 'admin@cloudron.io' ], membersOnly: false, active: true }, auditSource)); + const [error] = await safe(mail.addMailingList('people', domain.domain, { members: [ 'admin@cloudron.io' ], membersOnly: false, active: true }, auditSource)); expect(error.reason).to.be(BoxError.ALREADY_EXISTS); }); it('cannot get non-existing list', async function () { - const result = await mail.getList('random', domain.domain); + const result = await mail.getMailingList('random', domain.domain); expect(result).to.be(null); }); it('del list succeeds', async function () { - await mail.delList('people', domain.domain, auditSource); - const result = await mail.getList('people', domain.domain); + await mail.delMailingList('people', domain.domain, auditSource); + const result = await mail.getMailingList('people', domain.domain); expect(result).to.be(null); }); it('del non-existent list fails', async function () { - const [error] = await safe(mail.delList('people', domain.domain, auditSource)); + const [error] = await safe(mail.delMailingList('people', domain.domain, auditSource)); expect(error.reason).to.be(BoxError.NOT_FOUND); });