diff --git a/dashboard/src/components/app/Backups.vue b/dashboard/src/components/app/Backups.vue index be141fa69..81cd631e1 100644 --- a/dashboard/src/components/app/Backups.vue +++ b/dashboard/src/components/app/Backups.vue @@ -4,11 +4,12 @@ import { useI18n } from 'vue-i18n'; const i18n = useI18n(); const t = i18n.t; -import { ref, watch, onMounted, useTemplateRef } from 'vue'; +import { ref, onMounted, useTemplateRef } from 'vue'; import { Icon, Button, Switch, Checkbox, FormGroup, TextInput, TableView, ButtonGroup, Dialog } from 'pankow'; import { prettyLongDate } from 'pankow/utils'; import { API_ORIGIN, SECRET_PLACEHOLDER } from '../../constants.js'; import { download } from '../../utils.js'; +import SettingsItem from '../SettingsItem.vue'; import AppsModel from '../../models/AppsModel.js'; import BackupsModel from '../../models/BackupsModel.js'; @@ -52,10 +53,13 @@ const backups = ref([]); const editDialog = useTemplateRef('editDialog'); const restoreDialog = useTemplateRef('restoreDialog'); -watch(autoBackupsEnabled, async (newValue) => { - const [error] = await appsModel.configure(props.app.id, 'automatic_backup', { enable: newValue }); - if (error) return console.error(error); -}); +async function onChangeAutoBackups(value) { + const [error] = await appsModel.configure(props.app.id, 'automatic_backup', { enable: value }); + if (error) { + autoBackupsEnabled.value = !value; + return console.error(error); + } +} async function onCreate() { createBusy.value = true; @@ -253,15 +257,22 @@ onMounted(async () => {
- -

{{ $t('app.backups.import.description') }}

+ + + +
{{ $t('app.backups.import.description') }}
+
+
+ +
+
- - -
- - -

- + + + +
+
+ +
diff --git a/dashboard/src/components/app/Cron.vue b/dashboard/src/components/app/Cron.vue index 8fbe9cd63..9f5e6ffe3 100644 --- a/dashboard/src/components/app/Cron.vue +++ b/dashboard/src/components/app/Cron.vue @@ -76,8 +76,6 @@ onMounted(() => { -
- diff --git a/dashboard/src/components/app/Info.vue b/dashboard/src/components/app/Info.vue index 62038daff..8e8124c76 100644 --- a/dashboard/src/components/app/Info.vue +++ b/dashboard/src/components/app/Info.vue @@ -66,6 +66,7 @@ onMounted(() => { checklist.value = app.checklist; hasOldChecklist.value = !!Object.keys(app.checklist).find((k) => { return app.checklist[k].acknowledged; }); noteContent.value = app.notes === null ? app.manifest.postInstallMessage : app.notes; + console.log(app.manifest.postInstallMessage) editing.value = false; busy.value = false; }); @@ -141,3 +142,23 @@ onMounted(() => { + + \ No newline at end of file diff --git a/dashboard/src/models/AppsModel.js b/dashboard/src/models/AppsModel.js index fce2a6713..5c1636394 100644 --- a/dashboard/src/models/AppsModel.js +++ b/dashboard/src/models/AppsModel.js @@ -1,8 +1,14 @@ import { API_ORIGIN, APP_TYPES, PROXY_APP_ID, HSTATES, ISTATES, RSTATES } from '../constants.js'; +import { eachLimit } from 'async'; import { fetcher } from 'pankow'; import { sleep } from 'pankow/utils'; import moment from 'moment-timezone'; +import DashboardModel from './DashboardModel.js'; +import ProfileModel from './ProfileModel.js'; + +const dashboardModel = DashboardModel.create(); +const profileModel = ProfileModel.create(); function installationStateLabel(app) { if (!app) return ''; @@ -67,6 +73,20 @@ function appProgressMessage(app) { function create() { const accessToken = localStorage.token; + // TODO maybe we can share those globally + let config = null; + let profile = null; + + async function loadConfigAndProfile() { + let [error, result] = await dashboardModel.config(); + if (error) return console.error(error); + config = result; + + [error, result] = await profileModel.get(); + if (error) return console.error(error); + profile = result; + } + async function getTask(appId) { let error, result; try { @@ -83,6 +103,53 @@ function create() { return result.body; } + async function postProcess(app) { + if (!profile || !config) await loadConfigAndProfile(); + + app.ssoAuth = app.sso && (app.manifest.addons['ldap'] || app.manifest.addons['oidc'] || app.manifest.addons['proxyAuth']); // checking app.sso first ensures app.manifest.addons is not null + app.type = app.manifest.id === PROXY_APP_ID ? APP_TYPES.PROXIED : APP_TYPES.APP; + app.iconUrl = app.iconUrl ? `${API_ORIGIN}${app.iconUrl}?ts=${new Date(app.ts).getTime()}` : `${API_ORIGIN}/img/appicon_fallback.png`; // calculate full icon url with cache busting + + // only fetch if we have permissions and a taskId is set/active + if (!app.taskId || (app.accessLevel !== 'operator' && app.accessLevel !== 'admin')) { + app.progress = 0; + app.message = ''; + app.taskMinutesActive = 0; + } else { + const task = await getTask(app.id); + if (task) { + app.progress = task.percent; + app.message = task.message; + app.taskMinutesActive = moment.duration(moment.utc().diff(moment.utc(task.creationTime))).asMinutes(); + } else { + app.progress = 0; + app.message = ''; + app.taskMinutesActive = 0; + } + } + + if (app.manifest.postInstallMessage) { + var text= app.manifest.postInstallMessage; + // we chose - because underscore has special meaning in markdown + text = text.replace(/\$CLOUDRON-APP-LOCATION/g, app.subdomain); + text = text.replace(/\$CLOUDRON-APP-DOMAIN/g, app.domain); + text = text.replace(/\$CLOUDRON-APP-FQDN/g, app.fqdn); + text = text.replace(/\$CLOUDRON-APP-ORIGIN/g, 'https://' + app.fqdn); + text = text.replace(/\$CLOUDRON-API-DOMAIN/g, config.adminFqdn); + text = text.replace(/\$CLOUDRON-API-ORIGIN/g, 'https://' + config.adminFqdn); + text = text.replace(/\$CLOUDRON-USERNAME/g, profile.username); + text = text.replace(/\$CLOUDRON-APP-ID/g, app.id); + + // [^] matches even newlines. '?' makes it non-greedy + if (app.sso) text = text.replace(/[^]*?<\/nosso>/g, ''); + else text = text.replace(/[^]*?<\/sso>/g, ''); + + app.manifest.postInstallMessage = text; + } + + return app; + } + return { name: 'AppsModel', getTask, @@ -133,30 +200,7 @@ function create() { if (error || result.status !== 200) return [error || result]; - for (const app of result.body.apps) { - app.ssoAuth = app.sso && (app.manifest.addons['ldap'] || app.manifest.addons['oidc'] || app.manifest.addons['proxyAuth']); // checking app.sso first ensures app.manifest.addons is not null - app.type = app.manifest.id === PROXY_APP_ID ? APP_TYPES.PROXIED : APP_TYPES.APP; - app.iconUrl = app.iconUrl ? `${API_ORIGIN}${app.iconUrl}?ts=${new Date(app.ts).getTime()}` : `${API_ORIGIN}/img/appicon_fallback.png`; // calculate full icon url with cache busting - - // only fetch if we have permissions and a taskId is set/active - if (!app.taskId || (app.accessLevel !== 'operator' && app.accessLevel !== 'admin')) { - app.progress = 0; - app.message = ''; - app.taskMinutesActive = 0; - continue; - } - - const task = await getTask(app.id); - if (task) { - app.progress = task.percent; - app.message = task.message; - app.taskMinutesActive = moment.duration(moment.utc().diff(moment.utc(task.creationTime))).asMinutes(); - } else { - app.progress = 0; - app.message = ''; - app.taskMinutesActive = 0; - } - } + await eachLimit(result.body.apps, 10, postProcess); return [null, result.body.apps]; }, @@ -170,31 +214,7 @@ function create() { if (error || result.status !== 200) return [error || result]; - const app = result.body; - - app.ssoAuth = app.sso && (app.manifest.addons['ldap'] || app.manifest.addons['oidc'] || app.manifest.addons['proxyAuth']); // checking app.sso first ensures app.manifest.addons is not null - app.type = app.manifest.id === PROXY_APP_ID ? APP_TYPES.PROXIED : APP_TYPES.APP; - app.iconUrl = app.iconUrl ? `${API_ORIGIN}${app.iconUrl}?ts=${new Date(app.ts).getTime()}` : `${API_ORIGIN}/img/appicon_fallback.png`; // calculate full icon url with cache busting - - // only fetch if we have permissions and a taskId is set/active - if (!app.taskId || (app.accessLevel !== 'operator' && app.accessLevel !== 'admin')) { - app.progress = 0; - app.message = ''; - app.taskMinutesActive = 0; - } else { - const task = await getTask(app.id); - if (task) { - app.progress = task.percent; - app.message = task.message; - app.taskMinutesActive = moment.duration(moment.utc().diff(moment.utc(task.creationTime))).asMinutes(); - } else { - app.progress = 0; - app.message = ''; - app.taskMinutesActive = 0; - } - } - - return [null, app]; + return [null, await postProcess(result.body)]; }, async restart(id) { let result; diff --git a/dashboard/src/views/AppConfigureView.vue b/dashboard/src/views/AppConfigureView.vue index 4bd363e44..5e31326c7 100644 --- a/dashboard/src/views/AppConfigureView.vue +++ b/dashboard/src/views/AppConfigureView.vue @@ -329,6 +329,7 @@ onBeforeUnmount(() => { .configure-body { margin-top: 20px; + margin-bottom: 10px; display: flex; }