Files
automatic/ui/monitor.ts
T
2026-05-20 18:08:10 +02:00

120 lines
4.4 KiB
TypeScript

import { debug, log } from './logger';
import { authFetch } from './authWrap';
import { monitorOption } from './settings';
interface VersionInfo {
updated?: string;
commit?: string;
branch?: string;
model?: string;
}
let monitorActive = false;
export class ConnectionMonitorState {
static ws: WebSocket | undefined;
static url = '';
static delay = 1000;
static element: HTMLElement | undefined;
static version = '';
static commit = '';
static branch = '';
static model = '';
static startup: Date = new Date();
static online = false;
static ts: Date = new Date();
static getModel(): string {
const cp = window.opts?.sd_model_checkpoint || '';
return cp ? this.trimModelName(cp) : 'unknown model';
}
static trimModelName(name: string): string {
return name.replace(/\s*\[.*\]\s*$/, '').split(/[\\/]/).pop().trim() || 'unknown model';
}
static setData({ online, data }: { online: boolean; data: VersionInfo }) {
if (online !== this.online) {
this.online = online;
this.ts = new Date();
debug('monitorState', { online: ConnectionMonitorState.online, ts: ConnectionMonitorState.ts });
}
if (data?.updated) this.version = data.updated;
if (data?.commit) this.commit = data.commit;
if (data?.branch) this.branch = data.branch;
if (data?.model) this.model = this.trimModelName(data.model);
}
static toHTML(): string {
if (!this.model) this.model = this.getModel();
return `
Version: <b>${this.version}</b><br>
Commit: <b>${this.commit}</b><br>
Branch: <b>${this.branch}</b><br>
Status: ${this.online ? '<b style="color:lime">online</b>' : '<b style="color:darkred">offline</b>'}<br>
Model: <b>${this.model}</b><br>
Since: ${this.startup.toLocaleString()}<br>
`;
}
static updateState() {
if (!this.element) {
const el = document.getElementById('logo_nav');
if (el) this.element = el;
else return;
}
this.element.dataset.hint = this.toHTML();
this.element.style.backgroundColor = this.online ? 'var(--sd-main-accent-color)' : 'var(--color-error)';
}
}
async function updateIndicator(online: boolean, data: VersionInfo = {}, msg?: string): Promise<void> {
ConnectionMonitorState.setData({ online, data });
ConnectionMonitorState.updateState();
if (msg) log('monitorConnection:', { online, data, msg });
}
async function wsMonitorLoop() {
const delayed = Date.now() - ConnectionMonitorState.ts.getTime();
if ((delayed > 60 * 60) && (ConnectionMonitorState.delay < 10) && !ConnectionMonitorState.online) ConnectionMonitorState.delay = 10000;
else if ((delayed > 5 * 60) && (ConnectionMonitorState.delay < 5) && !ConnectionMonitorState.online) ConnectionMonitorState.delay = 5000;
else ConnectionMonitorState.delay = 2000;
try {
ConnectionMonitorState.ws = new WebSocket(`${ConnectionMonitorState.url}/queue/join`);
ConnectionMonitorState.ws.onopen = () => {};
ConnectionMonitorState.ws.onmessage = () => updateIndicator(true);
ConnectionMonitorState.ws.onclose = () => setTimeout(wsMonitorLoop, ConnectionMonitorState.delay); // main re-check loop
ConnectionMonitorState.ws.onerror = (e: Event) => updateIndicator(false, {}, String((e as ErrorEvent).message || 'unknown error')); // actual error
} catch (e) {
updateIndicator(false, {}, String((e as Error).message || e));
// eslint-disable-next-line @typescript-eslint/no-use-before-define
setTimeout(monitorConnection, ConnectionMonitorState.delay);
}
}
export async function monitorConnection() {
if (!monitorActive) { // start monitor loop only once on startup
monitorActive = true;
monitorOption('sd_model_checkpoint', (newVal) => { // runs before opt actually changes
ConnectionMonitorState.model = newVal;
ConnectionMonitorState.updateState();
});
}
ConnectionMonitorState.startup = new Date();
let data: VersionInfo = {};
try {
const res = await authFetch(`${window.api}/version`);
if (!res) throw new Error('No response');
data = await res.json();
log('monitorConnection:', { data });
ConnectionMonitorState.startup = new Date();
ConnectionMonitorState.url = res.url.split('/sdapi')[0].replace('https:', 'wss:').replace('http:', 'ws:'); // update global url as ws need fqdn
updateIndicator(true, data);
wsMonitorLoop();
} catch {
updateIndicator(false, data);
setTimeout(monitorConnection, ConnectionMonitorState.delay);
}
}