mirror of
https://github.com/gethomepage/homepage.git
synced 2025-12-05 21:47:48 +01:00
Compare commits
1 Commits
feature/60
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8887281246 |
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -38,4 +38,3 @@ What type of change does your PR introduce to Homepage?
|
||||
- [ ] If applicable, I have reviewed the [feature / enhancement](https://gethomepage.dev/more/development/#new-feature-guidelines) and / or [service widget guidelines](https://gethomepage.dev/more/development/#service-widget-guidelines).
|
||||
- [ ] I have checked that all code style checks pass using [pre-commit hooks](https://gethomepage.dev/more/development/#code-formatting-with-pre-commit-hooks) and [linting checks](https://gethomepage.dev/more/development/#code-linting).
|
||||
- [ ] If applicable, I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
|
||||
- [ ] In the description above I have disclosed the use of AI tools in the coding of this PR.
|
||||
|
||||
2
.github/workflows/crowdin.yml
vendored
2
.github/workflows/crowdin.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
|
||||
4
.github/workflows/docker-publish.yml
vendored
4
.github/workflows/docker-publish.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install python
|
||||
uses: actions/setup-python@v6
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
|
||||
6
.github/workflows/docs-publish.yml
vendored
6
.github/workflows/docs-publish.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Install python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
needs:
|
||||
- pre-commit
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
needs:
|
||||
- pre-commit
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- name: Configure Git Credentials
|
||||
run: |
|
||||
git config user.name github-actions[bot]
|
||||
|
||||
@@ -571,18 +571,3 @@ or per service widget (`services.yaml`) with:
|
||||
```
|
||||
|
||||
If either value is set to true, the error message will be hidden.
|
||||
|
||||
## Disable Search Engine Indexing
|
||||
|
||||
You can request that search engines not to index your Homepage instance by enabling the `disableIndexing` setting.
|
||||
|
||||
```yaml
|
||||
disableIndexing: true
|
||||
```
|
||||
|
||||
When enabled, this will:
|
||||
|
||||
- Disallow all crawlers in `robots.txt`
|
||||
- Add `<meta name="robots" content="noindex, nofollow">` tags to prevent indexing
|
||||
|
||||
By default this feature is disabled.
|
||||
|
||||
@@ -14,7 +14,7 @@ services:
|
||||
- 3000:3000
|
||||
volumes:
|
||||
- /path/to/config:/app/config # Make sure your local config directory exists
|
||||
- /var/run/docker.sock:/var/run/docker.sock # (optional) For docker integrations
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro # (optional) For docker integrations
|
||||
environment:
|
||||
HOMEPAGE_ALLOWED_HOSTS: gethomepage.dev # required, may need port. See gethomepage.dev/installation/#homepage_allowed_hosts
|
||||
```
|
||||
@@ -36,7 +36,7 @@ services:
|
||||
- 3000:3000
|
||||
volumes:
|
||||
- /path/to/config:/app/config # Make sure your local config directory exists
|
||||
- /var/run/docker.sock:/var/run/docker.sock # (optional) For docker integrations, see alternative methods
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro # (optional) For docker integrations, see alternative methods
|
||||
environment:
|
||||
HOMEPAGE_ALLOWED_HOSTS: gethomepage.dev # required, may need port. See gethomepage.dev/installation/#homepage_allowed_hosts
|
||||
PUID: $PUID
|
||||
|
||||
@@ -8,9 +8,6 @@ Learn more about [Crowdsec](https://crowdsec.net).
|
||||
See the [crowdsec docs](https://docs.crowdsec.net/docs/local_api/intro/#machines) for information about registering a machine,
|
||||
in most instances you can use the default credentials (`/etc/crowdsec/local_api_credentials.yaml`).
|
||||
|
||||
!!! note
|
||||
Without the `limit24h` option, the widget will fetch all alerts which is limited to 100 by the API to avoid performance issues.
|
||||
|
||||
Allowed fields: `["alerts", "bans"]`.
|
||||
|
||||
```yaml
|
||||
@@ -19,5 +16,4 @@ widget:
|
||||
url: http://crowdsechostorip:port
|
||||
username: localhost # machine_id in crowdsec
|
||||
password: password
|
||||
limit24h: true # optional, limits alerts to last 24h. Default: false
|
||||
```
|
||||
|
||||
@@ -14,6 +14,4 @@ widget:
|
||||
type: frigate
|
||||
url: http://frigate.host.or.ip:port
|
||||
enableRecentEvents: true # Optional, defaults to false
|
||||
username: username # optional
|
||||
password: password # optional
|
||||
```
|
||||
|
||||
@@ -15,7 +15,7 @@ See the [official docs](https://github.com/ghostfolio/ghostfolio#authorization-b
|
||||
|
||||
_Note that the Bearer token is valid for 6 months, after which a new one must be generated._
|
||||
|
||||
Allowed fields: `["gross_percent_today", "gross_percent_1y", "gross_percent_max", "net_worth"]`
|
||||
Allowed fields: `["gross_percent_today", "gross_percent_1y", "gross_percent_max"]`
|
||||
|
||||
```yaml
|
||||
widget:
|
||||
|
||||
@@ -12,17 +12,11 @@ Learn more about [Gluetun](https://github.com/qdm12/gluetun).
|
||||
Allowed fields: `["public_ip", "region", "country", "port_forwarded"]`.
|
||||
Default fields: `["public_ip", "region", "country"]`.
|
||||
|
||||
To setup authentication, follow [the official Gluetun documentation](https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md#authentication). Note that to use the api key method, you must add the route `GET /v1/publicip/ip` to the `routes` array in your Gluetun config.toml. Similarly, if you want to include the `port_forwarded` field, you must add the route `GET /v1/openvpn/portforwarded` (or `/v1/portforward`) to your Gluetun config.toml.
|
||||
|
||||
| Gluetun Version | Homepage Widget Version |
|
||||
| --------------- | ----------------------- |
|
||||
| < 3.40.1 | 1 (default) |
|
||||
| >= 3.40.1 | 2 |
|
||||
To setup authentication, follow [the official Gluetun documentation](https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md#authentication). Note that to use the api key method, you must add the route `GET /v1/publicip/ip` to the `routes` array in your Gluetun config.toml. Similarly, if you want to include the `port_forwarded` field, you must add the route `GET /v1/openvpn/portforwarded` to your Gluetun config.toml.
|
||||
|
||||
```yaml
|
||||
widget:
|
||||
type: gluetun
|
||||
url: http://gluetun.host.or.ip:port
|
||||
key: gluetunkey # Not required if /v1/publicip/ip endpoint is configured with `auth = none`
|
||||
version: 2 # optional, default is 1
|
||||
```
|
||||
|
||||
@@ -759,8 +759,7 @@
|
||||
"ghostfolio": {
|
||||
"gross_percent_today": "Today",
|
||||
"gross_percent_1y": "One year",
|
||||
"gross_percent_max": "All time",
|
||||
"net_worth": "Net Worth"
|
||||
"gross_percent_max": "All time"
|
||||
},
|
||||
"audiobookshelf": {
|
||||
"podcasts": "Podcasts",
|
||||
|
||||
@@ -14,8 +14,6 @@ export default function Error({ error }) {
|
||||
|
||||
if (typeof error === "string") {
|
||||
error = { message: error }; // eslint-disable-line no-param-reassign
|
||||
} else if (typeof error === "number") {
|
||||
error = { message: `Error ${error}` }; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
if (error?.data?.error) {
|
||||
|
||||
@@ -400,7 +400,6 @@ function Home({ initialSettings }) {
|
||||
"A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations."
|
||||
}
|
||||
/>
|
||||
{settings.disableIndexing && <meta name="robots" content="noindex, nofollow" />}
|
||||
{settings.base && <base href={settings.base} />}
|
||||
{settings.favicon ? (
|
||||
<>
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { getSettings } from "utils/config/config";
|
||||
|
||||
export async function getServerSideProps({ res }) {
|
||||
const settings = getSettings();
|
||||
const content = ["User-agent: *", !!settings.disableIndexing ? "Disallow: /" : "Allow: /"].join("\n");
|
||||
|
||||
res.setHeader("Content-Type", "text/plain");
|
||||
res.write(content);
|
||||
res.end();
|
||||
|
||||
return {
|
||||
props: {},
|
||||
};
|
||||
}
|
||||
|
||||
export default function RobotsTxt() {
|
||||
// placeholder component
|
||||
return null;
|
||||
}
|
||||
@@ -279,9 +279,6 @@ export function cleanServiceGroups(groups) {
|
||||
slugs,
|
||||
symbols,
|
||||
|
||||
// crowdsec
|
||||
limit24h,
|
||||
|
||||
// customapi
|
||||
mappings,
|
||||
display,
|
||||
@@ -476,10 +473,6 @@ export function cleanServiceGroups(groups) {
|
||||
if (defaultinterval) widget.defaultinterval = defaultinterval;
|
||||
}
|
||||
|
||||
if (limit24h !== undefined) {
|
||||
widget.limit24h = !!limit24h;
|
||||
}
|
||||
|
||||
if (type === "docker") {
|
||||
if (server) widget.server = server;
|
||||
if (container) widget.container = container;
|
||||
@@ -563,7 +556,6 @@ export function cleanServiceGroups(groups) {
|
||||
"speedtest",
|
||||
"wgeasy",
|
||||
"grafana",
|
||||
"gluetun",
|
||||
].includes(type)
|
||||
) {
|
||||
if (version) widget.version = parseInt(version, 10);
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function Component({ service }) {
|
||||
|
||||
const { widget } = service;
|
||||
|
||||
const { data: alerts, error: alertsError } = useWidgetAPI(widget, !!widget.limit24h ? "alerts24h" : "alerts");
|
||||
const { data: alerts, error: alertsError } = useWidgetAPI(widget, "alerts");
|
||||
const { data: bans, error: bansError } = useWidgetAPI(widget, "bans");
|
||||
|
||||
if (alertsError || bansError) {
|
||||
|
||||
@@ -9,9 +9,6 @@ const widget = {
|
||||
alerts: {
|
||||
endpoint: "alerts",
|
||||
},
|
||||
alerts24h: {
|
||||
endpoint: "alerts?limit=0&since=24h",
|
||||
},
|
||||
bans: {
|
||||
endpoint: "alerts?decision_type=ban&origin=crowdsec&has_active_decision=1",
|
||||
},
|
||||
|
||||
@@ -166,11 +166,7 @@ export default function Component({ service }) {
|
||||
refreshInterval: Math.max(1000, refreshInterval),
|
||||
});
|
||||
|
||||
// if mappings includes an error field and the data contains an error field then show data even if there is an error
|
||||
const mappingsIncludesError = Array.isArray(mappings) && mappings.find((mapping) => mapping.field === "error");
|
||||
const errorIsData = customData && typeof customData === "object" && "error" in customData;
|
||||
|
||||
if (customError && !(mappingsIncludesError && errorIsData)) {
|
||||
if (customError) {
|
||||
return <Container service={service} error={customError} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
import getServiceWidget from "utils/config/service-helpers";
|
||||
import createLogger from "utils/logger";
|
||||
import { asJson, formatApiCall, sanitizeErrorURL } from "utils/proxy/api-helpers";
|
||||
import { addCookieToJar } from "utils/proxy/cookie-jar";
|
||||
import { httpProxy } from "utils/proxy/http";
|
||||
import widgets from "widgets/widgets";
|
||||
|
||||
const proxyName = "frigateProxyHandler";
|
||||
const logger = createLogger(proxyName);
|
||||
|
||||
export default async function frigateProxyHandler(req, res, map) {
|
||||
const { group, service, endpoint, index } = req.query;
|
||||
|
||||
if (group && service) {
|
||||
const widget = await getServiceWidget(group, service, index);
|
||||
|
||||
if (!widgets?.[widget.type]?.api) {
|
||||
return res.status(403).json({ error: "Service does not support API calls" });
|
||||
}
|
||||
|
||||
if (widget) {
|
||||
const url = formatApiCall(widgets[widget.type].api, { endpoint, ...widget });
|
||||
|
||||
const params = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
};
|
||||
|
||||
let [status, , data] = await httpProxy(url, params);
|
||||
|
||||
if (status === 401 && widget.username && widget.password) {
|
||||
const loginUrl = `${widget.url}/api/login`;
|
||||
logger.debug("Attempting login to Frigate at %s", loginUrl);
|
||||
const [loginStatus, , , loginResponseHeaders] = await httpProxy(loginUrl, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ user: widget.username, password: widget.password }),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (loginStatus !== 200) {
|
||||
logger.error("HTTP Error %d calling %s", loginStatus, sanitizeErrorURL(loginUrl));
|
||||
return res.status(status).json({
|
||||
error: {
|
||||
message: `HTTP Error ${status} while trying to login to Frigate`,
|
||||
url: sanitizeErrorURL(url),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
addCookieToJar(url, loginResponseHeaders);
|
||||
// Retry original request with cookie set
|
||||
[status, , data] = await httpProxy(url, params);
|
||||
}
|
||||
|
||||
if (status >= 400) {
|
||||
logger.error("HTTP Error %d calling %s", status, sanitizeErrorURL(url));
|
||||
return res.status(status).json({
|
||||
error: {
|
||||
message: `HTTP Error ${status} from Frigate`,
|
||||
url: sanitizeErrorURL(url),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
data = asJson(data);
|
||||
|
||||
if (endpoint == "stats") {
|
||||
res.status(status).send({
|
||||
num_cameras: data?.cameras !== undefined ? Object.keys(data?.cameras).length : 0,
|
||||
uptime: data?.service?.uptime,
|
||||
version: data?.service.version,
|
||||
});
|
||||
} else if (endpoint == "events") {
|
||||
return res.status(status).send(
|
||||
data.slice(0, 5).map((event) => ({
|
||||
id: event.id,
|
||||
camera: event.camera,
|
||||
label: event.label,
|
||||
start_time: new Date(event.start_time * 1000),
|
||||
thumbnail: event.thumbnail,
|
||||
score: event.data.score,
|
||||
type: event.data.type,
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Invalid or missing proxy service type '%s' in group '%s'", service, group);
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
@@ -1,12 +1,37 @@
|
||||
import frigateProxyHandler from "./proxy";
|
||||
import { asJson } from "utils/proxy/api-helpers";
|
||||
import genericProxyHandler from "utils/proxy/handlers/generic";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/api/{endpoint}",
|
||||
proxyHandler: frigateProxyHandler,
|
||||
proxyHandler: genericProxyHandler,
|
||||
|
||||
mappings: {
|
||||
stats: { endpoint: "stats" },
|
||||
events: { endpoint: "events" },
|
||||
stats: {
|
||||
endpoint: "stats",
|
||||
map: (data) => {
|
||||
const jsonData = asJson(data);
|
||||
return {
|
||||
num_cameras: jsonData?.cameras !== undefined ? Object.keys(jsonData?.cameras).length : 0,
|
||||
uptime: jsonData?.service?.uptime,
|
||||
version: jsonData?.service.version,
|
||||
};
|
||||
},
|
||||
},
|
||||
events: {
|
||||
endpoint: "events",
|
||||
map: (data) =>
|
||||
asJson(data)
|
||||
.slice(0, 5)
|
||||
.map((event) => ({
|
||||
id: event.id,
|
||||
camera: event.camera,
|
||||
label: event.label,
|
||||
start_time: new Date(event.start_time * 1000),
|
||||
thumbnail: event.thumbnail,
|
||||
score: event.data.score,
|
||||
type: event.data.type,
|
||||
})),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -20,15 +20,13 @@ function getPerformancePercent(t, performanceRange) {
|
||||
export default function Component({ service }) {
|
||||
const { t } = useTranslation();
|
||||
const { widget } = service;
|
||||
const includeNetWorth = widget.fields?.includes("net_worth");
|
||||
|
||||
const { data: performanceToday, error: ghostfolioErrorToday } = useWidgetAPI(widget, "today");
|
||||
const { data: performanceYear, error: ghostfolioErrorYear } = useWidgetAPI(widget, "year");
|
||||
const { data: performanceMax, error: ghostfolioErrorMax } = useWidgetAPI(widget, "max");
|
||||
const { data: userInfo, error: ghostfolioErrorUserInfo } = useWidgetAPI(widget, includeNetWorth ? "userInfo" : "");
|
||||
|
||||
if (ghostfolioErrorToday || ghostfolioErrorYear || ghostfolioErrorMax || ghostfolioErrorUserInfo) {
|
||||
const finalError = ghostfolioErrorToday ?? ghostfolioErrorYear ?? ghostfolioErrorMax ?? ghostfolioErrorUserInfo;
|
||||
if (ghostfolioErrorToday || ghostfolioErrorYear || ghostfolioErrorMax) {
|
||||
const finalError = ghostfolioErrorToday ?? ghostfolioErrorYear ?? ghostfolioErrorMax;
|
||||
return <Container service={service} error={finalError} />;
|
||||
}
|
||||
|
||||
@@ -36,13 +34,12 @@ export default function Component({ service }) {
|
||||
return <Container service={service} error={performanceToday} />;
|
||||
}
|
||||
|
||||
if (!performanceToday || !performanceYear || !performanceMax || (includeNetWorth && !userInfo)) {
|
||||
if (!performanceToday || !performanceYear || !performanceMax) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="ghostfolio.gross_percent_today" />
|
||||
<Block label="ghostfolio.gross_percent_1y" />
|
||||
<Block label="ghostfolio.gross_percent_max" />
|
||||
{includeNetWorth && <Block label="ghostfolio.net_worth" />}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -52,12 +49,6 @@ export default function Component({ service }) {
|
||||
<Block label="ghostfolio.gross_percent_today" value={getPerformancePercent(t, performanceToday)} />
|
||||
<Block label="ghostfolio.gross_percent_1y" value={getPerformancePercent(t, performanceYear)} />
|
||||
<Block label="ghostfolio.gross_percent_max" value={getPerformancePercent(t, performanceMax)} />
|
||||
{includeNetWorth && (
|
||||
<Block
|
||||
label="ghostfolio.net_worth"
|
||||
value={`${performanceToday.performance.currentNetWorth.toFixed(2)} ${userInfo?.settings?.currency ?? ""}`}
|
||||
/>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/api/{endpoint}",
|
||||
api: "{url}/api/v2/portfolio/performance?range={endpoint}",
|
||||
proxyHandler: credentialedProxyHandler,
|
||||
|
||||
mappings: {
|
||||
today: {
|
||||
endpoint: "v2/portfolio/performance?range=1d",
|
||||
endpoint: "1d",
|
||||
},
|
||||
year: {
|
||||
endpoint: "v2/portfolio/performance?range=1y",
|
||||
endpoint: "1y",
|
||||
},
|
||||
max: {
|
||||
endpoint: "v2/portfolio/performance?range=max",
|
||||
},
|
||||
userInfo: {
|
||||
endpoint: "v1/user",
|
||||
endpoint: "max",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -12,8 +12,10 @@ export default function Component({ service }) {
|
||||
|
||||
const { data: gluetunData, error: gluetunError } = useWidgetAPI(widget, "ip");
|
||||
const includePF = widget.fields.includes("port_forwarded");
|
||||
const pfEndpoint = widget.version > 1 ? "port_forwarded_v2" : "port_forwarded";
|
||||
const { data: portForwardedData, error: portForwardedError } = useWidgetAPI(widget, includePF ? pfEndpoint : "");
|
||||
const { data: portForwardedData, error: portForwardedError } = useWidgetAPI(
|
||||
widget,
|
||||
includePF ? "port_forwarded" : "",
|
||||
);
|
||||
|
||||
if (gluetunError || (includePF && portForwardedError)) {
|
||||
return <Container service={service} error={gluetunError || portForwardedError} />;
|
||||
|
||||
@@ -13,10 +13,6 @@ const widget = {
|
||||
endpoint: "openvpn/portforwarded",
|
||||
validate: ["port"],
|
||||
},
|
||||
port_forwarded_v2: {
|
||||
endpoint: "portforward",
|
||||
validate: ["port"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ export default function Component({ service }) {
|
||||
if (!data || (data && data.length === 0)) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="myspeed.ping" />
|
||||
<Block label="myspeed.download" />
|
||||
<Block label="myspeed.upload" />
|
||||
<Block label="myspeed.ping" />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,13 +38,12 @@ export default function Component({ service }) {
|
||||
|
||||
if (Array.isArray(statusData.volume.volumeUseList.volumeUse)) {
|
||||
if (widget.volume) {
|
||||
const volumeSelected = statusData.volume.volumeList.volume.find((vl) => vl.volumeLabel._cdata === widget.volume);
|
||||
if (volumeSelected) {
|
||||
const volumeUsed = statusData.volume.volumeUseList.volumeUse.find(
|
||||
(vu) => vu.volumeValue._cdata === volumeSelected.volumeValue._cdata,
|
||||
);
|
||||
volumeTotalSize = volumeUsed.total_size._cdata;
|
||||
volumeFreeSize = volumeUsed.free_size._cdata;
|
||||
const volumeSelected = statusData.volume.volumeList.volume.findIndex(
|
||||
(vl) => vl.volumeLabel._cdata === widget.volume,
|
||||
);
|
||||
if (volumeSelected !== -1) {
|
||||
volumeTotalSize = statusData.volume.volumeUseList.volumeUse[volumeSelected].total_size._cdata;
|
||||
volumeFreeSize = statusData.volume.volumeUseList.volumeUse[volumeSelected].free_size._cdata;
|
||||
} else {
|
||||
validVolume = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user