|
@@ -0,0 +1,124 @@
|
|
|
|
|
+// Third party
|
|
|
|
|
+import { Component, ViewChild } from '@angular/core';
|
|
|
|
|
+import { ActivatedRoute } from '@angular/router';
|
|
|
|
|
+
|
|
|
|
|
+// Common
|
|
|
|
|
+import { ServiceCheckData } from '../../../../../common/lib/http-check-data.module';
|
|
|
|
|
+
|
|
|
|
|
+// App
|
|
|
|
|
+import { convertToStatusTimelineData } from 'src/app/lib/conversions.lib';
|
|
|
|
|
+import { ServiceApiService } from 'src/app/services/service-api.service';
|
|
|
|
|
+
|
|
|
|
|
+@Component({
|
|
|
|
|
+ selector: 'app-service-check-detail-page',
|
|
|
|
|
+ templateUrl: './service-check-detail-page.component.html',
|
|
|
|
|
+ styleUrls: ['./service-check-detail-page.component.scss'],
|
|
|
|
|
+ host: { class: 'd-flex flex-column h-100' }
|
|
|
|
|
+})
|
|
|
|
|
+export class ServiceCheckDetailPageComponent {
|
|
|
|
|
+ private rawData: ServiceCheckData[] = [];
|
|
|
|
|
+ private params?: { serverID: number; serviceID: number };
|
|
|
|
|
+
|
|
|
|
|
+ public statusData?: StatusTimelineData[];
|
|
|
|
|
+ public serviceCheck?: HttpCheckConfig;
|
|
|
|
|
+
|
|
|
|
|
+ public log: {
|
|
|
|
|
+ start?: Date;
|
|
|
|
|
+ end?: Date;
|
|
|
|
|
+ visible?: { first: Date; last?: Date };
|
|
|
|
|
+ renderHighlight?: { offsetLeft: number; width: number };
|
|
|
|
|
+ entries?: ServiceCheckData[];
|
|
|
|
|
+ loading?: boolean;
|
|
|
|
|
+ } = {};
|
|
|
|
|
+
|
|
|
|
|
+ constructor(route: ActivatedRoute, private serviceApi: ServiceApiService) {
|
|
|
|
|
+ route.params.subscribe({
|
|
|
|
|
+ next: params => {
|
|
|
|
|
+ this.params = { serverID: Number(params['serverID']), serviceID: Number(params['serviceID']) };
|
|
|
|
|
+ this.load(this.params.serverID, this.params.serviceID);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async load(serverID: number, serviceID: number) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ console.log('Loading Detail Page for Service Check:', { serverID, serviceID });
|
|
|
|
|
+
|
|
|
|
|
+ this.serviceCheck = await this.serviceApi.getServiceCheck(serverID, serviceID);
|
|
|
|
|
+
|
|
|
|
|
+ const end = new Date();
|
|
|
|
|
+ const start = new Date(end.getTime() - 1000 * 60 * 60 * 24);
|
|
|
|
|
+ this.rawData = await this.serviceApi.queryServiceData(serverID, serviceID, start, end);
|
|
|
|
|
+ this.statusData = convertToStatusTimelineData(start, end, this.rawData);
|
|
|
|
|
+
|
|
|
|
|
+ const pageEnd = end;
|
|
|
|
|
+ const pageStart = new Date(end.getTime() - 1000 * 60 * 60 * 4);
|
|
|
|
|
+ this.log.entries = await this.serviceApi.queryServiceLog(serverID, serviceID, pageStart, pageEnd);
|
|
|
|
|
+
|
|
|
|
|
+ this.log.end = end;
|
|
|
|
|
+ this.log.start = start;
|
|
|
|
|
+
|
|
|
|
|
+ setTimeout(this.logScroll.bind(this));
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error(err);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ logScroll(event?: Event) {
|
|
|
|
|
+ if (!this.log.start || !this.log.end) return;
|
|
|
|
|
+
|
|
|
|
|
+ const scrollContainer = (event ? event.target : document.getElementById('service-check-logs-scroller')) as HTMLDivElement;
|
|
|
|
|
+ this.log.visible = this.getVisibleTimespan(scrollContainer);
|
|
|
|
|
+
|
|
|
|
|
+ const absoluteWidth = this.log.end.getTime() - this.log.start.getTime();
|
|
|
|
|
+ this.log.renderHighlight = {
|
|
|
|
|
+ offsetLeft: ((this.log.visible.first.getTime() - this.log.start.getTime()) / absoluteWidth) * 100,
|
|
|
|
|
+ width: (((this.log.visible.last ?? this.log.visible.first).getTime() - this.log.visible.first.getTime()) / absoluteWidth) * 100
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (this.log.visible.first.getTime() === this.log.entries?.[0]?.time.getTime()) {
|
|
|
|
|
+ this.reloadOnScroll();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private async reloadOnScroll() {
|
|
|
|
|
+ if (this.log.loading) return;
|
|
|
|
|
+ if (!this.log.start || !this.log.end || !this.params || !this.log.visible) return;
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.log.loading = true;
|
|
|
|
|
+
|
|
|
|
|
+ const end = new Date(this.log.visible.first.getTime() - 1000);
|
|
|
|
|
+ let start = new Date(end.getTime() - 1000 * 60 * 60 * 2);
|
|
|
|
|
+
|
|
|
|
|
+ if (start.getTime() < this.log.start.getTime()) start = this.log.start;
|
|
|
|
|
+
|
|
|
|
|
+ const entries = await this.serviceApi.queryServiceLog(this.params.serverID, this.params.serviceID, start, end);
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: Problem: This runs into infinite reloading if entries.length is 0 here (because nothing got logged in the last hour)
|
|
|
|
|
+
|
|
|
|
|
+ this.log.entries = [...entries, ...(this.log.entries ?? [])];
|
|
|
|
|
+
|
|
|
|
|
+ this.log.loading = false;
|
|
|
|
|
+
|
|
|
|
|
+ setTimeout(this.logScroll.bind(this), 1000);
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ console.error(err);
|
|
|
|
|
+ this.log.loading = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private getVisibleTimespan(scrollContainer: HTMLDivElement) {
|
|
|
|
|
+ const trsInView: HTMLTableRowElement[] = [];
|
|
|
|
|
+ scrollContainer.querySelectorAll('tr').forEach(tr => {
|
|
|
|
|
+ if (tr.offsetTop + tr.clientHeight >= scrollContainer.scrollTop && tr.offsetTop <= scrollContainer.clientHeight + scrollContainer.scrollTop) {
|
|
|
|
|
+ trsInView.push(tr);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const datesInView = trsInView.map(tr => new Date(tr.dataset['time'] ?? ''));
|
|
|
|
|
+ const first = datesInView.reduce((res, cur) => (cur.getTime() < res ? cur.getTime() : res), Number.MAX_SAFE_INTEGER);
|
|
|
|
|
+ const last = datesInView.reduce((res, cur) => (cur.getTime() > res ? cur.getTime() : res), Number.MIN_SAFE_INTEGER);
|
|
|
|
|
+
|
|
|
|
|
+ return { first: new Date(first), last: new Date(last) };
|
|
|
|
|
+ }
|
|
|
|
|
+}
|