'use strict'; /** * Documentation.js controller * * @description: A set of functions called "actions" of the `documentation` plugin. */ // Core dependencies. const path = require('path'); // Public dependencies. const fs = require('fs-extra'); const cheerio = require('cheerio'); const _ = require('lodash'); const koaStatic = require('koa-static'); module.exports = { getInfos: async ctx => { try { const service = strapi.plugins.documentation.services.documentation; const docVersions = service.retrieveDocumentationVersions(); const form = await service.retrieveFrontForm(); ctx.send({ docVersions, currentVersion: service.getDocumentationVersion(), prefix: strapi.plugins.documentation.config['x-strapi-config'].path, form, }); } catch (err) { ctx.badRequest(null, err.message); } }, async index(ctx, next) { try { /** * We don't expose the specs using koa-static or something else due to security reasons. * That's why, we need to read the file localy and send the specs through it when we serve the Swagger UI. */ const { major, minor, patch } = ctx.params; const version = major && minor && patch ? `${major}.${minor}.${patch}` : strapi.plugins.documentation.config.info.version; const openAPISpecsPath = path.join( strapi.config.appPath, 'extensions', 'documentation', 'documentation', version, 'full_documentation.json' ); try { const documentation = fs.readFileSync(openAPISpecsPath, 'utf8'); const layout = fs.readFileSync( path.resolve(__dirname, '..', 'public', 'index.html'), 'utf8' ); const filledLayout = _.template(layout)({ backendUrl: strapi.config.server.url, spec: JSON.stringify(JSON.parse(documentation)), }); try { const layoutPath = path.resolve( strapi.config.appPath, 'extensions', 'documentation', 'public', 'index.html' ); await fs.ensureFile(layoutPath); await fs.writeFile(layoutPath, filledLayout); // Serve the file. ctx.url = path.basename(`${ctx.url}/index.html`); try { const staticFolder = path.resolve( strapi.config.appPath, 'extensions', 'documentation', 'public' ); return await koaStatic(staticFolder)(ctx, next); } catch (e) { strapi.log.error(e); } } catch (e) { strapi.log.error(e); } } catch (e) { strapi.log.error(e); } } catch (e) { strapi.log.error(e); } }, async loginView(ctx, next) { const { error } = ctx.query; try { const layout = fs.readFileSync(path.join(__dirname, '..', 'public', 'login.html')); const filledLayout = _.template(layout)({ actionUrl: `${strapi.config.server.url}${strapi.plugins.documentation.config['x-strapi-config'].path}/login`, }); const $ = cheerio.load(filledLayout); $('.error').text(_.isEmpty(error) ? '' : 'Wrong password...'); try { const layoutPath = path.resolve( strapi.config.appPath, 'extensions', 'documentation', 'public', 'login.html' ); await fs.ensureFile(layoutPath); await fs.writeFile(layoutPath, $.html()); ctx.url = path.basename(`${ctx.url}/login.html`); try { const staticFolder = path.resolve( strapi.config.appPath, 'extensions', 'documentation', 'public' ); return await koaStatic(staticFolder)(ctx, next); } catch (e) { strapi.log.error(e); } } catch (e) { strapi.log.error(e); } } catch (e) { strapi.log.error(e); } }, async login(ctx) { const { body: { password }, } = ctx.request; const { password: storedPassword } = await strapi .store({ environment: '', type: 'plugin', name: 'documentation', key: 'config', }) .get(); const isValid = await strapi.plugins['users-permissions'].services.user.validatePassword( password, storedPassword ); let querystring = '?error=password'; if (isValid) { ctx.session.documentation = password; querystring = ''; } ctx.redirect( `${strapi.config.server.url}${strapi.plugins.documentation.config['x-strapi-config'].path}${querystring}` ); }, regenerateDoc: async ctx => { const service = strapi.plugins.documentation.services.documentation; const documentationVersions = service.retrieveDocumentationVersions().map(el => el.version); const { request: { body: { version }, admin, }, } = ctx; if (_.isEmpty(version)) { return ctx.badRequest( null, admin ? 'documentation.error.noVersion' : 'Please provide a version.' ); } if (!documentationVersions.includes(version)) { return ctx.badRequest( null, admin ? 'documentation.error.regenerateDoc.versionMissing' : 'The version you are trying to generate does not exist.' ); } try { strapi.reload.isWatching = false; const fullDoc = service.generateFullDoc(version); const documentationPath = service.getMergedDocumentationPath(version); // Write the file fs.writeFileSync( path.resolve(documentationPath, 'full_documentation.json'), JSON.stringify(fullDoc, null, 2), 'utf8' ); ctx.send({ ok: true }); } catch (err) { ctx.badRequest(null, admin ? 'documentation.error.regenerateDoc' : 'An error occured'); } finally { strapi.reload.isWatching = true; } }, deleteDoc: async ctx => { strapi.reload.isWatching = false; const service = strapi.plugins.documentation.services.documentation; const documentationVersions = service.retrieveDocumentationVersions().map(el => el.version); const { request: { params: { version }, admin, }, } = ctx; if (_.isEmpty(version)) { return ctx.badRequest( null, admin ? 'documentation.error.noVersion' : 'Please provide a version.' ); } if (!documentationVersions.includes(version)) { return ctx.badRequest( null, admin ? 'documentation.error.deleteDoc.versionMissing' : 'The version you are trying to delete does not exist.' ); } try { await service.deleteDocumentation(version); ctx.send({ ok: true }); } catch (err) { ctx.badRequest(null, admin ? 'notification.error' : err.message); } finally { strapi.reload.isWatching = true; } }, updateSettings: async ctx => { const { admin, body: { restrictedAccess, password }, } = ctx.request; const usersPermService = strapi.plugins['users-permissions'].services; const pluginStore = strapi.store({ environment: '', type: 'plugin', name: 'documentation', }); const prevConfig = await pluginStore.get({ key: 'config' }); if (restrictedAccess && _.isEmpty(password)) { return ctx.badRequest( null, admin ? 'users-permissions.Auth.form.error.password.provide' : 'Please provide a password' ); } const isNewPassword = !_.isEmpty(password) && password !== prevConfig.password; if (isNewPassword && usersPermService.user.isHashed(password)) { // Throw an error if the password selected by the user // contains more than two times the symbol '$'. return ctx.badRequest( null, admin ? 'users-permissions.Auth.form.error.password.format' : 'our password cannot contain more than three times the symbol `$`.' ); } if (isNewPassword) { prevConfig.password = await usersPermService.user.hashPassword({ password, }); } _.set(prevConfig, 'restrictedAccess', restrictedAccess); await pluginStore.set({ key: 'config', value: prevConfig }); return ctx.send({ ok: true }); }, };