new features and working vite plugin

This commit is contained in:
Rolands 2022-11-03 19:26:32 +02:00
parent d4c5cffcde
commit 2510d05a39
10 changed files with 257 additions and 111 deletions

View file

@ -1,22 +1,26 @@
//https://stackoverflow.com/questions/38946112/es6-import-error-handling
let info = null
try { //for my logger
await import('@rolands/log')
var { info, log, error, done, soft_error, setPrefix, setShowTime } = await import('@rolands/log')
setPrefix('Socio Client')
setShowTime(false)
} catch (e) {
info = (...objs) => console.log('[Socio Client]', ...objs)
console.log('[Socio Client ERROR]', e)
var info = (...objs) => console.log('[Socio Client]', ...objs)
var done = (...objs) => console.log('[Socio Client]', ...objs)
var log = (...objs) => console.log('[Socio Client]', ...objs)
var soft_error = (...objs) => console.log('[Socio Client]', ...objs)
}
//"Because he not only wants to perform well, he wants to be well receivedand the latter lies outside his control." /Epictetus/
export class WSClient {
// private:
#queries = {} //sql:[callback]
#queries = {} //id:[callback]
#is_ready = false
#verbose = false
#ws=null
static #key = 0 //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
constructor(url, {name = '', verbose=false, keep_alive=true, reconnect_tries=3, push_callback=null} = {}) {
constructor(url, {name = '', verbose=false, keep_alive=true, reconnect_tries=1, push_callback=null} = {}) {
if (window || undefined && url.startsWith('ws://'))
info('UNSECURE WEBSOCKET URL CONNECTION! Please use wss:// and https:// protocols in production to protect against man-in-the-middle attacks.')
@ -25,35 +29,36 @@ export class WSClient {
this.push = push_callback
this.#connect(url, keep_alive, verbose, reconnect_tries)
this.#ws.addEventListener('message', this.message.bind(this));
this.#ws.addEventListener('message', this.#message.bind(this));
}
#connect(url, keep_alive, verbose, reconnect_tries){
this.#ws = new WebSocket(url)
if (keep_alive && reconnect_tries)
this.#ws.addEventListener("close", () => {
if (this.#verbose) info(`WebSocket closed. Retrying...`, this.name);
if (this.#verbose) soft_error(`WebSocket closed. Retrying...`, this.name);
this.#connect(url, keep_alive, verbose, reconnect_tries - 1)
}); // <- rise from your grave!
}
message(e) {
#message(e) {
const [kind, data] = JSON.parse(e.data)
if (this.#verbose) info(kind, data)
if (this.#verbose) info('recv:',kind, data)
switch(kind){
case 'CON': this.ses_id = data; this.#is_ready = true; if (this.#verbose) info(`WebSocket connected.`, this.name); break;
case 'CON': this.ses_id = data; this.#is_ready = true; if (this.#verbose) done(`WebSocket connected.`, this.name); break;
case 'UPD':
if (data.sql in this.#queries)
this.#queries[data.sql].forEach(f => f(data.result));
else if (this.#verbose) info(`UPD message for unregistered SQL query! [${data.sql}] with data:`, data)
if (data.id in this.#queries)
this.#queries[data.id].f.forEach(f => f(data.result));
else if (this.#verbose) soft_error(`${kind} message for unregistered SQL query! [${data.id}] with data:`, data)
break;
case 'SQL':
if (data.sql in this.#queries)
this.#queries[data.sql](data.result);
if (data.id in this.#queries)
this.#queries[data.id](data.result);
else if (this.#verbose) soft_error(`${kind} message for unregistered SQL query! [${data.id}] with data:`, data)
break;
case 'PONG': if (this.#verbose) info('pong', data?.num); break;
case 'PUSH': this.push(data); break;
case 'PONG': if (this.#verbose) info('pong', data?.id); break;
// case 'PUSH': this.push(data); break;
// case '': break;
default: info(`Unrecognized message kind! [${kind}] with data:`, data);
}
@ -74,29 +79,46 @@ export class WSClient {
//private method
#send(data=[]){
this.#ws.send(JSON.stringify([this.ses_id, ...data]))
if (this.#verbose) info('sent:', ...data)
}
//subscribe to an sql query. Can add multiple callbacks where ever in your code, if their sql queries are identical
subscribe({ sql = '', params = null } = {}, callback = null, t=null){
if (sql in this.#queries)
this.#queries[sql].push(t ? callback.bind(t) : callback)
const found = Object.entries(this.#queries).find(q => q[1].sql === sql)
if (found)
this.#queries[found[0]].f.push(t ? callback.bind(t) : callback)
else{
this.#queries[sql] = [t ? callback.bind(t) : callback]
this.#send(['REG', { sql: sql, params: params }])
const id = this.#gen_key
this.#queries[id] = { sql: sql, f: [t ? callback.bind(t) : callback] }
this.#send(['REG', { id: id, sql: sql, params: params }])
}
// info('Registered', sql)
}
async query(sql='', params=null){
//set up a promise which resolve function is in the queries data structure, such that in the message handler it can be called, therefor the promise resolved, therefor awaited and return from this function
const id = this.#gen_key;
const prom = new Promise((res) => {
this.#queries[sql] = res
this.#queries[id] = res
})
//send off the request, which will be resolved in the message handler
this.#send(['SQL', { sql: sql, params: params }])
this.#send(['SQL', { id:id, sql: sql, params: params }])
return await prom
}
//sends a ping with either the user provided number or an auto generated number, for keeping track of packets and debugging
ping(num=0){
this.#send(['PING', { num: num }])
this.#send(['PING', { id: num || this.#gen_key }])
}
//generates a unique key either via static counter or user provided key gen func
get #gen_key() {
if (this?.key_generator)
return this.key_generator()
else{
WSClient.#key += 1
return WSClient.#key //neat js trick - symbols are unique even if their strings are identical
}
}
}

View file

@ -8,58 +8,70 @@ import { WebSocketServer } from 'ws'; //https://github.com/websockets/ws https:/
export class SessionManager{
// private:
#wss=null
#sessions = {}//ses_id:websocket
#verbose=false
#sessions = {}//client_id:websocket
#secure=null
#lifecycleHooks = { CON: [], DISCON: [], message: [], update: [] } //, '': []
#lifecycleHooks = { con: null, discon: null, msg: null, upd: null }
constructor(opts = {}, DB_query_callback = null, { secure =null, verbose = false } = {}){
constructor(opts = {}, DB_query_function = null, { secure =null, verbose = false } = {}){
setPrefix('Socio'); setShowTime(false); //for my logger
this.#wss = new WebSocketServer(opts); //take a look at the WebSocketServer docs - the opts can have a server param, that can be your http server
this.Query = DB_query_callback
this.#verbose = verbose
this.Query = DB_query_function
this.verbose = verbose
this.#secure = secure
this.#wss.on('connection', this.Connect.bind(this)); //https://thenewstack.io/mastering-javascript-callbacks-bind-apply-call/ have to bind 'this' to the function, otherwise it will use the .on()'s 'this', so that this.[prop] are not undefined
this.#wss.on('connection', this.#Connect.bind(this)); //https://thenewstack.io/mastering-javascript-callbacks-bind-apply-call/ have to bind 'this' to the function, otherwise it will use the .on()'s 'this', so that this.[prop] are not undefined
this.#wss.on('close', (...stuff) => { info('WebSocketServer close event', ...stuff) });
this.#wss.on('error', (...stuff) => { error('WebSocketServer error event', ...stuff)});
}
async Connect(conn, req){
const ses_id = UUID()
this.#sessions[ses_id] = new Session(ses_id, conn)
conn.send(JSON.stringify(['CON', ses_id]));
if (this.#verbose) info('CON', ses_id)
#Connect(conn, req){
//construct the new session with a unique ID
const client_id = UUID()
this.#sessions[client_id] = new Session(client_id, conn, this.verbose)
conn.on('message', this.Message.bind(this));
//pass the object to the connection hook, if it exists
if (this.#lifecycleHooks.con)
this.#lifecycleHooks.con(this.#sessions[client_id])
//notify the client of their ID
conn.send(JSON.stringify(['CON', client_id]));
if (this.verbose) info('CON', client_id)
//set this client websockets event handlers
conn.on('message', this.#Message.bind(this));
conn.on('close', () => {
if (this.#verbose) info('DISCON', ses_id)
delete this.#sessions[ses_id]
//trigger hook
if (this.#lifecycleHooks.discon)
this.#lifecycleHooks.discon(this.#sessions[client_id])
//delete the connection object
delete this.#sessions[client_id]
if (this.verbose) info('DISCON', client_id)
});
}
async Message(req, head){
const [ses_id, kind, data] = JSON.parse(req.toString())
if (this.#verbose) info(`received [${kind}] from [${ses_id}]`);
async #Message(req, head){
const [client_id, kind, data] = JSON.parse(req.toString())
if (this.#secure && data?.sql) data.sql = this.#secure.DecryptString(data.sql) //if this is supposed to be secure and sql was received, then decrypt it before continuing
if (this.verbose) info(`received [${kind}] from [${client_id}]`, data);
switch (kind) {
case 'REG':
if(ses_id in this.#sessions)
this.#sessions[ses_id].Send(['UPD', { sql: data.sql, result: await this.Query(data.sql, data.params) }])
if(client_id in this.#sessions)
this.#sessions[client_id].Send(['UPD', { id:data.id, result: await this.Query(data.sql, data.params) }])
//set up hook
if (QueryUtils.QueryIsSelect(data.sql))
QueryUtils.ParseSQLForTables(data.sql).forEach(t => this.#sessions[ses_id].RegisterHook(t, data.sql, data.params));
QueryUtils.ParseSQLForTables(data.sql).forEach(t => this.#sessions[client_id].RegisterHook(t, data.id, data.sql, data.params));
break;
case 'SQL':
const is_select = QueryUtils.QueryIsSelect(data.sql)
if (ses_id in this.#sessions){
if (client_id in this.#sessions){
const res = this.Query(data.sql, data.params)
if (is_select) //wait for result
this.#sessions[ses_id].Send(['SQL', { sql: data.sql, result: await res }])
this.#sessions[client_id].Send(['SQL', { id: data.id, result: await res }])
}
//if the sql wasnt a SELECT, but altered some resource, then need to propogate that to other connection hooks
@ -67,65 +79,94 @@ export class SessionManager{
this.Update(QueryUtils.ParseSQLForTables(data.sql))
break;
case 'PING': this.#sessions[ses_id].Send(['PONG', { num: data?.num}]); break;
case 'PING': this.#sessions[client_id].Send(['PONG', { id: data?.id}]); break;
// case '': break;
default: if (this.#verbose) error(`Unrecognized message kind! [${kind}] with data:`, data);
default: if (this.verbose) error(`Unrecognized message kind! [${kind}] with data:`, data);
}
}
//OPTIMIZATION dont await the query, but queue up all of them on another thread then await and send there
async Update(tables=[]){
// if (this.#lifecycleHooks.update) this.#lifecycleHooks.update.forEach(f => f(tables)) //call all the lifecycle hooks
Object.values(this.#sessions).forEach(async (s) => {
tables.forEach(async (t) => {
if(t in s.hooks)
for await (const data of s.hooks[t]){
s.Send(['UPD', { sql: data.sql, result: (await this.Query(data.sql, data.params)) }])
if (s.hook_tables.includes(t)){
for await (const hook of s.GetHookObjs(t)) {
s.Send(['UPD', { id: hook.id, result: (await this.Query(hook.sql, hook.params)) }])
}
}
})
})
}
//when the server wants to send some data to a specific session client - can be any raw data
SendTo(ses_id='', data={}){
if (ses_id in this.#sessions)
this.#sessions[ses_id].Send(['PUSH', data])
else soft_error(`The provided session ID [${ses_id}] was not found in the tracked web socket connections!`)
SendTo(client_id='', data={}){
if (client_id in this.#sessions)
this.#sessions[client_id].Send(['PUSH', data])
else soft_error(`The provided session ID [${client_id}] was not found in the tracked web socket connections!`)
}
Emit(data=[]){
this.#wss.emit(JSON.stringify(['EMIT', ...data]));
}
RegisterLifecycleHook(name='', callback){
RegisterLifecycleHookHandler(name='', handler=null){
if(name in this.#lifecycleHooks)
this.#lifecycleHooks.push(callback)
this.#lifecycleHooks[name] = handler
else
error(`Lifecycle hook [${name}] does not exist!`)
}
UnRegisterLifecycleHookHandler(name = '') {
if (name in this.#lifecycleHooks)
delete this.#lifecycleHooks[name]
else
error(`Lifecycle hook [${name}] does not exist!`)
}
get LifecycleHookNames(){
return Object.keys(this.#lifecycleHooks)
}
GetClientSession(client_id=''){
return this.#sessions[client_id]
}
ClientIDsOfSession(ses_id = ''){
return this.#sessions.filter(s => s.ses_id === ses_id).map(s => s.id)
}
}
//Homo vitae commodatus non donatus est. - Man's life is lent, not given. /Syrus/
class Session{
constructor(session_id='', browser_ws_conn){
this.id = session_id
this.ws = browser_ws_conn
this.hooks = {} //table_name:[sql]
#ws=null
#hooks=[]
constructor(client_id='', browser_ws_conn=null, verbose=false){
this.id = client_id
this.ses_id = null
this.#ws = browser_ws_conn
this.#hooks = {} //table_name:[sql]
this.verbose = verbose
}
RegisterHook(table='', sql='', params=null){ //TODO this is actually very bad
if (table in this.hooks && !this.hooks[table].find((t) => t.sql == sql && t.params == params))
this.hooks[table].push({ sql: sql, params: params })
RegisterHook(table='', id='', sql='', params=null){ //TODO this is actually very bad
if (table in this.#hooks && !this.#hooks[table].find((t) => t.sql == sql && t.params == params))
this.#hooks[table].push({ id: id, sql: sql, params: params })
else
this.hooks[table] = [{ sql:sql, params:params}]
// log('reg hook', table, this.hooks[table])
this.#hooks[table] = [{ id: id, sql:sql, params:params}]
log('reg hook', table, this.#hooks[table])
}
Send(data=[]){
this.ws.send(JSON.stringify(data))
this.#ws.send(JSON.stringify(data))
if (this.verbose) info('sent:', ...data)
}
get hook_tables(){return Object.keys(this.#hooks)}
GetHookObjs(table = '') { return this.#hooks[table]}
}

34
core/package-lock.json generated
View file

@ -1,15 +1,16 @@
{
"name": "socio",
"version": "0.0.1",
"version": "0.0.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "socio",
"version": "0.0.1",
"version": "0.0.2",
"license": "ISC",
"dependencies": {
"@rolands/log": "^1.1.1",
"magic-string": "^0.26.7",
"ws": "^8.9.0"
}
},
@ -18,6 +19,22 @@
"resolved": "https://registry.npmjs.org/@rolands/log/-/log-1.1.1.tgz",
"integrity": "sha512-j8jZxm0tF+7ke6V+qtdoCR2KlOba3Mog39QyxU+3pbmgJ/wuIfF7QuIqTSLqrtPQY5FzQjCwlJcvIizp33UYxg=="
},
"node_modules/magic-string": {
"version": "0.26.7",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz",
"integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==",
"dependencies": {
"sourcemap-codec": "^1.4.8"
},
"engines": {
"node": ">=12"
}
},
"node_modules/sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
},
"node_modules/ws": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz",
@ -45,6 +62,19 @@
"resolved": "https://registry.npmjs.org/@rolands/log/-/log-1.1.1.tgz",
"integrity": "sha512-j8jZxm0tF+7ke6V+qtdoCR2KlOba3Mog39QyxU+3pbmgJ/wuIfF7QuIqTSLqrtPQY5FzQjCwlJcvIizp33UYxg=="
},
"magic-string": {
"version": "0.26.7",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz",
"integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==",
"requires": {
"sourcemap-codec": "^1.4.8"
}
},
"sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
},
"ws": {
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz",

View file

@ -1,6 +1,6 @@
{
"name": "socio",
"version": "0.0.1",
"version": "0.0.2",
"description": "a websocket based live and synced front and back end",
"main": "core.js",
"type": "module",
@ -29,6 +29,7 @@
"homepage": "https://github.com/Rolands-Laucis/Socio.js/blob/master/README.md",
"dependencies": {
"@rolands/log": "^1.1.1",
"magic-string": "^0.26.7",
"ws": "^8.9.0"
}
}

View file

@ -1,4 +1,36 @@
import { randomUUID, createCipheriv, createDecipheriv } from 'crypto'
import MagicString from 'magic-string'; //https://github.com/Rich-Harris/magic-string
import { randomUUID, createCipheriv, createDecipheriv, getCiphers } from 'crypto'
try { //for my logger
var { info, log, error, done, setPrefix, setShowTime } = await import('@rolands/log')
setPrefix('Socio Secure')
setShowTime(false)
} catch (e) {
console.log('[Socio Secure ERROR]', e)
var info = (...objs) => console.log('[Socio Secure]', ...objs)
var done = (...objs) => console.log('[Socio Secure]', ...objs)
var log = (...objs) => console.log('[Socio Secure]', ...objs)
}
//https://vitejs.dev/guide/api-plugin.html
export function SocioSecurityPlugin({ secure_private_key = '', cipther_algorithm = 'aes-256-ctr', cipher_iv = '', verbose = false } = {}){
const ss = new SocioSecurity({secure_private_key:secure_private_key, cipther_algorithm:cipther_algorithm, cipher_iv:cipher_iv, verbose:verbose})
return{
name:'vite-socio-security',
enforce: 'pre',
transform(code, id){
const ext = id.split('.').slice(-1)[0]
if (['js', 'svelte', 'vue', 'jsx', 'ts'].includes(ext) && !id.match(/\/(node_modules|socio\/(core|core-client|secure))\//)) { // , 'svelte'
const s = ss.SecureSouceCode(code) //uses MagicString lib
// log(id, s.toString())
return {
code: s.toString(),
map: s.generateMap({source:id, includeContent:true})
}
}
}
}
}
//The aim of the wise is not to secure pleasure, but to avoid pain. /Aristotle/
export class SocioSecurity{
@ -6,28 +38,35 @@ export class SocioSecurity{
#key=''
#algo=''
#iv=''
#sql_string_regex = /(?<pre>\.subscribe\(\s*|\.query\(\s*|sql\s*:\s*)"(?<sql>[^"]+?)(?<post>--socio)"/ig
constructor({ secure_private_key = '', cipther_algorithm = 'AES-256-ctr', cipher_iv =''} = {}){
constructor({ secure_private_key = '', cipther_algorithm = 'aes-256-ctr', cipher_iv ='', verbose=false} = {}){
if (!cipher_iv) cipher_iv = UUID()
if (!secure_private_key || !cipther_algorithm || !cipher_iv) throw `Missing constructor arguments!`
if (secure_private_key.length < 32) throw `secure_private_key has to be at least 32 characters! Got ${secure_private_key.length}`
if (cipher_iv.length < 16) throw `cipher_iv has to be at least 16 characters! Got ${cipher_iv.length}`
if (!crypto.getCiphers().includes(cipther_algorithm)) throw `Unsupported algorithm [${cipther_algorithm}] by the Node.js Crypto module!`
if (!(getCiphers().includes(cipther_algorithm))) throw `Unsupported algorithm [${cipther_algorithm}] by the Node.js Crypto module!`
const te = new TextEncoder()
this.#key = te.encode(secure_private_key).slice(0,32) //has to be this length
this.#algo = cipther_algorithm
this.#iv = te.encode(cipher_iv).slice(0, 16) //has to be this length
if (verbose) done('Initialized SocioSecurity object succesfully')
}
//sql strings must be in single quotes and have an sql single line comment at the end with the name socio - "--socio"
Secure(source_code = '') {
const sql_string_regex = /'(?<sql>[^']+?)--socio'/i
return source_code.split('\n').map(line => {
const m = line.match(sql_string_regex)
return m?.groups?.sql ? line.replace(sql_string_regex, '\'' + this.EncryptString(m.groups.sql) + '\'') : line
}).join('\n')
//sql strings must be in double quotes and have an sql single line comment at the end with the name socio - "--socio" ^ see the sql_string_regex pattern
SecureSouceCode(source_code = '') {
const s = new MagicString(source_code);
for (const m of source_code.matchAll(this.#sql_string_regex)){
if (m?.groups?.sql){
s.update(m.index, m.index + m[0].length, `${m.groups.pre}\"` + this.EncryptString(m.groups.sql) + `\"`)
}
}
return s
}
EncryptString(query = '') {

View file

@ -3,7 +3,7 @@ import { SessionManager } from 'socio/core.js' //for using the lib as a download
import express from 'express'
import { Sequelize } from 'sequelize';
import { log, info, setPrefix, setShowTime } from '@rolands/log'; setPrefix('EXPRESS'); setShowTime(false);
import { log, done, setPrefix, setShowTime } from '@rolands/log'; setPrefix('EXPRESS'); setShowTime(false);
//constants
const server_port = 5000, ws_port = 3000 //can be set up that the websockets run on the same port as the http server
@ -19,7 +19,7 @@ await sequelize.query('INSERT INTO Users VALUES("John", 69);')
//Either you in a wrapper function or your DB interface lib should do the sql validation and sanitization, as this lib does not!
const QueryWrap = async (sql='', params={}) => (await sequelize.query(sql, { logging: false, raw: true, replacements: params }))[0]
const manager = new SessionManager({ port: ws_port }, QueryWrap, {verbose:true} )
info(`Created SessionManager on port`, ws_port)
done(`Created SessionManager on port`, ws_port)
//init
// const sec = Secure({})
@ -33,5 +33,5 @@ app.use("", express.static(__dirname));
app.use("/", express.static(__dirname + "\\..\\..\\core"));
app.listen(server_port, () => {
info(`Express webserver listening on port`, server_port, `http://localhost:${server_port}/`)
done(`Express webserver listening on port`, server_port, `http://localhost:${server_port}/`)
})

View file

@ -1,8 +1,10 @@
// import { SessionManager } from '../../core/core.js' //i use this locally
// import { SocioSecurity } from '../../core/secure.js' //i use this locally
import {SessionManager} from 'socio/core.js' //for using the lib as a download from npm
import { SocioSecurity } from 'socio/secure.js' //for using the lib as a download from npm
import { Sequelize } from 'sequelize';
import { log, info, setPrefix, setShowTime } from '@rolands/log'; setPrefix('SERVER'); setShowTime(false);
import { log, done, setPrefix, setShowTime } from '@rolands/log'; setPrefix('SERVER'); setShowTime(false);
//constants
const ws_port = 3000 //can be set up that the websockets run on the same port as the http server
@ -17,8 +19,9 @@ await sequelize.query('INSERT INTO Users VALUES("John", 69);')
//it needs the raw sql string, which can contain formatting parameters - insert dynamic data into the string.
//Either you in a wrapper function or your DB interface lib should do the sql validation and sanitization, as this lib does not!
const QueryWrap = async (sql = '', params = {}) => (await sequelize.query(sql, { logging: false, raw: true, replacements: params }))[0]
const manager = new SessionManager({ port: ws_port }, QueryWrap, { verbose: true })
info(`Created SessionManager on port`, ws_port)
//init
// const sec = Secure({})
//note that these key and iv are here for demonstration purposes and you should always generate your own. You may also supply any cipher algorithm supported by node's crypto module
const ss = new SocioSecurity({ secure_private_key: 'skk#$U#Y$7643GJHKGDHJH#$K#$HLI#H$KBKDBDFKU34534', cipher_iv: 'dsjkfh45h4lu45ilULIY$%IUfdjg', verbose:true })
const manager = new SessionManager({ port: ws_port }, QueryWrap, { verbose: true, secure:ss })
done(`Created SessionManager on port`, ws_port)

View file

@ -13,7 +13,7 @@
let ws = null
let clienID = false
const static_queries = [{text:'once:', sql:'SELECT 42+69 AS RESULT;'}, {text:'once:', sql:'SELECT COUNT(*) AS RESULT FROM users;'}]
const static_queries = [{text:'once:', sql:"SELECT 42+69 AS RESULT;--socio"}, {text:'once:', sql:"SELECT COUNT(*) AS RESULT FROM users;--socio"}]
let users = [], bob_count=0;
const insert_fields = {name:'Bob', num:420}
@ -22,11 +22,11 @@
await ws.ready()
clienID = ws.ses_id
ws.subscribe({ sql: 'SELECT COUNT(*) AS RESULT FROM users WHERE name = :name;', params: { name: 'Bob' } }, (res) => {
ws.subscribe({ sql: "SELECT COUNT(*) AS RESULT FROM users WHERE name = :name;--socio", params: { name: 'Bob' } }, (res) => {
bob_count = res[0].RESULT //res is whatever object your particular DB interface lib returns from a raw query
})
ws.subscribe({ sql: 'SELECT * FROM users;'}, (res) => {
ws.subscribe({ sql: "SELECT * FROM users;--socio"}, (res) => {
users = res //res is whatever object your particular DB interface lib returns from a raw query
})
})
@ -34,7 +34,7 @@
<Nav></Nav>
<main>
<h1>Socio framework use demonstration - Svelte</h1>
<h1>Socio framework secured use demonstration - Svelte</h1>
{#if clienID}
<div class="horiz">
<h2 id="ready" class="status">Ready.</h2>
@ -53,14 +53,14 @@
{/each}
<div class="horiz">
<Button on:click={async () => await ws.query('INSERT INTO users VALUES(:name, :num);', insert_fields)} bind:name={insert_fields.name} bind:num={insert_fields.num}></Button>
<Button on:click={async () => await ws.query("INSERT INTO users VALUES(:name, :num);--socio", insert_fields)} bind:name={insert_fields.name} bind:num={insert_fields.num}></Button>
<input type="text" bind:value={insert_fields.name}>
<input type="number" bind:value={insert_fields.num}>
</div>
<h2 class="row horiz">
<h3>subscribed:</h3>
<Code >SELECT COUNT(*) AS RESULT FROM users WHERE name = :name; && :name = 'Bob'</Code>
<Code>SELECT COUNT(*) AS RESULT FROM users WHERE name = :name; && :name = 'Bob'</Code>
=
<span class="num grad_clip">{bob_count}</span>
</h2>
@ -69,21 +69,23 @@
{/if}
</main>
<section>
<div class="users">
<div class="horiz"><h3>subscribed:</h3> <Code>SELECT * AS RESULT FROM users;</Code>=</div>
<h2 class="grad_clip">{'{'}</h2>
{#each users as u}
<div class="horiz user_row" transition:slide>
<h2>name: <span class="grad_clip">{u.name}</span></h2>
<h2>num: <span class="grad_clip">{u.num}</span></h2>
</div>
{/each}
<h2 class="grad_clip">{'}'}</h2>
</div>
{#if users}
<section>
<div class="users">
<div class="horiz"><h3>subscribed:</h3> <Code>SELECT * AS RESULT FROM users;</Code>=</div>
<h2 class="grad_clip">{'{'}</h2>
{#each users as u}
<div class="horiz user_row" transition:slide>
<h2>name: <span class="grad_clip">{u.name}</span></h2>
<h2>num: <span class="grad_clip">{u.num}</span></h2>
</div>
{/each}
<h2 class="grad_clip">{'}'}</h2>
</div>
<h3>Check the dev console for verbose logs and the network panel for websocket connection messages ;)</h3>
</section>
<h3>Check the dev console for verbose logs and the network panel for websocket connection messages ;)</h3>
</section>
{/if}
<style lang="css">
main {

View file

@ -6,3 +6,4 @@ const app = new App({
})
export default app

View file

@ -1,7 +1,14 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import { SocioSecurityPlugin } from '../../core/secure.js'
// import { SocioSecurityPlugin } from 'socio/secure.js'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte()]
plugins: [
svelte(),
//note that these key and iv are here for demonstration purposes and you should always generate your own. You may also supply any cipher algorithm supported by node's crypto module
SocioSecurityPlugin({ secure_private_key: 'skk#$U#Y$7643GJHKGDHJH#$K#$HLI#H$KBKDBDFKU34534', cipher_iv: 'dsjkfh45h4lu45ilULIY$%IUfdjg', verbose: true })
]
})