From 40351f934e91f229f90a70651eab384fc0566d67 Mon Sep 17 00:00:00 2001 From: shockrah Date: Wed, 14 Apr 2021 22:51:35 -0700 Subject: [PATCH] + New cache ipc handlers for adding server caches Interface for this is fiddly and probably requires real docs to be further developed without losing my mind doc the whole cache at some point + New cache ipc handlers for adding a new open web socket Web socket comes with some basic listeners, however very litte/nothing is being done check the health of these connections or to try when possible. + Cache now adds actual message objects to its message arrays instead of raw strings (wew) + Events module has been added to move the parsing logic/validation away from everything else + The basic Event structure has some niftier-than-you-think behavior for data acccess which the cache can leverage for more concise+ correct behavior --- freechat-client/main.js | 55 ++++++++++++++++++++++++++++++ freechat-client/src/cache.ts | 57 +++++++++++++++++++++---------- freechat-client/src/events.ts | 63 +++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 17 deletions(-) create mode 100644 freechat-client/src/events.ts diff --git a/freechat-client/main.js b/freechat-client/main.js index 41f1712..3a77b5d 100644 --- a/freechat-client/main.js +++ b/freechat-client/main.js @@ -2,6 +2,8 @@ const { ipcMain } = require('electron') const { app, BrowserWindow } = require('electron') const { ArgumentParser } = require('argparse') const path = require('path') +const URL = require('url') +const WebSocket = require('ws') const { Config, from_cli_parser } = require('./src/config.js') const { Cache } = require('./src/cache') @@ -18,6 +20,8 @@ const arguments = (() => { let win let config = from_cli_parser(arguments) let cache = new Cache() +let wsclient = null + // NOTE: this line is only here for demonstration as the current make script exposes this var // so that we can test against self signed certificates but still use ssl @@ -72,3 +76,54 @@ ipcMain.handle('config-update', async function(event, updated_config) { await config.write_disk() }) + +ipcMain.handle('open-socket', async function(event, kwargs ) { + const server_conf = kwargs['conf'] + const conn_url = kwargs['url'] + + const cache_target = server_conf['wsurl'] + + // shutdown the websocket incase there is already one running + if(wsclient != null) { + console.log("Live websocket found, closing for replacement") + wsclient.close() + wsclient = null + } + + wsclient = new WebSocket(conn_url) + + // Setting some listeners for the websocket + wsclient.on('open', function() { + cache.set_active(cache_target) + }) + wsclient.on('close', function(code, reason) { + console.log("Websocket close event: ",code, reason) + }) + wsclient.on('error', function(error) { + console.log('Websocket runtime error: ', error) + }) + wsclient.on('message', function(message) { + try { + cache.new_message(cache_target, message) + } catch (err) { + console.log(err) + } + }) +}) + +ipcMain.handle('cache-new-server', function(event, kwargs) { + const serv_config = kwargs['cfg'] + const channels = kwargs['channels'] + const messages = kwargs['messages'] + // optional flag which tells the cache to set this as the active server + // NOTE: Must also be the wsurl + const active_target = kwargs['active_target'] + + cache.add_server(active_target, serv_config, channels, messages) + if(active_target) { + if(active_target != serv_config['wsurl']) { + throw 'Active target must be the same as the target\'s web-socket URL' + } + cache.set_active(active_target) + } +}) diff --git a/freechat-client/src/cache.ts b/freechat-client/src/cache.ts index 7409cb1..41bab01 100644 --- a/freechat-client/src/cache.ts +++ b/freechat-client/src/cache.ts @@ -1,14 +1,18 @@ +import * as EventEmitter from 'events'; // unused for now but maybe useful later import { ServerConfig, Message, Channel } from './types'; +import { Event } from './events'; + class ServerCache { channels: Array - // Channel id's are used to key into the list of messages for that given channel - messages: Map> + // Channel-id => Array + messages: Map> config: ServerConfig constructor(config: ServerConfig, channels: Array, messages: Array) { this.config = config this.channels = channels + this.messages = new Map() for(const message of messages) { const ref = this.messages.get(message.cid) @@ -18,45 +22,45 @@ class ServerCache { ref.push(message) } } + console.log('initial message cache: ', this.messages) } push_message(message: Message) { let ref = this.messages.get(message.cid) if(ref) { - ref.push(message) + ref.push(message) // NOTE: this one here? + console.log('pushed') } else { this.messages.set(message.cid, [message]) + console.log('set') } + console.log('message cache state: ', this.messages.get(message.cid)) + console.log('message: ', message) } } -export class Cache { +export class Cache extends EventEmitter { // hostname -> Cache - servers: Map + private servers: Map + public active_host: String constructor() { + super() this.servers = new Map() + this.active_host = null } - add_server(hostname: String, servercfg: ServerConfig, channels: Array, messages: Array) { + add_server(url: String, servercfg: ServerConfig, channels: Array, messages: Array) { let new_entry = new ServerCache(servercfg, channels, messages) - this.servers.set(servercfg.url, new_entry) - } - - add_message(hostname: String, message: Message) { - let ref = this.servers.get(hostname) - if(!ref) { - throw `No server config found for ${hostname}` - } - - ref.push_message(message) + this.servers.set(url, new_entry) + console.log(this.servers) } update_channels(hostname: String, channels: Array) { this.servers.get(hostname).channels = channels } - last_id(hostname: String, channel_id: Number) : Number|null { + last_id(hostname: String, channel_id: BigInt) : BigInt|null { try { let ref = this.servers.get(hostname).messages.get(channel_id) return ref[ref.length - 1].cid @@ -64,5 +68,24 @@ export class Cache { return null } } + + new_message(origin: String, message: string): void { + // we assume at this point that we have the hostname contained smoewhere + let ref = this.servers.get(origin) + if(ref) { + let event = new Event(message) + ref.push_message(event.message) + } else { + console.error(`No server with ${origin} as hostname`) + console.error('Active host: ', this.active_host) + console.error('Servers: ', this.servers) + } + } + + set_active(url: String) { + this.active_host = url + } } + + diff --git a/freechat-client/src/events.ts b/freechat-client/src/events.ts new file mode 100644 index 0000000..a5278d1 --- /dev/null +++ b/freechat-client/src/events.ts @@ -0,0 +1,63 @@ +import * as JSONBig from 'json-bigint'; +import { Channel, Message } from './types'; + +export class Event { + public type: string + public message: Message|null + // NOTE: these two fields may benefit from have a more legible property wrapping + // them, cost would be pretty minimal(probably but idrk) + public d_channel: Channel|null + public c_channel: Channel|null + + + + constructor(raw: string) { + const parsed = JSONBig.parse(raw) + this.type = parsed['type'] + + switch(this.type) { + case 'new-message' : { + this.message = this._message(parsed[this.type]) + this.d_channel = null + this.c_channel = null + break + } + case 'delete-channel' : { + this.d_channel = null + // todo + break + } + case 'create-channel' : { + this.c_channel = null + // todo + break + } + default: { + this.message = null + this.d_channel = null + this.c_channel = null + } + } + } + + // Attempts to setup a new Message object in the message field + private _message(obj: Object): Message|null { + // NOTE: this takes data from the network directly so its important + // to keep in mind the "user-friendly" field names + let uid = obj['author_id'] + let cid = obj['channel_id'] + let content = obj['content'] + let content_type = obj['content_type'] + let mid = obj['id'] + let uname = obj['name'] + let time = obj['time'] + + return new Message(mid, time, content, content_type, uid, uname, cid) + } + + // Sets up the inner channel data + private _channel(obj: Object) : Channel|null { + // todo: still require some testing to verify event data is appropriate + return null + } +}