Makes healthcheck data a server polling thing.

This commit is contained in:
2023-09-21 10:57:37 +02:00
parent e0be278630
commit 3311594f1e
7 changed files with 96 additions and 83 deletions

View File

@@ -1,5 +1,6 @@
import { dev } from '$app/environment';
import { watchDymamicConfig } from '$lib/server/config';
import { initServicePolling } from '$lib/server/serviceDataPolling';
import { initComponents, initServices } from '$lib/services/services';
if (!dev) {
@@ -8,3 +9,4 @@ if (!dev) {
await initServices();
await initComponents();
initServicePolling();

View File

@@ -1,6 +1,7 @@
import configData from '../../config.yml';
import { readFile, watch } from 'fs/promises';
import * as yml from 'js-yaml';
import type {
ColorConfig,
Config,
@@ -10,6 +11,7 @@ import type {
} from '$lib/config';
import * as ipRangeCheck from 'ip-range-check';
import { initializeServiceData } from './serviceDataPolling';
const requiredService: Required<ServiceConfig> = {
title: '',
@@ -197,6 +199,7 @@ export function watchDymamicConfig() {
const dynamicConfig = yml.load(await readFile(__filepath, 'utf8'));
_serverConfig = mergeConfig(defaultConfig, dynamicConfig);
_clientConfig = stripPrivateFields(_serverConfig);
initializeServiceData();
} catch (err) {
console.error('could not read or parse config: ' + err);
}

View File

@@ -1,45 +0,0 @@
import type { Config, ServiceConfig } from '$lib/config';
import type {
ServiceData,
ServiceHandler,
ServiceHandlerArgs,
ServiceStatus
} from '$lib/services/service';
import { getServiceHandler } from '$lib/services/services';
import { serverConfig } from './config';
export const serviceData: Array<Array<unknown>> = [];
function wrapGeneric(handler: ServiceHandler): ServiceHandler {
return handler;
}
function pollServices() {
const config: Config = serverConfig();
for (const [i, group] of config.services.entries()) {
if (serviceData[i] == undefined) {
serviceData[i] = [];
}
for (const [j, service] of group.items.entries()) {
let handler = getServiceHandler(service.type || '');
if (handler == undefined) {
handler = wrapGeneric(() => {
return {};
});
}
if (handler.constructor.name !== 'AsyncFucntion') {
handler = wrapGeneric(handler);
}
(handler({ config: service }) as Promise<ServiceData>).then((value) => {
serviceData[i][j] = value;
});
}
}
}
export function initServicePolling() {
setInterval(pollServices, 30000);
}

View File

@@ -0,0 +1,65 @@
import type { Config, ServiceConfig } from '$lib/config';
import type { AsyncServiceHandler, ServiceData, ServiceHandler } from '$lib/services/service';
import { getServiceHandler } from '$lib/services/services';
import { serverConfig } from './config';
export const serviceData: Array<Array<unknown>> = [];
function isPromise(p: any): boolean {
return typeof p === 'object' && typeof p.then === 'function';
}
async function pollGeneric(upstream: ServiceData, url: string): Promise<ServiceData> {
upstream.status = undefined;
return fetch(url).then((resp: Response): ServiceData => {
upstream.status = resp.ok ? 'online' : 'offline';
return upstream;
});
}
function wrapHandler(handler: ServiceHandler): AsyncServiceHandler {
return async (config: ServiceConfig): Promise<ServiceData> => {
const value = handler(config);
if (isPromise(value) === true) {
return (value as Promise<ServiceData>).then((data: ServiceData) => {
if (data.status != undefined) {
return data;
}
return pollGeneric(data, config.url);
});
}
return pollGeneric(value as ServiceData, config.url);
};
}
function pollServices() {
const config: Config = serverConfig();
for (const [i, group] of config.services.entries()) {
for (const [j, service] of group.items.entries()) {
const handler: ServiceHandler =
getServiceHandler(service.type || '') ||
(() => {
return {} as ServiceData;
});
wrapHandler(handler)(service).then((data: ServiceData) => {
serviceData[i][j] = data;
});
}
}
}
export function initializeServiceData() {
const config = serverConfig();
serviceData.length = config.services.length;
for (let i = 0; i < serviceData.length; i++) {
serviceData[i] = [];
}
}
export function initServicePolling() {
initializeServiceData();
pollServices();
setInterval(pollServices, 30000);
}

View File

@@ -1,11 +1,21 @@
import { type ServiceHandler } from '../service';
import type { ServiceConfig } from '$lib/config';
import { type ServiceData, type ServiceHandler } from '../service';
export const handle: ServiceHandler = ({ fetch, config }) => {
const url = config.url + '/api.php?summaryRaw&auth=' + config.api_token;
return {
logo: 'https://cdn.rawgit.com/pi-hole/graphics/master/Vortex/Vortex.svg',
raw: fetch(url).then((raw) => raw.json()),
url: url
export const handle: ServiceHandler = async (config: ServiceConfig) => {
const res: ServiceData = {
logo: 'https://cdn.rawgit.com/pi-hole/graphics/master/Vortex/Vortex.svg'
};
try {
const resp: Response = await fetch(
config.url + '/api.php?summaryRaw&auth=' + config.api_token
);
res.status = resp.ok ? 'online' : 'offline';
res.raw = resp.json();
} catch (error) {
res.status = undefined;
console.warn('could not fetch pihole status: ' + error);
}
return res;
};

View File

@@ -1,9 +1,5 @@
import type { ServiceConfig } from '$lib/config';
export interface ServiceHandlerArgs {
config: ServiceConfig;
}
export type ServiceStatus = 'online' | 'offline';
export interface ServiceData {
@@ -12,4 +8,5 @@ export interface ServiceData {
[x: string]: unknown;
}
export type ServiceHandler = (input: ServiceHandlerArgs) => ServiceData | Promise<ServiceData>;
export type ServiceHandler = (input: ServiceConfig) => ServiceData | Promise<ServiceData>;
export type AsyncServiceHandler = (input: ServiceConfig) => Promise<ServiceData>;

View File

@@ -1,31 +1,12 @@
import { clientAddressIsPrivate, clientConfig, serverConfig } from '$lib/server/config';
import type { PageServerLoad } from './$types';
import { getServiceHandler } from '$lib/services/services';
export const load: PageServerLoad = async ({ fetch, getClientAddress }) => {
const serviceData: Array<Array<unknown>> = [];
const config = clientConfig();
const privateConfig = serverConfig();
for (const [i, group] of privateConfig.services.entries()) {
const groupData: Array<unknown> = [];
serviceData.push(groupData);
for (const [j, service] of group.items.entries()) {
const handler = getServiceHandler(service.type || '');
if (handler == undefined) {
config.services[i].items[j].type = undefined;
groupData.push(undefined);
} else {
groupData.push(handler({ fetch, config: service }));
}
}
}
import type { PageServerLoad } from './$types.d';
import { serviceData } from '$lib/server/serviceDataPolling';
export const load: PageServerLoad = async ({ getClientAddress }) => {
return {
config,
serviceData,
config: clientConfig(),
serviceData: serviceData,
location: getClientAddress(),
privateAccess: clientAddressIsPrivate(getClientAddress(), privateConfig)
privateAccess: clientAddressIsPrivate(getClientAddress(), serverConfig())
};
};