Add cloudron registration via setupToken

This commit is contained in:
Johannes Zellner
2025-03-21 11:42:30 +01:00
parent c517b6db3b
commit 7c1160de92
3 changed files with 122 additions and 25 deletions
+44 -25
View File
@@ -1,9 +1,11 @@
<script setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, useTemplateRef } from 'vue';
import { Button } from 'pankow';
import { prettyDate } from 'pankow/utils';
import Section from '../components/Section.vue';
import SettingsItem from '../components/SettingsItem.vue';
import CloudronRegistrationDialog from '../components/CloudronRegistrationDialog.vue';
import AppstoreModel from '../models/AppstoreModel.js';
const appstoreModel = AppstoreModel.create();
@@ -21,7 +23,12 @@ const planName = ref('');
const cancelAt = ref(0);
const status = ref('');
onMounted(async () => {
const registrationDialog = useTemplateRef('registrationDialog');
function onOpenRegistrationDialog() {
registrationDialog.value.open();
}
async function refresh() {
const [error, result] = await appstoreModel.getSubscription();
if (error) {
if (error.status === 402) return busy.value = false; // not yet registered
@@ -38,7 +45,10 @@ onMounted(async () => {
planName.value = result.plan.name;
cancelAt.value = result.cancel_at || 0;
status.value = result.status;
}
onMounted(async () => {
await refresh();
busy.value = false;
});
@@ -46,35 +56,44 @@ onMounted(async () => {
<template>
<div>
<CloudronRegistrationDialog ref="registrationDialog" @success="refresh()"/>
<Section :title="$t('settings.appstoreAccount.title')">
<p>{{ $t('settings.appstoreAccount.description') }}</p>
<div v-if="!busy">
<div v-if="hasSubscription">
<p>{{ $t('settings.appstoreAccount.description') }}</p>
<a href="/#/appstore" target="_blank" v-show="!hasSubscription && !busy">{{ $t('settings.appstoreAccount.setupAction') }}</a>
<!-- TODO what is `subscription.externalCustomer` ? -->
<div class="info-row">
<div class="info-label">{{ $t('settings.appstoreAccount.email') }}</div>
<div class="info-value"><a :href="`${consoleServerOrigin}?email=${emailEncoded}`" target="_blank">{{ email }} <i v-show="!emailVerified" class="fas fa-exclamation-triangle text-danger" v-tooltip="$t('settings.appstoreAccount.emailNotVerified')"></i></a></div>
</div>
<div v-show="hasSubscription && !busy">
<!-- TODO what is `subscription.externalCustomer` ? -->
<div class="info-row">
<div class="info-label">{{ $t('settings.appstoreAccount.email') }}</div>
<div class="info-value"><a :href="`${consoleServerOrigin}?email=${emailEncoded}`" target="_blank">{{ email }} <i v-show="!emailVerified" class="fas fa-exclamation-triangle text-danger" v-tooltip="$t('settings.appstoreAccount.emailNotVerified')"></i></a></div>
<div class="info-row">
<div class="info-label">{{ $t('settings.appstoreAccount.cloudronId') }}</div>
<div class="info-value">{{ cloudronId }}</div>
</div>
<div class="info-row">
<div class="info-label">{{ $t('settings.appstoreAccount.subscription') }}</div>
<div class="info-value">{{ planName }}</div>
</div>
<p class="text-danger" v-show="cancelAt">{{ $t('settings.appstoreAccount.subscriptionEndsAt') }} {{ prettyDate(cancelAt*1000) }}</p>
<Button :href="`${consoleServerOrigin}/#/cloudron/${cloudronId}?email=${encodeURIComponent(email)}`" target="_blank">
<span v-show="planName === 'free'">{{ $t('settings.appstoreAccount.subscriptionSetupAction') }}</span>
<span v-show="cancelAt">{{ $t('settings.appstoreAccount.subscriptionReactivateAction') }}</span>
<span v-show="!cancelAt">{{ $t('settings.appstoreAccount.subscriptionChangeAction') }}</span>
</Button>
</div>
<div class="info-row">
<div class="info-label">{{ $t('settings.appstoreAccount.cloudronId') }}</div>
<div class="info-value">{{ cloudronId }}</div>
<div v-else>
<SettingsItem>
<div style="display: flex; align-items: center;">{{ $t('settings.appstoreAccount.description') }}</div>
<Button @click="onOpenRegistrationDialog()">{{ $t('settings.appstoreAccount.setupAction') }}</Button>
</SettingsItem>
</div>
<div class="info-row">
<div class="info-label">{{ $t('settings.appstoreAccount.subscription') }}</div>
<div class="info-value">{{ planName }}</div>
</div>
<p class="text-danger" v-show="cancelAt">{{ $t('settings.appstoreAccount.subscriptionEndsAt') }} {{ prettyDate(cancelAt*1000) }}</p>
<Button v-show="!busy" :href="`${consoleServerOrigin}/#/cloudron/${cloudronId}?email=${encodeURIComponent(email)}`" target="_blank">
<span v-show="planName === 'free'">{{ $t('settings.appstoreAccount.subscriptionSetupAction') }}</span>
<span v-show="cancelAt">{{ $t('settings.appstoreAccount.subscriptionReactivateAction') }}</span>
<span v-show="!cancelAt">{{ $t('settings.appstoreAccount.subscriptionChangeAction') }}</span>
</Button>
</div>
</Section>
</div>
@@ -0,0 +1,67 @@
<script setup>
import { ref, useTemplateRef } from 'vue';
import { Dialog, FormGroup, TextInput } from 'pankow';
import AppstoreModel from '../models/AppstoreModel.js';
const emit = defineEmits([ 'success' ]);
const appstoreModel = AppstoreModel.create();
const dialog = useTemplateRef('dialog');
const busy = ref(false);
const formError = ref('');
const setupToken = ref('');
async function onSubmit() {
busy.value = true;
formError.value = '';
const [error] = await appstoreModel.register(setupToken.value);
if (error) {
formError.value = error.body ? error.body.message : 'Internal error';
busy.value = false;
return console.error(error);
}
emit('success');
dialog.value.close();
busy.value = false;
}
defineExpose({
open() {
dialog.value.open();
}
});
</script>
<template>
<div>
<Dialog ref="dialog"
title="Register Cloudron"
:confirm-label="$t('main.dialog.save')"
:confirm-active="!busy && setupToken !== ''"
:confirm-busy="busy"
:reject-label="busy ? '' : $t('main.dialog.cancel')"
reject-style="secondary"
@confirm="onSubmit()"
>
<div>
<div class="text-danger" v-if="formError">{{ formError }}</div>
<form @submit.prevent="onSubmit()" autocomplete="off">
<fieldset :disabled="busy">
<FormGroup>
<label for="setupTokenInput">Setup token</label>
<TextInput id="setupTokenInput" v-model="setupToken" required />
</FormGroup>
</fieldset>
</form>
</div>
</Dialog>
</div>
</template>