Makes healthcheck data a server polling thing.
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { dev } from '$app/environment';
|
import { dev } from '$app/environment';
|
||||||
import { watchDymamicConfig } from '$lib/server/config';
|
import { watchDymamicConfig } from '$lib/server/config';
|
||||||
|
import { initServicePolling } from '$lib/server/serviceDataPolling';
|
||||||
import { initComponents, initServices } from '$lib/services/services';
|
import { initComponents, initServices } from '$lib/services/services';
|
||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
@@ -8,3 +9,4 @@ if (!dev) {
|
|||||||
|
|
||||||
await initServices();
|
await initServices();
|
||||||
await initComponents();
|
await initComponents();
|
||||||
|
initServicePolling();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import configData from '../../config.yml';
|
import configData from '../../config.yml';
|
||||||
import { readFile, watch } from 'fs/promises';
|
import { readFile, watch } from 'fs/promises';
|
||||||
import * as yml from 'js-yaml';
|
import * as yml from 'js-yaml';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ColorConfig,
|
ColorConfig,
|
||||||
Config,
|
Config,
|
||||||
@@ -10,6 +11,7 @@ import type {
|
|||||||
} from '$lib/config';
|
} from '$lib/config';
|
||||||
|
|
||||||
import * as ipRangeCheck from 'ip-range-check';
|
import * as ipRangeCheck from 'ip-range-check';
|
||||||
|
import { initializeServiceData } from './serviceDataPolling';
|
||||||
|
|
||||||
const requiredService: Required<ServiceConfig> = {
|
const requiredService: Required<ServiceConfig> = {
|
||||||
title: '',
|
title: '',
|
||||||
@@ -197,6 +199,7 @@ export function watchDymamicConfig() {
|
|||||||
const dynamicConfig = yml.load(await readFile(__filepath, 'utf8'));
|
const dynamicConfig = yml.load(await readFile(__filepath, 'utf8'));
|
||||||
_serverConfig = mergeConfig(defaultConfig, dynamicConfig);
|
_serverConfig = mergeConfig(defaultConfig, dynamicConfig);
|
||||||
_clientConfig = stripPrivateFields(_serverConfig);
|
_clientConfig = stripPrivateFields(_serverConfig);
|
||||||
|
initializeServiceData();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('could not read or parse config: ' + err);
|
console.error('could not read or parse config: ' + err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
65
src/lib/server/serviceDataPolling.ts
Normal file
65
src/lib/server/serviceDataPolling.ts
Normal 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);
|
||||||
|
}
|
||||||
@@ -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 }) => {
|
export const handle: ServiceHandler = async (config: ServiceConfig) => {
|
||||||
const url = config.url + '/api.php?summaryRaw&auth=' + config.api_token;
|
const res: ServiceData = {
|
||||||
|
logo: 'https://cdn.rawgit.com/pi-hole/graphics/master/Vortex/Vortex.svg'
|
||||||
return {
|
|
||||||
logo: 'https://cdn.rawgit.com/pi-hole/graphics/master/Vortex/Vortex.svg',
|
|
||||||
raw: fetch(url).then((raw) => raw.json()),
|
|
||||||
url: url
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import type { ServiceConfig } from '$lib/config';
|
import type { ServiceConfig } from '$lib/config';
|
||||||
|
|
||||||
export interface ServiceHandlerArgs {
|
|
||||||
config: ServiceConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ServiceStatus = 'online' | 'offline';
|
export type ServiceStatus = 'online' | 'offline';
|
||||||
|
|
||||||
export interface ServiceData {
|
export interface ServiceData {
|
||||||
@@ -12,4 +8,5 @@ export interface ServiceData {
|
|||||||
[x: string]: unknown;
|
[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>;
|
||||||
|
|||||||
@@ -1,31 +1,12 @@
|
|||||||
import { clientAddressIsPrivate, clientConfig, serverConfig } from '$lib/server/config';
|
import { clientAddressIsPrivate, clientConfig, serverConfig } from '$lib/server/config';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types.d';
|
||||||
import { getServiceHandler } from '$lib/services/services';
|
import { serviceData } from '$lib/server/serviceDataPolling';
|
||||||
|
|
||||||
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 }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ getClientAddress }) => {
|
||||||
return {
|
return {
|
||||||
config,
|
config: clientConfig(),
|
||||||
serviceData,
|
serviceData: serviceData,
|
||||||
location: getClientAddress(),
|
location: getClientAddress(),
|
||||||
privateAccess: clientAddressIsPrivate(getClientAddress(), privateConfig)
|
privateAccess: clientAddressIsPrivate(getClientAddress(), serverConfig())
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user