359 lines
10 KiB
TypeScript
359 lines
10 KiB
TypeScript
/**
|
||
* KRA - Database Init Page
|
||
* Database initialization wizard
|
||
*/
|
||
import React, { useState } from 'react';
|
||
import { Form, Input, Select, Button, message, Modal, Spin } from 'antd';
|
||
import { history, Helmet } from '@umijs/max';
|
||
import { initDB } from '@/services/kratos/initdb';
|
||
import { createStyles } from 'antd-style';
|
||
|
||
type DBType = 'mysql' | 'pgsql' | 'oracle' | 'mssql' | 'sqlite';
|
||
|
||
interface InitFormData {
|
||
adminPassword: string;
|
||
dbType: DBType;
|
||
host: string;
|
||
port: string;
|
||
userName: string;
|
||
password: string;
|
||
dbName: string;
|
||
dbPath?: string;
|
||
template?: string;
|
||
}
|
||
|
||
const dbDefaults: Record<DBType, Partial<InitFormData>> = {
|
||
mysql: {
|
||
host: '127.0.0.1',
|
||
port: '3306',
|
||
userName: 'root',
|
||
dbName: 'kra',
|
||
},
|
||
pgsql: {
|
||
host: '127.0.0.1',
|
||
port: '5432',
|
||
userName: 'postgres',
|
||
dbName: 'kra',
|
||
template: 'template0',
|
||
},
|
||
oracle: {
|
||
host: '127.0.0.1',
|
||
port: '1521',
|
||
userName: 'oracle',
|
||
dbName: 'kra',
|
||
},
|
||
mssql: {
|
||
host: '127.0.0.1',
|
||
port: '1433',
|
||
userName: 'mssql',
|
||
dbName: 'kra',
|
||
},
|
||
sqlite: {
|
||
host: '',
|
||
port: '',
|
||
userName: '',
|
||
dbName: 'kra',
|
||
dbPath: '',
|
||
},
|
||
};
|
||
|
||
const useStyles = createStyles(({ token }) => ({
|
||
container: {
|
||
width: '100%',
|
||
height: '100vh',
|
||
display: 'flex',
|
||
position: 'relative',
|
||
overflow: 'hidden',
|
||
},
|
||
leftSection: {
|
||
flex: 1,
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
background: '#fff',
|
||
position: 'relative',
|
||
zIndex: 10,
|
||
},
|
||
rightSection: {
|
||
width: '50%',
|
||
height: '100%',
|
||
background: '#194bfb',
|
||
display: 'none',
|
||
'@media (min-width: 768px)': {
|
||
display: 'block',
|
||
},
|
||
},
|
||
rightBanner: {
|
||
width: '100%',
|
||
height: '100%',
|
||
objectFit: 'cover',
|
||
},
|
||
oblique: {
|
||
position: 'absolute',
|
||
height: '130%',
|
||
width: '60%',
|
||
background: '#fff',
|
||
transform: 'rotate(-12deg)',
|
||
marginLeft: '-320px',
|
||
zIndex: 5,
|
||
},
|
||
content: {
|
||
zIndex: 999,
|
||
maxWidth: '500px',
|
||
padding: '24px',
|
||
},
|
||
readmeSection: {
|
||
animation: 'slideInFwdTop 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) both',
|
||
},
|
||
formSection: {
|
||
width: '384px',
|
||
animation: 'slideInLeft 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both',
|
||
},
|
||
title: {
|
||
fontSize: '32px',
|
||
fontWeight: 700,
|
||
textAlign: 'center' as const,
|
||
marginBottom: '16px',
|
||
color: token.colorText,
|
||
},
|
||
subtitle: {
|
||
fontSize: '16px',
|
||
color: token.colorTextSecondary,
|
||
marginBottom: '8px',
|
||
},
|
||
notice: {
|
||
fontSize: '14px',
|
||
color: token.colorTextSecondary,
|
||
marginBottom: '8px',
|
||
lineHeight: 1.8,
|
||
},
|
||
link: {
|
||
color: '#1890ff',
|
||
fontWeight: 600,
|
||
},
|
||
highlight: {
|
||
color: '#ff4d4f',
|
||
fontWeight: 700,
|
||
fontSize: '24px',
|
||
marginLeft: '8px',
|
||
},
|
||
buttonGroup: {
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
marginTop: '32px',
|
||
},
|
||
formItem: {
|
||
marginBottom: '16px',
|
||
},
|
||
}));
|
||
|
||
const InitPage: React.FC = () => {
|
||
const [form] = Form.useForm();
|
||
const [showForm, setShowForm] = useState(false);
|
||
const [loading, setLoading] = useState(false);
|
||
const { styles } = useStyles();
|
||
|
||
const handleShowForm = () => {
|
||
setShowForm(true);
|
||
};
|
||
|
||
const handleGoDoc = () => {
|
||
window.open('https://go-kratos.dev/docs/getting-started/start', '_blank');
|
||
};
|
||
|
||
const handleDBTypeChange = (value: DBType) => {
|
||
const defaults = dbDefaults[value];
|
||
form.setFieldsValue({
|
||
...defaults,
|
||
password: '',
|
||
adminPassword: form.getFieldValue('adminPassword'),
|
||
});
|
||
};
|
||
|
||
const handleSubmit = async (values: InitFormData) => {
|
||
if (values.adminPassword.length < 6) {
|
||
message.error('密码长度不能小于6位');
|
||
return;
|
||
}
|
||
|
||
setLoading(true);
|
||
try {
|
||
const res = await initDB(values);
|
||
if (res.code === 0) {
|
||
message.success(res.msg || '初始化成功');
|
||
|
||
Modal.confirm({
|
||
title: '配置完成',
|
||
content: '已经完成基础数据库初始化!建议先阅读官方文档,以获得更好的开发体验。',
|
||
okText: '查看文档',
|
||
cancelText: '稍后查看',
|
||
centered: true,
|
||
onOk: () => {
|
||
window.open('https://go-kratos.dev/docs/', '_blank');
|
||
history.push('/user/login');
|
||
},
|
||
onCancel: () => {
|
||
history.push('/user/login');
|
||
},
|
||
});
|
||
} else {
|
||
message.error(res.msg || '初始化失败');
|
||
}
|
||
} catch (error: any) {
|
||
message.error(error?.message || '初始化失败');
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className={styles.container}>
|
||
<Helmet>
|
||
<title>初始化 - Kratos Admin</title>
|
||
</Helmet>
|
||
|
||
<Spin spinning={loading} tip="正在初始化数据库,请稍候..." fullscreen />
|
||
|
||
<div className={styles.leftSection}>
|
||
<div className={styles.oblique} />
|
||
|
||
{!showForm ? (
|
||
<div className={`${styles.content} ${styles.readmeSection}`}>
|
||
<h1 className={styles.title}>KRATOS-ADMIN</h1>
|
||
<p className={styles.subtitle}>初始化须知</p>
|
||
<p className={styles.notice}>1. 您需有一定的 React 和 Golang 基础</p>
|
||
<p className={styles.notice}>
|
||
2. 请您确认是否已经阅读过
|
||
<a className={styles.link} href="https://go-kratos.dev/docs/" target="_blank" rel="noopener noreferrer">
|
||
官方文档
|
||
</a>
|
||
</p>
|
||
<p className={styles.notice}>3. 请您确认是否了解后续的配置流程</p>
|
||
<p className={styles.notice}>
|
||
4. 如果您使用 MySQL 数据库,请确认数据库引擎为
|
||
<span className={styles.highlight}>InnoDB</span>
|
||
</p>
|
||
<p className={styles.notice}>注:开发组不为文档中书写过的内容提供无偿服务</p>
|
||
|
||
<div className={styles.buttonGroup}>
|
||
<Button type="primary" size="large" onClick={handleGoDoc}>
|
||
阅读文档
|
||
</Button>
|
||
<Button type="primary" size="large" onClick={handleShowForm}>
|
||
我已确认
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<div className={`${styles.content} ${styles.formSection}`}>
|
||
<Form
|
||
form={form}
|
||
layout="horizontal"
|
||
labelCol={{ span: 6 }}
|
||
wrapperCol={{ span: 18 }}
|
||
initialValues={{
|
||
adminPassword: '123456',
|
||
dbType: 'mysql',
|
||
...dbDefaults.mysql,
|
||
password: '',
|
||
}}
|
||
onFinish={handleSubmit}
|
||
size="large"
|
||
>
|
||
<Form.Item
|
||
label="管理员密码"
|
||
name="adminPassword"
|
||
className={styles.formItem}
|
||
rules={[
|
||
{ required: true, message: '请输入管理员密码' },
|
||
{ min: 6, message: '密码至少6个字符' },
|
||
]}
|
||
>
|
||
<Input.Password placeholder="admin账号的默认密码" />
|
||
</Form.Item>
|
||
|
||
<Form.Item
|
||
label="数据库类型"
|
||
name="dbType"
|
||
className={styles.formItem}
|
||
>
|
||
<Select onChange={handleDBTypeChange}>
|
||
<Select.Option value="mysql">MySQL</Select.Option>
|
||
<Select.Option value="pgsql">PostgreSQL</Select.Option>
|
||
<Select.Option value="oracle">Oracle</Select.Option>
|
||
<Select.Option value="mssql">SQL Server</Select.Option>
|
||
<Select.Option value="sqlite">SQLite</Select.Option>
|
||
</Select>
|
||
</Form.Item>
|
||
|
||
<Form.Item noStyle shouldUpdate={(prev, cur) => prev.dbType !== cur.dbType}>
|
||
{({ getFieldValue }) => {
|
||
const dbType = getFieldValue('dbType');
|
||
if (dbType === 'sqlite') return null;
|
||
return (
|
||
<>
|
||
<Form.Item label="Host" name="host" className={styles.formItem}>
|
||
<Input placeholder="请输入数据库链接" />
|
||
</Form.Item>
|
||
<Form.Item label="Port" name="port" className={styles.formItem}>
|
||
<Input placeholder="请输入数据库端口" />
|
||
</Form.Item>
|
||
<Form.Item label="用户名" name="userName" className={styles.formItem}>
|
||
<Input placeholder="请输入数据库用户名" />
|
||
</Form.Item>
|
||
<Form.Item label="密码" name="password" className={styles.formItem}>
|
||
<Input.Password placeholder="请输入数据库密码(没有则为空)" />
|
||
</Form.Item>
|
||
</>
|
||
);
|
||
}}
|
||
</Form.Item>
|
||
|
||
<Form.Item label="数据库名" name="dbName" className={styles.formItem}>
|
||
<Input placeholder="请输入数据库名称" />
|
||
</Form.Item>
|
||
|
||
<Form.Item noStyle shouldUpdate={(prev, cur) => prev.dbType !== cur.dbType}>
|
||
{({ getFieldValue }) => {
|
||
const dbType = getFieldValue('dbType');
|
||
if (dbType === 'sqlite') {
|
||
return (
|
||
<Form.Item label="数据库路径" name="dbPath" className={styles.formItem}>
|
||
<Input placeholder="请输入sqlite数据库文件存放路径" />
|
||
</Form.Item>
|
||
);
|
||
}
|
||
if (dbType === 'pgsql') {
|
||
return (
|
||
<Form.Item label="Template" name="template" className={styles.formItem}>
|
||
<Input placeholder="请输入postgresql指定template" />
|
||
</Form.Item>
|
||
);
|
||
}
|
||
return null;
|
||
}}
|
||
</Form.Item>
|
||
|
||
<Form.Item wrapperCol={{ offset: 6, span: 18 }}>
|
||
<Button type="primary" htmlType="submit" block>
|
||
立即初始化
|
||
</Button>
|
||
</Form.Item>
|
||
</Form>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<div className={styles.rightSection}>
|
||
<img
|
||
src="/login_right_banner.jpg"
|
||
alt="banner"
|
||
className={styles.rightBanner}
|
||
/>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default InitPage;
|