123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- const _ = require('lodash')
- const sanitize = require('sanitize-filename')
- const graphHelper = require('../../helpers/graph')
- const assetHelper = require('../../helpers/asset')
- const path = require('node:path')
- const fs = require('fs-extra')
- const { v4: uuid } = require('uuid')
- module.exports = {
- Query: {
- async assets(obj, args, context) {
- let cond = {
- folderId: args.folderId === 0 ? null : args.folderId
- }
- if (args.kind !== 'ALL') {
- cond.kind = args.kind.toLowerCase()
- }
- const folderHierarchy = await WIKI.db.assetFolders.getHierarchy(args.folderId)
- const folderPath = folderHierarchy.map(h => h.slug).join('/')
- const results = await WIKI.db.assets.query().where(cond)
- return _.filter(results, r => {
- const path = folderPath ? `${folderPath}/${r.filename}` : r.filename
- return WIKI.auth.checkAccess(context.req.user, ['read:assets'], { path })
- }).map(a => ({
- ...a,
- kind: a.kind.toUpperCase()
- }))
- },
- async assetsFolders(obj, args, context) {
- const results = await WIKI.db.assetFolders.query().where({
- parentId: args.parentFolderId === 0 ? null : args.parentFolderId
- })
- const parentHierarchy = await WIKI.db.assetFolders.getHierarchy(args.parentFolderId)
- const parentPath = parentHierarchy.map(h => h.slug).join('/')
- return _.filter(results, r => {
- const path = parentPath ? `${parentPath}/${r.slug}` : r.slug
- return WIKI.auth.checkAccess(context.req.user, ['read:assets'], { path })
- })
- }
- },
- Mutation: {
- /**
- * Create New Asset Folder
- */
- async createAssetsFolder(obj, args, context) {
- try {
- const folderSlug = sanitize(args.slug).toLowerCase()
- const parentFolderId = args.parentFolderId === 0 ? null : args.parentFolderId
- const result = await WIKI.db.assetFolders.query().where({
- parentId: parentFolderId,
- slug: folderSlug
- }).first()
- if (!result) {
- await WIKI.db.assetFolders.query().insert({
- slug: folderSlug,
- name: folderSlug,
- parentId: parentFolderId
- })
- return {
- responseResult: graphHelper.generateSuccess('Asset Folder has been created successfully.')
- }
- } else {
- throw new WIKI.Error.AssetFolderExists()
- }
- } catch (err) {
- return graphHelper.generateError(err)
- }
- },
- /**
- * Rename an Asset
- */
- async renameAsset(obj, args, context) {
- try {
- const filename = sanitize(args.filename).toLowerCase()
- const asset = await WIKI.db.assets.query().findById(args.id)
- if (asset) {
- // Check for extension mismatch
- if (!_.endsWith(filename, asset.ext)) {
- throw new WIKI.Error.AssetRenameInvalidExt()
- }
- // Check for non-dot files changing to dotfile
- if (asset.ext.length > 0 && filename.length - asset.ext.length < 1) {
- throw new WIKI.Error.AssetRenameInvalid()
- }
- // Check for collision
- const assetCollision = await WIKI.db.assets.query().where({
- filename,
- folderId: asset.folderId
- }).first()
- if (assetCollision) {
- throw new WIKI.Error.AssetRenameCollision()
- }
- // Get asset folder path
- let hierarchy = []
- if (asset.folderId) {
- hierarchy = await WIKI.db.assetFolders.getHierarchy(asset.folderId)
- }
- // Check source asset permissions
- const assetSourcePath = (asset.folderId) ? hierarchy.map(h => h.slug).join('/') + `/${asset.filename}` : asset.filename
- if (!WIKI.auth.checkAccess(context.req.user, ['manage:assets'], { path: assetSourcePath })) {
- throw new WIKI.Error.AssetRenameForbidden()
- }
- // Check target asset permissions
- const assetTargetPath = (asset.folderId) ? hierarchy.map(h => h.slug).join('/') + `/${filename}` : filename
- if (!WIKI.auth.checkAccess(context.req.user, ['write:assets'], { path: assetTargetPath })) {
- throw new WIKI.Error.AssetRenameTargetForbidden()
- }
- // Update filename + hash
- const fileHash = assetHelper.generateHash(assetTargetPath)
- await WIKI.db.assets.query().patch({
- filename: filename,
- hash: fileHash
- }).findById(args.id)
- // Delete old asset cache
- await asset.deleteAssetCache()
- // Rename in Storage
- await WIKI.db.storage.assetEvent({
- event: 'renamed',
- asset: {
- ...asset,
- path: assetSourcePath,
- destinationPath: assetTargetPath,
- moveAuthorId: context.req.user.id,
- moveAuthorName: context.req.user.name,
- moveAuthorEmail: context.req.user.email
- }
- })
- return {
- responseResult: graphHelper.generateSuccess('Asset has been renamed successfully.')
- }
- } else {
- throw new WIKI.Error.AssetInvalid()
- }
- } catch (err) {
- return graphHelper.generateError(err)
- }
- },
- /**
- * Delete an Asset
- */
- async deleteAsset(obj, args, context) {
- try {
- const asset = await WIKI.db.assets.query().findById(args.id)
- if (asset) {
- // Check permissions
- const assetPath = await asset.getAssetPath()
- if (!WIKI.auth.checkAccess(context.req.user, ['manage:assets'], { path: assetPath })) {
- throw new WIKI.Error.AssetDeleteForbidden()
- }
- await WIKI.db.knex('assetData').where('id', args.id).del()
- await WIKI.db.assets.query().deleteById(args.id)
- await asset.deleteAssetCache()
- // Delete from Storage
- await WIKI.db.storage.assetEvent({
- event: 'deleted',
- asset: {
- ...asset,
- path: assetPath,
- authorId: context.req.user.id,
- authorName: context.req.user.name,
- authorEmail: context.req.user.email
- }
- })
- return {
- responseResult: graphHelper.generateSuccess('Asset has been deleted successfully.')
- }
- } else {
- throw new WIKI.Error.AssetInvalid()
- }
- } catch (err) {
- return graphHelper.generateError(err)
- }
- },
- /**
- * Upload Assets
- */
- async uploadAssets(obj, args, context) {
- try {
- const results = await Promise.allSettled(args.files.map(async fl => {
- const { filename, mimetype, createReadStream } = await fl
- WIKI.logger.debug(`Processing asset upload ${filename} of type ${mimetype}...`)
-
- if (!WIKI.extensions.ext.sharp.isInstalled) {
- throw new Error('This feature requires the Sharp extension but it is not installed.')
- }
- if (!['.png', '.jpg', 'webp', '.gif'].some(s => filename.endsWith(s))) {
- throw new Error('Invalid File Extension. Must be svg, png, jpg, webp or gif.')
- }
- const destFormat = mimetype.startsWith('image/svg') ? 'svg' : 'png'
- const destFolder = path.resolve(
- process.cwd(),
- WIKI.config.dataPath,
- `assets`
- )
- const destPath = path.join(destFolder, `logo-${args.id}.${destFormat}`)
- await fs.ensureDir(destFolder)
- // -> Resize
- await WIKI.extensions.ext.sharp.resize({
- format: destFormat,
- inputStream: createReadStream(),
- outputPath: destPath,
- height: 72
- })
- // -> Save logo meta to DB
- const site = await WIKI.db.sites.query().findById(args.id)
- if (!site.config.assets.logo) {
- site.config.assets.logo = uuid()
- }
- site.config.assets.logoExt = destFormat
- await WIKI.db.sites.query().findById(args.id).patch({ config: site.config })
- await WIKI.db.sites.reloadCache()
- // -> Save image data to DB
- const imgBuffer = await fs.readFile(destPath)
- await WIKI.db.knex('assetData').insert({
- id: site.config.assets.logo,
- data: imgBuffer
- }).onConflict('id').merge()
- }))
- WIKI.logger.debug('Asset(s) uploaded successfully.')
- return {
- operation: graphHelper.generateSuccess('Asset(s) uploaded successfully')
- }
- } catch (err) {
- WIKI.logger.warn(err)
- return graphHelper.generateError(err)
- }
- },
- /**
- * Flush Temporary Uploads
- */
- async flushTempUploads(obj, args, context) {
- try {
- await WIKI.db.assets.flushTempUploads()
- return {
- responseResult: graphHelper.generateSuccess('Temporary Uploads have been flushed successfully.')
- }
- } catch (err) {
- return graphHelper.generateError(err)
- }
- }
- }
- // File: {
- // folder(fl) {
- // return fl.getFolder()
- // }
- // }
- }
|