@strapi/strapi 4.0.0-beta.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.
- package/LICENSE +22 -0
- package/README.md +144 -0
- package/bin/strapi.js +186 -0
- package/lib/Strapi.js +470 -0
- package/lib/commands/admin-reset.js +51 -0
- package/lib/commands/build.js +56 -0
- package/lib/commands/configurationDump.js +50 -0
- package/lib/commands/configurationRestore.js +169 -0
- package/lib/commands/console.js +26 -0
- package/lib/commands/develop.js +157 -0
- package/lib/commands/generate-template.js +97 -0
- package/lib/commands/install.js +48 -0
- package/lib/commands/new.js +11 -0
- package/lib/commands/start.js +8 -0
- package/lib/commands/uninstall.js +68 -0
- package/lib/commands/watchAdmin.js +45 -0
- package/lib/container.js +45 -0
- package/lib/core/app-configuration/config-loader.js +20 -0
- package/lib/core/app-configuration/index.js +75 -0
- package/lib/core/app-configuration/load-config-file.js +43 -0
- package/lib/core/app-configuration/load-functions.js +28 -0
- package/lib/core/bootstrap.js +60 -0
- package/lib/core/domain/component/index.js +24 -0
- package/lib/core/domain/component/validator.js +29 -0
- package/lib/core/domain/content-type/index.js +140 -0
- package/lib/core/domain/content-type/validator.js +64 -0
- package/lib/core/domain/module/index.js +106 -0
- package/lib/core/domain/module/validation.js +36 -0
- package/lib/core/loaders/admin.js +16 -0
- package/lib/core/loaders/apis.js +157 -0
- package/lib/core/loaders/components.js +41 -0
- package/lib/core/loaders/index.js +11 -0
- package/lib/core/loaders/middlewares.js +86 -0
- package/lib/core/loaders/plugins/get-enabled-plugins.js +100 -0
- package/lib/core/loaders/plugins/index.js +109 -0
- package/lib/core/loaders/policies.js +28 -0
- package/lib/core/loaders/src-index.js +38 -0
- package/lib/core/registries/apis.js +43 -0
- package/lib/core/registries/config.js +21 -0
- package/lib/core/registries/content-types.js +53 -0
- package/lib/core/registries/controllers.js +43 -0
- package/lib/core/registries/hooks.js +37 -0
- package/lib/core/registries/middlewares.js +30 -0
- package/lib/core/registries/modules.js +44 -0
- package/lib/core/registries/plugins.js +28 -0
- package/lib/core/registries/policies.js +38 -0
- package/lib/core/registries/services.js +58 -0
- package/lib/core/utils.js +35 -0
- package/lib/core-api/controller/collection-type.js +84 -0
- package/lib/core-api/controller/index.js +26 -0
- package/lib/core-api/controller/single-type.js +44 -0
- package/lib/core-api/controller/transform.js +97 -0
- package/lib/core-api/index.js +39 -0
- package/lib/core-api/service/collection-type.js +84 -0
- package/lib/core-api/service/index.js +55 -0
- package/lib/core-api/service/pagination.js +125 -0
- package/lib/core-api/service/single-type.js +58 -0
- package/lib/index.d.ts +26 -0
- package/lib/index.js +3 -0
- package/lib/load/filepath-to-prop-path.js +22 -0
- package/lib/load/glob.js +15 -0
- package/lib/load/index.js +9 -0
- package/lib/load/load-files.js +56 -0
- package/lib/load/package-path.js +9 -0
- package/lib/middlewares/cors/index.js +66 -0
- package/lib/middlewares/error/defaults.json +5 -0
- package/lib/middlewares/error/index.js +147 -0
- package/lib/middlewares/favicon/defaults.json +7 -0
- package/lib/middlewares/favicon/index.js +31 -0
- package/lib/middlewares/gzip/defaults.json +6 -0
- package/lib/middlewares/gzip/index.js +19 -0
- package/lib/middlewares/helmet/defaults.json +18 -0
- package/lib/middlewares/helmet/index.js +9 -0
- package/lib/middlewares/index.js +120 -0
- package/lib/middlewares/ip/defaults.json +7 -0
- package/lib/middlewares/ip/index.js +25 -0
- package/lib/middlewares/logger/defaults.json +5 -0
- package/lib/middlewares/logger/index.js +37 -0
- package/lib/middlewares/parser/defaults.json +11 -0
- package/lib/middlewares/parser/index.js +75 -0
- package/lib/middlewares/poweredBy/defaults.json +5 -0
- package/lib/middlewares/poweredBy/index.js +16 -0
- package/lib/middlewares/public/assets/images/group_people_1.png +0 -0
- package/lib/middlewares/public/assets/images/group_people_2.png +0 -0
- package/lib/middlewares/public/assets/images/group_people_3.png +0 -0
- package/lib/middlewares/public/assets/images/logo_login.png +0 -0
- package/lib/middlewares/public/defaults.json +8 -0
- package/lib/middlewares/public/index.html +66 -0
- package/lib/middlewares/public/index.js +130 -0
- package/lib/middlewares/public/serve-static.js +23 -0
- package/lib/middlewares/responseTime/defaults.json +5 -0
- package/lib/middlewares/responseTime/index.js +25 -0
- package/lib/middlewares/responses/defaults.json +5 -0
- package/lib/middlewares/responses/index.js +19 -0
- package/lib/middlewares/router/defaults.json +7 -0
- package/lib/middlewares/router/index.js +97 -0
- package/lib/middlewares/session/defaults.json +18 -0
- package/lib/middlewares/session/index.js +140 -0
- package/lib/migrations/draft-publish.js +57 -0
- package/lib/services/auth/index.js +92 -0
- package/lib/services/core-store.js +145 -0
- package/lib/services/cron.js +54 -0
- package/lib/services/entity-service/components.js +365 -0
- package/lib/services/entity-service/index.d.ts +91 -0
- package/lib/services/entity-service/index.js +244 -0
- package/lib/services/entity-service/params.js +145 -0
- package/lib/services/entity-validator/index.js +187 -0
- package/lib/services/entity-validator/validators.js +123 -0
- package/lib/services/event-hub.js +15 -0
- package/lib/services/fs.js +58 -0
- package/lib/services/metrics/index.js +104 -0
- package/lib/services/metrics/is-truthy.js +9 -0
- package/lib/services/metrics/middleware.js +33 -0
- package/lib/services/metrics/rate-limiter.js +27 -0
- package/lib/services/metrics/sender.js +76 -0
- package/lib/services/metrics/stringify-deep.js +22 -0
- package/lib/services/server/admin-api.js +14 -0
- package/lib/services/server/api.js +32 -0
- package/lib/services/server/compose-endpoint.js +112 -0
- package/lib/services/server/content-api.js +16 -0
- package/lib/services/server/http-server.js +64 -0
- package/lib/services/server/index.js +108 -0
- package/lib/services/server/middleware.js +28 -0
- package/lib/services/server/policy.js +34 -0
- package/lib/services/server/routing.js +107 -0
- package/lib/services/utils/upload-files.js +79 -0
- package/lib/services/webhook-runner.js +155 -0
- package/lib/services/webhook-store.js +91 -0
- package/lib/services/worker-queue.js +58 -0
- package/lib/utils/addSlash.js +10 -0
- package/lib/utils/ee.js +123 -0
- package/lib/utils/get-dirs.js +15 -0
- package/lib/utils/get-prefixed-dependencies.js +7 -0
- package/lib/utils/index.js +11 -0
- package/lib/utils/is-initialized.js +23 -0
- package/lib/utils/open-browser.js +12 -0
- package/lib/utils/resources/key.pub +9 -0
- package/lib/utils/run-checks.js +22 -0
- package/lib/utils/startup-logger.js +90 -0
- package/lib/utils/success.js +31 -0
- package/lib/utils/update-notifier/index.js +97 -0
- package/lib/utils/url-from-segments.js +12 -0
- package/package.json +133 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The event hub is Strapi's event control center.
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const debug = require('debug')('strapi:webhook');
|
|
7
|
+
const _ = require('lodash');
|
|
8
|
+
const fetch = require('node-fetch');
|
|
9
|
+
|
|
10
|
+
const WorkerQueue = require('./worker-queue');
|
|
11
|
+
|
|
12
|
+
const defaultConfiguration = {
|
|
13
|
+
defaultHeaders: {},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
class WebhookRunner {
|
|
17
|
+
constructor({ eventHub, logger, configuration = {} }) {
|
|
18
|
+
debug('Initialized webhook runer');
|
|
19
|
+
this.eventHub = eventHub;
|
|
20
|
+
this.logger = logger;
|
|
21
|
+
this.webhooksMap = new Map();
|
|
22
|
+
this.listeners = new Map();
|
|
23
|
+
|
|
24
|
+
if (typeof configuration !== 'object') {
|
|
25
|
+
throw new Error(
|
|
26
|
+
'Invalid configuration provided to the webhookRunner.\nCheck your server.json -> webhooks configuration'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.config = _.merge(defaultConfiguration, configuration);
|
|
31
|
+
|
|
32
|
+
this.queue = new WorkerQueue({ logger, concurency: 5 });
|
|
33
|
+
this.queue.subscribe(this.executeListener.bind(this));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
deleteListener(event) {
|
|
37
|
+
debug(`Deleting listener for event '${event}'`);
|
|
38
|
+
if (this.listeners.has(event)) {
|
|
39
|
+
const fn = this.listeners.get(event);
|
|
40
|
+
|
|
41
|
+
this.eventHub.off(event, fn);
|
|
42
|
+
this.listeners.delete(event);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
createListener(event) {
|
|
47
|
+
debug(`Creating listener for event '${event}'`);
|
|
48
|
+
if (this.listeners.has(event)) {
|
|
49
|
+
this.logger.error(
|
|
50
|
+
`The webhook runner is already listening for the event '${event}'. Did you mean to call .register() ?`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const listen = info => {
|
|
55
|
+
this.queue.enqueue({ event, info });
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
this.listeners.set(event, listen);
|
|
59
|
+
this.eventHub.on(event, listen);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async executeListener({ event, info }) {
|
|
63
|
+
debug(`Executing webhook for event '${event}'`);
|
|
64
|
+
const webhooks = this.webhooksMap.get(event) || [];
|
|
65
|
+
const activeWebhooks = webhooks.filter(webhook => webhook.isEnabled === true);
|
|
66
|
+
|
|
67
|
+
for (const webhook of activeWebhooks) {
|
|
68
|
+
await this.run(webhook, event, info).catch(error => {
|
|
69
|
+
this.logger.error('Error running webhook');
|
|
70
|
+
this.logger.error(error);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
run(webhook, event, info = {}) {
|
|
76
|
+
const { url, headers } = webhook;
|
|
77
|
+
|
|
78
|
+
return fetch(url, {
|
|
79
|
+
method: 'post',
|
|
80
|
+
body: JSON.stringify({
|
|
81
|
+
event,
|
|
82
|
+
createdAt: new Date(),
|
|
83
|
+
...info,
|
|
84
|
+
}),
|
|
85
|
+
headers: {
|
|
86
|
+
...this.config.defaultHeaders,
|
|
87
|
+
...headers,
|
|
88
|
+
'X-Strapi-Event': event,
|
|
89
|
+
'Content-Type': 'application/json',
|
|
90
|
+
},
|
|
91
|
+
timeout: 10000,
|
|
92
|
+
})
|
|
93
|
+
.then(async res => {
|
|
94
|
+
if (res.ok) {
|
|
95
|
+
return {
|
|
96
|
+
statusCode: res.status,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
statusCode: res.status,
|
|
102
|
+
message: await res.text(),
|
|
103
|
+
};
|
|
104
|
+
})
|
|
105
|
+
.catch(err => {
|
|
106
|
+
return {
|
|
107
|
+
statusCode: 500,
|
|
108
|
+
message: err.message,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
add(webhook) {
|
|
114
|
+
debug(`Registering webhook '${webhook.id}'`);
|
|
115
|
+
const { events } = webhook;
|
|
116
|
+
|
|
117
|
+
events.forEach(event => {
|
|
118
|
+
if (this.webhooksMap.has(event)) {
|
|
119
|
+
this.webhooksMap.get(event).push(webhook);
|
|
120
|
+
} else {
|
|
121
|
+
this.webhooksMap.set(event, [webhook]);
|
|
122
|
+
this.createListener(event);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
update(webhook) {
|
|
128
|
+
debug(`Refreshing webhook '${webhook.id}'`);
|
|
129
|
+
this.remove(webhook);
|
|
130
|
+
this.add(webhook);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
remove(webhook) {
|
|
134
|
+
debug(`Unregistering webhook '${webhook.id}'`);
|
|
135
|
+
|
|
136
|
+
this.webhooksMap.forEach((webhooks, event) => {
|
|
137
|
+
const filteredWebhooks = webhooks.filter(value => value.id !== webhook.id);
|
|
138
|
+
|
|
139
|
+
// Cleanup hanging listeners
|
|
140
|
+
if (filteredWebhooks.length === 0) {
|
|
141
|
+
this.webhooksMap.delete(event);
|
|
142
|
+
this.deleteListener(event);
|
|
143
|
+
} else {
|
|
144
|
+
this.webhooksMap.set(event, filteredWebhooks);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Expose a factory function instead of the class
|
|
152
|
+
*/
|
|
153
|
+
module.exports = function createWebhookRunner(opts) {
|
|
154
|
+
return new WebhookRunner(opts);
|
|
155
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook store is the implementation of webhook storage over the core_store
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const webhookModel = {
|
|
7
|
+
uid: 'webhook',
|
|
8
|
+
collectionName: 'strapi_webhooks',
|
|
9
|
+
attributes: {
|
|
10
|
+
name: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
},
|
|
13
|
+
url: {
|
|
14
|
+
type: 'text',
|
|
15
|
+
},
|
|
16
|
+
headers: {
|
|
17
|
+
type: 'json',
|
|
18
|
+
},
|
|
19
|
+
events: {
|
|
20
|
+
type: 'json',
|
|
21
|
+
},
|
|
22
|
+
enabled: {
|
|
23
|
+
type: 'boolean',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const toDBObject = data => {
|
|
29
|
+
return {
|
|
30
|
+
name: data.name,
|
|
31
|
+
url: data.url,
|
|
32
|
+
headers: data.headers,
|
|
33
|
+
events: data.events,
|
|
34
|
+
enabled: data.isEnabled,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const fromDBObject = row => {
|
|
39
|
+
return {
|
|
40
|
+
id: row.id,
|
|
41
|
+
name: row.name,
|
|
42
|
+
url: row.url,
|
|
43
|
+
headers: row.headers,
|
|
44
|
+
events: row.events,
|
|
45
|
+
isEnabled: row.enabled,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const createWebhookStore = ({ db }) => {
|
|
50
|
+
const webhookQueries = db.query('webhook');
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
async findWebhooks() {
|
|
54
|
+
const results = await webhookQueries.findMany();
|
|
55
|
+
|
|
56
|
+
return results.map(fromDBObject);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
async findWebhook(id) {
|
|
60
|
+
const result = await webhookQueries.findOne({ where: { id } });
|
|
61
|
+
return result ? fromDBObject(result) : null;
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
createWebhook(data) {
|
|
65
|
+
return webhookQueries
|
|
66
|
+
.create({
|
|
67
|
+
data: toDBObject({ ...data, isEnabled: true }),
|
|
68
|
+
})
|
|
69
|
+
.then(fromDBObject);
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
async updateWebhook(id, data) {
|
|
73
|
+
const webhook = await webhookQueries.update({
|
|
74
|
+
where: { id },
|
|
75
|
+
data: toDBObject(data),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return webhook ? fromDBObject(webhook) : null;
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
async deleteWebhook(id) {
|
|
82
|
+
const webhook = await webhookQueries.delete({ where: { id } });
|
|
83
|
+
return webhook ? fromDBObject(webhook) : null;
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
module.exports = {
|
|
89
|
+
webhookModel,
|
|
90
|
+
createWebhookStore,
|
|
91
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple worker queue in memory
|
|
3
|
+
*/
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const debug = require('debug')('strapi:worker-queue');
|
|
7
|
+
|
|
8
|
+
module.exports = class WorkerQueue {
|
|
9
|
+
constructor({ logger, concurrency = 5 } = {}) {
|
|
10
|
+
debug('Initialize worker queue');
|
|
11
|
+
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
this.worker = noop;
|
|
14
|
+
|
|
15
|
+
this.concurrency = concurrency;
|
|
16
|
+
this.running = 0;
|
|
17
|
+
this.queue = [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
subscribe(worker) {
|
|
21
|
+
debug('Subscribe to worker queue');
|
|
22
|
+
this.worker = worker;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
enqueue(payload) {
|
|
26
|
+
debug('Enqueue event in worker queue');
|
|
27
|
+
if (this.running < this.concurrency) {
|
|
28
|
+
this.running++;
|
|
29
|
+
this.execute(payload);
|
|
30
|
+
} else {
|
|
31
|
+
this.queue.unshift(payload);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
pop() {
|
|
36
|
+
debug('Pop worker queue and execute');
|
|
37
|
+
const payload = this.queue.pop();
|
|
38
|
+
|
|
39
|
+
if (payload) {
|
|
40
|
+
this.execute(payload);
|
|
41
|
+
} else {
|
|
42
|
+
this.running--;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async execute(payload) {
|
|
47
|
+
debug('Execute worker');
|
|
48
|
+
try {
|
|
49
|
+
await this.worker(payload);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
this.logger.error(error);
|
|
52
|
+
} finally {
|
|
53
|
+
this.pop();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function noop() {}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = path => {
|
|
4
|
+
if (typeof path !== 'string') throw new Error('admin.url must be a string');
|
|
5
|
+
if (path === '' || path === '/') return '/';
|
|
6
|
+
|
|
7
|
+
if (path[0] != '/') path = '/' + path;
|
|
8
|
+
if (path[path.length - 1] != '/') path = path + '/';
|
|
9
|
+
return path;
|
|
10
|
+
};
|
package/lib/utils/ee.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const _ = require('lodash');
|
|
7
|
+
|
|
8
|
+
const publicKey = fs.readFileSync(path.join(__dirname, '../utils/resources/key.pub'));
|
|
9
|
+
|
|
10
|
+
const noop = () => {};
|
|
11
|
+
|
|
12
|
+
const noLog = {
|
|
13
|
+
warn: noop,
|
|
14
|
+
info: noop,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const internals = {};
|
|
18
|
+
const features = {
|
|
19
|
+
bronze: [],
|
|
20
|
+
silver: [],
|
|
21
|
+
gold: ['sso'],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
module.exports = ({ dir, logger = noLog }) => {
|
|
25
|
+
if (_.has(internals, 'isEE')) return internals.isEE;
|
|
26
|
+
|
|
27
|
+
const warnAndReturn = (msg = 'Invalid license. Starting in CE.') => {
|
|
28
|
+
logger.warn(msg);
|
|
29
|
+
internals.isEE = false;
|
|
30
|
+
return false;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (process.env.STRAPI_DISABLE_EE === 'true') {
|
|
34
|
+
internals.isEE = false;
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const licensePath = path.join(dir, 'license.txt');
|
|
39
|
+
|
|
40
|
+
let license;
|
|
41
|
+
if (_.has(process.env, 'STRAPI_LICENSE')) {
|
|
42
|
+
license = process.env.STRAPI_LICENSE;
|
|
43
|
+
} else if (fs.existsSync(licensePath)) {
|
|
44
|
+
license = fs.readFileSync(licensePath).toString();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (_.isNil(license)) {
|
|
48
|
+
internals.isEE = false;
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const plainLicense = Buffer.from(license, 'base64').toString();
|
|
54
|
+
const [signatureb64, contentb64] = plainLicense.split('\n');
|
|
55
|
+
|
|
56
|
+
const signature = Buffer.from(signatureb64, 'base64');
|
|
57
|
+
const content = Buffer.from(contentb64, 'base64').toString();
|
|
58
|
+
|
|
59
|
+
const verifier = crypto.createVerify('RSA-SHA256');
|
|
60
|
+
verifier.update(content);
|
|
61
|
+
verifier.end();
|
|
62
|
+
|
|
63
|
+
const isValid = verifier.verify(publicKey, signature);
|
|
64
|
+
if (!isValid) return warnAndReturn();
|
|
65
|
+
|
|
66
|
+
internals.licenseInfo = JSON.parse(content);
|
|
67
|
+
|
|
68
|
+
const expirationTime = new Date(internals.licenseInfo.expireAt).getTime();
|
|
69
|
+
if (expirationTime < new Date().getTime()) {
|
|
70
|
+
return warnAndReturn('License expired. Starting in CE');
|
|
71
|
+
}
|
|
72
|
+
} catch (err) {
|
|
73
|
+
return warnAndReturn();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
internals.isEE = true;
|
|
77
|
+
return true;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
Object.defineProperty(module.exports, 'licenseInfo', {
|
|
81
|
+
get() {
|
|
82
|
+
mustHaveKey('licenseInfo');
|
|
83
|
+
return internals.licenseInfo;
|
|
84
|
+
},
|
|
85
|
+
configurable: false,
|
|
86
|
+
enumerable: false,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
Object.defineProperty(module.exports, 'isEE', {
|
|
90
|
+
get() {
|
|
91
|
+
mustHaveKey('isEE');
|
|
92
|
+
return internals.isEE;
|
|
93
|
+
},
|
|
94
|
+
configurable: false,
|
|
95
|
+
enumerable: false,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
Object.defineProperty(module.exports, 'features', {
|
|
99
|
+
get() {
|
|
100
|
+
mustHaveKey('licenseInfo');
|
|
101
|
+
|
|
102
|
+
const { type: licenseType } = module.exports.licenseInfo;
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
isEnabled(feature) {
|
|
106
|
+
return features[licenseType].includes(feature);
|
|
107
|
+
},
|
|
108
|
+
getEnabled() {
|
|
109
|
+
return features[licenseType];
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
configurable: false,
|
|
114
|
+
enumerable: false,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const mustHaveKey = key => {
|
|
118
|
+
if (!_.has(internals, key)) {
|
|
119
|
+
const err = new Error('Tampering with license');
|
|
120
|
+
// err.stack = null;
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { join } = require('path');
|
|
4
|
+
|
|
5
|
+
const getDirs = root => ({
|
|
6
|
+
root,
|
|
7
|
+
src: join(root, 'src'),
|
|
8
|
+
api: join(root, 'src', 'api'),
|
|
9
|
+
components: join(root, 'src', 'components'),
|
|
10
|
+
extensions: join(root, 'src', 'extensions'),
|
|
11
|
+
policies: join(root, 'src', 'policies'),
|
|
12
|
+
config: join(root, 'config'),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
module.exports = getDirs;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { isEmpty, isNil } = require('lodash/fp');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Test if the strapi application is considered as initialized (1st user has been created)
|
|
7
|
+
* @param {Strapi} strapi
|
|
8
|
+
* @returns {boolean}
|
|
9
|
+
*/
|
|
10
|
+
module.exports = async function isInitialized(strapi) {
|
|
11
|
+
try {
|
|
12
|
+
if (isEmpty(strapi.admin)) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// test if there is at least one admin
|
|
17
|
+
const anyAdministrator = await strapi.query('admin::user').findOne({ select: ['id'] });
|
|
18
|
+
|
|
19
|
+
return !isNil(anyAdministrator);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
strapi.stopWithError(err);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-----BEGIN PUBLIC KEY-----
|
|
2
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApuI1XlPkYos3WsSeVPtS
|
|
3
|
+
l1Q2k8GnLEO5vFZ4EuSghMbqI+yE0tWVEaiptdV3KgERaALRXmH+IFrHqvSRjKQC
|
|
4
|
+
1ORUarBU5ntWbNEr713R3K0BPOzz9OOoWHdk+Wmr4ViOTk0iD1u4bw/97RpyMoBm
|
|
5
|
+
+pXeBLHbEESK2kelk+LEmKUoY5nXp6KzZV5wxgD5QweZheU7mjXL5WMpIBJva8kp
|
|
6
|
+
RZMYXEF+uSZIep0q5FHEo2AazGUMAU3GjY/dpXisLmtleOa1xlKZmkvaXl/D2Mhb
|
|
7
|
+
BBqPbDMa72ToZg2J8K5UP9zXUP41FHr7o9rwSJ2uOkuZPg5nhDXeoVbrJwxP/U1M
|
|
8
|
+
nQIDAQAB
|
|
9
|
+
-----END PUBLIC KEY-----
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
const requiredPaths = ['api', 'extensions', 'plugins', 'config', 'public'];
|
|
7
|
+
const checkFoldersExist = ({ appPath }) => {
|
|
8
|
+
let missingPaths = [];
|
|
9
|
+
for (let reqPath of requiredPaths) {
|
|
10
|
+
if (!fs.pathExistsSync(path.resolve(appPath, reqPath))) {
|
|
11
|
+
missingPaths.push(reqPath);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (missingPaths.length > 0) {
|
|
16
|
+
throw new Error(`Missing required folders:\n${missingPaths.map(p => `- ./${p}`).join('\n')}`);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
module.exports = config => {
|
|
21
|
+
checkFoldersExist(config);
|
|
22
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const CLITable = require('cli-table3');
|
|
5
|
+
const _ = require('lodash/fp');
|
|
6
|
+
const { getAbsoluteAdminUrl, getAbsoluteServerUrl } = require('@strapi/utils');
|
|
7
|
+
const ee = require('./ee');
|
|
8
|
+
|
|
9
|
+
module.exports = app => {
|
|
10
|
+
return {
|
|
11
|
+
logStats() {
|
|
12
|
+
const columns = Math.min(process.stderr.columns, 80) - 2;
|
|
13
|
+
console.log();
|
|
14
|
+
console.log(chalk.black.bgWhite(_.padEnd(columns, ' Project information')));
|
|
15
|
+
console.log();
|
|
16
|
+
|
|
17
|
+
const infoTable = new CLITable({
|
|
18
|
+
colWidths: [20, 50],
|
|
19
|
+
chars: { mid: '', 'left-mid': '', 'mid-mid': '', 'right-mid': '' },
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const isEE = app.EE === true && ee.isEE === true;
|
|
23
|
+
|
|
24
|
+
infoTable.push(
|
|
25
|
+
[chalk.blue('Time'), `${new Date()}`],
|
|
26
|
+
[chalk.blue('Launched in'), Date.now() - app.config.launchedAt + ' ms'],
|
|
27
|
+
[chalk.blue('Environment'), app.config.environment],
|
|
28
|
+
[chalk.blue('Process PID'), process.pid],
|
|
29
|
+
[chalk.blue('Version'), `${app.config.info.strapi} (node ${process.version})`],
|
|
30
|
+
[chalk.blue('Edition'), isEE ? 'Enterprise' : 'Community']
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
console.log(infoTable.toString());
|
|
34
|
+
console.log();
|
|
35
|
+
console.log(chalk.black.bgWhite(_.padEnd(columns, ' Actions available')));
|
|
36
|
+
console.log();
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
logFirstStartupMessage() {
|
|
40
|
+
this.logStats();
|
|
41
|
+
|
|
42
|
+
console.log(chalk.bold('One more thing...'));
|
|
43
|
+
console.log(
|
|
44
|
+
chalk.grey('Create your first administrator 💻 by going to the administration panel at:')
|
|
45
|
+
);
|
|
46
|
+
console.log();
|
|
47
|
+
|
|
48
|
+
const addressTable = new CLITable();
|
|
49
|
+
|
|
50
|
+
const adminUrl = getAbsoluteAdminUrl(strapi.config);
|
|
51
|
+
addressTable.push([chalk.bold(adminUrl)]);
|
|
52
|
+
|
|
53
|
+
console.log(`${addressTable.toString()}`);
|
|
54
|
+
console.log();
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
logDefaultStartupMessage() {
|
|
58
|
+
this.logStats();
|
|
59
|
+
|
|
60
|
+
console.log(chalk.bold('Welcome back!'));
|
|
61
|
+
|
|
62
|
+
if (app.config.serveAdminPanel === true) {
|
|
63
|
+
console.log(chalk.grey('To manage your project 🚀, go to the administration panel at:'));
|
|
64
|
+
const adminUrl = getAbsoluteAdminUrl(strapi.config);
|
|
65
|
+
console.log(chalk.bold(adminUrl));
|
|
66
|
+
console.log();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(chalk.grey('To access the server ⚡️, go to:'));
|
|
70
|
+
const serverUrl = getAbsoluteServerUrl(strapi.config);
|
|
71
|
+
console.log(chalk.bold(serverUrl));
|
|
72
|
+
console.log();
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
logStartupMessage({ isInitialized } = {}) {
|
|
76
|
+
// Should the startup message be displayed?
|
|
77
|
+
const hideStartupMessage = process.env.STRAPI_HIDE_STARTUP_MESSAGE
|
|
78
|
+
? process.env.STRAPI_HIDE_STARTUP_MESSAGE === 'true'
|
|
79
|
+
: false;
|
|
80
|
+
|
|
81
|
+
if (hideStartupMessage === false) {
|
|
82
|
+
if (!isInitialized) {
|
|
83
|
+
this.logFirstStartupMessage();
|
|
84
|
+
} else {
|
|
85
|
+
this.logDefaultStartupMessage();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Module dependencies
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fetch = require('node-fetch');
|
|
8
|
+
const { machineIdSync } = require('node-machine-id');
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* No need to worry about this file, we only retrieve anonymous data here.
|
|
12
|
+
* It allows us to know on how many times the package has been installed globally.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
if (
|
|
17
|
+
process.env.npm_config_global === 'true' ||
|
|
18
|
+
JSON.parse(process.env.npm_config_argv).original.includes('global')
|
|
19
|
+
) {
|
|
20
|
+
fetch('https://analytics.strapi.io/track', {
|
|
21
|
+
method: 'POST',
|
|
22
|
+
body: JSON.stringify({
|
|
23
|
+
event: 'didInstallStrapi',
|
|
24
|
+
deviceId: machineIdSync(),
|
|
25
|
+
}),
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
}).catch(() => {});
|
|
28
|
+
}
|
|
29
|
+
} catch (e) {
|
|
30
|
+
//...
|
|
31
|
+
}
|