2025-01-28 13:55:58 +01:00
|
|
|
<script setup>
|
|
|
|
|
|
2025-01-29 16:29:21 +01:00
|
|
|
const API_ORIGIN = import.meta.env.VITE_API_ORIGIN ? import.meta.env.VITE_API_ORIGIN : window.location.origin;
|
|
|
|
|
|
2025-01-28 13:55:58 +01:00
|
|
|
import { ref, useTemplateRef } from 'vue';
|
2025-01-29 12:15:53 +01:00
|
|
|
import { Dialog, TextInput, FormGroup, Checkbox } from 'pankow';
|
2025-01-28 21:25:12 +01:00
|
|
|
import { ENDPOINTS_OVH } from '../constants.js';
|
2025-01-29 16:29:21 +01:00
|
|
|
import DomainsModel from '../models/DomainsModel.js';
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits([ 'success' ]);
|
|
|
|
|
|
|
|
|
|
const domainsModel = DomainsModel.create(API_ORIGIN, localStorage.token);
|
2025-01-28 21:25:12 +01:00
|
|
|
|
|
|
|
|
// currently, validation of wildcard with various provider is done server side
|
|
|
|
|
const tlsProviders = [
|
|
|
|
|
{ name: 'Let\'s Encrypt Prod', value: 'letsencrypt-prod' },
|
|
|
|
|
{ name: 'Let\'s Encrypt Prod - Wildcard', value: 'letsencrypt-prod-wildcard' },
|
|
|
|
|
{ name: 'Let\'s Encrypt Staging', value: 'letsencrypt-staging' },
|
|
|
|
|
{ name: 'Let\'s Encrypt Staging - Wildcard', value: 'letsencrypt-staging-wildcard' },
|
|
|
|
|
{ name: 'Custom Wildcard Certificate', value: 'fallback' },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// keep in sync with setup.js
|
|
|
|
|
const domainProviders = [
|
2025-01-29 12:15:53 +01:00
|
|
|
{ name: 'AWS Route53', value: 'route53' },
|
|
|
|
|
{ name: 'Bunny', value: 'bunny' },
|
|
|
|
|
{ name: 'Cloudflare', value: 'cloudflare' },
|
|
|
|
|
{ name: 'deSEC', value: 'desec' },
|
|
|
|
|
{ name: 'DigitalOcean', value: 'digitalocean' },
|
|
|
|
|
{ name: 'DNSimple', value: 'dnsimple' },
|
|
|
|
|
{ name: 'Gandi LiveDNS', value: 'gandi' },
|
|
|
|
|
{ name: 'GoDaddy', value: 'godaddy' },
|
|
|
|
|
{ name: 'Google Cloud DNS', value: 'gcdns' },
|
|
|
|
|
{ name: 'Hetzner', value: 'hetzner' },
|
|
|
|
|
{ name: 'INWX', value: 'inwx' },
|
|
|
|
|
{ name: 'Linode', value: 'linode' },
|
|
|
|
|
{ name: 'Name.com', value: 'namecom' },
|
|
|
|
|
{ name: 'Namecheap', value: 'namecheap' },
|
|
|
|
|
{ name: 'Netcup', value: 'netcup' },
|
|
|
|
|
{ name: 'OVH', value: 'ovh' },
|
|
|
|
|
{ name: 'Porkbun', value: 'porkbun' },
|
|
|
|
|
{ name: 'Vultr', value: 'vultr' },
|
|
|
|
|
{ name: 'Wildcard', value: 'wildcard' },
|
|
|
|
|
{ name: 'Manual (not recommended)', value: 'manual' },
|
|
|
|
|
{ name: 'No-op (only for development)', value: 'noop' }
|
2025-01-28 21:25:12 +01:00
|
|
|
];
|
2025-01-28 13:55:58 +01:00
|
|
|
|
|
|
|
|
const dialog = useTemplateRef('dialog');
|
|
|
|
|
|
2025-01-28 21:25:12 +01:00
|
|
|
const busy = ref(false);
|
2025-01-29 16:29:21 +01:00
|
|
|
const errorMessage = ref('');
|
2025-01-29 12:15:53 +01:00
|
|
|
const editing = ref(false);
|
2025-01-28 13:55:58 +01:00
|
|
|
const domain = ref('');
|
2025-01-28 21:25:12 +01:00
|
|
|
const zoneName = ref('');
|
2025-01-28 13:55:58 +01:00
|
|
|
const provider = ref('');
|
2025-01-29 12:15:53 +01:00
|
|
|
const tlsProvider = ref('letsencrypt-prod-wildcard');
|
|
|
|
|
const showAdvanced = ref(false);
|
2025-01-28 21:25:12 +01:00
|
|
|
|
|
|
|
|
const accessKeyId = ref('');
|
|
|
|
|
const secretAccessKey = ref('');
|
|
|
|
|
const digitalOceanToken = ref('');
|
2025-01-29 16:29:21 +01:00
|
|
|
const gandiTokenType = ref('ApiKey');
|
2025-01-28 21:25:12 +01:00
|
|
|
const gandiApiKey = ref('');
|
|
|
|
|
const godaddyApiKey = ref('');
|
|
|
|
|
const godaddyApiSecret = ref('');
|
2025-01-29 17:59:46 +01:00
|
|
|
// user just uploads a ServiceAccount.json which we then open and assign
|
|
|
|
|
const gcdnsProjectId = ref('');
|
|
|
|
|
const gcdnsClientEmail = ref('');
|
|
|
|
|
const gcdnsPrivateKey = ref('');
|
2025-01-28 21:25:12 +01:00
|
|
|
const netcupCustomerNumber = ref('');
|
|
|
|
|
const netcupApiKey = ref('');
|
|
|
|
|
const netcupApiPassword = ref('');
|
2025-01-29 17:59:46 +01:00
|
|
|
const ovhEndpoint = ref(ENDPOINTS_OVH[0].value);
|
2025-01-28 21:25:12 +01:00
|
|
|
const ovhConsumerKey = ref('');
|
|
|
|
|
const ovhAppKey = ref('');
|
|
|
|
|
const ovhAppSecret = ref('');
|
|
|
|
|
const porkbunSecretapikey = ref('');
|
|
|
|
|
const porkbunApikey = ref('');
|
2025-01-29 12:15:53 +01:00
|
|
|
const cloudflareTokenType = ref('ApiToken');
|
2025-01-28 21:25:12 +01:00
|
|
|
const cloudflareToken = ref('');
|
|
|
|
|
const cloudflareEmail = ref('');
|
|
|
|
|
const cloudflareDefaultProxyStatus = ref(false);
|
|
|
|
|
const linodeToken = ref('');
|
|
|
|
|
const bunnyAccessKey = ref('');
|
|
|
|
|
const dnsimpleAccessToken = ref('');
|
|
|
|
|
const hetznerToken = ref('');
|
|
|
|
|
const vultrToken = ref('');
|
|
|
|
|
const deSecToken = ref('');
|
|
|
|
|
const nameComUsername = ref('');
|
|
|
|
|
const nameComToken = ref('');
|
|
|
|
|
const namecheapUsername = ref('');
|
|
|
|
|
const namecheapApiKey = ref('');
|
|
|
|
|
const inwxUsername = ref('');
|
|
|
|
|
const inwxPassword = ref('');
|
2025-01-28 13:55:58 +01:00
|
|
|
|
2025-01-29 12:15:53 +01:00
|
|
|
function needsPort80(dnsProvider, tlsProvider) {
|
|
|
|
|
return ((dnsProvider === 'manual' || dnsProvider === 'noop' || dnsProvider === 'wildcard') &&
|
|
|
|
|
(tlsProvider === 'letsencrypt-prod' || tlsProvider === 'letsencrypt-staging'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setDefaultTlsProvider() {
|
|
|
|
|
const dnsProvider = provider.value;
|
|
|
|
|
// wildcard LE won't work without automated DNS
|
|
|
|
|
if (dnsProvider === 'manual' || dnsProvider === 'noop' || dnsProvider === 'wildcard') {
|
|
|
|
|
tlsProvider.value = 'letsencrypt-prod';
|
|
|
|
|
} else {
|
|
|
|
|
tlsProvider.value = 'letsencrypt-prod-wildcard';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isFormValid = ref(false);
|
|
|
|
|
function checkValidity() {
|
|
|
|
|
isFormValid.value = form.value.checkValidity();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const form = useTemplateRef('form');
|
2025-01-29 16:29:21 +01:00
|
|
|
async function onSubmit() {
|
2025-01-29 12:15:53 +01:00
|
|
|
if (!form.value.reportValidity()) return;
|
|
|
|
|
|
2025-01-29 16:29:21 +01:00
|
|
|
busy.value = true;
|
|
|
|
|
errorMessage.value = '';
|
|
|
|
|
|
|
|
|
|
const config = {};
|
|
|
|
|
if (provider.value === 'route53') {
|
|
|
|
|
config.accessKeyId = accessKeyId.value;
|
|
|
|
|
config.secretAccessKey = secretAccessKey.value;
|
|
|
|
|
} else if (provider.value === 'gcdns') {
|
2025-01-29 17:59:46 +01:00
|
|
|
config.projectId = gcdnsProjectId.value;
|
|
|
|
|
config.credentials = {
|
|
|
|
|
client_email: gcdnsClientEmail.value,
|
|
|
|
|
private_key: gcdnsPrivateKey.value
|
|
|
|
|
};
|
2025-01-29 16:29:21 +01:00
|
|
|
} else if (provider.value === 'digitalocean') {
|
|
|
|
|
config.token = digitalOceanToken.value;
|
|
|
|
|
} else if (provider.value === 'linode') {
|
|
|
|
|
config.token = linodeToken.value;
|
|
|
|
|
} else if (provider.value === 'bunny') {
|
|
|
|
|
config.accessKey = bunnyAccessKey.value;
|
|
|
|
|
} else if (provider.value === 'dnsimple') {
|
|
|
|
|
config.accessToken = dnsimpleAccessToken.value;
|
|
|
|
|
} else if (provider.value === 'hetzner') {
|
|
|
|
|
config.token = hetznerToken.value;
|
|
|
|
|
} else if (provider.value === 'vultr') {
|
|
|
|
|
config.token = vultrToken.value;
|
|
|
|
|
} else if (provider.value === 'desec') {
|
|
|
|
|
config.token = deSecToken.value;
|
|
|
|
|
} else if (provider.value === 'gandi') {
|
|
|
|
|
config.token = gandiApiKey.value;
|
|
|
|
|
config.tokenType = gandiTokenType.value;
|
|
|
|
|
} else if (provider.value === 'godaddy') {
|
|
|
|
|
config.apiKey = godaddyApiKey.value;
|
|
|
|
|
config.apiSecret = godaddyApiSecret.value;
|
|
|
|
|
} else if (provider.value === 'cloudflare') {
|
|
|
|
|
config.token = cloudflareToken.value;
|
|
|
|
|
config.email = cloudflareEmail.value;
|
|
|
|
|
config.tokenType = cloudflareTokenType.value;
|
|
|
|
|
config.defaultProxyStatus = cloudflareDefaultProxyStatus.value;
|
|
|
|
|
} else if (provider.value === 'namecom') {
|
|
|
|
|
config.token = nameComToken.value;
|
|
|
|
|
config.username = nameComUsername.value;
|
|
|
|
|
} else if (provider.value === 'namecheap') {
|
|
|
|
|
config.token = namecheapApiKey.value;
|
|
|
|
|
config.username = namecheapUsername.value;
|
|
|
|
|
} else if (provider.value === 'inwx') {
|
|
|
|
|
config.username = inwxUsername.value;
|
|
|
|
|
config.password = inwxPassword.value;
|
|
|
|
|
} else if (provider.value === 'netcup') {
|
|
|
|
|
config.customerNumber = netcupCustomerNumber.value;
|
|
|
|
|
config.apiKey = netcupApiKey.value;
|
|
|
|
|
config.apiPassword = netcupApiPassword.value;
|
|
|
|
|
} else if (provider.value === 'ovh') {
|
|
|
|
|
config.endpoint = ovhEndpoint.value;
|
|
|
|
|
config.consumerKey = ovhConsumerKey.value;
|
|
|
|
|
config.appKey = ovhAppKey.value;
|
|
|
|
|
config.appSecret = ovhAppSecret.value;
|
|
|
|
|
} else if (provider.value === 'porkbun') {
|
|
|
|
|
config.apikey = porkbunApikey.value;
|
|
|
|
|
config.secretapikey = porkbunSecretapikey.value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const tlsConfig = {
|
|
|
|
|
provider: tlsProvider.value,
|
|
|
|
|
wildcard: false
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// UI uses -wildcard providers, API uses wildcard flag
|
|
|
|
|
if (tlsConfig.provider.indexOf('-wildcard') !== -1) {
|
|
|
|
|
tlsConfig.provider = tlsConfig.provider.replace('-wildcard', '');
|
|
|
|
|
tlsConfig.wildcard = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const func = editing.value ? domainsModel.update : domainsModel.add;
|
|
|
|
|
const [error] = await func(domain.value, zoneName.value, provider.value, config, null, tlsConfig);
|
|
|
|
|
if (error) {
|
|
|
|
|
errorMessage.value = error.body ? error.body.message : 'Internal error';
|
|
|
|
|
busy.value = false;
|
|
|
|
|
return console.error(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit('success');
|
|
|
|
|
dialog.value.close();
|
2025-01-29 12:15:53 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-29 17:59:46 +01:00
|
|
|
function onGcdnsFileInputChange(event) {
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
reader.onload = function (result) {
|
|
|
|
|
if (!result.target || !result.target.result) return console.error('Unable to read local file');
|
|
|
|
|
const serviceAccount = JSON.parse(result.target.result);
|
|
|
|
|
gcdnsProjectId.value = serviceAccount.project_id;
|
|
|
|
|
gcdnsClientEmail.value = serviceAccount.client_email;
|
|
|
|
|
gcdnsPrivateKey.value = serviceAccount.private_key;
|
|
|
|
|
};
|
|
|
|
|
reader.readAsText(event.target.files[0]);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-28 13:55:58 +01:00
|
|
|
defineExpose({
|
|
|
|
|
open(d) {
|
2025-01-29 16:29:21 +01:00
|
|
|
d = d || { config: {}, tlsConfig: {}};
|
2025-01-28 13:55:58 +01:00
|
|
|
|
2025-01-28 21:25:12 +01:00
|
|
|
busy.value = false;
|
2025-01-29 16:29:21 +01:00
|
|
|
showAdvanced.value = false;
|
|
|
|
|
errorMessage.value = '';
|
2025-01-29 12:15:53 +01:00
|
|
|
editing.value = !!d.domain;
|
2025-01-28 21:25:12 +01:00
|
|
|
domain.value = d.domain || '';
|
|
|
|
|
zoneName.value = d.zoneName || '';
|
|
|
|
|
provider.value = d.provider || '';
|
2025-01-29 16:29:21 +01:00
|
|
|
tlsProvider.value = d.tlsConfig.provider || 'letsencrypt-prod-wildcard';
|
|
|
|
|
|
2025-01-29 17:59:46 +01:00
|
|
|
gcdnsProjectId.value = (d.provider === 'gcdns' && d.config.projectId) || '';
|
|
|
|
|
gcdnsClientEmail.value = (d.provider === 'gcdns' && d.config?.credentials.client_email) || '';
|
|
|
|
|
gcdnsPrivateKey.value = (d.provider === 'gcdns' && d.config?.credentials.private_key) || '';
|
|
|
|
|
|
2025-01-29 16:29:21 +01:00
|
|
|
accessKeyId.value = (d.provider === 'route53' && d.config.accessKeyId) || '';
|
|
|
|
|
secretAccessKey.value = (d.provider === 'route53' && d.config.secretAccessKey) || '';
|
|
|
|
|
digitalOceanToken.value = (d.provider === 'digitalocean' && d.config.token) || '';
|
|
|
|
|
gandiTokenType.value = (d.provider === 'gandi' && d.config.tokenType) || 'ApiKey';
|
|
|
|
|
gandiApiKey.value = (d.provider === 'gandi' && d.config.token) || '';
|
|
|
|
|
godaddyApiKey.value = (d.provider === 'godaddy' && d.config.apiKey) || '';
|
|
|
|
|
godaddyApiSecret.value = (d.provider === 'godaddy' && d.config.apiSecret) || '';
|
|
|
|
|
netcupCustomerNumber.value = (d.provider === 'netcup' && d.config.customerNumber) || '';
|
|
|
|
|
netcupApiKey.value = (d.provider === 'netcup' && d.config.apiKey) || '';
|
|
|
|
|
netcupApiPassword.value = (d.provider === 'netcup' && d.config.apiPassword) || '';
|
2025-01-29 17:59:46 +01:00
|
|
|
ovhEndpoint.value = (d.provider === 'ovh' && d.config.endpoint) || ENDPOINTS_OVH[0].value;
|
2025-01-29 16:29:21 +01:00
|
|
|
ovhConsumerKey.value = (d.provider === 'ovh' && d.config.consumerKey) || '';
|
|
|
|
|
ovhAppKey.value = (d.provider === 'ovh' && d.config.appKey) || '';
|
|
|
|
|
ovhAppSecret.value = (d.provider === 'ovh' && d.config.appSecret) || '';
|
|
|
|
|
porkbunSecretapikey.value = (d.provider === 'porkbun' && d.config.secretapikey) || '';
|
|
|
|
|
porkbunApikey.value = (d.provider === 'porkbun' && d.config.apikey) || '';
|
|
|
|
|
cloudflareTokenType.value = (d.provider === 'cloudflare' && d.config.tokenType) || 'ApiToken';
|
|
|
|
|
cloudflareToken.value = (d.provider === 'cloudflare' && d.config.token) || '';
|
|
|
|
|
cloudflareEmail.value = (d.provider === 'cloudflare' && d.config.email) || '';
|
|
|
|
|
cloudflareDefaultProxyStatus.value = d.provider === 'cloudflare' ? d.config.defaultProxyStatus : false;
|
|
|
|
|
linodeToken.value = (d.provider === 'linode' && d.config.token) || '';
|
|
|
|
|
bunnyAccessKey.value = (d.provider === 'bunny' && d.config.accessKey) || '';
|
|
|
|
|
dnsimpleAccessToken.value = (d.provider === 'dnsimple' && d.config.accessToken) || '';
|
|
|
|
|
hetznerToken.value = (d.provider === 'hetzner' && d.config.token) || '';
|
|
|
|
|
vultrToken.value = (d.provider === 'vultr' && d.config.token) || '';
|
|
|
|
|
deSecToken.value = (d.provider === 'desec' && d.config.token) || '';
|
|
|
|
|
nameComUsername.value = (d.provider === 'namecom' && d.config.username) || '';
|
|
|
|
|
nameComToken.value = (d.provider === 'namecom' && d.config.token) || '';
|
|
|
|
|
namecheapUsername.value = (d.provider === 'namecheap' && d.config.username) || '';
|
|
|
|
|
namecheapApiKey.value = (d.provider === 'namecheap' && d.config.token) || '';
|
|
|
|
|
inwxUsername.value = (d.provider === 'inwx' && d.config.username) || '';
|
|
|
|
|
inwxPassword.value = (d.provider === 'inwx' && d.config.password) || '';
|
2025-01-28 13:55:58 +01:00
|
|
|
|
|
|
|
|
dialog.value.open();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<Dialog ref="dialog"
|
2025-01-29 12:15:53 +01:00
|
|
|
:title="editing ? $t('domains.domainDialog.editTitle', { domain: domain }) : $t('domains.domainDialog.addTitle')"
|
2025-01-29 16:29:21 +01:00
|
|
|
:modal="busy"
|
|
|
|
|
:confirm-busy="busy"
|
2025-01-29 12:15:53 +01:00
|
|
|
:confirm-active="isFormValid"
|
|
|
|
|
:confirm-label="$t('main.dialog.save')"
|
2025-01-29 16:29:21 +01:00
|
|
|
:reject-label="busy ? null : $t('main.dialog.cancel')"
|
|
|
|
|
:reject-active="!busy"
|
2025-01-29 12:15:53 +01:00
|
|
|
reject-style="secondary"
|
|
|
|
|
@confirm="onSubmit()"
|
2025-01-28 21:25:12 +01:00
|
|
|
>
|
2025-01-29 16:29:21 +01:00
|
|
|
<p class="text-danger" v-show="errorMessage">{{ errorMessage }}</p>
|
2025-01-28 21:25:12 +01:00
|
|
|
|
2025-01-29 12:15:53 +01:00
|
|
|
<form ref="form" @submit.prevent="onSubmit()" autocomplete="off" @input="checkValidity()">
|
2025-01-29 16:29:21 +01:00
|
|
|
<fieldset :disabled="busy">
|
2025-01-28 21:25:12 +01:00
|
|
|
<input style="display: none;" type="submit" :disabled="busy"/>
|
|
|
|
|
<p class="has-error text-center" v-show="error">{{ error }}</p>
|
|
|
|
|
|
|
|
|
|
<FormGroup>
|
|
|
|
|
<label for="domainInput">{{ $t('domains.domainDialog.domain') }}</label>
|
2025-01-29 16:29:21 +01:00
|
|
|
<TextInput id="domainInput" v-model="domain" placeholder="example.com" :readonly="editing ? true : undefined" required />
|
2025-01-28 21:25:12 +01:00
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<FormGroup>
|
|
|
|
|
<label for="providerInput">{{ $t('domains.domainDialog.provider') }} <sup><a ng-href="https://docs.cloudron.io/domains/#dns-providers" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
2025-01-29 12:15:53 +01:00
|
|
|
<select id="providerInput" v-model="provider" @change="setDefaultTlsProvider()" required>
|
|
|
|
|
<option v-for="p in domainProviders" :value="p.value" :key="p.value">{{ p.name }}</option>
|
|
|
|
|
</select>
|
2025-01-28 21:25:12 +01:00
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- Route53 -->
|
|
|
|
|
<FormGroup v-if="provider === 'route53'">
|
|
|
|
|
<label for="accessKeyIdInput">{{ $t('domains.domainDialog.route53AccessKeyId') }}</label>
|
|
|
|
|
<TextInput id="accessKeyIdInput" v-model="accessKeyId" minlength="16" maxlength="32" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'route53'">
|
|
|
|
|
<label for="secretAccessKeyInput">{{ $t('domains.domainDialog.route53SecretAccessKey') }}</label>
|
|
|
|
|
<TextInput id="secretAccessKeyInput" v-model="secretAccessKey" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- TODO Google Cloud DNS -->
|
|
|
|
|
<FormGroup v-if="provider === 'gcdns'">
|
|
|
|
|
<label class="control-label">{{ $t('domains.domainDialog.gcdnsServiceAccountKey') }}</label>
|
|
|
|
|
<div class="input-group">
|
2025-01-29 17:59:46 +01:00
|
|
|
<input type="file" id="gcdnsKeyFileInput" style="display:none" @change="onGcdnsFileInputChange"/>
|
|
|
|
|
<input type="text" class="form-control" placeholder="Service Account Key" v-model="gcdnsProjectId" onclick="getElementById('gcdnsKeyFileInput').click();" style="cursor: pointer;" ng-disabled="domainConfigure.busy" ng-required="domainConfigure.provider === 'gcdns'">
|
2025-01-28 21:25:12 +01:00
|
|
|
<span class="input-group-addon">
|
|
|
|
|
<i class="fa fa-upload" onclick="getElementById('gcdnsKeyFileInput').click();"></i>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- DigitalOcean -->
|
|
|
|
|
<FormGroup v-if="provider === 'digitalocean'">
|
|
|
|
|
<label for="digitalOceanTokenInput">{{ $t('domains.domainDialog.digitalOceanToken') }}</label>
|
|
|
|
|
<TextInput id="digitalOceanTokenInput" v-model="digitalOceanToken" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- Gandi -->
|
|
|
|
|
<FormGroup v-if="provider === 'gandi'">
|
|
|
|
|
<label for="gandiTokenTypeInput">{{ $t('domains.domainDialog.gandiTokenType') }}</label>
|
|
|
|
|
<select id="gandiTokenTypeInput" v-model="gandiTokenType">
|
|
|
|
|
<option value="ApiKey">{{ $t('domains.domainDialog.gandiTokenTypeApiKey') }}</option>
|
|
|
|
|
<option value="PAT">{{ $t('domains.domainDialog.gandiTokenTypePAT') }}</option>
|
|
|
|
|
</select>
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'gandi'">
|
|
|
|
|
<label for="gandiApiKeyInput">{{ $t('domains.domainDialog.gandiApiKey') }}</label>
|
|
|
|
|
<TextInput id="gandiApiKeyInput" v-model="gandiApiKey" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- GoDaddy -->
|
|
|
|
|
<FormGroup v-if="provider === 'godaddy'">
|
|
|
|
|
<label for="godaddyApiKeyInput">{{ $t('domains.domainDialog.goDaddyApiKey') }}</label>
|
|
|
|
|
<TextInput id="godaddyApiKeyInput" v-model="godaddyApiKey" minlength="1" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'godaddy'">
|
|
|
|
|
<label for="godaddyApiSecretInput">{{ $t('domains.domainDialog.goDaddyApiSecret') }}</label>
|
|
|
|
|
<TextInput for="godaddyApiSecretInput" v-model="godaddyApiSecret" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- Netcup -->
|
|
|
|
|
<FormGroup v-if="provider === 'netcup'">
|
|
|
|
|
<label for="netcupCustomerNumberInput">{{ $t('domains.domainDialog.netcupCustomerNumber') }}</label>
|
|
|
|
|
<TextInput id="netcupCustomerNumberInput" v-model="netcupCustomerNumber" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'netcup'">
|
|
|
|
|
<label for="netcupApiKeyInput">{{ $t('domains.domainDialog.netcupApiKey') }}</label>
|
|
|
|
|
<TextInput id="netcupApiKeyInput" v-model="netcupApiKey" minlength="1" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'netcup'">
|
|
|
|
|
<label for="netcupApiPasswordInput">{{ $t('domains.domainDialog.netcupApiPassword') }}</label>
|
|
|
|
|
<TextInput id="netcupApiPasswordInput" v-model="netcupApiPassword" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- OVH -->
|
|
|
|
|
<FormGroup v-if="provider === 'ovh'">
|
|
|
|
|
<label for="ovhEndpointInput">{{ $t('domains.domainDialog.ovhEndpoint') }}</label>
|
|
|
|
|
<select id="ovhEndpointInput" v-model="ovhEndpoint">
|
|
|
|
|
<option v-for="endpoint in ENDPOINTS_OVH" :value="endpoint.value" :key="endpoint.value">{{ endpoint.name }}</option>
|
|
|
|
|
</select>
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'ovh'">
|
|
|
|
|
<label for="ovhConsumerKeyInput">{{ $t('domains.domainDialog.ovhConsumerKey') }}</label>
|
|
|
|
|
<TextInput id="ovhConsumerKeyInput" v-model="ovhConsumerKey" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'ovh'">
|
|
|
|
|
<label for="ovhAppKeyInput">{{ $t('domains.domainDialog.ovhAppKey') }}</label>
|
|
|
|
|
<TextInput id="ovhAppKeyInput" v-model="ovhAppKey" minlength="1" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'ovh'">
|
|
|
|
|
<label for="ovhAppSecretInput">{{ $t('domains.domainDialog.ovhAppSecret') }}</label>
|
|
|
|
|
<TextInput id="ovhAppSecretInput" v-model="ovhAppSecret" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- Porkbun -->
|
|
|
|
|
<FormGroup v-if="provider === 'porkbun'">
|
|
|
|
|
<label for="porkbunApikeyInput">{{ $t('domains.domainDialog.porkbunApikey') }}</label>
|
|
|
|
|
<TextInput id="porkbunApikeyInput" v-model="porkbunApikey" minlength="1" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'porkbun'">
|
|
|
|
|
<label for="porkbunSecretapikeyInput">{{ $t('domains.domainDialog.porkbunSecretapikey') }}</label>
|
|
|
|
|
<TextInput id="porkbunSecretapikeyInput" v-model="porkbunSecretapikey" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- Cloudflare -->
|
|
|
|
|
<FormGroup v-if="provider === 'cloudflare'">
|
|
|
|
|
<label for="cloudflareTokenTypeInput">{{ $t('domains.domainDialog.cloudflareTokenType') }}</label>
|
|
|
|
|
<select id="cloudflareTokenTypeInput" v-model="cloudflareTokenType">
|
|
|
|
|
<option value="GlobalApiKey">{{ $t('domains.domainDialog.cloudflareTokenTypeGlobalApiKey') }}</option>
|
|
|
|
|
<option value="ApiToken">{{ $t('domains.domainDialog.cloudflareTokenTypeApiToken') }}</option>
|
|
|
|
|
</select>
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'cloudflare'">
|
|
|
|
|
<label for="cloudflareTokenInput" v-show="cloudflareTokenType === 'GlobalApiKey'">{{ $t('domains.domainDialog.cloudflareTokenTypeGlobalApiKey') }}</label>
|
|
|
|
|
<label for="cloudflareTokenInput" v-show="cloudflareTokenType === 'ApiToken'">{{ $t('domains.domainDialog.cloudflareTokenTypeApiToken') }}</label>
|
|
|
|
|
<TextInput id="cloudflareTokenInput" v-model="cloudflareToken" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'cloudflare' && cloudflareTokenType === 'GlobalApiKey'">
|
|
|
|
|
<label for="cloudflareEmailInput">{{ $t('domains.domainDialog.cloudflareEmail') }}</label>
|
|
|
|
|
<TextInput id="cloudflareEmailInput" type="email" v-model="cloudflareEmail" :required="cloudflareTokenType === 'GlobalApiKey'" />
|
|
|
|
|
</FormGroup>
|
2025-01-29 12:15:53 +01:00
|
|
|
<div v-if="provider === 'cloudflare'">
|
|
|
|
|
<Checkbox v-model="cloudflareDefaultProxyStatus" :label="$t('domains.domainDialog.cloudflareDefaultProxyStatus')" />
|
|
|
|
|
<sup><a ng-href="https://docs.cloudron.io/domains/#cloudflare-dns" class="help" target="_blank" tabIndex="-1"><i class="fa fa-question-circle"></i></a></sup>
|
|
|
|
|
</div>
|
2025-01-28 21:25:12 +01:00
|
|
|
|
|
|
|
|
<!-- Linode -->
|
|
|
|
|
<FormGroup v-if="provider === 'linode'">
|
|
|
|
|
<label for="linodeTokenInput">{{ $t('domains.domainDialog.linodeToken') }}</label>
|
|
|
|
|
<TextInput id="linodeTokenInput" v-model="linodeToken" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- Bunny -->
|
|
|
|
|
<FormGroup v-if="provider === 'bunny'">
|
|
|
|
|
<label for="bunnyAccessKeyInput">{{ $t('domains.domainDialog.bunnyAccessKey') }}</label>
|
|
|
|
|
<TextInput id="bunnyAccessKeyInput" v-model="bunnyAccessKey" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- dnsimple -->
|
|
|
|
|
<FormGroup v-if="provider === 'dnsimple'">
|
|
|
|
|
<label for="dnsimpleAccessTokenInput">{{ $t('domains.domainDialog.dnsimpleAccessToken') }}</label>
|
|
|
|
|
<TextInput id="dnsimpleAccessTokenInput" v-model="dnsimpleAccessToken" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- Hetzner -->
|
|
|
|
|
<FormGroup v-if="provider === 'hetzner'">
|
|
|
|
|
<label for="hetznerTokenInput">{{ $t('domains.domainDialog.hetznerToken') }}</label>
|
|
|
|
|
<TextInput id="hetznerTokenInput" v-model="hetznerToken" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- Vultr -->
|
|
|
|
|
<FormGroup v-if="provider === 'vultr'">
|
|
|
|
|
<label for="vultrTokenInput">{{ $t('domains.domainDialog.vultrToken') }}</label>
|
|
|
|
|
<TextInput id="vultrTokenInput" v-model="vultrToken" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- deSEC -->
|
|
|
|
|
<FormGroup v-if="provider === 'desec'">
|
|
|
|
|
<label for="deSecTokenInput">{{ $t('domains.domainDialog.deSecToken') }}</label>
|
|
|
|
|
<TextInput id="deSecTokenInput" v-model="deSecToken" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- Name.com -->
|
|
|
|
|
<FormGroup v-if="provider === 'namecom'">
|
|
|
|
|
<label for="nameComUsernameInput">{{ $t('domains.domainDialog.nameComUsername') }}</label>
|
|
|
|
|
<TextInput id="nameComUsernameInput" v-model="nameComUsername" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'namecom'">
|
|
|
|
|
<label for="nameComTokenInput">{{ $t('domains.domainDialog.nameComApiToken') }}</label>
|
|
|
|
|
<TextInput id="nameComTokenInput" v-model="nameComToken" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<!-- Namecheap -->
|
|
|
|
|
<FormGroup v-if="provider === 'namecheap'">
|
|
|
|
|
<label for="namecheapUsernameInput">{{ $t('domains.domainDialog.namecheapUsername') }}</label>
|
|
|
|
|
<TextInput id="namecheapUsernameInput" v-model="namecheapUsername" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'namecheap'">
|
|
|
|
|
<label for="namecheapApiKeyInput">{{ $t('domains.domainDialog.namecheapApiKey') }}</label>
|
|
|
|
|
<TextInput id="namecheapApiKeyInput" v-model="namecheapApiKey" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<p class="small text-info text-bold" v-if="provider === 'namecheap'" v-html="$t('domains.domainDialog.namecheapInfo')"></p>
|
|
|
|
|
|
|
|
|
|
<!-- INWX -->
|
|
|
|
|
<FormGroup v-if="provider === 'inwx'">
|
|
|
|
|
<label for="inwxUsernameInput">{{ $t('domains.domainDialog.inwxUsername') }}</label>
|
|
|
|
|
<TextInput id="inwxUsernameInput" v-model="inwxUsername" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<FormGroup v-if="provider === 'inwx'">
|
|
|
|
|
<label for="inwxPasswordInput">{{ $t('domains.domainDialog.inwxPassword') }}</label>
|
|
|
|
|
<TextInput id="inwxPasswordInput" v-model="inwxPassword" required />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<p class="small text-info text-bold" v-show="provider === 'wildcard'" v-html="$t('domains.domainDialog.wildcardInfo', { domain: domain })"></p>
|
|
|
|
|
<p class="small text-info text-bold" v-show="provider === 'manual'" v-html="$t('domains.domainDialog.manualInfo')"></p>
|
2025-01-29 12:15:53 +01:00
|
|
|
<p class="small text-info text-bold" v-show="needsPort80(provider, tlsProvider)" v-html="$t('domains.domainDialog.letsEncryptInfo')"></p>
|
|
|
|
|
|
|
|
|
|
<p style="margin-top: 15px" v-show="!showAdvanced" @click="showAdvanced = true" class="actionable">{{ $t('domains.domainDialog.advancedAction') }}</p>
|
|
|
|
|
|
|
|
|
|
<div v-show="showAdvanced">
|
|
|
|
|
<FormGroup>
|
|
|
|
|
<label for="zoneNameInput">{{ $t('domains.domainDialog.zoneName') }} <sup><a ng-href="https://docs.cloudron.io/domains/#zone-name" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
|
|
|
|
<TextInput id="zoneNameInput" v-model="zoneName" />
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<FormGroup>
|
|
|
|
|
<label for="tlsProviderInput">{{ $t('domains.domainDialog.certProvider') }} <sup><a ng-href="https://docs.cloudron.io/certificates/#certificate-providers" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
|
|
|
|
|
<select id="tlsProviderInput" v-model="tlsProvider">
|
|
|
|
|
<option v-for="provider in tlsProviders" :value="provider.value" :key="provider.value">{{ provider.name }}</option>
|
|
|
|
|
</select>
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
2025-01-31 13:11:00 +01:00
|
|
|
<!-- custom certificate -->
|
2025-01-29 12:15:53 +01:00
|
|
|
<div v-if="tlsProvider === 'fallback'">
|
|
|
|
|
<label >{{ $t('domains.domainDialog.fallbackCertCustomCert') }}</label>
|
|
|
|
|
<p v-html="$t('domains.domainDialog.fallbackCertCustomCertInfo', { customCertLink: 'https://docs.cloudron.io/certificates/#custom-certificates' })"></p>
|
|
|
|
|
</div>
|
2025-01-28 21:25:12 +01:00
|
|
|
|
2025-01-31 13:11:00 +01:00
|
|
|
<FormGroup v-if="tlsProvider === 'fallback'">
|
2025-01-29 12:15:53 +01:00
|
|
|
<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>
|
|
|
|
|
</FormGroup>
|
2025-01-31 13:11:00 +01:00
|
|
|
<FormGroup v-if="tlsProvider === 'fallback'">
|
2025-01-29 12:15:53 +01:00
|
|
|
<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>
|
2025-01-28 21:25:12 +01:00
|
|
|
</div>
|
|
|
|
|
</fieldset>
|
|
|
|
|
</form>
|
2025-01-28 13:55:58 +01:00
|
|
|
</Dialog>
|
|
|
|
|
</template>
|