🚧 Move user processing from widget to mixin

plus
* some template and style tweaking
* improve tooltips
* enforce Nextcloud app-password instead of login password
This commit is contained in:
Marcell Fülöp 2022-06-12 12:33:56 +00:00
parent caf131df23
commit a43988f3cd
2 changed files with 80 additions and 70 deletions

View File

@ -11,11 +11,11 @@
<div class="info"> <div class="info">
<p class="brand">{{ branding.name }}</p> <p class="brand">{{ branding.name }}</p>
<p class="version" v-if="version.string"> <p class="version" v-if="version.string">
{{ $t('widgets.nextcloud-info.label-version') }} {{ version.string }} <small>{{ $t('widgets.nextcloud-info.label-version') }} {{ version.string }}</small>
</p> </p>
<p class="username">{{ user.displayName }} <em v-if="user.id">({{ user.id }})</em></p> <p class="username">{{ user.displayName }} <em v-if="user.id">({{ user.id }})</em></p>
<p class="login" v-tooltip="lastLoginTooltip()"> <p class="login" v-tooltip="lastLoginTooltip()">
{{ $t('widgets.nextcloud-info.label-last-login') }}&nbsp; <span>{{ $t('widgets.nextcloud-info.label-last-login') }}</span>&nbsp;
<small>{{ getTimeAgo(user.lastLogin) }}</small> <small>{{ getTimeAgo(user.lastLogin) }}</small>
</p> </p>
</div> </div>
@ -76,10 +76,10 @@
<p v-tooltip="sharesTooltip()"> <p v-tooltip="sharesTooltip()">
<i class="fal fa-share"></i> <i class="fal fa-share"></i>
<em>{{ formatNumber(server.nextcloud.shares.num_shares) }}</em> <em>{{ formatNumber(server.nextcloud.shares.num_shares) }}</em>
<strong>{{ $t('shares') }}</strong> <small> {{ $t('and') }}</small> <strong>{{ $t('autonomous') }}</strong> <small> {{ $t('and') }}</small>
<em> <em>
{{ formatNumber(server.nextcloud.shares.num_fed_shares_sent) }} {{ formatNumber(server.nextcloud.shares.num_fed_shares_sent +
/ {{ formatNumber(server.nextcloud.shares.num_fed_shares_received) }} server.nextcloud.shares.num_fed_shares_received) }}
</em> </em>
<strong>{{ $t('federated shares') }}</strong> <strong>{{ $t('federated shares') }}</strong>
</p> </p>
@ -109,7 +109,7 @@
import WidgetMixin from '@/mixins/WidgetMixin'; import WidgetMixin from '@/mixins/WidgetMixin';
import NextcloudMixin from '@/mixins/NextcloudMixin'; import NextcloudMixin from '@/mixins/NextcloudMixin';
import { convertBytes } from '@/utils/MiscHelpers'; import { convertBytes } from '@/utils/MiscHelpers';
// //import { NcdUsr, NcdServer } from '@/utils/ncd'; // //import { NcdServer } from '@/utils/ncd';
const NextcloudSchema = { const NextcloudSchema = {
branding: { branding: {
@ -122,19 +122,6 @@ const NextcloudSchema = {
string: null, string: null,
edition: 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: {
server: { server: {
webserver: null, webserver: null,
@ -213,33 +200,15 @@ export default {
}, },
methods: { methods: {
async fetchData() { async fetchData() {
const promise = this.fetchCapabilities() await this.loadCapabilities();
.then(() => this.makeRequest(this.endpoint('user'), this.headers)) await this.loadUser();
// //.then(() => NcdUsr)
.then(this.processUser);
await promise;
if (this.user.isAdmin) { if (this.user.isAdmin) {
promise.then(() => this.makeRequest(this.endpoint('serverinfo'), this.headers)) this.processServerInfo(
// //promise.then(() => NcdServer) await this.makeRequest(this.endpoint('serverinfo'), this.headers),
.then(this.processServerInfo); // //NcdServer,
);
} }
this.finishLoading();
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) { processServerInfo(serverData) {
const data = serverData?.ocs?.data; const data = serverData?.ocs?.data;
@ -278,10 +247,10 @@ export default {
}; };
}, },
appUpdatesTooltip() { appUpdatesTooltip() {
const content = 'Updates are available for: ' const content = 'Updates are available for:<br><br>'
+ ` ${Object.keys(this.server.nextcloud.system.apps.app_updates).join(', ')}`; + ` ${Object.entries(this.server.nextcloud.system.apps.app_updates).join('<br>')}`;
return { return {
content, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip', content, html: true, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip',
}; };
}, },
storagesTooltip() { storagesTooltip() {
@ -302,7 +271,9 @@ export default {
+ `${this.server.nextcloud.shares.num_shares_room} chat room<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} private link<br>`
+ `${this.server.nextcloud.shares.num_shares_link_no_password} public link<br>` + `${this.server.nextcloud.shares.num_shares_link_no_password} public link<br>`
+ '<br><sup>*</sup>Federated shares: sent/received'; + '<br>Federated shares:<br><br>'
+ `${this.server.nextcloud.shares.num_fed_shares_sent} sent<br>`
+ `${this.server.nextcloud.shares.num_fed_shares_received} received<br>`;
return { return {
content, html: true, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip', content, html: true, trigger: 'hover focus', delay: 250, classes: 'nc-tooltip',
}; };
@ -351,6 +322,9 @@ export default {
font-size: 105%; font-size: 105%;
margin-left: .25rem; margin-left: .25rem;
} }
small {
opacity: .66;
}
hr { hr {
color: var(--widget-text-color); color: var(--widget-text-color);
border: none; border: none;
@ -376,14 +350,13 @@ export default {
margin: 0 0 1rem 0; margin: 0 0 1rem 0;
} }
p.brand { p.brand {
margin: 0 0 .23rem 0; margin: 0;
font-size: 135%; font-size: 135%;
font-weight: 800; font-weight: 800;
letter-spacing: 3px; letter-spacing: 3px;
} }
p.version { p.version small {
font-size: 80%; font-size: 75%;
opacity: .66;
} }
p.username { p.username {
font-size: 110%; font-size: 110%;
@ -392,17 +365,16 @@ export default {
} }
} }
p.login { p.login {
span {
font-size: 90%; font-size: 90%;
small { margin-right: .25rem;
opacity: .75;
margin-left: .25rem;
} }
} }
} }
div.server-info { div.server-info {
span[data-has-updates] { span[data-has-updates] {
color: var(--success); color: var(--success);
padding-left: .75rem; margin-left: 0.5rem;
} }
} }
} }

View File

@ -1,6 +1,6 @@
import { serviceEndpoints } from '@/utils/defaults'; import { serviceEndpoints } from '@/utils/defaults';
import { convertBytes, formatNumber, getTimeAgo } from '@/utils/MiscHelpers'; import { convertBytes, formatNumber, getTimeAgo } from '@/utils/MiscHelpers';
// //import { NcdCap } from '@/utils/ncd'; // //import { NcdCap, NcdUsr } from '@/utils/ncd';
/** Reusable mixin for Nextcloud widgets */ /** Reusable mixin for Nextcloud widgets */
export default { export default {
@ -11,6 +11,19 @@ export default {
activity: null, activity: null,
}, },
capabilitiesLastUpdated: 0, capabilitiesLastUpdated: 0,
user: {
id: null,
isAdmin: false,
displayName: null,
email: null,
quota: {
relative: null,
total: null,
used: null,
free: null,
quota: null,
},
},
}; };
}, },
computed: { computed: {
@ -24,6 +37,9 @@ export default {
}, },
password() { password() {
if (!this.options.password) this.error('An app-password is required'); if (!this.options.password) this.error('An app-password is required');
if (!/^([a-z0-9]{5}-){4}[a-z0-9]{5}$/i.test(this.options.password)) {
this.error('Please use an app-password for this widget, not your login password.');
}
return this.options.password; return this.options.password;
}, },
headers() { headers() {
@ -33,6 +49,9 @@ export default {
Authorization: `Basic ${window.btoa(`${this.username}:${this.password}`)}`, Authorization: `Basic ${window.btoa(`${this.username}:${this.password}`)}`,
}; };
}, },
capabilitiesTtl() {
return (parseInt(this.options.capabilitiesTtl, 10) || 3600) * 1000;
},
proxyReqEndpoint() { proxyReqEndpoint() {
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin; const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
return `${baseUrl}${serviceEndpoints.corsProxy}`; return `${baseUrl}${serviceEndpoints.corsProxy}`;
@ -40,21 +59,23 @@ export default {
}, },
methods: { methods: {
endpoint(id) { endpoint(id) {
const endpoints = { switch (id) {
capabilities: `${this.hostname}/ocs/v1.php/cloud/capabilities`, case 'capabilities':
user: `${this.hostname}/ocs/v1.php/cloud/users/${this.username}`, default:
serverinfo: `${this.hostname}/ocs/v2.php/apps/serverinfo/api/v1/info`, return `${this.hostname}/ocs/v1.php/cloud/capabilities`;
}; case 'user':
return endpoints[id]; return `${this.hostname}/ocs/v1.php/cloud/users/${this.username}`;
case 'serverinfo':
return `${this.hostname}/ocs/v2.php/apps/serverinfo/api/v1/info`;
}
}, },
fetchCapabilities() { loadCapabilities() {
const promise = Promise.resolve(); if ((new Date().getTime()) - this.capabilitiesLastUpdated > this.capabilitiesTtl) {
if ((new Date().getTime()) - this.capabilitiesLastUpdated > 3600000) { return this.makeRequest(this.endpoint('capabilities'), this.headers)
promise.then(() => this.makeRequest(this.endpoint('capabilities'), this.headers)) // //return Promise.resolve(NcdCap)
// //promise.then(() => NcdCap)
.then(this.processCapabilities); .then(this.processCapabilities);
} }
return promise; return Promise.resolve();
}, },
processCapabilities(data) { processCapabilities(data) {
const ocdata = data?.ocs?.data; const ocdata = data?.ocs?.data;
@ -69,6 +90,23 @@ export default {
this.version.edition = ocdata?.version?.edition; this.version.edition = ocdata?.version?.edition;
this.capabilitiesLastUpdated = new Date().getTime(); this.capabilitiesLastUpdated = new Date().getTime();
}, },
loadUser() {
return this.makeRequest(this.endpoint('user'), this.headers).then(this.processUser);
// //return Promise.resolve(NcdUsr).then(this.processUser);
},
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');
},
formatNumber(number) { formatNumber(number) {
return formatNumber(number); return formatNumber(number);
}, },