Fix:File permissions on cache dirs and cache images, Fix:Db delete read stream closing before write stream resulting in deletes sometimes not happening

This commit is contained in:
advplyr 2022-05-15 09:51:08 -05:00
parent c962090c3a
commit 853858825b
3 changed files with 49 additions and 24 deletions

View File

@ -1,6 +1,7 @@
const Path = require('path') const Path = require('path')
const fs = require('fs-extra') const fs = require('fs-extra')
const stream = require('stream') const stream = require('stream')
const filePerms = require('../utils/filePerms')
const Logger = require('../Logger') const Logger = require('../Logger')
const { resizeImage } = require('../utils/ffmpegHelpers') const { resizeImage } = require('../utils/ffmpegHelpers')
@ -9,6 +10,34 @@ class CacheManager {
this.CachePath = Path.join(global.MetadataPath, 'cache') this.CachePath = Path.join(global.MetadataPath, 'cache')
this.CoverCachePath = Path.join(this.CachePath, 'covers') this.CoverCachePath = Path.join(this.CachePath, 'covers')
this.ImageCachePath = Path.join(this.CachePath, 'images') this.ImageCachePath = Path.join(this.CachePath, 'images')
this.cachePathsExist = false
}
async ensureCachePaths() { // Creates cache paths if necessary and sets owner and permissions
if (this.cachePathsExist) return
var pathsCreated = false
if (!(await fs.pathExists(this.CachePath))) {
await fs.mkdir(this.CachePath)
pathsCreated = true
}
if (!(await fs.pathExists(this.CoverCachePath))) {
await fs.mkdir(this.CoverCachePath)
pathsCreated = true
}
if (!(await fs.pathExists(this.ImageCachePath))) {
await fs.mkdir(this.ImageCachePath)
pathsCreated = true
}
if (pathsCreated) {
await filePerms.setDefault(this.CachePath)
}
this.cachePathsExist = true
} }
async handleCoverCache(res, libraryItem, options = {}) { async handleCoverCache(res, libraryItem, options = {}) {
@ -34,7 +63,7 @@ class CacheManager {
} }
// Write cache // Write cache
await fs.ensureDir(this.CoverCachePath) await this.ensureCachePaths()
if (!libraryItem.media.coverPath || !await fs.pathExists(libraryItem.media.coverPath)) { if (!libraryItem.media.coverPath || !await fs.pathExists(libraryItem.media.coverPath)) {
return res.sendStatus(404) return res.sendStatus(404)
@ -43,6 +72,9 @@ class CacheManager {
let writtenFile = await resizeImage(libraryItem.media.coverPath, path, width, height) let writtenFile = await resizeImage(libraryItem.media.coverPath, path, width, height)
if (!writtenFile) return res.sendStatus(400) if (!writtenFile) return res.sendStatus(400)
// Set owner and permissions of cache image
await filePerms.setDefault(path)
var readStream = fs.createReadStream(writtenFile) var readStream = fs.createReadStream(writtenFile)
readStream.pipe(res) readStream.pipe(res)
} }
@ -57,7 +89,8 @@ class CacheManager {
async purgeEntityCache(entityId, cachePath) { async purgeEntityCache(entityId, cachePath) {
// If purgeAll has been called... The cover cache directory no longer exists // If purgeAll has been called... The cover cache directory no longer exists
await fs.ensureDir(cachePath) await this.ensureCachePaths()
return Promise.all((await fs.readdir(cachePath)).reduce((promises, file) => { return Promise.all((await fs.readdir(cachePath)).reduce((promises, file) => {
if (file.startsWith(entityId)) { if (file.startsWith(entityId)) {
Logger.debug(`[CacheManager] Going to purge ${file}`); Logger.debug(`[CacheManager] Going to purge ${file}`);
@ -109,11 +142,14 @@ class CacheManager {
} }
// Write cache // Write cache
await fs.ensureDir(this.ImageCachePath) await this.ensureCachePaths()
let writtenFile = await resizeImage(author.imagePath, path, width, height) let writtenFile = await resizeImage(author.imagePath, path, width, height)
if (!writtenFile) return res.sendStatus(400) if (!writtenFile) return res.sendStatus(400)
// Set owner and permissions of cache image
await filePerms.setDefault(path)
var readStream = fs.createReadStream(writtenFile) var readStream = fs.createReadStream(writtenFile)
readStream.pipe(res) readStream.pipe(res)
} }

View File

@ -480,7 +480,6 @@ const updateStoreData = async (store, match, update, tempstore, lockoptions) =>
release = await lock(store, lockoptions); release = await lock(store, lockoptions);
// console.log('Start updateStoreData for tempstore', tempstore, 'real store', store)
const handlerResults = await new Promise((resolve, reject) => { const handlerResults = await new Promise((resolve, reject) => {
const writer = createWriteStream(tempstore); const writer = createWriteStream(tempstore);
@ -490,14 +489,11 @@ const updateStoreData = async (store, match, update, tempstore, lockoptions) =>
// Reader was opening and closing before writer ever opened // Reader was opening and closing before writer ever opened
const reader = createInterface({ input: createReadStream(store), crlfDelay: Infinity }); const reader = createInterface({ input: createReadStream(store), crlfDelay: Infinity });
// console.log('Writer opened for tempstore', tempstore)
reader.on("line", record => { reader.on("line", record => {
handler.next(record, writer) handler.next(record, writer)
}); });
reader.on("close", () => { reader.on("close", () => {
// console.log('Closing reader for store', store)
writer.end(); writer.end();
resolve(handler.return()); resolve(handler.return());
}); });
@ -505,12 +501,7 @@ const updateStoreData = async (store, match, update, tempstore, lockoptions) =>
reader.on("error", error => reject(error)); reader.on("error", error => reject(error));
}); });
// writer.on('close', () => {
// console.log('Writer closed for tempstore', tempstore)
// })
writer.on("error", error => reject(error)); writer.on("error", error => reject(error));
}); });
results = Object.assign({ store: store, tempstore: tempstore }, handlerResults); results = Object.assign({ store: store, tempstore: tempstore }, handlerResults);
@ -579,26 +570,27 @@ const updateStoreDataSync = (store, match, update, tempstore) => {
const deleteStoreData = async (store, match, tempstore, lockoptions) => { const deleteStoreData = async (store, match, tempstore, lockoptions) => {
let release, results; let release, results;
release = await lock(store, lockoptions); release = await lock(store, lockoptions);
const handlerResults = await new Promise((resolve, reject) => { const handlerResults = await new Promise((resolve, reject) => {
const reader = createInterface({ input: createReadStream(store), crlfDelay: Infinity });
const writer = createWriteStream(tempstore); const writer = createWriteStream(tempstore);
const handler = Handler("delete", match); const handler = Handler("delete", match);
writer.on("open", () => { writer.on("open", () => {
// Create reader after writer opens otherwise the reader can sometimes close before the writer opens
const reader = createInterface({ input: createReadStream(store), crlfDelay: Infinity });
reader.on("line", record => handler.next(record, writer)); reader.on("line", record => handler.next(record, writer));
reader.on("close", () => {
writer.end();
resolve(handler.return());
});
reader.on("error", error => reject(error));
}); });
writer.on("error", error => reject(error)); writer.on("error", error => reject(error));
reader.on("close", () => {
writer.end();
resolve(handler.return());
});
reader.on("error", error => reject(error));
}); });
results = Object.assign({ store: store, tempstore: tempstore }, handlerResults); results = Object.assign({ store: store, tempstore: tempstore }, handlerResults);

View File

@ -120,9 +120,6 @@ module.exports.extractCoverArt = extractCoverArt
//This should convert based on the output file extension as well //This should convert based on the output file extension as well
async function resizeImage(filePath, outputPath, width, height) { async function resizeImage(filePath, outputPath, width, height) {
var dirname = Path.dirname(outputPath);
await fs.ensureDir(dirname);
return new Promise((resolve) => { return new Promise((resolve) => {
var ffmpeg = Ffmpeg(filePath) var ffmpeg = Ffmpeg(filePath)
ffmpeg.addOption(['-vf', `scale=${width || -1}:${height || -1}`]) ffmpeg.addOption(['-vf', `scale=${width || -1}:${height || -1}`])