Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f67d6f9ae | |||
| 30153f9656 | |||
| 3150201348 | |||
| bce6225e9a | |||
| 893774c557 | |||
| 145996055a | |||
| 1cae5ea864 | |||
| 0fe6e74eb4 | |||
| eb4a738746 | |||
| 90e5d219a2 | |||
| c397a3237f | |||
| 381a6799cc | |||
| 54178eaaf0 | |||
| a6e34f7a44 | |||
| 3e444b199a | |||
| a7c8c75a49 | |||
| 2db6465639 | |||
| 8ac3ab79a4 | |||
| 234a8f9b01 | |||
| 54baeb4c4e | |||
| 48bc7cedf4 | |||
| 9fc11b7140 | |||
| ea3fbc09f1 | |||
| 86145be2b1 |
@@ -3,7 +3,7 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '36 12 * * *'
|
- cron: '36 12 * * *'
|
||||||
push:
|
push:
|
||||||
branches: [ master, influxdb ]
|
branches: [ master, beta ]
|
||||||
# Publish semver tags as releases.
|
# Publish semver tags as releases.
|
||||||
tags: [ 'v*.*.*' ]
|
tags: [ 'v*.*.*' ]
|
||||||
|
|
||||||
@@ -42,6 +42,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
tags: |
|
tags: |
|
||||||
type=ref,enable=true,event=branch,suffix=-collector
|
type=ref,enable=true,event=branch,suffix=-collector
|
||||||
|
type=ref,enable=true,event=tag,suffix=-collector
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
# Build and push Docker image with Buildx (don't push on PR)
|
# Build and push Docker image with Buildx (don't push on PR)
|
||||||
@@ -86,6 +87,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
tags: |
|
tags: |
|
||||||
type=ref,enable=true,event=branch,suffix=-web
|
type=ref,enable=true,event=branch,suffix=-web
|
||||||
|
type=ref,enable=true,event=tag,suffix=-web
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
# Build and push Docker image with Buildx (don't push on PR)
|
# Build and push Docker image with Buildx (don't push on PR)
|
||||||
@@ -130,6 +132,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
tags: |
|
tags: |
|
||||||
type=ref,enable=true,event=branch,suffix=-omnibus
|
type=ref,enable=true,event=branch,suffix=-omnibus
|
||||||
|
type=ref,enable=true,event=tag,suffix=-omnibus
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
# Build and push Docker image with Buildx (don't push on PR)
|
# Build and push Docker image with Buildx (don't push on PR)
|
||||||
|
|||||||
@@ -5,11 +5,17 @@ on:
|
|||||||
release:
|
release:
|
||||||
# Only use the types keyword to narrow down the activity types that will trigger your workflow.
|
# Only use the types keyword to narrow down the activity types that will trigger your workflow.
|
||||||
types: [published]
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag_name:
|
||||||
|
description: 'tag to build artifacts for'
|
||||||
|
required: true
|
||||||
|
default: 'v0.0.0'
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
release-freebsd:
|
release-freebsd:
|
||||||
name: Release FreeBSD
|
name: Release FreeBSD
|
||||||
runs-on: macos-latest
|
runs-on: macos-10.15
|
||||||
env:
|
env:
|
||||||
PROJECT_PATH: /go/src/github.com/analogj/scrutiny
|
PROJECT_PATH: /go/src/github.com/analogj/scrutiny
|
||||||
GOPATH: /go
|
GOPATH: /go
|
||||||
@@ -19,9 +25,9 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: ${{github.event.release.tag_name}}
|
ref: ${{github.event.release.tag_name || github.event.inputs.tag_name }}
|
||||||
- name: Build Binaries
|
- name: Build Binaries
|
||||||
uses: vmactions/freebsd-vm@v0.1.3
|
uses: vmactions/freebsd-vm@v0.1.5
|
||||||
with:
|
with:
|
||||||
envs: 'PROJECT_PATH GOPATH GOOS GOARCH'
|
envs: 'PROJECT_PATH GOPATH GOOS GOARCH'
|
||||||
usesh: true
|
usesh: true
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ jobs:
|
|||||||
name: is-sponsor-label
|
name: is-sponsor-label
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: JasonEtco/is-sponsor-label-action@v1
|
- uses: JasonEtco/is-sponsor-label-action@v1.2.0
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
+2
-2
@@ -25,12 +25,12 @@ If you're working on the frontend and can use mocked data rather than a real bac
|
|||||||
```
|
```
|
||||||
cd webapp/frontend
|
cd webapp/frontend
|
||||||
npm install
|
npm install
|
||||||
ng serve
|
ng serve --deploy-url="/web/" --base-href="/web/"
|
||||||
```
|
```
|
||||||
|
|
||||||
However, if you need to also run the backend, and use real data, you'll need to run the following command:
|
However, if you need to also run the backend, and use real data, you'll need to run the following command:
|
||||||
```
|
```
|
||||||
cd webapp/frontend && ng build --watch --output-path=../../dist --deploy-url="/web/" --base-href="/web/" --prod
|
cd webapp/frontend && ng build --watch --output-path=../../dist --prod
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note: if you do not add `--prod` flag, app will display mocked data for api calls.
|
> Note: if you do not add `--prod` flag, app will display mocked data for api calls.
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ docker run --rm \
|
|||||||
--cap-add SYS_RAWIO \
|
--cap-add SYS_RAWIO \
|
||||||
--device=/dev/sda \
|
--device=/dev/sda \
|
||||||
--device=/dev/sdb \
|
--device=/dev/sdb \
|
||||||
-e SCRUTINY_API_ENDPOINT=http://SCRUTINY_WEB_IPADDRESS:8080 \
|
-e COLLECTOR_API_ENDPOINT=http://SCRUTINY_WEB_IPADDRESS:8080 \
|
||||||
--name scrutiny-collector \
|
--name scrutiny-collector \
|
||||||
ghcr.io/analogj/scrutiny:master-collector
|
ghcr.io/analogj/scrutiny:master-collector
|
||||||
```
|
```
|
||||||
@@ -225,6 +225,21 @@ Or if you're not using docker, you can pass CLI arguments to the collector durin
|
|||||||
scrutiny-collector-metrics run --debug --log-file /tmp/collector.log
|
scrutiny-collector-metrics run --debug --log-file /tmp/collector.log
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# Supported Architectures
|
||||||
|
|
||||||
|
|
||||||
|
| Architecture Name | Binaries | Docker |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| amd64 | :white_check_mark: | :white_check_mark: |
|
||||||
|
| arm-5 | :white_check_mark: | |
|
||||||
|
| arm-6 | :white_check_mark: | |
|
||||||
|
| arm-7 | :white_check_mark: | |
|
||||||
|
| arm64 | :white_check_mark: | :white_check_mark: |
|
||||||
|
| macos-amd64 | | :white_check_mark: |
|
||||||
|
| macos-arm64 | | :white_check_mark: |
|
||||||
|
| windows-amd64 | :white_check_mark: | |
|
||||||
|
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Please see the [CONTRIBUTING.md](CONTRIBUTING.md) for instructions for how to develop and contribute to the scrutiny codebase.
|
Please see the [CONTRIBUTING.md](CONTRIBUTING.md) for instructions for how to develop and contribute to the scrutiny codebase.
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
utils "github.com/analogj/go-util/utils"
|
utils "github.com/analogj/go-util/utils"
|
||||||
@@ -113,7 +114,10 @@ OPTIONS:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.IsSet("api-endpoint") {
|
if c.IsSet("api-endpoint") {
|
||||||
config.Set("api.endpoint", c.String("api-endpoint"))
|
//if the user is providing an api-endpoint with a basepath (eg. http://localhost:8080/scrutiny),
|
||||||
|
//we need to ensure the basepath has a trailing slash, otherwise the url.Parse() path concatenation doesnt work.
|
||||||
|
apiEndpoint := strings.TrimSuffix(c.String("api-endpoint"), "/") + "/"
|
||||||
|
config.Set("api.endpoint", apiEndpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
collectorLogger := logrus.WithFields(logrus.Fields{
|
collectorLogger := logrus.WithFields(logrus.Fields{
|
||||||
@@ -157,7 +161,8 @@ OPTIONS:
|
|||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "api-endpoint",
|
Name: "api-endpoint",
|
||||||
Usage: "The api server endpoint",
|
Usage: "The api server endpoint",
|
||||||
EnvVars: []string{"SCRUTINY_API_ENDPOINT"},
|
EnvVars: []string{"COLLECTOR_API_ENDPOINT", "SCRUTINY_API_ENDPOINT"},
|
||||||
|
//SCRUTINY_API_ENDPOINT is deprecated, but kept for backwards compatibility
|
||||||
},
|
},
|
||||||
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ OPTIONS:
|
|||||||
Name: "api-endpoint",
|
Name: "api-endpoint",
|
||||||
Usage: "The api server endpoint",
|
Usage: "The api server endpoint",
|
||||||
Value: "http://localhost:8080",
|
Value: "http://localhost:8080",
|
||||||
EnvVars: []string{"SCRUTINY_API_ENDPOINT"},
|
EnvVars: []string{"COLLECTOR_API_ENDPOINT"},
|
||||||
},
|
},
|
||||||
|
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func (mc *MetricsCollector) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apiEndpoint, _ := url.Parse(mc.apiEndpoint.String())
|
apiEndpoint, _ := url.Parse(mc.apiEndpoint.String())
|
||||||
apiEndpoint.Path = "/api/devices/register"
|
apiEndpoint, _ = apiEndpoint.Parse("api/devices/register") //this acts like filepath.Join()
|
||||||
|
|
||||||
deviceRespWrapper := new(models.DeviceWrapper)
|
deviceRespWrapper := new(models.DeviceWrapper)
|
||||||
|
|
||||||
@@ -73,6 +73,7 @@ func (mc *MetricsCollector) Run() error {
|
|||||||
|
|
||||||
if !deviceRespWrapper.Success {
|
if !deviceRespWrapper.Success {
|
||||||
mc.logger.Errorln("An error occurred while retrieving filtered devices")
|
mc.logger.Errorln("An error occurred while retrieving filtered devices")
|
||||||
|
mc.logger.Debugln(deviceRespWrapper)
|
||||||
return errors.ApiServerCommunicationError("An error occurred while retrieving filtered devices")
|
return errors.ApiServerCommunicationError("An error occurred while retrieving filtered devices")
|
||||||
} else {
|
} else {
|
||||||
mc.logger.Debugln(deviceRespWrapper)
|
mc.logger.Debugln(deviceRespWrapper)
|
||||||
@@ -146,7 +147,7 @@ func (mc *MetricsCollector) Publish(deviceWWN string, payload []byte) error {
|
|||||||
mc.logger.Infof("Publishing smartctl results for %s\n", deviceWWN)
|
mc.logger.Infof("Publishing smartctl results for %s\n", deviceWWN)
|
||||||
|
|
||||||
apiEndpoint, _ := url.Parse(mc.apiEndpoint.String())
|
apiEndpoint, _ := url.Parse(mc.apiEndpoint.String())
|
||||||
apiEndpoint.Path = fmt.Sprintf("/api/device/%s/smart", strings.ToLower(deviceWWN))
|
apiEndpoint, _ = apiEndpoint.Parse(fmt.Sprintf("api/device/%s/smart", strings.ToLower(deviceWWN)))
|
||||||
|
|
||||||
resp, err := httpClient.Post(apiEndpoint.String(), "application/json", bytes.NewBuffer(payload))
|
resp, err := httpClient.Post(apiEndpoint.String(), "application/json", bytes.NewBuffer(payload))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestApiEndpointParse(t *testing.T) {
|
||||||
|
baseURL, _ := url.Parse("http://localhost:8080/")
|
||||||
|
|
||||||
|
url1, _ := baseURL.Parse("d/e")
|
||||||
|
require.Equal(t, "http://localhost:8080/d/e", url1.String())
|
||||||
|
|
||||||
|
url2, _ := baseURL.Parse("/d/e")
|
||||||
|
require.Equal(t, "http://localhost:8080/d/e", url2.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiEndpointParse_WithBasepathWithoutTrailingSlash(t *testing.T) {
|
||||||
|
baseURL, _ := url.Parse("http://localhost:8080/scrutiny")
|
||||||
|
|
||||||
|
//This testcase is unexpected and can cause issues. We need to ensure the apiEndpoint always has a trailing slash.
|
||||||
|
url1, _ := baseURL.Parse("d/e")
|
||||||
|
require.Equal(t, "http://localhost:8080/d/e", url1.String())
|
||||||
|
|
||||||
|
url2, _ := baseURL.Parse("/d/e")
|
||||||
|
require.Equal(t, "http://localhost:8080/d/e", url2.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApiEndpointParse_WithBasepathWithTrailingSlash(t *testing.T) {
|
||||||
|
baseURL, _ := url.Parse("http://localhost:8080/scrutiny/")
|
||||||
|
|
||||||
|
url1, _ := baseURL.Parse("d/e")
|
||||||
|
require.Equal(t, "http://localhost:8080/scrutiny/d/e", url1.String())
|
||||||
|
|
||||||
|
url2, _ := baseURL.Parse("/d/e")
|
||||||
|
require.Equal(t, "http://localhost:8080/d/e", url2.String())
|
||||||
|
}
|
||||||
+1
-1
@@ -22,7 +22,7 @@ COPY webapp/frontend /opt/scrutiny/src
|
|||||||
RUN npm install -g @angular/cli@9.1.4 && \
|
RUN npm install -g @angular/cli@9.1.4 && \
|
||||||
mkdir -p /scrutiny/dist && \
|
mkdir -p /scrutiny/dist && \
|
||||||
npm install && \
|
npm install && \
|
||||||
ng build --output-path=/opt/scrutiny/dist --deploy-url="/web/" --base-href="/web/" --prod
|
ng build --output-path=/opt/scrutiny/dist --prod
|
||||||
|
|
||||||
|
|
||||||
########
|
########
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ COPY webapp/frontend /opt/scrutiny/src
|
|||||||
RUN npm install -g @angular/cli@9.1.4 && \
|
RUN npm install -g @angular/cli@9.1.4 && \
|
||||||
mkdir -p /opt/scrutiny/dist && \
|
mkdir -p /opt/scrutiny/dist && \
|
||||||
npm install && \
|
npm install && \
|
||||||
ng build --output-path=/opt/scrutiny/dist --deploy-url="/web/" --base-href="/web/" --prod
|
ng build --output-path=/opt/scrutiny/dist --prod
|
||||||
|
|
||||||
|
|
||||||
########
|
########
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# Reverse Proxy Support
|
||||||
|
|
||||||
|
Scrutiny is designed so that it can be used with a reverse proxy, leveraging `domain`, `port` or `path` based matching to correctly route to the Scrutiny service.
|
||||||
|
|
||||||
|
For simple `domain` and/or `port` based routing, this is easy.
|
||||||
|
|
||||||
|
If your domain:port pair is similar to `http://scrutiny.example.com` or `http://localhost:54321`, just update your reverse proxy configuration
|
||||||
|
to route traffic to the Scrutiny backend, which is listening on `0.0.0.0:8080` by default.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# default config
|
||||||
|
web:
|
||||||
|
listen:
|
||||||
|
port: 8080
|
||||||
|
host: 0.0.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
However if you're using `path` based routing to differentiate your reverse proxy protected services, things become more complicated.
|
||||||
|
|
||||||
|
If you'd like to access Scrutiny using a path like: `http://example.com/scrutiny/`, then we need a way to configure Scrutiny so that it
|
||||||
|
understands `http://example.com/scrutiny/api/health` actually means `http://localhost:8080/api/health`.
|
||||||
|
|
||||||
|
Thankfully this can be done by changing **two** settings (both are required).
|
||||||
|
|
||||||
|
1. The webserver has a `web.listen.basepath` key
|
||||||
|
2. The collectors have a `api.endpoint` key.
|
||||||
|
|
||||||
|
## Webserver Configuration
|
||||||
|
|
||||||
|
When setting the `web.listen.basepath` key in the web config file, make sure the `basepath` key is prefixed with `/`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# customized webserver config
|
||||||
|
web:
|
||||||
|
listen:
|
||||||
|
port: 8080
|
||||||
|
host: 0.0.0.0
|
||||||
|
# if you're using a reverse proxy like apache/nginx, you can override this value to serve scrutiny on a subpath.
|
||||||
|
# eg. http://example.com/custombasepath/* vs http://example.com:8080
|
||||||
|
basepath: '/custombasepath'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Collector Configuration
|
||||||
|
|
||||||
|
Here's how you can update the collector `api.endpoint` key:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# customized collector config
|
||||||
|
api:
|
||||||
|
endpoint: 'http://localhost:8080/custombasepath'
|
||||||
|
```
|
||||||
|
|
||||||
|
# Environmental Variables.
|
||||||
|
|
||||||
|
You may also configure these values using the following environmental variables (both are required).
|
||||||
|
|
||||||
|
- `COLLECTOR_API_ENDPOINT=http://localhost:8080/custombasepath`
|
||||||
|
- `SCRUTINY_WEB_LISTEN_BASEPATH=/custombasepath`
|
||||||
@@ -60,6 +60,10 @@ devices:
|
|||||||
#
|
#
|
||||||
#api:
|
#api:
|
||||||
# endpoint: 'http://localhost:8080'
|
# endpoint: 'http://localhost:8080'
|
||||||
|
# endpoint: 'http://localhost:8080/custombasepath'
|
||||||
|
# if you need to use a custom base path (for a reverse proxy), you can add a suffix to the endpoint.
|
||||||
|
# See docs/TROUBLESHOOTING_REVERSE_PROXY.md for more info,
|
||||||
|
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
# FEATURES COMING SOON
|
# FEATURES COMING SOON
|
||||||
|
|||||||
@@ -20,10 +20,18 @@ web:
|
|||||||
listen:
|
listen:
|
||||||
port: 8080
|
port: 8080
|
||||||
host: 0.0.0.0
|
host: 0.0.0.0
|
||||||
|
|
||||||
|
# if you're using a reverse proxy like apache/nginx, you can override this value to serve scrutiny on a subpath.
|
||||||
|
# eg. http://example.com/scrutiny/* vs http://example.com:8080
|
||||||
|
# see docs/TROUBLESHOOTING_REVERSE_PROXY.md
|
||||||
|
# basepath: `/scrutiny`
|
||||||
|
# leave empty unless behind a path prefixed proxy
|
||||||
|
basepath: ''
|
||||||
database:
|
database:
|
||||||
# can also set absolute path here
|
# can also set absolute path here
|
||||||
location: /opt/scrutiny/config/scrutiny.db
|
location: /opt/scrutiny/config/scrutiny.db
|
||||||
src:
|
src:
|
||||||
|
# the location on the filesystem where scrutiny javascript + css is located
|
||||||
frontend:
|
frontend:
|
||||||
path: /opt/scrutiny/web
|
path: /opt/scrutiny/web
|
||||||
influxdb:
|
influxdb:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ go 1.13
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/analogj/go-util v0.0.0-20190301173314-5295e364eb14
|
github.com/analogj/go-util v0.0.0-20190301173314-5295e364eb14
|
||||||
|
github.com/citilinkru/libudev v1.0.0 // indirect
|
||||||
github.com/containrrr/shoutrrr v0.4.4
|
github.com/containrrr/shoutrrr v0.4.4
|
||||||
github.com/fatih/color v1.10.0
|
github.com/fatih/color v1.10.0
|
||||||
github.com/gin-gonic/gin v1.6.3
|
github.com/gin-gonic/gin v1.6.3
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ func (c *configuration) Init() error {
|
|||||||
//set defaults
|
//set defaults
|
||||||
c.SetDefault("web.listen.port", "8080")
|
c.SetDefault("web.listen.port", "8080")
|
||||||
c.SetDefault("web.listen.host", "0.0.0.0")
|
c.SetDefault("web.listen.host", "0.0.0.0")
|
||||||
|
c.SetDefault("web.listen.basepath", "")
|
||||||
c.SetDefault("web.src.frontend.path", "/opt/scrutiny/web")
|
c.SetDefault("web.src.frontend.path", "/opt/scrutiny/web")
|
||||||
c.SetDefault("web.database.location", "/opt/scrutiny/config/scrutiny.db")
|
c.SetDefault("web.database.location", "/opt/scrutiny/config/scrutiny.db")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package m20220503120000
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/analogj/scrutiny/webapp/backend/pkg"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Device struct {
|
||||||
|
//GORM attributes, see: http://gorm.io/docs/conventions.html
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
DeletedAt *time.Time
|
||||||
|
|
||||||
|
WWN string `json:"wwn" gorm:"primary_key"`
|
||||||
|
|
||||||
|
DeviceName string `json:"device_name"`
|
||||||
|
Manufacturer string `json:"manufacturer"`
|
||||||
|
ModelName string `json:"model_name"`
|
||||||
|
InterfaceType string `json:"interface_type"`
|
||||||
|
InterfaceSpeed string `json:"interface_speed"`
|
||||||
|
SerialNumber string `json:"serial_number"`
|
||||||
|
Firmware string `json:"firmware"`
|
||||||
|
RotationSpeed int `json:"rotational_speed"`
|
||||||
|
Capacity int64 `json:"capacity"`
|
||||||
|
FormFactor string `json:"form_factor"`
|
||||||
|
SmartSupport bool `json:"smart_support"`
|
||||||
|
DeviceProtocol string `json:"device_protocol"` //protocol determines which smart attribute types are available (ATA, NVMe, SCSI)
|
||||||
|
DeviceType string `json:"device_type"` //device type is used for querying with -d/t flag, should only be used by collector.
|
||||||
|
|
||||||
|
// User provided metadata
|
||||||
|
Label string `json:"label"`
|
||||||
|
HostId string `json:"host_id"`
|
||||||
|
|
||||||
|
// Data set by Scrutiny
|
||||||
|
DeviceStatus pkg.DeviceStatus `json:"device_status"`
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20201107210306"
|
"github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20201107210306"
|
||||||
|
"github.com/analogj/scrutiny/webapp/backend/pkg/database/migrations/m20220503120000"
|
||||||
"github.com/analogj/scrutiny/webapp/backend/pkg/models"
|
"github.com/analogj/scrutiny/webapp/backend/pkg/models"
|
||||||
"github.com/analogj/scrutiny/webapp/backend/pkg/models/collector"
|
"github.com/analogj/scrutiny/webapp/backend/pkg/models/collector"
|
||||||
"github.com/analogj/scrutiny/webapp/backend/pkg/models/measurements"
|
"github.com/analogj/scrutiny/webapp/backend/pkg/models/measurements"
|
||||||
@@ -256,8 +257,8 @@ func (sr *scrutinyRepository) Migrate(ctx context.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//migrate the device database to the final version
|
//migrate the device database to the current version
|
||||||
return tx.AutoMigrate(models.Device{})
|
return tx.AutoMigrate(m20220503120000.Device{})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ package version
|
|||||||
|
|
||||||
// VERSION is the app-global version string, which will be replaced with a
|
// VERSION is the app-global version string, which will be replaced with a
|
||||||
// new value during packaging
|
// new value during packaging
|
||||||
const VERSION = "0.4.0"
|
const VERSION = "0.4.2"
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func LoggerMiddleware(logger logrus.FieldLogger) gin.HandlerFunc {
|
|||||||
entry.Info(msg)
|
entry.Info(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(path, "/api/") {
|
if strings.Contains(path, "/api/") {
|
||||||
//only debug log request/response from api endpoint.
|
//only debug log request/response from api endpoint.
|
||||||
if len(reqBody) > 0 {
|
if len(reqBody) > 0 {
|
||||||
entry.WithField("bodyType", "request").Debugln(reqBody) // Print request body
|
entry.WithField("bodyType", "request").Debugln(reqBody) // Print request body
|
||||||
|
|||||||
@@ -27,29 +27,35 @@ func (ae *AppEngine) Setup(logger logrus.FieldLogger) *gin.Engine {
|
|||||||
r.Use(middleware.ConfigMiddleware(ae.Config))
|
r.Use(middleware.ConfigMiddleware(ae.Config))
|
||||||
r.Use(gin.Recovery())
|
r.Use(gin.Recovery())
|
||||||
|
|
||||||
api := r.Group("/api")
|
basePath := ae.Config.GetString("web.listen.basepath")
|
||||||
{
|
logger.Debugf("basepath: %s", basePath)
|
||||||
api.GET("/health", func(c *gin.Context) {
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
|
||||||
"success": true,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
api.POST("/health/notify", handler.SendTestNotification) //check if notifications are configured correctly
|
|
||||||
|
|
||||||
api.POST("/devices/register", handler.RegisterDevices) //used by Collector to register new devices and retrieve filtered list
|
base := r.Group(basePath)
|
||||||
api.GET("/summary", handler.GetDevicesSummary) //used by Dashboard
|
{
|
||||||
api.GET("/summary/temp", handler.GetDevicesSummaryTempHistory) //used by Dashboard (Temperature history dropdown)
|
api := base.Group("/api")
|
||||||
api.POST("/device/:wwn/smart", handler.UploadDeviceMetrics) //used by Collector to upload data
|
{
|
||||||
api.POST("/device/:wwn/selftest", handler.UploadDeviceSelfTests)
|
api.GET("/health", func(c *gin.Context) {
|
||||||
api.GET("/device/:wwn/details", handler.GetDeviceDetails) //used by Details
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
api.POST("/health/notify", handler.SendTestNotification) //check if notifications are configured correctly
|
||||||
|
|
||||||
|
api.POST("/devices/register", handler.RegisterDevices) //used by Collector to register new devices and retrieve filtered list
|
||||||
|
api.GET("/summary", handler.GetDevicesSummary) //used by Dashboard
|
||||||
|
api.GET("/summary/temp", handler.GetDevicesSummaryTempHistory) //used by Dashboard (Temperature history dropdown)
|
||||||
|
api.POST("/device/:wwn/smart", handler.UploadDeviceMetrics) //used by Collector to upload data
|
||||||
|
api.POST("/device/:wwn/selftest", handler.UploadDeviceSelfTests)
|
||||||
|
api.GET("/device/:wwn/details", handler.GetDeviceDetails) //used by Details
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Static request routing
|
//Static request routing
|
||||||
r.StaticFS("/web", http.Dir(ae.Config.GetString("web.src.frontend.path")))
|
base.StaticFS("/web", http.Dir(ae.Config.GetString("web.src.frontend.path")))
|
||||||
|
|
||||||
//redirect base url to /web
|
//redirect base url to /web
|
||||||
r.GET("/", func(c *gin.Context) {
|
base.GET("/", func(c *gin.Context) {
|
||||||
c.Redirect(http.StatusFound, "/web")
|
c.Redirect(http.StatusFound, basePath+"/web")
|
||||||
})
|
})
|
||||||
|
|
||||||
//catch-all, serve index page.
|
//catch-all, serve index page.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -61,15 +62,36 @@ func helperReadSmartDataFileFixTimestamp(t *testing.T, smartDataFilepath string)
|
|||||||
return bytes.NewReader(updatedSmartDataBytes)
|
return bytes.NewReader(updatedSmartDataBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHealthRoute(t *testing.T) {
|
// Define the suite, and absorb the built-in basic suite
|
||||||
|
// functionality from testify - including a T() method which
|
||||||
|
// returns the current testing context
|
||||||
|
type ServerTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
Basepath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerTestSuite_WithEmptyBasePath(t *testing.T) {
|
||||||
|
emptyBasePathSuite := new(ServerTestSuite)
|
||||||
|
emptyBasePathSuite.Basepath = ""
|
||||||
|
suite.Run(t, emptyBasePathSuite)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerTestSuite_WithCustomBasePath(t *testing.T) {
|
||||||
|
emptyBasePathSuite := new(ServerTestSuite)
|
||||||
|
emptyBasePathSuite.Basepath = "/basepath"
|
||||||
|
suite.Run(t, emptyBasePathSuite)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ServerTestSuite) TestHealthRoute() {
|
||||||
//setup
|
//setup
|
||||||
parentPath, _ := ioutil.TempDir("", "")
|
parentPath, _ := ioutil.TempDir("", "")
|
||||||
defer os.RemoveAll(parentPath)
|
defer os.RemoveAll(parentPath)
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(suite.T())
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||||
fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes()
|
fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes()
|
fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes()
|
||||||
|
fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes()
|
||||||
|
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
||||||
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
||||||
@@ -92,23 +114,24 @@ func TestHealthRoute(t *testing.T) {
|
|||||||
|
|
||||||
//test
|
//test
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("GET", "/api/health", nil)
|
req, _ := http.NewRequest("GET", suite.Basepath+"/api/health", nil)
|
||||||
router.ServeHTTP(w, req)
|
router.ServeHTTP(w, req)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.Equal(t, 200, w.Code)
|
require.Equal(suite.T(), 200, w.Code)
|
||||||
require.Equal(t, "{\"success\":true}", w.Body.String())
|
require.Equal(suite.T(), "{\"success\":true}", w.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegisterDevicesRoute(t *testing.T) {
|
func (suite *ServerTestSuite) TestRegisterDevicesRoute() {
|
||||||
//setup
|
//setup
|
||||||
parentPath, _ := ioutil.TempDir("", "")
|
parentPath, _ := ioutil.TempDir("", "")
|
||||||
defer os.RemoveAll(parentPath)
|
defer os.RemoveAll(parentPath)
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(suite.T())
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||||
fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes()
|
fakeConfig.EXPECT().GetString("web.database.location").Return(path.Join(parentPath, "scrutiny_test.db")).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes()
|
fakeConfig.EXPECT().GetString("web.src.frontend.path").Return(parentPath).AnyTimes()
|
||||||
|
fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
||||||
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
||||||
@@ -127,26 +150,27 @@ func TestRegisterDevicesRoute(t *testing.T) {
|
|||||||
}
|
}
|
||||||
router := ae.Setup(logrus.New())
|
router := ae.Setup(logrus.New())
|
||||||
file, err := os.Open("testdata/register-devices-req.json")
|
file, err := os.Open("testdata/register-devices-req.json")
|
||||||
require.NoError(t, err)
|
require.NoError(suite.T(), err)
|
||||||
|
|
||||||
//test
|
//test
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/api/devices/register", file)
|
req, _ := http.NewRequest("POST", suite.Basepath+"/api/devices/register", file)
|
||||||
router.ServeHTTP(w, req)
|
router.ServeHTTP(w, req)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.Equal(t, 200, w.Code)
|
require.Equal(suite.T(), 200, w.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUploadDeviceMetricsRoute(t *testing.T) {
|
func (suite *ServerTestSuite) TestUploadDeviceMetricsRoute() {
|
||||||
//setup
|
//setup
|
||||||
parentPath, _ := ioutil.TempDir("", "")
|
parentPath, _ := ioutil.TempDir("", "")
|
||||||
defer os.RemoveAll(parentPath)
|
defer os.RemoveAll(parentPath)
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(suite.T())
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||||
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
||||||
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
||||||
|
fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
||||||
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
||||||
@@ -165,35 +189,36 @@ func TestUploadDeviceMetricsRoute(t *testing.T) {
|
|||||||
}
|
}
|
||||||
router := ae.Setup(logrus.New())
|
router := ae.Setup(logrus.New())
|
||||||
devicesfile, err := os.Open("testdata/register-devices-single-req.json")
|
devicesfile, err := os.Open("testdata/register-devices-single-req.json")
|
||||||
require.NoError(t, err)
|
require.NoError(suite.T(), err)
|
||||||
|
|
||||||
metricsfile := helperReadSmartDataFileFixTimestamp(t, "testdata/upload-device-metrics-req.json")
|
metricsfile := helperReadSmartDataFileFixTimestamp(suite.T(), "testdata/upload-device-metrics-req.json")
|
||||||
|
|
||||||
//test
|
//test
|
||||||
wr := httptest.NewRecorder()
|
wr := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/api/devices/register", devicesfile)
|
req, _ := http.NewRequest("POST", suite.Basepath+"/api/devices/register", devicesfile)
|
||||||
router.ServeHTTP(wr, req)
|
router.ServeHTTP(wr, req)
|
||||||
require.Equal(t, 200, wr.Code)
|
require.Equal(suite.T(), 200, wr.Code)
|
||||||
|
|
||||||
mr := httptest.NewRecorder()
|
mr := httptest.NewRecorder()
|
||||||
req, _ = http.NewRequest("POST", "/api/device/0x5000cca264eb01d7/smart", metricsfile)
|
req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5000cca264eb01d7/smart", metricsfile)
|
||||||
router.ServeHTTP(mr, req)
|
router.ServeHTTP(mr, req)
|
||||||
require.Equal(t, 200, mr.Code)
|
require.Equal(suite.T(), 200, mr.Code)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPopulateMultiple(t *testing.T) {
|
func (suite *ServerTestSuite) TestPopulateMultiple() {
|
||||||
//setup
|
//setup
|
||||||
parentPath, _ := ioutil.TempDir("", "")
|
parentPath, _ := ioutil.TempDir("", "")
|
||||||
defer os.RemoveAll(parentPath)
|
defer os.RemoveAll(parentPath)
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(suite.T())
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||||
//fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return("testdata/scrutiny_test.db")
|
//fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return("testdata/scrutiny_test.db")
|
||||||
fakeConfig.EXPECT().GetStringSlice("notify.urls").Return([]string{}).AnyTimes()
|
fakeConfig.EXPECT().GetStringSlice("notify.urls").Return([]string{}).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
||||||
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
||||||
|
fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
||||||
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
||||||
@@ -212,44 +237,44 @@ func TestPopulateMultiple(t *testing.T) {
|
|||||||
}
|
}
|
||||||
router := ae.Setup(logrus.New())
|
router := ae.Setup(logrus.New())
|
||||||
devicesfile, err := os.Open("testdata/register-devices-req.json")
|
devicesfile, err := os.Open("testdata/register-devices-req.json")
|
||||||
require.NoError(t, err)
|
require.NoError(suite.T(), err)
|
||||||
|
|
||||||
metricsfile := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-ata.json")
|
metricsfile := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-ata.json")
|
||||||
failfile := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-fail2.json")
|
failfile := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-fail2.json")
|
||||||
nvmefile := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-nvme.json")
|
nvmefile := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-nvme.json")
|
||||||
scsifile := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-scsi.json")
|
scsifile := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-scsi.json")
|
||||||
scsi2file := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-scsi2.json")
|
scsi2file := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-scsi2.json")
|
||||||
|
|
||||||
//test
|
//test
|
||||||
wr := httptest.NewRecorder()
|
wr := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/api/devices/register", devicesfile)
|
req, _ := http.NewRequest("POST", suite.Basepath+"/api/devices/register", devicesfile)
|
||||||
router.ServeHTTP(wr, req)
|
router.ServeHTTP(wr, req)
|
||||||
require.Equal(t, 200, wr.Code)
|
require.Equal(suite.T(), 200, wr.Code)
|
||||||
|
|
||||||
mr := httptest.NewRecorder()
|
mr := httptest.NewRecorder()
|
||||||
req, _ = http.NewRequest("POST", "/api/device/0x5000cca264eb01d7/smart", metricsfile)
|
req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5000cca264eb01d7/smart", metricsfile)
|
||||||
router.ServeHTTP(mr, req)
|
router.ServeHTTP(mr, req)
|
||||||
require.Equal(t, 200, mr.Code)
|
require.Equal(suite.T(), 200, mr.Code)
|
||||||
|
|
||||||
fr := httptest.NewRecorder()
|
fr := httptest.NewRecorder()
|
||||||
req, _ = http.NewRequest("POST", "/api/device/0x5000cca264ec3183/smart", failfile)
|
req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5000cca264ec3183/smart", failfile)
|
||||||
router.ServeHTTP(fr, req)
|
router.ServeHTTP(fr, req)
|
||||||
require.Equal(t, 200, fr.Code)
|
require.Equal(suite.T(), 200, fr.Code)
|
||||||
|
|
||||||
nr := httptest.NewRecorder()
|
nr := httptest.NewRecorder()
|
||||||
req, _ = http.NewRequest("POST", "/api/device/0x5002538e40a22954/smart", nvmefile)
|
req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5002538e40a22954/smart", nvmefile)
|
||||||
router.ServeHTTP(nr, req)
|
router.ServeHTTP(nr, req)
|
||||||
require.Equal(t, 200, nr.Code)
|
require.Equal(suite.T(), 200, nr.Code)
|
||||||
|
|
||||||
sr := httptest.NewRecorder()
|
sr := httptest.NewRecorder()
|
||||||
req, _ = http.NewRequest("POST", "/api/device/0x5000cca252c859cc/smart", scsifile)
|
req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5000cca252c859cc/smart", scsifile)
|
||||||
router.ServeHTTP(sr, req)
|
router.ServeHTTP(sr, req)
|
||||||
require.Equal(t, 200, sr.Code)
|
require.Equal(suite.T(), 200, sr.Code)
|
||||||
|
|
||||||
s2r := httptest.NewRecorder()
|
s2r := httptest.NewRecorder()
|
||||||
req, _ = http.NewRequest("POST", "/api/device/0x5000cca264ebc248/smart", scsi2file)
|
req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/0x5000cca264ebc248/smart", scsi2file)
|
||||||
router.ServeHTTP(s2r, req)
|
router.ServeHTTP(s2r, req)
|
||||||
require.Equal(t, 200, s2r.Code)
|
require.Equal(suite.T(), 200, s2r.Code)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
}
|
}
|
||||||
@@ -279,15 +304,16 @@ func TestPopulateMultiple(t *testing.T) {
|
|||||||
// require.Equal(t, 200, wr.Code)
|
// require.Equal(t, 200, wr.Code)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
func TestSendTestNotificationRoute_WebhookFailure(t *testing.T) {
|
func (suite *ServerTestSuite) TestSendTestNotificationRoute_WebhookFailure() {
|
||||||
//setup
|
//setup
|
||||||
parentPath, _ := ioutil.TempDir("", "")
|
parentPath, _ := ioutil.TempDir("", "")
|
||||||
defer os.RemoveAll(parentPath)
|
defer os.RemoveAll(parentPath)
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(suite.T())
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||||
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
||||||
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
||||||
|
fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
||||||
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
||||||
@@ -309,22 +335,23 @@ func TestSendTestNotificationRoute_WebhookFailure(t *testing.T) {
|
|||||||
|
|
||||||
//test
|
//test
|
||||||
wr := httptest.NewRecorder()
|
wr := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/api/health/notify", strings.NewReader("{}"))
|
req, _ := http.NewRequest("POST", suite.Basepath+"/api/health/notify", strings.NewReader("{}"))
|
||||||
router.ServeHTTP(wr, req)
|
router.ServeHTTP(wr, req)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.Equal(t, 500, wr.Code)
|
require.Equal(suite.T(), 500, wr.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendTestNotificationRoute_ScriptFailure(t *testing.T) {
|
func (suite *ServerTestSuite) TestSendTestNotificationRoute_ScriptFailure() {
|
||||||
//setup
|
//setup
|
||||||
parentPath, _ := ioutil.TempDir("", "")
|
parentPath, _ := ioutil.TempDir("", "")
|
||||||
defer os.RemoveAll(parentPath)
|
defer os.RemoveAll(parentPath)
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(suite.T())
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||||
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
||||||
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
||||||
|
fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
||||||
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
||||||
@@ -346,22 +373,23 @@ func TestSendTestNotificationRoute_ScriptFailure(t *testing.T) {
|
|||||||
|
|
||||||
//test
|
//test
|
||||||
wr := httptest.NewRecorder()
|
wr := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/api/health/notify", strings.NewReader("{}"))
|
req, _ := http.NewRequest("POST", suite.Basepath+"/api/health/notify", strings.NewReader("{}"))
|
||||||
router.ServeHTTP(wr, req)
|
router.ServeHTTP(wr, req)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.Equal(t, 500, wr.Code)
|
require.Equal(suite.T(), 500, wr.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendTestNotificationRoute_ScriptSuccess(t *testing.T) {
|
func (suite *ServerTestSuite) TestSendTestNotificationRoute_ScriptSuccess() {
|
||||||
//setup
|
//setup
|
||||||
parentPath, _ := ioutil.TempDir("", "")
|
parentPath, _ := ioutil.TempDir("", "")
|
||||||
defer os.RemoveAll(parentPath)
|
defer os.RemoveAll(parentPath)
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(suite.T())
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||||
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
||||||
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
||||||
|
fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
||||||
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
||||||
@@ -383,22 +411,23 @@ func TestSendTestNotificationRoute_ScriptSuccess(t *testing.T) {
|
|||||||
|
|
||||||
//test
|
//test
|
||||||
wr := httptest.NewRecorder()
|
wr := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/api/health/notify", strings.NewReader("{}"))
|
req, _ := http.NewRequest("POST", suite.Basepath+"/api/health/notify", strings.NewReader("{}"))
|
||||||
router.ServeHTTP(wr, req)
|
router.ServeHTTP(wr, req)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.Equal(t, 200, wr.Code)
|
require.Equal(suite.T(), 200, wr.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendTestNotificationRoute_ShoutrrrFailure(t *testing.T) {
|
func (suite *ServerTestSuite) TestSendTestNotificationRoute_ShoutrrrFailure() {
|
||||||
//setup
|
//setup
|
||||||
parentPath, _ := ioutil.TempDir("", "")
|
parentPath, _ := ioutil.TempDir("", "")
|
||||||
defer os.RemoveAll(parentPath)
|
defer os.RemoveAll(parentPath)
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(suite.T())
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||||
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
||||||
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
||||||
|
fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
||||||
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
||||||
@@ -419,22 +448,23 @@ func TestSendTestNotificationRoute_ShoutrrrFailure(t *testing.T) {
|
|||||||
|
|
||||||
//test
|
//test
|
||||||
wr := httptest.NewRecorder()
|
wr := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/api/health/notify", strings.NewReader("{}"))
|
req, _ := http.NewRequest("POST", suite.Basepath+"/api/health/notify", strings.NewReader("{}"))
|
||||||
router.ServeHTTP(wr, req)
|
router.ServeHTTP(wr, req)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.Equal(t, 500, wr.Code)
|
require.Equal(suite.T(), 500, wr.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetDevicesSummaryRoute_Nvme(t *testing.T) {
|
func (suite *ServerTestSuite) TestGetDevicesSummaryRoute_Nvme() {
|
||||||
//setup
|
//setup
|
||||||
parentPath, _ := ioutil.TempDir("", "")
|
parentPath, _ := ioutil.TempDir("", "")
|
||||||
defer os.RemoveAll(parentPath)
|
defer os.RemoveAll(parentPath)
|
||||||
mockCtrl := gomock.NewController(t)
|
mockCtrl := gomock.NewController(suite.T())
|
||||||
defer mockCtrl.Finish()
|
defer mockCtrl.Finish()
|
||||||
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
fakeConfig := mock_config.NewMockInterface(mockCtrl)
|
||||||
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(path.Join(parentPath, "scrutiny_test.db"))
|
||||||
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
fakeConfig.EXPECT().GetString("web.src.frontend.path").AnyTimes().Return(parentPath)
|
||||||
|
fakeConfig.EXPECT().GetString("web.listen.basepath").Return(suite.Basepath).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.port").Return("8086").AnyTimes()
|
||||||
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
fakeConfig.EXPECT().IsSet("web.influxdb.token").Return(true).AnyTimes()
|
||||||
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
fakeConfig.EXPECT().GetString("web.influxdb.token").Return("my-super-secret-auth-token").AnyTimes()
|
||||||
@@ -454,30 +484,30 @@ func TestGetDevicesSummaryRoute_Nvme(t *testing.T) {
|
|||||||
}
|
}
|
||||||
router := ae.Setup(logrus.New())
|
router := ae.Setup(logrus.New())
|
||||||
devicesfile, err := os.Open("testdata/register-devices-req-2.json")
|
devicesfile, err := os.Open("testdata/register-devices-req-2.json")
|
||||||
require.NoError(t, err)
|
require.NoError(suite.T(), err)
|
||||||
|
|
||||||
metricsfile := helperReadSmartDataFileFixTimestamp(t, "../models/testdata/smart-nvme2.json")
|
metricsfile := helperReadSmartDataFileFixTimestamp(suite.T(), "../models/testdata/smart-nvme2.json")
|
||||||
|
|
||||||
//test
|
//test
|
||||||
wr := httptest.NewRecorder()
|
wr := httptest.NewRecorder()
|
||||||
req, _ := http.NewRequest("POST", "/api/devices/register", devicesfile)
|
req, _ := http.NewRequest("POST", suite.Basepath+"/api/devices/register", devicesfile)
|
||||||
router.ServeHTTP(wr, req)
|
router.ServeHTTP(wr, req)
|
||||||
require.Equal(t, 200, wr.Code)
|
require.Equal(suite.T(), 200, wr.Code)
|
||||||
|
|
||||||
mr := httptest.NewRecorder()
|
mr := httptest.NewRecorder()
|
||||||
req, _ = http.NewRequest("POST", "/api/device/a4c8e8ed-11a0-4c97-9bba-306440f1b944/smart", metricsfile)
|
req, _ = http.NewRequest("POST", suite.Basepath+"/api/device/a4c8e8ed-11a0-4c97-9bba-306440f1b944/smart", metricsfile)
|
||||||
router.ServeHTTP(mr, req)
|
router.ServeHTTP(mr, req)
|
||||||
require.Equal(t, 200, mr.Code)
|
require.Equal(suite.T(), 200, mr.Code)
|
||||||
|
|
||||||
sr := httptest.NewRecorder()
|
sr := httptest.NewRecorder()
|
||||||
req, _ = http.NewRequest("GET", "/api/summary", nil)
|
req, _ = http.NewRequest("GET", suite.Basepath+"/api/summary", nil)
|
||||||
router.ServeHTTP(sr, req)
|
router.ServeHTTP(sr, req)
|
||||||
require.Equal(t, 200, sr.Code)
|
require.Equal(suite.T(), 200, sr.Code)
|
||||||
var deviceSummary models.DeviceSummaryWrapper
|
var deviceSummary models.DeviceSummaryWrapper
|
||||||
err = json.Unmarshal(sr.Body.Bytes(), &deviceSummary)
|
err = json.Unmarshal(sr.Body.Bytes(), &deviceSummary)
|
||||||
require.NoError(t, err)
|
require.NoError(suite.T(), err)
|
||||||
|
|
||||||
//assert
|
//assert
|
||||||
require.Equal(t, "a4c8e8ed-11a0-4c97-9bba-306440f1b944", deviceSummary.Data.Summary["a4c8e8ed-11a0-4c97-9bba-306440f1b944"].Device.WWN)
|
require.Equal(suite.T(), "a4c8e8ed-11a0-4c97-9bba-306440f1b944", deviceSummary.Data.Summary["a4c8e8ed-11a0-4c97-9bba-306440f1b944"].Device.WWN)
|
||||||
require.Equal(t, pkg.DeviceStatusFailedScrutiny, deviceSummary.Data.Summary["a4c8e8ed-11a0-4c97-9bba-306440f1b944"].Device.DeviceStatus)
|
require.Equal(suite.T(), pkg.DeviceStatusFailedScrutiny, deviceSummary.Data.Summary["a4c8e8ed-11a0-4c97-9bba-306440f1b944"].Device.DeviceStatus)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { NgModule, enableProdMode } from '@angular/core';
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { ExtraOptions, PreloadAllModules, RouterModule } from '@angular/router';
|
import { ExtraOptions, PreloadAllModules, RouterModule } from '@angular/router';
|
||||||
|
import { APP_BASE_HREF } from '@angular/common';
|
||||||
import { MarkdownModule } from 'ngx-markdown';
|
import { MarkdownModule } from 'ngx-markdown';
|
||||||
import { TreoModule } from '@treo';
|
import { TreoModule } from '@treo';
|
||||||
import { TreoConfigModule } from '@treo/services/config';
|
import { TreoConfigModule } from '@treo/services/config';
|
||||||
@@ -11,7 +12,7 @@ import { appConfig } from 'app/core/config/app.config';
|
|||||||
import { mockDataServices } from 'app/data/mock';
|
import { mockDataServices } from 'app/data/mock';
|
||||||
import { LayoutModule } from 'app/layout/layout.module';
|
import { LayoutModule } from 'app/layout/layout.module';
|
||||||
import { AppComponent } from 'app/app.component';
|
import { AppComponent } from 'app/app.component';
|
||||||
import { appRoutes } from 'app/app.routing';
|
import { appRoutes, getAppBaseHref } from 'app/app.routing';
|
||||||
|
|
||||||
const routerConfig: ExtraOptions = {
|
const routerConfig: ExtraOptions = {
|
||||||
scrollPositionRestoration: 'enabled',
|
scrollPositionRestoration: 'enabled',
|
||||||
@@ -54,7 +55,13 @@ if (process.env.NODE_ENV === 'production') {
|
|||||||
],
|
],
|
||||||
bootstrap : [
|
bootstrap : [
|
||||||
AppComponent
|
AppComponent
|
||||||
]
|
],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_BASE_HREF,
|
||||||
|
useValue: getAppBaseHref()
|
||||||
|
}
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class AppModule
|
export class AppModule
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,17 @@ import { Route } from '@angular/router';
|
|||||||
import { LayoutComponent } from 'app/layout/layout.component';
|
import { LayoutComponent } from 'app/layout/layout.component';
|
||||||
import { EmptyLayoutComponent } from 'app/layout/layouts/empty/empty.component';
|
import { EmptyLayoutComponent } from 'app/layout/layouts/empty/empty.component';
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
export function getAppBaseHref(): string {
|
||||||
|
return getBasePath() + '/web';
|
||||||
|
}
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
// tslint:disable:max-line-length
|
||||||
|
export function getBasePath(): string {
|
||||||
|
return window.location.pathname.split('/web').slice(0, 1)[0];
|
||||||
|
}
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
// tslint:disable:max-line-length
|
// tslint:disable:max-line-length
|
||||||
export const appRoutes: Route[] = [
|
export const appRoutes: Route[] = [
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { MatFormField } from '@angular/material/form-field';
|
|||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { debounceTime, filter, map, takeUntil } from 'rxjs/operators';
|
import { debounceTime, filter, map, takeUntil } from 'rxjs/operators';
|
||||||
import { TreoAnimations } from '@treo/animations/public-api';
|
import { TreoAnimations } from '@treo/animations/public-api';
|
||||||
|
import { getBasePath } from 'app/app.routing';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector : 'search',
|
selector : 'search',
|
||||||
@@ -199,7 +200,7 @@ export class SearchComponent implements OnInit, OnDestroy
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.subscribe((value) => {
|
.subscribe((value) => {
|
||||||
this._httpClient.post('api/common/search', {query: value})
|
this._httpClient.post(getBasePath() + '/api/common/search', {query: value})
|
||||||
.subscribe((response: any) => {
|
.subscribe((response: any) => {
|
||||||
this.results = response.results;
|
this.results = response.results;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
|
import { getBasePath } from 'app/app.routing';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -45,7 +46,7 @@ export class DashboardService
|
|||||||
*/
|
*/
|
||||||
getData(): Observable<any>
|
getData(): Observable<any>
|
||||||
{
|
{
|
||||||
return this._httpClient.get('/api/summary').pipe(
|
return this._httpClient.get(getBasePath() + '/api/summary').pipe(
|
||||||
tap((response: any) => {
|
tap((response: any) => {
|
||||||
this._data.next(response);
|
this._data.next(response);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
|
import { getBasePath } from 'app/app.routing';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -45,7 +46,7 @@ export class DetailService
|
|||||||
*/
|
*/
|
||||||
getData(wwn): Observable<any>
|
getData(wwn): Observable<any>
|
||||||
{
|
{
|
||||||
return this._httpClient.get(`/api/device/${wwn}/details`).pipe(
|
return this._httpClient.get(getBasePath() + `/api/device/${wwn}/details`).pipe(
|
||||||
tap((response: any) => {
|
tap((response: any) => {
|
||||||
this._data.next(response);
|
this._data.next(response);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
|
<browserconfig><msapplication><tile><square70x70logo src="./ms-icon-70x70.png"/><square150x150logo src="./ms-icon-150x150.png"/><square310x310logo src="./ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>scrutiny</title>
|
<title>scrutiny</title>
|
||||||
<base href="/">
|
|
||||||
|
|
||||||
<meta name="viewport"
|
<meta name="viewport"
|
||||||
content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0">
|
content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0">
|
||||||
@@ -22,7 +21,7 @@
|
|||||||
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="favicon-96x96.png">
|
<link rel="icon" type="image/png" sizes="96x96" href="favicon-96x96.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="./manifest.json" crossorigin="use-credentials">
|
||||||
<meta name="msapplication-TileColor" content="#ffffff">
|
<meta name="msapplication-TileColor" content="#ffffff">
|
||||||
<meta name="msapplication-TileImage" content="ms-icon-144x144.png">
|
<meta name="msapplication-TileImage" content="ms-icon-144x144.png">
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|||||||
@@ -2,37 +2,37 @@
|
|||||||
"name": "App",
|
"name": "App",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-36x36.png",
|
"src": ".\/android-icon-36x36.png",
|
||||||
"sizes": "36x36",
|
"sizes": "36x36",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "0.75"
|
"density": "0.75"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-48x48.png",
|
"src": ".\/android-icon-48x48.png",
|
||||||
"sizes": "48x48",
|
"sizes": "48x48",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "1.0"
|
"density": "1.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-72x72.png",
|
"src": ".\/android-icon-72x72.png",
|
||||||
"sizes": "72x72",
|
"sizes": "72x72",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "1.5"
|
"density": "1.5"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-96x96.png",
|
"src": ".\/android-icon-96x96.png",
|
||||||
"sizes": "96x96",
|
"sizes": "96x96",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "2.0"
|
"density": "2.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-144x144.png",
|
"src": ".\/android-icon-144x144.png",
|
||||||
"sizes": "144x144",
|
"sizes": "144x144",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "3.0"
|
"density": "3.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "\/android-icon-192x192.png",
|
"src": ".\/android-icon-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image\/png",
|
"type": "image\/png",
|
||||||
"density": "4.0"
|
"density": "4.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user