mirror of
https://github.com/certd/certd.git
synced 2026-07-10 23:27:34 +08:00
perf: 优化用户体验,首次访问时弹出邮箱账号绑定用以初始化账号
This commit is contained in:
@@ -31,6 +31,13 @@ export function useFormDialog() {
|
||||
crudOptions: {
|
||||
columns: req.columns,
|
||||
form: {
|
||||
labelCol: {
|
||||
// @ts-ignore
|
||||
span: null,
|
||||
style: {
|
||||
width: "100px",
|
||||
},
|
||||
},
|
||||
initialForm: req.initialForm,
|
||||
wrapper: warpper,
|
||||
async afterSubmit() {},
|
||||
@@ -44,7 +51,7 @@ export function useFormDialog() {
|
||||
};
|
||||
}
|
||||
const { crudOptions } = createCrudOptions();
|
||||
await openCrudFormDialog({ crudOptions });
|
||||
return await openCrudFormDialog({ crudOptions });
|
||||
}
|
||||
return {
|
||||
openFormDialog,
|
||||
|
||||
@@ -18,6 +18,10 @@ defineProps<{
|
||||
showButton: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "close"): void;
|
||||
}>();
|
||||
|
||||
let passwordFormRef = ref();
|
||||
|
||||
type OpenOptions = {
|
||||
@@ -68,8 +72,8 @@ const passwordFormOptions: CrudOptions = {
|
||||
},
|
||||
async afterSubmit() {
|
||||
const formData = passwordFormRef.value?.getFormData?.();
|
||||
const message = formData?.init ? t("authentication.initPasswordSuccessMessage") : t("authentication.successMessage");
|
||||
notification.success({ message });
|
||||
const msg = formData?.init ? t("authentication.initPasswordSuccessMessage") : t("authentication.successMessage");
|
||||
notification.success({ message: msg });
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
@@ -84,6 +88,7 @@ const passwordFormOptions: CrudOptions = {
|
||||
title: t("authentication.oldPassword"),
|
||||
type: "password",
|
||||
form: {
|
||||
//@ts-ignore
|
||||
show: compute(({ form }) => form.init !== true),
|
||||
rules: [{ required: true, message: t("authentication.oldPasswordRequired") }],
|
||||
},
|
||||
@@ -118,16 +123,18 @@ const passwordFormOptions: CrudOptions = {
|
||||
|
||||
async function open(opts: OpenOptions = {}) {
|
||||
const formOptions = buildFormOptions(passwordFormOptions);
|
||||
formOptions.newInstance = true; //新实例打开
|
||||
formOptions.newInstance = true;
|
||||
if (opts.init) {
|
||||
formOptions.wrapper.title = t("authentication.initPasswordTitle");
|
||||
}
|
||||
formOptions.wrapper.onClosed = () => {
|
||||
emit("close");
|
||||
};
|
||||
passwordFormRef.value = await openDialog(formOptions);
|
||||
passwordFormRef.value.setFormData({
|
||||
init: opts.init === true,
|
||||
password: opts.password || "",
|
||||
});
|
||||
console.log(passwordFormRef.value);
|
||||
}
|
||||
|
||||
const scope = ref({
|
||||
|
||||
@@ -2,22 +2,110 @@
|
||||
<fs-page class="home—index bg-neutral-100 dark:bg-black">
|
||||
<!-- <page-content />-->
|
||||
<dashboard-user />
|
||||
<change-password-button ref="changePasswordButtonRef" :show-button="false"></change-password-button>
|
||||
<change-password-button ref="changePasswordButtonRef" :show-button="false" @close="checkAndSetupAccount"></change-password-button>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script lang="tsx" setup>
|
||||
import DashboardUser from "./dashboard/index.vue";
|
||||
import { useUserStore } from "/@/store/user";
|
||||
import ChangePasswordButton from "/@/views/certd/mine/change-password-button.vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { Modal } from "ant-design-vue";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
import { useI18n } from "/src/locales";
|
||||
import { request } from "/@/api/service";
|
||||
import { useFormDialog } from "/@/use/use-dialog";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { openFormDialog } = useFormDialog();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const changePasswordButtonRef = ref();
|
||||
const emailFormWrapperRef = ref<any>();
|
||||
|
||||
const validateEmailConfirm = async (_rule: any, value: string) => {
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
const formData = emailFormWrapperRef.value?.getFormData?.();
|
||||
if (formData && value !== formData.email) {
|
||||
throw new Error("两次输入的邮箱地址不一致");
|
||||
}
|
||||
};
|
||||
|
||||
async function checkAndSetupAccount() {
|
||||
try {
|
||||
const userInfo = userStore.getUserInfo as any;
|
||||
if (!userInfo.needInitAccount) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (userInfo.email) {
|
||||
await request({
|
||||
url: "/mine/accountInit",
|
||||
method: "post",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
emailFormWrapperRef.value = await openFormDialog({
|
||||
title: "绑定邮箱",
|
||||
wrapper: {
|
||||
width: 560,
|
||||
},
|
||||
initialForm: { email: "", emailConfirm: "" },
|
||||
async onSubmit(form: any) {
|
||||
await request({
|
||||
url: "/mine/accountInit",
|
||||
method: "post",
|
||||
data: { email: form.email },
|
||||
});
|
||||
notification.success({
|
||||
message: "邮箱绑定成功",
|
||||
});
|
||||
},
|
||||
body: () => {
|
||||
return <a-alert class="mb-4" message="为保证用户体验,请先绑定邮箱,初始化您的账号" type="success" show-icon></a-alert>;
|
||||
},
|
||||
columns: {
|
||||
email: {
|
||||
title: "邮箱",
|
||||
type: "text",
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
component: {
|
||||
placeholder: "请输入邮箱地址",
|
||||
},
|
||||
helper: "请输入您的邮箱",
|
||||
rules: [
|
||||
{ required: true, message: "请输入邮箱地址" },
|
||||
{ type: "email", message: "请输入有效的邮箱地址" },
|
||||
],
|
||||
},
|
||||
},
|
||||
emailConfirm: {
|
||||
title: "确认邮箱",
|
||||
type: "text",
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
component: {
|
||||
placeholder: "请再次输入邮箱地址",
|
||||
},
|
||||
helper: "请再次输入邮箱,以确认邮箱地址无误",
|
||||
rules: [
|
||||
{ required: true, message: "请再次输入邮箱地址" },
|
||||
{ type: "email", message: "请输入有效的邮箱地址" },
|
||||
{ validator: validateEmailConfirm, trigger: "blur" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("AcmeAccount setup failed:", e);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (userStore.getUserInfo.isWeak === true) {
|
||||
Modal.info({
|
||||
@@ -30,6 +118,9 @@ onMounted(() => {
|
||||
},
|
||||
okText: t("authentication.changeNow"),
|
||||
});
|
||||
} else {
|
||||
//两个弹框不要同时出现
|
||||
checkAndSetupAccount();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { BaseController, Constants, SysSettingsService } from "@certd/lib-server";
|
||||
import { AccessGetter, AccessService, BaseController, Constants, SysSettingsService } from "@certd/lib-server";
|
||||
import { ALL, Body, Controller, Inject, Post, Provide } from "@midwayjs/core";
|
||||
import { PasskeyService } from "../../../modules/login/service/passkey-service.js";
|
||||
import { RoleService } from "../../../modules/sys/authority/service/role-service.js";
|
||||
import { UserService } from "../../../modules/sys/authority/service/user-service.js";
|
||||
import { NotificationService } from "../../../modules/pipeline/service/notification-service.js";
|
||||
import { newAccess } from "@certd/pipeline";
|
||||
import { http, logger, utils } from "@certd/basic";
|
||||
import { ApiTags } from "@midwayjs/swagger";
|
||||
import { CodeService } from "../../../modules/basic/service/code-service.js";
|
||||
import { EmailService } from "../../../modules/basic/service/email-service.js";
|
||||
|
||||
/**
|
||||
*/
|
||||
@@ -27,6 +31,15 @@ export class MineController extends BaseController {
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Inject()
|
||||
accessService: AccessService;
|
||||
|
||||
@Inject()
|
||||
notificationService: NotificationService;
|
||||
|
||||
@Inject()
|
||||
emailService: EmailService;
|
||||
|
||||
@Post("/info", { description: Constants.per.authOnly, summary: "查询用户信息" })
|
||||
public async info() {
|
||||
const userId = this.getUserId();
|
||||
@@ -41,6 +54,17 @@ export class MineController extends BaseController {
|
||||
delete user.password;
|
||||
//@ts-ignore
|
||||
user.needInitPassword = needInitPassword;
|
||||
|
||||
const { projectId } = await this.getProjectUserIdRead();
|
||||
const userProjectQuery = this.accessService.buildUserProjectQuery(userId, projectId);
|
||||
const existingAccess = await this.accessService.findOne({
|
||||
where: { type: "acmeAccount", subtype: "letsencrypt", ...userProjectQuery },
|
||||
});
|
||||
if (!existingAccess) {
|
||||
//@ts-ignore
|
||||
user.needInitAccount = true;
|
||||
}
|
||||
|
||||
return this.ok(user);
|
||||
}
|
||||
|
||||
@@ -122,4 +146,58 @@ export class MineController extends BaseController {
|
||||
});
|
||||
return this.ok({});
|
||||
}
|
||||
|
||||
@Post("/accountInit", { description: Constants.per.authOnly, summary: "初始化Let's Encrypt ACME账号和邮件通知" })
|
||||
public async accountInit(@Body("email") email?: string) {
|
||||
const { projectId, userId } = await this.getProjectUserIdWrite();
|
||||
|
||||
let userEmail = email;
|
||||
let user: any = null;
|
||||
if (!userEmail) {
|
||||
user = await this.userService.info(userId);
|
||||
userEmail = user.email;
|
||||
}
|
||||
if (!userEmail) {
|
||||
return this.ok({ needEmail: true });
|
||||
}
|
||||
|
||||
if (email) {
|
||||
if (!user) {
|
||||
user = await this.userService.info(userId);
|
||||
}
|
||||
if (!user.email) {
|
||||
await this.userService.updateEmail(userId, { email: userEmail });
|
||||
}
|
||||
}
|
||||
|
||||
await this.emailService.add(userId, userEmail);
|
||||
|
||||
await this.notificationService.getOrCreateDefault(userEmail, userId, projectId);
|
||||
|
||||
const getAccessById = this.accessService.getById.bind(this.accessService);
|
||||
const accessGetter = new AccessGetter(userId, projectId, getAccessById);
|
||||
const accessContext = {
|
||||
http,
|
||||
logger,
|
||||
utils,
|
||||
accessService: accessGetter,
|
||||
define: undefined,
|
||||
} as any;
|
||||
const access = await newAccess("acmeAccount", { caType: "letsencrypt", email: userEmail }, accessGetter, accessContext);
|
||||
const accountJson = await access.onGenerateAccount();
|
||||
|
||||
await this.accessService.add({
|
||||
type: "acmeAccount",
|
||||
name: "Let's Encrypt",
|
||||
userId,
|
||||
projectId,
|
||||
setting: JSON.stringify({
|
||||
caType: "letsencrypt",
|
||||
email: userEmail,
|
||||
account: accountJson,
|
||||
}),
|
||||
});
|
||||
|
||||
return this.ok({ success: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +441,8 @@ export class RuntimeDepsService {
|
||||
|
||||
private getDefineByPluginKey(pluginKey: string, owner?: RuntimeDependencyPluginDefine): RuntimeDependencyPluginDefine {
|
||||
const parts = pluginKey.split(":");
|
||||
let [pluginType, subtype, name] = parts;
|
||||
const [pluginType, subtype, rawName] = parts;
|
||||
let name = rawName;
|
||||
if (parts.length === 2) {
|
||||
name = subtype;
|
||||
} else if (parts.length === 3) {
|
||||
|
||||
Reference in New Issue
Block a user