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() 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 { status: graphHelper.generateSuccess('Site created successfully'), site: newSite } } catch (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 { status: 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 { status: 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 { status: 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 { status: graphHelper.generateSuccess('Site favicon uploaded successfully') } } } }