diff options
| author | Pitu <[email protected]> | 2018-09-16 00:55:41 -0300 |
|---|---|---|
| committer | Pitu <[email protected]> | 2018-09-16 00:55:41 -0300 |
| commit | a42cf4400eb00d3e476e29223d9c3587d61a105a (patch) | |
| tree | cc09b5e0c75179d19b4e77dac28e0201ede9fa02 /src/api/utils/Util.js | |
| parent | Base structures (diff) | |
| download | host.fuwn.me-a42cf4400eb00d3e476e29223d9c3587d61a105a.tar.xz host.fuwn.me-a42cf4400eb00d3e476e29223d9c3587d61a105a.zip | |
Utils
Diffstat (limited to 'src/api/utils/Util.js')
| -rw-r--r-- | src/api/utils/Util.js | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/src/api/utils/Util.js b/src/api/utils/Util.js new file mode 100644 index 0000000..46f56d5 --- /dev/null +++ b/src/api/utils/Util.js @@ -0,0 +1,181 @@ +const config = require('../../../config'); +const jetpack = require('fs-jetpack'); +const randomstring = require('randomstring'); +const path = require('path'); +const JWT = require('jsonwebtoken'); +const db = require('knex')(config.server.database); +const moment = require('moment'); +const log = require('../utils/Log'); +const crypto = require('crypto'); +const sharp = require('sharp'); +const ffmpeg = require('fluent-ffmpeg'); + +const imageExtensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png', '.webp']; +const videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov']; + +class Util { + static isExtensionBlocked(extension) { + return config.uploads.blockedExtensions.includes(extension); + } + + static generateThumbnails(filename) { + const ext = path.extname(filename).toLowerCase(); + const output = `${filename.slice(0, -ext.length)}.png`; + if (imageExtensions.includes(ext)) return this.generateThumbnailForImage(filename, output); + if (videoExtensions.includes(ext)) return this.generateThumbnailForVideo(filename); + return null; + } + + /* + static async removeExif(filename) { + This needs more testing. + Even though the exif data seems to be stripped, no other online service + is recognizing the file as an image file. + + const ExifTransformer = require('exif-be-gone'); + const toStream = require('buffer-to-stream'); + + const file = await jetpack.readAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename), 'buffer'); + const writer = jetpack.createWriteStream(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, `${filename}.noexif`)); + toStream(file).pipe(new ExifTransformer()).pipe(writer); + } + */ + + static async generateThumbnailForImage(filename, output) { + const file = await jetpack.readAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename), 'buffer'); + await sharp(file) + .resize(64, 64) + .toFormat('png') + .toFile(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'thumbs', 'square', output)); + await sharp(file) + .resize(225, null) + .toFormat('png') + .toFile(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'thumbs', output)); + } + + static generateThumbnailForVideo(filename) { + ffmpeg(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename)) + .thumbnail({ + timestamps: [0], + filename: '%b.png', + folder: path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'thumbs', 'square'), + size: '64x64' + }) + .on('error', error => log.error(error.message)); + ffmpeg(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename)) + .thumbnail({ + timestamps: [0], + filename: '%b.png', + folder: path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, 'thumbs'), + size: '150x?' + }) + .on('error', error => log.error(error.message)); + } + + static getFileThumbnail(filename) { + const ext = path.extname(filename).toLowerCase(); + if (!imageExtensions.includes(ext) && !videoExtensions.includes(ext)) return null; + return `${filename.slice(0, -ext.length)}.png`; + } + + static constructFilePublicLink(file) { + file.url = `${config.filesServeLocation}/${file.name}`; + const thumb = this.getFileThumbnail(file.name); + if (thumb) { + file.thumb = `${config.filesServeLocation}/thumbs/${thumb}`; + file.thumbSquare = `${config.filesServeLocation}/thumbs/square/${thumb}`; + } + return file; + } + + static getUniqueFilename(name) { + const retry = (i = 0) => { + const filename = randomstring.generate({ + length: config.uploads.generatedFilenameLength, + capitalization: 'lowercase' + }) + path.extname(name); + const exists = jetpack.exists(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename)); + if (!exists) return filename; + if (i < config.uploads.retryFilenameTimes) return retry(i++); + return null; + }; + return retry(); + } + + static getUniqueAlbumIdentifier() { + const retry = async (i = 0) => { + const identifier = randomstring.generate({ + length: config.uploads.generatedAlbumLinkLength, + capitalization: 'lowercase' + }); + const exists = await db.table('links').where({ identifier }).first(); + if (!exists) return identifier; + if (i < config.uploads.retryAlbumLinkTimes) return retry(i++); + return null; + }; + return retry(); + } + + static async getFileHash(filename) { + const file = await jetpack.readAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename), 'buffer'); + if (!file) { + log.error(`There was an error reading the file < ${filename} > for hashing`); + return null; + } + + const hash = crypto.createHash('md5'); + hash.update(file, 'utf8'); + return hash.digest('hex'); + } + + static getFilenameFromPath(fullPath) { + return fullPath.replace(/^.*[\\\/]/, ''); // eslint-disable-line no-useless-escape + } + + static async deleteFile(filename, deleteFromDB = false) { + try { + await jetpack.removeAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, filename)); + if (deleteFromDB) { + await db.table('files').where('name', filename).delete(); + } + } catch (error) { + log.error(`There was an error removing the file < ${filename} >`); + log.error(error); + } + } + + static async deleteAllFilesFromAlbum(id) { + try { + const files = await db.table('files').where({ albumId: id }); + for (const file of files) { + await jetpack.removeAsync(path.join(__dirname, '..', '..', '..', config.uploads.uploadFolder, file)); + } + await db.table('files').where({ albumId: id }).delete(); + } catch (error) { + log.error(error); + } + } + + static isAuthorized(req) { + if (!req.headers.authorization) return false; + const token = req.headers.authorization.split(' ')[1]; + if (!token) return false; + + return JWT.verify(token, config.server.secret, async (error, decoded) => { + if (error) { + log.error(error); + return false; + } + const id = decoded ? decoded.sub : ''; + const iat = decoded ? decoded.iat : ''; + + const user = await db.table('users').where({ id }).first(); + if (!user || !user.enabled) return false; + if (iat && iat < moment(user.passwordEditedAt).format('x')) return false; + + return user; + }); + } +} + +module.exports = Util; |