summaryrefslogtreecommitdiff
path: root/app/server/db
diff options
context:
space:
mode:
Diffstat (limited to 'app/server/db')
-rw-r--r--app/server/db/bookshelf.js24
-rw-r--r--app/server/db/crud.js47
-rw-r--r--app/server/db/index.js5
-rw-r--r--app/server/db/loader.js101
-rw-r--r--app/server/db/model.js157
-rw-r--r--app/server/db/models.js53
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,
+ }
+ }),
+}