diff --git a/fe/src/i18n/i18n.js b/fe/src/i18n/i18n.js
index 03224ded..c5a5d244 100644
--- a/fe/src/i18n/i18n.js
+++ b/fe/src/i18n/i18n.js
@@ -89,6 +89,8 @@ const lang = {
"db_name_required": "请输入数据库名",
"db_init_success": "数据库连接成功,数据库和数据表已初始化",
"db_init_failed": "数据库初始化失败,请检查连接信息和数据库权限",
+ "db_init_retry_tip": "连接失败不会跳转登录页,请修正信息后重新点击下一步。",
+ "db_request_failed": "数据库初始化请求失败,请检查 Smail 服务状态后重试",
"pg_user_ph": "请输入 PostgreSQL 用户名",
"pg_password_ph": "请输入 PostgreSQL 密码",
"pg_dsn": "PostgreSQL DSN",
diff --git a/fe/src/views/Setup/Setup.css b/fe/src/views/Setup/Setup.css
index 4274c8b7..461437d6 100644
--- a/fe/src/views/Setup/Setup.css
+++ b/fe/src/views/Setup/Setup.css
@@ -116,6 +116,17 @@
line-height: 1.7;
}
+.database-error {
+ margin: 0 0 18px;
+}
+
+.database-retry-tip {
+ margin: -4px 0 8px 120px;
+ color: var(--pm-text-secondary);
+ font-size: 12px;
+ line-height: 1.5;
+}
+
.database-inline-fields {
display: grid;
grid-template-columns: minmax(0, 1fr) 180px;
diff --git a/fe/src/views/Setup/Setup.js b/fe/src/views/Setup/Setup.js
index 19061d7b..e6b20a30 100644
--- a/fe/src/views/Setup/Setup.js
+++ b/fe/src/views/Setup/Setup.js
@@ -52,6 +52,16 @@ export function usePage() {
const fullscreenLoading = ref(false)
const databaseLoading = ref(false)
+
+ const databaseError = ref("")
+
+ const getDatabaseErrorMessage = (error) => {
+ const message = typeof error?.message === "string" ? error.message.trim() : ""
+ if (message && !/^network error$/i.test(message) && !/^timeout of \d+ms exceeded$/i.test(message) && !/^request failed with status code \d+$/i.test(message)) {
+ return message
+ }
+ return lang.db_request_failed
+ }
const dnsChecking = ref(false)
@@ -109,22 +119,19 @@ export function usePage() {
})
}
- const getPassword = () => {
- http.post("/api/v1/setup", {"action": "get", "step": "password"}).then((res) => {
- if (res.errorNo !== 0) {
- ElMessage.error(res.errorMsg)
- } else {
- adminSettings.hadSeted = res.data !== ""
- if (adminSettings.hadSeted) {
- adminSettings.account = res.data
- adminSettings.password = "*******"
- adminSettings.password2 = "*******"
- }
-
- }
- })
- }
-
+ const getPassword = () => http.post("/api/v1/setup", {"action": "get", "step": "password"}).then((res) => {
+ if (res.errorNo !== 0) {
+ throw new Error(res.errorMsg || lang.db_init_failed)
+ }
+
+ adminSettings.hadSeted = res.data != ""
+ if (adminSettings.hadSeted) {
+ adminSettings.account = res.data
+ adminSettings.password = "*******"
+ adminSettings.password2 = "*******"
+ }
+ })
+
const getDbConfig = () => {
http.post("/api/v1/setup", {"action": "get", "step": "database"}).then((res) => {
if (res.errorNo !== 0) {
@@ -155,6 +162,11 @@ export function usePage() {
}
const setDbConfig = () => {
+ if (databaseLoading.value) {
+ return
+ }
+ databaseError.value = ""
+
if (dbSettings.type === "sqlite" && !dbSettings.dsn) {
dbSettings.dsn = "./config/smail.db";
}
@@ -173,6 +185,10 @@ export function usePage() {
ElMessage.error(lang.db_port_required)
return
}
+ if (!/^\d+$/.test(dbSettings.port.trim()) || Number(dbSettings.port) < 1 || Number(dbSettings.port) > 65535) {
+ ElMessage.error("数据库端口必须是 1 到 65535 之间的数字")
+ return
+ }
if (!dbSettings.name.trim()) {
ElMessage.error(lang.db_name_required)
return
@@ -198,20 +214,27 @@ export function usePage() {
http.post("/api/v1/setup", payload).then((res) => {
if (res.errorNo !== 0) {
- ElMessage.error(res.errorMsg || lang.db_init_failed)
+ databaseError.value = res.errorMsg || lang.db_init_failed
+ ElMessage.error(databaseError.value)
return
}
- ElMessage.success(lang.db_init_success)
- active.value++;
- getPassword();
- }).catch(() => {
- ElMessage.error(lang.db_init_failed)
+ getPassword().then(() => {
+ ElMessage.success(lang.db_init_success)
+ active.value++
+ }).catch((error) => {
+ databaseError.value = getDatabaseErrorMessage(error)
+ ElMessage.error(databaseError.value)
+ })
+ }).catch((error) => {
+ databaseError.value = getDatabaseErrorMessage(error)
+ ElMessage.error(databaseError.value)
}).finally(() => {
databaseLoading.value = false
})
}
const handleDbTypeChange = (type) => {
+ databaseError.value = ""
dbSettings.dsn = ""
dbSettings.password = ""
if (type === "mysql" || type === "postgres") {
@@ -259,6 +282,10 @@ export function usePage() {
}
}
+ const clearDatabaseError = () => {
+ databaseError.value = ""
+ }
+
const formatDNSRecordTTL = (ttl) => {
if (ttl >= 3600 && ttl % 3600 === 0) {
return `${ttl / 3600}${lang.dns_ttl_hour}`
@@ -413,6 +440,7 @@ export function usePage() {
adminSettings,
dbSettings,
databaseLoading,
+ databaseError,
domainSettings,
sslSettings,
active,
@@ -428,6 +456,7 @@ export function usePage() {
getDomainConfig,
setDbConfig,
handleDbTypeChange,
+ clearDatabaseError,
getDNSConfig,
formatDNSRecordTTL,
copyDNSValue,
diff --git a/fe/src/views/Setup/Setup.vue b/fe/src/views/Setup/Setup.vue
index ae5e6cf6..37a5767b 100644
--- a/fe/src/views/Setup/Setup.vue
+++ b/fe/src/views/Setup/Setup.vue
@@ -33,40 +33,66 @@
+