feat: implement dashboard prototype

This commit is contained in:
divocat
2025-10-06 03:43:55 +03:00
parent c75dd3e78b
commit aad6d8c002
35 changed files with 2014 additions and 26 deletions

View File

@@ -0,0 +1,82 @@
import { Podkop } from './podkop/types';
type Listener<T> = (next: T, prev: T, diff: Partial<T>) => void;
// eslint-disable-next-line
class Store<T extends Record<string, any>> {
private value: T;
private listeners = new Set<Listener<T>>();
constructor(initial: T) {
this.value = initial;
}
get(): T {
return this.value;
}
set(next: Partial<T>): void {
const prev = this.value;
const merged = { ...this.value, ...next };
if (Object.is(prev, merged)) return;
this.value = merged;
const diff: Partial<T> = {};
for (const key in merged) {
if (merged[key] !== prev[key]) diff[key] = merged[key];
}
this.listeners.forEach((cb) => cb(this.value, prev, diff));
}
subscribe(cb: Listener<T>): () => void {
this.listeners.add(cb);
cb(this.value, this.value, {}); // первый вызов без diff
return () => this.listeners.delete(cb);
}
patch<K extends keyof T>(key: K, value: T[K]): void {
this.set({ ...this.value, [key]: value });
}
getKey<K extends keyof T>(key: K): T[K] {
return this.value[key];
}
subscribeKey<K extends keyof T>(
key: K,
cb: (value: T[K]) => void,
): () => void {
let prev = this.value[key];
const unsub = this.subscribe((val) => {
if (val[key] !== prev) {
prev = val[key];
cb(val[key]);
}
});
return unsub;
}
}
export const store = new Store<{
sections: Podkop.OutboundGroup[];
traffic: { up: number; down: number };
memory: { inuse: number; oslimit: number };
connections: {
connections: unknown[];
downloadTotal: number;
memory: number;
uploadTotal: number;
};
services: {
singbox: string;
podkop: string;
};
}>({
sections: [],
traffic: { up: 0, down: 0 },
memory: { inuse: 0, oslimit: 0 },
connections: { connections: [], memory: 0, downloadTotal: 0, uploadTotal: 0 },
services: { singbox: '', podkop: '' },
});