made statuspage universal
updated angular to v10 added renovate.json updated .drone.yml added arm image
This commit is contained in:
parent
8b89e10cc3
commit
a580a2ee6b
15 changed files with 261 additions and 106 deletions
82
.drone.yml
82
.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
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -47,6 +47,5 @@ testem.log
|
|||
Thumbs.db
|
||||
|
||||
# custom
|
||||
|
||||
config.json
|
||||
cache.json
|
||||
|
|
14
config.json
14
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"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
63
package.json
63
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"
|
||||
}
|
||||
}
|
||||
|
|
35
renovate.json
Normal file
35
renovate.json
Normal file
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
|
@ -10,7 +10,14 @@
|
|||
|
||||
<footer>
|
||||
<div class="container">
|
||||
Made with <span class="fas fa-heart"></span> by <a href="https://sp-codes.de">sp-codes</a>
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6">
|
||||
Powered by <a href="https://git.sp-codes.de/samuel-p/universal-statuspage">universal-statuspage</a>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 text-sm-right">
|
||||
Made with <span class="fas fa-heart"></span> by <a href="https://sp-codes.de">sp-codes</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<a *ngFor="let service of group.services; last as last" mat-list-item [href]="service.url" target="_blank">
|
||||
<div matLine class="d-flex">
|
||||
<i [class]="stateClasses[service.state]"></i>
|
||||
<span>{{service.name}}</span>
|
||||
<span class="text-truncate">{{service.name}}</span>
|
||||
<span class="flex-grow-1"></span>
|
||||
<span class="text-capitalize {{service.state}}">{{service.state}}</span>
|
||||
</div>
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
|
|
23
tsconfig.base.json
Normal file
23
tsconfig.base.json
Normal file
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
|
|
Reference in a new issue