const path = require('path'); const fs = require('fs'); const { AccessModel, ACCESS_TYPE, ACCESS_TO, ACCESS_GRANT } = require('./access.js'); function Media() { } function MediaStruct(i) { this.path = i.path; this.fileName = path.parse(i.path).name; this.md5sum = i.md5sum; this.fixedSum = i.fixedSum; this.date = i.date; this.meta = {}; this.tags = []; this.fixedTags = []; this.accessType = -1; } MediaStruct.prototype.pushMeta = function(key, value) { if (key && value && !this.meta[key]) { let type = ''; let roTypes = [ 'photochamberImport', 'height', 'width', 'iso', 'focal', 'fNumber', 'exposureTime', 'camera', 'lensModel', 'exposureTimeStr', 'libraryPath', 'compression', 'software', 'fileSize', 'geoHash', 'exposureProgram', 'orientation' ]; if (['photochamberImport', 'dateTime'].indexOf(key) >= 0) type = 'date'; else if (['height', 'width', 'iso', 'focal', 'fNumber', 'exposureTime', 'orientation'].indexOf(key) >= 0) type = 'number'; else if (['geoHash', 'gpsLocation'].indexOf(key) >= 0) type = 'geoData'; else if (['artist', 'camera', 'lensModel', 'exposureTimeStr', 'libraryPath', 'compression', 'software', 'geoCountry', 'geoAdmin', 'geoCity', 'exposureProgram'].indexOf(key) >= 0) type = 'string'; else if (['fileSize'].indexOf(key) >= 0) type = 'octet'; else console.log(`Unknown meta type ${key} (${value})`); this.meta[key] = { type: type, canWrite: roTypes.indexOf(key) === -1, value: value }; } } MediaStruct.prototype.pushTag = function(tag, isFixedTag) { if (!tag) return; if (!isFixedTag && this.tags.indexOf(tag) === -1) this.tags.push(tag); if (isFixedTag && this.fixedTags.indexOf(tag) === -1) this.fixedTags.push(tag); } MediaStruct.prototype.computeAccess = function(accessList) { if (this.accessType > -1) return this.accessType; if (!fs.existsSync(this.path)) return this.accessType = ACCESS_GRANT.none; const checkTag = function(tags, access) { if (!tags.length) return false; for (let i of tags) if (i.startsWith(access.accessToData+'/') || i === access.accessToData) return true; return false; } const checkMeta = function(metas, access) { if (!access.accessToDataDeserialized) return false; let metaKey = Object.keys(access.accessToDataDeserialized)[0]; let meta = metas[metaKey]?.value; return meta && metaKey && meta == access.accessToDataDeserialized[metaKey]; } this.accessType = ACCESS_GRANT.none; for (let i of accessList) { if (i.accessTo === ACCESS_TO.everything || (i.accessTo === ACCESS_TO.item && i.accessToData === this.fixedSum) || (i.accessTo === ACCESS_TO.meta && checkMeta(this.meta, i)) || (i.accessTo === ACCESS_TO.tag && checkTag([].concat(this.fixedTags, this.tags), i))) { if (i.grant === ACCESS_GRANT.write) return this.accessType = ACCESS_GRANT.write; if (i.grant === ACCESS_GRANT.read || (i.grant === ACCESS_GRANT.readNoMeta && this.accessType === ACCESS_GRANT.none)) this.accessType = i.grant; } } return this.accessType; } MediaStruct.prototype.HaveAccess = function(accessList) { return this.computeAccess(accessList) > 0; } async function buildAccessList(app, accessIds) { accessIds = Object.keys(accessIds || {}).reduce((acc, i) => { accessIds[i].linkId && acc.links.push(accessIds[i].linkId); accessIds[i].ldapDn && acc.ldap.push(accessIds[i].ldapDn); accessIds[i].email && acc.emails.push(accessIds[i].email); return acc; }, {links:[], emails: [], ldap: []}); accessIds.accData = [].concat(accessIds.ldap, accessIds.emails, accessIds.links); accessIds.links = accessIds.links.map(x => '?').join(','); accessIds.emails = accessIds.emails.map(x => '?').join(','); accessIds.ldap = accessIds.ldap.map(x => '?').join(','); let accessList = (await app.databaseHelper.runSql(`select * from access where ( (type=${ACCESS_TYPE.ldapAccount} AND typeData in (${accessIds.ldap})) OR (type=${ACCESS_TYPE.email} AND typeData in (${accessIds.emails})) OR (type=${ACCESS_TYPE.link} AND typeData in (${accessIds.links})) OR type=${ACCESS_TYPE.everyOne} )`, accessIds.accData)).map(data => { let result = new AccessModel; result.fromDb(data); return result; }); return accessList || []; } function reduceReqToMediaStruct(acc, i) { let obj = acc[i.fixedSum] = acc[i.fixedSum] || new MediaStruct(i); obj.pushMeta(i.metaKey, i.metaValue); obj.pushTag(i.mediaTag, i.isFixedTag); return acc; } module.exports.fetchOne = async function(app, md5sum, accessList) { let result = ((await app.databaseHelper.runSql(` select mediaFile.path, mediaFile.md5sum, mediaFile.date, mediaFile.fixedSum, mediaMeta.key as metaKey, mediaMeta.value as metaValue, mediaTag.tag as mediaTag, mediaTag.fromMeta as isFixedTag from mediaFile left join mediaMeta on mediaMeta.md5sum=mediaFile.fixedSum left join mediaTag on mediaTag.md5sum=mediaFile.fixedSum where mediaFile.fixedSum=?`, md5sum)) || []).reduce(reduceReqToMediaStruct, {})[md5sum] || null; accessList = await buildAccessList(app, accessList); return result?.HaveAccess(accessList) ? result : null; } module.exports.fetchMedias = async function(app, startTs, count) { let result = ((await app.databaseHelper.runSql(` select mediaFile.path, mediaFile.md5sum, mediaFile.date, mediaFile.fixedSum, mediaMeta.key as metaKey, mediaMeta.value as metaValue, mediaTag.tag as mediaTag, mediaTag.fromMeta as isFixedTag from mediaFile left join mediaMeta on mediaMeta.md5sum=mediaFile.fixedSum left join mediaTag on mediaTag.md5sum=mediaFile.fixedSum where mediaFile.fixedSum in (select fixedSum from mediaFile ` +(startTs ? "where date result[i]).sort((a, b) => b.date-a.date); return result; }; module.exports.fetchMediasWithAccess = async function(app, startTs, count, access) { let result = []; let lastTs = startTs; access = await buildAccessList(app, access); while (result.length < count) { let tmp = await module.exports.fetchMedias(app, lastTs, 25); if (!tmp.length) return result; lastTs = tmp[tmp.length-1].date; tmp = tmp.filter(i => i.HaveAccess(access)); if (tmp.length) result = result.concat(tmp); } return result.slice(0, count); }; module.exports.getMediaRange = async function(app) { let result = await app.databaseHelper.runSql("select min(date) as _min, max(date) as _max from mediaFile"); return [ result?.[0]?._min || 0, result?.[0]?._max || 0 ]; }