Merge branch 'Lissy93:master' into BUG/1608_glances-network-error

This commit is contained in:
hockwill
2024-08-19 17:53:23 -04:00
committed by GitHub
16 changed files with 1184 additions and 536 deletions

View File

@@ -34,6 +34,8 @@
:statusSuccess="statusResponse ? statusResponse.successStatus : undefined"
:statusText="statusResponse ? statusResponse.message : undefined"
/>
<!-- URL of the item (shown on hover, only on some themes) -->
<p class="item-url">{{ item.url | shortUrl }}</p>
<!-- Edit icon (displayed only when in edit mode) -->
<EditModeIcon v-if="isEditMode" class="edit-mode-item" @click="openItemSettings()" />
</a>
@@ -122,6 +124,26 @@ export default {
}
},
},
filters: {
shortUrl(value) {
if (!value || typeof value !== 'string') {
return '';
}
try {
// Use URL constructor to parse the input
const url = new URL(value);
return url.hostname;
} catch (e) {
// If the input is not a valid URL, try to handle it as an IP address
const ipPattern = /^(\d{1,3}\.){3}\d{1,3}/;
const match = value.match(ipPattern);
if (match) {
return match[0];
}
return '';
}
},
},
data() {
return {
editMenuOpen: false,
@@ -209,6 +231,9 @@ export default {
&.span-7 { min-width: 14%; }
&.span-8 { min-width: 12.5%; }
}
.item-url {
display: none;
}
}
.item {

View File

@@ -24,6 +24,8 @@ export default {
endpoint() {
if (this.provider === 'ipgeolocation') {
return `${widgetApiEndpoints.publicIp2}?apiKey=${this.apiKey}`;
} else if (this.provider === 'ip2location.io') {
return `${widgetApiEndpoints.publicIp4}?key=${this.apiKey}`;
} else if (this.provider === 'ipapi') {
return widgetApiEndpoints.publicIp3;
}
@@ -31,10 +33,11 @@ export default {
},
apiKey() {
if (this.provider === 'ipgeolocation' && !this.options.apiKey) this.error('Missing API Key');
if (this.provider === 'ip2location.io' && !this.options.apiKey) this.error('Missing API Key');
return this.options.apiKey;
},
provider() {
// Can be either `ip-api`, `ipapi.co` or `ipgeolocation`
// Can be either `ip-api`, `ipapi.co`, `ipgeolocation` or `ip2location.io`
return this.parseAsEnvVar(this.options.provider) || 'ipapi.co';
},
},
@@ -72,6 +75,12 @@ export default {
this.location = `${ipInfo.city}, ${ipInfo.regionName}`;
this.flagImg = getCountryFlag(ipInfo.countryCode);
this.mapsUrl = getMapUrl({ lat: ipInfo.lat, lon: ipInfo.lon });
} else if (this.provider === 'ip2location.io') {
this.ipAddr = ipInfo.ip;
this.ispName = ipInfo.isp || 'IP2Location.io Starter plan or higher required.';
this.location = `${ipInfo.city_name}, ${ipInfo.region_name}`;
this.flagImg = getCountryFlag(ipInfo.country_code);
this.mapsUrl = getMapUrl({ lat: ipInfo.latitude, lon: ipInfo.longitude });
} else {
this.error('Unknown API provider fo IP address');
}

View File

@@ -0,0 +1,227 @@
<template>
<div class="status-section">
<template v-if="statusData">
<div class="status-wrapper">
<div class="status-item">
<div class="title">Version</div>
<div class="value">{{ statusData.version }}</div>
</div>
<div class="status-item">
<div class="title">Agent Count</div>
<div class="value">{{ statusData.agent_count }}</div>
</div>
<div class="status-item">
<div class="title">Client Count</div>
<div class="value">{{ statusData.client_count }}</div>
</div>
<div class="status-item">
<div class="title">Site Count</div>
<div class="value">{{ statusData.site_count }}</div>
</div>
<div class="status-item">
<div class="title">Disk Usage</div>
<div class="value">{{ statusData.disk_usage_percent }}%</div>
</div>
<div class="status-item">
<div class="title">Memory Usage</div>
<div class="value">{{ statusData.mem_usage_percent }}%</div>
</div>
<div class="status-item">
<div class="title">Days Until Cert Expires</div>
<div class="value">{{ statusData.days_until_cert_expires }}</div>
</div>
<div class="status-item">
<div class="title">Cert Expired</div>
<div class="value">{{ statusData.cert_expired ? 'Yes' : 'No' }}</div>
</div>
<div class="status-item services">
<div class="title">Services Running</div>
<div class="services-list">
<div
v-for="(status, service) in statusData.services_running"
:key="service"
class="service"
>
<span class="service-name">{{ service }}</span>
<span :class="['service-status', status ? 'running' : 'stopped']">
{{ status ? 'Running' : 'Stopped' }}
</span>
</div>
</div>
</div>
</div>
</template>
<template v-if="errorMessage">
<div class="error-message">
<span class="text">{{ errorMessage }}</span>
</div>
</template>
</div>
</template>
<script>
import axios from 'axios';
import WidgetMixin from '@/mixins/WidgetMixin';
import { serviceEndpoints } from '@/utils/defaults';
export default {
mixins: [WidgetMixin],
props: {
options: {
type: Object,
default: () => ({}),
},
},
data() {
return {
statusData: null,
errorMessage: null,
errorMessageConstants: {
missingToken: 'No Token set',
missingUrl: 'No URL set',
},
};
},
mounted() {
this.fetchData();
},
computed: {
token() {
return this.parseAsEnvVar(this.options.token);
},
url() {
return this.parseAsEnvVar(this.options.url);
},
authHeaders() {
return {
'Content-Type': 'application/json',
};
},
proxyReqEndpoint() {
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
return `${baseUrl}${serviceEndpoints.corsProxy}`;
},
},
methods: {
update() {
this.startLoading();
this.fetchData();
},
fetchData() {
const {
authHeaders, url, token, proxyReqEndpoint,
} = this;
if (!this.optionsValid({ url, token })) {
return;
}
const targetURL = url;
const customHeaders = JSON.stringify(authHeaders);
axios.post(
proxyReqEndpoint,
{ auth: token },
{
headers: {
'Target-URL': targetURL,
CustomHeaders: customHeaders,
'Content-Type': 'application/json',
},
},
)
.then((response) => {
this.processData(response.data);
})
.catch(() => {
this.errorMessage = 'Failed to fetch data';
})
.finally(() => {
this.finishLoading();
});
},
processData(response) {
this.statusData = response;
},
optionsValid({ url, token }) {
const errors = [];
if (!url) {
errors.push(this.errorMessageConstants.missingUrl);
}
if (!token) {
errors.push(this.errorMessageConstants.missingToken);
}
if (errors.length === 0) {
return true;
}
this.errorMessage = errors.join('\n');
return false;
},
},
};
</script>
<style scoped lang="scss">
.status-section {
background-color: var(--item-background);
padding: 1em;
border-radius: 8px;
}
.status-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
color: var(--item-text-color);
}
.status-item {
width: 48%;
margin: 1em 0;
}
.title {
font-weight: bold;
color: var(--item-text-color);
}
.value {
margin-top: 0.5em;
color: var(--item-text-color);
}
.services-list {
display: flex;
flex-direction: column;
}
.service {
display: flex;
justify-content: space-between;
margin: 0.5em 0;
width: 100%;
}
.service-name {
font-weight: bold;
color: var(--item-text-color);
}
.service-status {
margin-left: 1em;
}
.service-status.running {
color: var(--success);
font-weight: bold;
}
.service-status.stopped {
color: var(--danger);
}
.error-message {
color: var(--item-text-color);
}
</style>

View File

@@ -46,7 +46,7 @@ const COMPAT = {
'adguard-filter-status': 'AdGuardFilterStatus',
'adguard-stats': 'AdGuardStats',
'adguard-top-domains': 'AdGuardTopDomains',
anonaddy: 'AnonAddy',
anonaddy: 'addy.io',
apod: 'Apod',
'blacklist-check': 'BlacklistCheck',
clock: 'Clock',
@@ -115,6 +115,7 @@ const COMPAT = {
'synology-download': 'SynologyDownload',
'system-info': 'SystemInfo',
'tfl-status': 'TflStatus',
trmm: 'TacticalRMM',
'uptime-kuma': 'UptimeKuma',
'wallet-balance': 'WalletBalance',
weather: 'Weather',

View File

@@ -1849,6 +1849,88 @@ html[data-theme='neomorphic'] {
}
html[data-theme="night-bat"] {
// Main colors
--primary: #4780ff;
--background: #252931;
--background-darker: #303540;
// Typography
--font-headings: 'Podkova', 'Roboto', serif;
--font-body: 'Roboto', serif;
--heading-text-color: #fff;
// Items
--item-background: #303540;
--item-background-hover: var(--item-background);
--item-shadow: 0px 3px 0px var(--primary), 2px 2px 6px var(--black);
--item-hover-shadow: 0px 20px 0px 0 var(--primary), 2px 2px 6px var(--black);
// Sections
--item-group-heading-text-color: var(--white);
--item-group-heading-text-color-hover: var(--white);
--item-group-shadow: none;
--item-group-background: none;
--item-group-outer-background: none;
// Nav Links
--nav-link-background-color: var(--background);
--nav-link-background-color-hover: var(--background);
--nav-link-border-color: transparent;
--nav-link-border-color-hover: transparent;
--nav-link-shadow: 4px 4px 0px var(--background-darker), -3px 0px 0px var(--primary), 2px 2px 6px var(--black);
--nav-link-shadow-hover: 6px 6px 0px var(--background-darker), -4px 0px 0px var(--primary), 2px 2px 9px var(--black);
// Misc
--curve-factor: 4px;
--curve-factor-navbar: 8px;
--widget-text-color: var(--white);
// Style overrides
label.lbl-toggle h3 { font-size: 1.3rem; font-weight: bold; }
.content-inner { border-top: 1px dashed var(--primary); }
.item.size-large .tile-title p.description { height: 3rem; }
.item, .nav-outer nav .nav-item { border-radius: 1px; }
.item.size-large { margin: 0.5rem; }
// Show outline when collapsed
.is-collapsed {
background: var(--item-background);
box-shadow: var(--item-shadow);
&:hover {
background: var(--item-background-hover);
box-shadow: var(--item-hover-shadow);
}
}
.widget-base {
background: var(--background-darker);
padding: 1rem 0.5rem;
margin: 0.5rem 0;
}
.item-wrapper {
.item-url {
display: block;
opacity: 0;
position: absolute;
bottom: -1.9rem;
font-size: 0.8rem;
color: var(--background);
transition: all 0.2s cubic-bezier(0.8, 0.8, 0.4, 1.4);
}
a {
transition: all 0.2s cubic-bezier(0.8, 0.8, 0.4, 1.4);
height: calc(100% - 1rem);
}
&:hover {
a { height: calc(100% - 2rem); }
.item-icon {
transform: scale(0.9);
}
.item-url {
display: block;
opacity: 1;
}
}
}
}
html[data-theme='cherry-blossom'] {
--primary: #e1e8ee;
--background: #11171d;
@@ -1999,7 +2081,7 @@ html[data-theme="tama"] {
// Background Image
body {
//update the query terms after the '?', to customize for images you want
background: url('https://source.unsplash.com/random/1920x1080/?dark,calm,nature,background');
background: url('https://picsum.photos/1920/1080');
background-color: var(--background-darker);
background-size: cover;
}

View File

@@ -565,7 +565,12 @@
"title": "OIDC Client Id",
"type": "string",
"description": "ClientId from OIDC provider"
}
},
"scope" : {
"title": "OIDC Scope",
"type": "string",
"description": "The scope(s) to request from the OIDC provider"
}
}
},
"enableHeaderAuth": {

View File

@@ -13,14 +13,14 @@ const getAppConfig = () => {
class OidcAuth {
constructor() {
const { auth } = getAppConfig();
const { clientId, endpoint } = auth.oidc;
const { clientId, endpoint, scope } = auth.oidc;
const settings = {
userStore: new WebStorageStateStore({ store: window.localStorage }),
authority: endpoint,
client_id: clientId,
redirect_uri: `${window.location.origin}`,
response_type: 'code',
scope: 'openid profile email roles groups',
scope: scope || 'openid profile email roles groups',
response_mode: 'query',
filterProtocolClaims: true,
};

View File

@@ -89,6 +89,7 @@ module.exports = {
'tama',
'neomorphic',
'glass-2',
'night-bat',
],
/* Default color options for the theme configurator swatches */
swatches: [
@@ -220,7 +221,7 @@ module.exports = {
},
/* API endpoints for widgets that need to fetch external data */
widgetApiEndpoints: {
anonAddy: 'https://app.anonaddy.com',
anonAddy: 'https://app.addy.io',
astronomyPictureOfTheDay: 'https://apod.as93.net/apod',
blacklistCheck: 'https://api.blacklistchecker.com/check',
codeStats: 'https://codestats.net/',
@@ -244,6 +245,7 @@ module.exports = {
publicIp: 'https://ipapi.co/json',
publicIp2: 'https://api.ipgeolocation.io/ipgeo',
publicIp3: 'http://ip-api.com/json',
publicIp4: 'https://api.ip2location.io/',
readMeStats: 'https://github-readme-stats.vercel.app/api',
rescueTime: 'https://www.rescuetime.com/anapi/data',
rssToJson: 'https://api.rss2json.com/v1/api.json',