From 0605f6546500edc5fb5016c61668e6b01c8fceab Mon Sep 17 00:00:00 2001 From: NGPixel Date: Sun, 22 Jan 2023 04:37:33 -0500 Subject: [PATCH] feat: file manager thumbnail preview --- package.json | 3 +- server/controllers/common.js | 45 ++++++++++++++------------ server/core/system.js | 3 ++ server/graph/resolvers/asset.js | 13 +++----- server/models/assets.js | 15 ++++++++- server/modules/extensions/sharp/ext.js | 6 ++-- server/views/base.pug | 6 ++-- ux/quasar.config.js | 9 +++--- ux/src/components/FileManager.vue | 7 ++-- 9 files changed, 64 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index ce0a52e5..25720b3f 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,7 @@ "dev": "nodemon server", "legacy:dev": "NODE_OPTIONS=--openssl-legacy-provider node dev", "legacy:build": "NODE_OPTIONS=--openssl-legacy-provider webpack --profile --config dev/webpack/webpack.prod.js", - "test": "eslint --format codeframe --ext .js,.vue . && pug-lint server/views && jest", - "cypress:open": "cypress open" + "test": "eslint --format codeframe --ext .js,.vue . && pug-lint server/views && jest" }, "repository": { "type": "git", diff --git a/server/controllers/common.js b/server/controllers/common.js index 3f75d9a7..ceae8650 100644 --- a/server/controllers/common.js +++ b/server/controllers/common.js @@ -5,8 +5,6 @@ const _ = require('lodash') const CleanCSS = require('clean-css') const moment = require('moment') const path = require('path') - -const tmplCreateRegex = /^[0-9]+(,[0-9]+)?$/ const siteAssetsPath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'assets') /** @@ -78,30 +76,35 @@ router.get('/_site/:siteId?/:resource', async (req, res, next) => { /** * Asset Thumbnails / Download */ -router.get('/_asset/:siteId/:mode/*', async (req, res, next) => { - const site = req.params.siteId ? WIKI.sites[req.params.siteId] : await WIKI.db.sites.getSiteByHostname({ hostname: req.hostname }) - if (!site) { - return res.status(404).send('Site Not Found') - } - const filePath = req.params[0] - console.info(filePath) - switch (req.params.mode) { - case 'thumb': { - try { +router.get('/_thumb/:id.png', async (req, res, next) => { + const thumb = await WIKI.db.assets.getThumbnail({ + id: req.params.id + }) - } catch (err) { + if (thumb) { + // TODO: Check permissions + switch (thumb.previewState) { + case 'pending': { + res.send('PENDING') + break + } + case 'ready': { + res.set('Content-Type', 'image/png') + res.send(thumb.preview) + break + } + case 'failed': { + res.status(500).send('Thumbnail Preview Failed').end() + break + } + default: { + return res.status(500).send('Invalid Thumbnail Preview State') } - break - } - case 'download': { - break - } - default: { - return res.status(404).send('Invalid Site Resource') } + } else { + return res.sendStatus(404) } - return res.send('BOB').end() }) /** diff --git a/server/core/system.js b/server/core/system.js index 468a3016..7f94415f 100644 --- a/server/core/system.js +++ b/server/core/system.js @@ -10,6 +10,9 @@ module.exports = { minimumNodeRequired: '18.0.0' }, init () { + fs.ensureDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'assets')) + fs.ensureDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'uploads')) + // Clear content cache fs.emptyDir(path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'cache')) diff --git a/server/graph/resolvers/asset.js b/server/graph/resolvers/asset.js index b075898a..c1a585bc 100644 --- a/server/graph/resolvers/asset.js +++ b/server/graph/resolvers/asset.js @@ -258,7 +258,7 @@ module.exports = { const asset = assetRaw[0] // Add to tree - const treeAsset = await WIKI.db.tree.addAsset({ + await WIKI.db.tree.addAsset({ id: asset.id, parentPath: folder.folderPath ? `${folder.folderPath}.${folder.fileName}` : folder.fileName, fileName: formattedFilename, @@ -309,13 +309,7 @@ module.exports = { WIKI.logger.warn('Cannot generate asset thumbnail because the Sharp extension is not installed.') } else { WIKI.logger.debug(`Generating thumbnail of asset ${sanitizedFilename}...`) - const previewDestFolder = path.resolve( - WIKI.ROOTPATH, - WIKI.config.dataPath, - 'assets' - ) - const previewDestPath = path.join(previewDestFolder, `asset-thumb-${treeAsset.hash}.png`) - await fs.ensureDir(previewDestFolder) + const previewDestPath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, `uploads/${tempFileId}-thumb.png`) // -> Resize await WIKI.extensions.ext.sharp.resize({ format: 'png', @@ -332,6 +326,9 @@ module.exports = { preview: await fs.readFile(previewDestPath), previewState: 'ready' }) + + // -> Delete + await fs.remove(previewDestPath) } } diff --git a/server/models/assets.js b/server/models/assets.js index 440f7543..721eafcb 100644 --- a/server/models/assets.js +++ b/server/models/assets.js @@ -3,6 +3,7 @@ const moment = require('moment') const path = require('path') const fs = require('fs-extra') const _ = require('lodash') +const commonHelper = require('../helpers/common') /** * Users model @@ -160,7 +161,19 @@ module.exports = class Asset extends Model { } } - static async getAsset(assetPath, res) { + static async getThumbnail ({ id, path, locale, siteId }) { + return WIKI.db.tree.query() + .select('tree.*', 'assets.preview', 'assets.previewState') + .innerJoin('assets', 'tree.id', 'assets.id') + .where(id ? { 'tree.id': id } : { + 'tree.hash': commonHelper.generateHash(path), + 'tree.localeCode': locale, + 'tree.siteId': siteId + }) + .first() + } + + static async getAsset({ path, locale, siteId }, res) { try { const fileInfo = '' // assetHelper.getPathInfo(assetPath) const fileHash = '' // assetHelper.generateHash(assetPath) diff --git a/server/modules/extensions/sharp/ext.js b/server/modules/extensions/sharp/ext.js index b25ecfa7..4e08e141 100644 --- a/server/modules/extensions/sharp/ext.js +++ b/server/modules/extensions/sharp/ext.js @@ -51,7 +51,8 @@ module.exports = { width = null, height = null, fit = 'cover', - background = { r: 0, g: 0, b: 0, alpha: 0 } + background = { r: 0, g: 0, b: 0, alpha: 0 }, + kernel = 'lanczos3' }) { this.load() @@ -75,7 +76,8 @@ module.exports = { width, height, fit, - background + background, + kernel }).toFormat(format) return pipeline([inputStream, transformer, outputStream]) diff --git a/server/views/base.pug b/server/views/base.pug index 61b4e283..a63b5ad6 100644 --- a/server/views/base.pug +++ b/server/views/base.pug @@ -44,7 +44,7 @@ html(lang=siteConfig.lang) link( type='text/css' rel='stylesheet' - href='/_assets-legacy/css/app.36b4c9522aa279325701.css' + href='/_assets-legacy/css/app.c05740c020721e44657c.css' ) @@ -54,14 +54,14 @@ html(lang=siteConfig.lang) script( type='text/javascript' - src='/_assets-legacy/js/runtime.js?1671237890' + src='/_assets-legacy/js/runtime.js?1674373130' ) script( type='text/javascript' - src='/_assets-legacy/js/app.js?1671237890' + src='/_assets-legacy/js/app.js?1674373130' ) diff --git a/ux/quasar.config.js b/ux/quasar.config.js index 153fa612..5e0cc01e 100644 --- a/ux/quasar.config.js +++ b/ux/quasar.config.js @@ -102,11 +102,12 @@ module.exports = configure(function (/* ctx */) { devServer: { // https: true open: false, // opens browser window automatically - port: 5001, + port: 3001, proxy: { - '/_graphql': 'http://localhost:5000/_graphql', - '/_site': 'http://localhost:5000', - '/_user': 'http://localhost:5000' + '/_graphql': 'http://127.0.0.1:3000/_graphql', + '/_site': 'http://127.0.0.1:3000', + '/_thumb': 'http://127.0.0.1:3000', + '/_user': 'http://127.0.0.1:3000' } }, diff --git a/ux/src/components/FileManager.vue b/ux/src/components/FileManager.vue index 3e1d9251..fc374368 100644 --- a/ux/src/components/FileManager.vue +++ b/ux/src/components/FileManager.vue @@ -68,7 +68,7 @@ q-layout.fileman(view='hHh lpR lFr', container) .q-pa-md template(v-if='currentFileDetails') q-img.rounded-borders.q-mb-md( - src='/_assets/illustrations/fileman-page.svg' + :src='currentFileDetails.thumbnail' width='100%' fit='cover' :ratio='16/10' @@ -450,8 +450,10 @@ const currentFileDetails = computed(() => { value: item.title } ] + let thumbnail = '' switch (item.type) { case 'page': { + thumbnail = '/_assets/illustrations/fileman-page.svg' items.push({ label: t('fileman.detailsPageType'), value: t(`fileman.${item.pageType}PageType`) @@ -471,6 +473,7 @@ const currentFileDetails = computed(() => { break } case 'asset': { + thumbnail = `/_thumb/${item.id}.png` items.push({ label: t('fileman.detailsAssetType'), value: fileTypes[item.fileExt] ? t(`fileman.${item.fileExt}FileType`) : t('fileman.unknownFileType', { type: item.fileExt.toUpperCase() }) @@ -483,7 +486,7 @@ const currentFileDetails = computed(() => { } } return { - thumbnail: '', + thumbnail, items } } else {