507 lines
24 KiB
Vue
507 lines
24 KiB
Vue
<script setup>
|
|
|
|
import { onMounted, ref, useTemplateRef, provide } from 'vue';
|
|
import { Notification, fetcher, SideBar } from '@cloudron/pankow';
|
|
import { setLanguage } from './i18n.js';
|
|
import { API_ORIGIN, TOKEN_TYPES } from './constants.js';
|
|
import { redirectIfNeeded } from './utils.js';
|
|
import ProfileModel from './models/ProfileModel.js';
|
|
import ProvisionModel from './models/ProvisionModel.js';
|
|
import DashboardModel from './models/DashboardModel.js';
|
|
import BrandingModel from './models/BrandingModel.js';
|
|
import Headerbar from './components/Headerbar.vue';
|
|
import SubscriptionRequiredDialog from './components/SubscriptionRequiredDialog.vue';
|
|
import OfflineOverlay from './components/OfflineOverlay.vue';
|
|
import AppsView from './views/AppsView.vue';
|
|
import AppConfigureView from './views/AppConfigureView.vue';
|
|
import AppearanceView from './views/AppearanceView.vue';
|
|
import AppstoreView from './views/AppstoreView.vue';
|
|
import BackupSitesView from './views/BackupSitesView.vue';
|
|
import BackupAppArchiveView from './views/BackupAppArchiveView.vue';
|
|
import BackupListView from './views/BackupListView.vue';
|
|
import CloudronAccountView from './views/CloudronAccountView.vue';
|
|
import DomainsView from './views/DomainsView.vue';
|
|
import EmailDomainView from './views/EmailDomainView.vue';
|
|
import EmailDomainsView from './views/EmailDomainsView.vue';
|
|
import EmailMailboxesView from './views/EmailMailboxesView.vue';
|
|
import EmailMailinglistsView from './views/EmailMailinglistsView.vue';
|
|
import EmailSettingsView from './views/EmailSettingsView.vue';
|
|
import EmailEventlogView from './views/EmailEventlogView.vue';
|
|
import EventlogView from './views/EventlogView.vue';
|
|
import NetworkView from './views/NetworkView.vue';
|
|
import ProfileView from './views/ProfileView.vue';
|
|
import ServicesView from './views/ServicesView.vue';
|
|
import SystemLocaleView from './views/SystemLocaleView.vue';
|
|
import SystemUpdateView from './views/SystemUpdateView.vue';
|
|
import SystemDockerView from './views/SystemDockerView.vue';
|
|
import MetricsView from './views/MetricsView.vue';
|
|
import UserDirectorySettingsView from './views/UserDirectorySettingsView.vue';
|
|
import UserDirectoryLdapServerView from './views/UserDirectoryLdapServerView.vue';
|
|
import UserDirectoryOpenIdProviderView from './views/UserDirectoryOpenIdProviderView.vue';
|
|
import UsersView from './views/UsersView.vue';
|
|
import GroupsView from './views/GroupsView.vue';
|
|
import VolumesView from './views/VolumesView.vue';
|
|
|
|
const VIEWS = {
|
|
APP: 'app',
|
|
APPEARANCE: 'appearance',
|
|
APPS: 'apps',
|
|
APPSTORE: 'appstore',
|
|
BACKUP_SITES: 'backup-sites',
|
|
BACKUP_LIST: 'backup-list',
|
|
BACKUP_APP_ARCHIVE: 'backup-app-archive',
|
|
CLOUDRON_ACCOUNT: 'cloudron-account',
|
|
DOMAINS: 'domains',
|
|
EMAIL_DOMAIN: 'email-domain',
|
|
EMAIL_DOMAINS: 'email-domains',
|
|
EMAIL_MAILBOXES: 'email-mailboxes',
|
|
EMAIL_MAILINGLISTS: 'email-mailinglists',
|
|
EMAIL_SETTINGS: 'email-settings',
|
|
EMAIL_EVENTLOG: 'email-eventlog',
|
|
METRICS: 'metrics',
|
|
NETWORK: 'network',
|
|
PROFILE: 'profile',
|
|
SERVICES: 'services',
|
|
SYSTEM_LOCALE: 'system-locale',
|
|
SYSTEM_DOCKER: 'system-docker',
|
|
SYSTEM_EVENTLOG: 'system-eventlog',
|
|
SYSTEM_UPDATE: 'system-update',
|
|
USER_DIRECTORY_SETTINGS: 'user-directory-settings',
|
|
USER_DIRECTORY_LDAP_SERVER: 'user-directory-ldap-server',
|
|
USER_DIRECTORY_OPENID_PROVIDER: 'user-directory-openid-provider',
|
|
USERS: 'users',
|
|
GROUPS: 'users-groups',
|
|
VOLUMES: 'volumes',
|
|
};
|
|
|
|
const offlineOverlay = useTemplateRef('offlineOverlay');
|
|
|
|
function onOnline() {
|
|
ready.value = true;
|
|
}
|
|
|
|
fetcher.globalOptions.errorHook = (error) => {
|
|
// network error, request killed by browser
|
|
if (error instanceof TypeError) {
|
|
return offlineOverlay.value.open();
|
|
}
|
|
|
|
// re-login will make the code get a new token
|
|
if (error.status === 401) return profileModel.logout();
|
|
|
|
if (error.status === 500 || error.status === 501) {
|
|
// actual internal server error, most likely a bug or timeout log to console only to not alert the user
|
|
console.error(error);
|
|
console.log('------\nCloudron Internal Error\n\nIf you see this, please send a mail with above log to support@cloudron.io\n------\n');
|
|
}
|
|
|
|
if (error.status >= 502) {
|
|
// This means the box service is not reachable. We just show offline banner for now
|
|
ready.value = false;
|
|
return offlineOverlay.value.open();
|
|
}
|
|
};
|
|
|
|
const dashboardModel = DashboardModel.create();
|
|
const profileModel = ProfileModel.create();
|
|
const provisionModel = ProvisionModel.create();
|
|
|
|
const sidebar = useTemplateRef('sidebar');
|
|
const subscriptionRequiredDialog = useTemplateRef('subscriptionRequiredDialog');
|
|
const ready = ref(false);
|
|
const view = ref('');
|
|
const profile = ref({});
|
|
const subscription = ref({
|
|
plan: {},
|
|
});
|
|
const config = ref({});
|
|
const avatarUrl = ref('');
|
|
const features = ref({});
|
|
|
|
function onSidebarClose() {
|
|
sidebar.value.close();
|
|
}
|
|
|
|
const activeSidebarItem = ref('');
|
|
const activeSidebarGroups = ref({
|
|
'backup': false,
|
|
'email': false,
|
|
'system': false,
|
|
'user-directory': false,
|
|
'users': false
|
|
});
|
|
function onToggleGroup(group) {
|
|
activeSidebarGroups.value[group] = !activeSidebarGroups.value[group];
|
|
}
|
|
|
|
function onHashChange() {
|
|
const v = location.hash.slice(2);
|
|
|
|
activeSidebarItem.value = v;
|
|
|
|
if (activeSidebarItem.value.indexOf('backup') === 0) activeSidebarGroups.value['backup'] = true;
|
|
else if (activeSidebarItem.value.indexOf('email') === 0) activeSidebarGroups.value['email'] = true;
|
|
else if (activeSidebarItem.value.indexOf('system') === 0) activeSidebarGroups.value['system'] = true;
|
|
else if (activeSidebarItem.value.indexOf('user-directory') === 0) activeSidebarGroups.value['user-directory'] = true;
|
|
else if (activeSidebarItem.value.indexOf('users') === 0) activeSidebarGroups.value['users'] = true;
|
|
|
|
if (v === VIEWS.APPS) {
|
|
view.value = VIEWS.APPS;
|
|
} else if (v.indexOf(VIEWS.APPSTORE) === 0 && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.APPSTORE;
|
|
} else if (v.indexOf(VIEWS.APP+'/') === 0) { // this checks permissions within the view as we may have an app operator
|
|
view.value = VIEWS.APP;
|
|
} else if (v === VIEWS.APPEARANCE && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.APPEARANCE;
|
|
} else if (v === VIEWS.BACKUP_SITES && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.BACKUP_SITES;
|
|
} else if (v === VIEWS.BACKUP_LIST && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.BACKUP_LIST;
|
|
} else if (v === VIEWS.BACKUP_APP_ARCHIVE && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.BACKUP_APP_ARCHIVE;
|
|
} else if (v === VIEWS.CLOUDRON_ACCOUNT && profile.value.isAtLeastOwner) {
|
|
view.value = VIEWS.CLOUDRON_ACCOUNT;
|
|
} else if (v === VIEWS.DOMAINS && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.DOMAINS;
|
|
} else if (v.indexOf(VIEWS.EMAIL_DOMAIN+'/') === 0 && profile.value.isAtLeastMailManager) {
|
|
view.value = VIEWS.EMAIL_DOMAIN;
|
|
} else if (v === VIEWS.EMAIL_DOMAINS && profile.value.isAtLeastMailManager) {
|
|
view.value = VIEWS.EMAIL_DOMAINS;
|
|
} else if (v === VIEWS.EMAIL_MAILBOXES && profile.value.isAtLeastMailManager) {
|
|
view.value = VIEWS.EMAIL_MAILBOXES;
|
|
} else if (v === VIEWS.EMAIL_MAILINGLISTS && profile.value.isAtLeastMailManager) {
|
|
view.value = VIEWS.EMAIL_MAILINGLISTS;
|
|
} else if (v === VIEWS.EMAIL_SETTINGS && profile.value.isAtLeastMailManager) {
|
|
view.value = VIEWS.EMAIL_SETTINGS;
|
|
} else if (v === VIEWS.EMAIL_EVENTLOG && profile.value.isAtLeastMailManager) {
|
|
view.value = VIEWS.EMAIL_EVENTLOG;
|
|
} else if (v === VIEWS.METRICS && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.METRICS;
|
|
} else if (v === VIEWS.NETWORK && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.NETWORK;
|
|
} else if (v.indexOf(VIEWS.PROFILE) === 0) {
|
|
view.value = VIEWS.PROFILE;
|
|
} else if (v === VIEWS.SERVICES && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.SERVICES;
|
|
} else if (v === VIEWS.SYSTEM_LOCALE && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.SYSTEM_LOCALE;
|
|
} else if (v === VIEWS.SYSTEM_DOCKER && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.SYSTEM_DOCKER;
|
|
} else if (v.indexOf(VIEWS.SYSTEM_EVENTLOG) === 0 && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.SYSTEM_EVENTLOG;
|
|
} else if (v === VIEWS.SYSTEM_UPDATE && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.SYSTEM_UPDATE;
|
|
} else if (v === VIEWS.USER_DIRECTORY_SETTINGS && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.USER_DIRECTORY_SETTINGS;
|
|
} else if (v === VIEWS.USER_DIRECTORY_LDAP_SERVER && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.USER_DIRECTORY_LDAP_SERVER;
|
|
} else if (v === VIEWS.USER_DIRECTORY_OPENID_PROVIDER && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.USER_DIRECTORY_OPENID_PROVIDER;
|
|
} else if (v === VIEWS.USERS && profile.value.isAtLeastUserManager) {
|
|
view.value = VIEWS.USERS;
|
|
} else if (v === VIEWS.GROUPS && profile.value.isAtLeastUserManager) {
|
|
view.value = VIEWS.GROUPS;
|
|
} else if (v === VIEWS.VOLUMES && profile.value.isAtLeastAdmin) {
|
|
view.value = VIEWS.VOLUMES;
|
|
} else {
|
|
window.location.hash = '/' + VIEWS.APPS;
|
|
}
|
|
}
|
|
|
|
BrandingModel.onChange(BrandingModel.KEYS.NAME, (value) => {
|
|
window.document.title = value;
|
|
config.value.cloudronName = value;
|
|
});
|
|
|
|
BrandingModel.onChange(BrandingModel.KEYS.AVATAR, (value) => {
|
|
avatarUrl.value = value;
|
|
if (document.querySelector('link[rel="icon"]')) document.querySelector('link[rel="icon"]').href = value;
|
|
});
|
|
|
|
ProfileModel.onChange(ProfileModel.KEYS.AVATAR, (value) => {
|
|
profile.value.avatarUrl = value;
|
|
});
|
|
|
|
async function refreshProfile() {
|
|
const [error, result] = await profileModel.get();
|
|
if (error) return console.error(error);
|
|
profile.value = result;
|
|
}
|
|
|
|
async function refreshConfigAndFeatures() {
|
|
const [error, result] = await dashboardModel.config();
|
|
if (error) return console.error(error);
|
|
config.value = result;
|
|
features.value = result.features;
|
|
}
|
|
|
|
provide('subscriptionRequiredDialog', subscriptionRequiredDialog);
|
|
provide('features', features);
|
|
provide('profile', profile);
|
|
provide('refreshProfile', refreshProfile);
|
|
provide('refreshFeatures', refreshConfigAndFeatures);
|
|
|
|
onMounted(async () => {
|
|
const [error, result] = await provisionModel.status();
|
|
if (error) return console.error(error);
|
|
|
|
if (redirectIfNeeded(result, 'dashboard')) return; // redirected to some other view...
|
|
|
|
if (!localStorage.token) {
|
|
localStorage.setItem('redirectToHash', window.location.hash);
|
|
|
|
// start oidc flow
|
|
window.location.href = `${API_ORIGIN}/openid/auth?client_id=` + (API_ORIGIN ? TOKEN_TYPES.ID_DEVELOPMENT : TOKEN_TYPES.ID_WEBADMIN) + '&scope=openid email profile&response_type=code token&redirect_uri=' + window.location.origin + '/authcallback.html';
|
|
|
|
return;
|
|
}
|
|
|
|
await refreshProfile();
|
|
|
|
// ensure language from profile if set
|
|
if (profile.value.language) await setLanguage(profile.value.language, true);
|
|
|
|
await refreshConfigAndFeatures();
|
|
|
|
avatarUrl.value = `https://${config.value.adminFqdn}/api/v1/cloudron/avatar`;
|
|
if (document.querySelector('link[rel="icon"]')) document.querySelector('link[rel="icon"]').href = `${API_ORIGIN}/api/v1/cloudron/avatar?ts=${Date.now()}`;
|
|
|
|
if (config.value.mandatory2FA && !profile.value.twoFactorAuthenticationEnabled) window.location.hash = `/${VIEWS.PROFILE}?setup2fa`;
|
|
|
|
window.addEventListener('hashchange', onHashChange);
|
|
onHashChange();
|
|
|
|
ready.value = true;
|
|
});
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div style="overflow: hidden; height: 100%;">
|
|
<Notification />
|
|
<OfflineOverlay ref="offlineOverlay" @online="onOnline()" :href="'https://docs.cloudron.io/troubleshooting/'" :label="$t('main.offline')" />
|
|
<SubscriptionRequiredDialog ref="subscriptionRequiredDialog"/>
|
|
|
|
<div v-if="ready" style="display: flex; flex-direction: row; overflow: hidden; height: 100%;">
|
|
<SideBar v-if="profile.isAtLeastUserManager" ref="sidebar">
|
|
<a href="#/" class="sidebar-logo" @click="onSidebarClose()">
|
|
<img :src="avatarUrl" :alt="(config.cloudronName || 'Cloudron') + ' icon'" width="40" height="40"/> {{ config.cloudronName || 'Cloudron' }}
|
|
</a>
|
|
<div class="sidebar-list">
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'apps' }" href="#/apps" @click="onSidebarClose()"><i class="fa fa-grip fa-fw"></i> {{ $t('apps.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'appstore' }" v-show="profile.isAtLeastAdmin" href="#/appstore" @click="onSidebarClose()"><i class="fa fa-cloud-download-alt fa-fw"></i> {{ $t('appstore.title') }}</a>
|
|
<hr/>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'appearance' }" v-show="profile.isAtLeastAdmin" href="#/appearance" @click="onSidebarClose()"><i class="fa fa-pen-ruler fa-fw"></i> {{ $t('appearance.title') }}</a>
|
|
|
|
<div class="sidebar-item" v-show="profile.isAtLeastAdmin" @click="onToggleGroup('backup')"><i class="fa fa-archive fa-fw"></i> {{ $t('backups.title') }} <i class="collapse fa-solid fa-angle-right" :class="{ expanded: activeSidebarGroups['backup'] }" style="margin-left: 6px;"></i></div>
|
|
<Transition name="sidebar-item-group-animation">
|
|
<div class="sidebar-item-group" v-if="activeSidebarGroups['backup']">
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'backup-sites' }" href="#/backup-sites" @click="onSidebarClose()"><i class="fa fa-fw fa-hard-drive"></i> {{ $t('backups.sites.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'backup-list' }" href="#/backup-list" @click="onSidebarClose()"><i class="fa fa-fw fa-list-check"></i> {{ $t('backups.listing.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'backup-app-archive' }" href="#/backup-app-archive" @click="onSidebarClose()"><i class="fa fa-fw fa-grip"></i> {{ $t('backups.archives.title') }}</a>
|
|
</div>
|
|
</Transition>
|
|
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'domains' }" v-show="profile.isAtLeastAdmin" href="#/domains" @click="onSidebarClose()"><i class="fa fa-globe fa-fw"></i> {{ $t('domains.title') }}</a>
|
|
|
|
<div class="sidebar-item" v-show="profile.isAtLeastMailManager" @click="onToggleGroup('email')"><i class="fa fa-envelope fa-fw"></i> {{ $t('emails.title') }} <i class="collapse fa-solid fa-angle-right" :class="{ expanded: activeSidebarGroups['email'] }" style="margin-left: 6px;"></i></div>
|
|
<Transition name="sidebar-item-group-animation">
|
|
<div class="sidebar-item-group" v-if="activeSidebarGroups['email']">
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'email-domains' }" href="#/email-domains" @click="onSidebarClose()"><i class="fa fa-fw fa-globe"></i> Domains</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'email-mailboxes' }" href="#/email-mailboxes" @click="onSidebarClose()"><i class="fa fa-fw fa-inbox"></i> {{ $t('email.incoming.mailboxes.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'email-mailinglists' }" href="#/email-mailinglists" @click="onSidebarClose()"><i class="fa fa-fw-solid fa-envelopes-bulk"></i> {{ $t('email.incoming.mailinglists.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'email-eventlog' }" href="#/email-eventlog" @click="onSidebarClose()"><i class="fa fa-fw fa-list-alt"></i> {{ $t('emails.eventlog.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'email-settings' }" href="#/email-settings" @click="onSidebarClose()"><i class="fa fa-fw fa-cog"></i> {{ $t('emails.settings.title') }}</a>
|
|
</div>
|
|
</Transition>
|
|
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'network' }" v-show="profile.isAtLeastAdmin" href="#/network" @click="onSidebarClose()"><i class="fas fa-network-wired fa-fw"></i> {{ $t('network.title') }}</a>
|
|
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'services' }" v-show="profile.isAtLeastAdmin" href="#/services" @click="onSidebarClose()"><i class="fa fa-cogs fa-fw"></i> {{ $t('services.title') }}</a>
|
|
|
|
<div class="sidebar-item" v-show="profile.isAtLeastAdmin" @click="onToggleGroup('system')"><i class="fa fa-wrench fa-fw"></i> {{ $t('settings.title') }} <i class="collapse fa-solid fa-angle-right" :class="{ expanded: activeSidebarGroups['system'] }" style="margin-left: 6px;"></i></div>
|
|
<Transition name="sidebar-item-group-animation">
|
|
<div class="sidebar-item-group" v-if="activeSidebarGroups['system']">
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'system-docker' }" href="#/system-docker" @click="onSidebarClose()"><i class="fa-brands fa-fw fa-docker"></i> {{ $t('dockerRegistries.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'system-eventlog' }" href="#/system-eventlog" @click="onSidebarClose()"><i class="fa fa-list-alt fa-fw"></i> {{ $t('eventlog.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'system-locale' }" href="#/system-locale" @click="onSidebarClose()"><i class="fa fa-fw fa-language"></i> {{ $t('system.locale.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'system-update' }" href="#/system-update" @click="onSidebarClose()"><i class="fa fa-fw fa-square-up-right"></i> {{ $t('settings.updates.title') }}</a>
|
|
</div>
|
|
</Transition>
|
|
|
|
<div class="sidebar-item" v-show="profile.isAtLeastUserManager" @click="onToggleGroup('users')"><i class="fa fa-users-gear fa-fw"></i> {{ $t('users.title') }} <i class="collapse fa-solid fa-angle-right" :class="{ expanded: activeSidebarGroups['users'] }" style="margin-left: 6px;"></i></div>
|
|
<Transition name="sidebar-item-group-animation">
|
|
<div class="sidebar-item-group" v-if="activeSidebarGroups['users']">
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'users' }" v-show="profile.isAtLeastUserManager" href="#/users" @click="onSidebarClose()"><i class="fa fa-user fa-fw"></i> {{ $t('main.navbar.users') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'users-groups' }" v-show="profile.isAtLeastUserManager" href="#/users-groups" @click="onSidebarClose()"><i class="fa fa-users fa-fw"></i> {{ $t('main.navbar.groups') }}</a>
|
|
</div>
|
|
</Transition>
|
|
|
|
<div class="sidebar-item" v-show="profile.isAtLeastAdmin" @click="onToggleGroup('user-directory')"><i class="fa fa-address-book fa-fw"></i> {{ $t('userDirectory.title') }} <i class="collapse fa-solid fa-angle-right" :class="{ expanded: activeSidebarGroups['user-directory'] }" style="margin-left: 6px;"></i></div>
|
|
<Transition name="sidebar-item-group-animation">
|
|
<div class="sidebar-item-group" v-if="activeSidebarGroups['user-directory']">
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'user-directory-ldap-server' }" href="#/user-directory-ldap-server" @click="onSidebarClose()"><i class="fa fa-fw fa-users-rays"></i> {{ $t('users.exposedLdap.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'user-directory-openid-provider' }" href="#/user-directory-openid-provider" @click="onSidebarClose()"><i class="fa fa-fw fa-brands fa-openid"></i> {{ $t('oidc.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'user-directory-settings' }" href="#/user-directory-settings" @click="onSidebarClose()"><i class="fa fa-fw fa-cog"></i> {{ $t('userdirectory.settings.title') }}</a>
|
|
</div>
|
|
</Transition>
|
|
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'volumes' }" v-show="profile.isAtLeastAdmin" href="#/volumes" @click="onSidebarClose()"><i class="fa fa-hdd fa-fw"></i> {{ $t('volumes.title') }}</a>
|
|
|
|
<hr v-show="profile.isAtLeastAdmin"/>
|
|
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'metrics' }" v-show="profile.isAtLeastAdmin" href="#/metrics" @click="onSidebarClose()"><i class="fa fa-chart-area fa-fw"></i> {{ $t('system.title') }}</a>
|
|
<a class="sidebar-item" :class="{ active: activeSidebarItem === 'cloudron-account' }" v-show="profile.isAtLeastOwner" href="#/cloudron-account" @click="onSidebarClose()"><i class="fa fa-crown fa-fw"></i> {{ $t('settings.appstoreAccount.title') }}</a>
|
|
</div>
|
|
</SideBar>
|
|
|
|
<div style="flex-grow: 1; display: flex; flex-direction: column; overflow: hidden; height: 100%;">
|
|
<Headerbar :config="config" :subscription="subscription"/>
|
|
|
|
<div style="display: flex; justify-content: center; overflow: auto; flex-grow: 1; padding: 0; margin: 0 10px; position: relative;">
|
|
<KeepAlive>
|
|
<AppsView v-if="view === VIEWS.APPS" />
|
|
<AppstoreView v-else-if="view === VIEWS.APPSTORE" />
|
|
</KeepAlive>
|
|
<AppConfigureView v-if="view === VIEWS.APP" />
|
|
<AppearanceView v-else-if="view === VIEWS.APPEARANCE" />
|
|
<BackupSitesView v-else-if="view === VIEWS.BACKUP_SITES" />
|
|
<BackupListView v-else-if="view === VIEWS.BACKUP_LIST" />
|
|
<BackupAppArchiveView v-else-if="view === VIEWS.BACKUP_APP_ARCHIVE" />
|
|
<CloudronAccountView v-else-if="view === VIEWS.CLOUDRON_ACCOUNT" />
|
|
<DomainsView v-else-if="view === VIEWS.DOMAINS" />
|
|
<EmailDomainsView v-else-if="view === VIEWS.EMAIL_DOMAINS" />
|
|
<EmailDomainView v-else-if="view === VIEWS.EMAIL_DOMAIN" />
|
|
<EmailMailboxesView v-else-if="view === VIEWS.EMAIL_MAILBOXES" />
|
|
<EmailMailinglistsView v-else-if="view === VIEWS.EMAIL_MAILINGLISTS" />
|
|
<EmailSettingsView v-else-if="view === VIEWS.EMAIL_SETTINGS" />
|
|
<EmailEventlogView v-else-if="view === VIEWS.EMAIL_EVENTLOG" />
|
|
<EventlogView v-else-if="view === VIEWS.SYSTEM_EVENTLOG" />
|
|
<MetricsView v-else-if="view === VIEWS.METRICS" />
|
|
<NetworkView v-else-if="view === VIEWS.NETWORK" />
|
|
<ProfileView v-else-if="view === VIEWS.PROFILE" />
|
|
<ServicesView v-else-if="view === VIEWS.SERVICES" />
|
|
<SystemLocaleView v-else-if="view === VIEWS.SYSTEM_LOCALE" />
|
|
<SystemDockerView v-else-if="view === VIEWS.SYSTEM_DOCKER" />
|
|
<SystemUpdateView v-else-if="view === VIEWS.SYSTEM_UPDATE" />
|
|
<UserDirectorySettingsView v-else-if="view === VIEWS.USER_DIRECTORY_SETTINGS" />
|
|
<UserDirectoryLdapServerView v-else-if="view === VIEWS.USER_DIRECTORY_LDAP_SERVER" />
|
|
<UserDirectoryOpenIdProviderView v-else-if="view === VIEWS.USER_DIRECTORY_OPENID_PROVIDER" />
|
|
<UsersView v-else-if="view === VIEWS.USERS" />
|
|
<GroupsView v-else-if="view === VIEWS.GROUPS" />
|
|
<VolumesView v-else-if="view === VIEWS.VOLUMES" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
|
|
.pankow-sidebar {
|
|
background-color: var(--navbar-background);
|
|
padding: 22px 10px 10px 10px;
|
|
margin-right: 20px;
|
|
/* width is optimized for english */
|
|
min-width: 250px;
|
|
}
|
|
|
|
.sidebar-logo img {
|
|
margin-right: 10px;
|
|
border-radius: var(--pankow-border-radius);
|
|
}
|
|
|
|
.sidebar-logo,
|
|
.sidebar-logo:hover {
|
|
display: flex;
|
|
align-items: center;
|
|
color: var(--pankow-text-color);
|
|
text-decoration: none;
|
|
padding-left: 10px;
|
|
}
|
|
|
|
.sidebar-list {
|
|
overflow: auto;
|
|
padding-top: 25px;
|
|
scrollbar-color: transparent transparent;
|
|
scrollbar-width: thin;
|
|
}
|
|
|
|
.sidebar-list:hover {
|
|
scrollbar-color: var(--color-neutral-border) transparent;
|
|
}
|
|
|
|
.sidebar-item {
|
|
display: block;
|
|
color: var(--pankow-text-color);
|
|
border-radius: 3px;
|
|
padding: 10px 15px;
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
transition: all 180ms ease-out;
|
|
}
|
|
|
|
.sidebar-item i {
|
|
opacity: 0.5;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.sidebar-item.active {
|
|
color: var(--pankow-color-primary);
|
|
text-decoration: none;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.sidebar-item:hover {
|
|
background-color: #e9ecef;
|
|
text-decoration: none;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
.sidebar-item:hover {
|
|
background-color: var(--card-background);
|
|
}
|
|
}
|
|
|
|
.sidebar-item.active i ,
|
|
.sidebar-item:hover i {
|
|
opacity: 1;
|
|
}
|
|
|
|
.sidebar-item-group {
|
|
padding-left: 20px;
|
|
height: auto;
|
|
overflow: hidden;
|
|
/* we need height to auto so we animate max-height. needs to be bigger than we need */
|
|
max-height: 300px;
|
|
}
|
|
|
|
.sidebar-item-group-animation-enter-active,
|
|
.sidebar-item-group-animation-leave-active {
|
|
transition: all 0.2s linear;
|
|
}
|
|
|
|
.sidebar-item-group-animation-leave-to,
|
|
.sidebar-item-group-animation-enter-from {
|
|
transform: translateX(-100px);
|
|
opacity: 0;
|
|
max-height: 0;
|
|
}
|
|
|
|
.slide-fade-enter-active {
|
|
transition: all 0.1s ease-out;
|
|
}
|
|
|
|
.slide-fade-leave-active {
|
|
transition: all 0.1s ease-out;
|
|
}
|
|
|
|
.slide-fade-enter-from,
|
|
.slide-fade-leave-to {
|
|
transform: translateX(20px);
|
|
opacity: 0;
|
|
}
|
|
|
|
|
|
</style>
|