Unverified Commit 057ef829 authored by NGPixel's avatar NGPixel

feat: mail processing with vue email templates

parent 97ee3af4
import nodemailer from 'nodemailer'
import { get, has, kebabCase, set, template } from 'lodash-es'
import fs from 'node:fs/promises'
import { get } from 'lodash-es'
import path from 'node:path'
import { config } from '@vue-email/compiler'
export default {
vueEmail: null,
transport: null,
templates: {},
init() {
......@@ -37,6 +38,12 @@ export default {
}
}
this.transport = nodemailer.createTransport(conf)
this.vueEmail = config(path.join(WIKI.SERVERPATH, 'templates/mail'), {
verbose: false,
options: {
baseUrl: WIKI.config.mail.defaultBaseURL
}
})
} else {
WIKI.logger.warn('Mail is not setup! Please set the configuration in the administration area!')
this.transport = null
......@@ -46,34 +53,27 @@ export default {
async send(opts) {
if (!this.transport) {
WIKI.logger.warn('Cannot send email because mail is not setup in the administration area!')
throw new WIKI.Error.MailNotConfigured()
throw new Error('ERR_MAIL_NOT_CONFIGURED')
}
await this.loadTemplate(opts.template)
return this.transport.sendMail({
headers: {
'x-mailer': 'Wiki.js'
},
from: `"${WIKI.config.mail.senderName}" <${WIKI.config.mail.senderEmail}>`,
to: opts.to,
subject: `${opts.subject} - ${WIKI.config.title}`,
subject: opts.subject,
text: opts.text,
html: get(this.templates, opts.template)({
logo: (WIKI.config.logoUrl.startsWith('http') ? '' : WIKI.config.host) + WIKI.config.logoUrl,
siteTitle: WIKI.config.title,
copyright: WIKI.config.company.length > 0 ? WIKI.config.company : 'Powered by Wiki.js',
...opts.data
})
html: await this.loadTemplate(opts.template, opts.data)
})
},
async loadTemplate(key) {
if (has(this.templates, key)) { return }
const keyKebab = kebabCase(key)
async loadTemplate(key, opts = {}) {
try {
const rawTmpl = await fs.readFile(path.join(WIKI.SERVERPATH, `templates/${keyKebab}.html`), 'utf8')
set(this.templates, key, template(rawTmpl))
return this.vueEmail.render(`${key}.vue`, {
props: opts
})
} catch (err) {
WIKI.logger.warn(err)
throw new WIKI.Error.MailTemplateFailed()
throw new Error('ERR_MAIL_RENDER_FAILED')
}
}
}
......@@ -502,6 +502,7 @@ export async function up (knex) {
value: {
senderName: '',
senderEmail: '',
defaultBaseURL: 'https://wiki.example.com',
host: '',
port: 465,
name: '',
......
import _ from 'lodash-es'
import { generateError, generateSuccess } from '../../helpers/graph.mjs'
import { withoutTrailingSlash } from 'ufo'
export default {
Query: {
......@@ -22,17 +23,15 @@ export default {
}
if (_.isEmpty(args.recipientEmail) || args.recipientEmail.length < 6) {
throw new WIKI.Error.MailInvalidRecipient()
throw new Error('ERR_MAIL_INVALID_RECIPIENT')
}
await WIKI.mail.send({
template: 'test',
template: 'Test',
to: args.recipientEmail,
subject: 'A test email from your wiki',
text: 'This is a test email sent from your wiki.',
data: {
preheadertext: 'This is a test email sent from your wiki.'
}
data: {}
})
return {
......@@ -51,6 +50,7 @@ export default {
WIKI.config.mail = {
senderName: args.senderName,
senderEmail: args.senderEmail,
defaultBaseURL: withoutTrailingSlash(args.defaultBaseURL),
host: args.host,
port: args.port,
name: args.name,
......
......@@ -14,6 +14,7 @@ extend type Mutation {
updateMailConfig(
senderName: String!
senderEmail: String!
defaultBaseURL: String!
host: String!
port: Int!
name: String!
......@@ -35,6 +36,7 @@ extend type Mutation {
type MailConfig {
senderName: String
senderEmail: String
defaultBaseURL: String
host: String
port: Int
name: String
......
......@@ -232,6 +232,7 @@
"admin.general.companyNameHint": "Name to use when displaying copyright notice in the footer. Leave empty to hide.",
"admin.general.contentLicense": "Content License",
"admin.general.contentLicenseHint": "License shown in the footer of all content pages.",
"admin.general.defaultBaseURLHint": "The default base URL to use when a site URL is not available. (e.g. https://wiki.example.com)",
"admin.general.defaultDateFormat": "Default Date Format",
"admin.general.defaultDateFormatHint": "The default date format for new users.",
"admin.general.defaultTimeFormat": "Default Time Format",
......@@ -423,6 +424,7 @@
"admin.login.welcomeRedirect": "First-time Login Redirect",
"admin.login.welcomeRedirectHint": "Optionally redirect the user to a specific page when he/she login for the first time. This can be overridden at the group level.",
"admin.mail.configuration": "Configuration",
"admin.mail.defaultBaseURL": "Default Base URL",
"admin.mail.dkim": "DKIM (optional)",
"admin.mail.dkimDomainName": "Domain Name",
"admin.mail.dkimDomainNameHint": "Domain name used for DKIM validation.",
......@@ -454,6 +456,7 @@
"admin.mail.smtpVerifySSL": "Verify SSL Certificate",
"admin.mail.smtpVerifySSLHint": "Some hosts requires SSL certificate checking to be disabled. Leave enabled for proper security.",
"admin.mail.subtitle": "Configure mail settings",
"admin.mail.templateEditor": "Mail Template Editor",
"admin.mail.templateResetPwd": "Password Reset Email",
"admin.mail.templateWelcome": "Welcome Email",
"admin.mail.templates": "Mail Templates",
......
import { Model } from 'objection'
import { has, intersection, templateSettings } from 'lodash-es'
import { has, intersection } from 'lodash-es'
/**
* Navigation model
......
......@@ -8,7 +8,6 @@ import {
generateHash
} from '../helpers/common.mjs'
import { Locale } from './locales.mjs'
import { Site } from './sites.mjs'
const rePathName = /^[a-z0-9-]+$/
......
......@@ -48,6 +48,7 @@
"@root/keypairs": "0.10.3",
"@root/pem": "1.0.4",
"@simplewebauthn/server": "8.3.2",
"@vue-email/compiler": "0.8.0-beta.4",
"acme": "3.0.3",
"akismet-api": "6.0.0",
"aws-sdk": "2.1478.0",
......@@ -164,9 +165,11 @@
"tar-fs": "3.0.4",
"turndown": "7.1.2",
"twemoji": "14.0.2",
"ufo": "1.3.1",
"uslug": "1.0.4",
"uuid": "9.0.1",
"validate.js": "0.13.1",
"vue": "3.3.7",
"xss": "1.0.14",
"yargs": "17.7.2"
},
......
<template>
<EHtml lang="en">
<EHead />
<EPreview>This is a test email sent from your Wiki.js installation.</EPreview>
<EBody :style="main">
<EContainer :style="container">
<ESection :style="box">
<img src="https://static.requarks.io/logo/wikijs-full.svg" height="50" alt="Wiki.js" />
<EHr :style="hr" />
<EText :style="paragraph"> Hello there! </EText>
<EText :style="paragraph"> This is a test email sent from your Wiki.js installation. </EText>
<EHr :style="hr" />
<EText :style="footer"> Wiki.js, an open source project. </EText>
</ESection>
</EContainer>
</EBody>
</EHtml>
</template>
<script setup>
const main = {
backgroundColor: '#f6f9fc',
fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
}
const container = {
backgroundColor: '#ffffff',
margin: '0 auto',
padding: '20px 0 48px',
marginBottom: '64px',
}
const box = {
padding: '0 48px',
}
const hr = {
borderColor: '#e6ebf1',
margin: '20px 0',
}
const paragraph = {
color: '#525f7f',
fontSize: '16px',
lineHeight: '24px',
textAlign: 'left',
}
const footer = {
color: '#8898aa',
fontSize: '12px',
lineHeight: '16px',
}
</script>
......@@ -43,6 +43,7 @@
"@tiptap/pm": "2.1.12",
"@tiptap/starter-kit": "2.1.12",
"@tiptap/vue-3": "2.1.12",
"@vue/repl": "2.6.1",
"apollo-upload-client": "17.0.0",
"browser-fs-access": "0.35.0",
"clipboard": "2.0.11",
......@@ -95,6 +96,7 @@
"tabulator-tables": "5.5.2",
"tippy.js": "6.3.7",
"twemoji": "14.0.2",
"typescript": "5.2.2",
"uuid": "9.0.1",
"v-network-graph": "0.9.10",
"vue": "3.3.6",
......
......@@ -95,6 +95,9 @@ dependencies:
'@tiptap/vue-3':
specifier: 2.1.12
version: 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)(vue@3.3.6)
'@vue/repl':
specifier: 2.6.1
version: 2.6.1
apollo-upload-client:
specifier: 17.0.0
version: 17.0.0(@apollo/client@3.8.6)(graphql@16.6.0)
......@@ -202,7 +205,7 @@ dependencies:
version: 2.1.0
pinia:
specifier: 2.1.7
version: 2.1.7(vue@3.3.6)
version: 2.1.7(typescript@5.2.2)(vue@3.3.6)
prosemirror-commands:
specifier: 1.5.2
version: 1.5.2
......@@ -251,6 +254,9 @@ dependencies:
twemoji:
specifier: 14.0.2
version: 14.0.2
typescript:
specifier: 5.2.2
version: 5.2.2
uuid:
specifier: 9.0.1
version: 9.0.1
......@@ -259,7 +265,7 @@ dependencies:
version: 0.9.10(vue@3.3.6)
vue:
specifier: 3.3.6
version: 3.3.6
version: 3.3.6(typescript@5.2.2)
vue-i18n:
specifier: 9.5.0
version: 9.5.0(vue@3.3.6)
......@@ -617,7 +623,7 @@ packages:
lodash: 4.17.21
minimist: 1.2.8
open: 8.4.2
pinia: 2.1.7(vue@3.3.6)
pinia: 2.1.7(typescript@5.2.2)(vue@3.3.6)
quasar: 2.13.0
register-service-worker: 1.7.2
rollup-plugin-visualizer: 5.9.2
......@@ -626,7 +632,7 @@ packages:
serialize-javascript: 6.0.1
table: 6.8.1
vite: 2.9.16(sass@1.32.12)
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
vue-router: 4.2.5(vue@3.3.6)
webpack-merge: 5.9.0
transitivePeerDependencies:
......@@ -659,7 +665,7 @@ packages:
'@vitejs/plugin-vue': 2.3.4(vite@2.9.16)(vue@3.3.6)
quasar: 2.13.0
vite: 2.9.16(sass@1.32.12)
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
dev: true
/@remirror/core-constants@2.0.2:
......@@ -1126,7 +1132,7 @@ packages:
'@tiptap/extension-bubble-menu': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
'@tiptap/extension-floating-menu': 2.1.12(@tiptap/core@2.1.12)(@tiptap/pm@2.1.12)
'@tiptap/pm': 2.1.12
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
dev: false
/@types/body-parser@1.19.3:
......@@ -1291,7 +1297,7 @@ packages:
vue: ^3.2.25
dependencies:
vite: 2.9.16(sass@1.32.12)
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
dev: true
/@volar/language-core@1.10.4:
......@@ -1429,6 +1435,10 @@ packages:
dependencies:
'@vue/shared': 3.3.6
/@vue/repl@2.6.1:
resolution: {integrity: sha512-Ju7ndfKF02eyLMe/9FAWyvtWwarcLK8+A9DBBSGIIdysiCw5CBLKfPE+amUyaSV0riZZtxREQVEOKLh81xDb8g==}
dev: false
/@vue/runtime-core@3.3.6:
resolution: {integrity: sha512-qp7HTP1iw1UW2ZGJ8L3zpqlngrBKvLsDAcq5lA6JvEXHmpoEmjKju7ahM9W2p/h51h0OT5F2fGlP/gMhHOmbUA==}
dependencies:
......@@ -1449,7 +1459,7 @@ packages:
dependencies:
'@vue/compiler-ssr': 3.3.6
'@vue/shared': 3.3.6
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
/@vue/shared@3.3.4:
resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==}
......@@ -4231,7 +4241,7 @@ packages:
engines: {node: '>=8.6'}
dev: true
/pinia@2.1.7(vue@3.3.6):
/pinia@2.1.7(typescript@5.2.2)(vue@3.3.6):
resolution: {integrity: sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==}
peerDependencies:
'@vue/composition-api': ^1.4.0
......@@ -4244,7 +4254,8 @@ packages:
optional: true
dependencies:
'@vue/devtools-api': 6.5.1
vue: 3.3.6
typescript: 5.2.2
vue: 3.3.6(typescript@5.2.2)
vue-demi: 0.14.6(vue@3.3.6)
/pkg-types@1.0.3:
......@@ -4927,7 +4938,7 @@ packages:
vue: ^3.2.25
dependencies:
sortablejs: 1.15.0
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
dev: false
/sortablejs@1.14.0:
......@@ -5223,6 +5234,11 @@ packages:
is-typed-array: 1.1.12
dev: true
/typescript@5.2.2:
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
engines: {node: '>=14.17'}
hasBin: true
/uc.micro@1.0.6:
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
dev: false
......@@ -5317,7 +5333,7 @@ packages:
'@dash14/svg-pan-zoom': 3.6.9
lodash-es: 4.17.21
mitt: 3.0.1
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
dev: false
/vary@1.1.2:
......@@ -5426,7 +5442,7 @@ packages:
'@vue/composition-api':
optional: true
dependencies:
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
/vue-eslint-parser@9.3.1(eslint@8.52.0):
resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==}
......@@ -5455,7 +5471,7 @@ packages:
'@intlify/core-base': 9.5.0
'@intlify/shared': 9.5.0
'@vue/devtools-api': 6.5.0
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
/vue-router@4.2.5(vue@3.3.6):
resolution: {integrity: sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==}
......@@ -5463,7 +5479,7 @@ packages:
vue: ^3.2.0
dependencies:
'@vue/devtools-api': 6.5.0
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
/vue3-otp-input@0.4.1(vue@3.3.6):
resolution: {integrity: sha512-wVl9i3DcWlO0C7fBI9V+RIP3crm/1tY72fuhvb3YM2JfbLoYofB96aPl5AgFhA0Cse5bQEMYtIvOeiqW3rfbAw==}
......@@ -5471,10 +5487,10 @@ packages:
peerDependencies:
vue: ^3.0.*
dependencies:
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
dev: false
/vue@3.3.6:
/vue@3.3.6(typescript@5.2.2):
resolution: {integrity: sha512-jJIDETeWJnoY+gfn4ZtMPMS5KtbP4ax+CT4dcQFhTnWEk8xMupFyQ0JxL28nvT/M4+p4a0ptxaV2WY0LiIxvRg==}
peerDependencies:
typescript: '*'
......@@ -5487,6 +5503,7 @@ packages:
'@vue/runtime-dom': 3.3.6
'@vue/server-renderer': 3.3.6(vue@3.3.6)
'@vue/shared': 3.3.6
typescript: 5.2.2
/vuedraggable@4.1.0(vue@3.3.6):
resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
......@@ -5494,7 +5511,7 @@ packages:
vue: ^3.0.1
dependencies:
sortablejs: 1.14.0
vue: 3.3.6
vue: 3.3.6(typescript@5.2.2)
dev: false
/w3c-keyname@2.2.8:
......
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="96px" height="96px"><linearGradient id="rycTnbCCmyN0Z_WqHqZZ_a" x1="24" x2="24" y1="3.34" y2="43.733" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#7dd8f3"/><stop offset="1" stop-color="#45b0d0"/></linearGradient><path fill="url(#rycTnbCCmyN0Z_WqHqZZ_a)" d="M37,5H11C9.895,5,9,5.895,9,7v24v10c0,1.105,0.895,2,2,2h17h9c1.105,0,2-0.895,2-2v-9v-1V7 C39,5.895,38.105,5,37,5z"/><path fill="#057093" d="M33,11H23c-0.552,0-1,0.448-1,1l0,0c0,0.552,0.448,1,1,1h10c0.552,0,1-0.448,1-1l0,0 C34,11.448,33.552,11,33,11z"/><path fill="#057093" d="M14,13h5c0.552,0,1-0.448,1-1l0,0c0-0.552-0.448-1-1-1h-5c-0.552,0-1,0.448-1,1l0,0 C13,12.552,13.448,13,14,13z"/><path fill="#057093" d="M33,38H14c-0.552,0-1-0.448-1-1V17c0-0.552,0.448-1,1-1h19c0.552,0,1,0.448,1,1v20 C34,37.552,33.552,38,33,38z"/></svg>
\ No newline at end of file
<template lang="pug">
q-layout(view='hHh lpR fFf', container)
q-header.card-header.q-px-md.q-py-sm
q-icon(name='img:/_assets/icons/fluent-template.svg', left, size='md')
span {{t(`admin.mail.templateEditor`)}}
q-space
q-btn.q-mr-sm(
flat
rounded
color='white'
:aria-label='t(`common.actions.viewDocs`)'
icon='las la-question-circle'
:href='siteStore.docsBase + `/system/mail`'
target='_blank'
type='a'
)
q-btn-group(push)
q-btn(
push
color='white'
text-color='grey-7'
:label='t(`common.actions.cancel`)'
:aria-label='t(`common.actions.cancel`)'
icon='las la-times'
@click='close'
)
q-btn(
push
color='positive'
text-color='white'
:label='t(`common.actions.save`)'
:aria-label='t(`common.actions.save`)'
icon='las la-check'
@click=''
:disabled='state.loading > 0'
)
q-page-container
q-page
//--------------------------------------------------------
//- MONACO EDITOR
//--------------------------------------------------------
.mail-template-editor
repl(:editor='Monaco' :store='store' :showTsConfig='false' theme='dark' :autoResize='true' :ssr='false' :showCompileOutput='false')
q-inner-loading(:showing='state.loading > 0')
q-spinner(color='accent', size='lg')
</template>
<script setup>
import { useI18n } from 'vue-i18n'
import { useQuasar } from 'quasar'
import { onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import gql from 'graphql-tag'
import { cloneDeep, debounce } from 'lodash-es'
import { Repl, ReplStore, File } from '@vue/repl'
import Monaco from '@vue/repl/monaco-editor'
import '@vue/repl/style.css'
import { useAdminStore } from 'src/stores/admin'
import { useEditorStore } from 'src/stores/editor'
import { useSiteStore } from 'src/stores/site'
// QUASAR
const $q = useQuasar()
// STORES
const adminStore = useAdminStore()
const siteStore = useSiteStore()
// I18N
const { t } = useI18n()
// DATA
const state = reactive({
loading: 0
})
const store = new ReplStore({
// initialize repl with previously serialized state
serializedState: location.hash.slice(1),
// starts on the output pane (mobile only) if the URL has a showOutput query
showOutput: false,
// starts on a different tab on the output pane if the URL has a outputMode query
// and default to the "preview" tab
outputMode: 'preview'
})
let editor
const monacoRef = ref(null)
// METHODS
function close () {
adminStore.$patch({ overlay: '' })
}
// MOUNTED
// onMounted(async () => {
// setTimeout(() => {
// // -> Define Monaco Theme
// monaco.editor.defineTheme('wikijs', {
// base: 'vs-dark',
// inherit: true,
// rules: [],
// colors: {
// 'editor.background': '#070a0d',
// 'editor.lineHighlightBackground': '#0d1117',
// 'editorLineNumber.foreground': '#546e7a',
// 'editorGutter.background': '#0d1117'
// }
// })
// // -> Initialize Monaco Editor
// editor = monaco.editor.create(monacoRef.value, {
// automaticLayout: true,
// cursorBlinking: 'blink',
// // cursorSmoothCaretAnimation: true,
// fontSize: 16,
// formatOnType: true,
// language: 'markdown',
// lineNumbersMinChars: 4,
// padding: { top: 10, bottom: 10 },
// scrollBeyondLastLine: false,
// tabSize: 2,
// theme: 'wikijs',
// value: '',
// wordWrap: 'on'
// })
// // -> Handle content change
// // editor.onDidChangeModelContent(debounce(ev => {
// // editor.getValue()
// // }, 500))
// // -> Post init
// editor.focus()
// console.info('BOB')
// }, 1000)
// })
// onBeforeUnmount(() => {
// if (editor) {
// editor.dispose()
// }
// })
</script>
<style lang="scss">
.mail-template-editor {
height: calc(100vh - 101px);
display: block;
position: relative;
> div {
height: 100%;
}
}
</style>
......@@ -260,6 +260,7 @@ import FooterNav from 'src/components/FooterNav.vue'
const overlays = {
EditorMarkdownConfig: defineAsyncComponent(() => import('../components/EditorMarkdownConfigOverlay.vue')),
GroupEditOverlay: defineAsyncComponent(() => import('../components/GroupEditOverlay.vue')),
MailTemplateEditorOverlay: defineAsyncComponent(() => import('../components/MailTemplateEditorOverlay.vue')),
UserEditOverlay: defineAsyncComponent(() => import('../components/UserEditOverlay.vue'))
}
......
......@@ -69,6 +69,19 @@ q-page.admin-mail
dense
:aria-label='t(`admin.mail.senderEmail`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='dns')
q-item-section
q-item-label {{t(`admin.mail.defaultBaseURL`)}}
q-item-label(caption) {{t(`admin.general.defaultBaseURLHint`)}}
q-item-section
q-input(
outlined
v-model='state.config.defaultBaseURL'
dense
:aria-label='t(`admin.mail.defaultBaseURL`)'
)
//- -----------------------
//- SMTP
//- -----------------------
......@@ -102,20 +115,6 @@ q-page.admin-mail
:aria-label='t(`admin.mail.smtpPort`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='server')
q-item-section
q-item-label {{t(`admin.mail.smtpName`)}}
q-item-label(caption) {{t(`admin.mail.smtpNameHint`)}}
q-item-section
q-input(
outlined
v-model='state.config.name'
dense
hide-bottom-space
:aria-label='t(`admin.mail.smtpName`)'
)
q-separator.q-my-sm(inset)
q-item(tag='label')
blueprint-icon(icon='secure')
q-item-section
......@@ -169,6 +168,20 @@ q-page.admin-mail
dense
:aria-label='t(`admin.mail.smtpPwd`)'
)
q-separator.q-my-sm(inset)
q-item
blueprint-icon(icon='server')
q-item-section
q-item-label {{t(`admin.mail.smtpName`)}}
q-item-label(caption) {{t(`admin.mail.smtpNameHint`)}}
q-item-section
q-input(
outlined
v-model='state.config.name'
dense
hide-bottom-space
:aria-label='t(`admin.mail.smtpName`)'
)
//- -----------------------
//- DKIM
//- -----------------------
......@@ -241,7 +254,7 @@ q-page.admin-mail
//- -----------------------
//- MAIL TEMPLATES
//- -----------------------
q-card.q-pb-sm
q-card.q-pb-sm.q-mb-md(v-if='flagStore.experimental')
q-card-section
.text-subtitle1 {{t('admin.mail.templates')}}
q-list
......@@ -255,7 +268,7 @@ q-page.admin-mail
no-caps
icon='las la-edit'
color='primary'
@click=''
@click='editTemplate(`welcome`)'
:label='t(`common.actions.edit`)'
)
q-separator(inset)
......@@ -269,13 +282,13 @@ q-page.admin-mail
no-caps
icon='las la-edit'
color='primary'
@click=''
@click='editTemplate(`pwdreset`)'
:label='t(`common.actions.edit`)'
)
//- -----------------------
//- SMTP TEST
//- -----------------------
q-card.q-pb-sm.q-mt-md
q-card.q-pb-sm
q-card-section
.text-subtitle1 {{t('admin.mail.test')}}
q-item
......@@ -310,6 +323,7 @@ import { useMeta, useQuasar } from 'quasar'
import { computed, onMounted, reactive, watch } from 'vue'
import { useAdminStore } from 'src/stores/admin'
import { useFlagsStore } from 'src/stores/flags'
import { useSiteStore } from 'src/stores/site'
// QUASAR
......@@ -319,6 +333,7 @@ const $q = useQuasar()
// STORES
const adminStore = useAdminStore()
const flagStore = useFlagsStore()
const siteStore = useSiteStore()
// I18N
......@@ -337,6 +352,7 @@ const state = reactive({
config: {
senderName: '',
senderEmail: '',
defaultBaseURL: '',
host: '',
port: 0,
secure: false,
......@@ -363,6 +379,7 @@ async function load () {
mailConfig {
senderName
senderEmail
defaultBaseURL
host
port
secure
......@@ -403,6 +420,7 @@ async function save () {
mutation saveMailConfig (
$senderName: String!
$senderEmail: String!
$defaultBaseURL: String!
$host: String!
$port: Int!
$name: String!
......@@ -418,6 +436,7 @@ async function save () {
updateMailConfig (
senderName: $senderName
senderEmail: $senderEmail
defaultBaseURL: $defaultBaseURL
host: $host
port: $port
name: $name
......@@ -441,6 +460,7 @@ async function save () {
variables: {
senderName: state.config.senderName || '',
senderEmail: state.config.senderEmail || '',
defaultBaseURL: state.config.defaultBaseURL || '',
host: state.config.host || '',
port: toSafeInteger(state.config.port) || 0,
name: state.config.name || '',
......@@ -468,6 +488,13 @@ async function save () {
state.loading--
}
function editTemplate (tmplId) {
adminStore.$patch({
overlayOpts: { id: tmplId },
overlay: 'MailTemplateEditorOverlay'
})
}
async function sendTest () {
state.loading++
try {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment