From 057ef829c117be0d4589c2178340a9915c0997bd Mon Sep 17 00:00:00 2001 From: NGPixel Date: Mon, 30 Oct 2023 00:42:42 +0000 Subject: [PATCH] feat: mail processing with vue email templates --- server/core/mail.mjs | 34 +- server/db/migrations/3.0.0.mjs | 1 + server/graph/resolvers/mail.mjs | 10 +- server/graph/schemas/mail.graphql | 2 + server/locales/en.json | 3 + server/models/navigation.mjs | 2 +- server/models/tree.mjs | 1 - server/package.json | 3 + server/pnpm-lock.yaml | 559 ++++++++++++++++++ server/templates/account-reset-pwd.html | 304 ---------- server/templates/account-verify.html | 304 ---------- server/templates/mail/Test.vue | 55 ++ server/templates/test.html | 291 --------- ux/package.json | 2 + ux/pnpm-lock.yaml | 53 +- ux/public/_assets/icons/fluent-template.svg | 1 + .../components/MailTemplateEditorOverlay.vue | 164 +++++ ux/src/layouts/AdminLayout.vue | 1 + ux/src/pages/AdminMail.vue | 63 +- 19 files changed, 894 insertions(+), 959 deletions(-) delete mode 100644 server/templates/account-reset-pwd.html delete mode 100644 server/templates/account-verify.html create mode 100644 server/templates/mail/Test.vue delete mode 100644 server/templates/test.html create mode 100644 ux/public/_assets/icons/fluent-template.svg create mode 100644 ux/src/components/MailTemplateEditorOverlay.vue diff --git a/server/core/mail.mjs b/server/core/mail.mjs index 64a91eaf..1afb10a2 100644 --- a/server/core/mail.mjs +++ b/server/core/mail.mjs @@ -1,9 +1,10 @@ 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') } } } diff --git a/server/db/migrations/3.0.0.mjs b/server/db/migrations/3.0.0.mjs index acef92f5..c1f3c39c 100644 --- a/server/db/migrations/3.0.0.mjs +++ b/server/db/migrations/3.0.0.mjs @@ -502,6 +502,7 @@ export async function up (knex) { value: { senderName: '', senderEmail: '', + defaultBaseURL: 'https://wiki.example.com', host: '', port: 465, name: '', diff --git a/server/graph/resolvers/mail.mjs b/server/graph/resolvers/mail.mjs index 25bec4ba..66438028 100644 --- a/server/graph/resolvers/mail.mjs +++ b/server/graph/resolvers/mail.mjs @@ -1,5 +1,6 @@ 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, diff --git a/server/graph/schemas/mail.graphql b/server/graph/schemas/mail.graphql index fb5836ca..94e9e0cc 100644 --- a/server/graph/schemas/mail.graphql +++ b/server/graph/schemas/mail.graphql @@ -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 diff --git a/server/locales/en.json b/server/locales/en.json index b8697312..eb6d5f7d 100644 --- a/server/locales/en.json +++ b/server/locales/en.json @@ -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", diff --git a/server/models/navigation.mjs b/server/models/navigation.mjs index 0a98a423..1b723147 100644 --- a/server/models/navigation.mjs +++ b/server/models/navigation.mjs @@ -1,5 +1,5 @@ import { Model } from 'objection' -import { has, intersection, templateSettings } from 'lodash-es' +import { has, intersection } from 'lodash-es' /** * Navigation model diff --git a/server/models/tree.mjs b/server/models/tree.mjs index 6970a88e..4007c694 100644 --- a/server/models/tree.mjs +++ b/server/models/tree.mjs @@ -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-]+$/ diff --git a/server/package.json b/server/package.json index 740c6e42..86658f24 100644 --- a/server/package.json +++ b/server/package.json @@ -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" }, diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml index fc8d4c42..fd26a6f5 100644 --- a/server/pnpm-lock.yaml +++ b/server/pnpm-lock.yaml @@ -41,6 +41,9 @@ dependencies: '@simplewebauthn/server': specifier: 8.3.2 version: 8.3.2 + '@vue-email/compiler': + specifier: 0.8.0-beta.4 + version: 0.8.0-beta.4(typescript@5.2.2) acme: specifier: 3.0.3 version: 3.0.3 @@ -389,6 +392,9 @@ dependencies: twemoji: specifier: 14.0.2 version: 14.0.2 + ufo: + specifier: 1.3.1 + version: 1.3.1 uslug: specifier: 1.0.4 version: 1.0.4 @@ -398,6 +404,9 @@ dependencies: validate.js: specifier: 0.13.1 version: 0.13.1 + vue: + specifier: 3.3.7 + version: 3.3.7(typescript@5.2.2) xss: specifier: 1.0.14 version: 1.0.14 @@ -745,6 +754,33 @@ packages: - encoding dev: false + /@babel/helper-string-parser@7.22.5: + resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} + dev: false + + /@babel/parser@7.23.0: + resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.23.0 + dev: false + + /@babel/types@7.23.0: + resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 + to-fast-properties: 2.0.0 + dev: false + /@cbor-extract/cbor-extract-darwin-arm64@2.1.1: resolution: {integrity: sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA==} cpu: [arm64] @@ -793,6 +829,24 @@ packages: dev: false optional: true + /@esbuild/android-arm@0.15.18: + resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-loong64@0.15.18: + resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.52.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -836,6 +890,15 @@ packages: passport-oauth2: 1.7.0 dev: false + /@flowko/tw-to-css@0.0.6: + resolution: {integrity: sha512-JGCOIt0ubKSpNqarwrAfm4bAlyQD+53R2ukhNZf0F8r9P+BJ2zOUTSG91dccTNUzrP8zEYvdURaVDx71vNrong==} + engines: {node: '>=16.0.0'} + dependencies: + postcss: 8.4.21 + postcss-css-variables: 0.18.0(postcss@8.4.21) + postcss-js: 4.0.1(postcss@8.4.21) + dev: false + /@graphql-tools/merge@8.4.2(graphql@16.8.1): resolution: {integrity: sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw==} peerDependencies: @@ -944,6 +1007,10 @@ packages: resolution: {integrity: sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==} dev: false + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: false + /@kwsites/file-exists@1.1.1: resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} dependencies: @@ -1444,6 +1511,10 @@ packages: resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} dev: false + /@swc/wasm@1.3.95: + resolution: {integrity: sha512-cOE6Cu8bKR/69qyJKhLOQnUTZu3lUKHqI6XDhfLuG/zg/7LCwfECXhetkYBnzhB4pHre/8ZrRKaXCjcY9XJ+rQ==} + dev: false + /@tokenizer/token@0.3.0: resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} dev: false @@ -1500,6 +1571,12 @@ packages: '@types/ms': 0.7.32 dev: false + /@types/dompurify@3.0.4: + resolution: {integrity: sha512-1Jk8S/IRzNSbwQRbuGuLFHviwxQ8pX81ZEW3INY9432Cwb4VedkBYan8gSIXVLOLHBtimOmUTEYphjRVmo+30g==} + dependencies: + '@types/trusted-types': 2.0.5 + dev: false + /@types/express-serve-static-core@4.17.37: resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==} dependencies: @@ -1634,6 +1711,10 @@ packages: '@types/node': 20.8.3 dev: false + /@types/trusted-types@2.0.5: + resolution: {integrity: sha512-I3pkr8j/6tmQtKV/ZzHtuaqYSQvyjGRKH4go60Rr0IDLlFxuRT5V32uvB1mecM5G1EVAUyF/4r4QZ1GHgz+mxA==} + dev: false + /@types/tunnel@0.0.3: resolution: {integrity: sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==} dependencies: @@ -1671,6 +1752,129 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true + /@vue-email/compiler@0.8.0-beta.4(typescript@5.2.2): + resolution: {integrity: sha512-uir2SsAtcHes5gVTTmjq0ARY1TAYRU2c1Si0yNcAmoApg1bSZzCwjTYHA6yi3xIOHygKyHabAsVsOs0fUDDylw==} + dependencies: + '@vue-email/core': 0.8.0-beta.4 + '@vue-email/types': 0.8.0-beta.4 + '@vue-email/utils': 0.8.0-beta.4 + import-string: 0.1.0(typescript@5.2.2) + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - typescript + - utf-8-validate + dev: false + + /@vue-email/core@0.8.0-beta.4: + resolution: {integrity: sha512-yj8EF789cKwCrIUBhXonHw9x6b2dbU7OcjVsCAJ/vaKogaWfa4prdljpJDOhheuOmqCw03Pkapyw+0vfYsL6lA==} + dependencies: + '@flowko/tw-to-css': 0.0.6 + '@swc/wasm': 1.3.95 + '@vue-email/types': 0.8.0-beta.4 + '@vue-email/utils': 0.8.0-beta.4 + isomorphic-dompurify: 1.9.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + dev: false + + /@vue-email/types@0.8.0-beta.4: + resolution: {integrity: sha512-8g9GsZgzMhPzM6PlC1klY9c3ke2pr53ARek6LIPxPEafKsYkzt9HuDmjJYsLn+zMZo2ExQ2lGmj9ORIJunzLuQ==} + dev: false + + /@vue-email/utils@0.8.0-beta.4: + resolution: {integrity: sha512-gBE4/luBUTtZwqWb4KisuIwY3tVym2YJHH9Jme1lfonXVlFwbSs7WnrcvqmNO86ni7pI8o0Y+JuZ7OYQzJUquA==} + dependencies: + '@vue-email/types': 0.8.0-beta.4 + dev: false + + /@vue/compiler-core@3.3.7: + resolution: {integrity: sha512-pACdY6YnTNVLXsB86YD8OF9ihwpolzhhtdLVHhBL6do/ykr6kKXNYABRtNMGrsQXpEXXyAdwvWWkuTbs4MFtPQ==} + dependencies: + '@babel/parser': 7.23.0 + '@vue/shared': 3.3.7 + estree-walker: 2.0.2 + source-map-js: 1.0.2 + dev: false + + /@vue/compiler-dom@3.3.7: + resolution: {integrity: sha512-0LwkyJjnUPssXv/d1vNJ0PKfBlDoQs7n81CbO6Q0zdL7H1EzqYRrTVXDqdBVqro0aJjo/FOa1qBAPVI4PGSHBw==} + dependencies: + '@vue/compiler-core': 3.3.7 + '@vue/shared': 3.3.7 + dev: false + + /@vue/compiler-sfc@3.3.7: + resolution: {integrity: sha512-7pfldWy/J75U/ZyYIXRVqvLRw3vmfxDo2YLMwVtWVNew8Sm8d6wodM+OYFq4ll/UxfqVr0XKiVwti32PCrruAw==} + dependencies: + '@babel/parser': 7.23.0 + '@vue/compiler-core': 3.3.7 + '@vue/compiler-dom': 3.3.7 + '@vue/compiler-ssr': 3.3.7 + '@vue/reactivity-transform': 3.3.7 + '@vue/shared': 3.3.7 + estree-walker: 2.0.2 + magic-string: 0.30.5 + postcss: 8.4.31 + source-map-js: 1.0.2 + dev: false + + /@vue/compiler-ssr@3.3.7: + resolution: {integrity: sha512-TxOfNVVeH3zgBc82kcUv+emNHo+vKnlRrkv8YvQU5+Y5LJGJwSNzcmLUoxD/dNzv0bhQ/F0s+InlgV0NrApJZg==} + dependencies: + '@vue/compiler-dom': 3.3.7 + '@vue/shared': 3.3.7 + dev: false + + /@vue/reactivity-transform@3.3.7: + resolution: {integrity: sha512-APhRmLVbgE1VPGtoLQoWBJEaQk4V8JUsqrQihImVqKT+8U6Qi3t5ATcg4Y9wGAPb3kIhetpufyZ1RhwbZCIdDA==} + dependencies: + '@babel/parser': 7.23.0 + '@vue/compiler-core': 3.3.7 + '@vue/shared': 3.3.7 + estree-walker: 2.0.2 + magic-string: 0.30.5 + dev: false + + /@vue/reactivity@3.3.7: + resolution: {integrity: sha512-cZNVjWiw00708WqT0zRpyAgduG79dScKEPYJXq2xj/aMtk3SKvL3FBt2QKUlh6EHBJ1m8RhBY+ikBUzwc7/khg==} + dependencies: + '@vue/shared': 3.3.7 + dev: false + + /@vue/runtime-core@3.3.7: + resolution: {integrity: sha512-LHq9du3ubLZFdK/BP0Ysy3zhHqRfBn80Uc+T5Hz3maFJBGhci1MafccnL3rpd5/3wVfRHAe6c+PnlO2PAavPTQ==} + dependencies: + '@vue/reactivity': 3.3.7 + '@vue/shared': 3.3.7 + dev: false + + /@vue/runtime-dom@3.3.7: + resolution: {integrity: sha512-PFQU1oeJxikdDmrfoNQay5nD4tcPNYixUBruZzVX/l0eyZvFKElZUjW4KctCcs52nnpMGO6UDK+jF5oV4GT5Lw==} + dependencies: + '@vue/runtime-core': 3.3.7 + '@vue/shared': 3.3.7 + csstype: 3.1.2 + dev: false + + /@vue/server-renderer@3.3.7(vue@3.3.7): + resolution: {integrity: sha512-UlpKDInd1hIZiNuVVVvLgxpfnSouxKQOSE2bOfQpBuGwxRV/JqqTCyyjXUWiwtVMyeRaZhOYYqntxElk8FhBhw==} + peerDependencies: + vue: 3.3.7 + dependencies: + '@vue/compiler-ssr': 3.3.7 + '@vue/shared': 3.3.7 + vue: 3.3.7(typescript@5.2.2) + dev: false + + /@vue/shared@3.3.7: + resolution: {integrity: sha512-N/tbkINRUDExgcPTBvxNkvHGu504k8lzlNQRITVnm6YjOjwa4r0nnbd4Jb01sNpur5hAllyRJzSK5PvB9PPwRg==} + dev: false + /@wry/context@0.7.3: resolution: {integrity: sha512-Nl8WTesHp89RF803Se9X3IiHjdmLBrIvPMaJkl+rKVJAYyPsz1TEUbu89943HpvujtSJgDUx9W4vZw3K1Mr3sA==} engines: {node: '>=8'} @@ -2228,6 +2432,11 @@ packages: engines: {node: '>=6'} dev: true + /camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: false + /cbor-extract@2.1.1: resolution: {integrity: sha512-1UX977+L+zOJHsp0mWFG13GLwO6ucKgSmSW6JTl8B9GUvACvHeIVpFqhU92299Z6PfD09aTXDell5p+lp1rUFA==} hasBin: true @@ -2563,6 +2772,10 @@ packages: rrweb-cssom: 0.6.0 dev: false + /csstype@3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + dev: false + /cuint@0.2.2: resolution: {integrity: sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==} dev: false @@ -2945,6 +3158,216 @@ packages: resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} dev: false + /esbuild-android-64@0.15.18: + resolution: {integrity: sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /esbuild-android-arm64@0.15.18: + resolution: {integrity: sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /esbuild-darwin-64@0.15.18: + resolution: {integrity: sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /esbuild-darwin-arm64@0.15.18: + resolution: {integrity: sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /esbuild-freebsd-64@0.15.18: + resolution: {integrity: sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /esbuild-freebsd-arm64@0.15.18: + resolution: {integrity: sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-32@0.15.18: + resolution: {integrity: sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-64@0.15.18: + resolution: {integrity: sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-arm64@0.15.18: + resolution: {integrity: sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-arm@0.15.18: + resolution: {integrity: sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-mips64le@0.15.18: + resolution: {integrity: sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-ppc64le@0.15.18: + resolution: {integrity: sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-riscv64@0.15.18: + resolution: {integrity: sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-s390x@0.15.18: + resolution: {integrity: sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-netbsd-64@0.15.18: + resolution: {integrity: sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: false + optional: true + + /esbuild-openbsd-64@0.15.18: + resolution: {integrity: sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: false + optional: true + + /esbuild-sunos-64@0.15.18: + resolution: {integrity: sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: false + optional: true + + /esbuild-windows-32@0.15.18: + resolution: {integrity: sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /esbuild-windows-64@0.15.18: + resolution: {integrity: sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /esbuild-windows-arm64@0.15.18: + resolution: {integrity: sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /esbuild@0.15.18: + resolution: {integrity: sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.15.18 + '@esbuild/linux-loong64': 0.15.18 + esbuild-android-64: 0.15.18 + esbuild-android-arm64: 0.15.18 + esbuild-darwin-64: 0.15.18 + esbuild-darwin-arm64: 0.15.18 + esbuild-freebsd-64: 0.15.18 + esbuild-freebsd-arm64: 0.15.18 + esbuild-linux-32: 0.15.18 + esbuild-linux-64: 0.15.18 + esbuild-linux-arm: 0.15.18 + esbuild-linux-arm64: 0.15.18 + esbuild-linux-mips64le: 0.15.18 + esbuild-linux-ppc64le: 0.15.18 + esbuild-linux-riscv64: 0.15.18 + esbuild-linux-s390x: 0.15.18 + esbuild-netbsd-64: 0.15.18 + esbuild-openbsd-64: 0.15.18 + esbuild-sunos-64: 0.15.18 + esbuild-windows-32: 0.15.18 + esbuild-windows-64: 0.15.18 + esbuild-windows-arm64: 0.15.18 + dev: false + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -2954,6 +3377,11 @@ packages: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} dev: false + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: false + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -3257,6 +3685,10 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: false + /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -3350,6 +3782,10 @@ packages: - supports-color dev: false + /extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + dev: false + /extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -3988,6 +4424,16 @@ packages: resolve-from: 4.0.0 dev: true + /import-string@0.1.0(typescript@5.2.2): + resolution: {integrity: sha512-T65Iwz31bzXlp5xP+uxwRSZDefyH/7cBw0SquWuNksdetqrGIOWIAoPo3m5pYXgrw8Jj850jLTxNJJyw1wweXw==} + peerDependencies: + typescript: ^5.0.0 + dependencies: + '@swc/wasm': 1.3.95 + module-from-string: 3.3.0 + typescript: 5.2.2 + dev: false + /imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -4209,6 +4655,19 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true + /isomorphic-dompurify@1.9.0: + resolution: {integrity: sha512-DehfjDqzqDIX6ltkpcpXXzOOUm5Qi+3OioI0ZMzZh1C7xTsUpPMVT/UCaPmYXnOf4PjbTDA1tAyxnt8rBkYudA==} + dependencies: + '@types/dompurify': 3.0.4 + dompurify: 3.0.6 + jsdom: 22.1.0 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + dev: false + /jmespath@0.16.0: resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} engines: {node: '>= 0.6.0'} @@ -4612,6 +5071,13 @@ packages: engines: {node: '>=12'} dev: false + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + /markdown-it-abbr@1.0.4: resolution: {integrity: sha512-ZeA4Z4SaBbYysZap5iZcxKmlPL6bYA8grqhzJIHB1ikn7njnzaP8uwbtuXc4YXD5LicI4/2Xmc0VwmSiFV04gg==} dev: false @@ -4772,6 +5238,14 @@ packages: minimist: 1.2.8 dev: false + /module-from-string@3.3.0: + resolution: {integrity: sha512-VsjwtQtXZloDF7ZpBXON53U4Zz02K1/njJmfZcK+QDlYKgdL0ETq8/FeuU0G9EHxdG5XiTaITcGaldDAqJpGXA==} + engines: {node: '>=12.20.0'} + dependencies: + esbuild: 0.15.18 + nanoid: 3.3.6 + dev: false + /moment@2.29.4: resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} requiresBuild: true @@ -4822,6 +5296,12 @@ packages: dev: false optional: true + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: false + /nanoid@5.0.2: resolution: {integrity: sha512-2ustYUX1R2rL/Br5B/FMhi8d5/QzvkJ912rBYxskcpu0myTHzSZfTr1LAS2Sm7jxRUObRrSBFoyzwAhL49aVSg==} engines: {node: ^18 || >=20} @@ -5520,6 +6000,10 @@ packages: split2: 4.2.0 dev: false + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: false + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -5550,6 +6034,45 @@ packages: requiresBuild: true dev: false + /postcss-css-variables@0.18.0(postcss@8.4.21): + resolution: {integrity: sha512-lYS802gHbzn1GI+lXvy9MYIYDuGnl1WB4FTKoqMQqJ3Mab09A7a/1wZvGTkCEZJTM8mSbIyb1mJYn8f0aPye0Q==} + peerDependencies: + postcss: ^8.2.6 + dependencies: + balanced-match: 1.0.2 + escape-string-regexp: 1.0.5 + extend: 3.0.2 + postcss: 8.4.21 + dev: false + + /postcss-js@4.0.1(postcss@8.4.21): + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.21 + dev: false + + /postcss@8.4.21: + resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: false + + /postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: false + /postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -6245,6 +6768,11 @@ packages: smart-buffer: 4.2.0 dev: false + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: false + /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -6457,6 +6985,11 @@ packages: engines: {node: '>=8'} dev: false + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: false + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -6624,10 +7157,20 @@ packages: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: false + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: false + /uc.micro@1.0.6: resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} dev: false + /ufo@1.3.1: + resolution: {integrity: sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==} + dev: false + /uid-safe@2.1.5: resolution: {integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==} engines: {node: '>= 0.8'} @@ -6828,6 +7371,22 @@ packages: extsprintf: 1.4.1 dev: false + /vue@3.3.7(typescript@5.2.2): + resolution: {integrity: sha512-YEMDia1ZTv1TeBbnu6VybatmSteGOS3A3YgfINOfraCbf85wdKHzscD6HSS/vB4GAtI7sa1XPX7HcQaJ1l24zA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@vue/compiler-dom': 3.3.7 + '@vue/compiler-sfc': 3.3.7 + '@vue/runtime-dom': 3.3.7 + '@vue/server-renderer': 3.3.7(vue@3.3.7) + '@vue/shared': 3.3.7 + typescript: 5.2.2 + dev: false + /w3c-xmlserializer@4.0.0: resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} engines: {node: '>=14'} diff --git a/server/templates/account-reset-pwd.html b/server/templates/account-reset-pwd.html deleted file mode 100644 index b3d55cc5..00000000 --- a/server/templates/account-reset-pwd.html +++ /dev/null @@ -1,304 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- <%= preheadertext %> -
- - - - -
- ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌  -
- - - - - - -
- - diff --git a/server/templates/account-verify.html b/server/templates/account-verify.html deleted file mode 100644 index b3d55cc5..00000000 --- a/server/templates/account-verify.html +++ /dev/null @@ -1,304 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- <%= preheadertext %> -
- - - - -
- ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌  -
- - - - - - -
- - diff --git a/server/templates/mail/Test.vue b/server/templates/mail/Test.vue new file mode 100644 index 00000000..0dad6cf1 --- /dev/null +++ b/server/templates/mail/Test.vue @@ -0,0 +1,55 @@ + + + diff --git a/server/templates/test.html b/server/templates/test.html deleted file mode 100644 index cdb2978b..00000000 --- a/server/templates/test.html +++ /dev/null @@ -1,291 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
- <%= preheadertext %> -
- - - - -
- ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌  -
- - - - - - -
- - diff --git a/ux/package.json b/ux/package.json index 9ea95dd4..a6e26599 100644 --- a/ux/package.json +++ b/ux/package.json @@ -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", diff --git a/ux/pnpm-lock.yaml b/ux/pnpm-lock.yaml index 3b0c0c2f..97ba5dea 100644 --- a/ux/pnpm-lock.yaml +++ b/ux/pnpm-lock.yaml @@ -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: diff --git a/ux/public/_assets/icons/fluent-template.svg b/ux/public/_assets/icons/fluent-template.svg new file mode 100644 index 00000000..f7323f2f --- /dev/null +++ b/ux/public/_assets/icons/fluent-template.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ux/src/components/MailTemplateEditorOverlay.vue b/ux/src/components/MailTemplateEditorOverlay.vue new file mode 100644 index 00000000..fa148746 --- /dev/null +++ b/ux/src/components/MailTemplateEditorOverlay.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/ux/src/layouts/AdminLayout.vue b/ux/src/layouts/AdminLayout.vue index 20fe0344..051fafd8 100644 --- a/ux/src/layouts/AdminLayout.vue +++ b/ux/src/layouts/AdminLayout.vue @@ -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')) } diff --git a/ux/src/pages/AdminMail.vue b/ux/src/pages/AdminMail.vue index 4b38bdf8..60ff519f 100644 --- a/ux/src/pages/AdminMail.vue +++ b/ux/src/pages/AdminMail.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 {