2024-12-04 02:35:06 +08:00
|
|
|
|
import React, { createContext, useState } from "react";
|
|
|
|
|
import { DEFAULT_CONFIG } from "app/env";
|
2024-12-03 01:37:02 +08:00
|
|
|
|
import { HttpClient } from "core/http";
|
|
|
|
|
import { ThemeModeToggle } from "hooks/themeMode";
|
2024-12-04 02:35:06 +08:00
|
|
|
|
import {
|
|
|
|
|
Theme,
|
|
|
|
|
Button,
|
|
|
|
|
Select,
|
|
|
|
|
Flex,
|
|
|
|
|
Container,
|
|
|
|
|
Heading,
|
|
|
|
|
Text,
|
|
|
|
|
Box,
|
|
|
|
|
TextField,
|
|
|
|
|
} from "@radix-ui/themes";
|
|
|
|
|
import { toast } from "hooks/notification";
|
|
|
|
|
import { Echoes } from "hooks/echoes";
|
2024-11-30 02:15:46 +08:00
|
|
|
|
|
2024-11-27 19:52:49 +08:00
|
|
|
|
interface SetupContextType {
|
|
|
|
|
currentStep: number;
|
|
|
|
|
setCurrentStep: (step: number) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const SetupContext = createContext<SetupContextType>({
|
|
|
|
|
currentStep: 1,
|
|
|
|
|
setCurrentStep: () => {},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 步骤组件的通用属性接口
|
|
|
|
|
interface StepProps {
|
|
|
|
|
onNext: () => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const StepContainer: React.FC<{ title: string; children: React.ReactNode }> = ({
|
|
|
|
|
title,
|
|
|
|
|
children,
|
|
|
|
|
}) => (
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<Box style={{ width: "90%", maxWidth: "600px", margin: "0 auto" }}>
|
|
|
|
|
<Heading size="5" mb="4" weight="bold">
|
|
|
|
|
{title}
|
|
|
|
|
</Heading>
|
2024-12-03 01:37:02 +08:00
|
|
|
|
<Flex direction="column" gap="4">
|
2024-11-27 19:52:49 +08:00
|
|
|
|
{children}
|
2024-12-03 01:37:02 +08:00
|
|
|
|
</Flex>
|
2024-12-04 02:35:06 +08:00
|
|
|
|
</Box>
|
2024-11-27 19:52:49 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 通用的导航按钮组件
|
2024-12-04 02:35:06 +08:00
|
|
|
|
const NavigationButtons: React.FC<
|
|
|
|
|
StepProps & { loading?: boolean; disabled?: boolean }
|
|
|
|
|
> = ({ onNext, loading = false, disabled = false }) => (
|
|
|
|
|
<Flex justify="end" mt="4">
|
|
|
|
|
<Button
|
|
|
|
|
size="3"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
disabled={loading || disabled}
|
2024-12-04 02:35:06 +08:00
|
|
|
|
onClick={onNext}
|
|
|
|
|
style={{ width: "100%" }}
|
2024-11-27 19:52:49 +08:00
|
|
|
|
>
|
2024-12-04 02:35:06 +08:00
|
|
|
|
{loading ? "处理中..." : "下一步"}
|
2024-12-03 01:37:02 +08:00
|
|
|
|
</Button>
|
|
|
|
|
</Flex>
|
2024-11-27 19:52:49 +08:00
|
|
|
|
);
|
|
|
|
|
|
2024-12-03 01:37:02 +08:00
|
|
|
|
// 修改输入框组件
|
2024-11-27 19:52:49 +08:00
|
|
|
|
const InputField: React.FC<{
|
|
|
|
|
label: string;
|
|
|
|
|
name: string;
|
|
|
|
|
defaultValue?: string | number;
|
|
|
|
|
hint?: string;
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required?: boolean;
|
2024-12-03 01:37:02 +08:00
|
|
|
|
}> = ({ label, name, defaultValue, hint, required = true }) => (
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<Box mb="4">
|
|
|
|
|
<Text as="label" size="2" weight="medium" className="block mb-2">
|
2024-12-03 01:37:02 +08:00
|
|
|
|
{label} {required && <Text color="red">*</Text>}
|
|
|
|
|
</Text>
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<TextField.Root
|
2024-11-27 19:52:49 +08:00
|
|
|
|
name={name}
|
2024-12-03 01:37:02 +08:00
|
|
|
|
defaultValue={defaultValue?.toString()}
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required={required}
|
2024-12-04 02:35:06 +08:00
|
|
|
|
>
|
|
|
|
|
<TextField.Slot></TextField.Slot>
|
|
|
|
|
</TextField.Root>
|
|
|
|
|
{hint && (
|
|
|
|
|
<Text color="gray" size="1" mt="1">
|
|
|
|
|
{hint}
|
|
|
|
|
</Text>
|
|
|
|
|
)}
|
2024-12-03 01:37:02 +08:00
|
|
|
|
</Box>
|
2024-11-27 19:52:49 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const Introduction: React.FC<StepProps> = ({ onNext }) => (
|
|
|
|
|
<StepContainer title="安装说明">
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<Text size="3" style={{ lineHeight: 1.6 }}>
|
|
|
|
|
欢迎使用 Echoes
|
|
|
|
|
</Text>
|
|
|
|
|
<NavigationButtons onNext={onNext} />
|
2024-11-27 19:52:49 +08:00
|
|
|
|
</StepContainer>
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-28 23:10:00 +08:00
|
|
|
|
const DatabaseConfig: React.FC<StepProps> = ({ onNext }) => {
|
2024-11-27 19:52:49 +08:00
|
|
|
|
const [dbType, setDbType] = useState("postgresql");
|
2024-11-30 02:15:46 +08:00
|
|
|
|
const [loading, setLoading] = useState(false);
|
2024-12-03 01:37:02 +08:00
|
|
|
|
const http = HttpClient.getInstance();
|
2024-11-30 02:15:46 +08:00
|
|
|
|
|
|
|
|
|
const validateForm = () => {
|
|
|
|
|
const getRequiredFields = () => {
|
|
|
|
|
switch (dbType) {
|
2024-12-04 02:35:06 +08:00
|
|
|
|
case "sqllite":
|
|
|
|
|
return ["db_prefix", "db_name"];
|
|
|
|
|
case "postgresql":
|
|
|
|
|
case "mysql":
|
|
|
|
|
return [
|
|
|
|
|
"db_host",
|
|
|
|
|
"db_prefix",
|
|
|
|
|
"db_port",
|
|
|
|
|
"db_user",
|
|
|
|
|
"db_password",
|
|
|
|
|
"db_name",
|
|
|
|
|
];
|
2024-11-30 02:15:46 +08:00
|
|
|
|
default:
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const requiredFields = getRequiredFields();
|
|
|
|
|
const emptyFields: string[] = [];
|
|
|
|
|
|
2024-12-04 02:35:06 +08:00
|
|
|
|
requiredFields.forEach((field) => {
|
|
|
|
|
const input = document.querySelector(
|
|
|
|
|
`[name="${field}"]`,
|
|
|
|
|
) as HTMLInputElement;
|
|
|
|
|
if (input && (!input.value || input.value.trim() === "")) {
|
2024-11-30 02:15:46 +08:00
|
|
|
|
emptyFields.push(field);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (emptyFields.length > 0) {
|
2024-12-04 02:35:06 +08:00
|
|
|
|
const fieldNames = emptyFields.map((field) => {
|
2024-11-30 02:15:46 +08:00
|
|
|
|
switch (field) {
|
2024-12-04 02:35:06 +08:00
|
|
|
|
case "db_host":
|
|
|
|
|
return "数据库地址";
|
|
|
|
|
case "db_prefix":
|
|
|
|
|
return "数据库前缀";
|
|
|
|
|
case "db_port":
|
|
|
|
|
return "端口";
|
|
|
|
|
case "db_user":
|
|
|
|
|
return "用户名";
|
|
|
|
|
case "db_password":
|
|
|
|
|
return "密码";
|
|
|
|
|
case "db_name":
|
|
|
|
|
return "数据库名";
|
|
|
|
|
default:
|
|
|
|
|
return field;
|
2024-11-30 02:15:46 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
2024-12-04 02:35:06 +08:00
|
|
|
|
toast.error(`请填写以下必填项:${fieldNames.join("、")}`);
|
2024-11-30 02:15:46 +08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleNext = async () => {
|
2024-12-03 01:37:02 +08:00
|
|
|
|
const validation = validateForm();
|
|
|
|
|
if (validation !== true) {
|
2024-11-30 02:15:46 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
try {
|
|
|
|
|
const formData = {
|
|
|
|
|
db_type: dbType,
|
2024-12-04 02:35:06 +08:00
|
|
|
|
host:
|
|
|
|
|
(
|
|
|
|
|
document.querySelector('[name="db_host"]') as HTMLInputElement
|
|
|
|
|
)?.value?.trim() ?? "",
|
|
|
|
|
db_prefix:
|
|
|
|
|
(
|
|
|
|
|
document.querySelector('[name="db_prefix"]') as HTMLInputElement
|
|
|
|
|
)?.value?.trim() ?? "",
|
|
|
|
|
port: Number(
|
|
|
|
|
(
|
|
|
|
|
document.querySelector('[name="db_port"]') as HTMLInputElement
|
|
|
|
|
)?.value?.trim() ?? 0,
|
|
|
|
|
),
|
|
|
|
|
user:
|
|
|
|
|
(
|
|
|
|
|
document.querySelector('[name="db_user"]') as HTMLInputElement
|
|
|
|
|
)?.value?.trim() ?? "",
|
|
|
|
|
password:
|
|
|
|
|
(
|
|
|
|
|
document.querySelector('[name="db_password"]') as HTMLInputElement
|
|
|
|
|
)?.value?.trim() ?? "",
|
|
|
|
|
db_name:
|
|
|
|
|
(
|
|
|
|
|
document.querySelector('[name="db_name"]') as HTMLInputElement
|
|
|
|
|
)?.value?.trim() ?? "",
|
2024-11-30 02:15:46 +08:00
|
|
|
|
};
|
|
|
|
|
|
2024-12-04 02:35:06 +08:00
|
|
|
|
await http.post("/sql", formData);
|
2024-11-30 02:15:46 +08:00
|
|
|
|
|
2024-11-30 22:24:35 +08:00
|
|
|
|
let oldEnv = import.meta.env ?? DEFAULT_CONFIG;
|
2024-12-04 02:35:06 +08:00
|
|
|
|
const viteEnv = Object.entries(oldEnv).reduce(
|
|
|
|
|
(acc, [key, value]) => {
|
|
|
|
|
if (key.startsWith("VITE_")) {
|
|
|
|
|
acc[key] = value;
|
|
|
|
|
}
|
|
|
|
|
return acc;
|
|
|
|
|
},
|
|
|
|
|
{} as Record<string, any>,
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-30 02:15:46 +08:00
|
|
|
|
const newEnv = {
|
|
|
|
|
...viteEnv,
|
2024-12-04 02:35:06 +08:00
|
|
|
|
VITE_INIT_STATUS: "2",
|
2024-11-30 02:15:46 +08:00
|
|
|
|
};
|
|
|
|
|
|
2024-11-30 22:24:35 +08:00
|
|
|
|
await http.dev("/env", {
|
2024-11-30 02:15:46 +08:00
|
|
|
|
method: "POST",
|
|
|
|
|
body: JSON.stringify(newEnv),
|
|
|
|
|
});
|
|
|
|
|
|
2024-11-30 22:24:35 +08:00
|
|
|
|
Object.assign(import.meta.env, newEnv);
|
2024-11-30 02:15:46 +08:00
|
|
|
|
|
2024-12-04 02:35:06 +08:00
|
|
|
|
toast.success("数据库配置成功!");
|
|
|
|
|
|
2024-11-30 02:15:46 +08:00
|
|
|
|
setTimeout(() => onNext(), 1000);
|
|
|
|
|
} catch (error: any) {
|
2024-12-03 01:37:02 +08:00
|
|
|
|
console.error(error);
|
|
|
|
|
toast.error(error.message, error.title);
|
2024-11-30 02:15:46 +08:00
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-11-27 19:52:49 +08:00
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<StepContainer title="数据库配置">
|
2024-11-28 23:10:00 +08:00
|
|
|
|
<div>
|
2024-12-03 01:37:02 +08:00
|
|
|
|
<Box mb="6">
|
|
|
|
|
<Text as="label" size="2" weight="medium" mb="2" className="block">
|
2024-11-27 19:52:49 +08:00
|
|
|
|
数据库类型
|
2024-12-03 01:37:02 +08:00
|
|
|
|
</Text>
|
|
|
|
|
<Select.Root value={dbType} onValueChange={setDbType}>
|
|
|
|
|
<Select.Trigger />
|
|
|
|
|
<Select.Content>
|
|
|
|
|
<Select.Group>
|
|
|
|
|
<Select.Item value="postgresql">PostgreSQL</Select.Item>
|
|
|
|
|
<Select.Item value="mysql">MySQL</Select.Item>
|
|
|
|
|
<Select.Item value="sqllite">SQLite</Select.Item>
|
|
|
|
|
</Select.Group>
|
|
|
|
|
</Select.Content>
|
|
|
|
|
</Select.Root>
|
|
|
|
|
</Box>
|
2024-11-27 19:52:49 +08:00
|
|
|
|
|
|
|
|
|
{dbType === "postgresql" && (
|
|
|
|
|
<>
|
|
|
|
|
<InputField
|
|
|
|
|
label="数据库地址"
|
|
|
|
|
name="db_host"
|
|
|
|
|
defaultValue="localhost"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
hint="通常使 localhost"
|
|
|
|
|
required
|
2024-11-27 19:52:49 +08:00
|
|
|
|
/>
|
2024-11-28 23:10:00 +08:00
|
|
|
|
<InputField
|
|
|
|
|
label="数据库前缀"
|
|
|
|
|
name="db_prefix"
|
|
|
|
|
defaultValue="echoec_"
|
|
|
|
|
hint="通常使用 echoec_"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-28 23:10:00 +08:00
|
|
|
|
/>
|
2024-11-27 19:52:49 +08:00
|
|
|
|
<InputField
|
|
|
|
|
label="端口"
|
|
|
|
|
name="db_port"
|
|
|
|
|
defaultValue={5432}
|
|
|
|
|
hint="PostgreSQL 默认端口为 5432"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-27 19:52:49 +08:00
|
|
|
|
/>
|
|
|
|
|
<InputField
|
|
|
|
|
label="用户名"
|
|
|
|
|
name="db_user"
|
|
|
|
|
defaultValue="postgres"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-27 19:52:49 +08:00
|
|
|
|
/>
|
|
|
|
|
<InputField
|
|
|
|
|
label="密码"
|
|
|
|
|
name="db_password"
|
|
|
|
|
defaultValue="postgres"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-27 19:52:49 +08:00
|
|
|
|
/>
|
|
|
|
|
<InputField
|
|
|
|
|
label="数据库名"
|
|
|
|
|
name="db_name"
|
|
|
|
|
defaultValue="echoes"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-27 19:52:49 +08:00
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2024-11-28 23:10:00 +08:00
|
|
|
|
{dbType === "mysql" && (
|
|
|
|
|
<>
|
|
|
|
|
<InputField
|
|
|
|
|
label="数据库地址"
|
|
|
|
|
name="db_host"
|
|
|
|
|
defaultValue="localhost"
|
|
|
|
|
hint="通常使用 localhost"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-28 23:10:00 +08:00
|
|
|
|
/>
|
|
|
|
|
<InputField
|
|
|
|
|
label="数据库前缀"
|
|
|
|
|
name="db_prefix"
|
|
|
|
|
defaultValue="echoec_"
|
|
|
|
|
hint="通常使用 echoec_"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-28 23:10:00 +08:00
|
|
|
|
/>
|
|
|
|
|
<InputField
|
|
|
|
|
label="端口"
|
|
|
|
|
name="db_port"
|
|
|
|
|
defaultValue={3306}
|
|
|
|
|
hint="mysql 默认端口为 3306"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-28 23:10:00 +08:00
|
|
|
|
/>
|
|
|
|
|
<InputField
|
|
|
|
|
label="用户名"
|
|
|
|
|
name="db_user"
|
|
|
|
|
defaultValue="root"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-28 23:10:00 +08:00
|
|
|
|
/>
|
|
|
|
|
<InputField
|
|
|
|
|
label="密码"
|
|
|
|
|
name="db_password"
|
|
|
|
|
defaultValue="mysql"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-28 23:10:00 +08:00
|
|
|
|
/>
|
|
|
|
|
<InputField
|
|
|
|
|
label="数据库名"
|
|
|
|
|
name="db_name"
|
|
|
|
|
defaultValue="echoes"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-28 23:10:00 +08:00
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{dbType === "sqllite" && (
|
|
|
|
|
<>
|
|
|
|
|
<InputField
|
|
|
|
|
label="数据库前缀"
|
|
|
|
|
name="db_prefix"
|
|
|
|
|
defaultValue="echoec_"
|
|
|
|
|
hint="通常使用 echoec_"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-28 23:10:00 +08:00
|
|
|
|
/>
|
|
|
|
|
<InputField
|
|
|
|
|
label="数据库名"
|
|
|
|
|
name="db_name"
|
|
|
|
|
defaultValue="echoes.db"
|
2024-11-30 02:15:46 +08:00
|
|
|
|
required
|
2024-11-28 23:10:00 +08:00
|
|
|
|
/>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<NavigationButtons
|
|
|
|
|
onNext={handleNext}
|
2024-11-30 02:15:46 +08:00
|
|
|
|
loading={loading}
|
|
|
|
|
disabled={loading}
|
|
|
|
|
/>
|
2024-11-27 19:52:49 +08:00
|
|
|
|
</div>
|
|
|
|
|
</StepContainer>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2024-11-30 02:15:46 +08:00
|
|
|
|
interface InstallReplyData {
|
2024-12-04 02:35:06 +08:00
|
|
|
|
token: string;
|
|
|
|
|
username: string;
|
|
|
|
|
password: string;
|
2024-11-30 02:15:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const AdminConfig: React.FC<StepProps> = ({ onNext }) => {
|
|
|
|
|
const [loading, setLoading] = useState(false);
|
2024-12-03 01:37:02 +08:00
|
|
|
|
const http = HttpClient.getInstance();
|
2024-11-30 02:15:46 +08:00
|
|
|
|
|
|
|
|
|
const handleNext = async () => {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
try {
|
|
|
|
|
const formData = {
|
2024-12-04 02:35:06 +08:00
|
|
|
|
username: (
|
|
|
|
|
document.querySelector('[name="admin_username"]') as HTMLInputElement
|
|
|
|
|
)?.value,
|
|
|
|
|
password: (
|
|
|
|
|
document.querySelector('[name="admin_password"]') as HTMLInputElement
|
|
|
|
|
)?.value,
|
|
|
|
|
email: (
|
|
|
|
|
document.querySelector('[name="admin_email"]') as HTMLInputElement
|
|
|
|
|
)?.value,
|
2024-11-30 02:15:46 +08:00
|
|
|
|
};
|
|
|
|
|
|
2024-12-04 02:35:06 +08:00
|
|
|
|
const response = (await http.post(
|
|
|
|
|
"/administrator",
|
|
|
|
|
formData,
|
|
|
|
|
)) as InstallReplyData;
|
2024-11-30 02:15:46 +08:00
|
|
|
|
const data = response;
|
2024-12-04 02:35:06 +08:00
|
|
|
|
|
|
|
|
|
localStorage.setItem("token", data.token);
|
|
|
|
|
|
2024-11-30 02:15:46 +08:00
|
|
|
|
let oldEnv = import.meta.env ?? DEFAULT_CONFIG;
|
2024-12-04 02:35:06 +08:00
|
|
|
|
const viteEnv = Object.entries(oldEnv).reduce(
|
|
|
|
|
(acc, [key, value]) => {
|
|
|
|
|
if (key.startsWith("VITE_")) {
|
|
|
|
|
acc[key] = value;
|
|
|
|
|
}
|
|
|
|
|
return acc;
|
|
|
|
|
},
|
|
|
|
|
{} as Record<string, any>,
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-30 02:15:46 +08:00
|
|
|
|
const newEnv = {
|
|
|
|
|
...viteEnv,
|
2024-12-04 02:35:06 +08:00
|
|
|
|
VITE_INIT_STATUS: "3",
|
2024-11-30 22:24:35 +08:00
|
|
|
|
VITE_API_USERNAME: data.username,
|
2024-12-04 02:35:06 +08:00
|
|
|
|
VITE_API_PASSWORD: data.password,
|
2024-11-30 02:15:46 +08:00
|
|
|
|
};
|
|
|
|
|
|
2024-11-30 22:24:35 +08:00
|
|
|
|
await http.dev("/env", {
|
2024-11-30 02:15:46 +08:00
|
|
|
|
method: "POST",
|
|
|
|
|
body: JSON.stringify(newEnv),
|
|
|
|
|
});
|
|
|
|
|
|
2024-11-30 22:24:35 +08:00
|
|
|
|
Object.assign(import.meta.env, newEnv);
|
|
|
|
|
|
2024-12-04 02:35:06 +08:00
|
|
|
|
toast.success("管理员账号创建成功!");
|
2024-11-30 02:15:46 +08:00
|
|
|
|
onNext();
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error(error);
|
2024-12-03 01:37:02 +08:00
|
|
|
|
toast.error(error.message, error.title);
|
2024-11-30 02:15:46 +08:00
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<StepContainer title="创建管理员账号">
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<InputField label="用户名" name="admin_username" />
|
|
|
|
|
<InputField label="密码" name="admin_password" />
|
|
|
|
|
<InputField label="邮箱" name="admin_email" />
|
|
|
|
|
<NavigationButtons onNext={handleNext} loading={loading} />
|
|
|
|
|
</div>
|
|
|
|
|
</StepContainer>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2024-12-04 02:35:06 +08:00
|
|
|
|
const SetupComplete: React.FC = () => (
|
|
|
|
|
<StepContainer title="安装完成">
|
|
|
|
|
<Flex direction="column" align="center" gap="4">
|
|
|
|
|
<Text size="5" weight="medium">
|
|
|
|
|
恭喜!安装已完成
|
|
|
|
|
</Text>
|
|
|
|
|
<Text size="3">系统正在重启中,请稍候...</Text>
|
|
|
|
|
<Box mt="4">
|
|
|
|
|
<Flex justify="center">
|
|
|
|
|
<Box className="animate-spin rounded-full h-8 w-8 border-b-2 border-current"></Box>
|
|
|
|
|
</Flex>
|
|
|
|
|
</Box>
|
|
|
|
|
</Flex>
|
|
|
|
|
</StepContainer>
|
|
|
|
|
);
|
2024-11-27 19:52:49 +08:00
|
|
|
|
|
2024-12-03 01:37:02 +08:00
|
|
|
|
export default function SetupPage() {
|
|
|
|
|
const [currentStep, setCurrentStep] = useState(() => {
|
|
|
|
|
return Number(import.meta.env.VITE_INIT_STATUS ?? 0) + 1;
|
|
|
|
|
});
|
|
|
|
|
|
2024-11-28 23:10:00 +08:00
|
|
|
|
return (
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<Theme
|
|
|
|
|
grayColor="gray"
|
|
|
|
|
accentColor="gray"
|
2024-12-03 01:37:02 +08:00
|
|
|
|
radius="medium"
|
2024-12-04 02:35:06 +08:00
|
|
|
|
panelBackground="solid"
|
|
|
|
|
appearance="inherit"
|
2024-11-28 23:10:00 +08:00
|
|
|
|
>
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<Box className="min-h-screen w-full">
|
|
|
|
|
<Box position="fixed" top="2" right="4">
|
2024-12-03 01:37:02 +08:00
|
|
|
|
<ThemeModeToggle />
|
2024-12-04 02:35:06 +08:00
|
|
|
|
</Box>
|
|
|
|
|
|
|
|
|
|
<Flex justify="center" pt="2">
|
|
|
|
|
<Box className="w-20 h-20">
|
2024-12-03 01:37:02 +08:00
|
|
|
|
<Echoes />
|
2024-12-04 02:35:06 +08:00
|
|
|
|
</Box>
|
|
|
|
|
</Flex>
|
|
|
|
|
|
2024-12-03 01:37:02 +08:00
|
|
|
|
<Flex direction="column" className="min-h-screen w-full pb-4">
|
|
|
|
|
<Container className="w-full">
|
|
|
|
|
<SetupContext.Provider value={{ currentStep, setCurrentStep }}>
|
|
|
|
|
{currentStep === 1 && (
|
|
|
|
|
<Introduction onNext={() => setCurrentStep(currentStep + 1)} />
|
|
|
|
|
)}
|
|
|
|
|
{currentStep === 2 && (
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<DatabaseConfig
|
2024-12-03 01:37:02 +08:00
|
|
|
|
onNext={() => setCurrentStep(currentStep + 1)}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
{currentStep === 3 && (
|
2024-12-04 02:35:06 +08:00
|
|
|
|
<AdminConfig onNext={() => setCurrentStep(currentStep + 1)} />
|
2024-12-03 01:37:02 +08:00
|
|
|
|
)}
|
|
|
|
|
{currentStep === 4 && <SetupComplete />}
|
|
|
|
|
</SetupContext.Provider>
|
|
|
|
|
</Container>
|
|
|
|
|
</Flex>
|
2024-12-04 02:35:06 +08:00
|
|
|
|
</Box>
|
2024-12-03 01:37:02 +08:00
|
|
|
|
</Theme>
|
2024-11-27 19:52:49 +08:00
|
|
|
|
);
|
2024-12-04 02:35:06 +08:00
|
|
|
|
}
|