mirror of
https://github.com/Rolands-Laucis/Socio.git
synced 2026-05-15 14:15:57 -06:00
many updates
This commit is contained in:
parent
fc12f68151
commit
a941eba154
9 changed files with 226 additions and 92 deletions
|
|
@ -1,44 +1,69 @@
|
|||
//https://stackoverflow.com/questions/38946112/es6-import-error-handling
|
||||
let info = null
|
||||
try { //for my logger
|
||||
await import('@rolands/log')
|
||||
setPrefix('Socio Client')
|
||||
setShowTime(false)
|
||||
} catch (e) {
|
||||
info = (...objs) => console.log('[Socio Client]', ...objs)
|
||||
}
|
||||
|
||||
//"Because he not only wants to perform well, he wants to be well received — and the latter lies outside his control." /Epictetus/
|
||||
|
||||
export class WSClient {
|
||||
constructor(url, {verbose=false} = {}) {
|
||||
this.ws = new WebSocket(url)
|
||||
if(window || undefined && url.startsWith('ws://'))
|
||||
console.log('UNSECURE WEBSOCKET URL CONNECTION! Please use wss:// protocol to protect against man-in-the-middle attacks.')
|
||||
// this.ws.addEventListener("close", () => {this.ws = new WebSocket(url)}); // <- rise from your grave!
|
||||
// private:
|
||||
#queries = {} //sql:[callback]
|
||||
#is_ready = false
|
||||
#verbose = false
|
||||
#ws=null
|
||||
|
||||
this.queries = {} //sql:[callback]
|
||||
this.is_ready = false
|
||||
this.verbose = verbose
|
||||
constructor(url, {name = '', verbose=false, keep_alive=true, reconnect_tries=3, 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.')
|
||||
|
||||
this.ws.addEventListener('message', this.message.bind(this));
|
||||
// this.ws.addEventListener('pong', () => { console.log('pong')});
|
||||
this.name = name
|
||||
this.#verbose = verbose
|
||||
this.push = push_callback
|
||||
this.#connect(url, keep_alive, verbose, reconnect_tries)
|
||||
|
||||
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(this.name, `WebSocket closed. Retrying...`);
|
||||
this.#connect(url, keep_alive, verbose, reconnect_tries - 1)
|
||||
}); // <- rise from your grave!
|
||||
}
|
||||
|
||||
message(e) {
|
||||
const [kind, data] = JSON.parse(e.data)
|
||||
if (this.verbose) console.log(kind, data)
|
||||
if (this.#verbose) info(kind, data)
|
||||
|
||||
switch(kind){
|
||||
case 'CON': this.ses_id = data; this.is_ready = true; break;
|
||||
case 'CON': this.ses_id = data; this.#is_ready = true; if (this.#verbose) info(this.name, `WebSocket connected.`); break;
|
||||
case 'UPD':
|
||||
if (data.sql in this.queries)
|
||||
this.queries[data.sql].forEach(f => f(data.result));
|
||||
else console.log(`UPD message for unregistered SQL query! [${data.sql}] with data:`, data)
|
||||
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)
|
||||
break;
|
||||
case 'SQL':
|
||||
if (data.sql in this.queries)
|
||||
this.queries[data.sql](data.result);
|
||||
if (data.sql in this.#queries)
|
||||
this.#queries[data.sql](data.result);
|
||||
break;
|
||||
case 'PONG': if (this.#verbose) info('pong', data?.num); break;
|
||||
case 'PUSH': this.push(data); break;
|
||||
// case '': break;
|
||||
default: console.log(`Unrecognized message kind! [${kind}] with data:`, data);
|
||||
default: info(`Unrecognized message kind! [${kind}] with data:`, data);
|
||||
}
|
||||
}
|
||||
|
||||
ready(){
|
||||
//idk a better solution. Checks every n ms if the ready flag is set
|
||||
return new Promise((res) => {
|
||||
const timer = setInterval(() => {
|
||||
if(this.is_ready){
|
||||
if(this.#is_ready){
|
||||
clearInterval(timer)
|
||||
res()
|
||||
}
|
||||
|
|
@ -46,23 +71,32 @@ export class WSClient {
|
|||
})
|
||||
}
|
||||
|
||||
//private method
|
||||
#send(data=[]){
|
||||
this.#ws.send(JSON.stringify([this.ses_id, ...data]))
|
||||
}
|
||||
|
||||
subscribe({ sql = '', params = null } = {}, callback = null, t=null){
|
||||
if (sql in this.queries)
|
||||
this.queries[sql].push(t ? callback.bind(t) : callback)
|
||||
if (sql in this.#queries)
|
||||
this.#queries[sql].push(t ? callback.bind(t) : callback)
|
||||
else{
|
||||
this.queries[sql] = [t ? callback.bind(t) : callback]
|
||||
this.ws.send(JSON.stringify([this.ses_id, 'REG', { sql: sql, params: params }]))
|
||||
this.#queries[sql] = [t ? callback.bind(t) : callback]
|
||||
this.#send(['REG', { sql: sql, params: params }])
|
||||
}
|
||||
// console.log('Registered', sql)
|
||||
// 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 prom = new Promise((res) => {
|
||||
this.queries[sql] = res
|
||||
this.#queries[sql] = res
|
||||
})
|
||||
//send off the request, which will be resolved in the message handler
|
||||
this.ws.send(JSON.stringify([this.ses_id, 'SQL', { sql: sql, params:params }]))
|
||||
this.#send(['SQL', { sql: sql, params: params }])
|
||||
return await prom
|
||||
}
|
||||
|
||||
ping(num=0){
|
||||
this.#send(['PING', { num: num }])
|
||||
}
|
||||
}
|
||||
79
core/core.js
79
core/core.js
|
|
@ -1,57 +1,65 @@
|
|||
//Nullum magnum ingenium sine mixture dementia fuit. - There has been no great wisdom without an element of madness.
|
||||
|
||||
import { log, error, info, setPrefix} from '@rolands/log'
|
||||
import { UUID, Secure } from './secure.js';
|
||||
import { log, error, soft_error, info, setPrefix, setShowTime} from '@rolands/log'
|
||||
import { UUID } from './secure.js';
|
||||
import { WebSocketServer } from 'ws'; //https://github.com/websockets/ws https://github.com/websockets/ws/blob/master/doc/ws.md
|
||||
//https://stackoverflow.com/questions/16280747/sending-message-to-a-specific-connected-users-using-websocket
|
||||
|
||||
export class SessionManager{
|
||||
constructor(opts = {}, DB_query_callback = null, { verbose = false } = {}){
|
||||
setPrefix('Socio') //for my logger
|
||||
// private:
|
||||
#wss=null
|
||||
#sessions = {}//ses_id:websocket
|
||||
#verbose=false
|
||||
#secure=null
|
||||
#lifecycleHooks = { CON: [], DISCON: [], message: [], update: [] } //, '': []
|
||||
|
||||
const 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.sessions = {} //ses_id:websocket
|
||||
constructor(opts = {}, DB_query_callback = 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.#verbose = verbose
|
||||
this.#secure = secure
|
||||
|
||||
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
|
||||
wss.on('close', (...stuff) => { info('WebSocketServer close event', ...stuff) });
|
||||
wss.on('error', (...stuff) => { error('WebSocketServer error event', ...stuff)});
|
||||
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)
|
||||
this.#sessions[ses_id] = new Session(ses_id, conn)
|
||||
conn.send(JSON.stringify(['CON', ses_id]));
|
||||
log('CON', ses_id)
|
||||
if (this.#verbose) info('CON', ses_id)
|
||||
|
||||
conn.on('message', this.Message.bind(this));
|
||||
conn.on('close', () => {
|
||||
log('DISCON', ses_id)
|
||||
delete this.sessions[ses_id]
|
||||
if (this.#verbose) info('DISCON', ses_id)
|
||||
delete this.#sessions[ses_id]
|
||||
});
|
||||
}
|
||||
|
||||
async Message(req, head){
|
||||
const [ses_id, kind, data] = JSON.parse(req.toString())
|
||||
if (this.verbose) log(`received [${kind}] from [${ses_id}]: `, data);
|
||||
if (this.#verbose) info(`received [${kind}] from [${ses_id}]`);
|
||||
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
|
||||
|
||||
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(ses_id in this.#sessions)
|
||||
this.#sessions[ses_id].Send(['UPD', { sql: data.sql, 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[ses_id].RegisterHook(t, data.sql, data.params));
|
||||
|
||||
break;
|
||||
case 'SQL':
|
||||
const is_select = QueryUtils.QueryIsSelect(data.sql)
|
||||
if (ses_id in this.sessions){
|
||||
if (ses_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[ses_id].Send(['SQL', { sql: data.sql, result: await res }])
|
||||
}
|
||||
|
||||
//if the sql wasnt a SELECT, but altered some resource, then need to propogate that to other connection hooks
|
||||
|
|
@ -59,14 +67,17 @@ export class SessionManager{
|
|||
this.Update(QueryUtils.ParseSQLForTables(data.sql))
|
||||
|
||||
break;
|
||||
case 'PING': this.#sessions[ses_id].Send(['PONG', { num: data?.num}]); break;
|
||||
// case '': break;
|
||||
default: log(`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=[]){
|
||||
Object.values(this.sessions).forEach(async (s) => {
|
||||
// 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]){
|
||||
|
|
@ -75,6 +86,24 @@ export class SessionManager{
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
//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!`)
|
||||
}
|
||||
|
||||
Emit(data=[]){
|
||||
this.#wss.emit(JSON.stringify(['EMIT', ...data]));
|
||||
}
|
||||
|
||||
RegisterLifecycleHook(name='', callback){
|
||||
if(name in this.#lifecycleHooks)
|
||||
this.#lifecycleHooks.push(callback)
|
||||
else
|
||||
error(`Lifecycle hook [${name}] does not exist!`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -91,10 +120,10 @@ class Session{
|
|||
this.hooks[table].push({ sql: sql, params: params })
|
||||
else
|
||||
this.hooks[table] = [{ sql:sql, params:params}]
|
||||
log('reg hook', table, this.hooks[table])
|
||||
// log('reg hook', table, this.hooks[table])
|
||||
}
|
||||
|
||||
Send(data){
|
||||
Send(data=[]){
|
||||
this.ws.send(JSON.stringify(data))
|
||||
}
|
||||
}
|
||||
|
|
@ -107,7 +136,7 @@ class QueryUtils{
|
|||
|
||||
static ParseSQLForTables(sql = '') {
|
||||
return sql
|
||||
.match(/(?:FROM|INTO)[\s\n\t](?<tables>[\w,\s\n\t]+)[\s\n\t]?(?:WHERE|VALUES|;|LIMIT|GROUP|ORDER)/mi)
|
||||
.match(/(?:FROM|INTO)[\s\n\t](?<tables>[\w,\s\n\t]+?)[\s\n\t]?(?:WHERE|VALUES|;|LIMIT|GROUP|ORDER)/mi)
|
||||
?.groups?.tables
|
||||
.split(/,[\s\n\t\r]*/mig)
|
||||
.map((t) => t.split(/[\s\n\t\r]/mi)[0].trim()) || []
|
||||
|
|
|
|||
18
core/package-lock.json
generated
18
core/package-lock.json
generated
|
|
@ -1,22 +1,22 @@
|
|||
{
|
||||
"name": "wsdb",
|
||||
"name": "socio",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wsdb",
|
||||
"name": "socio",
|
||||
"version": "0.0.1",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@rolands/log": "^1.0.1",
|
||||
"@rolands/log": "^1.1.1",
|
||||
"ws": "^8.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolands/log": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rolands/log/-/log-1.0.1.tgz",
|
||||
"integrity": "sha512-VlQJG4UG+pQpJjGERcGlqBJm+s8z+TfbEKq9cbVgWxfIvFWD/TiQ+0sPQ9dOhH6O8/Ew7ktPZlfbIcU4oGe2ow=="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@rolands/log/-/log-1.1.1.tgz",
|
||||
"integrity": "sha512-j8jZxm0tF+7ke6V+qtdoCR2KlOba3Mog39QyxU+3pbmgJ/wuIfF7QuIqTSLqrtPQY5FzQjCwlJcvIizp33UYxg=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.9.0",
|
||||
|
|
@ -41,9 +41,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@rolands/log": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rolands/log/-/log-1.0.1.tgz",
|
||||
"integrity": "sha512-VlQJG4UG+pQpJjGERcGlqBJm+s8z+TfbEKq9cbVgWxfIvFWD/TiQ+0sPQ9dOhH6O8/Ew7ktPZlfbIcU4oGe2ow=="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@rolands/log/-/log-1.1.1.tgz",
|
||||
"integrity": "sha512-j8jZxm0tF+7ke6V+qtdoCR2KlOba3Mog39QyxU+3pbmgJ/wuIfF7QuIqTSLqrtPQY5FzQjCwlJcvIizp33UYxg=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.9.0",
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/Rolands-Laucis/Socio.js/blob/master/README.md",
|
||||
"dependencies": {
|
||||
"@rolands/log": "^1.0.1",
|
||||
"@rolands/log": "^1.1.1",
|
||||
"ws": "^8.9.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,24 @@
|
|||
import { randomUUID, createCipheriv, createDecipheriv, randomBytes } from 'crypto'
|
||||
import { randomUUID, createCipheriv, createDecipheriv } from 'crypto'
|
||||
|
||||
//The aim of the wise is not to secure pleasure, but to avoid pain. /Aristotle/
|
||||
export class SocioSecurity{
|
||||
//private:
|
||||
#key=''
|
||||
#algo=''
|
||||
#iv=''
|
||||
|
||||
export class Secure{
|
||||
constructor({ secure_private_key = '', cipther_algorithm = 'AES-256-ctr', cipher_iv =''} = {}){
|
||||
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!`
|
||||
|
||||
const te = new TextEncoder()
|
||||
|
||||
this.key = te.encode(secure_private_key).slice(0,32)
|
||||
this.algo = cipther_algorithm
|
||||
this.iv = te.encode(cipher_iv).slice(0, 16)
|
||||
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
|
||||
}
|
||||
|
||||
//sql strings must be in single quotes and have an sql single line comment at the end with the name socio - "--socio"
|
||||
|
|
@ -22,13 +31,12 @@ export class Secure{
|
|||
}
|
||||
|
||||
EncryptString(query = '') {
|
||||
const cipher = createCipheriv(this.algo, Buffer.from(this.key), this.iv)
|
||||
return (cipher.update(query, 'utf-8', 'base64') + cipher.final('base64')).replace(/\\/g, '\\\\') //escape backslashes to avoid escaping in the js string when this is put back into the souce code
|
||||
const cipher = createCipheriv(this.#algo, Buffer.from(this.#key), this.#iv)
|
||||
return (cipher.update(query, 'utf-8', 'base64') + cipher.final('base64')) //Base64 only contains A–Z , a–z , 0–9 , + , / and =
|
||||
}
|
||||
|
||||
DecryptString(query = '') {
|
||||
const decipther = createDecipheriv(this.algo, Buffer.from(this.key), this.iv)
|
||||
query = query.replace(/\\\\/g, '\\') //remove the escaped backslashes to just backslashes
|
||||
const decipther = createDecipheriv(this.#algo, Buffer.from(this.#key), this.#iv)
|
||||
return decipther.update(query, 'base64', 'utf-8') + decipther.final('utf-8')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Totum demo page</title>
|
||||
<title>Socio demonstration - basic</title>
|
||||
|
||||
<!-- font -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&family=Roboto+Mono:ital,wght@0,300;1,300&display=swap"
|
||||
href="https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@200;400;700&family=Trispace:wght@100;200&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
<script src="./client.js" type="module"></script>
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
background-color: #16161b;
|
||||
color: white;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-family: 'Josefin Sans', 'Open Sans', sans-serif;
|
||||
}
|
||||
main{
|
||||
height: 100%;
|
||||
|
|
@ -49,20 +49,23 @@
|
|||
filter: var(--shad);
|
||||
}
|
||||
h1{
|
||||
font-size: 37.5px;
|
||||
font-size: 48.83px;
|
||||
font-weight: 700;
|
||||
}
|
||||
h3{
|
||||
font-size: 15.36px;
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
color: #4d4d52;
|
||||
}
|
||||
h2{
|
||||
font-size: 24px;
|
||||
font-size: 31.25px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
h3{
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
font-style: italic;
|
||||
color: #5a5a5f;
|
||||
}
|
||||
a{
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
h1, h2{
|
||||
display: flex;
|
||||
|
|
@ -74,8 +77,8 @@
|
|||
.status{align-items: baseline;}
|
||||
|
||||
.code{
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-weight: 300;
|
||||
font-family: 'Trispace', monospace;
|
||||
font-weight: 100;
|
||||
font-size: 19.20px;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
|
|
@ -84,7 +87,7 @@
|
|||
}
|
||||
.num{
|
||||
font-weight: 700;
|
||||
font-size: 37.5px;
|
||||
font-size: 48.83px;
|
||||
}
|
||||
button{
|
||||
font-weight: 700;
|
||||
|
|
@ -111,15 +114,15 @@
|
|||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-position: 50% 0px;
|
||||
background-size: 150%;
|
||||
background-position: 30% 0px;
|
||||
background-size: 130%;
|
||||
transition: var(--trans);
|
||||
}
|
||||
.grad_clip:hover{
|
||||
background-position: 0px 0px;
|
||||
}
|
||||
|
||||
.status.ready{color: #5fe757;}
|
||||
/* .status.ready{color: #5fe757;} */
|
||||
</style>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
60
demos/basic/package-lock.json
generated
60
demos/basic/package-lock.json
generated
|
|
@ -1,16 +1,18 @@
|
|||
{
|
||||
"name": "demo",
|
||||
"name": "basic",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "demo",
|
||||
"name": "basic",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@rolands/log": "^1.1.0",
|
||||
"express": "^4.18.2",
|
||||
"sequelize": "^6.25.3",
|
||||
"socio": "^0.0.1",
|
||||
"sqlite3": "^5.1.2"
|
||||
}
|
||||
},
|
||||
|
|
@ -62,6 +64,11 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolands/log": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@rolands/log/-/log-1.1.1.tgz",
|
||||
"integrity": "sha512-j8jZxm0tF+7ke6V+qtdoCR2KlOba3Mog39QyxU+3pbmgJ/wuIfF7QuIqTSLqrtPQY5FzQjCwlJcvIizp33UYxg=="
|
||||
},
|
||||
"node_modules/@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
|
|
@ -1635,6 +1642,15 @@
|
|||
"npm": ">= 3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socio": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/socio/-/socio-0.0.1.tgz",
|
||||
"integrity": "sha512-YjOk596DV092CL1mIsS3moKUQ0W7Ge4RM5L2NT/PbimcuuWYQy3ZcjIlaAIMIcxlwA7Dm9nwyXD26wadKvNrWA==",
|
||||
"dependencies": {
|
||||
"@rolands/log": "^1.0.1",
|
||||
"ws": "^8.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socks": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
|
||||
|
|
@ -1919,6 +1935,26 @@
|
|||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz",
|
||||
"integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
|
@ -1968,6 +2004,11 @@
|
|||
"rimraf": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"@rolands/log": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@rolands/log/-/log-1.1.1.tgz",
|
||||
"integrity": "sha512-j8jZxm0tF+7ke6V+qtdoCR2KlOba3Mog39QyxU+3pbmgJ/wuIfF7QuIqTSLqrtPQY5FzQjCwlJcvIizp33UYxg=="
|
||||
},
|
||||
"@tootallnate/once": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||
|
|
@ -3148,6 +3189,15 @@
|
|||
"integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
|
||||
"optional": true
|
||||
},
|
||||
"socio": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/socio/-/socio-0.0.1.tgz",
|
||||
"integrity": "sha512-YjOk596DV092CL1mIsS3moKUQ0W7Ge4RM5L2NT/PbimcuuWYQy3ZcjIlaAIMIcxlwA7Dm9nwyXD26wadKvNrWA==",
|
||||
"requires": {
|
||||
"@rolands/log": "^1.0.1",
|
||||
"ws": "^8.9.0"
|
||||
}
|
||||
},
|
||||
"socks": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
|
||||
|
|
@ -3366,6 +3416,12 @@
|
|||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.10.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz",
|
||||
"integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==",
|
||||
"requires": {}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@
|
|||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@rolands/log": "^1.1.1",
|
||||
"express": "^4.18.2",
|
||||
"sequelize": "^6.25.3",
|
||||
"socio": "^0.0.1",
|
||||
"sqlite3": "^5.1.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import express from 'express'
|
||||
import {SessionManager} from '../../core/core.js'
|
||||
// import {SessionManager} from 'socio'
|
||||
import { Sequelize } from 'sequelize';
|
||||
import { log, info, 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
|
||||
|
|
@ -16,7 +18,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} )
|
||||
console.log('Created SessionManager on port', ws_port)
|
||||
info(`Created SessionManager on port`, ws_port)
|
||||
|
||||
//init
|
||||
// const sec = Secure({})
|
||||
|
|
@ -30,5 +32,5 @@ app.use("", express.static(__dirname));
|
|||
app.use("/", express.static(__dirname + "\\..\\..\\core"));
|
||||
|
||||
app.listen(server_port, () => {
|
||||
console.log(`Express webserver listening on port ${server_port}`, `http://localhost:${server_port}/`)
|
||||
info(`Express webserver listening on port`, server_port, `http://localhost:${server_port}/`)
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue