Smail/fe/src/views/SetupView.vue
HuangZhe 6b123ffab3
fix: 修复 db_dsn 为空的错误 (#200)
1. 在切换数据库类型时, sqlite 的存储位置会被清空. 现于 placeholder 保持一致.
2024-09-10 11:39:31 +08:00

543 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div id="main">
<el-steps :active="active" align-center finish-status="success" id="status">
<el-step :title="lang.welcome" />
<el-step :title="lang.setDatabase" />
<el-step :title="lang.setAdminPassword" />
<el-step :title="lang.SetDomail" />
<el-step :title="lang.setDNS" />
<el-step :title="lang.setSSL" />
</el-steps>
<div v-if="active == 0" class="ctn">
<div class="desc">
<h2>{{ lang.tks_pmail }}</h2>
<div style="margin-top: 10px;">{{ lang.guid_desc }}</div>
</div>
</div>
<div v-if="active == 1" class="ctn">
<div class="desc">
<h2>{{ lang.select_db }}</h2>
<div style="margin-top: 10px;">{{ lang.db_desc }}</div>
</div>
<div class="form" style="width: 400px;">
<el-form label-width="120px">
<el-form-item :label="lang.type">
<el-select :placeholder="lang.db_select_ph" v-model="dbSettings.type"
@change="dbSettings.dsn = ''">
<el-option label="MySQL" value="mysql" />
<el-option label="SQLite3" value="sqlite" />
<el-option label="PostgreSQL" value="postgres" />
</el-select>
</el-form-item>
<el-form-item :label="lang.mysql_dsn" v-if="dbSettings.type == 'mysql'">
<el-input :rows="2" type="textarea" v-model="dbSettings.dsn"
placeholder="root:12345@tcp(127.0.0.1:3306)/pmail?parseTime=True&loc=Local"></el-input>
</el-form-item>
<el-form-item :label="lang.pg_dsn" v-if="dbSettings.type == 'postgres'">
<el-input :rows="2" type="textarea" v-model="dbSettings.dsn"
placeholder="postgres://postgres:12345@127.0.0.1:5432/pmail?sslmode=disable"></el-input>
</el-form-item>
<el-form-item :label="lang.sqlite_db_path" v-if="dbSettings.type == 'sqlite'">
<el-input v-model="dbSettings.dsn" placeholder="./config/pmail.db"></el-input>
</el-form-item>
</el-form>
</div>
</div>
<div v-if="active == 2" class="ctn">
<div class="desc">
<h2>{{ lang.setAdminPassword }}</h2>
<!-- <div style="margin-top: 10px;">{{ lang.domain_desc }}</div> -->
</div>
<div class="form" style="width: 400px;">
<el-form label-width="120px">
<el-form-item :label="lang.admin_account">
<el-input v-bind:disabled="adminSettings.hadSeted" placeholder="admin"
v-model="adminSettings.account"></el-input>
</el-form-item>
<el-form-item :label="lang.password">
<el-input type="password" v-bind:disabled="adminSettings.hadSeted" placeholder=""
v-model="adminSettings.password"></el-input>
</el-form-item>
<el-form-item :label="lang.enter_again">
<el-input type="password" v-bind:disabled="adminSettings.hadSeted" placeholder=""
v-model="adminSettings.password2"></el-input>
</el-form-item>
</el-form>
</div>
</div>
<div v-if="active == 3" class="ctn">
<div class="desc">
<h2>{{ lang.SetDomail }}</h2>
<!-- <div style="margin-top: 10px;">{{ lang.domain_desc }}</div> -->
</div>
<div class="form" style="width: 400px;">
<el-form label-width="120px">
<el-form-item :label="lang.smtp_domain">
<el-input placeholder="domaim.com" v-model="domainSettings.smtp_domain">
<template #prepend>smtp.</template>
</el-input>
</el-form-item>
<el-form-item :label="lang.web_domain">
<el-input placeholder="pmail.domain.com" v-model="domainSettings.web_domain"></el-input>
</el-form-item>
<el-form-item :label="lang.multi_domain_setting">
<span>{{ lang.multi_domain_setting_desc }} <el-button @click="addDomain" size="small"
type="success" :icon="Plus" circle></el-button></span>
<el-input :placeholder="'domain' + i + '.com'" v-for="(item, i) in domainSettings.multi_domain"
v-model="domainSettings.multi_domain[i]"></el-input>
</el-form-item>
</el-form>
</div>
</div>
<div v-if="active == 4" class="ctn_s">
<div class="desc">
<h2>{{ lang.setDNS }}</h2>
<div style="margin-top: 10px;">{{ lang.dns_desc }}</div>
</div>
<div class="form" width="600px" v-for="(info,domain) in dnsInfos">
<h3>{{ domain }}</h3>
<el-table :data="info" border style="width: 100%">
<el-table-column prop="host" label="HOSTNAME" width="110px" >
<template #default="scope">
<div style="display: flex; align-items: center">
<el-tooltip :content="lang.dns_root_desc" placement="top" v-if="scope.row.host == '' || scope.row.host == '@' ">
{{ scope.row.host }}
</el-tooltip>
<span v-else>{{ scope.row.host }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="type" label="TYPE" width="110px" />
<el-table-column prop="value" label="VALUE">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-tooltip :content="scope.row.tips" placement="top" v-if="scope.row.tips != ''">
{{ scope.row.value }}
</el-tooltip>
<span v-else>{{ scope.row.value }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="ttl" label="TTL" width="110px" />
</el-table>
</div>
</div>
<el-alert :closable="false" title="Warning!" type="error" center
v-if="active == 5 && sslSettings.type == 0 && port != 80" :description="lang.autoSSLWarn" />
<div v-if="active == 5" class="ctn">
<div class="desc">
<h2>{{ lang.setSSL }}</h2>
<div style="margin-top: 10px;">{{ lang.setSSL }}</div>
</div>
<div class="form" width="600px">
<el-form label-width="120px">
<el-form-item :label="lang.type">
<el-select :placeholder="lang.ssl_auto" v-model="sslSettings.type" :disabled="dnsChecking">
<el-option :label="lang.ssl_auto" value="0" />
<el-option :label="lang.ssl_manuallyf" value="1" />
</el-select>
</el-form-item>
<el-form-item :label="lang.ssl_challenge_type" v-if="sslSettings.type == '0'">
<el-select :placeholder="lang.ssl_auto_http" v-model="sslSettings.challenge"
:disabled="dnsChecking">
<el-option :label="lang.ssl_auto_http" value="http" />
<el-option :label="lang.ssl_auto_dns" value="dns" />
</el-select>
<el-tooltip class="box-item" effect="dark" :content="lang.challenge_typ_desc"
placement="top-start">
<span style="margin-left: 6px; font-size:18px; font-weight: bolder;">?</span>
</el-tooltip>
</el-form-item>
<el-form-item :label="lang.ssl_key_path" v-if="sslSettings.type == '1'">
<el-input placeholder="./config/ssl/private.key" v-model="sslSettings.key_path"></el-input>
</el-form-item>
<el-form-item :label="lang.ssl_crt_path" v-if="sslSettings.type == '1'">
<el-input placeholder="./config/ssl/public.crt" v-model="sslSettings.crt_path"></el-input>
</el-form-item>
</el-form>
</div>
</div>
<div v-if="dnsChecking">
<label>{{ lang.dns_desc }}</label>
<el-table :data="sslSettings.paramsList" border v-loading="sslSettings.paramsList.length == 0">
<el-table-column prop="host" label="HOSTNAME" width="110px" />
<el-table-column prop="type" label="TYPE" width="110px" />
<el-table-column prop="value" label="VALUE">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-tooltip :content="scope.row.tips" placement="top" v-if="scope.row.tips != ''">
{{ scope.row.value }}
</el-tooltip>
<span v-else>{{ scope.row.value }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="ttl" label="TTL" width="110px" />
</el-table>
</div>
<el-button :element-loading-text="waitDesc" v-loading.fullscreen.lock="fullscreenLoading" id="next"
style="margin-top: 12px" @click="next">{{
lang.next }}</el-button>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import lang from '../i18n/i18n';
import axios from 'axios'
import { getCurrentInstance } from 'vue'
import {
Plus
} from '@element-plus/icons-vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
const waitDesc = ref(lang.wait_desc)
const adminSettings = reactive({
"account": "admin",
"password": "",
"password2": "",
"hadSeted": false
})
const dbSettings = reactive({
"type": "sqlite",
"dsn": "./config/pmail.db",
"lable": ""
})
const domainSettings = reactive({
"web_domain": "",
"smtp_domain": "",
"multi_domain": []
})
const sslSettings = reactive({
"type": "0",
"challenge": "http",
"key_path": "./config/ssl/private.key",
"crt_path": "./config/ssl/public.crt",
"paramsList": [],
})
const active = ref(0)
const fullscreenLoading = ref(false)
const dnsChecking = ref(false)
const dnsInfos = ref({})
const port = ref(80)
const addDomain = () => {
domainSettings.multi_domain.push([])
}
const setPassword = () => {
if (adminSettings.hadSeted) {
active.value++;
getDomainConfig();
return;
}
if (adminSettings.password != adminSettings.password2) {
ElMessage.error(lang.err_pwd_diff)
} else {
$http.post("/api/setup", { "action": "set", "step": "password", "account": adminSettings.account, "password": adminSettings.password }).then((res) => {
if (res.errorNo != 0) {
ElMessage.error(res.errorMsg)
} else {
active.value++;
getDomainConfig();
}
})
}
}
const getPassword = () => {
$http.post("/api/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 getDbConfig = () => {
$http.post("/api/setup", { "action": "get", "step": "database" }).then((res) => {
if (res.errorNo != 0) {
ElMessage.error(res.errorMsg)
} else {
dbSettings.type = res.data.db_type;
dbSettings.dsn = res.data.db_dsn;
}
})
}
const getDomainConfig = () => {
$http.post("/api/setup", { "action": "get", "step": "domain" }).then((res) => {
if (res.errorNo != 0) {
ElMessage.error(res.errorMsg)
} else {
domainSettings.web_domain = res.data.web_domain;
domainSettings.smtp_domain = res.data.smtp_domain;
domainSettings.multi_domain = res.data.domains;
}
})
}
const setDbConfig = () => {
// 切换数据库类型为sqlite时,数据库路径为空,则使用默认路径
if (dbSettings.type === "sqlite" && !dbSettings.dsn) dbSettings.dsn = "./config/pmail.db";
else if (!dbSettings.dsn) ElMessage({
title: "Error",
message: lang.err_db_dsn_empty,
type: "error",
});
$http.post("/api/setup", { "action": "set", "step": "database", "db_type": dbSettings.type, "db_dsn": dbSettings.dsn }).then((res) => {
if (res.errorNo != 0) {
ElMessage.error(res.errorMsg)
} else {
active.value++;
getPassword();
}
})
}
const getDNSConfig = () => {
$http.post("/api/setup", { "action": "get", "step": "dns" }).then((res) => {
if (res.errorNo != 0) {
ElMessage.error(res.errorMsg)
} else {
dnsInfos.value = res.data
}
})
}
const getSSLConfig = () => {
$http.post("/api/setup", { "action": "get", "step": "ssl" }).then((res) => {
if (res.errorNo != 0) {
ElMessage.error(res.errorMsg)
} else {
sslSettings.type = res.data.type
if (sslSettings.type == "2") {
sslSettings.type = "0"
sslSettings.challenge = "dns"
}
port.value = res.data.port
}
})
}
const setSSLConfig = () => {
fullscreenLoading.value = true;
let sslType = sslSettings.type;
if (sslType == "0" && sslSettings.challenge == "dns") {
sslType = "2"
}
$http.post("/api/setup", {
"action": "set",
"step": "ssl",
"ssl_type": sslType,
"key_path": sslSettings.key_path,
"crt_path": sslSettings.crt_path
}).then((res) => {
if (res.errorNo != 0) {
fullscreenLoading.value = false;
ElMessage.error(res.errorMsg)
} else {
if (sslType == 2) {
fullscreenLoading.value = false;
dnsChecking.value = true;
getSSLDNSParams();
}
checkStatus();
}
})
}
const checkStatus = () => {
axios.post("/api/ping", {}).then((res) => {
if (res.data.errorNo != 0) {
setTimeout(function () {
checkStatus()
}, 1000);
} else {
if(sslSettings.type == 1){
window.location.href = "http://" + domainSettings.web_domain;
}else{
window.location.href = "https://" + domainSettings.web_domain;
}
}
}).catch((error) => {
setTimeout(function () {
checkStatus()
}, 1000);
})
}
const setDomainConfig = () => {
$http.post("/api/setup", {
"action": "set",
"step": "domain",
"web_domain": domainSettings.web_domain,
"smtp_domain": domainSettings.smtp_domain,
"multi_domain": domainSettings.multi_domain.join(",")
}).then((res) => {
if (res.errorNo != 0) {
ElMessage.error(res.errorMsg)
} else {
active.value++;
getDNSConfig();
}
})
}
const getSSLDNSParams = () => {
$http.post("/api/setup", { "action": "getParams", "step": "ssl" }).then((res) => {
if (res.errorNo != 0) {
ElMessage.error(res.errorMsg)
} else {
sslSettings.paramsList = res.data
console.log(sslSettings.paramsList)
}
})
if (sslSettings.paramsList.length == 0) {
setTimeout(function () {
getSSLDNSParams()
}, 1000);
}
}
const next = () => {
switch (active.value) {
case 0:
active.value++
getDbConfig();
break
case 1:
setDbConfig();
break;
case 2:
setPassword();
break;
case 3:
setDomainConfig();
break;
case 4:
getSSLConfig();
active.value++
break
case 5:
if (dnsChecking.value) {
fullscreenLoading.value = true;
waitDesc.value = lang.dns_challenge_wait;
} else {
setSSLConfig();
}
break
}
}
</script>
<style scoped>
#main {
width: 100%;
height: 100%;
background-color: #f1f1f1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.desc {
padding-right: 20px;
}
#status {}
.ctn {
display: flex;
justify-content: center;
}
.ctn_s {
display: flex;
flex-direction: column;
}
#next {}
</style>