mirror of https://github.com/certd/certd
perf: 邮箱设置改为系统设置,普通用户无需配置发件邮箱
parent
f23c4af2ad
commit
4244569211
|
@ -1,7 +1,7 @@
|
||||||
version: '3.3' # 兼容旧版docker-compose
|
version: '3.3' # 兼容旧版docker-compose
|
||||||
services:
|
services:
|
||||||
certd:
|
certd:
|
||||||
# 镜像 # ↓↓↓↓↓ --- 镜像版本号,建议改成固定版本号
|
# 镜像 # ↓↓↓↓↓ ---- 镜像版本号,建议改成固定版本号
|
||||||
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||||
container_name: certd # 容器名
|
container_name: certd # 容器名
|
||||||
restart: unless-stopped # 自动重启
|
restart: unless-stopped # 自动重启
|
||||||
|
|
|
@ -45,6 +45,26 @@ export class SysLicenseInfo extends BaseSettings {
|
||||||
license?: string;
|
license?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SysEmailConf extends BaseSettings {
|
||||||
|
static __title__ = '邮箱配置';
|
||||||
|
static __key__ = 'sys.email';
|
||||||
|
static __access__ = 'private';
|
||||||
|
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
auth: {
|
||||||
|
user: string;
|
||||||
|
pass: string;
|
||||||
|
};
|
||||||
|
secure: boolean; // use TLS
|
||||||
|
tls: {
|
||||||
|
// do not fail on invalid certs
|
||||||
|
rejectUnauthorized: boolean;
|
||||||
|
};
|
||||||
|
sender: string;
|
||||||
|
usePlus?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class SysSiteInfo extends BaseSettings {
|
export class SysSiteInfo extends BaseSettings {
|
||||||
static __title__ = '站点信息';
|
static __title__ = '站点信息';
|
||||||
static __key__ = 'sys.site';
|
static __key__ = 'sys.site';
|
||||||
|
|
|
@ -83,6 +83,7 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
|
||||||
let newSetting: T = new type();
|
let newSetting: T = new type();
|
||||||
const savedSettings = await this.getSettingByKey(key);
|
const savedSettings = await this.getSettingByKey(key);
|
||||||
newSetting = _.merge(newSetting, savedSettings);
|
newSetting = _.merge(newSetting, savedSettings);
|
||||||
|
await this.saveSetting(newSetting);
|
||||||
await this.cache.set(cacheKey, newSetting);
|
await this.cache.set(cacheKey, newSetting);
|
||||||
return newSetting;
|
return newSetting;
|
||||||
}
|
}
|
||||||
|
@ -95,6 +96,7 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
|
||||||
const entity = await this.getByKey(key);
|
const entity = await this.getByKey(key);
|
||||||
if (entity) {
|
if (entity) {
|
||||||
entity.setting = JSON.stringify(bean);
|
entity.setting = JSON.stringify(bean);
|
||||||
|
entity.access = type.__access__;
|
||||||
await this.repository.save(entity);
|
await this.repository.save(entity);
|
||||||
} else {
|
} else {
|
||||||
const newEntity = new SysSettingsEntity();
|
const newEntity = new SysSettingsEntity();
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
<div class="fs-bootstrap__loading"></div>
|
<div class="fs-bootstrap__loading"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="fs-bootstrap__footer">
|
<div class="fs-bootstrap__footer">
|
||||||
<a href="https://github.com/certd/certd" target="_blank">
|
<!-- <a href="https://github.com/certd/certd" target="_blank">-->
|
||||||
https://github.com/certd/certd
|
<!-- https://github.com/certd/certd-->
|
||||||
</a>
|
<!-- </a>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="layout-vip isPlus" @click="openUpgrade">
|
<div v-if="!settingStore.isComm" class="layout-vip isPlus" @click="openUpgrade">
|
||||||
<contextHolder />
|
<contextHolder />
|
||||||
<fs-icon icon="mingcute:vip-1-line" :title="text.title" />
|
<fs-icon icon="mingcute:vip-1-line" :title="text.title" />
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
<!-- Button-->
|
<!-- Button-->
|
||||||
<!-- </button>-->
|
<!-- </button>-->
|
||||||
<fs-menu
|
<fs-menu
|
||||||
v-if="settingStore?.siteEnv?.agent?.enabled === false"
|
v-if="settingStore?.siteEnv?.agent?.enabled === false || !settingStore.isComm"
|
||||||
class="header-menu"
|
class="header-menu"
|
||||||
mode="horizontal"
|
mode="horizontal"
|
||||||
:expand-selected="false"
|
:expand-selected="false"
|
||||||
|
@ -67,19 +67,22 @@
|
||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
<a-layout-footer class="fs-framework-footer">
|
<a-layout-footer class="fs-framework-footer">
|
||||||
<div>
|
<div>
|
||||||
<span>Powered by</span>
|
<span v-if="!settingStore.isComm">
|
||||||
<a> handsfree.work </a>
|
<span>Powered by</span>
|
||||||
|
<a> handsfree.work </a>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<template v-if="siteInfo.licenseTo">
|
||||||
|
<a-divider type="vertical" />
|
||||||
|
<a :href="siteInfo.licenseToUrl || ''">{{ siteInfo.licenseTo }}</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template v-if="siteInfo.icpNo">
|
<template v-if="siteInfo.icpNo">
|
||||||
<a-divider type="vertical" />
|
<a-divider type="vertical" />
|
||||||
<span>
|
<span>
|
||||||
<a href="https://beian.miit.gov.cn/" target="_blank">{{ siteInfo.icpNo }}</a>
|
<a href="https://beian.miit.gov.cn/" target="_blank">{{ siteInfo.icpNo }}</a>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="siteInfo.licenseTo">
|
|
||||||
<a-divider type="vertical" />
|
|
||||||
<a :href="siteInfo.licenseToUrl || ''">{{ siteInfo.licenseTo }}</a>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
<div>v{{ version }}</div>
|
<div>v{{ version }}</div>
|
||||||
|
|
||||||
|
|
|
@ -13,26 +13,24 @@
|
||||||
<router-view />
|
<router-view />
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<!-- <div class="links">-->
|
|
||||||
<!-- <a href="_self">帮助</a>-->
|
|
||||||
<!-- <a href="_self">隐私</a>-->
|
|
||||||
<!-- <a href="_self">条款</a>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<div class="copyright">
|
<div class="copyright">
|
||||||
<span>Copyright</span>
|
<span v-if="!settingStore.isComm">
|
||||||
<span>©</span>
|
<span>Copyright</span>
|
||||||
<span>{{ envRef.COPYRIGHT_YEAR }}</span>
|
<span>©</span>
|
||||||
<span>
|
<span>{{ envRef.COPYRIGHT_YEAR }}</span>
|
||||||
<a :href="envRef.COPYRIGHT_URL" target="_blank">{{ envRef.COPYRIGHT_NAME }}</a>
|
<span>
|
||||||
|
<a :href="envRef.COPYRIGHT_URL" target="_blank">{{ envRef.COPYRIGHT_NAME }}</a>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span v-if="siteInfo.licenseTo">
|
||||||
|
<a-divider type="vertical" />
|
||||||
|
<a :href="siteInfo.licenseToUrl" target="_blank">{{ siteInfo.licenseTo }}</a>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="siteInfo.icpNo">
|
<span v-if="siteInfo.icpNo">
|
||||||
<a-divider type="vertical" />
|
<a-divider type="vertical" />
|
||||||
<a href="https://beian.miit.gov.cn/" target="_blank">{{ siteInfo.icpNo }}</a>
|
<a href="https://beian.miit.gov.cn/" target="_blank">{{ siteInfo.icpNo }}</a>
|
||||||
</span>
|
</span>
|
||||||
<span v-if="siteInfo.licenseTo">
|
|
||||||
<a-divider type="vertical" />
|
|
||||||
<a :href="siteInfo.licenseToUrl" target="_blank">{{ siteInfo.licenseTo }}</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -56,16 +56,16 @@ export const certdResources = [
|
||||||
auth: true
|
auth: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: "邮箱设置",
|
// title: "邮箱设置",
|
||||||
name: "EmailSetting",
|
// name: "EmailSetting",
|
||||||
path: "/certd/settings/email",
|
// path: "/certd/settings/email",
|
||||||
component: "/certd/settings/email-setting.vue",
|
// component: "/certd/settings/email-setting.vue",
|
||||||
meta: {
|
// meta: {
|
||||||
icon: "ion:mail-outline",
|
// icon: "ion:mail-outline",
|
||||||
auth: true
|
// auth: true
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
title: "账号信息",
|
title: "账号信息",
|
||||||
name: "UserProfile",
|
name: "UserProfile",
|
||||||
|
|
|
@ -77,6 +77,16 @@ export const sysResources = [
|
||||||
permission: "sys:settings:view"
|
permission: "sys:settings:view"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "邮箱设置",
|
||||||
|
name: "EmailSetting",
|
||||||
|
path: "/sys/settings/email",
|
||||||
|
component: "/sys/settings/email-setting.vue",
|
||||||
|
meta: {
|
||||||
|
icon: "ion:mail-outline",
|
||||||
|
auth: true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "站点个性化",
|
title: "站点个性化",
|
||||||
name: "SiteSetting",
|
name: "SiteSetting",
|
||||||
|
@ -91,6 +101,7 @@ export const sysResources = [
|
||||||
permission: "sys:settings:view"
|
permission: "sys:settings:view"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// title: "商业版设置",
|
// title: "商业版设置",
|
||||||
// name: "SysCommercial",
|
// name: "SysCommercial",
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { request } from "/@/api/service";
|
|
||||||
const apiPrefix = "/user/settings";
|
|
||||||
|
|
||||||
export const SettingKeys = {
|
|
||||||
Email: "email"
|
|
||||||
};
|
|
||||||
export async function SettingsGet(key: string) {
|
|
||||||
return await request({
|
|
||||||
url: apiPrefix + "/get",
|
|
||||||
method: "post",
|
|
||||||
params: {
|
|
||||||
key
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function SettingsSave(key: string, setting: any) {
|
|
||||||
await request({
|
|
||||||
url: apiPrefix + "/save",
|
|
||||||
method: "post",
|
|
||||||
data: {
|
|
||||||
key,
|
|
||||||
setting: JSON.stringify(setting)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { request } from "/@/api/service";
|
import { request } from "/src/api/service";
|
||||||
const apiPrefix = "/basic/email";
|
const apiPrefix = "/basic/email";
|
||||||
|
|
||||||
export async function TestSend(receiver: string) {
|
export async function TestSend(receiver: string) {
|
|
@ -4,7 +4,8 @@ const apiPrefix = "/sys/settings";
|
||||||
|
|
||||||
export const SettingKeys = {
|
export const SettingKeys = {
|
||||||
SysPublic: "sys.public",
|
SysPublic: "sys.public",
|
||||||
SysPrivate: "sys.private"
|
SysPrivate: "sys.private",
|
||||||
|
SysEmail: "sys.email"
|
||||||
};
|
};
|
||||||
export async function SettingsGet(key: string) {
|
export async function SettingsGet(key: string) {
|
||||||
return await request({
|
return await request({
|
||||||
|
@ -27,6 +28,13 @@ export async function SettingsSave(key: string, setting: any) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function EmailSettingsGet() {
|
||||||
|
await request({
|
||||||
|
url: apiPrefix + "/getEmailSettings",
|
||||||
|
method: "post"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function PublicSettingsSave(setting: any) {
|
export async function PublicSettingsSave(setting: any) {
|
||||||
await request({
|
await request({
|
||||||
url: apiPrefix + "/savePublicSettings",
|
url: apiPrefix + "/savePublicSettings",
|
||||||
|
|
|
@ -82,7 +82,7 @@ import * as api from "./api";
|
||||||
import { SettingKeys } from "./api";
|
import { SettingKeys } from "./api";
|
||||||
import * as emailApi from "./api.email";
|
import * as emailApi from "./api.email";
|
||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
import { useSettingStore } from "/@/store/modules/settings";
|
import { useSettingStore } from "/src/store/modules/settings";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "EmailSetting"
|
name: "EmailSetting"
|
||||||
|
@ -114,19 +114,15 @@ const formState = reactive<Partial<FormState>>({
|
||||||
});
|
});
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
const data: any = await api.SettingsGet(SettingKeys.Email);
|
const data: any = await api.EmailSettingsGet();
|
||||||
if (!data?.setting) {
|
_.merge(formState, data);
|
||||||
return;
|
|
||||||
}
|
|
||||||
const setting = JSON.parse(data.setting);
|
|
||||||
Object.assign(formState, setting);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
load();
|
load();
|
||||||
|
|
||||||
const onFinish = async (form: any) => {
|
const onFinish = async (form: any) => {
|
||||||
console.log("Success:", form);
|
console.log("Success:", form);
|
||||||
await api.SettingsSave(SettingKeys.Email, form);
|
await api.SettingsSave(SettingKeys.SysEmail, form);
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "保存成功"
|
message: "保存成功"
|
||||||
});
|
});
|
||||||
|
@ -137,7 +133,7 @@ const onFinishFailed = (errorInfo: any) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
async function onUsePlusChanged() {
|
async function onUsePlusChanged() {
|
||||||
await api.SettingsSave(SettingKeys.Email, formState);
|
await api.SettingsSave(SettingKeys.SysEmail, formState);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TestFormState {
|
interface TestFormState {
|
|
@ -21,6 +21,7 @@ typeorm:
|
||||||
dataSource:
|
dataSource:
|
||||||
default:
|
default:
|
||||||
database: './data/db-comm.sqlite'
|
database: './data/db-comm.sqlite'
|
||||||
|
|
||||||
plus:
|
plus:
|
||||||
server:
|
server:
|
||||||
baseUrls: ['https://api.ai.handsfree.work', 'https://api.ai.docmirror.cn']
|
baseUrls: ['https://api.ai.handsfree.work', 'https://api.ai.docmirror.cn']
|
||||||
|
@ -29,7 +30,7 @@ account:
|
||||||
server:
|
server:
|
||||||
baseUrl: 'https://ai.handsfree.work/subject'
|
baseUrl: 'https://ai.handsfree.work/subject'
|
||||||
|
|
||||||
|
#
|
||||||
#plus:
|
#plus:
|
||||||
# server:
|
# server:
|
||||||
# baseUrls: ['http://127.0.0.1:11007']
|
# baseUrls: ['http://127.0.0.1:11007']
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||||
import type { EmailSend } from '@certd/pipeline';
|
import type { EmailSend } from '@certd/pipeline';
|
||||||
import { IEmailService, isPlus } from '@certd/pipeline';
|
import { IEmailService, isPlus, logger } from '@certd/pipeline';
|
||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
import type SMTPConnection from 'nodemailer/lib/smtp-connection';
|
import type SMTPConnection from 'nodemailer/lib/smtp-connection';
|
||||||
import { logger } from '@certd/pipeline';
|
|
||||||
import { UserSettingsService } from '../../mine/service/user-settings-service.js';
|
import { UserSettingsService } from '../../mine/service/user-settings-service.js';
|
||||||
import { PlusService } from '@certd/lib-server';
|
import { PlusService, SysEmailConf, SysSettingsService } from '@certd/lib-server';
|
||||||
|
import { merge } from 'lodash-es';
|
||||||
|
|
||||||
export type EmailConfig = {
|
export type EmailConfig = {
|
||||||
host: string;
|
host: string;
|
||||||
|
@ -27,6 +27,9 @@ export type EmailConfig = {
|
||||||
export class EmailService implements IEmailService {
|
export class EmailService implements IEmailService {
|
||||||
@Inject()
|
@Inject()
|
||||||
settingsService: UserSettingsService;
|
settingsService: UserSettingsService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
sysSettingsService: SysSettingsService;
|
||||||
@Inject()
|
@Inject()
|
||||||
plusService: PlusService;
|
plusService: PlusService;
|
||||||
|
|
||||||
|
@ -55,21 +58,30 @@ export class EmailService implements IEmailService {
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
async send(email: EmailSend) {
|
async send(email: EmailSend) {
|
||||||
console.log('sendEmail', email);
|
logger.info('sendEmail', email);
|
||||||
|
|
||||||
const emailConfigEntity = await this.settingsService.getByKey('email', email.userId);
|
const emailConf = await this.sysSettingsService.getSetting<SysEmailConf>(SysEmailConf);
|
||||||
if (emailConfigEntity == null || !emailConfigEntity.setting) {
|
if (!emailConf.host && emailConf.usePlus != null) {
|
||||||
|
const emailConfigEntity = await this.settingsService.getByKey('email', 1);
|
||||||
|
if (emailConfigEntity) {
|
||||||
|
const emailConfig = JSON.parse(emailConfigEntity.setting) as EmailConfig;
|
||||||
|
merge(emailConf, emailConfig);
|
||||||
|
await this.sysSettingsService.saveSetting(emailConf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emailConf.host) {
|
||||||
if (isPlus()) {
|
if (isPlus()) {
|
||||||
//自动使用plus发邮件
|
//自动使用plus发邮件
|
||||||
return await this.sendByPlus(email);
|
return await this.sendByPlus(email);
|
||||||
}
|
}
|
||||||
throw new Error('email settings 未设置');
|
throw new Error('email settings 还未设置');
|
||||||
}
|
}
|
||||||
const emailConfig = JSON.parse(emailConfigEntity.setting) as EmailConfig;
|
|
||||||
if (emailConfig.usePlus && isPlus()) {
|
if (emailConf.usePlus && isPlus()) {
|
||||||
return await this.sendByPlus(email);
|
return await this.sendByPlus(email);
|
||||||
}
|
}
|
||||||
await this.sendByCustom(emailConfig, email);
|
await this.sendByCustom(emailConf, email);
|
||||||
logger.info('sendEmail complete: ', email);
|
logger.info('sendEmail complete: ', email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
|
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
|
||||||
import { CrudController } from '@certd/lib-server';
|
import { CrudController, SysEmailConf } from '@certd/lib-server';
|
||||||
import { SysSettingsService } from '@certd/lib-server';
|
import { SysSettingsService } from '@certd/lib-server';
|
||||||
import { SysSettingsEntity } from '../entity/sys-settings.js';
|
import { SysSettingsEntity } from '../entity/sys-settings.js';
|
||||||
import { SysPublicSettings } from '@certd/lib-server';
|
import { SysPublicSettings } from '@certd/lib-server';
|
||||||
import * as _ from 'lodash-es';
|
import * as _ from 'lodash-es';
|
||||||
import { PipelineService } from '../../../pipeline/service/pipeline-service.js';
|
import { PipelineService } from '../../../pipeline/service/pipeline-service.js';
|
||||||
|
import { UserSettingsService } from '../../../mine/service/user-settings-service.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
@ -14,6 +15,8 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
|
||||||
@Inject()
|
@Inject()
|
||||||
service: SysSettingsService;
|
service: SysSettingsService;
|
||||||
@Inject()
|
@Inject()
|
||||||
|
userSettingsService: UserSettingsService;
|
||||||
|
@Inject()
|
||||||
pipelineService: PipelineService;
|
pipelineService: PipelineService;
|
||||||
|
|
||||||
getService() {
|
getService() {
|
||||||
|
@ -68,6 +71,22 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
|
||||||
return this.ok(entity);
|
return this.ok(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// savePublicSettings
|
||||||
|
@Post('/getEmailSettings', { summary: 'sys:settings:edit' })
|
||||||
|
async getEmailSettings(@Body(ALL) body) {
|
||||||
|
let conf = await this.service.getSetting<SysEmailConf>(SysEmailConf);
|
||||||
|
if (!conf.host && conf.usePlus != null) {
|
||||||
|
//到userSetting里面去找
|
||||||
|
const adminEmailSetting = await this.userSettingsService.getByKey('email', 1);
|
||||||
|
if (adminEmailSetting) {
|
||||||
|
const setting = JSON.parse(adminEmailSetting.setting);
|
||||||
|
conf = _.merge(conf, setting);
|
||||||
|
await this.service.saveSetting(conf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.ok(conf);
|
||||||
|
}
|
||||||
|
|
||||||
// savePublicSettings
|
// savePublicSettings
|
||||||
@Post('/savePublicSettings', { summary: 'sys:settings:edit' })
|
@Post('/savePublicSettings', { summary: 'sys:settings:edit' })
|
||||||
async savePublicSettings(@Body(ALL) body) {
|
async savePublicSettings(@Body(ALL) body) {
|
||||||
|
|
Loading…
Reference in New Issue