mirror of https://github.com/Lissy93/dashy.git
🎉 Add Nextcloud widget
Add a widget supporting the `capabilites`, `user` and `serverinfo` Nextcloud APIs. Basic branding, user and quota information is always displayed and when the provided credentials are for and admin user then server information is also displayed. APIs: * [capabilities](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html#capabilities-api) * [user](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html#user-metadata) * [serverinfo](https://github.com/nextcloud/serverinfo)
This commit is contained in:
parent
e24fa10f0f
commit
0bf6fee180
|
@ -303,6 +303,10 @@
|
||||||
"remaining": "Remaining",
|
"remaining": "Remaining",
|
||||||
"up": "Up",
|
"up": "Up",
|
||||||
"down": "Down"
|
"down": "Down"
|
||||||
|
},
|
||||||
|
"nextcloud-info": {
|
||||||
|
"label-version": "Nextcloud version",
|
||||||
|
"label-last-login": "Last login"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,398 @@
|
||||||
|
<template>
|
||||||
|
<div class="nextcloud-info-wrapper">
|
||||||
|
<div>
|
||||||
|
<div class="logo">
|
||||||
|
<a :href="branding.url" target="_blank">
|
||||||
|
<img :src="branding.logo" />
|
||||||
|
</a>
|
||||||
|
<p>{{ branding.slogan }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="info">
|
||||||
|
<p class="brand">{{ branding.name }}</p>
|
||||||
|
<p class="version" v-if="version.string">
|
||||||
|
{{ $t('widgets.nextcloud-info.label-version') }} {{ version.string }}
|
||||||
|
</p>
|
||||||
|
<p class="username">{{ user.displayName }} <em v-if="user.id">({{ user.id }})</em></p>
|
||||||
|
<p class="login" v-tooltip="lastLoginTooltip()">
|
||||||
|
{{ $t('widgets.nextcloud-info.label-last-login') }}
|
||||||
|
<small>{{ getTimeAgo(user.lastLogin) }}</small>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="user.quota.quota > 0" v-tooltip="quotaTooltip()">
|
||||||
|
<p>
|
||||||
|
<i class="fal fa-disc-drive"></i>
|
||||||
|
<strong>{{ $t('Disk Quota') }}</strong>
|
||||||
|
<em>{{ user.quota.relative }}%</em>
|
||||||
|
<small>of</small> <strong>{{ convertBytes(user.quota.total) }}</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div v-if="user.isAdmin" class="server-info">
|
||||||
|
<div>
|
||||||
|
<p v-tooltip="activeUsersTooltip()">
|
||||||
|
<i class="fal fa-user"></i>
|
||||||
|
<em>{{ formatNumber(server.nextcloud.storage.num_users) }}</em>
|
||||||
|
<strong>{{ $t('total users') }}</strong> <small>{{ $t('of which') }}</small>
|
||||||
|
<em>{{ formatNumber(server.activeUsers.last24hours) }}</em>
|
||||||
|
<strong>{{ $t('active') }}</strong> <small>({{ $t('last 24 hours') }})</small>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<i class="fal fa-browser"></i>
|
||||||
|
<em>{{ formatNumber(server.nextcloud.system.apps.num_installed) }}</em>
|
||||||
|
<strong>{{ $t('applications') }}</strong>
|
||||||
|
<span v-if="server.nextcloud.system.apps.num_updates_available"
|
||||||
|
data-has-updates v-tooltip="appUpdatesTooltip()">
|
||||||
|
<i class="fal fa-download"></i>
|
||||||
|
<em>{{ server.nextcloud.system.apps.num_updates_available }}</em>
|
||||||
|
<strong>{{ $t('updates available') }}</strong>
|
||||||
|
</span>
|
||||||
|
<span v-else >
|
||||||
|
{{ $t('no pending updates') }}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<hr />
|
||||||
|
<p v-tooltip="storagesTooltip()">
|
||||||
|
<i class="fal fa-file"></i>
|
||||||
|
<em>{{ formatNumber(server.nextcloud.storage.num_files) }}</em>
|
||||||
|
<strong>{{ $t('files') }}</strong> <small>{{ $t('in') }}</small>
|
||||||
|
<em>{{ server.nextcloud.storage.num_storages }}</em>
|
||||||
|
<strong>{{ $t('storages') }}</strong> |
|
||||||
|
<strong>{{ convertBytes(server.nextcloud.system.freespace) }}</strong>
|
||||||
|
<small>{{ $t('free') }}</small>
|
||||||
|
</p>
|
||||||
|
<hr />
|
||||||
|
<p v-tooltip="sharesTooltip()">
|
||||||
|
<i class="fal fa-share"></i>
|
||||||
|
<em>{{ formatNumber(server.nextcloud.shares.num_shares) }}</em>
|
||||||
|
<strong>{{ $t('shares') }}</strong> <small> {{ $t('and') }}</small>
|
||||||
|
<em>
|
||||||
|
{{ formatNumber(server.nextcloud.shares.num_fed_shares_sent) }}
|
||||||
|
/ {{ formatNumber(server.nextcloud.shares.num_fed_shares_received) }}
|
||||||
|
</em>
|
||||||
|
<strong>{{ $t('federated shares') }}</strong>
|
||||||
|
</p>
|
||||||
|
<hr />
|
||||||
|
<p>
|
||||||
|
<i class="fal fa-server"></i>
|
||||||
|
<strong>{{ $t('Nextcloud') }}</strong>
|
||||||
|
<em>{{ server.nextcloud.system.version }}</em> |
|
||||||
|
<strong>{{ server.server.webserver }}/PHP</strong>
|
||||||
|
<em>{{ server.server.php.version }}</em>
|
||||||
|
</p>
|
||||||
|
<hr />
|
||||||
|
<p>
|
||||||
|
<i class="fal fa-database"></i>
|
||||||
|
<strong>{{ server.server.database.type }}</strong>
|
||||||
|
<em>{{ server.server.database.version }}</em> <small>{{ $t('using') }}</small>
|
||||||
|
<em>{{ convertBytes(server.server.database.size) }}</em>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import WidgetMixin from '@/mixins/WidgetMixin';
|
||||||
|
import NextcloudMixin from '@/mixins/NextcloudMixin';
|
||||||
|
import { convertBytes } from '@/utils/MiscHelpers';
|
||||||
|
// //import { NcdUsr, NcdServer } from '@/utils/ncd';
|
||||||
|
|
||||||
|
const NextcloudSchema = {
|
||||||
|
branding: {
|
||||||
|
name: null,
|
||||||
|
logo: null,
|
||||||
|
url: null,
|
||||||
|
slogan: null,
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
string: null,
|
||||||
|
edition: null,
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
id: null,
|
||||||
|
isAdmin: false,
|
||||||
|
displayName: null,
|
||||||
|
email: null,
|
||||||
|
quota: {
|
||||||
|
relative: null,
|
||||||
|
total: null,
|
||||||
|
used: null,
|
||||||
|
free: null,
|
||||||
|
quota: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
server: {
|
||||||
|
webserver: null,
|
||||||
|
php: {
|
||||||
|
version: null,
|
||||||
|
},
|
||||||
|
opCache: {
|
||||||
|
enabled: false,
|
||||||
|
full: false,
|
||||||
|
stats: {
|
||||||
|
num_cached_scripts: null,
|
||||||
|
num_cached_keys: null,
|
||||||
|
max_cached_keys: null,
|
||||||
|
hits: null,
|
||||||
|
start_time: null,
|
||||||
|
last_restart_time: 0,
|
||||||
|
misses: null,
|
||||||
|
opcache_hit_rate: null,
|
||||||
|
},
|
||||||
|
memory: {
|
||||||
|
used_memory: null,
|
||||||
|
free_memory: null,
|
||||||
|
wasted_memory: null,
|
||||||
|
current_wasted_percentage: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
database: {
|
||||||
|
type: null,
|
||||||
|
version: null,
|
||||||
|
size: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nextcloud: {
|
||||||
|
system: {
|
||||||
|
version: null,
|
||||||
|
freespace: null,
|
||||||
|
cpuload: [],
|
||||||
|
mem_total: null,
|
||||||
|
mem_free: null,
|
||||||
|
apps: {
|
||||||
|
num_installed: null,
|
||||||
|
num_updates_available: 0,
|
||||||
|
app_updates: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
storage: {
|
||||||
|
num_users: null,
|
||||||
|
num_files: null,
|
||||||
|
num_storages: null,
|
||||||
|
},
|
||||||
|
shares: {
|
||||||
|
num_shares: null,
|
||||||
|
num_shares_user: null,
|
||||||
|
num_shares_groups: null,
|
||||||
|
num_shares_link: null,
|
||||||
|
num_shares_mail: null,
|
||||||
|
num_shares_room: null,
|
||||||
|
num_shares_link_no_password: null,
|
||||||
|
num_fed_shares_sent: null,
|
||||||
|
num_fed_shares_received: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
activeUsers: {
|
||||||
|
last5minutes: null,
|
||||||
|
last1hour: null,
|
||||||
|
last24hours: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [WidgetMixin, NextcloudMixin],
|
||||||
|
components: {},
|
||||||
|
data() {
|
||||||
|
return NextcloudSchema;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchData() {
|
||||||
|
const promise = this.fetchCapabilities()
|
||||||
|
.then(() => this.makeRequest(this.endpoint('user'), this.headers))
|
||||||
|
// //.then(() => NcdUsr)
|
||||||
|
.then(this.processUser);
|
||||||
|
|
||||||
|
await promise;
|
||||||
|
|
||||||
|
if (this.user.isAdmin) {
|
||||||
|
promise.then(() => this.makeRequest(this.endpoint('serverinfo'), this.headers))
|
||||||
|
// //promise.then(() => NcdServer)
|
||||||
|
.then(this.processServerInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.finally(() => this.finishLoading());
|
||||||
|
},
|
||||||
|
processUser(userData) {
|
||||||
|
const user = userData?.ocs?.data;
|
||||||
|
if (!user) {
|
||||||
|
this.error('Invalid response');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.user.id = user.id;
|
||||||
|
this.user.email = user.email;
|
||||||
|
this.user.quota = user.quota;
|
||||||
|
this.user.displayName = user.displayname;
|
||||||
|
this.user.lastLogin = user.lastLogin;
|
||||||
|
this.user.isAdmin = user.groups && user.groups.includes('admin');
|
||||||
|
},
|
||||||
|
processServerInfo(serverData) {
|
||||||
|
const data = serverData?.ocs?.data;
|
||||||
|
if (!data) {
|
||||||
|
this.error('Invalid response');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.server.nextcloud = data?.nextcloud;
|
||||||
|
this.server.server.php.version = data?.server?.php?.version;
|
||||||
|
this.server.server.opCache.enabled = data?.server?.php?.opcache?.opcache_enabled;
|
||||||
|
this.server.server.opCache.full = data?.server?.php?.opcache?.cache_full;
|
||||||
|
this.server.server.opCache.stats = data?.server?.php?.opcache?.opcache_statistics;
|
||||||
|
this.server.server.database = data?.server?.database;
|
||||||
|
this.server.server.webserver = data?.server?.webserver;
|
||||||
|
this.server.activeUsers = data?.activeUsers;
|
||||||
|
},
|
||||||
|
quotaTooltip() {
|
||||||
|
const content = `${convertBytes(this.user.quota.used)} used<br>`
|
||||||
|
+ `${convertBytes(this.user.quota.free)} free<br>`
|
||||||
|
+ `${convertBytes(this.user.quota.total)} total`;
|
||||||
|
return {
|
||||||
|
content, html: true, trigger: 'hover focus', delay: 250, classes: 'nc-user-quota',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
lastLoginTooltip() {
|
||||||
|
const content = new Date(this.user.lastLogin).toLocaleString();
|
||||||
|
return {
|
||||||
|
content, html: true, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
activeUsersTooltip() {
|
||||||
|
const content = `${this.server.activeUsers.last5minutes} in the last 5 minutes<br>`
|
||||||
|
+ `${this.server.activeUsers.last1hour} in the last 1 hour<br>`;
|
||||||
|
return {
|
||||||
|
content, html: true, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
appUpdatesTooltip() {
|
||||||
|
const content = 'Updates are available for: '
|
||||||
|
+ ` ${Object.keys(this.server.nextcloud.system.apps.app_updates).join(', ')}`;
|
||||||
|
return {
|
||||||
|
content, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
storagesTooltip() {
|
||||||
|
const content = 'Storages by type:<br><br>'
|
||||||
|
+ `${this.server.nextcloud.storage.num_storages_local} local<br>`
|
||||||
|
+ `${this.server.nextcloud.storage.num_storages_home} home<br>`
|
||||||
|
+ `${this.server.nextcloud.storage.num_storages_other} other<br><br>`
|
||||||
|
+ `${this.server.nextcloud.storage.num_files} files`;
|
||||||
|
return {
|
||||||
|
content, html: true, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
sharesTooltip() {
|
||||||
|
const content = 'Autonomous shares:<br><br>'
|
||||||
|
+ `${this.server.nextcloud.shares.num_shares_user} user<br>`
|
||||||
|
+ `${this.server.nextcloud.shares.num_shares_groups} groups<br>`
|
||||||
|
+ `${this.server.nextcloud.shares.num_shares_mail} email<br>`
|
||||||
|
+ `${this.server.nextcloud.shares.num_shares_room} chat room<br>`
|
||||||
|
+ `${this.server.nextcloud.shares.num_shares_link} private link<br>`
|
||||||
|
+ `${this.server.nextcloud.shares.num_shares_link_no_password} public link<br>`
|
||||||
|
+ '<br><sup>*</sup>Federated shares: sent/received';
|
||||||
|
return {
|
||||||
|
content, html: true, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.overrideUpdateInterval = 120;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.nextcloud-info-wrapper {
|
||||||
|
> div:first-child {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
> div:not(:first-child) {
|
||||||
|
border-top: 1px dashed var(--widget-text-color);
|
||||||
|
width: 96%;
|
||||||
|
padding: .4rem 0;
|
||||||
|
margin: auto;
|
||||||
|
> div:not(:first-child) {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
color: var(--widget-text-color);
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
> div:first-child {
|
||||||
|
min-height: 8em;
|
||||||
|
}
|
||||||
|
p i {
|
||||||
|
font-size: 110%;
|
||||||
|
min-width: 18px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
p em {
|
||||||
|
font-size: 110%;
|
||||||
|
margin: 0 4px;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
strong {
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 105%;
|
||||||
|
margin-left: .25rem;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
color: var(--widget-text-color);
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid;
|
||||||
|
margin-top: 0.8rem;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
opacity: .25;
|
||||||
|
}
|
||||||
|
div.logo {
|
||||||
|
width: 40%;
|
||||||
|
text-align: center;
|
||||||
|
img {
|
||||||
|
width: 8rem;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
font-size: 90%;
|
||||||
|
opacity: .85;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div.info {
|
||||||
|
width: 56%;
|
||||||
|
p {
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
}
|
||||||
|
p.brand {
|
||||||
|
margin: 0 0 .23rem 0;
|
||||||
|
font-size: 135%;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
}
|
||||||
|
p.version {
|
||||||
|
font-size: 80%;
|
||||||
|
opacity: .66;
|
||||||
|
}
|
||||||
|
p.username {
|
||||||
|
font-size: 110%;
|
||||||
|
em {
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.login {
|
||||||
|
font-size: 90%;
|
||||||
|
small {
|
||||||
|
opacity: .75;
|
||||||
|
margin-left: .25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div.server-info {
|
||||||
|
span[data-has-updates] {
|
||||||
|
color: var(--success);
|
||||||
|
padding-left: .75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -321,6 +321,13 @@
|
||||||
@error="handleError"
|
@error="handleError"
|
||||||
:ref="widgetRef"
|
:ref="widgetRef"
|
||||||
/>
|
/>
|
||||||
|
<NextcloudInfo
|
||||||
|
v-else-if="widgetType === 'nextcloud-info'"
|
||||||
|
:options="widgetOptions"
|
||||||
|
@loading="setLoaderState"
|
||||||
|
@error="handleError"
|
||||||
|
:ref="widgetRef"
|
||||||
|
/>
|
||||||
<PiHoleStats
|
<PiHoleStats
|
||||||
v-else-if="widgetType === 'pi-hole-stats'"
|
v-else-if="widgetType === 'pi-hole-stats'"
|
||||||
:options="widgetOptions"
|
:options="widgetOptions"
|
||||||
|
@ -499,6 +506,7 @@ export default {
|
||||||
NdLoadHistory: () => import('@/components/Widgets/NdLoadHistory.vue'),
|
NdLoadHistory: () => import('@/components/Widgets/NdLoadHistory.vue'),
|
||||||
NdRamHistory: () => import('@/components/Widgets/NdRamHistory.vue'),
|
NdRamHistory: () => import('@/components/Widgets/NdRamHistory.vue'),
|
||||||
NewsHeadlines: () => import('@/components/Widgets/NewsHeadlines.vue'),
|
NewsHeadlines: () => import('@/components/Widgets/NewsHeadlines.vue'),
|
||||||
|
NextcloudInfo: () => import('@/components/Widgets/NextcloudInfo.vue'),
|
||||||
PiHoleStats: () => import('@/components/Widgets/PiHoleStats.vue'),
|
PiHoleStats: () => import('@/components/Widgets/PiHoleStats.vue'),
|
||||||
PiHoleTopQueries: () => import('@/components/Widgets/PiHoleTopQueries.vue'),
|
PiHoleTopQueries: () => import('@/components/Widgets/PiHoleTopQueries.vue'),
|
||||||
PiHoleTraffic: () => import('@/components/Widgets/PiHoleTraffic.vue'),
|
PiHoleTraffic: () => import('@/components/Widgets/PiHoleTraffic.vue'),
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { serviceEndpoints } from '@/utils/defaults';
|
||||||
|
import { convertBytes, formatNumber, getTimeAgo } from '@/utils/MiscHelpers';
|
||||||
|
// //import { NcdCap } from '@/utils/ncd';
|
||||||
|
|
||||||
|
/** Reusable mixin for Nextcloud widgets */
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
capabilities: {
|
||||||
|
notifications: null,
|
||||||
|
activity: null,
|
||||||
|
},
|
||||||
|
capabilitiesLastUpdated: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hostname() {
|
||||||
|
if (!this.options.hostname) this.error('A hostname is required');
|
||||||
|
return this.options.hostname;
|
||||||
|
},
|
||||||
|
username() {
|
||||||
|
if (!this.options.username) this.error('A username is required');
|
||||||
|
return this.options.username;
|
||||||
|
},
|
||||||
|
password() {
|
||||||
|
if (!this.options.password) this.error('An app-password is required');
|
||||||
|
return this.options.password;
|
||||||
|
},
|
||||||
|
headers() {
|
||||||
|
return {
|
||||||
|
'OCS-APIREQUEST': true,
|
||||||
|
Accept: 'application/json',
|
||||||
|
Authorization: `Basic ${window.btoa(`${this.username}:${this.password}`)}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
proxyReqEndpoint() {
|
||||||
|
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
|
||||||
|
return `${baseUrl}${serviceEndpoints.corsProxy}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
endpoint(id) {
|
||||||
|
const endpoints = {
|
||||||
|
capabilities: `${this.hostname}/ocs/v1.php/cloud/capabilities`,
|
||||||
|
user: `${this.hostname}/ocs/v1.php/cloud/users/${this.username}`,
|
||||||
|
serverinfo: `${this.hostname}/ocs/v2.php/apps/serverinfo/api/v1/info`,
|
||||||
|
};
|
||||||
|
return endpoints[id];
|
||||||
|
},
|
||||||
|
fetchCapabilities() {
|
||||||
|
const promise = Promise.resolve();
|
||||||
|
if ((new Date().getTime()) - this.capabilitiesLastUpdated > 3600000) {
|
||||||
|
promise.then(() => this.makeRequest(this.endpoint('capabilities'), this.headers))
|
||||||
|
// //promise.then(() => NcdCap)
|
||||||
|
.then(this.processCapabilities);
|
||||||
|
}
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
processCapabilities(data) {
|
||||||
|
const ocdata = data?.ocs?.data;
|
||||||
|
if (!ocdata) {
|
||||||
|
this.error('Invalid response');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.branding = ocdata?.capabilities?.theming;
|
||||||
|
this.capabilities.notifications = ocdata?.capabilities?.notifications?.['ocs-endpoints'];
|
||||||
|
this.capabilities.activity = ocdata?.capabilities?.activity?.apiv2;
|
||||||
|
this.version.string = ocdata?.version?.string;
|
||||||
|
this.version.edition = ocdata?.version?.edition;
|
||||||
|
this.capabilitiesLastUpdated = new Date().getTime();
|
||||||
|
},
|
||||||
|
formatNumber(number) {
|
||||||
|
return formatNumber(number);
|
||||||
|
},
|
||||||
|
convertBytes(bytes) {
|
||||||
|
return convertBytes(bytes);
|
||||||
|
},
|
||||||
|
getTimeAgo(time) {
|
||||||
|
return getTimeAgo(time);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
|
@ -105,6 +105,15 @@ export const convertBytes = (bytes, decimals = 2) => {
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
return `${parseFloat((bytes / (k ** i)).toFixed(decimals))} ${sizes[i]}`;
|
return `${parseFloat((bytes / (k ** i)).toFixed(decimals))} ${sizes[i]}`;
|
||||||
};
|
};
|
||||||
|
/* Returns a numbers shortened version with suffixes for thousand, million, billion
|
||||||
|
and trillion, e.g. 105_411 => 105.4K, 4_294_967_295 => 4.3B */
|
||||||
|
export const formatNumber = (number) => {
|
||||||
|
if (number > -1000 && number < 1000) return number;
|
||||||
|
const k = 1000;
|
||||||
|
const units = ['', 'K', 'M', 'B', 'T'];
|
||||||
|
const i = Math.floor(Math.log(number) / Math.log(k));
|
||||||
|
return `${(number / (k ** i)).toFixed(1)}${units[i]}`;
|
||||||
|
};
|
||||||
|
|
||||||
/* Round price to appropriate number of decimals */
|
/* Round price to appropriate number of decimals */
|
||||||
export const roundPrice = (price) => {
|
export const roundPrice = (price) => {
|
||||||
|
|
Loading…
Reference in New Issue