diff options
Diffstat (limited to 'app/server/db')
| -rw-r--r-- | app/server/db/bookshelf.js | 24 | ||||
| -rw-r--r-- | app/server/db/crud.js | 47 | ||||
| -rw-r--r-- | app/server/db/index.js | 5 | ||||
| -rw-r--r-- | app/server/db/loader.js | 101 | ||||
| -rw-r--r-- | app/server/db/model.js | 157 | ||||
| -rw-r--r-- | app/server/db/models.js | 53 |
6 files changed, 387 insertions, 0 deletions
diff --git a/app/server/db/bookshelf.js b/app/server/db/bookshelf.js new file mode 100644 index 0000000..27d9dbb --- /dev/null +++ b/app/server/db/bookshelf.js @@ -0,0 +1,24 @@ +const knex = require('knex')({ + client: 'mysql2', + connection: { + host : process.env.DB_HOST, + user : process.env.DB_USER, + password : process.env.DB_PASS, + database : process.env.DB_NAME, + charset : 'utf8', + typecast : function (field, next) { + console.log(field.type) + if (field.type == 'BLOB') { + return field.string() + } + return next() + } + } +}) + +const bookshelf = require('bookshelf')(knex) + +module.exports = { + bookshelf: bookshelf, + knex: knex, +} diff --git a/app/server/db/crud.js b/app/server/db/crud.js new file mode 100644 index 0000000..3aa0127 --- /dev/null +++ b/app/server/db/crud.js @@ -0,0 +1,47 @@ +module.exports = function(model) { + return { + index: (q) => { + return model.query( (qb) => { + const limit = q.limit || 100 + const offset = q.offset || 0 + const orderBy = q.orderBy || 'id desc' + if (limit) { + delete q.limit + } + if (q.offset) { + delete q.offset + } + delete q.orderBy + if (Object.keys(q).length > 0) qb.where(q) + if (orderBy) { + const ob = orderBy.split(" ") + const ob_field = ob[0] || 'id' + const ob_dir = ob[1] || 'desc' + qb.orderBy(ob_field, ob_dir) + } + if (limit) qb.limit( limit ) + if (offset) qb.offset( offset ) + // console.log(qb) + return qb + }).fetchAll() + }, + show: (id) => { + return new model({'id': id}).fetch() + }, + show_ids: (ids) => { + return model.query( (qb) => { + qb.whereIn('id', ids) + return qb + }).fetchAll() + }, + create: (data) => { + return new model(data).save() + }, + update: (id, data) => { + return new model({'id': id}).save(data) + }, + destroy: (id) => { + return new model({'id': id}).destroy() + }, + } +} diff --git a/app/server/db/index.js b/app/server/db/index.js new file mode 100644 index 0000000..c89afc3 --- /dev/null +++ b/app/server/db/index.js @@ -0,0 +1,5 @@ +let db = module.exports + +db.crud = require('./crud') +db.model = require('./model') +db.models = require('./models') diff --git a/app/server/db/loader.js b/app/server/db/loader.js new file mode 100644 index 0000000..ad42b17 --- /dev/null +++ b/app/server/db/loader.js @@ -0,0 +1,101 @@ +module.exports = (function(){ + function Loader (readyCallback, view){ + this.assets = {}; + this.images = []; + this.readyCallback = readyCallback || function(){}; + this.count = 0 + this.view = view + this.loaded = false + } + + // Set the callback when the loader is ready + Loader.prototype.onReady = function(readyCallback){ + this.readyCallback = readyCallback || function(){}; + } + + // Register an asset as loading + Loader.prototype.register = function(s){ + this.assets[s] = false; + this.count += 1 + } + + // Signal that an asset has loaded + Loader.prototype.ready = function(s){ + // window.debug && console.log("ready >> " + s); + + this.assets[s] = true; + if (this.loaded) return; + + this.view && this.view.update( this.percentRemaining() ) + + if (! this.isReady()) return; + + this.loaded = true; + if (this.view) { + this.view && this.view.finish(this.readyCallback) + } + else { + this.readyCallback && this.readyCallback(); + } + } + + // (boolean) Is the loader ready? + Loader.prototype.isReady = function(){ + return ! Object.keys(this.assets).some( (key) => { + return ! this.assets[key] + }) + } + + // (float) Percentage of assets remaining + Loader.prototype.percentRemaining = function(){ + return this.remainingAssets() / this.count + } + + // (int) Number of assets remaining + Loader.prototype.remainingAssets = function(){ + var n = 0; + for (var s in this.assets) { + if (this.assets.hasOwnProperty(s) && this.assets[s] != true) { + n++; + // console.log('remaining: ' + s); + } + } + return n; + } + + // Preload the images in config.images + Loader.prototype.preloadImages = function(images){ + this.register("preload"); + for (var i = 0; i < images.length; i++) { + this.preloadImage(images[i]); + } + this.ready("preload"); + } + Loader.prototype.preloadImage = function(src, register, cb){ + if (! src || src == "none") return; + var _this = this; + if (! cb && typeof register === "function") { + cb = register + register = null + } + if (register) { + this.register(src); + } + var img = new Image(), loaded = false; + img.onload = function(){ + if (loaded) return + loaded = true + if (cb) { + cb(img); + } + if (register) { + _this.ready(src); + } + } + img.src = src; + if (img.complete) img.onload(); + _this.images.push(img); + } + + return Loader; +})(); diff --git a/app/server/db/model.js b/app/server/db/model.js new file mode 100644 index 0000000..d84f138 --- /dev/null +++ b/app/server/db/model.js @@ -0,0 +1,157 @@ +const Loader = require('./loader') +const db_crud = require('./crud') + +module.exports = function modelScope(type, db_model, _props) { + + const props = Object.assign({ + hasOne: {}, + afterCreate: () => {}, + }, _props) + + const crud = db_crud(db_model) + + const model = { + type: type, + db_model: db_model, + crud: crud, + + index: (query) => { + + return new Promise( (resolve, reject) => { + crud.index(query).then( (data) => { + + if (! props.hasOne) { + resolve(data ? data.toJSON() : []) + } + else { + let recs = data.toJSON() + const loader = new Loader () + loader.onReady( () => { + // console.log(type, 'ready') + resolve(recs) + }) + // console.log('hasOne') + loader.register('hasOne') + Object.keys(props.hasOne).forEach( (key,i) => { + loader.register(key) + // console.log('register', key) + const type = props.hasOne[key] + const id_lookup = {} + recs.forEach(r => { + const id = r[key + '_id'] + id_lookup[id] = id_lookup[id] || [] + id_lookup[id].push(r) + }) + // console.log('\n\n%%%%%%%%%%%%%%%%%%%%%%%% index > hasOne ' + key + '\n\n\n') + // console.log(recs.length, Object.keys(id_lookup).length) + db_crud(type).show_ids(Object.keys(id_lookup)).then( (sub_recs) => { + // console.log(key, 'sub_recs', sub_recs) + const short_key = key.replace('_id','') + sub_recs.toJSON().forEach(rec => { + id_lookup[rec.id].forEach( parent_rec => parent_rec[short_key] = rec ) + }) + // console.log('ready', key) + loader.ready(key) + }) + }) + loader.ready('hasOne') + } + }) // }).catch( () => res.sendStatus(500) ) + }) + }, + + show: (id) => { + return new Promise( (resolve, reject) => { + crud.show(id).then( (data) => { + if (! props.hasOne) { + resolve(data.toJSON()) + } + else { + let rec = data.toJSON() + const loader = new Loader () + loader.onReady( () => { + resolve(rec) + }) + loader.register('hasOne') + Object.keys(props.hasOne).forEach( (key,i) => { + loader.register(key) + const type = props.hasOne[key] + db_crud(type).show(rec[key + '_id']).then( (sub_rec) => { + rec[key] = sub_rec + loader.ready(key) + }) + }) + loader.ready('hasOne') + } + }) // .catch( (err) => res.sendStatus(500) ) + }) + }, + + findOrCreate: (data) => { + return new Promise( (resolve, reject) => { + let query = Object.assign({}, data) + query.limit = 1 + crud.index(query).then( (recs) => { + if (recs && recs.length) { + const rec = recs.at(0) + // console.log('found rec', data.name) + return resolve(rec) + } + // console.log('creating rec', data.name) + model.create(data).then( (rec) => { + resolve(rec) + }) + }) + }) + }, + + create: (data) => { + return new Promise( (resolve, reject) => { + crud.create( model.sanitize(data) ).then( (data) => { + resolve(data.toJSON()) + props.afterCreate && props.afterCreate(data) + }).catch( (e) => { + console.error('error creating', e) + reject() + }) + }) + }, + + update: (id, data) => { + // console.log('update', id) + return new Promise( (resolve, reject) => { + crud.update(id, model.sanitize(data)).then( (data) => { + resolve(data.toJSON()) + }).catch( (e) => { + console.error('error updating', e) + reject() + }) + }) + }, + + destroy: (id) => { + return new Promise( (resolve, reject) => { + crud.destroy(id).then( (data) => { + resolve(data.toJSON()) + })// .catch( () => res.sendStatus(500) ) + }) + }, + + sanitize: (data) => { + var valid = {} + props.fields.forEach(key => { + if (props.hasOne[key]) { + return + } + if (key in data) { + valid[key] = data[key] + } + }) + // console.log(valid) + return valid + }, + + } + + return model +} diff --git a/app/server/db/models.js b/app/server/db/models.js new file mode 100644 index 0000000..2108148 --- /dev/null +++ b/app/server/db/models.js @@ -0,0 +1,53 @@ + +let fs = require('fs') +let model = require('./model') +let bookshelf = require("./bookshelf").bookshelf +import bridge from '../bridge' + +let Folder = bookshelf.Model.extend({ + tableName: 'folders', + hasTimestamps: true, +}) +let File = bookshelf.Model.extend({ + tableName: 'files', + hasTimestamps: true, +}) +let Job = bookshelf.Model.extend({ + tableName: 'jobs', + hasTimestamps: true, +}) +let Task = bookshelf.Model.extend({ + tableName: 'tasks', + hasTimestamps: true, +}) + +module.exports = { + folder: model('folder', Folder, { + fields: "name username description".split(" "), + afterCreate: (folder) => { + fs.mkdir('data/' + folder.get('id') + '/', function(){ + console.log('created folder', folder.get('id'), folder.get('name')) + }) + } + }), + file: model('file', File, { + fields: "folder_id username name mime type duration analysis size processed generated".split(" "), + afterCreate: (file) => { + bridge.processFiles() + } + }), + job: model('job', Job, { + fields: "name username completed tool".split(" "), + }), + task: model('task', Task, { + fields: "job_id username completed processing tool content_file_id style_file_id output_file_id alpha iterations stdout stderr".split(" "), + afterCreate: (task) => { + bridge.processTasks() + }, + hasOne: { + content_file: File, + style_file: File, + output_file: File, + } + }), +} |
