@strapi/plugin-documentation 4.0.0-next.6 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/admin/src/components/FieldActionWrapper/index.js +14 -0
  2. package/admin/src/components/PluginIcon/index.js +12 -0
  3. package/admin/src/index.js +27 -12
  4. package/admin/src/pages/PluginPage/index.js +199 -0
  5. package/admin/src/pages/PluginPage/tests/index.test.js +873 -0
  6. package/admin/src/pages/PluginPage/tests/server.js +23 -0
  7. package/admin/src/pages/SettingsPage/index.js +181 -0
  8. package/admin/src/pages/SettingsPage/tests/index.test.js +612 -0
  9. package/admin/src/pages/SettingsPage/tests/server.js +18 -0
  10. package/admin/src/pages/{HomePage/utils → utils}/api.js +5 -4
  11. package/admin/src/pages/{HomePage/utils → utils}/schema.js +0 -0
  12. package/admin/src/pages/utils/useReactQuery.js +46 -0
  13. package/admin/src/permissions.js +7 -7
  14. package/admin/src/translations/ar.json +0 -3
  15. package/admin/src/translations/cs.json +0 -3
  16. package/admin/src/translations/de.json +0 -3
  17. package/admin/src/translations/en.json +14 -3
  18. package/admin/src/translations/es.json +0 -3
  19. package/admin/src/translations/fr.json +0 -3
  20. package/admin/src/translations/id.json +0 -3
  21. package/admin/src/translations/it.json +0 -3
  22. package/admin/src/translations/ko.json +0 -3
  23. package/admin/src/translations/ms.json +0 -3
  24. package/admin/src/translations/nl.json +0 -3
  25. package/admin/src/translations/pl.json +0 -3
  26. package/admin/src/translations/pt-BR.json +0 -3
  27. package/admin/src/translations/pt.json +0 -3
  28. package/admin/src/translations/ru.json +0 -3
  29. package/admin/src/translations/sk.json +0 -3
  30. package/admin/src/translations/th.json +0 -3
  31. package/admin/src/translations/tr.json +0 -3
  32. package/admin/src/translations/uk.json +0 -3
  33. package/admin/src/translations/vi.json +0 -3
  34. package/admin/src/translations/zh-Hans.json +3 -6
  35. package/admin/src/translations/zh.json +0 -3
  36. package/package.json +32 -45
  37. package/server/bootstrap.js +56 -0
  38. package/server/config/default-config.js +45 -0
  39. package/server/config/index.js +16 -0
  40. package/server/controllers/documentation.js +240 -0
  41. package/server/controllers/index.js +7 -0
  42. package/server/index.js +17 -0
  43. package/server/middlewares/documentation.js +30 -0
  44. package/server/middlewares/index.js +7 -0
  45. package/server/middlewares/restrict-access.js +24 -0
  46. package/{public → server/public}/index.html +0 -0
  47. package/{public → server/public}/login.html +0 -0
  48. package/server/register.js +11 -0
  49. package/server/routes/index.js +83 -0
  50. package/server/services/documentation.js +155 -0
  51. package/server/services/index.js +7 -0
  52. package/{services → server/services}/utils/components.json +0 -0
  53. package/{services → server/services}/utils/parametersOptions.json +0 -0
  54. package/{services → server/services}/utils/unknownComponent.json +0 -0
  55. package/server/utils/builders/build-api-endpoint-path.js +174 -0
  56. package/server/utils/builders/build-api-requests.js +41 -0
  57. package/server/utils/builders/build-api-responses.js +108 -0
  58. package/server/utils/builders/index.js +11 -0
  59. package/server/utils/clean-schema-attributes.js +205 -0
  60. package/server/utils/error-response.js +22 -0
  61. package/server/utils/get-schema-data.js +32 -0
  62. package/server/utils/query-params.js +84 -0
  63. package/strapi-admin.js +3 -0
  64. package/strapi-server.js +3 -0
  65. package/admin/src/assets/images/logo.svg +0 -1
  66. package/admin/src/components/Block/components.js +0 -26
  67. package/admin/src/components/Block/index.js +0 -39
  68. package/admin/src/components/Copy/index.js +0 -36
  69. package/admin/src/components/Header/index.js +0 -72
  70. package/admin/src/components/Row/ButtonContainer.js +0 -67
  71. package/admin/src/components/Row/components.js +0 -83
  72. package/admin/src/components/Row/index.js +0 -51
  73. package/admin/src/pages/App/index.js +0 -21
  74. package/admin/src/pages/HomePage/components.js +0 -59
  75. package/admin/src/pages/HomePage/index.js +0 -168
  76. package/admin/src/pages/HomePage/useHomePage.js +0 -56
  77. package/config/functions/bootstrap.js +0 -142
  78. package/config/policies/index.js +0 -33
  79. package/config/routes.json +0 -74
  80. package/config/settings.json +0 -46
  81. package/controllers/Documentation.js +0 -302
  82. package/middlewares/documentation/defaults.json +0 -5
  83. package/middlewares/documentation/index.js +0 -59
  84. package/services/Documentation.js +0 -1863
  85. package/services/utils/forms.json +0 -29
@@ -0,0 +1,240 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Documentation.js controller
5
+ *
6
+ * @description: A set of functions called "actions" of the `documentation` plugin.
7
+ */
8
+
9
+ // Core dependencies.
10
+ const path = require('path');
11
+ const bcrypt = require('bcryptjs');
12
+
13
+ // Public dependencies.
14
+ const fs = require('fs-extra');
15
+ const _ = require('lodash');
16
+ const koaStatic = require('koa-static');
17
+
18
+ module.exports = {
19
+ async getInfos(ctx) {
20
+ try {
21
+ const docService = strapi.plugin('documentation').service('documentation');
22
+ const docVersions = docService.getDocumentationVersions();
23
+ const documentationAccess = await docService.getDocumentationAccess();
24
+
25
+ ctx.send({
26
+ docVersions,
27
+ currentVersion: docService.getDocumentationVersion(),
28
+ prefix: strapi.plugin('documentation').config('x-strapi-config').path,
29
+ documentationAccess,
30
+ });
31
+ } catch (err) {
32
+ ctx.badRequest(null, err.message);
33
+ }
34
+ },
35
+
36
+ async index(ctx, next) {
37
+ try {
38
+ /**
39
+ * We don't expose the specs using koa-static or something else due to security reasons.
40
+ * That's why, we need to read the file localy and send the specs through it when we serve the Swagger UI.
41
+ */
42
+ const { major, minor, patch } = ctx.params;
43
+ const version =
44
+ major && minor && patch
45
+ ? `${major}.${minor}.${patch}`
46
+ : strapi
47
+ .plugin('documentation')
48
+ .service('documentation')
49
+ .getDocumentationVersion();
50
+
51
+ const openAPISpecsPath = path.join(
52
+ strapi.dirs.extensions,
53
+ 'documentation',
54
+ 'documentation',
55
+ version,
56
+ 'full_documentation.json'
57
+ );
58
+
59
+ try {
60
+ const documentation = fs.readFileSync(openAPISpecsPath, 'utf8');
61
+ const layout = fs.readFileSync(
62
+ path.resolve(__dirname, '..', 'public', 'index.html'),
63
+ 'utf8'
64
+ );
65
+ const filledLayout = _.template(layout)({
66
+ backendUrl: strapi.config.server.url,
67
+ spec: JSON.stringify(JSON.parse(documentation)),
68
+ });
69
+
70
+ try {
71
+ const layoutPath = path.resolve(
72
+ strapi.dirs.extensions,
73
+ 'documentation',
74
+ 'public',
75
+ 'index.html'
76
+ );
77
+ await fs.ensureFile(layoutPath);
78
+ await fs.writeFile(layoutPath, filledLayout);
79
+
80
+ // Serve the file.
81
+ ctx.url = path.basename(`${ctx.url}/index.html`);
82
+
83
+ try {
84
+ const staticFolder = path.resolve(strapi.dirs.extensions, 'documentation', 'public');
85
+ return koaStatic(staticFolder)(ctx, next);
86
+ } catch (e) {
87
+ strapi.log.error(e);
88
+ }
89
+ } catch (e) {
90
+ strapi.log.error(e);
91
+ }
92
+ } catch (e) {
93
+ strapi.log.error(e);
94
+ }
95
+ } catch (e) {
96
+ strapi.log.error(e);
97
+ }
98
+ },
99
+
100
+ async loginView(ctx, next) {
101
+ // lazy require cheerio
102
+ const cheerio = require('cheerio');
103
+
104
+ const { error } = ctx.query;
105
+
106
+ try {
107
+ const layout = fs.readFileSync(path.join(__dirname, '..', 'public', 'login.html'));
108
+ const filledLayout = _.template(layout)({
109
+ actionUrl: `${strapi.config.server.url}${
110
+ strapi.config.get('plugin.documentation.x-strapi-config').path
111
+ }/login`,
112
+ });
113
+ const $ = cheerio.load(filledLayout);
114
+
115
+ $('.error').text(_.isEmpty(error) ? '' : 'Wrong password...');
116
+
117
+ try {
118
+ const layoutPath = path.resolve(
119
+ strapi.dirs.extensions,
120
+ 'documentation',
121
+ 'public',
122
+ 'login.html'
123
+ );
124
+ await fs.ensureFile(layoutPath);
125
+ await fs.writeFile(layoutPath, $.html());
126
+
127
+ ctx.url = path.basename(`${ctx.url}/login.html`);
128
+
129
+ try {
130
+ const staticFolder = path.resolve(strapi.dirs.extensions, 'documentation', 'public');
131
+ return koaStatic(staticFolder)(ctx, next);
132
+ } catch (e) {
133
+ strapi.log.error(e);
134
+ }
135
+ } catch (e) {
136
+ strapi.log.error(e);
137
+ }
138
+ } catch (e) {
139
+ strapi.log.error(e);
140
+ }
141
+ },
142
+
143
+ async login(ctx) {
144
+ const {
145
+ body: { password },
146
+ } = ctx.request;
147
+
148
+ const { password: hash } = await strapi
149
+ .store({ type: 'plugin', name: 'documentation', key: 'config' })
150
+ .get();
151
+
152
+ const isValid = await bcrypt.compare(password, hash);
153
+
154
+ let querystring = '?error=password';
155
+
156
+ if (isValid) {
157
+ ctx.session.documentation = {
158
+ logged: true,
159
+ };
160
+
161
+ querystring = '';
162
+ }
163
+
164
+ ctx.redirect(
165
+ `${strapi.config.server.url}${
166
+ strapi.config.get('plugin.documentation.x-strapi-config').path
167
+ }${querystring}`
168
+ );
169
+ },
170
+
171
+ async regenerateDoc(ctx) {
172
+ const { version } = ctx.request.body;
173
+
174
+ const service = strapi.service('plugin::documentation.documentation');
175
+
176
+ const documentationVersions = service.getDocumentationVersions().map(el => el.version);
177
+
178
+ if (_.isEmpty(version)) {
179
+ return ctx.badRequest('Please provide a version.');
180
+ }
181
+
182
+ if (!documentationVersions.includes(version)) {
183
+ return ctx.badRequest('The version you are trying to generate does not exist.');
184
+ }
185
+
186
+ try {
187
+ strapi.reload.isWatching = false;
188
+ await service.generateFullDoc(version);
189
+ ctx.send({ ok: true });
190
+ } finally {
191
+ strapi.reload.isWatching = true;
192
+ }
193
+ },
194
+
195
+ async deleteDoc(ctx) {
196
+ const { version } = ctx.params;
197
+
198
+ const service = strapi.service('plugin::documentation.documentation');
199
+
200
+ const documentationVersions = service.getDocumentationVersions().map(el => el.version);
201
+
202
+ if (_.isEmpty(version)) {
203
+ return ctx.badRequest('Please provide a version.');
204
+ }
205
+
206
+ if (!documentationVersions.includes(version)) {
207
+ return ctx.badRequest('The version you are trying to delete does not exist.');
208
+ }
209
+
210
+ try {
211
+ strapi.reload.isWatching = false;
212
+ await service.deleteDocumentation(version);
213
+ ctx.send({ ok: true });
214
+ } finally {
215
+ strapi.reload.isWatching = true;
216
+ }
217
+ },
218
+
219
+ async updateSettings(ctx) {
220
+ const { restrictedAccess, password } = ctx.request.body;
221
+
222
+ const pluginStore = strapi.store({ type: 'plugin', name: 'documentation' });
223
+
224
+ const config = {
225
+ restrictedAccess: Boolean(restrictedAccess),
226
+ };
227
+
228
+ if (restrictedAccess) {
229
+ if (_.isEmpty(password)) {
230
+ return ctx.badRequest('Please provide a password');
231
+ }
232
+
233
+ config.password = await bcrypt.hash(password, 10);
234
+ }
235
+
236
+ await pluginStore.set({ key: 'config', value: config });
237
+
238
+ return ctx.send({ ok: true });
239
+ },
240
+ };
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const documentation = require('./documentation');
4
+
5
+ module.exports = {
6
+ documentation,
7
+ };
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const bootstrap = require('./bootstrap');
4
+ const services = require('./services');
5
+ const routes = require('./routes');
6
+ const controllers = require('./controllers');
7
+ const register = require('./register');
8
+ const config = require('./config');
9
+
10
+ module.exports = () => ({
11
+ bootstrap,
12
+ config,
13
+ routes,
14
+ controllers,
15
+ register,
16
+ services,
17
+ });
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const koaStatic = require('koa-static');
5
+ const session = require('koa-session');
6
+ const swaggerUi = require('swagger-ui-dist');
7
+
8
+ module.exports = async ({ strapi }) => {
9
+ const sessionConfig = strapi.config.get('plugin.documentation').session;
10
+ strapi.server.app.keys = sessionConfig.secretKeys;
11
+ strapi.server.app.use(session(sessionConfig, strapi.server.app));
12
+
13
+ strapi.server.routes([
14
+ {
15
+ method: 'GET',
16
+ path: '/plugins/documentation/(.*)',
17
+ async handler(ctx, next) {
18
+ ctx.url = path.basename(ctx.url);
19
+
20
+ return koaStatic(swaggerUi.getAbsoluteFSPath(), {
21
+ maxage: 86400000,
22
+ defer: true,
23
+ })(ctx, next);
24
+ },
25
+ config: {
26
+ auth: false,
27
+ },
28
+ },
29
+ ]);
30
+ };
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const documentation = require('./documentation');
4
+
5
+ module.exports = {
6
+ documentation,
7
+ };
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ module.exports = async (ctx, next) => {
4
+ const pluginStore = strapi.store({ type: 'plugin', name: 'documentation' });
5
+
6
+ const config = await pluginStore.get({ key: 'config' });
7
+
8
+ if (!config.restrictedAccess) {
9
+ return next();
10
+ }
11
+
12
+ if (!ctx.session.documentation || !ctx.session.documentation.logged) {
13
+ const querystring = ctx.querystring ? `?${ctx.querystring}` : '';
14
+
15
+ return ctx.redirect(
16
+ `${strapi.config.server.url}${
17
+ strapi.config.get('plugin.documentation.x-strapi-config').path
18
+ }/login${querystring}`
19
+ );
20
+ }
21
+
22
+ // Execute the action.
23
+ return next();
24
+ };
File without changes
File without changes
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const registerDocumentationMiddleWare = require('./middlewares/documentation');
4
+
5
+ /**
6
+ * Register upload plugin
7
+ * @param {{ strapi: import('@strapi/strapi').Strapi }}
8
+ */
9
+ module.exports = async ({ strapi }) => {
10
+ await registerDocumentationMiddleWare({ strapi });
11
+ };
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+ const restrictAccess = require('../middlewares/restrict-access');
3
+
4
+ module.exports = [
5
+ {
6
+ method: 'GET',
7
+ path: '/',
8
+ handler: 'documentation.index',
9
+ config: {
10
+ auth: false,
11
+ middlewares: [restrictAccess],
12
+ },
13
+ },
14
+ {
15
+ method: 'GET',
16
+ path: '/v:major(\\d+).:minor(\\d+).:patch(\\d+)',
17
+ handler: 'documentation.index',
18
+ config: {
19
+ auth: false,
20
+ middlewares: [restrictAccess],
21
+ },
22
+ },
23
+ {
24
+ method: 'GET',
25
+ path: '/login',
26
+ handler: 'documentation.loginView',
27
+ config: {
28
+ auth: false,
29
+ },
30
+ },
31
+ {
32
+ method: 'POST',
33
+ path: '/login',
34
+ handler: 'documentation.login',
35
+ config: {
36
+ auth: false,
37
+ },
38
+ },
39
+ {
40
+ method: 'GET',
41
+ path: '/getInfos',
42
+ handler: 'documentation.getInfos',
43
+ config: {
44
+ policies: [
45
+ { name: 'admin::hasPermissions', config: { actions: ['plugin::documentation.read'] } },
46
+ ],
47
+ },
48
+ },
49
+ {
50
+ method: 'POST',
51
+ path: '/regenerateDoc',
52
+ handler: 'documentation.regenerateDoc',
53
+ config: {
54
+ policies: [
55
+ {
56
+ name: 'admin::hasPermissions',
57
+ config: { actions: ['plugin::documentation.settings.regenerate'] },
58
+ },
59
+ ],
60
+ },
61
+ },
62
+ {
63
+ method: 'PUT',
64
+ path: '/updateSettings',
65
+ handler: 'documentation.updateSettings',
66
+ config: {
67
+ policies: [
68
+ {
69
+ name: 'admin::hasPermissions',
70
+ config: { actions: ['plugin::documentation.settings.update'] },
71
+ },
72
+ ],
73
+ },
74
+ },
75
+ {
76
+ method: 'DELETE',
77
+ path: '/deleteDoc/:version',
78
+ handler: 'documentation.deleteDoc',
79
+ config: {
80
+ policies: [],
81
+ },
82
+ },
83
+ ];
@@ -0,0 +1,155 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const fs = require('fs-extra');
5
+ const _ = require('lodash');
6
+ const { getAbsoluteServerUrl } = require('@strapi/utils');
7
+
8
+ const { builApiEndpointPath } = require('../utils/builders');
9
+ const defaultConfig = require('../config/default-config');
10
+
11
+ module.exports = ({ strapi }) => {
12
+ const config = strapi.config.get('plugin.documentation');
13
+
14
+ return {
15
+ getDocumentationVersion() {
16
+ return _.get(config, 'info.version');
17
+ },
18
+
19
+ getFullDocumentationPath() {
20
+ return path.join(strapi.dirs.extensions, 'documentation', 'documentation');
21
+ },
22
+
23
+ getDocumentationVersions() {
24
+ return fs
25
+ .readdirSync(this.getFullDocumentationPath())
26
+ .map(version => {
27
+ try {
28
+ const doc = JSON.parse(
29
+ fs.readFileSync(
30
+ path.resolve(this.getFullDocumentationPath(), version, 'full_documentation.json')
31
+ )
32
+ );
33
+ const generatedDate = _.get(doc, ['info', 'x-generation-date'], null);
34
+
35
+ return { version, generatedDate, url: '' };
36
+ } catch (err) {
37
+ return null;
38
+ }
39
+ })
40
+ .filter(x => x);
41
+ },
42
+
43
+ /**
44
+ * Returns settings stored in core-store
45
+ */
46
+ async getDocumentationAccess() {
47
+ const { restrictedAccess } = await strapi
48
+ .store({
49
+ environment: '',
50
+ type: 'plugin',
51
+ name: 'documentation',
52
+ key: 'config',
53
+ })
54
+ .get();
55
+
56
+ return { restrictedAccess };
57
+ },
58
+
59
+ /**
60
+ * @description - Gets the path for an api or plugin
61
+ *
62
+ * @param {object} api
63
+ * @property {string} api.name - Name of the api
64
+ * @property {string} api.getter - api | plugin
65
+ *
66
+ * @returns path to the api | plugin
67
+ */
68
+ getApiDocumentationPath(api) {
69
+ if (api.getter === 'plugin') {
70
+ return path.join(strapi.dirs.extensions, api.name, 'documentation');
71
+ }
72
+
73
+ return path.join(strapi.dirs.api, api.name, 'documentation');
74
+ },
75
+
76
+ async deleteDocumentation(version) {
77
+ const apis = this.getPluginAndApiInfo();
78
+ for (const api of apis) {
79
+ await fs.remove(path.join(this.getApiDocumentationPath(api), version));
80
+ }
81
+
82
+ await fs.remove(path.join(this.getFullDocumentationPath(), version));
83
+ },
84
+
85
+ getPluginAndApiInfo() {
86
+ const plugins = _.get(config, 'x-strapi-config.plugins');
87
+ const pluginsToDocument = plugins.map(plugin => {
88
+ return {
89
+ name: plugin,
90
+ getter: 'plugin',
91
+ ctNames: Object.keys(strapi.plugin(plugin).contentTypes),
92
+ };
93
+ });
94
+
95
+ const apisToDocument = Object.keys(strapi.api).map(api => {
96
+ return {
97
+ name: api,
98
+ getter: 'api',
99
+ ctNames: Object.keys(strapi.api[api].contentTypes),
100
+ };
101
+ });
102
+
103
+ return [...apisToDocument, ...pluginsToDocument];
104
+ },
105
+
106
+ /**
107
+ * @description - Creates the Swagger json files
108
+ */
109
+ async generateFullDoc(version = this.getDocumentationVersion()) {
110
+ let paths = {};
111
+
112
+ const apis = this.getPluginAndApiInfo();
113
+ for (const api of apis) {
114
+ const apiName = api.name;
115
+ const apiDirPath = path.join(this.getApiDocumentationPath(api), version);
116
+
117
+ const apiDocPath = path.join(apiDirPath, `${apiName}.json`);
118
+ const apiPathsObject = builApiEndpointPath(api);
119
+
120
+ if (!apiPathsObject) {
121
+ continue;
122
+ }
123
+
124
+ await fs.ensureFile(apiDocPath);
125
+ await fs.writeJson(apiDocPath, apiPathsObject, { spaces: 2 });
126
+
127
+ paths = { ...paths, ...apiPathsObject.paths };
128
+ }
129
+
130
+ const fullDocJsonPath = path.join(
131
+ this.getFullDocumentationPath(),
132
+ version,
133
+ 'full_documentation.json'
134
+ );
135
+
136
+ const settings = _.cloneDeep(defaultConfig);
137
+
138
+ const serverUrl = getAbsoluteServerUrl(strapi.config);
139
+ const apiPath = strapi.config.get('api.rest.prefix');
140
+
141
+ _.set(settings, 'servers', [
142
+ {
143
+ url: `${serverUrl}${apiPath}`,
144
+ description: 'Development server',
145
+ },
146
+ ]);
147
+
148
+ _.set(settings, ['info', 'x-generation-date'], new Date().toISOString());
149
+ _.set(settings, ['info', 'version'], version);
150
+
151
+ await fs.ensureFile(fullDocJsonPath);
152
+ await fs.writeJson(fullDocJsonPath, { ...settings, paths }, { spaces: 2 });
153
+ },
154
+ };
155
+ };
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const documentation = require('./documentation');
4
+
5
+ module.exports = {
6
+ documentation,
7
+ };