diff --git a/browserslist b/.browserslistrc similarity index 100% rename from browserslist rename to .browserslistrc diff --git a/.drone.yml b/.drone.yml index dd07674..ebccfba 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,18 +1,92 @@ kind: pipeline -name: default +type: docker +name: linux-amd64 + +platform: + os: linux + arch: amd64 steps: - name: build - image: node + image: node:alpine commands: - npm install - npm run build:ssr - - name: docker image: plugins/docker settings: - repo: samuelph/grafana-statuspage + auto_tag: true + auto_tag_suffix: linux-amd64 + repo: samuelph/universal-statuspage username: from_secret: USERNAME password: from_secret: PASSWORD + +trigger: + branch: + - master + +--- +kind: pipeline +type: docker +name: linux-arm + +platform: + os: linux + arch: arm + +steps: + - name: build + image: node:alpine + commands: + - npm install + - npm run build:ssr + - name: docker + image: plugins/docker + settings: + auto_tag: true + auto_tag_suffix: linux-arm + repo: samuelph/universal-statuspage + username: + from_secret: USERNAME + password: + from_secret: PASSWORD + when: + branch: + - master + event: + - push + +--- +kind: pipeline +type: docker +name: manifest + +platform: + os: linux + arch: arm + +steps: + - name: publish + image: plugins/manifest + settings: + auto_tag: true + ignore_missing: true + target: samuelph/universal-statuspage + template: samuelph/universal-statuspage:OS-ARCH + platforms: + - linux/amd64 + - linux/arm + username: + from_secret: USERNAME + password: + from_secret: PASSWORD + +depends_on: + - linux-amd64 + - linux-arm + +trigger: + branch: + - master diff --git a/.gitignore b/.gitignore index 1c9bcc3..a3ed5d0 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,5 @@ testem.log Thumbs.db # custom - config.json cache.json diff --git a/config.json b/config.json index 57777d9..a01e3b9 100644 --- a/config.json +++ b/config.json @@ -2,6 +2,11 @@ "authToken": "test", "title": "sp-status", "description": "Services hosted by sp-codes", + "statePath": "$.status", + "stateValues": { + "operational": ["OK"], + "maintenance": ["PAUSED"] + }, "groups": [ { "id": "test", @@ -9,8 +14,13 @@ "services": [ { "id": "test", - "name": "Test", - "url": "http://test.de" + "name": "test", + "url": "http://sp-codes.de", + "statePath": "$.state", + "stateValues": { + "operational": ["ok"], + "maintenance": ["paused"] + } } ] } diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json index 39b800f..6b87cc4 100644 --- a/e2e/tsconfig.json +++ b/e2e/tsconfig.json @@ -1,9 +1,9 @@ { - "extends": "../tsconfig.json", + "extends": "../tsconfig.base.json", "compilerOptions": { "outDir": "../out-tsc/e2e", "module": "commonjs", - "target": "es5", + "target": "es2018", "types": [ "jasmine", "jasminewd2", diff --git a/package.json b/package.json index 10eef30..30d0dae 100644 --- a/package.json +++ b/package.json @@ -15,47 +15,48 @@ }, "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/platform-browser": "~9.1.4", - "@angular/platform-browser-dynamic": "~9.1.4", - "@angular/platform-server": "~9.1.4", - "@angular/router": "~9.1.4", - "@fortawesome/fontawesome-free": "^5.13.0", - "@nguniversal/express-engine": "^9.1.0", - "bootstrap": "^4.4.1", + "@angular/animations": "~10.0.2", + "@angular/cdk": "^10.0.1", + "@angular/common": "~10.0.2", + "@angular/compiler": "~10.0.2", + "@angular/core": "~10.0.2", + "@angular/forms": "~10.0.2", + "@angular/material": "^10.0.1", + "@angular/platform-browser": "~10.0.2", + "@angular/platform-browser-dynamic": "~10.0.2", + "@angular/platform-server": "~10.0.2", + "@angular/router": "~10.0.2", + "@fortawesome/fontawesome-free": "^5.13.1", + "@nguniversal/express-engine": "^10.0.1", + "bootstrap": "^4.5.0", "express": "^4.15.2", + "jsonpath": "^1.0.2", "roboto-fontface": "^0.10.0", - "rxjs": "~6.5.4", - "tslib": "^1.10.0", + "rxjs": "~6.6.0", + "tslib": "^2.0.0", "zone.js": "~0.10.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", - "@nguniversal/builders": "^9.1.0", + "@angular-devkit/build-angular": "~0.1000.1", + "@angular/cli": "~10.0.1", + "@angular/compiler-cli": "~10.0.2", + "@angular/language-service": "~10.0.2", + "@nguniversal/builders": "^10.0.1", "@types/express": "^4.17.0", - "@types/node": "^12.11.1", - "@types/jasmine": "~3.5.0", + "@types/node": "^14.0.14", + "@types/jasmine": "~3.5.11", "@types/jasminewd2": "~2.0.3", - "codelyzer": "^5.1.2", + "codelyzer": "^6.0.0", "jasmine-core": "~3.5.0", - "jasmine-spec-reporter": "~4.2.1", + "jasmine-spec-reporter": "~5.0.0", "karma": "~5.0.0", "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~2.1.0", - "karma-jasmine": "~3.0.1", - "karma-jasmine-html-reporter": "^1.4.2", - "protractor": "~5.4.3", - "ts-node": "~8.3.0", + "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-jasmine": "~3.3.0", + "karma-jasmine-html-reporter": "^1.5.0", + "protractor": "~7.0.0", + "ts-node": "~8.10.2", "tslint": "~6.1.0", - "typescript": "~3.8.3" + "typescript": "~3.9.6" } } diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..b10109a --- /dev/null +++ b/renovate.json @@ -0,0 +1,35 @@ +{ + "assignees": [ + "samuel-p" + ], + "baseBranches": [ + "develop" + ], + "rangeStrategy": "bump", + "packageRules": [ + { + "managers": [ + "npm" + ], + "packageNames": [ + "@types/node", + "@types/jasmine", + "@types/jasminewd2", + "codelyzer", + "protractor", + "rxjs", + "ts-node", + "tslib", + "tslint", + "typescript", + "zone.js" + ], + "packagePatterns": [ + "^angular", + "^karma", + "^jasmine" + ], + "enabled": false + } + ] +} diff --git a/src/app/app.component.html b/src/app/app.component.html index 03637e3..9ad4c05 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -10,7 +10,14 @@ diff --git a/src/app/status/status.component.html b/src/app/status/status.component.html index 59fa836..6ef500a 100644 --- a/src/app/status/status.component.html +++ b/src/app/status/status.component.html @@ -11,7 +11,7 @@
- {{service.name}} + {{service.name}} {{service.state}}
diff --git a/src/main.status.ts b/src/main.status.ts index 8bd25db..b3e5c30 100644 --- a/src/main.status.ts +++ b/src/main.status.ts @@ -1,15 +1,21 @@ import {json, Router} from 'express'; -import {CurrentStatus, State} from "./app/_data/data"; -import {existsSync, readFileSync, writeFileSync} from "fs"; +import {CurrentStatus, State} from './app/_data/data'; +import {existsSync, readFileSync, writeFileSync} from 'fs'; +import * as jp from 'jsonpath'; interface Cache { - [id: string]: State + [id: string]: State; } interface Config { authToken: string; title: string; description: string; + statePath: string; + stateValues: { + operational: string[]; + maintenance: string[]; + }; groups: { id: string; name: string; @@ -17,34 +23,40 @@ interface Config { id: string; name: string; url: string; + statePath?: string; + stateValues?: { + operational?: string[]; + maintenance?: string[]; + }; }[]; }[]; } -interface GrafanaWebhookBody { - dashboardId: number; - evalMatches: { - value: number, - metric: string, - tags: any - }[]; - imageUrl: string, - message: string, - orgId: number, - panelId: number, - ruleId: number, - ruleName: string, - ruleUrl: string, - state: "ok" | "paused" | "alerting" | "pending" | "no_data"; - tags: { [key: string]: string }, - title: string +interface StateKey { + statePath: string; + stateValues: { + operational: string[]; + maintenance: string[]; + }; } const api = Router(); api.use(json()); -const config = JSON.parse(readFileSync('config.json', {encoding: 'UTF-8'})) as Config; -const serviceStates = existsSync('cache.json') ? JSON.parse(readFileSync('cache.json', {encoding: 'UTF-8'})) : {} as Cache; +const config = JSON.parse(readFileSync('config.json', {encoding: 'utf-8'})) as Config; +const stateKeys: { [service: string]: StateKey } = config.groups + .map(g => g.services).reduce((x, y) => x.concat(y), []) + .reduce((services, service) => { + services[service.id] = { + statePath: service.statePath || config.statePath, + stateValues: { + operational: service.stateValues ? service.stateValues.operational || config.stateValues.operational : config.stateValues.operational, + maintenance: service.stateValues ? service.stateValues.maintenance || config.stateValues.maintenance : config.stateValues.maintenance, + } + }; + return services; + }, {}); +const serviceStates = existsSync('cache.json') ? JSON.parse(readFileSync('cache.json', {encoding: 'utf-8'})) : {} as Cache; let cache: CurrentStatus; updateCache(); @@ -55,23 +67,20 @@ api.post('/update/health', (req, res) => { return res.status(401).send('invalid token'); } const serviceId = req.query.service as string; - const message = req.body as GrafanaWebhookBody; + const keys = stateKeys[serviceId]; + const state = jp.value(req.body, keys.statePath); - switch (message.state) { - case "no_data": - case "alerting": - serviceStates[serviceId] = "outage"; - break; - case "paused": - serviceStates[serviceId] = "maintenance"; - break; - default: - serviceStates[serviceId] = "operational" + if (keys.stateValues.operational.includes(state)) { + serviceStates[serviceId] = 'operational'; + } else if (keys.stateValues.maintenance.includes(state)) { + serviceStates[serviceId] = 'maintenance'; + } else { + serviceStates[serviceId] = 'outage'; } updateCache(); - writeFileSync('cache.json', JSON.stringify(serviceStates), {encoding: 'UTF-8'}); + writeFileSync('cache.json', JSON.stringify(serviceStates), {encoding: 'utf-8'}); return res.send('OK'); }); @@ -94,15 +103,15 @@ function updateCache(): void { id: service.id, name: service.name, url: service.url, - state: serviceStates[service.id] || "operational" - } + state: serviceStates[service.id] || 'operational' + }; }); return { id: group.id, name: group.name, state: calculateOverallState(services.map(s => s.state)), services: services - } + }; }); cache = { state: calculateOverallState(groups.map(g => g.state)), @@ -111,7 +120,7 @@ function updateCache(): void { } function calculateOverallState(states: State[]): State { - return states.includes("outage") ? "outage" : states.includes("maintenance") ? "maintenance" : "operational" + return states.includes('outage') ? 'outage' : states.includes('maintenance') ? 'maintenance' : 'operational'; } export {api}; diff --git a/tsconfig.app.json b/tsconfig.app.json index f758d98..44795bd 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -1,5 +1,5 @@ { - "extends": "./tsconfig.json", + "extends": "./tsconfig.base.json", "compilerOptions": { "outDir": "./out-tsc/app", "types": [] diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..ac05639 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,23 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "module": "es2020", + "moduleResolution": "node", + "importHelpers": true, + "target": "es2015", + "lib": [ + "es2018", + "dom" + ] + }, + "angularCompilerOptions": { + "fullTemplateTypeCheck": true, + "strictInjectionParameters": true + } +} diff --git a/tsconfig.json b/tsconfig.json index 8c4ef3b..e574583 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,23 +1,20 @@ +/* + This is a "Solution Style" tsconfig.json file, and is used by editors and TypeScript’s language server to improve development experience. + It is not intended to be used to perform a compilation. + + To learn more about this file see: https://angular.io/config/solution-tsconfig. +*/ { - "compileOnSave": false, - "compilerOptions": { - "baseUrl": "./", - "outDir": "./dist/out-tsc", - "sourceMap": true, - "declaration": false, - "downlevelIteration": true, - "experimentalDecorators": true, - "module": "esnext", - "moduleResolution": "node", - "importHelpers": true, - "target": "es2015", - "lib": [ - "es2018", - "dom" - ] - }, - "angularCompilerOptions": { - "fullTemplateTypeCheck": true, - "strictInjectionParameters": true - } -} + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.server.json" + } + ] +} \ No newline at end of file diff --git a/tsconfig.server.json b/tsconfig.server.json index 61aa6e8..7b4ee4b 100644 --- a/tsconfig.server.json +++ b/tsconfig.server.json @@ -2,11 +2,11 @@ "extends": "./tsconfig.app.json", "compilerOptions": { "outDir": "./out-tsc/app-server", - "module": "commonjs", "types": [ "node" ] - }, + , "target": "es2016" +}, "files": [ "src/main.server.ts", "server.ts" diff --git a/tsconfig.spec.json b/tsconfig.spec.json index 6400fde..1db2e6e 100644 --- a/tsconfig.spec.json +++ b/tsconfig.spec.json @@ -1,5 +1,5 @@ { - "extends": "./tsconfig.json", + "extends": "./tsconfig.base.json", "compilerOptions": { "outDir": "./out-tsc/spec", "types": [