mirror of
https://github.com/Rolands-Laucis/Socio.git
synced 2026-05-15 06:05:53 -06:00
86 lines
3.5 KiB
TypeScript
86 lines
3.5 KiB
TypeScript
//If God did not exist, it would be necessary to invent Him. /Voltaire/
|
|
|
|
import { LogHandler, E } from "./logging.js";
|
|
import { WebSocket as nodeWebSocket } from "ws";
|
|
import { socio_decode, socio_encode, ClientMessageKind } from './utils.js';
|
|
|
|
//types
|
|
import type { id, PropValue, LoggingOpts } from './types.d.ts';
|
|
type MessageDataObj = { id: id, status?: string, result?: string | object | boolean | PropValue, data?: object };
|
|
type AdminClientOptions = { url: string, client_secret: string } & LoggingOpts;
|
|
|
|
export class AdminClient extends LogHandler{
|
|
//private:
|
|
#ws: nodeWebSocket;
|
|
#client_id = '';
|
|
#client_secret = '';
|
|
static #key = 1 //all instances will share this number, such that they are always kept unique. Tho each of these clients would make a different session on the backend, but still
|
|
#is_ready: Function | boolean = false;
|
|
#queries: { [id: id]: Function } = {}; //keeps a dict of all querie promises
|
|
|
|
constructor({ url = '', client_secret = '', logging = { verbose: false, hard_crash: false } }: AdminClientOptions){
|
|
super({prefix:'SocioAdmin', ...logging});
|
|
|
|
if (client_secret.length < 16)
|
|
throw new E('client_secret length must be at least 16 char for safety. Got ', client_secret.length);
|
|
|
|
this.#client_secret = client_secret;
|
|
this.#ws = new nodeWebSocket(url);
|
|
// throw new E('Must pass websocket class! Either the browser websocket, or a backend ws lib websocket')
|
|
this.#ws.on('error', this.HandleError);
|
|
this.#ws.on('message', this.#Message.bind(this));
|
|
}
|
|
|
|
#Message(d:Uint8Array, isBinary:boolean){
|
|
const { kind, data }: { kind: ClientMessageKind; data: MessageDataObj } = socio_decode(d)
|
|
|
|
switch(kind){
|
|
case ClientMessageKind.CON:{
|
|
//@ts-expect-error
|
|
this.#client_id = data;//should just be a string
|
|
if (this.#is_ready !== false && typeof this.#is_ready === "function")
|
|
this.#is_ready(true); //resolve promise to true
|
|
if (this.verbose) this.done(`Connected.`, this.#client_id);
|
|
|
|
this.#is_ready = true;
|
|
break;
|
|
}
|
|
case ClientMessageKind.RES:{
|
|
this.HandleInfo('recv:', kind, data);
|
|
this.#HandleBasicPromiseMessage(data);
|
|
break;
|
|
}
|
|
default: throw new E(`Unrecognized message kind!`, kind, data);
|
|
}
|
|
}
|
|
|
|
//generates a unique key either via static counter or user provided key gen func
|
|
get #GenKey(): id {
|
|
AdminClient.#key += 1;
|
|
return AdminClient.#key;
|
|
}
|
|
|
|
#HandleBasicPromiseMessage(data: MessageDataObj) {
|
|
if (data.id in this.#queries)
|
|
this.#queries[data.id](data?.result || null); //resolve query promise
|
|
delete this.#queries[data.id]; //clear memory
|
|
}
|
|
|
|
Run(function_name:string, ...args:any[]){
|
|
//create the promise
|
|
const id = this.#GenKey;
|
|
const prom = new Promise((res) => {
|
|
this.#queries[id] = res
|
|
});
|
|
|
|
//send out the request
|
|
this.#ws.send(socio_encode({ kind: 'ADMIN', data: { id: id, client_secret:this.#client_secret, function: function_name, args: args } }));
|
|
|
|
//let the caller await the promise resolve
|
|
return prom;
|
|
}
|
|
|
|
ready(): Promise<boolean> { return this.#is_ready === true ? (new Promise(res => res(true))) : (new Promise(res => this.#is_ready = res)) }
|
|
|
|
Close(){this.#ws.close();}
|
|
}
|