Skip to content

Commit 080ea59

Browse files
committed
enhance(certificate): add copy functionality and update auto cert states
1 parent 6c4849f commit 080ea59

File tree

7 files changed

+208
-35
lines changed

7 files changed

+208
-35
lines changed

app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,4 @@
8888
"vite-svg-loader": "^5.1.0",
8989
"vue-tsc": "^3.0.5"
9090
}
91-
}
91+
}

app/src/constants/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ export enum ConfigStatus {
77
}
88

99
export enum AutoCertState {
10-
Disable = 0,
10+
Disable = -1,
1111
Enable = 1,
12+
Sync = 2,
1213
}
1314

1415
export enum NotificationTypeT {

app/src/views/certificate/CertificateEditor.vue

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,8 @@ const id = computed(() => {
2323
2424
const { data } = storeToRefs(certStore)
2525
26-
const notShowInAutoCert = computed(() => {
27-
return data.value.auto_cert !== AutoCertState.Enable
28-
})
29-
3026
const isManaged = computed(() => {
31-
return data.value.auto_cert === AutoCertState.Enable
27+
return data.value.auto_cert === AutoCertState.Enable || data.value.auto_cert === AutoCertState.Sync
3228
})
3329
3430
function init() {
@@ -111,7 +107,7 @@ const log = computed(() => {
111107
<CertificateContentEditor
112108
v-model:data="data"
113109
:errors="errors"
114-
:readonly="!notShowInAutoCert"
110+
:readonly="isManaged"
115111
class="max-w-600px"
116112
/>
117113
</AForm>

app/src/views/certificate/components/ACMEUserSelector.vue

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,25 @@ const fieldNames = {
4545
function filterOption(input: string, option?: unknown) {
4646
return (option as AcmeUser)?.name?.toLowerCase().includes(input.toLowerCase()) ?? false
4747
}
48+
49+
const value = computed({
50+
set(value: number) {
51+
data.value.acme_user_id = value
52+
},
53+
get() {
54+
if (data.value.acme_user_id && data.value.acme_user_id > 0) {
55+
return data.value.acme_user_id
56+
}
57+
return undefined
58+
},
59+
})
4860
</script>
4961

5062
<template>
5163
<AForm layout="vertical">
5264
<AFormItem :label="$gettext('ACME User')">
5365
<ASelect
54-
v-model:value="data.acme_user_id"
66+
v-model:value="value"
5567
:placeholder="$gettext('System Initial User')"
5668
:loading="loading"
5769
show-search
Lines changed: 120 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<script setup lang="ts">
22
import type { Cert } from '@/api/cert'
3+
import { CopyOutlined } from '@ant-design/icons-vue'
4+
import { useClipboard } from '@vueuse/core'
5+
import { message } from 'ant-design-vue'
36
import NodeSelector from '@/components/NodeSelector'
47
58
interface Props {
@@ -12,6 +15,23 @@ defineProps<Props>()
1215
1316
// Use defineModel for two-way binding
1417
const data = defineModel<Cert>('data', { required: true })
18+
19+
const { copy } = useClipboard()
20+
21+
async function copyToClipboard(text: string, label: string) {
22+
if (!text) {
23+
message.warning($gettext('Nothing to copy'))
24+
return
25+
}
26+
try {
27+
await copy(text)
28+
message.success($gettext(`{label} copied to clipboard`).replace('{label}', label))
29+
}
30+
catch (error) {
31+
console.error(error)
32+
message.error($gettext('Failed to copy to clipboard'))
33+
}
34+
}
1535
</script>
1636

1737
<template>
@@ -26,13 +46,31 @@ const data = defineModel<Cert>('data', { required: true })
2646
? $gettext('This field is required')
2747
: ''"
2848
>
29-
<p v-if="isManaged">
30-
{{ data.name }}
31-
</p>
32-
<AInput
33-
v-else
34-
v-model:value="data.name"
35-
/>
49+
<div v-if="isManaged" class="copy-container">
50+
<p class="copy-text">
51+
{{ data.name }}
52+
</p>
53+
<AButton
54+
v-if="data.name"
55+
type="text"
56+
size="small"
57+
@click="copyToClipboard(data.name, $gettext('Name'))"
58+
>
59+
<CopyOutlined />
60+
</AButton>
61+
</div>
62+
<div v-else class="input-with-copy">
63+
<AInput v-model:value="data.name" />
64+
<AButton
65+
v-if="data.name"
66+
type="text"
67+
size="small"
68+
class="copy-button"
69+
@click="copyToClipboard(data.name, $gettext('Name'))"
70+
>
71+
<CopyOutlined />
72+
</AButton>
73+
</div>
3674
</AFormItem>
3775

3876
<AFormItem
@@ -42,13 +80,31 @@ const data = defineModel<Cert>('data', { required: true })
4280
: errors.ssl_certificate_path === 'certificate_path'
4381
? $gettext('The path exists, but the file is not a certificate') : ''"
4482
>
45-
<p v-if="isManaged">
46-
{{ data.ssl_certificate_path }}
47-
</p>
48-
<AInput
49-
v-else
50-
v-model:value="data.ssl_certificate_path"
51-
/>
83+
<div v-if="isManaged" class="copy-container">
84+
<p class="copy-text">
85+
{{ data.ssl_certificate_path }}
86+
</p>
87+
<AButton
88+
v-if="data.ssl_certificate_path"
89+
type="text"
90+
size="small"
91+
@click="copyToClipboard(data.ssl_certificate_path, $gettext('SSL Certificate Path'))"
92+
>
93+
<CopyOutlined />
94+
</AButton>
95+
</div>
96+
<div v-else class="input-with-copy">
97+
<AInput v-model:value="data.ssl_certificate_path" />
98+
<AButton
99+
v-if="data.ssl_certificate_path"
100+
type="text"
101+
size="small"
102+
class="copy-button"
103+
@click="copyToClipboard(data.ssl_certificate_path, $gettext('SSL Certificate Path'))"
104+
>
105+
<CopyOutlined />
106+
</AButton>
107+
</div>
52108
</AFormItem>
53109

54110
<AFormItem
@@ -58,13 +114,31 @@ const data = defineModel<Cert>('data', { required: true })
58114
: errors.ssl_certificate_key_path === 'privatekey_path'
59115
? $gettext('The path exists, but the file is not a private key') : ''"
60116
>
61-
<p v-if="isManaged">
62-
{{ data.ssl_certificate_key_path }}
63-
</p>
64-
<AInput
65-
v-else
66-
v-model:value="data.ssl_certificate_key_path"
67-
/>
117+
<div v-if="isManaged" class="copy-container">
118+
<p class="copy-text">
119+
{{ data.ssl_certificate_key_path }}
120+
</p>
121+
<AButton
122+
v-if="data.ssl_certificate_key_path"
123+
type="text"
124+
size="small"
125+
@click="copyToClipboard(data.ssl_certificate_key_path, $gettext('SSL Certificate Key Path'))"
126+
>
127+
<CopyOutlined />
128+
</AButton>
129+
</div>
130+
<div v-else class="input-with-copy">
131+
<AInput v-model:value="data.ssl_certificate_key_path" />
132+
<AButton
133+
v-if="data.ssl_certificate_key_path"
134+
type="text"
135+
size="small"
136+
class="copy-button"
137+
@click="copyToClipboard(data.ssl_certificate_key_path, $gettext('SSL Certificate Key Path'))"
138+
>
139+
<CopyOutlined />
140+
</AButton>
141+
</div>
68142
</AFormItem>
69143

70144
<AFormItem :label="$gettext('Sync to')">
@@ -77,4 +151,29 @@ const data = defineModel<Cert>('data', { required: true })
77151
</template>
78152

79153
<style scoped lang="less">
154+
.copy-container {
155+
display: flex;
156+
align-items: center;
157+
gap: 8px;
158+
159+
.copy-text {
160+
margin: 0;
161+
flex: 1;
162+
word-break: break-all;
163+
}
164+
}
165+
166+
.input-with-copy {
167+
display: flex;
168+
align-items: center;
169+
gap: 8px;
170+
171+
.ant-input {
172+
flex: 1;
173+
}
174+
175+
.copy-button {
176+
flex-shrink: 0;
177+
}
178+
}
80179
</style>

app/src/views/certificate/components/CertificateContentEditor.vue

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<script setup lang="ts">
22
import type { Cert } from '@/api/cert'
3-
import { InboxOutlined } from '@ant-design/icons-vue'
3+
import { CopyOutlined, InboxOutlined } from '@ant-design/icons-vue'
4+
import { useClipboard } from '@vueuse/core'
5+
import { message } from 'ant-design-vue'
46
import CodeEditor from '@/components/CodeEditor'
57
import CertificateFileUpload from './CertificateFileUpload.vue'
68
@@ -15,6 +17,23 @@ defineProps<Props>()
1517
// Use defineModel for two-way binding
1618
const data = defineModel<Cert>('data', { required: true })
1719
20+
const { copy } = useClipboard()
21+
22+
async function copyToClipboard(text: string, label: string) {
23+
if (!text) {
24+
message.warning($gettext('Nothing to copy'))
25+
return
26+
}
27+
try {
28+
await copy(text)
29+
message.success($gettext(`{label} copied to clipboard`).replace('{label}', label))
30+
}
31+
catch (error) {
32+
console.error(error)
33+
message.error($gettext('Failed to copy to clipboard'))
34+
}
35+
}
36+
1837
// Drag and drop state
1938
const isDragOverCert = ref(false)
2039
const isDragOverKey = ref(false)
@@ -90,11 +109,23 @@ function handleDrop(e: DragEvent, type: 'certificate' | 'key') {
90109
<div class="certificate-content-editor">
91110
<!-- SSL Certificate Content -->
92111
<AFormItem
93-
:label="$gettext('SSL Certificate Content')"
94112
:validate-status="errors.ssl_certificate ? 'error' : ''"
95113
:help="errors.ssl_certificate === 'certificate'
96114
? $gettext('The input is not a SSL Certificate') : ''"
97115
>
116+
<template #label>
117+
<div class="label-with-copy">
118+
<span class="label-text">{{ $gettext('SSL Certificate Content') }}</span>
119+
<AButton
120+
v-if="data.ssl_certificate"
121+
type="text"
122+
size="small"
123+
@click="copyToClipboard(data.ssl_certificate, $gettext('SSL Certificate Content'))"
124+
>
125+
<CopyOutlined />
126+
</AButton>
127+
</div>
128+
</template>
98129
<!-- Certificate File Upload -->
99130
<CertificateFileUpload
100131
v-if="!readonly"
@@ -139,11 +170,23 @@ function handleDrop(e: DragEvent, type: 'certificate' | 'key') {
139170

140171
<!-- SSL Certificate Key Content -->
141172
<AFormItem
142-
:label="$gettext('SSL Certificate Key Content')"
143173
:validate-status="errors.ssl_certificate_key ? 'error' : ''"
144174
:help="errors.ssl_certificate_key === 'privatekey'
145175
? $gettext('The input is not a SSL Certificate Key') : ''"
146176
>
177+
<template #label>
178+
<div class="label-with-copy">
179+
<span class="label-text">{{ $gettext('SSL Certificate Key Content') }}</span>
180+
<AButton
181+
v-if="data.ssl_certificate_key"
182+
type="text"
183+
size="small"
184+
@click="copyToClipboard(data.ssl_certificate_key, $gettext('SSL Certificate Key Content'))"
185+
>
186+
<CopyOutlined />
187+
</AButton>
188+
</div>
189+
</template>
147190
<!-- Private Key File Upload -->
148191
<CertificateFileUpload
149192
v-if="!readonly"
@@ -190,6 +233,18 @@ function handleDrop(e: DragEvent, type: 'certificate' | 'key') {
190233

191234
<style scoped lang="less">
192235
.certificate-content-editor {
236+
.label-with-copy {
237+
display: flex;
238+
align-items: center;
239+
justify-content: space-between;
240+
width: 100%;
241+
242+
.label-text {
243+
font-weight: 500;
244+
color: rgba(0, 0, 0, 0.85);
245+
}
246+
}
247+
193248
.code-editor-container {
194249
position: relative;
195250

0 commit comments

Comments
 (0)