src/actions/remove.js
const { ActionTransport } = require('@microfleet/plugin-router');
const Promise = require('bluebird');
const hasAccess = require('../utils/has-access');
const fetchData = require('../utils/fetch-data');
const isUnlisted = require('../utils/is-unlisted');
const { assertUpdatable, isClone } = require('../utils/check-data');
const { bustCache } = require('../utils/bust-cache');
const { getReferenceData, updateReferences } = require('../utils/reference');
const {
FILES_INDEX,
FILES_INDEX_PUBLIC,
FILES_DATA,
FILES_PUBLIC_FIELD,
FILES_OWNER_FIELD,
FILES_INDEX_TAGS,
FILES_TAGS_FIELD,
FILES_ALIAS_FIELD,
FILES_USR_ALIAS_PTR,
FILES_USER_INDEX_KEY,
FILES_USER_INDEX_PUBLIC_KEY,
FILES_REFERENCES_FIELD,
} = require('../constant');
const pipelineError = require('../utils/pipeline-error');
/**
* This function used as coroutine, we don't track the result
* of this action
*/
function cleanupFileProvider(files, provider, log, opts = { concurrency: 20 }) {
return Promise
.map(files, (file) => (
provider
.remove(file.filename)
.catch({ code: 404 }, (err) => {
log.warn('file %s was already deleted', file.filename, err.code, err.message);
})
), opts);
}
/**
* Initiates upload
* @param {Object} opts
* @param {String} opts.username
* @param {String} opts.filename
* @return {Promise}
*/
async function removeFile({ params }) {
const { filename, username, softDelete } = params;
const { redis, log } = this;
const provider = this.provider('remove', params);
const key = `${FILES_DATA}:${filename}`;
const data = await Promise
.bind(this, key)
.then(fetchData)
.then(isUnlisted)
.then(hasAccess(username))
.then(assertUpdatable({}, true));
if (!softDelete && !isClone(data)) {
// we do not track this
cleanupFileProvider(data.files, provider, log)
.catch((e) => log.fatal({ err: e }, 'failed to cleanup file provider for %s', filename));
}
// cleanup local database
const pipeline = redis.pipeline();
const owner = data[FILES_OWNER_FIELD];
pipeline
.del(key)
.srem(FILES_INDEX, filename)
.srem(FILES_USER_INDEX_KEY(owner), filename);
if (data[FILES_PUBLIC_FIELD]) {
pipeline.srem(FILES_INDEX_PUBLIC, filename);
pipeline.srem(FILES_USER_INDEX_PUBLIC_KEY(owner), filename);
}
if (data[FILES_REFERENCES_FIELD]) {
const referenceData = getReferenceData(redis, data[FILES_REFERENCES_FIELD]);
updateReferences({ ...data, [FILES_REFERENCES_FIELD]: [] }, data, referenceData, pipeline);
}
const tags = data[FILES_TAGS_FIELD];
if (tags) {
tags.forEach((tag) => pipeline.srem(`${FILES_INDEX_TAGS}:${tag}`, filename));
}
// remove pointer for the alias if it existed
const alias = data[FILES_ALIAS_FIELD];
if (alias) {
pipeline.hdel(`${FILES_USR_ALIAS_PTR}:${owner}`, alias);
}
// execute pipeline and bustCache right after
return pipeline
.exec()
.tap(bustCache(redis, data, false))
.then(pipelineError);
}
removeFile.transports = [ActionTransport.amqp];
module.exports = removeFile;