Support custom certificates for domains again

This commit is contained in:
Johannes Zellner
2025-10-10 16:10:16 +02:00
parent 057f75ca5f
commit f1aee1d9a4
2 changed files with 70 additions and 17 deletions
+59 -17
View File
@@ -1,7 +1,8 @@
<script setup>
import { ref, useTemplateRef } from 'vue';
import { Dialog, TextInput, FormGroup, Checkbox } from '@cloudron/pankow';
import { Dialog, TextInput, InputGroup, FormGroup, Checkbox, Button } from '@cloudron/pankow';
import { getTextFromFile } from '../utils.js';
import DomainsModel from '../models/DomainsModel.js';
import DomainProviderForm from './DomainProviderForm.vue';
@@ -10,6 +11,8 @@ const emit = defineEmits([ 'success' ]);
const domainsModel = DomainsModel.create();
const dialog = useTemplateRef('dialog');
const keyFileInput = useTemplateRef('keyFileInput');
const certificateFileInput = useTemplateRef('certificateFileInput');
const busy = ref(false);
const errorMessage = ref('');
@@ -20,6 +23,8 @@ const provider = ref('');
const tlsProvider = ref('letsencrypt-prod-wildcard');
const showAdvanced = ref(false);
const customNameservers = ref(false);
const certificateFileName = ref('');
const keyFileName = ref('');
const dnsConfig = ref(DomainsModel.createEmptyConfig());
@@ -49,8 +54,27 @@ async function onSubmit() {
tlsConfig.wildcard = true;
}
let fallbackCertificate = null;
if (tlsConfig.provider === 'fallback') {
const certFile = certificateFileInput.value.files[0] || null;
const keyFile = keyFileInput.value.files[0] || null;
if ((!certFile && keyFile) || (certFile && !keyFile)) {
errorMessage.value = 'Both certificate and key file need to be provided';
busy.value = false;
return;
} else if (certFile && keyFile) {
fallbackCertificate = {
cert: await getTextFromFile(certFile),
key: await getTextFromFile(keyFile),
};
} else {
// unchanged
}
}
const func = editing.value ? domainsModel.update : domainsModel.add;
const [error] = await func(domain.value, zoneName.value, provider.value, config, null, tlsConfig);
const [error] = await func(domain.value, zoneName.value, provider.value, config, fallbackCertificate, tlsConfig);
if (error) {
errorMessage.value = error.body ? error.body.message : 'Internal error';
busy.value = false;
@@ -61,6 +85,20 @@ async function onSubmit() {
dialog.value.close();
}
function onCertificateFileChange() {
const file = certificateFileInput.value.files[0] || '';
if (!file) return;
certificateFileName.value = file ? file.name : '';
}
function onKeyFileChange() {
const file = keyFileInput.value.files[0] || '';
if (!file) return;
keyFileName.value = file ? file.name : '';
}
defineExpose({
open(d) {
d = d ? JSON.parse(JSON.stringify(d)) : { config: {}, tlsConfig: { provider: 'letsencrypt-prod', wildcard: true } }; // make a copy
@@ -79,8 +117,9 @@ defineExpose({
editing.value = !!d.domain;
domain.value = d.domain || '';
zoneName.value = d.zoneName || '';
customNameservers.value = d.config.customNameservers;
certificateFileName.value = d.tlsConfig?.provider === 'fallback' ? String.fromCharCode(0x25CF).repeat(8) : '';
keyFileName.value = d.tlsConfig?.provider === 'fallback' ? String.fromCharCode(0x25CF).repeat(8) : '';
dialog.value.open();
@@ -119,24 +158,27 @@ defineExpose({
<!-- custom certificate -->
<div v-if="tlsProvider === 'fallback'">
<label >{{ $t('domains.domainDialog.fallbackCertCustomCert') }}</label>
<label>{{ $t('domains.domainDialog.fallbackCertCustomCert') }}</label>
<p v-html="$t('domains.domainDialog.fallbackCertCustomCertInfo', { customCertLink: 'https://docs.cloudron.io/certificates/#custom-certificates' })"></p>
</div>
<FormGroup v-if="tlsProvider === 'fallback'">
<div class="input-group">
<input type="file" id="fallbackCertFileInput" style="display:none"/>
<input type="text" class="form-control" :placeholder="$t('domains.domainDialog.fallbackCertCertificatePlaceholder')" ng-model="domainConfigure.fallbackCert.certificateFileName" name="cert" onclick="getElementById('fallbackCertFileInput').click();" style="cursor: pointer;" ng-disabled="domainConfigure.busy">
<span class="input-group-addon"><i class="fa fa-upload" onclick="getElementById('fallbackCertFileInput').click();"></i></span>
<div v-if="tlsProvider === 'fallback'">
<input type="file" ref="certificateFileInput" style="display: none" @change="onCertificateFileChange"/>
<input type="file" ref="keyFileInput" style="display: none" @change="onKeyFileChange"/>
<div style="display: grid; grid-template-columns: auto 1fr; gap: 5px 10px">
<label>{{ $t('domains.domainDialog.fallbackCertCertificatePlaceholder') }}</label>
<InputGroup>
<TextInput v-model="certificateFileName" @click="certificateFileInput.click()" style="cursor: pointer; flex-grow: 1;" :disabled="busy" />
<Button tool secondary icon="fa-solid fa-upload" @click="certificateFileInput.click()"></Button>
</InputGroup>
<label>{{ $t('domains.domainDialog.fallbackCertKeyPlaceholder') }}</label>
<InputGroup>
<TextInput v-model="keyFileName" @click="keyFileInput.click()" style="cursor: pointer; flex-grow: 1;" :disabled="busy" />
<Button tool secondary icon="fa-solid fa-upload" @click="keyFileInput.click()"></Button>
</InputGroup>
</div>
</FormGroup>
<FormGroup v-if="tlsProvider === 'fallback'">
<div class="input-group">
<input type="file" id="fallbackKeyFileInput" style="display:none"/>
<input type="text" class="form-control" :placeholder="$t('domains.domainDialog.fallbackCertKeyPlaceholder')" ng-model="domainConfigure.fallbackCert.keyFileName" id="fallbackKeyInput" name="key" onclick="getElementById('fallbackKeyFileInput').click();" style="cursor: pointer;" ng-disabled="domainConfigure.busy">
<span class="input-group-addon"><i class="fa fa-upload" onclick="getElementById('fallbackKeyFileInput').click();"></i></span>
</div>
</FormGroup>
</div>
</div>
<div v-if="!showAdvanced" style="margin-top: 15px" class="actionable" @click="showAdvanced = true">{{ $t('domains.domainDialog.advancedAction') }}</div>