Custom Email Templates #45
12 changed files with 224 additions and 81 deletions
|
@ -677,6 +677,11 @@ smtpHost: "Host"
|
||||||
smtpPort: "Port"
|
smtpPort: "Port"
|
||||||
smtpUser: "Username"
|
smtpUser: "Username"
|
||||||
smtpPass: "Password"
|
smtpPass: "Password"
|
||||||
|
customEmailTemplate: {
|
||||||
|
title: "Custom Email Template (Advanced)",
|
||||||
|
description: "This allows you to modify the default email template that is used for any automated sent emails.",
|
||||||
|
reference: "Please refer to {link} to check if what you want to use is supported by email.",
|
||||||
|
}
|
||||||
emptyToDisableSmtpAuth: "Leave username and password empty to disable SMTP authentication"
|
emptyToDisableSmtpAuth: "Leave username and password empty to disable SMTP authentication"
|
||||||
smtpSecure: "Use implicit SSL/TLS for SMTP connections"
|
smtpSecure: "Use implicit SSL/TLS for SMTP connections"
|
||||||
smtpSecureInfo: "Turn this off when using STARTTLS"
|
smtpSecureInfo: "Turn this off when using STARTTLS"
|
||||||
|
|
14
locales/index.d.ts
vendored
14
locales/index.d.ts
vendored
|
@ -2728,6 +2728,20 @@ export interface Locale extends ILocale {
|
||||||
* パスワード
|
* パスワード
|
||||||
*/
|
*/
|
||||||
"smtpPass": string;
|
"smtpPass": string;
|
||||||
|
"customEmailTemplate": {
|
||||||
|
/**
|
||||||
|
* カスタムメールテンプレート(上級者向け)
|
||||||
|
*/
|
||||||
|
"title": string;
|
||||||
|
/**
|
||||||
|
* これにより、自動送信メールに使用されるデフォルトのメールテンプレートを変更することができます。
|
||||||
|
*/
|
||||||
|
"description": string;
|
||||||
|
/**
|
||||||
|
* {link}を参照して、使用したいものが電子メールでサポートされているかどうかを確認してください。
|
||||||
|
*/
|
||||||
|
"reference": ParameterizedString<"link">;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* ユーザー名とパスワードを空欄にすることで、SMTP認証を無効化出来ます
|
* ユーザー名とパスワードを空欄にすることで、SMTP認証を無効化出来ます
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -678,6 +678,11 @@ smtpHost: "ホスト"
|
||||||
smtpPort: "ポート"
|
smtpPort: "ポート"
|
||||||
smtpUser: "ユーザー名"
|
smtpUser: "ユーザー名"
|
||||||
smtpPass: "パスワード"
|
smtpPass: "パスワード"
|
||||||
|
customEmailTemplate: {
|
||||||
|
title: "カスタムメールテンプレート(上級者向け)",
|
||||||
|
description: "これにより、自動送信メールに使用されるデフォルトのメールテンプレートを変更することができます。",
|
||||||
|
reference: "{link}を参照して、使用したいものが電子メールでサポートされているかどうかを確認してください。",
|
||||||
|
}
|
||||||
emptyToDisableSmtpAuth: "ユーザー名とパスワードを空欄にすることで、SMTP認証を無効化出来ます"
|
emptyToDisableSmtpAuth: "ユーザー名とパスワードを空欄にすることで、SMTP認証を無効化出来ます"
|
||||||
smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
|
smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
|
||||||
smtpSecureInfo: "STARTTLS使用時はオフにします。"
|
smtpSecureInfo: "STARTTLS使用時はオフにします。"
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: leah and other forkey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class CustomEmailTemplates1737293151180 {
|
||||||
|
name = 'CustomEmailTemplates1737293151180'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" ADD "emailTemplate" text`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "emailTemplate"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -118,6 +118,7 @@
|
||||||
"fluent-ffmpeg": "2.1.3",
|
"fluent-ffmpeg": "2.1.3",
|
||||||
"form-data": "4.0.1",
|
"form-data": "4.0.1",
|
||||||
"got": "14.4.5",
|
"got": "14.4.5",
|
||||||
|
"handlebars": "^4.7.8",
|
||||||
"hpagent": "1.2.0",
|
"hpagent": "1.2.0",
|
||||||
"htmlescape": "1.1.1",
|
"htmlescape": "1.1.1",
|
||||||
"http-link-header": "1.1.3",
|
"http-link-header": "1.1.3",
|
||||||
|
|
|
@ -18,9 +18,106 @@ import { LoggerService } from '@/core/LoggerService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
import { HttpRequestService } from '@/core/HttpRequestService.js';
|
||||||
import { RedisKVCache } from '@/misc/cache.js';
|
import { RedisKVCache } from '@/misc/cache.js';
|
||||||
|
import handlebars from 'handlebars';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EmailService {
|
export class EmailService {
|
||||||
|
public static defaultEmailTemplate: string = `<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>{{ subject }}</title>
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
padding: 16px;
|
||||||
|
margin: 0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #74fff1;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: #fff;
|
||||||
|
color: #555;
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
main > header {
|
||||||
|
padding: 32px;
|
||||||
|
background: #50f0ff;
|
||||||
|
border-top-left-radius: 16px;
|
||||||
|
border-top-right-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > header > img {
|
||||||
|
max-width: 128px;
|
||||||
|
max-height: 28px;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > article {
|
||||||
|
padding: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > article > h1 {
|
||||||
|
margin: 0 0 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > footer {
|
||||||
|
padding: 32px;
|
||||||
|
border-top: solid 1px #eee;
|
||||||
|
border-bottom-left-radius: 16px;
|
||||||
|
border-bottom-right-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 16px auto;
|
||||||
|
padding: 0 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > a {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<header>
|
||||||
|
<img alt="The instances logo" src="{{logo}}"/>
|
||||||
|
</header>
|
||||||
|
<article>
|
||||||
|
<h1>{{ subject }}</h1>
|
||||||
|
<div>{{ html }}</div>
|
||||||
|
</article>
|
||||||
|
<footer>
|
||||||
|
<a href="{{ emailSettingUrl }}">Email settings</a>
|
||||||
|
</footer>
|
||||||
|
</main>
|
||||||
|
<nav>
|
||||||
|
<a href="{{ url }}">{{ host }}</a>
|
||||||
|
</nav>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
private verifymailResponseCache: RedisKVCache<{
|
private verifymailResponseCache: RedisKVCache<{
|
||||||
|
@ -67,6 +164,19 @@ export class EmailService {
|
||||||
|
|
||||||
const enableAuth = meta.smtpUser != null && meta.smtpUser !== '';
|
const enableAuth = meta.smtpUser != null && meta.smtpUser !== '';
|
||||||
|
|
||||||
|
const emailTemplateSource = meta.emailTemplate ?? EmailService.defaultEmailTemplate;
|
||||||
|
// console.log(handlebar.)
|
||||||
|
const template = handlebars.compile(emailTemplateSource);
|
||||||
|
|
||||||
|
const htmlToSend = template({
|
||||||
|
subject,
|
||||||
|
html,
|
||||||
|
emailSettingUrl,
|
||||||
|
logo: meta.logoImageUrl ?? meta.iconUrl ?? iconUrl,
|
||||||
|
url: this.config.url,
|
||||||
|
host: this.config.host,
|
||||||
|
});
|
||||||
|
|
||||||
const transporter = nodemailer.createTransport({
|
const transporter = nodemailer.createTransport({
|
||||||
host: meta.smtpHost,
|
host: meta.smtpHost,
|
||||||
port: meta.smtpPort,
|
port: meta.smtpPort,
|
||||||
|
@ -86,86 +196,7 @@ export class EmailService {
|
||||||
to: to,
|
to: to,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
text: text,
|
text: text,
|
||||||
html: `<!doctype html>
|
html: htmlToSend,
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>${ subject }</title>
|
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
background: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
padding: 16px;
|
|
||||||
margin: 0;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #86b300;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
max-width: 500px;
|
|
||||||
margin: 0 auto;
|
|
||||||
background: #fff;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
main > header {
|
|
||||||
padding: 32px;
|
|
||||||
background: #86b300;
|
|
||||||
}
|
|
||||||
main > header > img {
|
|
||||||
max-width: 128px;
|
|
||||||
max-height: 28px;
|
|
||||||
vertical-align: bottom;
|
|
||||||
}
|
|
||||||
main > article {
|
|
||||||
padding: 32px;
|
|
||||||
}
|
|
||||||
main > article > h1 {
|
|
||||||
margin: 0 0 1em 0;
|
|
||||||
}
|
|
||||||
main > footer {
|
|
||||||
padding: 32px;
|
|
||||||
border-top: solid 1px #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
box-sizing: border-box;
|
|
||||||
max-width: 500px;
|
|
||||||
margin: 16px auto 0 auto;
|
|
||||||
padding: 0 32px;
|
|
||||||
}
|
|
||||||
nav > a {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<header>
|
|
||||||
<img src="${ meta.logoImageUrl ?? meta.iconUrl ?? iconUrl }"/>
|
|
||||||
</header>
|
|
||||||
<article>
|
|
||||||
<h1>${ subject }</h1>
|
|
||||||
<div>${ html }</div>
|
|
||||||
</article>
|
|
||||||
<footer>
|
|
||||||
<a href="${ emailSettingUrl }">${ 'Email setting' }</a>
|
|
||||||
</footer>
|
|
||||||
</main>
|
|
||||||
<nav>
|
|
||||||
<a href="${ this.config.url }">${ this.config.host }</a>
|
|
||||||
</nav>
|
|
||||||
</body>
|
|
||||||
</html>`,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.logger.info(`Message sent: ${info.messageId}`);
|
this.logger.info(`Message sent: ${info.messageId}`);
|
||||||
|
|
|
@ -349,6 +349,11 @@ export class MiMeta {
|
||||||
})
|
})
|
||||||
public smtpPass: string | null;
|
public smtpPass: string | null;
|
||||||
|
|
||||||
|
@Column('text', {
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public emailTemplate: string | null;
|
||||||
|
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: false,
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { MetaService } from '@/core/MetaService.js';
|
||||||
import type { Config } from '@/config.js';
|
import type { Config } from '@/config.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||||
|
import { EmailService } from "@/core/EmailService.js";
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['meta'],
|
tags: ['meta'],
|
||||||
|
@ -269,6 +270,10 @@ export const meta = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
|
emailTemplate: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
swPrivateKey: {
|
swPrivateKey: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
@ -559,7 +564,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
name: instance.name,
|
name: instance.name,
|
||||||
shortName: instance.shortName,
|
shortName: instance.shortName,
|
||||||
uri: this.config.url,
|
uri: this.config.url,
|
||||||
description: instance.description,
|
description: instance.description ?? EmailService.defaultEmailTemplate,
|
||||||
langs: instance.langs,
|
langs: instance.langs,
|
||||||
tosUrl: instance.termsOfServiceUrl,
|
tosUrl: instance.termsOfServiceUrl,
|
||||||
repositoryUrl: instance.repositoryUrl,
|
repositoryUrl: instance.repositoryUrl,
|
||||||
|
@ -624,6 +629,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
smtpPort: instance.smtpPort,
|
smtpPort: instance.smtpPort,
|
||||||
smtpUser: instance.smtpUser,
|
smtpUser: instance.smtpUser,
|
||||||
smtpPass: instance.smtpPass,
|
smtpPass: instance.smtpPass,
|
||||||
|
emailTemplate: instance.emailTemplate ?? EmailService.defaultEmailTemplate,
|
||||||
swPrivateKey: instance.swPrivateKey,
|
swPrivateKey: instance.swPrivateKey,
|
||||||
useObjectStorage: instance.useObjectStorage,
|
useObjectStorage: instance.useObjectStorage,
|
||||||
objectStorageBaseUrl: instance.objectStorageBaseUrl,
|
objectStorageBaseUrl: instance.objectStorageBaseUrl,
|
||||||
|
|
|
@ -99,6 +99,7 @@ export const paramDef = {
|
||||||
deeplIsPro: { type: 'boolean' },
|
deeplIsPro: { type: 'boolean' },
|
||||||
enableEmail: { type: 'boolean' },
|
enableEmail: { type: 'boolean' },
|
||||||
email: { type: 'string', nullable: true },
|
email: { type: 'string', nullable: true },
|
||||||
|
emailTemplate: { type: 'string', nullable: true },
|
||||||
smtpSecure: { type: 'boolean' },
|
smtpSecure: { type: 'boolean' },
|
||||||
smtpHost: { type: 'string', nullable: true },
|
smtpHost: { type: 'string', nullable: true },
|
||||||
smtpPort: { type: 'integer', nullable: true },
|
smtpPort: { type: 'integer', nullable: true },
|
||||||
|
@ -459,6 +460,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
set.smtpPass = ps.smtpPass;
|
set.smtpPass = ps.smtpPass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.emailTemplate !== undefined) {
|
||||||
|
set.emailTemplate = ps.emailTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
if (ps.enableServiceWorker !== undefined) {
|
if (ps.enableServiceWorker !== undefined) {
|
||||||
set.enableServiceWorker = ps.enableServiceWorker;
|
set.enableServiceWorker = ps.enableServiceWorker;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,20 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template #label>{{ i18n.ts.smtpSecure }}</template>
|
<template #label>{{ i18n.ts.smtpSecure }}</template>
|
||||||
<template #caption>{{ i18n.ts.smtpSecureInfo }}</template>
|
<template #caption>{{ i18n.ts.smtpSecureInfo }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
|
||||||
|
<MkFolder>
|
||||||
|
<template #label>{{ i18n.ts.customEmailTemplate.title }}</template>
|
||||||
|
<div>
|
||||||
|
<MkCodeEditor v-model="emailTemplate" lang="html">
|
||||||
|
<template #label>{{ i18n.ts.customEmailTemplate.description }}</template>
|
||||||
|
</MkCodeEditor>
|
||||||
|
<I18n :src="i18n.ts.customEmailTemplate.reference" tag="small">
|
||||||
|
<template #link>
|
||||||
|
<a href="https://www.caniemail.com/">https://www.caniemail.com/</a>
|
||||||
|
</template>
|
||||||
|
</I18n>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
</template>
|
</template>
|
||||||
|
@ -78,6 +92,8 @@ import { fetchInstance, instance } from '@/instance.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import MkFolder from "@/components/MkFolder.vue";
|
||||||
|
import MkCodeEditor from "@/components/MkCodeEditor.vue";
|
||||||
|
|
||||||
const enableEmail = ref<boolean>(false);
|
const enableEmail = ref<boolean>(false);
|
||||||
const email = ref<string | null>(null);
|
const email = ref<string | null>(null);
|
||||||
|
@ -86,6 +102,7 @@ const smtpHost = ref<string>('');
|
||||||
const smtpPort = ref<number>(0);
|
const smtpPort = ref<number>(0);
|
||||||
const smtpUser = ref<string>('');
|
const smtpUser = ref<string>('');
|
||||||
const smtpPass = ref<string>('');
|
const smtpPass = ref<string>('');
|
||||||
|
const emailTemplate = ref<string>('');
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
const meta = await misskeyApi('admin/meta');
|
const meta = await misskeyApi('admin/meta');
|
||||||
|
@ -96,6 +113,7 @@ async function init() {
|
||||||
smtpPort.value = meta.smtpPort;
|
smtpPort.value = meta.smtpPort;
|
||||||
smtpUser.value = meta.smtpUser;
|
smtpUser.value = meta.smtpUser;
|
||||||
smtpPass.value = meta.smtpPass;
|
smtpPass.value = meta.smtpPass;
|
||||||
|
emailTemplate.value = meta.emailTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testEmail() {
|
async function testEmail() {
|
||||||
|
@ -123,6 +141,7 @@ function save() {
|
||||||
smtpPort: smtpPort.value,
|
smtpPort: smtpPort.value,
|
||||||
smtpUser: smtpUser.value,
|
smtpUser: smtpUser.value,
|
||||||
smtpPass: smtpPass.value,
|
smtpPass: smtpPass.value,
|
||||||
|
emailTemplate: emailTemplate.value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
fetchInstance(true);
|
fetchInstance(true);
|
||||||
});
|
});
|
||||||
|
|
|
@ -5432,6 +5432,7 @@ export type operations = {
|
||||||
smtpPort: number | null;
|
smtpPort: number | null;
|
||||||
smtpUser: string | null;
|
smtpUser: string | null;
|
||||||
smtpPass: string | null;
|
smtpPass: string | null;
|
||||||
|
emailTemplate: string | null;
|
||||||
swPrivateKey: string | null;
|
swPrivateKey: string | null;
|
||||||
useObjectStorage: boolean;
|
useObjectStorage: boolean;
|
||||||
objectStorageBaseUrl: string | null;
|
objectStorageBaseUrl: string | null;
|
||||||
|
|
|
@ -240,6 +240,9 @@ importers:
|
||||||
got:
|
got:
|
||||||
specifier: 14.4.5
|
specifier: 14.4.5
|
||||||
version: 14.4.5
|
version: 14.4.5
|
||||||
|
handlebars:
|
||||||
|
specifier: ^4.7.8
|
||||||
|
version: 4.7.8
|
||||||
hpagent:
|
hpagent:
|
||||||
specifier: 1.2.0
|
specifier: 1.2.0
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
|
@ -6004,6 +6007,11 @@ packages:
|
||||||
resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==}
|
resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==}
|
||||||
engines: {node: '>=0.8.0'}
|
engines: {node: '>=0.8.0'}
|
||||||
|
|
||||||
|
handlebars@4.7.8:
|
||||||
|
resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
|
||||||
|
engines: {node: '>=0.4.7'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
happy-dom@16.6.0:
|
happy-dom@16.6.0:
|
||||||
resolution: {integrity: sha512-Zz5S9sog8a3p8XYZbO+eI1QMOAvCNnIoyrH8A8MLX+X2mJrzADTy+kdETmc4q+uD9AGAvQYGn96qBAn2RAciKw==}
|
resolution: {integrity: sha512-Zz5S9sog8a3p8XYZbO+eI1QMOAvCNnIoyrH8A8MLX+X2mJrzADTy+kdETmc4q+uD9AGAvQYGn96qBAn2RAciKw==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
|
@ -7381,6 +7389,9 @@ packages:
|
||||||
resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
|
resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
neo-async@2.6.2:
|
||||||
|
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
|
||||||
|
|
||||||
nested-property@4.0.0:
|
nested-property@4.0.0:
|
||||||
resolution: {integrity: sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==}
|
resolution: {integrity: sha512-yFehXNWRs4cM0+dz7QxCd06hTbWbSkV0ISsqBfkntU6TOY4Qm3Q88fRRLOddkGh2Qq6dZvnKVAahfhjcUvLnyA==}
|
||||||
|
|
||||||
|
@ -9442,6 +9453,11 @@ packages:
|
||||||
resolution: {integrity: sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==}
|
resolution: {integrity: sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==}
|
||||||
engines: {node: '>=12.17'}
|
engines: {node: '>=12.17'}
|
||||||
|
|
||||||
|
uglify-js@3.19.3:
|
||||||
|
resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==}
|
||||||
|
engines: {node: '>=0.8.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
uid2@0.0.4:
|
uid2@0.0.4:
|
||||||
resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==}
|
resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==}
|
||||||
|
|
||||||
|
@ -9906,6 +9922,9 @@ packages:
|
||||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
wordwrap@1.0.0:
|
||||||
|
resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
|
||||||
|
|
||||||
wordwrapjs@5.1.0:
|
wordwrapjs@5.1.0:
|
||||||
resolution: {integrity: sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==}
|
resolution: {integrity: sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==}
|
||||||
engines: {node: '>=12.17'}
|
engines: {node: '>=12.17'}
|
||||||
|
@ -16060,6 +16079,15 @@ snapshots:
|
||||||
|
|
||||||
hammerjs@2.0.8: {}
|
hammerjs@2.0.8: {}
|
||||||
|
|
||||||
|
handlebars@4.7.8:
|
||||||
|
dependencies:
|
||||||
|
minimist: 1.2.8
|
||||||
|
neo-async: 2.6.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
wordwrap: 1.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
uglify-js: 3.19.3
|
||||||
|
|
||||||
happy-dom@16.6.0:
|
happy-dom@16.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
webidl-conversions: 7.0.0
|
webidl-conversions: 7.0.0
|
||||||
|
@ -17789,6 +17817,8 @@ snapshots:
|
||||||
|
|
||||||
negotiator@0.6.4: {}
|
negotiator@0.6.4: {}
|
||||||
|
|
||||||
|
neo-async@2.6.2: {}
|
||||||
|
|
||||||
nested-property@4.0.0: {}
|
nested-property@4.0.0: {}
|
||||||
|
|
||||||
netmask@2.0.2: {}
|
netmask@2.0.2: {}
|
||||||
|
@ -19946,6 +19976,9 @@ snapshots:
|
||||||
|
|
||||||
typical@7.3.0: {}
|
typical@7.3.0: {}
|
||||||
|
|
||||||
|
uglify-js@3.19.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
uid2@0.0.4: {}
|
uid2@0.0.4: {}
|
||||||
|
|
||||||
uid@2.0.2:
|
uid@2.0.2:
|
||||||
|
@ -20424,6 +20457,8 @@ snapshots:
|
||||||
|
|
||||||
word-wrap@1.2.5: {}
|
word-wrap@1.2.5: {}
|
||||||
|
|
||||||
|
wordwrap@1.0.0: {}
|
||||||
|
|
||||||
wordwrapjs@5.1.0: {}
|
wordwrapjs@5.1.0: {}
|
||||||
|
|
||||||
wrap-ansi@6.2.0:
|
wrap-ansi@6.2.0:
|
||||||
|
|
Loading…
Reference in a new issue