diff --git a/dashboard/index.html b/dashboard/index.html index 8316698cd..e6c9f9f56 100644 --- a/dashboard/index.html +++ b/dashboard/index.html @@ -150,6 +150,7 @@ {{ 'domains.title' | tr }} {{ 'emails.title' | tr }} {{ 'email.incoming.mailboxes.title' | tr }} + {{ 'email.incoming.mailinglists.title' | tr }} {{ 'eventlog.title' | tr }} {{ 'network.title' | tr }} {{ 'services.title' | tr }} diff --git a/dashboard/public/js/index.js b/dashboard/public/js/index.js index 00122fb17..3466f73dc 100644 --- a/dashboard/public/js/index.js +++ b/dashboard/public/js/index.js @@ -88,6 +88,9 @@ app.config(['$routeProvider', function ($routeProvider) { }).when('/emails-mailboxes', { // controller: 'EmailsEventlogController', // templateUrl: 'views/emails-eventlog.html?' + window.VITE_CACHE_ID + }).when('/emails-mailinglists', { + // controller: 'EmailsEventlogController', + // templateUrl: 'views/emails-eventlog.html?' + window.VITE_CACHE_ID }).when('/emails-queue', { // controller: 'EmailsQueueController', // templateUrl: 'views/emails-queue.html?' + window.VITE_CACHE_ID diff --git a/dashboard/src/Index.vue b/dashboard/src/Index.vue index 49fee5fb7..8fa7f69fd 100644 --- a/dashboard/src/Index.vue +++ b/dashboard/src/Index.vue @@ -13,6 +13,7 @@ import EmailDomainView from './views/EmailDomainView.vue'; import EmailsEventlogView from './views/EmailsEventlogView.vue'; import EventlogView from './views/EventlogView.vue'; import MailboxesView from './views/MailboxesView.vue'; +import MailinglistsView from './views/MailinglistsView.vue'; import NetworkView from './views/NetworkView.vue'; import ProfileView from './views/ProfileView.vue'; import ServicesView from './views/ServicesView.vue'; @@ -34,6 +35,7 @@ const VIEWS = { EMAIL_DOMAIN: 'email-domain', EMAILS_EVENTLOG: 'emails-eventlog', EMAILS_MAILBOXES: 'emails-mailboxes', + EMAILS_MAILINGLISTS: 'emails-mailinglists', EVENTLOG: 'eventlog', NETWORK: 'network', PROFILE: 'profile', @@ -69,6 +71,8 @@ function onHashChange() { view.value = VIEWS.EMAILS_EVENTLOG; } else if (v === VIEWS.EMAILS_MAILBOXES) { view.value = VIEWS.EMAILS_MAILBOXES; + } else if (v === VIEWS.EMAILS_MAILINGLISTS) { + view.value = VIEWS.EMAILS_MAILINGLISTS; } else if (v.indexOf(VIEWS.EMAIL) === 0) { view.value = VIEWS.EMAIL_DOMAIN; } else if (v === VIEWS.EVENTLOG) { @@ -126,6 +130,7 @@ onMounted(async () => { + diff --git a/dashboard/src/components/CatchAllSettingsItem.vue b/dashboard/src/components/CatchAllSettingsItem.vue index 491df5899..8351f5cc5 100644 --- a/dashboard/src/components/CatchAllSettingsItem.vue +++ b/dashboard/src/components/CatchAllSettingsItem.vue @@ -5,11 +5,13 @@ import { MultiSelect, InputGroup, Button, FormGroup } from 'pankow'; import SettingsItem from './SettingsItem.vue'; import DomainsModel from '../models/DomainsModel.js'; import MailModel from '../models/MailModel.js'; +import MailboxesModel from '../models/MailboxesModel.js'; const props = defineProps([ 'domainConfig' ]); const domainsModel = DomainsModel.create(); const mailModel = MailModel.create(); +const mailboxesModel = MailboxesModel.create(); const busy = ref(true); const addresses = ref([]); @@ -46,7 +48,7 @@ onMounted(async () => { // only for inbound enabled but then we have extra rest calls for (const domain of result) { - const [error, result] = await mailModel.listMailboxes(domain.domain); + const [error, result] = await mailboxesModel.list(domain.domain); if (error) return console.error(error); allAddresses.value = allAddresses.value.concat(result.map(mailbox => { diff --git a/dashboard/src/components/MailinglistDialog.vue b/dashboard/src/components/MailinglistDialog.vue new file mode 100644 index 000000000..2157245d6 --- /dev/null +++ b/dashboard/src/components/MailinglistDialog.vue @@ -0,0 +1,106 @@ + + + \ No newline at end of file diff --git a/dashboard/src/models/MailModel.js b/dashboard/src/models/MailModel.js index 3c9693bdd..244daa907 100644 --- a/dashboard/src/models/MailModel.js +++ b/dashboard/src/models/MailModel.js @@ -39,28 +39,6 @@ function create() { if (result.status !== 200) return [result]; return [null, result.body.count]; }, - async listMailboxes(domain, search = '', page = 1, per_page = 1000) { - let result; - try { - result = await fetcher.get(`${API_ORIGIN}/api/v1/mail/${domain}/mailboxes`, { search, page, per_page, access_token: accessToken }); - } catch (e) { - return [e]; - } - - if (result.status !== 200) return [result]; - return [null, result.body.mailboxes]; - }, - async getMailbox(domain, mailboxName) { - let result; - try { - result = await fetcher.get(`${API_ORIGIN}/api/v1/mail/${domain}/mailboxes/${mailboxName}`, { access_token: accessToken }); - } catch (e) { - return [e]; - } - - if (result.status !== 200) return [result]; - return [null, result.body.mailbox]; - }, async setCatchallAddresses(domain, addresses) { let result; try { diff --git a/dashboard/src/models/MailinglistsModel.js b/dashboard/src/models/MailinglistsModel.js new file mode 100644 index 000000000..4ee14a381 --- /dev/null +++ b/dashboard/src/models/MailinglistsModel.js @@ -0,0 +1,82 @@ + +import { fetcher } from 'pankow'; +import { API_ORIGIN } from '../constants.js'; + +function create() { + const accessToken = localStorage.token; + + return { + async list(domain) { + let result; + try { + result = await fetcher.get(`${API_ORIGIN}/api/v1/mail/${domain}/lists`, { page: 1, per_page: 1000, access_token: accessToken }); + } catch (e) { + return [e]; + } + + if (result.status !== 200) return [result]; + return [null, result.body.lists]; + }, + async get(domain, name) { + let result; + try { + result = await fetcher.get(`${API_ORIGIN}/api/v1/mail/${domain}/lists/${name}`, { access_token: accessToken }); + } catch (e) { + return [e]; + } + + if (result.status !== 200) return [result]; + return [null, result.body.list]; + }, + async add(domain, name, options) { + const data = { + name: name, + members: options.members || [], + membersOnly: !!options.membersOnly, + active: !!options.active, + }; + + let result; + try { + result = await fetcher.post(`${API_ORIGIN}/api/v1/mail/${domain}/lists`, data, { access_token: accessToken }); + } catch (e) { + return [e]; + } + + if (result.status !== 201) return [result]; + return [null]; + }, + async update(domain, name, options) { + const data = { + members: options.members || [], + membersOnly: !!options.membersOnly, + active: !!options.active, + }; + + let result; + try { + result = await fetcher.post(`${API_ORIGIN}/api/v1/mail/${domain}/lists/${name}`, data, { access_token: accessToken }); + } catch (e) { + return [e]; + } + + if (result.status !== 204) return [result]; + return [null]; + }, + async remove(domain, name) { + let result; + try { + result = await fetcher.del(`${API_ORIGIN}/api/v1/mail/${domain}/lists/${name}`, null, { access_token: accessToken }); + } catch (e) { + return [e]; + } + + if (result.status !== 204) return [result]; + return [null]; + }, + }; +} + +export default { + create, +}; diff --git a/dashboard/src/views/MailboxesView.vue b/dashboard/src/views/MailboxesView.vue index 0ccfe856b..19b5524ae 100644 --- a/dashboard/src/views/MailboxesView.vue +++ b/dashboard/src/views/MailboxesView.vue @@ -86,7 +86,7 @@ async function refresh() { const usage = result; - [error, result] = await mailModel.listMailboxes(domain); + [error, result] = await mailboxesModel.list(domain); if (error) throw error; result.forEach((m) => { @@ -171,7 +171,7 @@ onMounted(async () => { diff --git a/dashboard/src/views/MailinglistsView.vue b/dashboard/src/views/MailinglistsView.vue new file mode 100644 index 000000000..907316153 --- /dev/null +++ b/dashboard/src/views/MailinglistsView.vue @@ -0,0 +1,143 @@ + + +