Files
Nexus/frontend/src/pages/btpanel/BtPanelSslPage.vue
T
r 82426b1941 feat(btpanel): 独立宝塔模块与 SSH 自动获取 API
新增侧栏「宝塔面板」菜单、代理 API 与连接配置页;通过 SSH 读写子机
api.json 自动写入凭据,5 分钟后台重试,含审计修复、检测缓存与并发锁。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-21 09:13:14 +08:00

99 lines
3.3 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>
<BtPanelPageShell title="宝塔 · SSL 证书" subtitle="站点证书查看 / 上传 / Let's EncryptC15">
<template #default>
<v-data-table :headers="headers" :items="rows" :loading="loading" density="compact">
<template #item.ssl="{ item }">
<span>{{ formatSsl(item) }}</span>
</template>
<template #item.actions="{ item }">
<v-btn size="x-small" variant="text" @click="applyLe(item)">申请 LE</v-btn>
</template>
</v-data-table>
<v-expansion-panels class="mt-4" variant="accordion">
<v-expansion-panel title="上传证书(SetSSL">
<v-expansion-panel-text>
<v-text-field v-model="upload.siteName" label="网站名" density="compact" class="mb-2" />
<v-textarea v-model="upload.csr" label="证书 PEM" rows="4" class="mb-2" />
<v-textarea v-model="upload.key" label="私钥 PEM" rows="4" class="mb-2" />
<v-btn color="primary" :loading="loading" @click="uploadSsl">上传</v-btn>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
</template>
</BtPanelPageShell>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import BtPanelPageShell from '@/components/btpanel/BtPanelPageShell.vue'
import { btApplySsl, btSetSsl, btSslSites } from '@/api/btpanel'
import { useBtPanelPageLoad } from '@/composables/btpanel/useBtPanelPageLoad'
defineOptions({ name: 'BtPanelSslPage' })
interface SiteRow {
id: number
name: string
ssl?: { subject?: string; notAfter?: string; endtime?: string } | number
}
const loading = ref(false)
const rows = ref<SiteRow[]>([])
const upload = reactive({ siteName: '', key: '', csr: '' })
const headers = [
{ title: 'ID', key: 'id', width: 70 },
{ title: '网站', key: 'name' },
{ title: 'SSL', key: 'ssl' },
{ title: '操作', key: 'actions', sortable: false, width: 100 },
]
const { serverId, run } = useBtPanelPageLoad(async (id) => {
loading.value = true
try {
const res = await btSslSites(id)
rows.value = (res.data ?? []) as SiteRow[]
} catch (e: unknown) {
window.$snackbar?.(e instanceof Error ? e.message : '加载失败', 'error')
} finally {
loading.value = false
}
})
function formatSsl(item: SiteRow) {
if (!item.ssl || item.ssl === -1) return '无'
if (typeof item.ssl === 'object') return item.ssl.subject || item.ssl.notAfter || '已配置'
return String(item.ssl)
}
async function applyLe(item: SiteRow) {
const sid = serverId.value
if (!sid) return
loading.value = true
try {
await btApplySsl(sid, { domains: item.name, id: item.id, auth_type: 'http' })
window.$snackbar?.('已提交申请', 'success')
await run()
} catch (e: unknown) {
window.$snackbar?.(e instanceof Error ? e.message : '申请失败', 'error')
} finally {
loading.value = false
}
}
async function uploadSsl() {
const sid = serverId.value
if (!sid || !upload.siteName) return
loading.value = true
try {
await btSetSsl(sid, { siteName: upload.siteName, key: upload.key, csr: upload.csr })
window.$snackbar?.('证书已上传', 'success')
await run()
} catch (e: unknown) {
window.$snackbar?.(e instanceof Error ? e.message : '上传失败', 'error')
} finally {
loading.value = false
}
}
</script>