Merge pull request #33 from stephb9959/main

Making dev up to date
This commit is contained in:
Charles
2021-10-11 18:22:38 -04:00
committed by GitHub
17 changed files with 178 additions and 125 deletions

View File

@@ -1,53 +0,0 @@
name: Build Docker image
on:
push:
paths-ignore:
- '**.md'
branches:
- main
pull_request:
branches:
- main
defaults:
run:
shell: bash
jobs:
docker:
runs-on: ubuntu-20.04
env:
DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io
DOCKER_REGISTRY_USERNAME: ucentral
steps:
- uses: actions/checkout@v2
- name: Build Docker image
run: docker build -t wlan-cloud-ucentralgw-ui:${{ github.sha }} .
- name: Log into Docker registry
uses: docker/login-action@v1
with:
registry: ${{ env.DOCKER_REGISTRY_URL }}
username: ${{ env.DOCKER_REGISTRY_USERNAME }}
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
- name: Push Docker image
run: |
TAGS="${{ github.sha }}"
if [ ${GITHUB_REF} == "refs/heads/main" ]
then
TAGS="$TAGS ${GITHUB_REF#refs/heads/}"
else # PR build
CURRENT_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
TAGS="$TAGS $CURRENT_TAG"
fi
echo "Pushing tags $TAGS"
for tag in $TAGS; do
docker tag wlan-cloud-ucentralgw-ui:${{ github.sha }} ${{ env.DOCKER_REGISTRY_URL }}/ucentralgw-ui:$tag
docker push ${{ env.DOCKER_REGISTRY_URL }}/ucentralgw-ui:$tag
done

68
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,68 @@
name: Build Docker image
on:
push:
paths-ignore:
- '**.md'
branches:
- main
- 'release/*'
tags:
- 'v*'
pull_request:
branches:
- main
defaults:
run:
shell: bash
jobs:
docker:
runs-on: ubuntu-20.04
env:
DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io
DOCKER_REGISTRY_USERNAME: ucentral
steps:
- uses: actions/checkout@v2
- name: Build Docker image
run: docker build -t owgw-ui:${{ github.sha }} .
- name: Tag Docker image
run: |
TAGS="${{ github.sha }}"
if [[ ${GITHUB_REF} == "refs/heads/"* ]]
then
CURRENT_TAG=$(echo ${GITHUB_REF#refs/heads/} | tr '/' '-')
TAGS="$TAGS $CURRENT_TAG"
else
if [[ ${GITHUB_REF} == "refs/tags/"* ]]
then
CURRENT_TAG=$(echo ${GITHUB_REF#refs/tags/} | tr '/' '-')
TAGS="$TAGS $CURRENT_TAG"
else # PR build
CURRENT_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
TAGS="$TAGS $CURRENT_TAG"
fi
fi
echo "Result tags: $TAGS"
for tag in $TAGS; do
docker tag owgw-ui:${{ github.sha }} ${{ env.DOCKER_REGISTRY_URL }}/owgw-ui:$tag
done
- name: Log into Docker registry
if: startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/pull/') || github.ref == 'refs/heads/main'
uses: docker/login-action@v1
with:
registry: ${{ env.DOCKER_REGISTRY_URL }}
username: ${{ env.DOCKER_REGISTRY_USERNAME }}
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
- name: Push Docker images
if: startsWith(github.ref, 'refs/tags/') || startsWith(github.ref, 'refs/pull/') || github.ref == 'refs/heads/main'
run: |
docker images | grep ${{ env.DOCKER_REGISTRY_URL }}/owgw-ui | awk -F ' ' '{print $1":"$2}' | xargs -I {} docker push {}

View File

@@ -16,4 +16,4 @@ jobs:
steps:
- run: |
export PR_BRANCH_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
curl -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/ucentralgw-ui/$PR_BRANCH_TAG"
curl -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owgw-ui/$PR_BRANCH_TAG"

View File

@@ -1,4 +1,4 @@
FROM node:16-alpine3.11 AS build
FROM node:14-alpine3.11 AS build
COPY package.json package-lock.json /

View File

@@ -9,18 +9,29 @@ NOTE: This UI will be evolving as micro services are added to the uCentral progr
## Running the solution
### Development
Here are the instructions to run the solution on your machine for development purposes. You need to run these in the root folder of the project and also have npm installed on your machine. Please install `npm` for the platform you are using.
You need to run these commands in the root folder of the project and also have npm installed on your machine.
```
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui
cd wlan-cloud-ucentralgw-ui
npm install
npm start
```
Run these commands if you want to run the solution on your machine while also doing development on the [uCentral UI Library](https://github.com/Telecominfraproject/wlan-cloud-ucentral-ui-libs).
```
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentral-ui-libs
cd wlan-cloud-ucentralgw-ui
npm link ../wlan-cloud-ucentral-ui-libs // Add sudo at the start of this command if it fails because of permissions
npm start
```
### Production
Here are the instructions to build the production veresion of the application. You need to run this in the root folder of the project and also have npm installed on your machine.
You need to run this in the root folder of the project and also have npm installed on your machine.
```
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui
cd wlan-cloud-ucentralgw-ui
npm install
npm run build
```
Once the build is done, you can move the `build` folder on your server.

View File

@@ -1,6 +1,6 @@
#!/bin/ash
# Check if variables are set
export DEFAULT_GATEWAY_URL="${DEFAULT_GATEWAY_URL:-https://ucentral.dpaas.arilia.com:16001}"
export ALLOW_GATEWAY_CHANGE="${ALLOW_GATEWAY_CHANGE:-false}"
export DEFAULT_OWSEC_URL="${DEFAULT_OWSEC_URL:-https://ucentral.dpaas.arilia.com:16001}"
export ALLOW_OWSEC_CHANGE="${ALLOW_OWSEC_CHANGE:-false}"
echo '{"DEFAULT_GATEWAY_URL": "'$DEFAULT_GATEWAY_URL'","ALLOW_GATEWAY_CHANGE": '$ALLOW_GATEWAY_CHANGE'}' > /usr/share/nginx/html/config.json
echo '{"DEFAULT_UCENTRALSEC_URL": "'$DEFAULT_UCENTRALSEC_URL'","ALLOW_UCENTRALSEC_CHANGE": '$ALLOW_UCENTRALSEC_CHANGE'}' > /usr/share/nginx/html/config.json

View File

@@ -1,5 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: ucentralgwui
name: owgwui
version: 0.1.0

View File

@@ -1,6 +1,7 @@
# ucentralgwui
# owgwui
This Helm chart helps to deploy OpenWIFI Web UI (further on refered as __Web UI__) to the Kubernetes clusters. It is mainly used in [assembly chart](https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy/tree/main/chart) as Web UI requires other services as dependencies that are considered in that Helm chart. This chart is purposed to define deployment logic close to the application code itself and define default values that could be overriden during deployment.
> Application description placeholder
## TL;DR;
@@ -10,17 +11,19 @@ $ helm install .
## Introduction
This chart bootstraps an ucentralgwui on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
This chart bootstraps the Web UI on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
## Installing the Chart
Currently this chart is not assembled in charts archives, so [helm-git](https://github.com/aslafy-z/helm-git) is required for remote the installation
To install the chart with the release name `my-release`:
```bash
$ helm install --name my-release stable/ipfs
$ helm install --name my-release git+https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui@helm?ref=main
```
The command deploys ucentralgwui on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation.
The command deploys the Web UI on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation.
> **Tip**: List all releases using `helm list`
@@ -29,18 +32,36 @@ The command deploys ucentralgwui on the Kubernetes cluster in the default config
To uninstall/delete the `my-release` deployment:
```bash
$ helm delete --purge my-release
$ helm delete my-release
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
## Configuration
The following table lists the configurable parameters of the Memcached chart and their default values.
The following table lists the configurable parameters of the chart and their default values. If Default value is not listed in the table, please refer to the [Values](values.yaml) files for details.
| Parameter | Type | Description | Default |
|-----------|------|-------------|---------|
| replicaCount | number | Amount of replicas to be deployed | `1` |
| nameOverride | string | Override to be used for application deployment | |
| fullnameOverride | string | Override to be used for application deployment (has priority over nameOverride) | |
| images.owgwui.repository | string | Docker image repository | |
| images.owgwui.tag | string | Docker image tag | `'master'` |
| images.owgwui.pullPolicy | string | Docker image pull policy | `'Always'` |
| services.owgwui.type | string | OpenWIFI Web UI service type | `'ClusterIP'` |
| services.owgwui.ports.http.servicePort | number | Websocket endpoint port to be exposed on service | `80` |
| services.owgwui.ports.http.targetPort | number | Websocket endpoint port to be targeted by service | `80` |
| services.owgwui.ports.http.protocol | string | Websocket endpoint protocol | `'TCP'` |
| checks.owgwui.liveness.httpGet.path | string | Liveness check path to be used | `'/'` |
| checks.owgwui.liveness.httpGet.port | number | Liveness check port to be used (should be pointint to ALB endpoint) | `http` |
| checks.owgwui.readiness.httpGet.path | string | Readiness check path to be used | `'/'` |
| checks.owgwui.readiness.httpGet.port | number | Readiness check port to be used | `http` |
| ingresses.default.enabled | boolean | Defines if the Web UI should be exposed via Ingress controller | `False` |
| ingresses.default.hosts | array | List of hosts for the exposed Web UI | |
| ingresses.default.paths | array | List of paths to be exposed for the Web UI | |
| public_env_variables | hash | Defines list of environment variables to be passed to the Web UI (required for application configuration) | |
| Parameter | Description | Default |
|-----------|-------------|---------|
|||
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
@@ -59,4 +80,3 @@ $ helm install --name my-release -f values.yaml .
```
> **Tip**: You can use the default [values.yaml](values.yaml) as a base for customization.

View File

@@ -2,7 +2,7 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "ucentralgwui.name" -}}
{{- define "owgwui.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
@@ -11,7 +11,7 @@ Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "ucentralgwui.fullname" -}}
{{- define "owgwui.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
@@ -27,6 +27,6 @@ If release name contains chart name it will be used as a full name.
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "ucentralgwui.chart" -}}
{{- define "owgwui.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}

View File

@@ -3,57 +3,59 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "ucentralgwui.fullname" . }}
name: {{ include "owgwui.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "ucentralgwui.name" . }}
helm.sh/chart: {{ include "ucentralgwui.chart" . }}
app.kubernetes.io/name: {{ include "owgwui.name" . }}
helm.sh/chart: {{ include "owgwui.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "ucentralgwui.name" . }}
app.kubernetes.io/name: {{ include "owgwui.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- with .Values.services.ucentralgwui.labels }}
{{- with .Values.services.owgwui.labels }}
{{- toYaml . | nindent 6 }}
{{- end }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "ucentralgwui.name" . }}
app.kubernetes.io/name: {{ include "owgwui.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- with .Values.services.ucentralgwui.labels }}
{{- with .Values.services.owgwui.labels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
containers:
- name: ucentralgwui
image: "{{ .Values.images.ucentralgwui.repository }}:{{ .Values.images.ucentralgwui.tag }}"
imagePullPolicy: {{ .Values.images.ucentralgwui.pullPolicy }}
- name: owgwui
image: "{{ .Values.images.owgwui.repository }}:{{ .Values.images.owgwui.tag }}"
imagePullPolicy: {{ .Values.images.owgwui.pullPolicy }}
env:
- name: KUBERNETES_DEPLOYED
value: "{{ now }}"
{{- range $key, $value := .Values.public_env_variables }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
ports:
{{- range $key, $value := .Values.services.ucentralgwui.ports }}
{{- range $key, $value := .Values.services.owgwui.ports }}
- name: {{ $key }}
containerPort: {{ $value.targetPort }}
protocol: {{ $value.protocol }}
{{- end }}
{{- if .Values.checks.ucentralgwui.liveness }}
{{- if .Values.checks.owgwui.liveness }}
livenessProbe:
{{- toYaml .Values.checks.ucentralgwui.liveness | nindent 12 }}
{{- toYaml .Values.checks.owgwui.liveness | nindent 12 }}
{{- end }}
{{- if .Values.checks.ucentralgwui.readiness }}
{{- if .Values.checks.owgwui.readiness }}
readinessProbe:
{{- toYaml .Values.checks.ucentralgwui.readiness | nindent 12 }}
{{- toYaml .Values.checks.owgwui.readiness | nindent 12 }}
{{- end }}
{{- with .Values.resources }}
@@ -64,7 +66,7 @@ spec:
imagePullSecrets:
{{- range $image, $imageValue := .Values.images }}
{{- if $imageValue.regcred }}
- name: {{ include "ucentralgwui.fullname" $root }}-{{ $image }}-regcred
- name: {{ include "owgwui.fullname" $root }}-{{ $image }}-regcred
{{- end }}
{{- end }}

View File

@@ -5,10 +5,10 @@
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ include "ucentralgwui.fullname" $root }}-{{ $ingress }}
name: {{ include "owgwui.fullname" $root }}-{{ $ingress }}
labels:
app.kubernetes.io/name: {{ include "ucentralgwui.name" $root }}
helm.sh/chart: {{ include "ucentralgwui.chart" $root }}
app.kubernetes.io/name: {{ include "owgwui.name" $root }}
helm.sh/chart: {{ include "owgwui.chart" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
app.kubernetes.io/managed-by: {{ $root.Release.Service }}
{{- with $ingressValue.annotations }}
@@ -37,7 +37,7 @@ spec:
{{- range $ingressValue.paths }}
- path: {{ .path }}
backend:
serviceName: {{ include "ucentralgwui.fullname" $root }}-{{ .serviceName }}
serviceName: {{ include "owgwui.fullname" $root }}-{{ .serviceName }}
servicePort: {{ .servicePort }}
{{- end }}
{{- end }}

View File

@@ -10,11 +10,11 @@ kind: Secret
type: kubernetes.io/dockerconfigjson
metadata:
labels:
app.kuberentes.io/name: {{ include "ucentralgwui.name" $root }}
helm.sh/chart: {{ include "ucentralgwui.chart" $root }}
app.kuberentes.io/name: {{ include "owgwui.name" $root }}
helm.sh/chart: {{ include "owgwui.chart" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
app.kubernetes.io/managed-by: {{ $root.Release.Service }}
name: {{ include "ucentralgwui.fullname" $root }}-{{ $image }}-regcred
name: {{ include "owgwui.fullname" $root }}-{{ $image }}-regcred
data:
.dockerconfigjson: {{ template "imagePullSecret" $imageValue.regcred }}
{{- end }}

View File

@@ -4,14 +4,14 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "ucentralgwui.fullname" $root }}-{{ $service }}
name: {{ include "owgwui.fullname" $root }}-{{ $service }}
{{- with $serviceValue.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
labels:
app.kubernetes.io/name: {{ include "ucentralgwui.name" $root }}
helm.sh/chart: {{ include "ucentralgwui.chart" $root }}
app.kubernetes.io/name: {{ include "owgwui.name" $root }}
helm.sh/chart: {{ include "owgwui.chart" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
app.kubernetes.io/managed-by: {{ $root.Release.Service }}
@@ -39,7 +39,7 @@ spec:
{{- end }}
{{- end }}
selector:
app.kubernetes.io/name: {{ include "ucentralgwui.name" $root }}
app.kubernetes.io/name: {{ include "owgwui.name" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
{{- with $serviceValue.labels }}
{{- toYaml . | nindent 4 }}

View File

@@ -5,13 +5,13 @@ nameOverride: ""
fullnameOverride: ""
images:
ucentralgwui:
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/ucentralgw-ui
owgwui:
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw-ui
tag: main
pullPolicy: Always
services:
ucentralgwui:
owgwui:
type: ClusterIP
ports:
http:
@@ -20,7 +20,7 @@ services:
protocol: TCP
checks:
ucentralgwui:
owgwui:
liveness:
httpGet:
path: /
@@ -37,7 +37,7 @@ ingresses:
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
# tls:
# - secretName: '{{ include "ucentralgwui.fullname" . }}-default-tls' # template may be used
# - secretName: '{{ include "owgwui.fullname" . }}-default-tls' # template may be used
# cert: |
# CERT_HERE_IN_PEM
# key: |
@@ -48,7 +48,7 @@ ingresses:
- chart-example.local
paths:
- path: /
serviceName: ucentralgwui
serviceName: owgwui
servicePort: http
resources: {}
@@ -71,5 +71,5 @@ affinity: {}
# Application
public_env_variables:
DEFAULT_GATEWAY_URL: https://ucentral.dpaas.arilia.com:16001
ALLOW_GATEWAY_CHANGE: false
DEFAULT_UCENTRALSEC_URL: https://ucentral.dpaas.arilia.com:16001
ALLOW_UCENTRALSEC_CHANGE: false

18
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "ucentral-client",
"version": "2.2.4",
"version": "2.2.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ucentral-client",
"version": "2.2.4",
"version": "2.2.5",
"dependencies": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",
@@ -32,7 +32,7 @@
"react-tooltip": "^4.2.21",
"react-widgets": "^5.1.1",
"sass": "^1.35.1",
"ucentral-libs": "^0.9.44",
"ucentral-libs": "^0.9.58",
"uuid": "^8.3.2"
},
"devDependencies": {
@@ -14812,9 +14812,9 @@
}
},
"node_modules/ucentral-libs": {
"version": "0.9.44",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.44.tgz",
"integrity": "sha512-h7iXlmX7bueJuhzFRKJnJWD/UAvNFn+0Ausqkq8H4tzI9uxObsWhRSO6JhJUZpSW2hyGXnXZWRXE0OZwqs9pXA==",
"version": "0.9.58",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.58.tgz",
"integrity": "sha512-K0bacVDGzjK//pddaMNVh2WDOep0/22tV1EhEfMKBAzaXgYEx2Ir3fg/GQzswCkfwg9i0kDWKiAXqRQ8pWLblg==",
"dependencies": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",
@@ -27653,9 +27653,9 @@
}
},
"ucentral-libs": {
"version": "0.9.44",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.44.tgz",
"integrity": "sha512-h7iXlmX7bueJuhzFRKJnJWD/UAvNFn+0Ausqkq8H4tzI9uxObsWhRSO6JhJUZpSW2hyGXnXZWRXE0OZwqs9pXA==",
"version": "0.9.58",
"resolved": "https://registry.npmjs.org/ucentral-libs/-/ucentral-libs-0.9.58.tgz",
"integrity": "sha512-K0bacVDGzjK//pddaMNVh2WDOep0/22tV1EhEfMKBAzaXgYEx2Ir3fg/GQzswCkfwg9i0kDWKiAXqRQ8pWLblg==",
"requires": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",

View File

@@ -1,6 +1,6 @@
{
"name": "ucentral-client",
"version": "2.2.4",
"version": "2.2.5",
"dependencies": {
"@coreui/coreui": "^3.4.0",
"@coreui/icons": "^2.0.1",
@@ -26,7 +26,7 @@
"react-tooltip": "^4.2.21",
"react-widgets": "^5.1.1",
"sass": "^1.35.1",
"ucentral-libs": "^0.9.44",
"ucentral-libs": "^0.9.58",
"uuid": "^8.3.2"
},
"main": "index.js",

View File

@@ -150,10 +150,15 @@ const FirmwareDashboard = () => {
? parsedData.unknownFirmwares.reduce((acc, firmware) => acc + firmware.value, 0)
: 0;
const devicesForAverage = parsedData.numberOfDevices - usingUnknownFirmwareFromArray;
parsedData.averageFirmwareAge =
parsedData.totalSecondsOld[0].value /
(devicesForAverage > 0 ? devicesForAverage : 1) /
(24 * 60 * 60);
if (parsedData.totalSecondsOld.length > 0) {
parsedData.averageFirmwareAge =
parsedData.totalSecondsOld[0].value /
(devicesForAverage > 0 ? devicesForAverage : 1) /
(24 * 60 * 60);
} else {
parsedData.averageFirmwareAge = 0;
}
// Latest firmware distribution
const latestDs = [];