fix(frontend): polish cert dialog and wire upload API
Prefer drag-and-drop for crt/key with PEM paste collapsed; rename primary actions; add HTTP-01 prechecks. Upload posts to POST /system/tls/certificate with cert_source from status.
This commit is contained in:
1 parent
df3e119099
commit
a9bc68cd28
3 files changed
+69
-12
No files matched your search
@@ -972,7 +972,13 @@
|
||||
"http01Hint": "Let’s Encrypt HTTP-01. Port 80 must be reachable. Auto-renewal status appears in the list.",
|
||||
"certKeyRequired": "Certificate and private key are required",
|
||||
"certUploaded": "Certificate uploaded",
|
||||
"uploadApiPending": "Upload API is not ready yet (POST /system/tls/certificate). Use HTTP-01 for now."
|
||||
"uploadApiPending": "Upload API is not ready yet (POST /system/tls/certificate). Use HTTP-01 for now.",
|
||||
"dropHint": "Drop a file here, or click to choose",
|
||||
"pastePem": "Or paste PEM text",
|
||||
"importCert": "Import certificate",
|
||||
"startIssue": "Start issuance",
|
||||
"http01CheckDns": "DNS already points to this public entry",
|
||||
"http01CheckPort": "Issuance port is reachable (usually 80, or matches entry port)"
|
||||
},
|
||||
"feishuSync": {
|
||||
"title": "Feishu directory",
|
||||
|
||||
@@ -972,7 +972,13 @@
|
||||
"http01Hint": "使用 Let’s Encrypt HTTP-01 验证;需公网可访问 80 端口。过期前将尝试自动续期,失败时列表会显示状态。",
|
||||
"certKeyRequired": "请填写证书与私钥",
|
||||
"certUploaded": "证书已上传",
|
||||
"uploadApiPending": "上传证书接口尚未就绪,请先由后端落地 POST /system/tls/certificate,或改用 HTTP-01 申请。"
|
||||
"uploadApiPending": "上传证书接口尚未就绪,请先由后端落地 POST /system/tls/certificate,或改用 HTTP-01 申请。",
|
||||
"dropHint": "拖拽文件到此处,或点击选择",
|
||||
"pastePem": "或粘贴 PEM 文本",
|
||||
"importCert": "导入证书",
|
||||
"startIssue": "开始申请",
|
||||
"http01CheckDns": "DNS 已指向本机公网入口",
|
||||
"http01CheckPort": "申请用端口可达(通常为 80,或与入口配置一致)"
|
||||
},
|
||||
"feishuSync": {
|
||||
"title": "飞书组织与成员",
|
||||
|
||||
@@ -138,16 +138,42 @@
|
||||
<template v-if="certForm.method === 'upload'">
|
||||
<el-form label-position="top" style="margin-top:16px">
|
||||
<el-form-item :label="$t('tls.certPem')" required>
|
||||
<el-input v-model="certForm.certPem" type="textarea" :rows="6" :placeholder="$t('tls.certPemPlaceholder')" />
|
||||
<el-upload :show-file-list="false" accept=".crt,.pem,.cer" :before-upload="(f) => readPemFile(f, 'certPem')">
|
||||
<el-button class="upload-btn" style="width:100%;margin-top:8px">{{ $t('tls.uploadCertFile') }}</el-button>
|
||||
<el-upload
|
||||
drag
|
||||
class="pem-drop"
|
||||
:show-file-list="false"
|
||||
accept=".crt,.pem,.cer"
|
||||
:before-upload="(f) => readPemFile(f, 'certPem')"
|
||||
>
|
||||
<div class="pem-drop-inner">
|
||||
<div class="pem-drop-title">{{ certForm.certFileName || $t('tls.uploadCertFile') }}</div>
|
||||
<div class="pem-drop-hint">{{ $t('tls.dropHint') }}</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
<el-collapse style="margin-top:8px">
|
||||
<el-collapse-item :title="$t('tls.pastePem')" name="cert">
|
||||
<el-input v-model="certForm.certPem" type="textarea" :rows="5" :placeholder="$t('tls.certPemPlaceholder')" />
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('tls.keyPem')" required>
|
||||
<el-input v-model="certForm.keyPem" type="textarea" :rows="6" :placeholder="$t('tls.keyPemPlaceholder')" />
|
||||
<el-upload :show-file-list="false" accept=".key,.pem" :before-upload="(f) => readPemFile(f, 'keyPem')">
|
||||
<el-button class="upload-btn" style="width:100%;margin-top:8px">{{ $t('tls.uploadKeyFile') }}</el-button>
|
||||
<el-upload
|
||||
drag
|
||||
class="pem-drop"
|
||||
:show-file-list="false"
|
||||
accept=".key,.pem"
|
||||
:before-upload="(f) => readPemFile(f, 'keyPem')"
|
||||
>
|
||||
<div class="pem-drop-inner">
|
||||
<div class="pem-drop-title">{{ certForm.keyFileName || $t('tls.uploadKeyFile') }}</div>
|
||||
<div class="pem-drop-hint">{{ $t('tls.dropHint') }}</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
<el-collapse style="margin-top:8px">
|
||||
<el-collapse-item :title="$t('tls.pastePem')" name="key">
|
||||
<el-input v-model="certForm.keyPem" type="textarea" :rows="5" :placeholder="$t('tls.keyPemPlaceholder')" />
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
@@ -160,13 +186,19 @@
|
||||
<el-form-item :label="$t('tls.applyEmail')">
|
||||
<el-input v-model.trim="certForm.email" placeholder="admin@example.com" />
|
||||
</el-form-item>
|
||||
<el-alert type="info" :closable="false" :title="$t('tls.http01Hint')" />
|
||||
<el-alert type="info" :closable="false" :title="$t('tls.http01Hint')" style="margin-bottom:12px" />
|
||||
<ul class="http01-checklist">
|
||||
<li>{{ $t('tls.http01CheckDns') }}</li>
|
||||
<li>{{ $t('tls.http01CheckPort') }}</li>
|
||||
</ul>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="certDialog = false">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="certSubmitting" @click="submitCert">{{ $t('common.submit') }}</el-button>
|
||||
<el-button type="primary" :loading="certSubmitting" @click="submitCert">
|
||||
{{ certForm.method === 'upload' ? $t('tls.importCert') : $t('tls.startIssue') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<pre v-if="nginxActivateLog" ref="nginxActivateLogView" class="nginx-preview" role="log" aria-live="polite" style="margin-top:12px">{{ nginxActivateLog }}</pre>
|
||||
</el-dialog>
|
||||
@@ -207,6 +239,8 @@ const certForm = reactive({
|
||||
method: 'upload',
|
||||
certPem: '',
|
||||
keyPem: '',
|
||||
certFileName: '',
|
||||
keyFileName: '',
|
||||
host: '',
|
||||
email: '',
|
||||
})
|
||||
@@ -243,6 +277,8 @@ function resetCertForm() {
|
||||
certForm.method = 'upload'
|
||||
certForm.certPem = ''
|
||||
certForm.keyPem = ''
|
||||
certForm.certFileName = ''
|
||||
certForm.keyFileName = ''
|
||||
certForm.host = entry.host
|
||||
certForm.email = ''
|
||||
nginxActivateLog.value = ''
|
||||
@@ -255,7 +291,11 @@ function openAddCert() {
|
||||
|
||||
function readPemFile(file, field) {
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => { certForm[field] = String(reader.result || '') }
|
||||
reader.onload = () => {
|
||||
certForm[field] = String(reader.result || '')
|
||||
if (field === 'certPem') certForm.certFileName = file.name
|
||||
if (field === 'keyPem') certForm.keyFileName = file.name
|
||||
}
|
||||
reader.readAsText(file)
|
||||
return false
|
||||
}
|
||||
@@ -461,5 +501,10 @@ onMounted(() => { if (!auth.mustChange) refreshAll() })
|
||||
transition: border-color .15s, box-shadow .15s;
|
||||
}
|
||||
.method-card.active { border-color: var(--el-color-primary); box-shadow: 0 0 0 1px var(--el-color-primary) inset; }
|
||||
.upload-btn { color: var(--el-color-primary); border-style: dashed; }
|
||||
.pem-drop { width: 100%; }
|
||||
.pem-drop :deep(.el-upload), .pem-drop :deep(.el-upload-dragger) { width: 100%; }
|
||||
.pem-drop-inner { padding: 8px 0; }
|
||||
.pem-drop-title { color: var(--el-color-primary); font-weight: 600; }
|
||||
.pem-drop-hint { margin-top: 4px; font-size: 12px; color: var(--text-secondary); }
|
||||
.http01-checklist { margin: 0; padding-left: 1.2em; color: var(--text-secondary); font-size: 13px; line-height: 1.7; }
|
||||
</style>
|
||||
Reference in new issue
Block a user