file sending refactor

This commit is contained in:
Rolands 2023-02-20 20:02:13 +02:00
parent 2d45394414
commit 454e714027
3 changed files with 37 additions and 32 deletions

View file

@ -6,6 +6,7 @@ import b64 from 'base64-js'
//types
import type { id, PropKey, PropValue, CoreMessageKind, ClientMessageKind, Bit } from './types.js'
import type { RateLimit } from './ratelimit.js'
import type { SocioFiles } from './types.js';
type MessageDataObj = { id: id, verb?: string, table?: string, status?:string, result?:string|object|boolean|PropValue, prop?:PropKey, data?:object };
type SubscribeCallbackObjectSuccess = ((res: object | object[]) => void) | null;
type SubscribeCallbackObject = { success: SubscribeCallbackObjectSuccess, error?: Function};
@ -145,12 +146,11 @@ export class SocioClient extends LogHandler {
}else throw new E('Not enough prop info sent from server to perform prop update.', data)
break;
case 'CMD': if(this.lifecycle_hooks?.cmd) this.lifecycle_hooks.cmd(data?.data); break; //the server pushed some data to this client, let the dev handle it
case 'ERR'://when using this, make sure that the setup query is a promise func. The result field is used as a cause of error msg on the backend
this.#FindID(kind, data?.id);
case 'ERR'://The result field is sometimes used as a cause of error msg on the backend
if (typeof this.#queries[data.id] == 'function')
(this.#queries[data.id] as Function)(null);
throw new E(`Request to Server returned ERROR response for query id, reason #[err-msg-kind]`, data.id, data?.result as Bit);
(this.#queries[data.id] as Function)();
this.HandleError(new E(`Request to Server returned ERROR response for query id, reason #[err-msg-kind]`, data?.id, data?.result as Bit))
case 'RECON':
this.#FindID(kind, data?.id);
//@ts-expect-error
@ -169,7 +169,27 @@ export class SocioClient extends LogHandler {
this.#ws?.send(JSON.stringify(Object.assign({}, { kind, data:data[0] }, ...data.slice(1))));
this.HandleInfo('sent:', kind, data);
}
SendBlob(blob: Blob | ArrayBuffer | ArrayBufferView){
async SendFiles(files:File[], other_data:object|undefined={}){
const proc_files: SocioFiles = {}; //my own kind of FormData, specific for files, because FormData is actually a very riggid type
//add each file
for(const file of files){
//relevant info about files is stored in file_meta
const file_meta = {
lastModified: file.lastModified,
size: file.size,
type: file.type
};
proc_files[file.name] = { file_meta, bin: b64.fromByteArray(new Uint8Array(await file.arrayBuffer()))}; //this is the best way that i could find. JS is really unhappy about binary data
}
//create the server request as usual
const {id, prom} = this.CreateQueryPromise();
this.Send('FILES', { id, files:proc_files, other_data});
return prom;
}
SendBinary(blob: Blob | ArrayBuffer | ArrayBufferView) { //send binary. Unfortunately, it is not useful for me to invent my own byte formats and build functionality. You can tho. This is just low level access.
if (this.#queries[`BLOB`]) throw new E('BLOB already being uploaded. Wait until the last query completes!');
this.#ws?.send(blob);
@ -179,25 +199,6 @@ export class SocioClient extends LogHandler {
this.#queries[`BLOB`] = res
});
}
async SendFiles(files:File[]){
if (this.#queries[`FILES`]) throw new E('FILES already being uploaded. Wait until the last query completes!');
const proc_files = {};
for(const file of files){
const file_meta = {
lastModified: file.lastModified,
size: file.size,
type: file.type
};
proc_files[file.name] = { file_meta, bin: b64.fromByteArray(new Uint8Array(await file.arrayBuffer()))};
}
const {id, prom} = this.CreateQueryPromise();
this.Send('FILES', { id:id, files:proc_files});
return prom;
}
CreateQueryPromise(){
const id = this.GenKey;
const prom = new Promise((res) => {
@ -361,10 +362,10 @@ export class SocioClient extends LogHandler {
return SocioClient.#key
}
}
//checks if the ID of a query exists (i.e. has been registered), otherwise rejects and logs
//checks if the ID of a query exists, otherwise rejects and logs
#FindID(kind: string, id: id) {
if (!(id in this.#queries))
throw new E(`${kind} message for unregistered SQL query! msg_id -`, id, Object.keys(this.#queries))
throw new E(`${kind} message for unregistered SQL query! msg_id -`, id);
}
#HandleBasicPromiseMessage(kind:string, data:MessageDataObj){
this.#FindID(kind, data?.id);

View file

@ -1,9 +1,9 @@
import fs from 'fs';
import b64 from 'base64-js'
import { default as os_path } from "path";
// import { log } from './logging.js';
export type SocioFiles = {[filename:string]:{file_meta:{}, bin:string}};
//types
import type { SocioFiles } from './types.js';
export async function SaveFilesToDiskPath(path_array: string[], files: SocioFiles){
for (const [filename, file_data] of Object.entries(files)) {

10
core/types.d.ts vendored
View file

@ -1,12 +1,16 @@
//general types
export type id = string | number;
export type Bit = 0 | 1;
//props
export type PropKey = string;
export type PropValue = object | string | number | null;
export type PropAssigner = (key: PropKey, new_val:PropValue) => boolean;
//misc
export type SocioFiles = {[filename: string]: {file_meta: {lastModified: number, size: number, type: string}, bin: string } }; //bin is a base64 string of the bytes of the raw file
export type QueryMarker = 'socio' | 'auth' | 'perm';
//msg kinds
export type CoreMessageKind = 'SUB' | 'UNSUB' | 'SQL' | 'PING' | 'AUTH' | 'GET_PERM' | 'PROP_SUB' | 'PROP_UNSUB' | 'PROP_GET' | 'PROP_SET' | 'SERV' | 'ADMIN' | 'RECON' | 'FILES';
export type ClientMessageKind = 'CON' | 'UPD' | 'PONG' | 'AUTH' | 'GET_PERM' | 'RES' | 'ERR' | 'PROP_UPD' | 'CMD' | 'RECON';
export type QueryMarker = 'socio' | 'auth' | 'perm';
export type ClientMessageKind = 'CON' | 'UPD' | 'PONG' | 'AUTH' | 'GET_PERM' | 'RES' | 'ERR' | 'PROP_UPD' | 'CMD' | 'RECON';