mirror of
				https://github.com/strapi/strapi.git
				synced 2025-10-31 09:56:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			242 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			242 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| '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(`
 | |
|           <script class="custom-swagger-ui">
 | |
|             window.onload = function() {
 | |
| 
 | |
|               // Build a system
 | |
|               const ui = SwaggerUIBundle({
 | |
|                 url: "https://petstore.swagger.io/v2/swagger.json",
 | |
|                 spec: ${JSON.stringify(JSON.parse(documentation))},
 | |
|                 dom_id: '#swagger-ui',
 | |
|                 docExpansion: "none",
 | |
|                 deepLinking: true,
 | |
|                 presets: [
 | |
|                   SwaggerUIBundle.presets.apis,
 | |
|                   SwaggerUIStandalonePreset
 | |
|                 ],
 | |
|                 plugins: [
 | |
|                   SwaggerUIBundle.plugins.DownloadUrl
 | |
|                 ],
 | |
|                 layout: "StandaloneLayout"
 | |
|               })
 | |
| 
 | |
|               window.ui = ui
 | |
|             }
 | |
|           </script>
 | |
|         `);
 | |
| 
 | |
|         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 });
 | |
|   },
 | |
| };
 | 
