UNPKG

captcha-igid

Version:
568 lines (485 loc) 16.9 kB
// TESTING ONLY const fs = require('fs') // const apiKeys = require(process.cwd() + '/local/api_keys') // // // TESTING ONLY const DB_STASH_INTERVAL = 10000 class BigFauxTest { // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- // constructor(persistence,relays) { this.storage_map = {} this.dirty = false this.root_path = process.cwd() } app_shutdown() { fs.writeFileSync(this.db_file,JSON.stringify(this.storage_map)) } initialize(conf) { this.db_file = this.root_path + '/' + (conf.db_file ? conf.db_file : 'userdata.db') try { this.storage_map = JSON.parse(fs.readFileSync(this.db_file,'ascii').toString()) } catch(e) { if ( e.code !== "ENOENT" ) { console.dir(e) process.exit(1) } } let extant_interval = setInterval(() => { if ( this.dirty ) { fs.writeFile(this.db_file,JSON.stringify(this.storage_map),() => { this.dirty = false }) } },DB_STASH_INTERVAL) //this.add_interval(extant_interval) } create(obj,cb) { if ( obj._id && this.storage_map[obj._id] ) { if ( cb ) cb(new Error("already exists"),null) } if ( !obj._id ) { obj._id = uuid() } this.storage_map[obj._id] = obj if ( cb ) cb(null) } update(obj,cb) { if ( !(obj._id) || !(this.storage_map[obj._id]) ) { if ( cb ) cb(new Error("does not exists"),null) } this.storage_map[obj._id] = obj this.dirty = true if ( cb ) cb(null) } delete(id,cb) { if ( !(this.storage_map[id]) ) { if ( cb ) cb(new Error("does not exists"),null) } delete this.storage_map[id] this.dirty = true if ( cb ) cb(null) } findOne(id,cb) { let obj = this.storage_map[id] if ( !( obj ) ) { if ( cb ) cb(new Error("does not exists"),null) return(false) } if ( cb ) cb(null,obj) else return(obj) return(true) } all_keys() { return(Object.keys(this.storage_map)) } get(id) { let obj = this.storage_map[id] return obj } set(id,value) { this.storage_map[id] = value } hash_set(key,value) { this.storage_map[key] = value } } const g_persistence = new BigFauxTest(apiKeys.persistence,apiKeys.message_relays) // these keys live as long as a session and no longer.. const g_ephemeral = new BigFauxTest(apiKeys.session,apiKeys.session_relay) const SLOW_MESSAGE_QUERY_INTERVAL = 5000 const FAST_MESSAGE_QUERY_INTERVAL = 1000 const WRITE_OBJECT_MAP_EVERY_INTERVAL = 1000*60*15 // 15 minutes const WRITE_UNUSED_LARGE_ENTRIES_EVERY_INTERVAL = 1000*60*60 // ones an hour // const g_keyValueDB = g_persistence; // leave it to the module to figure out how to connect const g_keyValueSessions = g_ephemeral // .get_LRUManager(); // // const DEFAULT_STATIC_SYNC_INTERVAL = 10000 class GeneralDBWrapperImpl { // constructor(keyValueDB,sessionKeyValueDB,persistentDB,staticDB) { // //super() // // EPHEMERAL (in memory will be erased when the application turns off.. BUT -- may be replicated on nodes with checkpoint writes) this.key_value_db = keyValueDB // MEMORY BASED (SHARED MEM) but with with likely backup (size less determined) this.session_key_value_db = !(sessionKeyValueDB) ? keyValueDB : sessionKeyValueDB // MEMORY BASED (SHARED MEM) size fixed (all back up is elastic and ephemeral) // // CACHEABLE (WILL WRITE TO DISK SOMEWHERE) if ( !(persistentDB) ) { // DISK within LOCAL and PEER NODES this.pdb = require('./default_persistent_db') } else { this.pdb = persistentDB } // if ( !(staticDB) ) { // LOCAL ON DISK (LOCAL CACHE AND STARTUP CACHE) this.sdb = require('./default_persistent_db') } else { this.sdb = staticDB } if ( this.sdb && (typeof this.sdb.setPersistence === 'function ') ) { this.sdb.setPersistence(this.pdb) } if ( this.key_value_db && (typeof this.key_value_db.setPersistence === 'function ') ) { this.key_value_db.setPersistence(this.pdb) } } // configuration for persistent DB... initialize(conf) { this.key_value_db.initialize(conf) if ( this.session_key_value_db !== this.key_value_db ) { this.session_key_value_db.initialize(conf) } this.pdb.initialize(conf) // initialize persitence...relative to the database this.sdb.initialize(conf) this.static_sync_interval = conf.static_sync ? conf.static_sync : DEFAULT_STATIC_SYNC_INTERVAL } // KEY VALUE cache like DB in memory. e.g. an interface to shm-lru-cache set_key_value(key,value) { this.key_value_db.set(key,value) } // del_key_value(token) { this.key_value_db.delete(token) } // async get_key_value(key) { try { let value = await this.key_value_db.get(key) return value } catch (e) { console.log(e) return null } } // --- SESSION --- set_session_key_value(key,value) { return this.session_key_value_db.hash_set(key,value) } // del_session_key_value(key) { this.session_key_value_db.delete(key) } // get_session_key_value(key) { try { let value = this.session_key_value_db.get(key) return value } catch (e) { console.log(e) return null } } // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- // fetch_fields(fields_key) { // let fields = {} if ( fields_key ) { fields = JSON.parse(this.fetch(fields_key)) } return(fields) } // fetch(key) { let data = "" return(data) } // // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- async static_store(asset) { // for small size objects depending on the initialization parameters of the cache DB let data = "" if ( asset ) { try { data = await this.sdb.get_key_value(asset) if ( data ) { if ( typeof data === "string" ) { return(JSON.parse(data)) } else { return(data) } } } catch(e) { } } return(data) } // wrap the object in a carrier, in which the object is serialized with its mime-type for future transport.. // put the object in the static db, which attempts to keep a copy of the asset close to the processor on its own local disks... put_static_store(whokey,text,mime_type,extension) { if ( (typeof text) !== 'string' ) { text = JSON.stringify(text) } if ( this.sdb === undefined ) return let data = { 'string' : text, 'mime_type' : mime_type } if ( extension && ( typeof extension === 'object' ) ) { // don't overwrite the fields this storage is about. if ( extension.string !== undefined ) delete extension.string if ( extension.mime_type !== undefined ) delete extension.mime_type data = Object.assign(data,extension) } this.sdb.set_key_value(whokey,data) } del_static_store(whokey) { if ( this.sdb === undefined ) return this.sdb.del_key_value(whokey) } static_synchronizer(sync_function) { if ( this.sdb === undefined ) return if ( this.sdb.schedule === undefined ) return this.sdb.schedule(sync_function,this.static_sync_interval) } // // ---- ---- ---- ---- ---- ---- ---- ---- ---- // May use a backref store_cache(key,data,back_ref) { // make use of the application chosen key-value implementation... // let ref = back_ref ? data[back_ref] : false; if ( ref ) { const hash = crypto.createHash('sha256'); hash.update(key); let ehash = hash.digest('hex'); // this.set_key_value(ref,ehash) this.set_key_value(ehash,data) } else { // this.set_key_value(key,data) // } } update_cache(key,data,back_ref) { let ref = back_ref ? data[back_ref] : false; if ( ref ) { let ehash = this.get_key_value(ref) if ( ehash !== false ) { this.set_key_value(ehash,JSON.stringify(data)) } } else { // this.set_key_value(key,JSON.stringify(data)) // } } async cache_stored(key,body) { const hash = crypto.createHash('sha256'); hash.update(key); let ehash = hash.digest('hex'); try { let data = this.get_key_value(ehash) if ( data ) { if ( typeof data === "string" ) { return(JSON.parse(data)) } else { return(data) } } else return body } catch(e) { return body } } // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- // BASIC USER // Some applications may elect to store user in the KV store. // That is not the default behavior. The default behavior is to store the user in the persistence DB. async fetch_user_from_key_value_store(key) { if ( key == undefined ) return(false); try { let ehash = await this.get_key_value(key) if ( ehash === null || ehash === false ) { return(false) } if ( isHex(ehash) ) { try { let u_data = await this.get_key_value(ehash) if ( u_data ) { if ( typeof u_data === "string" ) { return(JSON.parse(u_data)) } else { return(u_data) } } return false } catch (e) { return false } } else { try { let u_data = ehash if ( typeof u_data === "string" ) { return(JSON.parse(u_data)) } else { return(u_data) } } catch (e) { return false } } } catch(e) { return false } } id_hashing(user_txt) { let id = global_hasher(user_txt) // or this.oracular_storage() return id } // store_user // // just work with the presistent DB async store_user(udata,key,cb) { // up to the application...(?) let id = '' if ( key ) { id = udata[key] } else { id = udata.id if ( id === undefined ) { let user_txt = JSON.stringify(udata) id = await this.id_hashing(user_txt) // or this.oracular_storage() } } udata._id = id udata._tx_directory_ensurance = true // for the endpoint -- might not need it // m_path is user let result = await this.pdb.update(udata,false,'create') // the end point will write information into files and create directories... // a response service will react sending new information to subscribers... if ( cb !== undefined ) cb(result) return(id) } // fetch_user(id,cb) { // up to the application...(?) // just work with the presistent DB return(this.pdb.findOne(id,cb)) } // async update_user(udata,key,cb) { // just work with the presistent DB let id = '' if ( key ) { id = udata[key] } else { id = udata.id } if ( udata._id == undefined ) { udata._id = id } udata._tx_directory_ensurance = true // m_path is user let result = await this.pdb.update(udata,false,'update') // the end point will write information into files and create directories... if ( cb !== undefined ) cb(result) } // BASIC USER (end) // ---- ---- ---- ---- ---- ---- ---- ---- ---- // // in subclass // store(collection,data) {} // exists(collection,query_components) { return(false) } // last_step_initalization() {} // drop() {} // disconnect() { console.log("implement in instance") return(false) } } // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- // // store_cache -- in DBClass --> set_key_value --> (M)g_keyValueDB. ..._LRUManager class CaptchaDBClass extends GeneralDBWrapperImpl { // constructor() { // pass app messages to the backend // from the default copious-transitions == CustomPersistenceDB, CustomStaticDB let stash_interval = WRITE_OBJECT_MAP_EVERY_INTERVAL // initialize the persistence interface with the same communication as the CategoricalUserManager let persistenceDB = new BigFauxTest(g_persistence.message_fowarding,stash_interval,'user') // static will provide basic components if not obtained from nxinx stash_interval = WRITE_UNUSED_LARGE_ENTRIES_EVERY_INTERVAL let staticDB = new BigFauxTest(g_persistence.message_fowarding,stash_interval,'user','igid') // // The key value DB is an LRU DB (e.g. such as Redis) that takes on large values of less predictable size. // super(g_keyValueDB,g_keyValueSessions,persistenceDB,staticDB) this.derivation_keys = {} } // // FileAndRelays will use the MessageRelayer from global_persistence to send the user data to the backend... // // apiKeys = require(process.cwd() + '/local/api_keys') configures it... // // The relayer may talk to a relay service or an endpoint.... (for captcha, it will be user endpoint...) // // the end points are categorical handlers that are tied to message pathways... in this case a 'user' pathway.. // // (see path_handlers.js) // // // store_user(fdata,all) { if ( G_users_trns.tagged('user') ) { let [udata,axioms] = G_users_trns.update(fdata) // custom user storage (seconday service) clean up fields // let key_key = G_users_trns.kv_store_key() // application key for key-value store from data object let key = udata[key_key] if ( all ) { let ucwid = fdata.ucwid this.derivation_keys[ucwid] = axioms } // store the user in just the LRU this.store_cache(key,udata,G_users_trns.back_ref()); // this app will use cache to echo persitent storage //return relationship_id } } // // // async fetch_user(fdata,all) { if ( G_users_trns.from_cache() ) { let udata = await this.fetch_user_from_key_value_store(fdata[G_users_trns.kv_store_key()]) if ( udata ) { if ( all && this.derivation_keys[udata.ucwid] ) { let axioms = this.derivation_keys[udata.ucwid] udata.public_derivation = axioms.public_derivation udata.signer_public_key = axioms.signer_public_key } return(udata) } } return(false) // never saw this user } update_user(udata) { // let key_key = G_users_trns.kv_store_key() let key = udata[key_key] // this.store_cache(key,udata,G_users_trns.back_ref()); // this app will use cache to echo persitent storage super.update_user(udata,key_key) // use persitent storage (change the remotely stored disk record + local cache (static)) } // exists // // a bit more severe than fetch... will fail by default when going to persistent storage async exists(collection,post_body) { let query = post_body if ( G_users_trns.tagged(collection) ) { if ( G_users_trns.from_cache() ) { // try to find the user in value storage (local LRU or in-house horizontally scaled LRU) let udata = await this.fetch_user_from_key_value_store(post_body[G_users_trns.kv_store_key()]) if ( udata ) { return(true) } } query = G_users_trns.existence_query(post_body) } // failing a local lookup go to the persistant big database in the sky or nothing return(super.exists(collection,query)) } // disconnect() { return new Promise((resolve,reject) => { g_persistence.client_going_down() if ( g_keyValueDB.disconnect(true) ) { resolve(true) } else { reject(false) } }) } } // // module.exports = new CaptchaDBClass()