'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'); module.exports = { getInfos: async (ctx) => { try { const prefix = _.get(strapi.plugins, ['documentation', 'config', 'x-strapi-config', 'path'], '/documentation'); const service = strapi.plugins.documentation.services.documentation; const docVersions = service.retrieveDocumentationVersions(); const form = await service.retrieveFrontForm(); ctx.send({ docVersions, currentVersion: service.getDocumentationVersion(), prefix: `/${prefix}`.replace('//', '/'), form }); } catch(err) { ctx.badRequest(null, err.message); } }, async index(ctx, next) { // Read layout file. try { const layout = fs.readFileSync(path.resolve(__dirname, '..', 'public', 'index.html'), 'utf8'); const $ = cheerio.load(layout); /** * 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'); // Remove previous Swagger configuration. $('.custom-swagger-ui').remove(); // Set new Swagger configuration $('body').append(` `); try { // Write the layout with the new Swagger configuration. // fs.writeFileSync(layoutPath, $.html()); const layoutPath = path.resolve(strapi.config.appPath, 'extensions', 'documentation', 'public', 'index.html'); await fs.ensureFile(layoutPath); await fs.writeFile(layoutPath, $.html()); // Serve the file. ctx.url = path.basename(`${ctx.url}/index.html`); try { const staticFolder = path.resolve(strapi.config.appPath, 'extensions', 'documentation', 'public'); return await strapi.koaMiddlewares.static(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 $ = cheerio.load(layout); $('form').attr('action', `${strapi.plugins.documentation.config['x-strapi-config'].path}/login`); $('.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 strapi.koaMiddlewares.static(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 = strapi.plugins['users-permissions'].services.user.validatePassword(password, storedPassword); let querystring = '?error=password'; if (isValid) { ctx.session.documentation = password; querystring = ''; } ctx.redirect(`${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 }); }, };