Skip to content

Commit f29238f

Browse files
committed
feat(certificate): add new components for certificate management and editing, including upload, download, and actions
1 parent 85817f5 commit f29238f

File tree

11 files changed

+824
-241
lines changed

11 files changed

+824
-241
lines changed

app/components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ declare module 'vue' {
6565
ATag: typeof import('ant-design-vue/es')['Tag']
6666
ATextarea: typeof import('ant-design-vue/es')['Textarea']
6767
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
68+
AUpload: typeof import('ant-design-vue/es')['Upload']
6869
AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
6970
AutoCertFormAutoCertForm: typeof import('./src/components/AutoCertForm/AutoCertForm.vue')['default']
7071
AutoCertFormDNSChallenge: typeof import('./src/components/AutoCertForm/DNSChallenge.vue')['default']

app/src/components/Notification/detailRender.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export function detailRender(args: Pick<CustomRenderArgs, 'record' | 'text'>) {
66
try {
77
return (
88
<div>
9-
<div class="mb-2">
9+
<div>
1010
{
1111
notifications[args.record.title]?.content(args.record.details)
1212
|| args.record.content || args.record.details

app/src/views/certificate/CertificateEditor.vue

Lines changed: 82 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import type { Ref } from 'vue'
33
import type { Cert } from '@/api/cert'
44
import { message } from 'ant-design-vue'
55
import cert from '@/api/cert'
6-
import AutoCertForm from '@/components/AutoCertForm'
7-
import CertInfo from '@/components/CertInfo'
8-
import CodeEditor from '@/components/CodeEditor'
9-
import FooterToolBar from '@/components/FooterToolbar'
10-
import NodeSelector from '@/components/NodeSelector'
116
import { AutoCertState } from '@/constants'
12-
import RenewCert from './components/RenewCert.vue'
7+
8+
import AutoCertManagement from './components/AutoCertManagement.vue'
9+
import CertificateActions from './components/CertificateActions.vue'
10+
import CertificateBasicInfo from './components/CertificateBasicInfo.vue'
11+
import CertificateContentEditor from './components/CertificateContentEditor.vue'
12+
import CertificateDownload from './components/CertificateDownload.vue'
1313
import { useCertStore } from './store'
1414
1515
const route = useRoute()
@@ -27,6 +27,10 @@ const notShowInAutoCert = computed(() => {
2727
return data.value.auto_cert !== AutoCertState.Enable
2828
})
2929
30+
const isManaged = computed(() => {
31+
return data.value.auto_cert === AutoCertState.Enable
32+
})
33+
3034
function init() {
3135
if (id.value > 0) {
3236
cert.getItem(id.value).then(r => {
@@ -55,6 +59,10 @@ async function save() {
5559
}
5660
}
5761
62+
function handleBack() {
63+
router.push('/certificates/list')
64+
}
65+
5866
const log = computed(() => {
5967
if (!data.value.log)
6068
return ''
@@ -72,177 +80,50 @@ const log = computed(() => {
7280
}
7381
}).join('\n')
7482
})
75-
76-
const isManaged = computed(() => {
77-
return data.value.auto_cert === AutoCertState.Enable
78-
})
7983
</script>
8084

8185
<template>
8286
<ACard :title="id > 0 ? $gettext('Modify Certificate') : $gettext('Import Certificate')">
83-
<div
84-
v-if="isManaged"
85-
class="mb-4"
86-
>
87-
<div class="mb-2">
88-
<AAlert
89-
:message="$gettext('This certificate is managed by Nginx UI')"
90-
type="success"
91-
show-icon
92-
/>
93-
</div>
94-
<div
95-
v-if="!data.filename"
96-
class="mt-4 mb-4"
97-
>
98-
<AAlert
99-
:message="$gettext('This Auto Cert item is invalid, please remove it.')"
100-
type="error"
101-
show-icon
102-
/>
103-
</div>
104-
<div
105-
v-else-if="!data.domains"
106-
class="mt-4 mb-4"
107-
>
108-
<AAlert
109-
:message="$gettext('Domains list is empty, try to reopen Auto Cert for %{config}', { config: data.filename })"
110-
type="error"
111-
show-icon
112-
/>
113-
</div>
114-
</div>
115-
116-
<ARow>
87+
<ARow :gutter="[16, 16]">
11788
<ACol
11889
:sm="24"
119-
:md="12"
90+
:lg="12"
12091
>
121-
<AForm
122-
v-if="data.certificate_info"
123-
layout="vertical"
124-
>
125-
<AFormItem :label="$gettext('Certificate Status')">
126-
<CertInfo
127-
:cert="data.certificate_info"
128-
class="max-w-96"
129-
/>
130-
</AFormItem>
131-
</AForm>
92+
<!-- Auto Certificate Management -->
93+
<AutoCertManagement
94+
v-model:data="data"
95+
:is-managed="isManaged"
96+
@renewed="init"
97+
/>
13298

133-
<template v-if="isManaged">
134-
<RenewCert
135-
:options="{
136-
name: data.name,
137-
domains: data.domains,
138-
key_type: data.key_type,
139-
challenge_method: data.challenge_method,
140-
dns_credential_id: data.dns_credential_id,
141-
acme_user_id: data.acme_user_id,
142-
revoke_old: data.revoke_old,
143-
}"
144-
@renewed="init"
99+
<AForm layout="vertical">
100+
<!-- Certificate Basic Information -->
101+
<CertificateBasicInfo
102+
v-model:data="data"
103+
:errors="errors"
104+
:is-managed="isManaged"
145105
/>
146106

147-
<AutoCertForm
148-
v-model:options="data"
149-
key-type-read-only
150-
style="max-width: 600px"
151-
hide-note
152-
/>
153-
</template>
107+
<!-- Download Certificate Files -->
108+
<CertificateDownload :data="data" />
154109

155-
<AForm
156-
layout="vertical"
157-
style="max-width: 600px"
158-
>
159-
<AFormItem
160-
:label="$gettext('Name')"
161-
:validate-status="errors.name ? 'error' : ''"
162-
:help="errors.name === 'required'
163-
? $gettext('This field is required')
164-
: ''"
165-
>
166-
<p v-if="isManaged">
167-
{{ data.name }}
168-
</p>
169-
<AInput
170-
v-else
171-
v-model:value="data.name"
172-
/>
173-
</AFormItem>
174-
<AFormItem
175-
:label="$gettext('SSL Certificate Path')"
176-
:validate-status="errors.ssl_certificate_path ? 'error' : ''"
177-
:help="errors.ssl_certificate_path === 'required' ? $gettext('This field is required')
178-
: errors.ssl_certificate_path === 'certificate_path'
179-
? $gettext('The path exists, but the file is not a certificate') : ''"
180-
>
181-
<p v-if="isManaged">
182-
{{ data.ssl_certificate_path }}
183-
</p>
184-
<AInput
185-
v-else
186-
v-model:value="data.ssl_certificate_path"
187-
/>
188-
</AFormItem>
189-
<AFormItem
190-
:label="$gettext('SSL Certificate Key Path')"
191-
:validate-status="errors.ssl_certificate_key_path ? 'error' : ''"
192-
:help="errors.ssl_certificate_key_path === 'required' ? $gettext('This field is required')
193-
: errors.ssl_certificate_key_path === 'privatekey_path'
194-
? $gettext('The path exists, but the file is not a private key') : ''"
195-
>
196-
<p v-if="isManaged">
197-
{{ data.ssl_certificate_key_path }}
198-
</p>
199-
<AInput
200-
v-else
201-
v-model:value="data.ssl_certificate_key_path"
202-
/>
203-
</AFormItem>
204-
<AFormItem :label="$gettext('Sync to')">
205-
<NodeSelector
206-
v-model:target="data.sync_node_ids"
207-
hidden-local
208-
/>
209-
</AFormItem>
210-
<AFormItem
211-
:label="$gettext('SSL Certificate Content')"
212-
:validate-status="errors.ssl_certificate ? 'error' : ''"
213-
:help="errors.ssl_certificate === 'certificate'
214-
? $gettext('The input is not a SSL Certificate') : ''"
215-
>
216-
<CodeEditor
217-
v-model:content="data.ssl_certificate"
218-
default-height="300px"
219-
:readonly="!notShowInAutoCert"
220-
disable-code-completion
221-
:placeholder="$gettext('Leave blank will not change anything')"
222-
/>
223-
</AFormItem>
224-
<AFormItem
225-
:label="$gettext('SSL Certificate Key Content')"
226-
:validate-status="errors.ssl_certificate_key ? 'error' : ''"
227-
:help="errors.ssl_certificate_key === 'privatekey'
228-
? $gettext('The input is not a SSL Certificate Key') : ''"
229-
>
230-
<CodeEditor
231-
v-model:content="data.ssl_certificate_key"
232-
default-height="300px"
233-
:readonly="!notShowInAutoCert"
234-
disable-code-completion
235-
:placeholder="$gettext('Leave blank will not change anything')"
236-
/>
237-
</AFormItem>
110+
<!-- Certificate Content Editor -->
111+
<CertificateContentEditor
112+
v-model:data="data"
113+
:errors="errors"
114+
:readonly="!notShowInAutoCert"
115+
class="max-w-600px"
116+
/>
238117
</AForm>
239118
</ACol>
119+
120+
<!-- Log Column for Auto Cert -->
240121
<ACol
241122
v-if="data.auto_cert === AutoCertState.Enable"
242123
:sm="24"
243-
:md="12"
124+
:lg="12"
244125
>
245-
<ACard :title="$gettext('Log')">
126+
<ACard size="small" :title="$gettext('Log')">
246127
<pre
247128
v-dompurify-html="log"
248129
class="log-container"
@@ -251,20 +132,11 @@ const isManaged = computed(() => {
251132
</ACol>
252133
</ARow>
253134

254-
<FooterToolBar>
255-
<ASpace>
256-
<AButton @click="$router.push('/certificates/list')">
257-
{{ $gettext('Back') }}
258-
</AButton>
259-
260-
<AButton
261-
type="primary"
262-
@click="save"
263-
>
264-
{{ $gettext('Save') }}
265-
</AButton>
266-
</ASpace>
267-
</FooterToolBar>
135+
<!-- Certificate Actions -->
136+
<CertificateActions
137+
@save="save"
138+
@back="handleBack"
139+
/>
268140
</ACard>
269141
</template>
270142

@@ -277,4 +149,40 @@ const isManaged = computed(() => {
277149
font-size: 12px;
278150
line-height: 2;
279151
}
152+
153+
.code-editor-container {
154+
position: relative;
155+
156+
.drag-overlay {
157+
position: absolute;
158+
top: 0;
159+
left: 0;
160+
right: 0;
161+
bottom: 0;
162+
background-color: rgba(24, 144, 255, 0.1);
163+
border: 2px dashed #1890ff;
164+
border-radius: 6px;
165+
display: flex;
166+
align-items: center;
167+
justify-content: center;
168+
z-index: 10;
169+
170+
.drag-content {
171+
text-align: center;
172+
color: #1890ff;
173+
174+
.drag-icon {
175+
font-size: 48px;
176+
margin-bottom: 16px;
177+
display: block;
178+
}
179+
180+
p {
181+
font-size: 16px;
182+
margin: 0;
183+
font-weight: 500;
184+
}
185+
}
186+
}
187+
}
280188
</style>

0 commit comments

Comments
 (0)