feat: auto-generation of certificate paths and names #1442

This commit is contained in:
0xJacky
2025-11-24 16:07:08 +08:00
parent 6a826549b7
commit 8522ba7807
2 changed files with 98 additions and 8 deletions

View File

@@ -2,6 +2,7 @@
import type { Cert } from '@/api/cert'
import { CopyOutlined, InboxOutlined } from '@ant-design/icons-vue'
import { useClipboard } from '@vueuse/core'
import config from '@/api/config'
import CodeEditor from '@/components/CodeEditor'
import CertificateFileUpload from './CertificateFileUpload.vue'
@@ -20,6 +21,85 @@ const data = defineModel<Cert>('data', { required: true })
const { copy } = useClipboard()
// Lazy load nginx config base path
const nginxBasePath = ref<string>('')
const basePathLoaded = ref(false)
async function loadNginxBasePath() {
if (basePathLoaded.value)
return
try {
const res = await config.get_base_path()
nginxBasePath.value = res.base_path
basePathLoaded.value = true
}
catch (error) {
console.error('Failed to load nginx base path:', error)
message.warning($gettext('Failed to load nginx configuration path'))
}
}
// Generate slug from name or filename
function generateSlug(text: string): string {
let result = text
.toLowerCase()
.replace(/\s+/g, '_') // Replace spaces with underscores
.replace(/[^a-z0-9_./-]/g, '') // Remove invalid characters
// Remove leading dots
while (result.startsWith('.')) {
result = result.slice(1)
}
// Remove trailing dots
while (result.endsWith('.')) {
result = result.slice(0, -1)
}
return result
}
// Auto-generate certificate paths and name
async function autoGeneratePaths(fileName: string, _type: 'certificate' | 'key') {
// Only generate paths in add mode
if (data.value.id)
return
await loadNginxBasePath()
if (!nginxBasePath.value)
return
// Extract base name from filename (remove extension)
const baseName = fileName.replace(/\.(crt|pem|cer|cert|key|private)$/i, '')
// Auto-fill name if empty
if (!data.value.name) {
data.value.name = baseName
}
// Generate directory name from cert name or filename
const slug = data.value.name
? generateSlug(data.value.name)
: generateSlug(baseName)
if (!slug)
return
const certDir = `${nginxBasePath.value}/ssl/${slug}`
// Auto-fill certificate path if empty
if (!data.value.ssl_certificate_path) {
data.value.ssl_certificate_path = `${certDir}/fullchain.cer`
}
// Auto-fill key path if empty
if (!data.value.ssl_certificate_key_path) {
data.value.ssl_certificate_key_path = `${certDir}/private.key`
}
}
async function copyToClipboard(text: string, label: string) {
if (!text) {
message.warning($gettext('Nothing to copy'))
@@ -40,13 +120,23 @@ const isDragOverCert = ref(false)
const isDragOverKey = ref(false)
// Handle certificate file upload
function handleCertificateUpload(content: string) {
function handleCertificateUpload(content: string, fileName?: string) {
data.value.ssl_certificate = content
// Auto-generate paths if in add mode
if (fileName) {
autoGeneratePaths(fileName, 'certificate')
}
}
// Handle private key file upload
function handlePrivateKeyUpload(content: string) {
function handlePrivateKeyUpload(content: string, fileName?: string) {
data.value.ssl_certificate_key = content
// Auto-generate paths if in add mode
if (fileName) {
autoGeneratePaths(fileName, 'key')
}
}
// Drag and drop handlers
@@ -95,10 +185,10 @@ function handleDrop(e: DragEvent, type: 'certificate' | 'key') {
reader.onload = e => {
const content = e.target?.result as string
if (type === 'certificate') {
handleCertificateUpload(content)
handleCertificateUpload(content, file.name)
}
else {
handlePrivateKeyUpload(content)
handlePrivateKeyUpload(content, file.name)
}
}
reader.readAsText(file)
@@ -131,7 +221,7 @@ function handleDrop(e: DragEvent, type: 'certificate' | 'key') {
<CertificateFileUpload
v-if="!readonly"
type="certificate"
@upload="handleCertificateUpload"
@upload="(content, fileName) => handleCertificateUpload(content, fileName)"
/>
<div
@@ -192,7 +282,7 @@ function handleDrop(e: DragEvent, type: 'certificate' | 'key') {
<CertificateFileUpload
v-if="!readonly"
type="key"
@upload="handlePrivateKeyUpload"
@upload="(content, fileName) => handlePrivateKeyUpload(content, fileName)"
/>
<div

View File

@@ -11,7 +11,7 @@ const props = withDefaults(defineProps<Props>(), {
})
const emit = defineEmits<{
upload: [content: string]
upload: [content: string, fileName: string]
}>()
const { message } = App.useApp()
@@ -96,7 +96,7 @@ async function handleFileUpload(file: File) {
}
}
emit('upload', content)
emit('upload', content, file.name)
message.success($gettext('File uploaded successfully'))
}
catch (error) {