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;
}