You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wiki/server/graph/resolvers/site.js

183 lines
6.1 KiB

const graphHelper = require('../../helpers/graph')
const _ = require('lodash')
const CleanCSS = require('clean-css')
const path = require('path')
/* global WIKI */
module.exports = {
Query: {
async sites () {
const sites = await WIKI.models.sites.query().orderBy('hostname')
return sites.map(s => ({
...s.config,
id: s.id,
hostname: s.hostname,
isEnabled: s.isEnabled
}))
},
async siteById (obj, args) {
const site = await WIKI.models.sites.query().findById(args.id)
return site ? {
...site.config,
id: site.id,
hostname: site.hostname,
isEnabled: site.isEnabled
} : null
},
async siteByHostname (obj, args) {
let site = await WIKI.models.sites.query().where({
hostname: args.hostname
}).first()
if (!site && !args.exact) {
site = await WIKI.models.sites.query().where({
hostname: '*'
}).first()
}
return site ? {
...site.config,
id: site.id,
hostname: site.hostname,
isEnabled: site.isEnabled
} : null
}
},
Mutation: {
/**
* CREATE SITE
*/
async createSite (obj, args) {
try {
// -> Validate inputs
if (!args.hostname || args.hostname.length < 1 || !/^(\\*)|([a-z0-9\-.:]+)$/.test(args.hostname)) {
throw WIKI.ERROR(new Error('Invalid Site Hostname'), 'SiteCreateInvalidHostname')
}
if (!args.title || args.title.length < 1 || !/^[^<>"]+$/.test(args.title)) {
throw WIKI.ERROR(new Error('Invalid Site Title'), 'SiteCreateInvalidTitle')
}
// -> Check for duplicate catch-all
if (args.hostname === '*') {
const site = await WIKI.models.sites.query().where({
hostname: args.hostname
}).first()
if (site) {
throw WIKI.ERROR(new Error('A site with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.'), 'SiteCreateDuplicateCatchAll')
}
}
// -> Create site
const newSite = await WIKI.models.sites.createSite(args.hostname, {
title: args.title
})
return {
operation: graphHelper.generateSuccess('Site created successfully'),
site: newSite
}
} catch (err) {
WIKI.logger.warn(err)
return graphHelper.generateError(err)
}
},
/**
* UPDATE SITE
*/
async updateSite (obj, args) {
try {
// -> Load site
const site = await WIKI.models.sites.query().findById(args.id)
if (!site) {
throw WIKI.ERROR(new Error('Invalid Site ID'), 'SiteInvalidId')
}
// -> Check for bad input
if (_.has(args.patch, 'hostname') && _.trim(args.patch.hostname).length < 1) {
throw WIKI.ERROR(new Error('Hostname is invalid.'), 'SiteInvalidHostname')
}
// -> Check for duplicate catch-all
if (args.patch.hostname === '*' && site.hostname !== '*') {
const dupSite = await WIKI.models.sites.query().where({ hostname: '*' }).first()
if (dupSite) {
throw WIKI.ERROR(new Error(`Site ${dupSite.config.title} with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.`), 'SiteUpdateDuplicateCatchAll')
}
}
// -> Format Code
if (args.patch?.theme?.injectCSS) {
args.patch.theme.injectCSS = new CleanCSS({ inline: false }).minify(args.patch.theme.injectCSS).styles
}
// -> Update site
await WIKI.models.sites.updateSite(args.id, {
hostname: args.patch.hostname ?? site.hostname,
isEnabled: args.patch.isEnabled ?? site.isEnabled,
config: _.defaultsDeep(_.omit(args.patch, ['hostname', 'isEnabled']), site.config)
})
return {
operation: graphHelper.generateSuccess('Site updated successfully')
}
} catch (err) {
WIKI.logger.warn(err)
return graphHelper.generateError(err)
}
},
/**
* DELETE SITE
*/
async deleteSite (obj, args) {
try {
// -> Ensure site isn't last one
const sitesCount = await WIKI.models.sites.query().count('id').first()
if (sitesCount?.count && _.toNumber(sitesCount?.count) <= 1) {
throw WIKI.ERROR(new Error('Cannot delete the last site. At least 1 site must exists at all times.'), 'SiteDeleteLastSite')
}
// -> Delete site
await WIKI.models.sites.deleteSite(args.id)
return {
operation: graphHelper.generateSuccess('Site deleted successfully')
}
} catch (err) {
WIKI.logger.warn(err)
return graphHelper.generateError(err)
}
},
/**
* UPLOAD LOGO
*/
async uploadSiteLogo (obj, args) {
try {
const { filename, mimetype, createReadStream } = await args.image
WIKI.logger.info(`Processing site logo ${filename} of type ${mimetype}...`)
if (!WIKI.extensions.ext.sharp.isInstalled) {
throw new Error('This feature requires the Sharp extension but it is not installed.')
}
console.info(mimetype)
const destFormat = mimetype.startsWith('image/svg') ? 'svg' : 'png'
const destPath = path.resolve(
process.cwd(),
WIKI.config.dataPath,
`assets/logo.${destFormat}`
)
await WIKI.extensions.ext.sharp.resize({
format: destFormat,
inputStream: createReadStream(),
outputPath: destPath,
width: 100
})
WIKI.logger.info('New site logo processed successfully.')
return {
operation: graphHelper.generateSuccess('Site logo uploaded successfully')
}
} catch (err) {
return graphHelper.generateError(err)
}
},
/**
* UPLOAD FAVICON
*/
async uploadSiteFavicon (obj, args) {
const { filename, mimetype, createReadStream } = await args.image
console.info(filename, mimetype)
return {
operation: graphHelper.generateSuccess('Site favicon uploaded successfully')
}
}
}
}