150 lines
4.8 KiB
TypeScript
150 lines
4.8 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { Alert, Button, Card, Form, Input, Typography } from "antd";
|
||
import { KeyRound } from "lucide-react";
|
||
import { useNavigate, useParams } from "react-router";
|
||
import { ApiError, authApi } from "../api";
|
||
import { useRuntime } from "../app/RuntimeProvider";
|
||
|
||
interface FormValues {
|
||
password: string;
|
||
confirm: string;
|
||
}
|
||
|
||
function readAndClearToken(activationId: string): string {
|
||
const storageKey = `tiku-activation:${activationId}`;
|
||
const fragment = new URLSearchParams(window.location.hash.replace(/^#/, ""));
|
||
const fromUrl = fragment.get("token");
|
||
if (fromUrl) sessionStorage.setItem(storageKey, fromUrl);
|
||
if (window.location.hash)
|
||
history.replaceState(
|
||
null,
|
||
"",
|
||
window.location.pathname + window.location.search,
|
||
);
|
||
return fromUrl || sessionStorage.getItem(storageKey) || "";
|
||
}
|
||
|
||
const errorMessages: Record<string, string> = {
|
||
activation_invalid: "建站密钥无效,请联系平台重新获取。",
|
||
activation_expired: "建站密钥已经过期,请联系平台重新签发。",
|
||
activation_consumed: "建站密钥已经使用,不能再次激活。",
|
||
activation_password_invalid: "密码至少 8 位,并同时包含字母和数字。",
|
||
};
|
||
|
||
export default function ActivationPage() {
|
||
const { activationId = "" } = useParams();
|
||
const { runtime } = useRuntime();
|
||
const navigate = useNavigate();
|
||
const [token, setToken] = useState("");
|
||
const [error, setError] = useState("");
|
||
const [submitting, setSubmitting] = useState(false);
|
||
|
||
useEffect(() => {
|
||
setToken(readAndClearToken(activationId));
|
||
}, [activationId]);
|
||
|
||
const submit = async (values: FormValues) => {
|
||
if (!token) {
|
||
setError("链接中没有可用的建站密钥,请联系平台重新获取。");
|
||
return;
|
||
}
|
||
setSubmitting(true);
|
||
setError("");
|
||
try {
|
||
await authApi.completeOwnerActivation({
|
||
activationId,
|
||
token,
|
||
newPassword: values.password,
|
||
});
|
||
sessionStorage.removeItem(`tiku-activation:${activationId}`);
|
||
navigate("/manage/onboarding", { replace: true });
|
||
} catch (reason) {
|
||
const code = reason instanceof ApiError ? reason.code : "";
|
||
setError(errorMessages[code] ?? "激活失败,请联系平台服务人员。");
|
||
} finally {
|
||
setSubmitting(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen grid place-items-center bg-slate-950 p-5">
|
||
<Card
|
||
className="w-full max-w-md shadow-2xl"
|
||
styles={{ body: { padding: 32 } }}
|
||
>
|
||
<div className="mb-7 flex items-center gap-4">
|
||
<div
|
||
className="grid h-12 w-12 place-items-center rounded-2xl text-white"
|
||
style={{ background: runtime?.theme.primaryColor }}
|
||
>
|
||
<KeyRound size={24} />
|
||
</div>
|
||
<div>
|
||
<Typography.Text type="secondary">
|
||
{runtime?.tenantName}
|
||
</Typography.Text>
|
||
<Typography.Title level={3} style={{ margin: 0 }}>
|
||
激活管理员账号
|
||
</Typography.Title>
|
||
</div>
|
||
</div>
|
||
<Typography.Paragraph type="secondary">
|
||
设置管理员密码后,将自动登录并进入建站向导。一次性密钥使用后立即失效。
|
||
</Typography.Paragraph>
|
||
{!token && (
|
||
<Alert
|
||
className="mb-5"
|
||
type="warning"
|
||
showIcon
|
||
title="缺少建站密钥"
|
||
/>
|
||
)}
|
||
{error && (
|
||
<Alert className="mb-5" type="error" showIcon title={error} />
|
||
)}
|
||
<Form layout="vertical" onFinish={submit} requiredMark={false}>
|
||
<Form.Item
|
||
name="password"
|
||
label="管理员密码"
|
||
rules={[
|
||
{ required: true },
|
||
{ min: 8 },
|
||
{
|
||
pattern: /^(?=.*[A-Za-z])(?=.*\d).+$/,
|
||
message: "必须同时包含字母和数字",
|
||
},
|
||
]}
|
||
>
|
||
<Input.Password autoComplete="new-password" size="large" />
|
||
</Form.Item>
|
||
<Form.Item
|
||
name="confirm"
|
||
label="确认密码"
|
||
dependencies={["password"]}
|
||
rules={[
|
||
{ required: true },
|
||
({ getFieldValue }) => ({
|
||
validator: (_, value) =>
|
||
value === getFieldValue("password")
|
||
? Promise.resolve()
|
||
: Promise.reject(new Error("两次密码不一致")),
|
||
}),
|
||
]}
|
||
>
|
||
<Input.Password autoComplete="new-password" size="large" />
|
||
</Form.Item>
|
||
<Button
|
||
block
|
||
size="large"
|
||
type="primary"
|
||
htmlType="submit"
|
||
loading={submitting}
|
||
>
|
||
激活并开始建站
|
||
</Button>
|
||
</Form>
|
||
</Card>
|
||
</div>
|
||
);
|
||
}
|