Compare commits

..

45 Commits

Author SHA1 Message Date
packagrio-bot ee893cc360 (v0.7.1) Automated packaging of release by Packagr 2023-04-08 23:01:22 +00:00
Jason Kulatunga d73907d357 Merge pull request #474 from AnalogJ/beta 2023-04-08 15:58:51 -07:00
Jason Kulatunga 0b50305f38 fix invalid COPY instruction. 2023-04-07 00:00:11 -07:00
Jason Kulatunga ee3d719c3a simplify docker image build
changes contributed by @modem7

fixes #461
2023-04-06 23:57:15 -07:00
Jason Kulatunga d76cdca4a5 Merge pull request #472 from adamantike/misc/add-support-for-yml-config-files 2023-04-06 17:46:03 -07:00
Jason Kulatunga b34ed607b7 Merge pull request #471 from adamantike/feat/add-support-for-ntfy-notifications 2023-04-06 17:42:50 -07:00
Michael Manganiello 932e191510 Allow configuration files with yml extension
If a `collector.yml` or `scrutiny.yml` configuration file is present,
use it as long as a `.yaml` version is not available too.

Fixes #79
2023-04-06 20:55:22 -03:00
Michael Manganiello 3a6c407fe7 Add support for ntfy notifications
Updates [`shoutrrr`](containrrr.dev/shoutrrr/) to `v0.7.1` to enable
support for [ntfy](https://ntfy.sh/) notifications.

Fixes #433.
2023-04-06 17:04:48 -03:00
Jason Kulatunga 8c3afc31f4 Merge pull request #449 from adamantike/fix/delete-influxdb-deb-from-docker-image 2023-04-05 23:25:52 -07:00
packagrio-bot 2e4ba44952 (v0.7.0) Automated packaging of release by Packagr 2023-04-06 05:22:42 +00:00
Jason Kulatunga 4192ac719e Merge pull request #470 from AnalogJ/beta 2023-04-05 22:07:13 -07:00
Jason Kulatunga 539c94595f Merge pull request #469 from AnalogJ/angular-v13-upgrade 2023-04-05 21:09:35 -07:00
Jason Kulatunga de21e611a3 fixing migration for line_stroke setting. 2023-04-05 21:06:09 -07:00
Jason Kulatunga dea362361e Merge pull request #468 from AnalogJ/angular-v13-upgrade 2023-04-05 20:58:56 -07:00
Jason Kulatunga 7b77519f49 trying to fix tests. 2023-04-05 20:48:07 -07:00
Jason Kulatunga 94df7e1ec3 trying to fix tests. 2023-04-05 20:44:07 -07:00
Jason Kulatunga babd8d3089 trying to fix tests. 2023-04-05 20:35:18 -07:00
Jason Kulatunga 52ef28f091 removing NODE_OPTIONS. 2023-04-05 20:34:29 -07:00
Jason Kulatunga 80d72f8a1b regenerate package-lock with angular 13-lts packages. 2023-04-05 20:10:53 -07:00
Jason Kulatunga 2e8f4a0581 update with instructions for adding host id to UI.
- fixes #464
- related #151
2023-04-05 19:50:09 -07:00
Michael Manganiello 7fd2e2b050 Upgrade to Go 1.20
With the release of Go 1.20, version 1.18 is not supported anymore. This
change upgrades the project to Go 1.20.
2023-04-05 19:50:09 -07:00
Jason Kulatunga 8c65166a90 Merge pull request #446 from adamantike/misc/upgrade-go-1.20 2023-04-05 19:48:31 -07:00
Jason Kulatunga 733b49c2c4 Merge branch 'master' into misc/upgrade-go-1.20 2023-04-05 07:53:01 -07:00
Jason Kulatunga 711b5c40b1 update with instructions for adding host id to UI.
- fixes #464
- related #151
2023-04-04 21:53:39 -07:00
Jason Kulatunga f94e616d8d Merge branch 'master' into angular-refactoring 2023-04-04 21:38:57 -07:00
Michael Manganiello fb7848f341 Delete temporary deb file from omnibus Docker image
This considerably reduces the Docker image size for the `omnibus` variant:

```
scrutiny-omnibus-master         latest            cd7a7dde100b   9 minutes ago   538MB
scrutiny-omnibus-fix-applied    latest            5f7ac124ef50   6 minutes ago   431MB
```
2023-02-22 16:25:09 -03:00
Michael Manganiello 007857afd5 Upgrade to Go 1.20
With the release of Go 1.20, version 1.18 is not supported anymore. This
change upgrades the project to Go 1.20.
2023-02-19 21:18:51 -03:00
Jason Kulatunga e4bbe8c035 Merge pull request #441 from padhi-forks/master
Fixes https://github.com/AnalogJ/scrutiny/issues/440
2023-02-08 21:43:45 -08:00
Saswat Padhi cb5226f6e4 Update backend tests failing on insecure_skip_verify 2023-02-07 08:24:39 +00:00
Saswat Padhi c69770d1dd Add insecure_skip_verify in example.scrutiny.yaml 2023-02-06 22:34:06 +00:00
Saswat Padhi e07a53046f [FEAT] Allow insecure certificates on InfluxDB
This change allows users to skip TLS certificate verification on their
InfluxDB server, if they wish to do so, for instance when using self-
signed certificates.
Without this change, scrutiny failed to start and paniced with a
`x509: certificate signed by unknown authority` error.
2023-02-06 22:26:40 +00:00
Jason Kulatunga 19a0b8c2ac Update TROUBLESHOOTING_INFLUXDB.md
change volume mounts when upgrading from LSIO image
2023-02-04 08:41:47 -08:00
Jason Kulatunga 97f73703b1 update docs with instructions for customizing Influxdb creds. 2023-01-19 22:21:48 -08:00
Jason Kulatunga 4fcd11f497 update coverage makefile with workaround. 2023-01-11 21:31:25 -08:00
Jason Kulatunga 7f1023fa9b temporary fix for #426
using legacy open ssl provider for fixing `"error:0308010C:digital envelope routines::unsupported"` error.

See https://stackoverflow.com/questions/69692842/error-message-error0308010cdigital-envelope-routinesunsupported

We need to upgrade Angular version.
2023-01-11 21:30:16 -08:00
Jason Kulatunga d49497da80 update hub/spoke guide - thanks @TinJoy59
fixes #417
2023-01-11 18:03:49 -08:00
Jason Kulatunga d8c359bd8a instructions documenting added ability to trigger collector at startup.
Disabled by default, (enable by setting `-e COLLECTOR_RUN_STARTUP=true`)

Added COLLECTOR_RUN_STARTUP_SLEEP env variable to specify seconds before calling scrutiny collector on first run, default sleep value = 1s.
2023-01-11 17:49:58 -08:00
Jason Kulatunga ad4b117f6e fixes #412
Added ability to trigger collector at startup.
Disabled by default, (enable by setting `-e COLLECTOR_RUN_STARTUP=true`)

Added COLLECTOR_RUN_STARTUP_SLEEP env variable to specify seconds before calling scrutiny collector on first run, default sleep value = 1s.
2023-01-11 17:41:09 -08:00
Jason Kulatunga 22d2f9847c fixes #418
when using device type `sat,auto`, scrutiny config parser will treat `type: 'sat,auto'` as a list, which will cause it to be treated like a raid disk. force single command execution using `type: ['sat,auto']`
2023-01-11 17:24:01 -08:00
adripo e9cf8a9180 fix: igeneric types 2022-11-12 22:27:07 +01:00
adripo 64ad353628 fix: remove fullcalendar 2022-11-12 22:26:03 +01:00
adripo 5518865bc6 fix: remove outdated option 2022-11-12 22:24:38 +01:00
adripo 50321d897a fix: prod build command 2022-11-12 22:24:02 +01:00
adripo e18a7e9ce0 refactor: update dependencies version 2022-11-12 22:23:37 +01:00
adripo 536b590080 feat: dynamic line stroke settings 2022-11-11 00:19:51 +01:00
65 changed files with 9726 additions and 28368 deletions
+2 -2
View File
@@ -101,7 +101,7 @@ jobs:
uses: actions/checkout@v2
- uses: actions/setup-go@v3
with:
go-version: '^1.18.3'
go-version: '^1.20.1'
- name: Build Binaries
run: |
make binary-clean binary-all
@@ -111,4 +111,4 @@ jobs:
name: binaries.zip
path: |
scrutiny-web-*
scrutiny-collector-metrics-*
scrutiny-collector-metrics-*
-14
View File
@@ -74,13 +74,6 @@ jobs:
uses: actions/checkout@v2
- name: "Populate frontend version information"
run: "cd webapp/frontend && ./git.version.sh"
- name: "Install Node"
uses: actions/setup-node@v3
with:
node-version: 16
- name: "Generate frontend"
run: |
make binary-frontend && echo "print contents of ./dist" && ls -alt ./dist
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
@@ -132,13 +125,6 @@ jobs:
uses: actions/checkout@v2
- name: "Populate frontend version information"
run: "cd webapp/frontend && ./git.version.sh"
- name: "Install Node"
uses: actions/setup-node@v3
with:
node-version: 16
- name: "Generate frontend"
run: |
make binary-frontend && echo "print contents of ./dist" && ls -alt ./dist
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
-7
View File
@@ -19,13 +19,6 @@ jobs:
uses: actions/checkout@v2
- name: "Populate frontend version information"
run: "cd webapp/frontend && ./git.version.sh"
- name: "Install Node"
uses: actions/setup-node@v3
with:
node-version: 16
- name: "Generate frontend"
run: |
make binary-frontend && echo "print contents of ./dist" && ls -alt ./dist
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
+1 -1
View File
@@ -96,7 +96,7 @@ jobs:
name: workspace
- uses: actions/setup-go@v3
with:
go-version: '1.18.3' # The Go version to download (if necessary) and use.
go-version: '1.20.1' # The Go version to download (if necessary) and use.
- name: Build Binaries
run: |
make binary-clean binary-all
+11 -11
View File
@@ -5,18 +5,18 @@ The Scrutiny repository is a [monorepo](https://en.wikipedia.org/wiki/Monorepo)
- Scrutiny Frontend Angular SPA
- S.M.A.R.T Collector
Depending on the functionality you are adding, you may need to setup a development environment for 1 or more projects.
Depending on the functionality you are adding, you may need to setup a development environment for 1 or more projects.
# Modifying the Scrutiny Backend Server (API)
1. install the [Go runtime](https://go.dev/doc/install) (v1.18+)
1. install the [Go runtime](https://go.dev/doc/install) (v1.20+)
2. download the `scrutiny-web-frontend.tar.gz` for
the [latest release](https://github.com/AnalogJ/scrutiny/releases/latest). Extract to a folder named `dist`
3. create a `scrutiny.yaml` config file
```yaml
# config file for local development. store as scrutiny.yaml
version: 1
web:
listen:
port: 8080
@@ -29,13 +29,13 @@ Depending on the functionality you are adding, you may need to setup a developme
path: ./dist
influxdb:
retention_policy: false
log:
file: 'web.log' #absolute or relative paths allowed, eg. web.log
level: DEBUG
```
4. start a InfluxDB docker container.
4. start a InfluxDB docker container.
```bash
docker run -p 8086:8086 --rm influxdb:2.2
```
@@ -55,21 +55,21 @@ The frontend is written in Angular. If you're working on the frontend and can us
```bash
cd webapp/frontend
npm install
npm run start -- --deploy-url="/web/" --base-href="/web/" --port 4200
npm run start -- --serve-path="/web/" --port 4200
```
3. open your browser and visit [http://localhost:4200/web](http://localhost:4200/web)
# Modifying both Scrutiny Backend and Frontend Applications
If you're developing a feature that requires changes to the backend and the frontend, or a frontend feature that requires real data,
If you're developing a feature that requires changes to the backend and the frontend, or a frontend feature that requires real data,
you'll need to follow the steps below:
1. install the [Go runtime](https://go.dev/doc/install) (v1.18+)
1. install the [Go runtime](https://go.dev/doc/install) (v1.20+)
2. install [NodeJS](https://nodejs.org/en/download/)
3. create a `scrutiny.yaml` config file
```yaml
# config file for local development. store as scrutiny.yaml
version: 1
web:
listen:
port: 8080
@@ -82,7 +82,7 @@ you'll need to follow the steps below:
path: ./dist
influxdb:
retention_policy: false
log:
file: 'web.log' #absolute or relative paths allowed, eg. web.log
level: DEBUG
@@ -185,4 +185,4 @@ docker run -p 8086:8086 -d --rm \
influxdb:2.2
go test ./...
```
```
+1 -1
View File
@@ -100,7 +100,7 @@ binary-frontend: export NPM_CONFIG_LOGLEVEL = warn
binary-frontend: export NG_CLI_ANALYTICS = false
binary-frontend:
cd webapp/frontend
npm install -g @angular/cli@9.1.4
npm install -g @angular/cli@v13-lts
mkdir -p $(CURDIR)/dist
npm ci
npm run build:prod -- --output-path=$(CURDIR)/dist
+4 -3
View File
@@ -69,7 +69,7 @@ See [docs/TROUBLESHOOTING_DEVICE_COLLECTOR.md](./docs/TROUBLESHOOTING_DEVICE_COL
If you're using Docker, getting started is as simple as running the following command:
> See [docker/example.omnibus.docker-compose.yml](./docker/example.omnibus.docker-compose.yml) for a docker-compose file.
> See [docker/example.omnibus.docker-compose.yml](./docker/example.omnibus.docker-compose.yml) for a docker-compose file.
```bash
docker run -it --rm -p 8080:8080 -p 8086:8086 \
@@ -157,7 +157,7 @@ Neither file is required, however if provided, it allows you to configure how Sc
## Cron Schedule
Unfortunately the Cron schedule cannot be configured via the `collector.yaml` (as the collector binary needs to be trigged by a scheduler/cron).
However, if you are using the official `ghcr.io/analogj/scrutiny:master-collector` or `ghcr.io/analogj/scrutiny:master-omnibus` docker images,
However, if you are using the official `ghcr.io/analogj/scrutiny:master-collector` or `ghcr.io/analogj/scrutiny:master-omnibus` docker images,
you can use the `COLLECTOR_CRON_SCHEDULE` environmental variable to override the default cron schedule (daily @ midnight - `0 0 * * *`).
`docker run -e COLLECTOR_CRON_SCHEDULE="0 0 * * *" ...`
@@ -174,6 +174,7 @@ Scrutiny supports sending SMART device failure notifications via the following s
- IFTTT
- Join
- Mattermost
- ntfy
- Pushbullet
- Pushover
- Slack
@@ -243,7 +244,7 @@ scrutiny-collector-metrics run --debug --log-file /tmp/collector.log
| linux-arm-6 | :white_check_mark: | |
| linux-arm-7 | :white_check_mark: | web/collector only. see [#236](https://github.com/AnalogJ/scrutiny/issues/236) |
| linux-arm64 | :white_check_mark: | :white_check_mark: |
| freebsd-amd64 | :white_check_mark: | |
| freebsd-amd64 | :white_check_mark: | |
| macos-amd64 | :white_check_mark: | :white_check_mark: |
| macos-arm64 | :white_check_mark: | :white_check_mark: |
| windows-amd64 | :white_check_mark: | WIP, see [#15](https://github.com/AnalogJ/scrutiny/issues/15) |
@@ -30,8 +30,14 @@ func main() {
os.Exit(1)
}
configFilePath := "/opt/scrutiny/config/collector.yaml"
configFilePathAlternative := "/opt/scrutiny/config/collector.yml"
if !utils.FileExists(configFilePath) && utils.FileExists(configFilePathAlternative) {
configFilePath = configFilePathAlternative
}
//we're going to load the config file manually, since we need to validate it.
err = config.ReadConfig("/opt/scrutiny/config/collector.yaml") // Find and read the config file
err = config.ReadConfig(configFilePath) // Find and read the config file
if _, ok := err.(errors.ConfigFileMissingError); ok { // Handle errors reading the config file
//ignore "could not find config file"
} else if err != nil {
+19
View File
@@ -36,6 +36,25 @@ func TestConfiguration_GetScanOverrides_Simple(t *testing.T) {
require.Equal(t, []models.ScanOverride{{Device: "/dev/sda", DeviceType: []string{"sat"}, Ignore: false}}, scanOverrides)
}
// fixes #418
func TestConfiguration_GetScanOverrides_DeviceTypeComma(t *testing.T) {
t.Parallel()
//setup
testConfig, _ := config.Create()
//test
err := testConfig.ReadConfig(path.Join("testdata", "device_type_comma.yaml"))
require.NoError(t, err, "should correctly load simple device config")
scanOverrides := testConfig.GetDeviceOverrides()
//assert
require.Equal(t, []models.ScanOverride{
{Device: "/dev/sda", DeviceType: []string{"sat", "auto"}, Ignore: false},
{Device: "/dev/sdb", DeviceType: []string{"sat,auto"}, Ignore: false},
}, scanOverrides)
}
func TestConfiguration_GetScanOverrides_Ignore(t *testing.T) {
t.Parallel()
+9
View File
@@ -0,0 +1,9 @@
version: 1
devices:
# the scrutiny config parser will detect `sat,auto` as two separate items in a list. If you want to use `-d sat,auto` you must
# set 'sat,auto' in a list (see eg. /dev/sbd)
- device: /dev/sda
type: 'sat,auto'
- device: /dev/sdb
type:
- sat,auto
+29 -17
View File
@@ -1,50 +1,62 @@
# syntax=docker/dockerfile:1.4
########################################################################################################################
# Omnibus Image
# NOTE: this image requires the `make binary-frontend` target to have been run before `docker build` The `dist` directory must exist.
########################################################################################################################
######## Build the frontend
FROM --platform=${BUILDPLATFORM} node AS frontendbuild
WORKDIR /go/src/github.com/analogj/scrutiny
COPY --link . /go/src/github.com/analogj/scrutiny
########
FROM golang:1.18-bullseye as backendbuild
RUN make binary-frontend
######## Build the backend
FROM golang:1.20-bullseye as backendbuild
WORKDIR /go/src/github.com/analogj/scrutiny
COPY . /go/src/github.com/analogj/scrutiny
COPY --link . /go/src/github.com/analogj/scrutiny
RUN make binary-clean binary-all WEB_BINARY_NAME=scrutiny
########
######## Combine build artifacts in runtime image
FROM debian:bullseye-slim as runtime
ARG TARGETARCH
EXPOSE 8080
WORKDIR /opt/scrutiny
ENV PATH="/opt/scrutiny/bin:${PATH}"
ENV INFLUXD_CONFIG_PATH=/opt/scrutiny/influxdb
ENV S6VER="1.21.8.0"
ENV INFLUXVER="2.2.0"
RUN apt-get update && apt-get install -y cron smartmontools ca-certificates curl tzdata \
RUN apt-get update && DEBIAN_FRONTEND=noninteractive \
apt-get install -y --no-install-recommends \
ca-certificates \
cron \
curl \
smartmontools \
tzdata \
&& update-ca-certificates \
&& case ${TARGETARCH} in \
"amd64") S6_ARCH=amd64 ;; \
"arm64") S6_ARCH=aarch64 ;; \
esac \
&& curl https://github.com/just-containers/s6-overlay/releases/download/v1.21.8.0/s6-overlay-${S6_ARCH}.tar.gz -L -s --output /tmp/s6-overlay-${S6_ARCH}.tar.gz \
&& curl https://github.com/just-containers/s6-overlay/releases/download/v${S6VER}/s6-overlay-${S6_ARCH}.tar.gz -L -s --output /tmp/s6-overlay-${S6_ARCH}.tar.gz \
&& tar xzf /tmp/s6-overlay-${S6_ARCH}.tar.gz -C / \
&& rm -rf /tmp/s6-overlay-${S6_ARCH}.tar.gz \
&& curl -L https://dl.influxdata.com/influxdb/releases/influxdb2-2.2.0-${TARGETARCH}.deb --output /tmp/influxdb2-2.2.0-${TARGETARCH}.deb \
&& dpkg -i --force-all /tmp/influxdb2-2.2.0-${TARGETARCH}.deb
&& curl -L https://dl.influxdata.com/influxdb/releases/influxdb2-${INFLUXVER}-${TARGETARCH}.deb --output /tmp/influxdb2-${INFLUXVER}-${TARGETARCH}.deb \
&& dpkg -i --force-all /tmp/influxdb2-${INFLUXVER}-${TARGETARCH}.deb \
&& rm -rf /tmp/influxdb2-2.2.0-${TARGETARCH}.deb
COPY /rootfs /
COPY /rootfs/etc/cron.d/scrutiny /etc/cron.d/scrutiny
COPY --from=backendbuild /go/src/github.com/analogj/scrutiny/scrutiny /opt/scrutiny/bin/
COPY --from=backendbuild /go/src/github.com/analogj/scrutiny/scrutiny-collector-metrics /opt/scrutiny/bin/
COPY dist /opt/scrutiny/web
RUN chmod +x /opt/scrutiny/bin/scrutiny && \
chmod +x /opt/scrutiny/bin/scrutiny-collector-metrics && \
chmod 0644 /etc/cron.d/scrutiny && \
COPY --link --from=backendbuild --chmod=755 /go/src/github.com/analogj/scrutiny/scrutiny /opt/scrutiny/bin/
COPY --link --from=backendbuild --chmod=755 /go/src/github.com/analogj/scrutiny/scrutiny-collector-metrics /opt/scrutiny/bin/
COPY --link --from=frontendbuild --chmod=644 /go/src/github.com/analogj/scrutiny/dist /opt/scrutiny/web
RUN chmod 0644 /etc/cron.d/scrutiny && \
rm -f /etc/cron.daily/* && \
mkdir -p /opt/scrutiny/web && \
mkdir -p /opt/scrutiny/config && \
chmod -R ugo+rwx /opt/scrutiny/config
CMD ["/init"]
+1 -1
View File
@@ -4,7 +4,7 @@
########
FROM golang:1.18-bullseye as backendbuild
FROM golang:1.20-bullseye as backendbuild
WORKDIR /go/src/github.com/analogj/scrutiny
+14 -10
View File
@@ -1,20 +1,25 @@
# syntax=docker/dockerfile:1.4
########################################################################################################################
# Web Image
# NOTE: this image requires the `make binary-frontend` target to have been run before `docker build` The `dist` directory must exist.
########################################################################################################################
######## Build the frontend
FROM --platform=${BUILDPLATFORM} node AS frontendbuild
WORKDIR /go/src/github.com/analogj/scrutiny
COPY --link . /go/src/github.com/analogj/scrutiny
########
FROM golang:1.18-bullseye as backendbuild
RUN make binary-frontend
######## Build the backend
FROM golang:1.20-bullseye as backendbuild
WORKDIR /go/src/github.com/analogj/scrutiny
COPY . /go/src/github.com/analogj/scrutiny
COPY --link . /go/src/github.com/analogj/scrutiny
RUN make binary-clean binary-all WEB_BINARY_NAME=scrutiny
########
######## Combine build artifacts in runtime image
FROM debian:bullseye-slim as runtime
EXPOSE 8080
WORKDIR /opt/scrutiny
@@ -22,10 +27,9 @@ ENV PATH="/opt/scrutiny/bin:${PATH}"
RUN apt-get update && apt-get install -y ca-certificates curl tzdata && update-ca-certificates
COPY --from=backendbuild /go/src/github.com/analogj/scrutiny/scrutiny /opt/scrutiny/bin/
COPY dist /opt/scrutiny/web
RUN chmod +x /opt/scrutiny/bin/scrutiny && \
mkdir -p /opt/scrutiny/web && \
COPY --link --from=backendbuild --chmod=755 /go/src/github.com/analogj/scrutiny/scrutiny /opt/scrutiny/bin/
COPY --link --from=frontendbuild --chmod=644 /go/src/github.com/analogj/scrutiny/dist /opt/scrutiny/web
RUN mkdir -p /opt/scrutiny/web && \
mkdir -p /opt/scrutiny/config && \
chmod -R ugo+rwx /opt/scrutiny/config
CMD ["/opt/scrutiny/bin/scrutiny", "start"]
+9
View File
@@ -7,6 +7,8 @@
# adding ability to customize the cron schedule.
COLLECTOR_CRON_SCHEDULE=${COLLECTOR_CRON_SCHEDULE:-"0 0 * * *"}
COLLECTOR_RUN_STARTUP=${COLLECTOR_RUN_STARTUP:-"false"}
COLLECTOR_RUN_STARTUP_SLEEP=${COLLECTOR_RUN_STARTUP_SLEEP:-"1"}
# if the cron schedule has been overridden via env variable (eg docker-compose) we should make sure to strip quotes
[[ "${COLLECTOR_CRON_SCHEDULE}" == \"*\" || "${COLLECTOR_CRON_SCHEDULE}" == \'*\' ]] && COLLECTOR_CRON_SCHEDULE="${COLLECTOR_CRON_SCHEDULE:1:-1}"
@@ -14,6 +16,13 @@ COLLECTOR_CRON_SCHEDULE=${COLLECTOR_CRON_SCHEDULE:-"0 0 * * *"}
# replace placeholder with correct value
sed -i 's|{COLLECTOR_CRON_SCHEDULE}|'"${COLLECTOR_CRON_SCHEDULE}"'|g' /etc/cron.d/scrutiny
if [[ "${COLLECTOR_RUN_STARTUP}" == "true" ]]; then
sleep ${COLLECTOR_RUN_STARTUP_SLEEP}
echo "starting scrutiny collector (run-once mode. subsequent calls will be triggered via cron service)"
/opt/scrutiny/bin/scrutiny-collector-metrics run
fi
# now that we have the env start cron in the foreground
echo "starting cron"
su -c "cron -f -L 15" root
+181 -1
View File
@@ -1 +1,181 @@
> See [docker/example.hubspoke.docker-compose.yml](https://github.com/AnalogJ/scrutiny/blob/master/docker/example.hubspoke.docker-compose.yml) for a docker-compose file.
>
See [docker/example.hubspoke.docker-compose.yml](https://github.com/AnalogJ/scrutiny/blob/master/docker/example.hubspoke.docker-compose.yml)
for a docker-compose file.
> The following guide was contributed by @TinJoy59 in #417
> It describes how to deploy the Scrutiny in Hub/Spoke mode, where the Hub is running in Docker, and the Spokes (
> collectors) are running as binaries.
> He's using Proxmox & Synology in his guide, however this should be applicable for almost anyone
# S.M.A.R.T. Monitoring with Scrutiny across machines
![drawing-3-1671744407](https://user-images.githubusercontent.com/86809766/209230023-bf1ef9f8-65c4-454e-9e1a-be1293cd737e.png)
### 🤔 The problem:
Scrutiny offers a nice Docker package called "Omnibus" that can monitor HDDs attached to a Docker host with relative
ease. Scrutiny can also be installed in a Hub-Spoke layout where Web interface, Database and Collector come in 3
separate packages. The official documentation assumes that the spokes in the "Hub-Spokes layout" run Docker, which is
not always the case. The third approach is to install Scrutiny manually, entirely outside of Docker.
### 💡 The solution:
This tutorial provides a hybrid configuration where the Hub lives in a Docker instance while the spokes have only
Scrutiny Collector installed manually. The Collector periodically send data to the Hub. It's not mind-boggling hard to
understand but someone might struggle with the setup. This is for them.
### 🖥️ My setup:
I have a Proxmox cluster where one VM runs Docker and all monitoring services - Grafana, Prometheus, various exporters,
InfluxDB and so forth. Another VM runs the NAS - OpenMediaVault v6, where all hard drives reside. The Scrutiny Collector
is triggered every 30min to collect data on the drives. The data is sent to the Docker VM, running InfluxDB.
## Setting up the Hub
![drawing-3-1671744714](https://user-images.githubusercontent.com/86809766/209230113-c954d834-521b-4555-bcd2-eb6b80f343be.png)
The Hub consists of Scrutiny Web - a web interface for viewing the SMART data. And InfluxDB, where the smartmon data is
stored.
[🔗This is the official Hub-Spoke layout in docker-compose.](https://github.com/AnalogJ/scrutiny/blob/master/docker/example.hubspoke.docker-compose.yml)
We are going to reuse parts of it. The ENV variables provide the necessary configuration for the initial setup, both for
InfluxDB and Scrutiny.
If you are working with and existing InfluxDB instance, you can forgo all the `INIT` variables as they already exist.
The official Scrutiny documentation has a
sample [scrutiny.yaml ](https://github.com/AnalogJ/scrutiny/blob/master/example.scrutiny.yaml)file that normally
contains the connection and notification details but I always find it easier to configure as much as possible in the
docker-compose.
```yaml
version: "3.4"
networks:
monitoring: # A common network for all monitoring services to communicate into
external: true
notifications: # To Gotify or another Notification service
external: true
services:
influxdb:
container_name: influxdb
image: influxdb:2.1-alpine
ports:
- 8086:8086
volumes:
- ${DIR_CONFIG}/influxdb2/db:/var/lib/influxdb2
- ${DIR_CONFIG}/influxdb2/config:/etc/influxdb2
environment:
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=Admin
- DOCKER_INFLUXDB_INIT_PASSWORD=${PASSWORD}
- DOCKER_INFLUXDB_INIT_ORG=homelab
- DOCKER_INFLUXDB_INIT_BUCKET=scrutiny
- DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=your-very-secret-token
restart: unless-stopped
networks:
- monitoring
scrutiny:
container_name: scrutiny
image: ghcr.io/analogj/scrutiny:master-web
ports:
- 8080:8080
volumes:
- ${DIR_CONFIG}/scrutiny/config:/opt/scrutiny/config
environment:
- SCRUTINY_WEB_INFLUXDB_HOST=influxdb
- SCRUTINY_WEB_INFLUXDB_PORT=8086
- SCRUTINY_WEB_INFLUXDB_TOKEN=your-very-secret-token
- SCRUTINY_WEB_INFLUXDB_ORG=homelab
- SCRUTINY_WEB_INFLUXDB_BUCKET=scrutiny
# Optional but highly recommended to notify you in case of a problem
- SCRUTINY_WEB_NOTIFY_URLS=["http://gotify:80/message?token=a-gotify-token"]
depends_on:
- influxdb
restart: unless-stopped
networks:
- notifications
- monitoring
```
A freshly initialized Scrutiny instance can be accessed on port 8080, eg. `192.168.0.100:8080`. The interface will be
empty because no metrics have been collected yet.
## Setting up a Spoke ***without*** Docker
![drawing-3-1671744208](https://user-images.githubusercontent.com/86809766/209230155-386a8644-b506-497f-8245-0d24e15c9063.png)
A spoke consists of the Scrutiny Collector binary that is run on a set interval via crontab and sends the data to the
Hub. The official
documentation [describes the manual setup of the Collector](https://github.com/AnalogJ/scrutiny/blob/master/docs/INSTALL_MANUAL.md#collector)
- dependencies and step by step commands. I have a shortened version that does the same thing but in one line of code.
```bash
# Installing dependencies
apt install smartmontools -y
# 1. Create directory for the binary
# 2. Download the binary into that directory
# 3. Make it exacutable
# 4. List the contents of the library for confirmation
mkdir -p /opt/scrutiny/bin && \
curl -L https://github.com/AnalogJ/scrutiny/releases/download/v0.5.0/scrutiny-collector-metrics-linux-amd64 > /opt/scrutiny/bin/scrutiny-collector-metrics-linux-amd64 && \
chmod +x /opt/scrutiny/bin/scrutiny-collector-metrics-linux-amd64 && \
ls -lha /opt/scrutiny/bin
```
<p class="callout warning">When downloading Github Release Assests, make sure that you have the correct version. The provided example is with Release v0.5.0. [The release list can be found here.](https://github.com/analogj/scrutiny/releases) </p>
Once the Collector is installed, you can run it with the following command. Make sure to add the correct address and
port of your Hub as `--api-endpoint`.
```bash
/opt/scrutiny/bin/scrutiny-collector-metrics-linux-amd64 run --api-endpoint "http://192.168.0.100:8080"
```
This will run the Collector once and populate the Web interface of your Scrutiny instance. In order to collect metrics
for a time series, you need to run the command repeatedly. Here is an example for crontab, running the Collector every
15min.
```bash
# open crontab
crontab -e
# add a line for Scrutiny
*/15 * * * * /opt/scrutiny/bin/scrutiny-collector-metrics-linux-amd64 run --api-endpoint "http://192.168.0.100:8080"
```
The Collector has its own independent config file that lives in `/opt/scrutiny/config/collector.yaml` but I did not find
a need to modify
it. [A default collector.yaml can be found in the official documentation.](https://github.com/AnalogJ/scrutiny/blob/master/example.collector.yaml)
## Setting up a Spoke ***with*** Docker
![drawing-3-1671744277](https://user-images.githubusercontent.com/86809766/209230176-87c9e55a-4e3e-4f5f-9609-335d41529f3d.png)
Setting up a remote Spoke in Docker requires you to split
the [official Hub-Spoke layout docker-compose.yml](https://github.com/AnalogJ/scrutiny/blob/master/docker/example.hubspoke.docker-compose.yml)
. In the following docker-compose you need to provide the `${API_ENDPOINT}`, in my case `http://192.168.0.100:8080`.
Also all drives that you wish to monitor need to be presented to the container under `devices`.
The image handles the periodic scanning of the drives.
```yaml
version: "3.4"
services:
collector:
image: 'ghcr.io/analogj/scrutiny:master-collector'
cap_add:
- SYS_RAWIO
volumes:
- '/run/udev:/run/udev:ro'
environment:
COLLECTOR_API_ENDPOINT: ${API_ENDPOINT}
devices:
- "/dev/sda"
- "/dev/sdb"
```
+37 -5
View File
@@ -275,10 +275,12 @@ to disable Scrutiny analysis for them. Both are non-critical, and have low-corre
If this is effecting your drives, you'll need to do the following:
1. Upgrade to v0.4.13+
2. Reset your drive status using the SQLite script in [#device-failed-but-smart--scrutiny-passed](https://github.com/AnalogJ/scrutiny/blob/master/docs/TROUBLESHOOTING_DEVICE_COLLECTOR.md#device-failed-but-smart--scrutiny-passed)
2. Reset your drive status using the SQLite script
in [#device-failed-but-smart--scrutiny-passed](https://github.com/AnalogJ/scrutiny/blob/master/docs/TROUBLESHOOTING_DEVICE_COLLECTOR.md#device-failed-but-smart--scrutiny-passed)
3. Wait for (or manually start) the collector.
If you'd like to learn more about how the Seagate Ironwolf SMART attributes work under the hood, and how they differ from
If you'd like to learn more about how the Seagate Ironwolf SMART attributes work under the hood, and how they differ
from
other drives, please read the following:
- http://www.users.on.net/~fzabkar/HDD/Seagate_SER_RRER_HEC.html
@@ -286,10 +288,23 @@ other drives, please read the following:
## Hub & Spoke model, with multiple Hosts.
When deploying Scrutiny in a hub & spoke model, it can be difficult to determine exactly which node a set of devices are associated with.
Thankfully the collector has a special `--host-id` flag (or `COLLECTOR_HOST_ID` env variable) that can be used to associate devices with a friendly host name.
![multiple-host-ids image](multiple-host-ids.png)
See the [docs/INSTALL_HUB_SPOKE.md](/docs/INSTALL_HUB_SPOKE.md) guide for more information.
When deploying Scrutiny in a hub & spoke model, it can be difficult to determine exactly which node a set of devices are
associated with.
Thankfully the collector has a special `--host-id` flag (or `COLLECTOR_HOST_ID` env variable) that can be used to
associate devices with a friendly host name.
The host-id is passed from the collector to the web-api when SMART device data is uploaded. There's 3 ways you can set
the host-id:
- using the collector config
file: [master/example.collector.yaml#L19-L22](https://github.com/AnalogJ/scrutiny/blob/master/example.collector.yaml?rgh-link-date=2022-05-25T15%3A08%3A56Z#L19-L22)
- using the `--host-id` collector CLI
argument: [master/collector/cmd/collector-metrics/collector-metrics.go#L180-L185](https://github.com/AnalogJ/scrutiny/blob/master/collector/cmd/collector-metrics/collector-metrics.go?rgh-link-date=2022-05-25T15%3A08%3A56Z#L180-L185)
- using the `COLLECTOR_HOST_ID` environmental variable.
See the [docs/INSTALL_HUB_SPOKE.md](/docs/INSTALL_HUB_SPOKE.md) guide for more information.
## Collector DEBUG mode
@@ -305,3 +320,20 @@ Or if you're not using docker, you can pass CLI arguments to the collector durin
```bash
scrutiny-collector-metrics run --debug --log-file /tmp/collector.log
```
## Collector trigger on startup
When the `omnibus` docker image starts up, it will automatically trigger the collector, which will populate the Scrutiny
Webui with your disks.
This is not the case when running the collector docker image in **hub/spoke** mode, as the collector and webui are
running in different containers (and potentially different host machines), so
the web container may not be ready for incoming connections. By default the container will only run the collector at the
time specified in the cron schedule.
You can force the collector to run on startup using the following env variables:
- `-e COLLECTOR_RUN_STARTUP=true` - forces the collector to run on startup (cron will be started after the collector
completes)
- `-e COLLECTOR_RUN_STARTUP_SLEEP=10` - if `COLLECTOR_RUN_STARTUP` is enabled, you can use this env variable to
configure the delay before the collector is run (default: `1` second). Used to ensure the web container has started
successfully.
+30
View File
@@ -82,6 +82,7 @@ this usually related to either:
variables
- remove the `SCRUTINY_WEB=true` and `SCRUTINY_COLLECTOR=true` environmental variables. They were used by the LSIO
image, but are unnecessary and cause issues with the official Scrutiny image.
- Change your volume mappings to `/opt/scrutiny` from `/scrutiny`
- Updated versions of the [LSIO Scrutiny images are broken](https://github.com/linuxserver/docker-scrutiny/issues/22),
as they have not installed InfluxDB which is a required dependency of Scrutiny v0.4.x
- You can revert to an earlier version of the LSIO image (`lscr.io/linuxserver/scrutiny:060ac7b8-ls34`), or just
@@ -394,3 +395,32 @@ After running the Curl command above, you'll see a JSON response that looks like
You must copy the token field from the JSON response, and save it in your `scrutiny.yaml` config file. After that's
done, you can start the Scrutiny server
## Customize InfluxDB Admin Username & Password
The full set of InfluxDB configuration options are available
in [code](https://github.com/AnalogJ/scrutiny/blob/master/webapp/backend/pkg/config/config.go?rgh-link-date=2023-01-19T16%3A23%3A40Z#L49-L51)
.
During first startup Scrutiny will connect to the unprotected InfluxDB server, start the setup process (via API) using a
username and password of `admin`:`password12345` and then create an API token of `scrutiny-default-admin-token`.
After that's complete, it will use the api token for all subsequent communication with InfluxDB.
You can configure the values for the Admin username, password and token using the config file, or env variables:
#### Config File Example
```yaml
web:
influxdb:
token: 'my-custom-token'
init_username: 'my-custom-username'
init_password: 'my-custom-password'
```
#### Environmental Variables Example
`SCRUTINY_WEB_INFLUXDB_TOKEN` , `SCRUTINY_WEB_INFLUXDB_INIT_USERNAME` and `SCRUTINY_WEB_INFLUXDB_INIT_PASSWORD`
It's safe to change the InfluxDB Admin username/password after setup has completed, only the API token is used for
subsequent communication with InfluxDB.
Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

+4
View File
@@ -31,6 +31,10 @@ devices:
# - device: /dev/sda
# type: 'sat'
#
# # example for using `-d sat,auto`, notice the square brackets (workaround for #418)
# - device: /dev/sda
# type: ['sat,auto']
#
# # example to show how to ignore a specific disk/device.
# - device: /dev/sda
# ignore: true
+6
View File
@@ -47,6 +47,11 @@ web:
# org: 'my-org'
# bucket: 'bucket'
retention_policy: true
# if you wish to disable TLS certificate verification,
# when using self-signed certificates for example,
# then uncomment the lines below and set `insecure_skip_verify: true`
# tls:
# insecure_skip_verify: false
log:
file: '' #absolute or relative paths allowed, eg. web.log
@@ -68,6 +73,7 @@ log:
# - "pushbullet://api-token[/device/#channel/email]"
# - "ifttt://key/?events=event1[,event2,...]&value1=value1&value2=value2&value3=value3"
# - "mattermost://[username@]mattermost-host/token[/channel]"
# - "ntfy://username:password@host:port/topic"
# - "hangouts://chat.googleapis.com/v1/spaces/FOO/messages?key=bar&token=baz"
# - "zulip://bot-mail:bot-key@zulip-domain/?stream=name-or-id&topic=name"
# - "join://shoutrrr:api-key@join/?devices=device1[,device2, ...][&icon=icon][&title=title]"
+31 -30
View File
@@ -1,33 +1,33 @@
module github.com/analogj/scrutiny
go 1.18
go 1.20
require (
github.com/analogj/go-util v0.0.0-20190301173314-5295e364eb14
github.com/containrrr/shoutrrr v0.6.1
github.com/fatih/color v1.10.0
github.com/containrrr/shoutrrr v0.7.1
github.com/fatih/color v1.15.0
github.com/gin-gonic/gin v1.6.3
github.com/glebarez/sqlite v1.4.5
github.com/go-gormigrate/gormigrate/v2 v2.0.0
github.com/golang/mock v1.4.3
github.com/golang/mock v1.6.0
github.com/influxdata/influxdb-client-go/v2 v2.9.0
github.com/jaypipes/ghw v0.6.1
github.com/mitchellh/mapstructure v1.2.2
github.com/mitchellh/mapstructure v1.5.0
github.com/samber/lo v1.25.0
github.com/sirupsen/logrus v1.4.2
github.com/spf13/viper v1.7.0
github.com/stretchr/testify v1.7.1
github.com/sirupsen/logrus v1.6.0
github.com/spf13/viper v1.14.0
github.com/stretchr/testify v1.8.1
github.com/urfave/cli/v2 v2.2.0
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
golang.org/x/sync v0.1.0
gorm.io/gorm v1.23.5
)
require (
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deepmap/oapi-codegen v1.8.2 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.17.2 // indirect
@@ -35,44 +35,45 @@ require (
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.2.0 // indirect
github.com/golang/protobuf v1.4.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
github.com/jaypipes/pcidb v0.5.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/json-iterator/go v1.1.9 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
github.com/kvz/logstreamer v0.0.0-20201023134116-02d20f4338f5 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/pelletier/go-toml v1.7.0 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/crypto v0.1.0 // indirect
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64 // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
golang.org/x/text v0.3.5 // indirect
google.golang.org/protobuf v1.23.0 // indirect
gopkg.in/ini.v1 v1.55.0 // indirect
golang.org/x/net v0.1.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/term v0.1.0 // indirect
golang.org/x/text v0.4.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect
modernc.org/libc v1.16.8 // indirect
modernc.org/mathutil v1.4.1 // indirect
+864 -122
View File
File diff suppressed because it is too large Load Diff
+7 -1
View File
@@ -29,8 +29,14 @@ func main() {
os.Exit(1)
}
configFilePath := "/opt/scrutiny/config/scrutiny.yaml"
configFilePathAlternative := "/opt/scrutiny/config/scrutiny.yml"
if !utils.FileExists(configFilePath) && utils.FileExists(configFilePathAlternative) {
configFilePath = configFilePathAlternative
}
//we're going to load the config file manually, since we need to validate it.
err = config.ReadConfig("/opt/scrutiny/config/scrutiny.yaml") // Find and read the config file
err = config.ReadConfig(configFilePath) // Find and read the config file
if _, ok := err.(errors.ConfigFileMissingError); ok { // Handle errors reading the config file
//ignore "could not find config file"
} else if err != nil {
+1
View File
@@ -49,6 +49,7 @@ func (c *configuration) Init() error {
c.SetDefault("web.influxdb.init_username", "admin")
c.SetDefault("web.influxdb.init_password", "password12345")
c.SetDefault("web.influxdb.token", "scrutiny-default-admin-token")
c.SetDefault("web.influxdb.tls.insecure_skip_verify", false)
c.SetDefault("web.influxdb.retention_policy", true)
//c.SetDefault("disks.include", []string{})
@@ -2,6 +2,7 @@ package database
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"github.com/analogj/scrutiny/webapp/backend/pkg/config"
@@ -95,11 +96,20 @@ func NewScrutinyRepository(appConfig config.Interface, globalLogger logrus.Field
influxdbUrl := fmt.Sprintf("%s://%s:%s", appConfig.GetString("web.influxdb.scheme"), appConfig.GetString("web.influxdb.host"), appConfig.GetString("web.influxdb.port"))
globalLogger.Debugf("InfluxDB url: %s", influxdbUrl)
client := influxdb2.NewClient(influxdbUrl, appConfig.GetString("web.influxdb.token"))
tlsConfig := &tls.Config{
InsecureSkipVerify: appConfig.GetBool("web.influxdb.tls.insecure_skip_verify"),
}
globalLogger.Infof("InfluxDB certificate verification: %t\n", !tlsConfig.InsecureSkipVerify)
client := influxdb2.NewClientWithOptions(
influxdbUrl,
appConfig.GetString("web.influxdb.token"),
influxdb2.DefaultOptions().SetTLSConfig(tlsConfig),
)
//if !appConfig.IsSet("web.influxdb.token") {
globalLogger.Debugf("Determine Influxdb setup status...")
influxSetupComplete, err := InfluxSetupComplete(influxdbUrl)
influxSetupComplete, err := InfluxSetupComplete(influxdbUrl, tlsConfig)
if err != nil {
return nil, fmt.Errorf("failed to check influxdb setup status - %w", err)
}
@@ -218,7 +228,7 @@ func (sr *scrutinyRepository) HealthCheck(ctx context.Context) error {
}
func InfluxSetupComplete(influxEndpoint string) (bool, error) {
func InfluxSetupComplete(influxEndpoint string, tlsConfig *tls.Config) (bool, error) {
influxUri, err := url.Parse(influxEndpoint)
if err != nil {
return false, err
@@ -228,7 +238,8 @@ func InfluxSetupComplete(influxEndpoint string) (bool, error) {
return false, err
}
res, err := http.Get(influxUri.String())
client := &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}
res, err := client.Get(influxUri.String())
if err != nil {
return false, err
}
@@ -325,6 +325,12 @@ func (sr *scrutinyRepository) Migrate(ctx context.Context) error {
SettingDataType: "bool",
SettingValueBool: false,
},
{
SettingKeyName: "line_stroke",
SettingKeyDescription: "Temperature chart line stroke ('smooth' | 'straight' | 'stepline')",
SettingDataType: "string",
SettingValueString: "smooth",
},
{
SettingKeyName: "metrics.notify_level",
@@ -348,6 +354,21 @@ func (sr *scrutinyRepository) Migrate(ctx context.Context) error {
return tx.Create(&defaultSettings).Error
},
},
{
ID: "m20221115214900", // add line_stroke setting.
Migrate: func(tx *gorm.DB) error {
//add line_stroke setting default.
var defaultSettings = []m20220716214900.Setting{
{
SettingKeyName: "line_stroke",
SettingKeyDescription: "Temperature chart line stroke ('smooth' | 'straight' | 'stepline')",
SettingDataType: "string",
SettingValueString: "smooth",
},
}
return tx.Create(&defaultSettings).Error
},
},
})
if err := m.Migrate(); err != nil {
+1
View File
@@ -14,6 +14,7 @@ type Settings struct {
DashboardSort string `json:"dashboard_sort" mapstructure:"dashboard_sort"`
TemperatureUnit string `json:"temperature_unit" mapstructure:"temperature_unit"`
FileSizeSIUnits bool `json:"file_size_si_units" mapstructure:"file_size_si_units"`
LineStroke string `json:"line_stroke" mapstructure:"line_stroke"`
Metrics struct {
NotifyLevel int `json:"notify_level" mapstructure:"notify_level"`
+10 -6
View File
@@ -5,6 +5,13 @@ import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
"github.com/analogj/go-util/utils"
"github.com/analogj/scrutiny/webapp/backend/pkg"
"github.com/analogj/scrutiny/webapp/backend/pkg/config"
@@ -15,12 +22,6 @@ import (
shoutrrrTypes "github.com/containrrr/shoutrrr/pkg/types"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
const NotifyFailureTypeEmailTest = "EmailTest"
@@ -386,6 +387,9 @@ func (n *Notify) GenShoutrrrNotificationParams(shoutrrrUrl string) (string, *sho
case "join":
(*params)["title"] = subject
(*params)["icon"] = logoUrl
case "ntfy":
(*params)["title"] = subject
(*params)["icon"] = logoUrl
case "opsgenie":
(*params)["title"] = subject
case "pushbullet":
+1 -1
View File
@@ -2,4 +2,4 @@ package version
// VERSION is the app-global version string, which will be replaced with a
// new value during packaging
const VERSION = "0.6.0"
const VERSION = "0.7.1"
+9
View File
@@ -103,6 +103,7 @@ func (suite *ServerTestSuite) TestHealthRoute() {
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions {
// when running test suite in github actions, we run an influxdb service as a sidecar.
@@ -145,6 +146,7 @@ func (suite *ServerTestSuite) TestRegisterDevicesRoute() {
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions {
// when running test suite in github actions, we run an influxdb service as a sidecar.
@@ -187,6 +189,7 @@ func (suite *ServerTestSuite) TestUploadDeviceMetricsRoute() {
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions {
// when running test suite in github actions, we run an influxdb service as a sidecar.
@@ -244,6 +247,7 @@ func (suite *ServerTestSuite) TestPopulateMultiple() {
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
if _, isGithubActions := os.LookupEnv("GITHUB_ACTIONS"); isGithubActions {
// when running test suite in github actions, we run an influxdb service as a sidecar.
@@ -342,6 +346,7 @@ func (suite *ServerTestSuite) TestSendTestNotificationRoute_WebhookFailure() {
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"https://unroutable.domain.example.asdfghj"})
fakeConfig.EXPECT().GetInt(fmt.Sprintf("%s.metrics.notify_level", config.DB_USER_SETTINGS_SUBKEY)).AnyTimes().Return(int(pkg.MetricsNotifyLevelFail))
@@ -387,6 +392,7 @@ func (suite *ServerTestSuite) TestSendTestNotificationRoute_ScriptFailure() {
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"script:///missing/path/on/disk"})
fakeConfig.EXPECT().GetInt(fmt.Sprintf("%s.metrics.notify_level", config.DB_USER_SETTINGS_SUBKEY)).AnyTimes().Return(int(pkg.MetricsNotifyLevelFail))
@@ -432,6 +438,7 @@ func (suite *ServerTestSuite) TestSendTestNotificationRoute_ScriptSuccess() {
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"script:///usr/bin/env"})
fakeConfig.EXPECT().GetInt(fmt.Sprintf("%s.metrics.notify_level", config.DB_USER_SETTINGS_SUBKEY)).AnyTimes().Return(int(pkg.MetricsNotifyLevelFail))
@@ -477,6 +484,7 @@ func (suite *ServerTestSuite) TestSendTestNotificationRoute_ShoutrrrFailure() {
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{"discord://invalidtoken@channel"})
fakeConfig.EXPECT().GetInt(fmt.Sprintf("%s.metrics.notify_level", config.DB_USER_SETTINGS_SUBKEY)).AnyTimes().Return(int(pkg.MetricsNotifyLevelFail))
@@ -521,6 +529,7 @@ func (suite *ServerTestSuite) TestGetDevicesSummaryRoute_Nvme() {
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.org").Return("scrutiny").AnyTimes()
fakeConfig.EXPECT().GetString("web.influxdb.bucket").Return("metrics").AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.tls.insecure_skip_verify").Return(false).AnyTimes()
fakeConfig.EXPECT().GetBool("web.influxdb.retention_policy").Return(false).AnyTimes()
fakeConfig.EXPECT().GetStringSlice("notify.urls").AnyTimes().Return([]string{})
fakeConfig.EXPECT().GetInt(fmt.Sprintf("%s.metrics.notify_level", config.DB_USER_SETTINGS_SUBKEY)).AnyTimes().Return(int(pkg.MetricsNotifyLevelFail))
-2
View File
@@ -52,7 +52,6 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
@@ -91,7 +90,6 @@
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"defaultConfiguration": "production",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
+10 -10
View File
@@ -10,10 +10,10 @@ module.exports = function (config)
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client : {
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
@@ -21,13 +21,13 @@ module.exports = function (config)
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters : ['progress', 'kjhtml'],
port : 9876,
colors : true,
logLevel : config.LOG_INFO,
autoWatch : true,
browsers : ['Chrome'],
singleRun : false,
restartOnFileChange : true
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
+8291 -27143
View File
File diff suppressed because it is too large Load Diff
+49 -60
View File
@@ -7,7 +7,7 @@
"start": "ng serve --open",
"start:mem": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng serve --open",
"build": "ng build",
"build:prod": "ng build --prod",
"build:prod": "ng build --configuration production",
"build:prod:mem": "node --max_old_space_size=6144 ./node_modules/@angular/cli/bin/ng build --prod",
"test": "ng test",
"lint": "ng lint",
@@ -20,66 +20,55 @@
},
"private": true,
"dependencies": {
"@angular/animations": "9.1.4",
"@angular/cdk": "9.2.2",
"@angular/common": "9.1.4",
"@angular/compiler": "9.1.4",
"@angular/core": "9.1.4",
"@angular/forms": "9.1.4",
"@angular/material": "9.2.2",
"@angular/material-moment-adapter": "9.2.2",
"@angular/platform-browser": "9.1.4",
"@angular/platform-browser-dynamic": "9.1.4",
"@angular/router": "9.1.4",
"@fullcalendar/angular": "4.4.5-beta",
"@fullcalendar/core": "4.4.0",
"@fullcalendar/daygrid": "4.4.0",
"@fullcalendar/interaction": "4.4.0",
"@fullcalendar/list": "4.4.0",
"@fullcalendar/moment": "4.4.0",
"@fullcalendar/rrule": "4.4.0",
"@fullcalendar/timegrid": "4.4.0",
"@types/humanize-duration": "^3.18.1",
"apexcharts": "3.19.2",
"crypto-js": "3.3.0",
"highlight.js": "10.0.1",
"humanize-duration": "^3.24.0",
"lodash": "4.17.15",
"moment": "2.24.0",
"ng-apexcharts": "1.5.12",
"ngx-markdown": "9.0.0",
"ngx-quill": "9.1.0",
"perfect-scrollbar": "1.5.0",
"quill": "1.3.7",
"rrule": "2.6.4",
"rxjs": "6.5.5",
"tslib": "1.11.1",
"web-animations-js": "2.3.2",
"zone.js": "0.10.3"
"@angular/animations": "v13-lts",
"@angular/cdk": "v13-lts",
"@angular/common": "v13-lts",
"@angular/compiler": "v13-lts",
"@angular/core": "v13-lts",
"@angular/forms": "v13-lts",
"@angular/material": "v13-lts",
"@angular/material-moment-adapter": "v13-lts",
"@angular/platform-browser": "v13-lts",
"@angular/platform-browser-dynamic": "v13-lts",
"@angular/router": "v13-lts",
"@types/humanize-duration": "^3.27.1",
"crypto-js": "^4.1.1",
"highlight.js": "^11.6.0",
"humanize-duration": "^3.27.3",
"lodash": "4.17.21",
"moment": "^2.29.4",
"ng-apexcharts": "^1.7.4",
"ngx-markdown": "^13.1.0",
"perfect-scrollbar": "^1.5.5",
"quill": "^1.3.7",
"rrule": "^2.7.1",
"rxjs": "^7.5.7",
"tslib": "^2.4.1",
"web-animations-js": "^2.3.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "0.901.4",
"@angular/cli": "9.1.4",
"@angular/compiler-cli": "9.1.4",
"@angular/language-service": "9.1.4",
"@types/crypto-js": "3.1.45",
"@types/highlight.js": "9.12.3",
"@types/jasmine": "3.5.10",
"@types/jasminewd2": "2.0.8",
"@types/lodash": "4.14.150",
"@types/node": "12.12.37",
"codelyzer": "5.2.2",
"jasmine-core": "3.5.0",
"jasmine-spec-reporter": "4.2.1",
"karma": "5.0.4",
"karma-chrome-launcher": "3.1.0",
"karma-coverage-istanbul-reporter": "2.1.1",
"karma-jasmine": "3.0.3",
"karma-jasmine-html-reporter": "1.5.3",
"protractor": "5.4.4",
"tailwindcss": "1.4.4",
"ts-node": "8.3.0",
"tslint": "6.1.2",
"typescript": "3.8.3"
"@angular-devkit/build-angular": "v13-lts",
"@angular/cli": "v13-lts",
"@angular/compiler-cli": "v13-lts",
"@angular/language-service": "v13-lts",
"@types/crypto-js": "^4.1.1",
"@types/highlight.js": "^10.1.0",
"@types/jasmine": "^4.3.0",
"@types/jasminewd2": "^2.0.10",
"@types/lodash": "^4.14.188",
"@types/node": "^18.11.9",
"codelyzer": "^6.0.2",
"jasmine-core": "^4.5.0",
"jasmine-spec-reporter": "^7.0.0",
"karma": "^6.4.1",
"karma-chrome-launcher": "^3.1.1",
"karma-coverage": "^2.2.0",
"karma-jasmine": "^5.1.0",
"karma-jasmine-html-reporter": "^2.0.0",
"protractor": "^7.0.0",
"tailwindcss": "^3.2.3",
"ts-node": "^10.9.1",
"tslint": "^6.1.3",
"typescript": "^4.6.4"
}
}
@@ -56,7 +56,7 @@ export class TreoDateRangeComponent implements ControlValueAccessor, OnInit, OnD
private _timeFormat: string;
private _timeRange: boolean;
private readonly _timeRegExp: RegExp;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -31,7 +31,7 @@ export class TreoMessageComponent implements OnInit, OnDestroy
private _dismissed: null | boolean;
private _showIcon: boolean;
private _type: TreoMessageType;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -23,7 +23,7 @@ export class TreoHorizontalNavigationBasicItemComponent implements OnInit, OnDes
// Private
private _treoHorizontalNavigationComponent: TreoHorizontalNavigationComponent;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -32,7 +32,7 @@ export class TreoHorizontalNavigationBranchItemComponent implements OnInit, OnDe
// Private
private _treoHorizontalNavigationComponent: TreoHorizontalNavigationComponent;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -23,7 +23,7 @@ export class TreoHorizontalNavigationDividerItemComponent implements OnInit, OnD
// Private
private _treoHorizontalNavigationComponent: TreoHorizontalNavigationComponent;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -23,7 +23,7 @@ export class TreoHorizontalNavigationSpacerItemComponent implements OnInit, OnDe
// Private
private _treoHorizontalNavigationComponent: TreoHorizontalNavigationComponent;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -23,7 +23,7 @@ export class TreoHorizontalNavigationComponent implements OnInit, OnDestroy
// Private
private _navigation: TreoNavigationItem[];
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -35,7 +35,7 @@ export class TreoVerticalNavigationAsideItemComponent implements OnInit, OnDestr
// Private
private _treoVerticalNavigationComponent: TreoVerticalNavigationComponent;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -23,7 +23,7 @@ export class TreoVerticalNavigationBasicItemComponent implements OnInit, OnDestr
// Private
private _treoVerticalNavigationComponent: TreoVerticalNavigationComponent;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -38,7 +38,7 @@ export class TreoVerticalNavigationCollapsableItemComponent implements OnInit, O
// Private
private _treoVerticalNavigationComponent: TreoVerticalNavigationComponent;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -23,7 +23,7 @@ export class TreoVerticalNavigationDividerItemComponent implements OnInit, OnDes
// Private
private _treoVerticalNavigationComponent: TreoVerticalNavigationComponent;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -27,7 +27,7 @@ export class TreoVerticalNavigationGroupItemComponent implements OnInit, OnDestr
// Private
private _treoVerticalNavigationComponent: TreoVerticalNavigationComponent;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -23,7 +23,7 @@ export class TreoVerticalNavigationSpacerItemComponent implements OnInit, OnDest
// Private
private _treoVerticalNavigationComponent: TreoVerticalNavigationComponent;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -65,7 +65,7 @@ export class TreoVerticalNavigationComponent implements OnInit, AfterViewInit, O
private _position: TreoVerticalNavigationPosition;
private _scrollStrategy: ScrollStrategy;
private _transparentOverlay: boolean | '';
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
@HostBinding('class.treo-vertical-navigation-animations-enabled')
private _animationsEnabled: boolean;
@@ -12,7 +12,7 @@ export class TreoAutogrowDirective implements OnInit, OnDestroy
// Private
private _padding: number;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -24,7 +24,7 @@ export class TreoScrollbarDirective implements OnInit, OnDestroy
private _animation: number | null;
private _enabled: boolean;
private _options: any;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -20,7 +20,7 @@ export class TreoMockApiModule
*
* @param mockDataServices
*/
static forRoot(mockDataServices: any[]): ModuleWithProviders
static forRoot(mockDataServices: any[]): ModuleWithProviders<TreoMockApiModule>
{
return {
ngModule : TreoMockApiModule,
@@ -26,7 +26,6 @@
// 6. Overrides
@import 'overrides/angular-material';
@import 'overrides/fullcalendar';
@import 'overrides/highlightjs';
@import 'overrides/perfect-scrollbar';
@import 'overrides/quill';
@@ -1,878 +0,0 @@
// -----------------------------------------------------------------------------------------------------
// @ FullCalendar overrides
// -----------------------------------------------------------------------------------------------------
.fc {
.fc-view-container {
// Day Grid - Month view
.fc-view.fc-dayGridMonth-view {
.fc-head {
> tr > .fc-head-container {
border: none;
.fc-row {
.fc-day-header {
span {
display: flex;
align-items: center;
justify-content: center;
padding-top: 8px;
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
}
}
}
}
}
.fc-body {
> tr > .fc-widget-content {
border: none;
.fc-day-grid {
.fc-week {
.fc-content-skeleton {
.fc-day-top {
text-align: center;
&.fc-other-month {
opacity: 1;
}
.fc-day-number {
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 21px;
margin: 4px 0;
font-size: 12px;
border-radius: 50%;
float: none;
}
}
.fc-event-container {
.fc-day-grid-event {
display: flex;
align-items: center;
height: 22px;
min-height: 22px;
max-height: 22px;
margin: 0 6px 4px 6px;
padding: 0 8px;
font-size: 12px;
border-radius: 4px;
border: none;
cursor: pointer;
@include treo-breakpoint('xs') {
padding: 0 5px;
}
}
}
.fc-more {
padding: 0 6px;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
@include treo-breakpoint('xs') {
padding: 0 3px;
}
}
}
.fc-highlight-skeleton {
.fc-highlight {
position: relative;
}
}
}
}
}
}
.fc-popover {
&.fc-more-popover {
border: none;
border-radius: 4px;
@include treo-elevation('2xl');
.fc-header {
height: 32px;
min-height: 32px;
max-height: 32px;
padding: 0 8px;
.fc-title {
margin: 0;
padding: 0;
font-size: 12px;
}
}
.fc-body {
max-height: 160px;
overflow: hidden auto;
.fc-event-container {
padding: 8px;
.fc-day-grid-event {
display: flex;
align-items: center;
height: 22px;
min-height: 22px;
max-height: 22px;
margin: 0 0 6px 0;
padding: 0 8px;
font-size: 12px;
line-height: 1;
border-radius: 4px;
border: none;
cursor: pointer;
&:last-child {
margin-bottom: 0;
}
}
}
}
}
}
}
// Time Grid - Week view
.fc-view.fc-timeGridWeek-view {
.fc-head {
> tr > .fc-head-container {
border: none;
.fc-row {
.fc-axis {
width: 48px !important;
}
.fc-day-header {
span {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
&.fc-weekday {
padding-top: 16px;
font-size: 12px;
letter-spacing: 0.055em;
text-transform: uppercase;
font-weight: 400;
}
&.fc-date {
padding-bottom: 12px;
font-size: 26px;
font-weight: 300;
}
}
}
}
}
}
.fc-body {
> tr > .fc-widget-content {
border: none;
.fc-day-grid {
.fc-row {
min-height: 0;
.fc-bg {
.fc-axis {
width: 48px !important;
}
}
.fc-content-skeleton {
padding-bottom: 0;
.fc-axis {
width: 48px !important;
}
.fc-event-container {
.fc-day-grid-event {
display: flex;
align-items: center;
height: 22px;
min-height: 22px;
max-height: 22px;
margin: 0 6px 6px 6px;
padding: 0 8px;
font-size: 12px;
line-height: 1;
border-radius: 4px;
border: none;
cursor: pointer;
}
}
}
}
}
.fc-divider {
border: none;
}
.fc-time-grid {
.fc-bg {
.fc-axis {
border: none;
width: 48px !important;
+ .fc-day {
border: none;
}
}
}
.fc-slats {
.fc-axis {
width: 48px !important;
height: 48px;
text-align: center;
span {
font-size: 12px;
width: 48px;
min-width: 48px;
}
}
}
.fc-content-skeleton {
.fc-axis {
width: 48px !important;
}
.fc-event-container {
margin: 0 12px 0 0;
.fc-time-grid-event {
display: flex;
padding: 8px;
border-radius: 4px;
border: none;
cursor: pointer;
.fc-time,
.fc-title {
font-size: 12px;
}
}
}
}
}
}
}
}
// Time Grid - Day view
.fc-view.fc-timeGridDay-view {
.fc-head {
> tr > .fc-head-container {
border: none;
.fc-row {
.fc-axis {
width: 48px !important;
}
.fc-day-header {
span {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
&.fc-weekday {
padding-top: 16px;
font-size: 12px;
letter-spacing: 0.055em;
text-transform: uppercase;
font-weight: 400;
}
&.fc-date {
padding-bottom: 12px;
font-size: 26px;
font-weight: 300;
}
}
}
}
}
}
.fc-body {
> tr > .fc-widget-content {
border: none;
.fc-day-grid {
.fc-row {
min-height: 0;
.fc-bg {
.fc-axis {
width: 48px !important;
}
}
.fc-content-skeleton {
padding-bottom: 0;
.fc-axis {
width: 48px !important;
}
.fc-event-container {
.fc-day-grid-event {
display: flex;
align-items: center;
height: 22px;
min-height: 22px;
max-height: 22px;
margin: 0 6px 6px 6px;
padding: 0 8px;
font-size: 12px;
line-height: 1;
border-radius: 4px;
border: none;
cursor: pointer;
}
}
}
}
}
.fc-divider {
border: none;
}
.fc-time-grid {
.fc-bg {
.fc-axis {
border: none;
width: 48px !important;
+ .fc-day {
border: none;
}
}
}
.fc-slats {
.fc-axis {
width: 48px !important;
height: 48px;
text-align: center;
span {
font-size: 12px;
width: 48px;
min-width: 48px;
}
}
}
.fc-content-skeleton {
.fc-axis {
width: 48px !important;
}
.fc-event-container {
margin: 0 12px 0 0;
.fc-time-grid-event {
display: flex;
padding: 8px;
border-radius: 4px;
border: none;
cursor: pointer;
.fc-time,
.fc-title {
font-size: 12px;
}
}
}
}
}
}
}
}
// List - Year view
.fc-view.fc-listYear-view {
border: none;
.fc-list-table {
.fc-list-heading {
display: none;
}
.fc-list-item {
display: flex;
cursor: pointer;
td {
display: flex;
align-items: center;
width: auto;
height: 48px;
min-height: 48px;
padding: 0 8px;
border-width: 0 0 1px 0;
&.fc-list-item-date {
order: 1;
padding-left: 16px;
width: 120px;
min-width: 120px;
max-width: 120px;
@include treo-breakpoint('xs') {
width: 100px;
min-width: 100px;
max-width: 100px;
}
> span {
display: flex;
align-items: baseline;
span {
&:first-child {
display: flex;
justify-content: center;
padding-right: 8px;
width: 32px;
min-width: 32px;
max-width: 32px;
font-size: 18px;
@include treo-breakpoint('xs') {
padding-right: 2px;
}
+ span {
display: flex;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.055em;
text-transform: uppercase;
}
}
}
}
}
&.fc-list-item-time {
flex: 0 0 auto;
order: 3;
width: 160px;
min-width: 160px;
max-width: 160px;
@include treo-breakpoint('xs') {
width: 120px;
min-width: 120px;
max-width: 120px;
}
}
&.fc-list-item-marker {
flex: 0 0 auto;
order: 2;
.fc-event-dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
}
&.fc-list-item-title {
flex: 1 1 auto;
order: 4;
padding-right: 24px;
font-weight: 500;
}
}
}
}
}
}
// Day grid event - Dragging
.fc-day-grid-event {
&.fc-dragging,
&.fc-resizing {
display: flex;
align-items: center;
height: 22px;
min-height: 22px;
max-height: 22px;
margin: 0 6px 4px 6px;
padding: 0 8px;
font-size: 12px;
line-height: 1;
border-radius: 4px;
border: none;
}
}
}
// -----------------------------------------------------------------------------------------------------
// @ Theming
// -----------------------------------------------------------------------------------------------------
@include treo-theme {
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);
$primary: map-get($theme, primary);
.fc {
.fc-view-container {
// Day Grid - Month view
.fc-view.fc-dayGridMonth-view {
.fc-head {
> tr > .fc-head-container {
.fc-row {
.fc-day-header {
border-color: map-get($foreground, divider);
span {
color: map-get($foreground, secondary-text);
}
}
}
}
}
.fc-body {
> tr > .fc-widget-content {
.fc-day-grid {
.fc-week {
.fc-bg {
.fc-day {
border-color: map-get($foreground, divider);
&.fc-today {
background: none;
}
}
}
.fc-content-skeleton {
.fc-day-top {
&.fc-other-month {
.fc-day-number {
color: map-get($foreground, hint-text);
}
}
&.fc-today {
.fc-day-number {
background: map-get($primary, default);
color: map-get($primary, default-contrast);
}
}
}
.fc-more {
color: map-get($foreground, secondary-text);
}
}
.fc-highlight-skeleton {
.fc-highlight {
background: treo-color('cool-gray', 100);
opacity: 1;
}
}
}
}
}
}
.fc-popover {
background: map-get($background, card);
&.fc-more-popover {
.fc-header {
background: map-get($background, hover);
}
}
}
}
// Time Grid - Week view
.fc-view.fc-timeGridWeek-view {
.fc-head {
> tr > .fc-head-container {
.fc-row {
.fc-axis {
border-color: map-get($foreground, divider);
}
.fc-day-header {
border-color: map-get($foreground, divider);
span {
color: map-get($foreground, secondary-text);
}
}
}
}
}
.fc-body {
> tr > .fc-widget-content {
border: none;
.fc-day-grid {
.fc-bg {
.fc-axis {
border-color: map-get($foreground, divider);
}
.fc-day {
border-color: map-get($foreground, divider);
&.fc-today {
background: none;
}
}
}
}
.fc-divider {
background: map-get($foreground, divider);
}
.fc-time-grid {
.fc-bg {
.fc-day {
border-color: map-get($foreground, divider);
&.fc-today {
background: none;
}
}
}
.fc-slats {
.fc-time {
border-color: map-get($foreground, divider);
}
.fc-widget-content {
border-color: map-get($foreground, divider);
}
}
}
}
}
}
// Time Grid - Day view
.fc-view.fc-timeGridDay-view {
.fc-head {
> tr > .fc-head-container {
.fc-row {
.fc-axis {
border-color: map-get($foreground, divider);
}
.fc-day-header {
border-color: map-get($foreground, divider);
span {
color: map-get($foreground, secondary-text);
}
}
}
}
}
.fc-body {
> tr > .fc-widget-content {
border: none;
.fc-day-grid {
.fc-bg {
.fc-axis {
border-color: map-get($foreground, divider);
}
.fc-day {
border-color: map-get($foreground, divider);
&.fc-today {
background: none;
}
}
}
}
.fc-divider {
background: map-get($foreground, divider);
}
.fc-time-grid {
.fc-bg {
.fc-day {
border-color: map-get($foreground, divider);
&.fc-today {
background: none;
}
}
}
.fc-slats {
.fc-time {
border-color: map-get($foreground, divider);
}
.fc-widget-content {
border-color: map-get($foreground, divider);
}
}
}
}
}
}
// List - Year view
.fc-view.fc-listYear-view {
.fc-list-table {
.fc-list-item {
&:hover {
td {
background-color: map-get($background, hover);
}
}
td {
border-color: map-get($foreground, divider);
&.fc-list-item-date {
> span {
span {
&:first-child {
+ span {
color: map-get($foreground, secondary-text);
}
}
}
}
}
}
}
}
}
}
}
}
@@ -10,6 +10,8 @@ export type DashboardSort = 'status' | 'title' | 'age'
export type TemperatureUnit = 'celsius' | 'fahrenheit'
export type LineStroke = 'smooth' | 'straight' | 'stepline'
export enum MetricsNotifyLevel {
Warn = 1,
@@ -45,6 +47,8 @@ export interface AppConfig {
file_size_si_units?: boolean;
line_stroke?: LineStroke;
// Settings from Scrutiny API
metrics?: {
@@ -73,6 +77,8 @@ export const appConfig: AppConfig = {
temperature_unit: 'celsius',
file_size_si_units: false,
line_stroke: 'smooth',
metrics: {
notify_level: MetricsNotifyLevel.Fail,
status_filter_attributes: MetricsStatusFilterAttributes.All,
@@ -19,7 +19,7 @@ export class ScrutinyConfigModule {
*
* @param config
*/
static forRoot(config: any): ModuleWithProviders {
static forRoot(config: any): ModuleWithProviders<ScrutinyConfigModule> {
return {
ngModule: ScrutinyConfigModule,
providers: [
@@ -32,7 +32,7 @@ export class DashboardDeviceComponent implements OnInit {
config: AppConfig;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
readonly humanizeDuration = humanizeDuration;
@@ -53,6 +53,17 @@
</mat-form-field>
</div>
<div class="flex flex-col mt-5 gt-md:flex-row">
<mat-form-field class="flex-auto gt-xs:pr-3 gt-md:pr-3">
<mat-label>Line stroke</mat-label>
<mat-select [(ngModel)]="lineStroke">
<mat-option value="smooth">Smooth</mat-option>
<mat-option value="straight">Straight</mat-option>
<mat-option value="stepline">Stepline</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="flex flex-col mt-5 gt-md:flex-row">
<mat-form-field class="flex-auto gt-xs:pr-3 gt-md:pr-3">
<mat-label>Device Status - Thresholds</mat-label>
@@ -6,6 +6,7 @@ import {
MetricsStatusFilterAttributes,
MetricsStatusThreshold,
TemperatureUnit,
LineStroke,
Theme
} from 'app/core/config/app.config';
import {ScrutinyConfigService} from 'app/core/config/scrutiny-config.service';
@@ -23,12 +24,13 @@ export class DashboardSettingsComponent implements OnInit {
dashboardSort: string;
temperatureUnit: string;
fileSizeSIUnits: boolean;
lineStroke: string;
theme: string;
statusThreshold: number;
statusFilterAttributes: number;
// Private
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
constructor(
private _configService: ScrutinyConfigService,
@@ -48,6 +50,7 @@ export class DashboardSettingsComponent implements OnInit {
this.dashboardSort = config.dashboard_sort;
this.temperatureUnit = config.temperature_unit;
this.fileSizeSIUnits = config.file_size_si_units;
this.lineStroke = config.line_stroke;
this.theme = config.theme;
this.statusFilterAttributes = config.metrics.status_filter_attributes;
@@ -63,6 +66,7 @@ export class DashboardSettingsComponent implements OnInit {
dashboard_sort: this.dashboardSort as DashboardSort,
temperature_unit: this.temperatureUnit as TemperatureUnit,
file_size_si_units: this.fileSizeSIUnits,
line_stroke: this.lineStroke as LineStroke,
theme: this.theme as Theme,
metrics: {
status_filter_attributes: this.statusFilterAttributes as MetricsStatusFilterAttributes,
@@ -35,7 +35,7 @@ export class SearchComponent implements OnInit, OnDestroy
// Private
private _appearance: 'basic' | 'bar';
private _opened: boolean;
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -21,7 +21,7 @@ export class LayoutComponent implements OnInit, OnDestroy {
theme: Theme;
// Private
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
private systemPrefersDark: boolean;
/**
@@ -10,7 +10,7 @@ import { Subject } from 'rxjs';
export class EmptyLayoutComponent implements OnInit, OnDestroy
{
// Private
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -25,7 +25,7 @@ export class MaterialLayoutComponent implements OnInit, OnDestroy
fixedFooter: boolean;
// Private
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
/**
* Constructor
@@ -36,7 +36,7 @@ export class DashboardComponent implements OnInit, AfterViewInit, OnDestroy
config: AppConfig;
// Private
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
@ViewChild('tempChart', { static: false }) tempChart: ChartComponent;
/**
@@ -193,15 +193,15 @@ export class DashboardComponent implements OnInit, AfterViewInit, OnDestroy
enabled: true
}
},
colors : ['#A3BFFA', '#667EEA'],
colors : ['#667eea', '#9066ea', '#66c0ea', '#66ead2', '#d266ea', '#66ea90'],
fill : {
colors : ['#CED9FB', '#AECDFD'],
colors : ['#b2bef4', '#c7b2f4', '#b2dff4', '#b2f4e8', '#e8b2f4', '#b2f4c7'],
opacity: 0.5,
type : 'solid'
type : 'gradient'
},
series : this._deviceDataTemperatureSeries(),
stroke : {
curve: 'straight',
curve: this.config.line_stroke,
width: 2
},
tooltip: {
@@ -85,7 +85,7 @@ export class DetailComponent implements OnInit, AfterViewInit, OnDestroy {
smartAttributeTableMatSort: MatSort;
// Private
private _unsubscribeAll: Subject<any>;
private _unsubscribeAll: Subject<void>;
private systemPrefersDark: boolean;
readonly humanizeDuration = humanizeDuration;
-6
View File
@@ -7,12 +7,6 @@
// that Treo doesn't support out-of-the-box visually compatible with your application.
// -----------------------------------------------------------------------------------------------------
// FullCalendar
@import '~@fullcalendar/core/main.css';
@import '~@fullcalendar/daygrid/main.css';
@import '~@fullcalendar/timegrid/main.css';
@import '~@fullcalendar/list/main.css';
// Perfect scrollbar
@import '~perfect-scrollbar/css/perfect-scrollbar.css';