|
|
|
const _ = require('lodash')
|
|
|
|
const sanitize = require('sanitize-filename')
|
|
|
|
const graphHelper = require('../../helpers/graph')
|
|
|
|
const path = require('node:path')
|
|
|
|
const fs = require('fs-extra')
|
|
|
|
const { v4: uuid } = require('uuid')
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
Query: {
|
|
|
|
async assetById(obj, args, context) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Mutation: {
|
|
|
|
/**
|
|
|
|
* 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 {
|
|
|
|
// -> Get Folder
|
|
|
|
const folder = await WIKI.db.tree.query().findById(args.folderId)
|
|
|
|
if (!folder) {
|
|
|
|
throw new Error('ERR_INVALID_FOLDER_ID')
|
|
|
|
}
|
|
|
|
|
|
|
|
// -> Get Site
|
|
|
|
const site = await WIKI.db.sites.query().findById(folder.siteId)
|
|
|
|
if (!site) {
|
|
|
|
throw new Error('ERR_INVALID_SITE_ID')
|
|
|
|
}
|
|
|
|
|
|
|
|
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}...`)
|
|
|
|
|
|
|
|
// Format filename
|
|
|
|
const formattedFilename = ''
|
|
|
|
|
|
|
|
// Save asset to DB
|
|
|
|
const asset = await WIKI.db.knex('assets').insert({
|
|
|
|
|
|
|
|
}).returning('id')
|
|
|
|
|
|
|
|
// Add to tree
|
|
|
|
await WIKI.db.knex('tree').insert({
|
|
|
|
id: asset.id,
|
|
|
|
folderPath: folder.folderPath ? `${folder.folderPath}.${folder.fileName}` : folder.fileName,
|
|
|
|
fileName: formattedFilename,
|
|
|
|
type: 'asset',
|
|
|
|
localeCode: ''
|
|
|
|
})
|
|
|
|
|
|
|
|
// Create thumbnail
|
|
|
|
if (!['.png', '.jpg', 'webp', '.gif'].some(s => filename.endsWith(s))) {
|
|
|
|
if (!WIKI.extensions.ext.sharp.isInstalled) {
|
|
|
|
WIKI.logger.warn('Cannot generate asset thumbnail because the Sharp extension is not installed.')
|
|
|
|
} else {
|
|
|
|
const destFormat = mimetype.startsWith('image/svg') ? 'svg' : 'png'
|
|
|
|
const destFolder = path.resolve(
|
|
|
|
process.cwd(),
|
|
|
|
WIKI.config.dataPath,
|
|
|
|
`assets`
|
|
|
|
)
|
|
|
|
const destPath = path.join(destFolder, `asset-${site.id}-${hash}.${destFormat}`)
|
|
|
|
await fs.ensureDir(destFolder)
|
|
|
|
// -> Resize
|
|
|
|
await WIKI.extensions.ext.sharp.resize({
|
|
|
|
format: destFormat,
|
|
|
|
inputStream: createReadStream(),
|
|
|
|
outputPath: destPath,
|
|
|
|
height: 72
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -> 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|