Files
NextEdu/src/modules/settings/actions-notifications.ts
SpecialX 1fcef5c3aa feat(settings): add security center, 2FA/TOTP, avatar upload, system settings
- Add TOTP implementation and two-factor data-access for 2FA enrollment

- Add security center card with password policy and session management

- Add avatar upload action and component

- Add system settings actions and data-access (actions-system-settings, data-access-system-settings)

- Add notification preferences and service actions

- Add security-utils and student-overview-data with tests

- Update existing settings views, data-access, and types for new features
2026-06-23 17:37:06 +08:00

62 lines
2.0 KiB
TypeScript

"use server"
import { z } from "zod"
import type { ActionState } from "@/shared/types/action-state"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import { sendNotification } from "@/modules/notifications/dispatcher"
import type { NotificationChannel } from "@/modules/notifications/types"
const TestNotificationSchema = z.object({
channel: z.enum(["push", "email", "sms"]),
})
type TestNotificationInput = z.infer<typeof TestNotificationSchema>
/** 将表单渠道名映射到 dispatcher 渠道名 */
const CHANNEL_MAP: Record<TestNotificationInput["channel"], NotificationChannel> = {
push: "in_app",
email: "email",
sms: "sms",
}
/**
* 发送测试通知
*
* 向当前用户发送一条测试通知,用于验证通知渠道是否配置正确。
* 调用 notifications/dispatcher.sendNotification 发送真实通知。
*/
export async function sendTestNotificationAction(
input: TestNotificationInput
): Promise<ActionState<null>> {
try {
const ctx = await requirePermission(Permissions.USER_PROFILE_UPDATE)
const parsed = TestNotificationSchema.parse(input)
const targetChannel = CHANNEL_MAP[parsed.channel]
const payload = {
userId: ctx.userId,
title: "Test Notification",
content: `This is a test notification sent via the ${parsed.channel} channel at ${new Date().toISOString()}.`,
type: "info" as const,
metadata: { test: true, channel: parsed.channel },
}
const results = await sendNotification(payload)
// 检查目标渠道的发送结果
const targetResult = results.find((r) => r.channel === targetChannel)
if (!targetResult || !targetResult.success) {
const errorMsg = targetResult?.error ?? `Failed to send via ${parsed.channel} channel`
return { success: false, message: errorMsg, data: null }
}
return { success: true, data: null }
} catch (error) {
const message = error instanceof Error ? error.message : "Failed to send test notification"
return { success: false, message }
}
}