Add app configure location view

This commit is contained in:
Johannes Zellner
2025-03-03 12:45:40 +01:00
parent 87a222748f
commit 6ef6caaca4
2 changed files with 205 additions and 2 deletions
+150
View File
@@ -0,0 +1,150 @@
<script setup>
import { ref, onMounted, computed } from 'vue';
import { Button, SingleSelect, InputGroup, FormGroup, TextInput } from 'pankow';
import AppsModel from '../../models/AppsModel.js';
import DomainsModel from '../../models/DomainsModel.js';
const props = defineProps([ 'app' ]);
const appsModel = AppsModel.create();
const domainsModel = DomainsModel.create();
const domains = ref([]);
const busy = ref(false);
const errorMessage = ref('');
const overwriteDns = ref(false);
const needsOverwriteDns = ref(false);
const domain = ref('');
const subdomain = ref('');
const provider = computed(() => {
const d = domains.value.find(d => d.domain === domain.value);
return d ? d.provider : '';
});
const secondaryDomains = ref({});
const aliasDomains = ref([]);
function onAddAliasDomain() {
aliasDomains.value.push({
domain: '',
subdomain: ''
});
}
function onRemoveAliasDomain(index) {
aliasDomains.value.splice(index, 1);
}
async function onSubmit() {
busy.value = true;
errorMessage.value = '';
needsOverwriteDns.value = false;
const checkForDomains = [{
domain: domain.value,
subdomain: subdomain.value,
}];
for (const d in secondaryDomains.value) checkForDomains.push({ domain: secondaryDomains.value[d].domain, subdomain: secondaryDomains.value[d].subdomain });
for (const d of aliasDomains.value) checkForDomains.push({ domain: d.domain, subdomain: d.subdomain });
for (const d of checkForDomains) {
const [error, result] = await domainsModel.checkRecords(d.domain, d.subdomain);
if (error) {
errorMessage.value = error.body ? error.body.message : 'Internal error';
busy.value = false;
return console.error(error);
}
if (result.needsOverwrite) return needsOverwriteDns.value = true;
}
const data = {
overwriteDns: overwriteDns.value,
subdomain: subdomain.value,
domain: domain.value,
ports: {}, // TODO
secondaryDomains: secondaryDomains.value,
redirectDomains: [], // TODO
aliasDomains: aliasDomains.value,
};
const [error] = await appsModel.configure(props.app.id, 'location', data);
if (error) {
errorMessage.value = error.body ? error.body.message : 'Internal error';
busy.value = false;
return console.error(error);
}
busy.value = false;
}
onMounted(async () => {
const [error, result] = await domainsModel.list();
if (error) return console.error(error);
domains.value = result;
subdomain.value = props.app.subdomain;
domain.value = props.app.domain;
// map manifest httpPorts into existing secondary domains from app
secondaryDomains.value = props.app.manifest.httpPorts || {};
for (const d of props.app.secondaryDomains) {
if (!secondaryDomains.value[d.environmentVariable]) continue;
secondaryDomains.value[d.environmentVariable].domain = d.domain;
secondaryDomains.value[d.environmentVariable].subdomain = d.subdomain;
}
aliasDomains.value = props.app.aliasDomains;
});
</script>
<template>
<div>
<form @submit.prevent="onSubmit()" autocomplete="off" novalidate>
<fieldset :disabled="busy">
<input type="submit" style="display: none;" :disabled="app.error || app.taskId"/>
<FormGroup>
<label>{{ $t('app.location.location') }}</label>
<div class="has-error" v-if="errorMessage">{{ errorMessage }}</div>
<InputGroup style="flex-grow: 1">
<TextInput style="flex-grow: 1" v-model="subdomain" :placeholder="$t('app.location.locationPlaceholder')"/>
<SingleSelect :disabled="busy" :options="domains" v-model="domain" option-key="domain" option-label="domain"/>
</InputGroup>
</FormGroup>
<FormGroup v-for="secondaryDomain in secondaryDomains" :key="secondaryDomain.containerPort">
<label :for="'secondaryDomainInput' + secondaryDomain.containerPort">{{ secondaryDomain.title }}</label>
<small>{{ secondaryDomain.description }}</small>
<InputGroup style="flex-grow: 1">
<TextInput style="flex-grow: 1" :id="'secondaryDomainInput' + secondaryDomain.containerPort" v-model="secondaryDomain.subdomain" :placeholder="$t('appstore.installDialog.locationPlaceholder')"/>
<SingleSelect :disabled="busy" :options="domains" v-model="secondaryDomain.domain" option-key="domain" option-label="domain"/>
</InputGroup>
</FormGroup>
<div v-if="app.manifest.multiDomain">
<label>{{ $t('app.location.aliases') }} <sup><a href="https://docs.cloudron.io/apps/#aliases" class="help" target="_blank"><i class="fa fa-question-circle"></i></a></sup></label>
<div style="display: flex; gap: 10px; margin-bottom: 10px" v-for="(aliasDomain, index) in aliasDomains" :key="aliasDomain">
<InputGroup style="flex-grow: 1">
<TextInput style="flex-grow: 1" v-model="aliasDomain.subdomain" :placeholder="$t('app.location.locationPlaceholder')"/>
<SingleSelect :disabled="busy" :options="domains" v-model="aliasDomain.domain" option-key="domain" option-label="domain"/>
</InputGroup>
<Button danger tool :disabled="busy" icon="fa-solid fa-trash" @click="onRemoveAliasDomain(index)"/>
</div>
<div class="actionable" v-if="!busy" @click="onAddAliasDomain()">{{ $t('app.location.addAliasAction') }}</div>
</div>
</fieldset>
</form>
<p class="text-bold text-warning" v-if="provider === 'noop' || provider === 'manual'" v-html="$t('appstore.installDialog.manualWarning', { location: ((subdomain ? subdomain + '.' : '') + domain) })"></p>
<br/>
<Button @click="onSubmit()" :loading="busy" :disabled="busy || app.error || app.taskId" v-tooltip="app.error ? 'App is in error state' : (app.taskId ? 'App is busy' : '')">{{ $t('app.location.saveAction') }}</Button>
</div>
</template>