directus-template-cli 0.4.3 → 0.5.0-beta.10
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/dist/commands/apply.d.ts +23 -1
- package/dist/commands/apply.js +281 -108
- package/dist/commands/extract.d.ts +12 -0
- package/dist/commands/extract.js +85 -17
- package/dist/lib/constants.d.ts +3 -0
- package/dist/lib/constants.js +6 -0
- package/dist/lib/extract/extract-access.d.ts +1 -0
- package/dist/lib/extract/extract-access.js +25 -0
- package/dist/lib/extract/extract-assets.d.ts +257 -25
- package/dist/lib/extract/extract-extensions.d.ts +4 -0
- package/dist/lib/extract/extract-extensions.js +22 -0
- package/dist/lib/extract/extract-fields.js +2 -3
- package/dist/lib/extract/extract-permissions.d.ts +3 -0
- package/dist/lib/extract/extract-permissions.js +11 -4
- package/dist/lib/extract/extract-policies.d.ts +4 -0
- package/dist/lib/extract/extract-policies.js +28 -0
- package/dist/lib/extract/extract-presets.js +1 -1
- package/dist/lib/extract/index.js +6 -0
- package/dist/lib/load/index.d.ts +13 -1
- package/dist/lib/load/index.js +38 -20
- package/dist/lib/load/load-access.d.ts +1 -0
- package/dist/lib/load/load-access.js +60 -0
- package/dist/lib/load/load-collections.js +47 -21
- package/dist/lib/load/load-dashboards.js +32 -11
- package/dist/lib/load/load-data.js +72 -46
- package/dist/lib/load/load-extensions.d.ts +1 -0
- package/dist/lib/load/load-extensions.js +76 -0
- package/dist/lib/load/load-files.d.ts +1 -2
- package/dist/lib/load/load-files.js +52 -24
- package/dist/lib/load/load-flows.d.ts +1 -1
- package/dist/lib/load/load-flows.js +48 -32
- package/dist/lib/load/load-folders.js +32 -11
- package/dist/lib/load/load-permissions.js +15 -12
- package/dist/lib/load/load-policies.d.ts +1 -0
- package/dist/lib/load/load-policies.js +37 -0
- package/dist/lib/load/load-presets.js +26 -10
- package/dist/lib/load/load-relations.js +17 -7
- package/dist/lib/load/load-roles.js +47 -16
- package/dist/lib/load/load-settings.js +44 -4
- package/dist/lib/load/load-translations.js +24 -7
- package/dist/lib/load/load-users.js +28 -5
- package/dist/lib/sdk.d.ts +1 -1
- package/dist/lib/sdk.js +10 -3
- package/dist/lib/types/extension.d.ts +42 -0
- package/dist/lib/types/extension.js +2 -0
- package/dist/lib/utils/auth.js +9 -3
- package/dist/lib/utils/catch-error.d.ts +6 -0
- package/dist/lib/utils/catch-error.js +35 -0
- package/dist/lib/utils/check-template.js +1 -16
- package/dist/lib/utils/chunk-array.d.ts +1 -0
- package/dist/lib/utils/chunk-array.js +7 -0
- package/dist/lib/utils/get-role-ids.d.ts +3 -53
- package/dist/lib/utils/get-role-ids.js +4 -2
- package/dist/lib/utils/get-template.d.ts +9 -0
- package/dist/lib/utils/get-template.js +99 -0
- package/dist/lib/utils/logger.d.ts +12 -0
- package/dist/lib/utils/logger.js +55 -0
- package/dist/lib/utils/read-file.js +2 -1
- package/dist/lib/utils/read-templates.js +4 -2
- package/oclif.manifest.json +192 -5
- package/package.json +4 -5
- package/dist/lib/load/load-schema.d.ts +0 -14
- package/dist/lib/load/load-schema.js +0 -95
- package/dist/lib/utils/log-error.d.ts +0 -14
- package/dist/lib/utils/log-error.js +0 -25
package/dist/commands/apply.d.ts
CHANGED
|
@@ -2,6 +2,28 @@ import { Command } from '@oclif/core';
|
|
|
2
2
|
export default class ApplyCommand extends Command {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
|
-
static flags: {
|
|
5
|
+
static flags: {
|
|
6
|
+
content: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
dashboards: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
directusToken: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
directusUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
extensions: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
files: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
flows: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
partial: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
permissions: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
programmatic: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
schema: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
settings: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
templateLocation: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
19
|
+
templateType: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
20
|
+
users: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
21
|
+
};
|
|
6
22
|
run(): Promise<void>;
|
|
23
|
+
private initializeDirectusApi;
|
|
24
|
+
private redirectToDirectusPlus;
|
|
25
|
+
private runInteractive;
|
|
26
|
+
private runProgrammatic;
|
|
27
|
+
private selectLocalTemplate;
|
|
28
|
+
private validateFlags;
|
|
7
29
|
}
|
package/dist/commands/apply.js
CHANGED
|
@@ -1,135 +1,308 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
+
const sdk_1 = require("@directus/sdk");
|
|
4
5
|
const core_1 = require("@oclif/core");
|
|
5
|
-
const giget_1 = require("giget");
|
|
6
6
|
const inquirer = tslib_1.__importStar(require("inquirer"));
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const constants_1 = require("../lib/constants");
|
|
8
|
+
const index_js_1 = tslib_1.__importDefault(require("../lib/load/index.js"));
|
|
9
|
+
const sdk_2 = require("../lib/sdk");
|
|
9
10
|
const auth_1 = require("../lib/utils/auth");
|
|
10
|
-
const
|
|
11
|
+
const catch_error_1 = tslib_1.__importDefault(require("../lib/utils/catch-error"));
|
|
12
|
+
const get_template_1 = require("../lib/utils/get-template");
|
|
13
|
+
const logger_1 = require("../lib/utils/logger");
|
|
11
14
|
const open_url_1 = tslib_1.__importDefault(require("../lib/utils/open-url"));
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
name: 'Community templates',
|
|
22
|
-
value: 'community',
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
name: 'From a local directory',
|
|
26
|
-
value: 'local',
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: 'From a public GitHub repository',
|
|
30
|
-
value: 'github',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
name: 'Get premium templates',
|
|
34
|
-
value: 'directus-plus',
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
message: 'What type of template would you like to apply?',
|
|
38
|
-
name: 'templateType',
|
|
39
|
-
type: 'list',
|
|
40
|
-
},
|
|
41
|
-
]);
|
|
42
|
-
let template;
|
|
43
|
-
if (templateType.templateType === 'community') {
|
|
44
|
-
// Get community templates
|
|
45
|
-
let templates = [];
|
|
46
|
-
// Resolve the path for downloading
|
|
47
|
-
const downloadDir = (0, path_1.default)(node_path_1.default.join(__dirname, '..', 'downloads', 'official'), false);
|
|
48
|
-
if (!downloadDir) {
|
|
49
|
-
throw new Error(`Invalid download directory: ${node_path_1.default.join(__dirname, '..', 'downloads', 'official')}`);
|
|
50
|
-
}
|
|
15
|
+
class ApplyCommand extends core_1.Command {
|
|
16
|
+
// MAIN FUNCTION
|
|
17
|
+
async run() {
|
|
18
|
+
const { flags } = await this.parse(ApplyCommand);
|
|
19
|
+
const typedFlags = flags;
|
|
20
|
+
await (typedFlags.programmatic ? this.runProgrammatic(typedFlags) : this.runInteractive(typedFlags));
|
|
21
|
+
}
|
|
22
|
+
async initializeDirectusApi(flags) {
|
|
23
|
+
sdk_2.api.initialize(flags.directusUrl);
|
|
51
24
|
try {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
preferOffline: true,
|
|
56
|
-
});
|
|
57
|
-
templates = await (0, read_templates_1.readAllTemplates)(dir);
|
|
25
|
+
sdk_2.api.setAuthToken(flags.directusToken);
|
|
26
|
+
const response = await sdk_2.api.client.request((0, sdk_1.readMe)());
|
|
27
|
+
core_1.ux.log(`-- Logged in as ${response.first_name} ${response.last_name}`);
|
|
58
28
|
}
|
|
59
|
-
catch
|
|
60
|
-
(0,
|
|
29
|
+
catch {
|
|
30
|
+
(0, catch_error_1.default)('-- Invalid Directus token. Please check your credentials.', {
|
|
31
|
+
fatal: true,
|
|
32
|
+
});
|
|
61
33
|
}
|
|
62
|
-
|
|
63
|
-
|
|
34
|
+
}
|
|
35
|
+
redirectToDirectusPlus() {
|
|
36
|
+
(0, open_url_1.default)('https://directus.io/plus?utm_source=directus-template-cli&utm_content=apply-command');
|
|
37
|
+
core_1.ux.log('Redirecting to Directus website.');
|
|
38
|
+
core_1.ux.exit(0);
|
|
39
|
+
}
|
|
40
|
+
async runInteractive(flags) {
|
|
41
|
+
const validatedFlags = this.validateFlags(flags);
|
|
42
|
+
core_1.ux.styledHeader(core_1.ux.colorize(constants_1.DIRECTUS_PURPLE, 'Welcome to the Directus Template CLI'));
|
|
43
|
+
const templateType = await inquirer.prompt([
|
|
64
44
|
{
|
|
65
|
-
choices:
|
|
66
|
-
|
|
67
|
-
|
|
45
|
+
choices: [
|
|
46
|
+
{ name: 'Community templates', value: 'community' },
|
|
47
|
+
{ name: 'From a local directory', value: 'local' },
|
|
48
|
+
{ name: 'From a public GitHub repository', value: 'github' },
|
|
49
|
+
{ name: 'Get premium templates', value: 'directus-plus' },
|
|
50
|
+
],
|
|
51
|
+
message: 'What type of template would you like to apply?',
|
|
52
|
+
name: 'templateType',
|
|
68
53
|
type: 'list',
|
|
69
54
|
},
|
|
70
55
|
]);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
56
|
+
let template;
|
|
57
|
+
switch (templateType.templateType) {
|
|
58
|
+
case 'community': {
|
|
59
|
+
const templates = await (0, get_template_1.getCommunityTemplates)();
|
|
60
|
+
const { selectedTemplate } = await inquirer.prompt([
|
|
61
|
+
{
|
|
62
|
+
choices: templates.map(t => ({ name: t.templateName, value: t })),
|
|
63
|
+
message: 'Select a template.',
|
|
64
|
+
name: 'selectedTemplate',
|
|
65
|
+
type: 'list',
|
|
66
|
+
},
|
|
67
|
+
]);
|
|
68
|
+
template = selectedTemplate;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case 'local': {
|
|
72
|
+
const localTemplateDir = await core_1.ux.prompt('What is the local template directory?');
|
|
73
|
+
template = await this.selectLocalTemplate(localTemplateDir);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case 'github': {
|
|
77
|
+
const ghTemplateUrl = await core_1.ux.prompt('What is the public GitHub repository URL?');
|
|
78
|
+
template = await (0, get_template_1.getGithubTemplate)(ghTemplateUrl);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case 'directus-plus': {
|
|
82
|
+
this.redirectToDirectusPlus();
|
|
83
|
+
}
|
|
77
84
|
}
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
core_1.ux.log(`You selected ${core_1.ux.colorize(constants_1.DIRECTUS_PINK, template.templateName)}`);
|
|
86
|
+
core_1.ux.log(constants_1.SEPARATOR);
|
|
87
|
+
// Get Directus URL and token
|
|
88
|
+
const directusUrl = await (0, auth_1.getDirectusUrl)();
|
|
89
|
+
const directusToken = await (0, auth_1.getDirectusToken)(directusUrl);
|
|
90
|
+
flags.directusUrl = directusUrl;
|
|
91
|
+
flags.directusToken = directusToken;
|
|
92
|
+
if (template) {
|
|
93
|
+
core_1.ux.styledHeader(core_1.ux.colorize(constants_1.DIRECTUS_PURPLE, `Applying template - ${template.templateName} to ${directusUrl}`));
|
|
94
|
+
await (0, index_js_1.default)(template.directoryPath, validatedFlags);
|
|
95
|
+
core_1.ux.action.stop();
|
|
96
|
+
core_1.ux.log(constants_1.SEPARATOR);
|
|
97
|
+
core_1.ux.info('Template applied successfully.');
|
|
98
|
+
core_1.ux.exit(0);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async runProgrammatic(flags) {
|
|
102
|
+
const validatedFlags = this.validateFlags(flags);
|
|
103
|
+
let template;
|
|
104
|
+
switch (validatedFlags.templateType) {
|
|
105
|
+
case 'community': {
|
|
106
|
+
const templates = await (0, get_template_1.getCommunityTemplates)();
|
|
107
|
+
template = templates.find(t => t.templateName === validatedFlags.templateLocation) || templates[0];
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'local': {
|
|
111
|
+
template = await (0, get_template_1.getLocalTemplate)(validatedFlags.templateLocation);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
case 'github': {
|
|
115
|
+
template = await (0, get_template_1.getGithubTemplate)(validatedFlags.templateLocation);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
default: {
|
|
119
|
+
(0, catch_error_1.default)('Invalid template type. Please check your template type.', {
|
|
120
|
+
fatal: true,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
80
123
|
}
|
|
124
|
+
await this.initializeDirectusApi(validatedFlags);
|
|
125
|
+
const logMessage = `Applying template - ${template.templateName} to ${validatedFlags.directusUrl}`;
|
|
126
|
+
core_1.ux.styledHeader(logMessage);
|
|
127
|
+
logger_1.logger.log('info', logMessage);
|
|
128
|
+
await (0, index_js_1.default)(template.directoryPath, validatedFlags);
|
|
129
|
+
core_1.ux.action.stop();
|
|
130
|
+
core_1.ux.log(constants_1.SEPARATOR);
|
|
131
|
+
core_1.ux.info('Template applied successfully.');
|
|
132
|
+
core_1.ux.exit(0);
|
|
81
133
|
}
|
|
82
|
-
|
|
83
|
-
const ghTemplateUrl = await core_1.ux.prompt('What is the public GitHub repository URL?');
|
|
134
|
+
async selectLocalTemplate(localTemplateDir) {
|
|
84
135
|
try {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (!downloadDir) {
|
|
89
|
-
throw new Error(`Invalid download directory: ${node_path_1.default.join(__dirname, '..', 'downloads', 'github')}`);
|
|
90
|
-
}
|
|
91
|
-
// Download the template
|
|
92
|
-
const { dir } = await (0, giget_1.downloadTemplate)(ghString, {
|
|
93
|
-
dir: downloadDir,
|
|
94
|
-
force: true,
|
|
95
|
-
forceClean: true,
|
|
96
|
-
});
|
|
97
|
-
// Check if the directory exists after download
|
|
98
|
-
const resolvedDir = (0, path_1.default)(dir);
|
|
99
|
-
if (!resolvedDir) {
|
|
100
|
-
throw new Error(`Downloaded template directory does not exist: ${dir}`);
|
|
136
|
+
const templates = await (0, get_template_1.getInteractiveLocalTemplate)(localTemplateDir);
|
|
137
|
+
if (templates.length === 1) {
|
|
138
|
+
return templates[0];
|
|
101
139
|
}
|
|
102
|
-
|
|
140
|
+
const { selectedTemplate } = await inquirer.prompt([
|
|
141
|
+
{
|
|
142
|
+
choices: templates.map(t => ({ name: `${t.templateName} (${core_1.ux.colorize('dim', t.directoryPath)})`, value: t })),
|
|
143
|
+
message: 'Multiple templates found. Please select one:',
|
|
144
|
+
name: 'selectedTemplate',
|
|
145
|
+
type: 'list',
|
|
146
|
+
},
|
|
147
|
+
]);
|
|
148
|
+
return selectedTemplate;
|
|
103
149
|
}
|
|
104
150
|
catch (error) {
|
|
105
|
-
|
|
151
|
+
if (error instanceof Error) {
|
|
152
|
+
core_1.ux.error(error.message);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
core_1.ux.error('An unknown error occurred while getting the local template.');
|
|
156
|
+
}
|
|
106
157
|
}
|
|
107
158
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
159
|
+
validateFlags(flags) {
|
|
160
|
+
if (flags.programmatic) {
|
|
161
|
+
if (!flags.directusUrl || !flags.directusToken) {
|
|
162
|
+
core_1.ux.error('Directus URL and token are required for programmatic mode.');
|
|
163
|
+
}
|
|
164
|
+
if (!flags.templateLocation) {
|
|
165
|
+
core_1.ux.error('Template location is required for programmatic mode.');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const loadFlags = [
|
|
169
|
+
'content',
|
|
170
|
+
'dashboards',
|
|
171
|
+
'extensions',
|
|
172
|
+
'files',
|
|
173
|
+
'flows',
|
|
174
|
+
'permissions',
|
|
175
|
+
'schema',
|
|
176
|
+
'settings',
|
|
177
|
+
'users',
|
|
178
|
+
];
|
|
179
|
+
const validatedFlags = { ...flags };
|
|
180
|
+
if (flags.partial) {
|
|
181
|
+
const enabledFlags = loadFlags.filter(flag => flags[flag] === true);
|
|
182
|
+
if (enabledFlags.length === 0) {
|
|
183
|
+
core_1.ux.error('When using --partial, at least one component flag must be set to true.');
|
|
184
|
+
}
|
|
185
|
+
// Handle dependencies
|
|
186
|
+
if (flags.content) {
|
|
187
|
+
validatedFlags.schema = true;
|
|
188
|
+
validatedFlags.files = true;
|
|
189
|
+
if (!flags.schema || !flags.files) {
|
|
190
|
+
core_1.ux.warn('Content loading requires schema and files. Enabling schema and files flags.');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (flags.users) {
|
|
194
|
+
validatedFlags.permissions = true;
|
|
195
|
+
if (!flags.permissions) {
|
|
196
|
+
core_1.ux.warn('User loading requires permissions. Enabling permissions flag.');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// If not partial, set all flags to true
|
|
202
|
+
for (const flag of loadFlags) {
|
|
203
|
+
validatedFlags[flag] = true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return validatedFlags;
|
|
130
207
|
}
|
|
131
208
|
}
|
|
132
209
|
ApplyCommand.description = 'Apply a template to a blank Directus instance.';
|
|
133
|
-
ApplyCommand.examples = [
|
|
134
|
-
|
|
210
|
+
ApplyCommand.examples = [
|
|
211
|
+
'$ directus-template-cli apply',
|
|
212
|
+
'$ directus-template-cli apply -p --directusUrl="http://localhost:8055" --directusToken="admin-token-here" --templateLocation="./my-template" --templateType="local"',
|
|
213
|
+
'$ directus-template-cli@beta apply -p --directusUrl="http://localhost:8055" --directusToken="admin-token-here" --templateLocation="./my-template" --templateType="local" --partial --no-content --no-users',
|
|
214
|
+
];
|
|
215
|
+
ApplyCommand.flags = {
|
|
216
|
+
content: core_1.Flags.boolean({
|
|
217
|
+
allowNo: true,
|
|
218
|
+
default: templateFlagsDefault,
|
|
219
|
+
description: 'Load Content (data)',
|
|
220
|
+
relationships: [
|
|
221
|
+
{ flags: ['schema', 'files'], type: 'all' },
|
|
222
|
+
],
|
|
223
|
+
}),
|
|
224
|
+
dashboards: core_1.Flags.boolean({
|
|
225
|
+
allowNo: true,
|
|
226
|
+
default: templateFlagsDefault,
|
|
227
|
+
description: 'Load Dashboards (dashboards, panels)',
|
|
228
|
+
}),
|
|
229
|
+
directusToken: core_1.Flags.string({
|
|
230
|
+
description: 'Token to use for the Directus instance',
|
|
231
|
+
env: 'TARGET_DIRECTUS_TOKEN',
|
|
232
|
+
}),
|
|
233
|
+
directusUrl: core_1.Flags.string({
|
|
234
|
+
description: 'URL of the Directus instance to apply the template to',
|
|
235
|
+
env: 'TARGET_DIRECTUS_URL',
|
|
236
|
+
}),
|
|
237
|
+
extensions: core_1.Flags.boolean({
|
|
238
|
+
allowNo: true,
|
|
239
|
+
default: templateFlagsDefault,
|
|
240
|
+
description: 'Load Extensions',
|
|
241
|
+
}),
|
|
242
|
+
files: core_1.Flags.boolean({
|
|
243
|
+
allowNo: true,
|
|
244
|
+
default: templateFlagsDefault,
|
|
245
|
+
description: 'Load Files (files, folders)',
|
|
246
|
+
}),
|
|
247
|
+
flows: core_1.Flags.boolean({
|
|
248
|
+
allowNo: true,
|
|
249
|
+
default: templateFlagsDefault,
|
|
250
|
+
description: 'Load Flows (operations, flows)',
|
|
251
|
+
}),
|
|
252
|
+
partial: core_1.Flags.boolean({
|
|
253
|
+
dependsOn: ['programmatic'],
|
|
254
|
+
description: 'Enable partial template application (all components enabled by default)',
|
|
255
|
+
summary: 'Enable partial template application',
|
|
256
|
+
}),
|
|
257
|
+
permissions: core_1.Flags.boolean({
|
|
258
|
+
allowNo: true,
|
|
259
|
+
default: templateFlagsDefault,
|
|
260
|
+
description: 'Loads permissions data. Collections include: directus_roles, directus_policies, directus_access, directus_permissions.',
|
|
261
|
+
summary: 'Load permissions (roles, policies, access, permissions)',
|
|
262
|
+
}),
|
|
263
|
+
programmatic: core_1.Flags.boolean({
|
|
264
|
+
char: 'p',
|
|
265
|
+
default: false,
|
|
266
|
+
description: 'Run in programmatic mode (non-interactive) for use cases such as CI/CD pipelines.',
|
|
267
|
+
summary: 'Run in programmatic mode',
|
|
268
|
+
}),
|
|
269
|
+
schema: core_1.Flags.boolean({
|
|
270
|
+
allowNo: true,
|
|
271
|
+
default: templateFlagsDefault,
|
|
272
|
+
description: 'Load schema (collections, relations)',
|
|
273
|
+
}),
|
|
274
|
+
settings: core_1.Flags.boolean({
|
|
275
|
+
allowNo: true,
|
|
276
|
+
default: templateFlagsDefault,
|
|
277
|
+
description: 'Load settings (project settings, translations, presets)',
|
|
278
|
+
}),
|
|
279
|
+
templateLocation: core_1.Flags.string({
|
|
280
|
+
dependsOn: ['programmatic', 'templateType'],
|
|
281
|
+
description: 'Location of the template to apply',
|
|
282
|
+
env: 'TEMPLATE_LOCATION',
|
|
283
|
+
}),
|
|
284
|
+
templateType: core_1.Flags.string({
|
|
285
|
+
default: 'local',
|
|
286
|
+
dependsOn: ['programmatic'],
|
|
287
|
+
description: 'Type of template to apply. You can apply templates from our community repo, local directories, or public repositories from Github. Defaults to local. ',
|
|
288
|
+
env: 'TEMPLATE_TYPE',
|
|
289
|
+
options: ['community', 'local', 'github'],
|
|
290
|
+
summary: 'Type of template to apply. Options: community, local, github.',
|
|
291
|
+
}),
|
|
292
|
+
users: core_1.Flags.boolean({
|
|
293
|
+
allowNo: true,
|
|
294
|
+
default: templateFlagsDefault,
|
|
295
|
+
description: 'Load users',
|
|
296
|
+
relationships: [
|
|
297
|
+
{ flags: ['permissions'], type: 'all' },
|
|
298
|
+
],
|
|
299
|
+
}),
|
|
300
|
+
};
|
|
135
301
|
exports.default = ApplyCommand;
|
|
302
|
+
function templateFlagsDefault({ flags }) {
|
|
303
|
+
// If programmatic is true, and partial is not set, return true
|
|
304
|
+
if (flags.programmatic && !flags.partial) {
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
@@ -2,5 +2,17 @@ import { Command } from '@oclif/core';
|
|
|
2
2
|
export default class ExtractCommand extends Command {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
directusToken: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
directusUrl: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
programmatic: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
templateLocation: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
templateName: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
5
12
|
run(): Promise<void>;
|
|
13
|
+
private extractTemplate;
|
|
14
|
+
private initializeDirectusApi;
|
|
15
|
+
private runInteractive;
|
|
16
|
+
private runProgrammatic;
|
|
17
|
+
private validateProgrammaticFlags;
|
|
6
18
|
}
|
package/dist/commands/extract.js
CHANGED
|
@@ -1,49 +1,117 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
+
const sdk_1 = require("@directus/sdk");
|
|
4
5
|
const core_1 = require("@oclif/core");
|
|
5
6
|
const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
|
|
6
7
|
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
7
8
|
const slugify_1 = tslib_1.__importDefault(require("slugify"));
|
|
9
|
+
const constants_1 = require("../lib/constants");
|
|
8
10
|
const extract_1 = tslib_1.__importDefault(require("../lib/extract/"));
|
|
11
|
+
const sdk_2 = require("../lib/sdk");
|
|
9
12
|
const auth_1 = require("../lib/utils/auth");
|
|
13
|
+
const catch_error_1 = tslib_1.__importDefault(require("../lib/utils/catch-error"));
|
|
10
14
|
const template_defaults_1 = require("../lib/utils/template-defaults");
|
|
11
|
-
const separator = '------------------';
|
|
12
15
|
class ExtractCommand extends core_1.Command {
|
|
13
16
|
async run() {
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
this.
|
|
17
|
+
const { flags } = await this.parse(ExtractCommand);
|
|
18
|
+
const typedFlags = flags;
|
|
19
|
+
await (typedFlags.programmatic ? this.runProgrammatic(typedFlags) : this.runInteractive(typedFlags));
|
|
20
|
+
}
|
|
21
|
+
async extractTemplate(templateName, directory, flags) {
|
|
17
22
|
try {
|
|
18
|
-
// Check if directory exists, if not, then create it.
|
|
19
23
|
if (!node_fs_1.default.existsSync(directory)) {
|
|
20
24
|
node_fs_1.default.mkdirSync(directory, { recursive: true });
|
|
21
25
|
}
|
|
22
|
-
// Create package.json and README.md
|
|
23
26
|
const packageJSONContent = (0, template_defaults_1.generatePackageJsonContent)(templateName);
|
|
24
27
|
const readmeContent = (0, template_defaults_1.generateReadmeContent)(templateName);
|
|
25
|
-
// Write the content to the specified directory
|
|
26
28
|
const packageJSONPath = node_path_1.default.join(directory, 'package.json');
|
|
27
29
|
const readmePath = node_path_1.default.join(directory, 'README.md');
|
|
28
30
|
node_fs_1.default.writeFileSync(packageJSONPath, packageJSONContent);
|
|
29
31
|
node_fs_1.default.writeFileSync(readmePath, readmeContent);
|
|
30
32
|
}
|
|
31
33
|
catch (error) {
|
|
32
|
-
|
|
34
|
+
core_1.ux.error(`Failed to create directory or write files: ${error.message}`);
|
|
33
35
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
await (0, auth_1.getDirectusToken)(directusUrl);
|
|
37
|
-
this.log(separator);
|
|
38
|
-
// Run the extract script
|
|
39
|
-
core_1.ux.action.start(`Extracting template - from ${directusUrl} to ${directory}`);
|
|
36
|
+
core_1.ux.log(constants_1.SEPARATOR);
|
|
37
|
+
core_1.ux.action.start(`Extracting template - from ${flags.directusUrl} to ${directory}`);
|
|
40
38
|
await (0, extract_1.default)(directory);
|
|
41
39
|
core_1.ux.action.stop();
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
core_1.ux.log(constants_1.SEPARATOR);
|
|
41
|
+
core_1.ux.log('Template extracted successfully.');
|
|
44
42
|
this.exit(0);
|
|
45
43
|
}
|
|
44
|
+
async initializeDirectusApi(flags) {
|
|
45
|
+
sdk_2.api.initialize(flags.directusUrl);
|
|
46
|
+
try {
|
|
47
|
+
sdk_2.api.setAuthToken(flags.directusToken);
|
|
48
|
+
const response = await sdk_2.api.client.request((0, sdk_1.readMe)());
|
|
49
|
+
core_1.ux.log(`-- Logged in as ${response.first_name} ${response.last_name}`);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
(0, catch_error_1.default)('Invalid Directus token. Please check your credentials.', {
|
|
53
|
+
fatal: true,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async runInteractive(flags) {
|
|
58
|
+
const templateName = await core_1.ux.prompt('What is the name of the template?');
|
|
59
|
+
const directory = await core_1.ux.prompt("What directory would you like to extract the template to? If it doesn't exist, it will be created.", { default: `templates/${(0, slugify_1.default)(templateName, { lower: true, strict: true })}` });
|
|
60
|
+
core_1.ux.log(`You selected ${directory}`);
|
|
61
|
+
// Get Directus URL and token
|
|
62
|
+
const directusUrl = await (0, auth_1.getDirectusUrl)();
|
|
63
|
+
const directusToken = await (0, auth_1.getDirectusToken)(directusUrl);
|
|
64
|
+
flags.directusUrl = directusUrl;
|
|
65
|
+
flags.directusToken = directusToken;
|
|
66
|
+
await this.extractTemplate(templateName, directory, flags);
|
|
67
|
+
}
|
|
68
|
+
async runProgrammatic(flags) {
|
|
69
|
+
this.validateProgrammaticFlags(flags);
|
|
70
|
+
const { templateLocation, templateName } = flags;
|
|
71
|
+
await this.initializeDirectusApi(flags);
|
|
72
|
+
await this.extractTemplate(templateName, templateLocation, flags);
|
|
73
|
+
}
|
|
74
|
+
validateProgrammaticFlags(flags) {
|
|
75
|
+
if (!flags.directusUrl || !flags.directusToken) {
|
|
76
|
+
core_1.ux.error('Directus URL and token are required for programmatic mode.');
|
|
77
|
+
}
|
|
78
|
+
if (!flags.templateLocation) {
|
|
79
|
+
core_1.ux.error('Template location is required for programmatic mode.');
|
|
80
|
+
}
|
|
81
|
+
if (!flags.templateName) {
|
|
82
|
+
core_1.ux.error('Template name is required for programmatic mode.');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
46
85
|
}
|
|
47
86
|
ExtractCommand.description = 'Extract a template from a Directus instance.';
|
|
48
|
-
ExtractCommand.examples = [
|
|
87
|
+
ExtractCommand.examples = [
|
|
88
|
+
'$ directus-template-cli extract',
|
|
89
|
+
'$ directus-template-cli extract -p --templateName="My Template" --templateLocation="./my-template" --directusToken="admin-token-here" --directusUrl="http://localhost:8055"',
|
|
90
|
+
];
|
|
91
|
+
ExtractCommand.flags = {
|
|
92
|
+
directusToken: core_1.Flags.string({
|
|
93
|
+
description: 'Token to use for the Directus instance',
|
|
94
|
+
env: 'SOURCE_DIRECTUS_TOKEN',
|
|
95
|
+
}),
|
|
96
|
+
directusUrl: core_1.Flags.string({
|
|
97
|
+
description: 'URL of the Directus instance to extract the template from',
|
|
98
|
+
env: 'SOURCE_DIRECTUS_URL',
|
|
99
|
+
}),
|
|
100
|
+
programmatic: core_1.Flags.boolean({
|
|
101
|
+
char: 'p',
|
|
102
|
+
default: false,
|
|
103
|
+
description: 'Run in programmatic mode (non-interactive) for use cases such as CI/CD pipelines.',
|
|
104
|
+
summary: 'Run in programmatic mode',
|
|
105
|
+
}),
|
|
106
|
+
templateLocation: core_1.Flags.string({
|
|
107
|
+
dependsOn: ['programmatic'],
|
|
108
|
+
description: 'Directory to extract the template to',
|
|
109
|
+
env: 'TEMPLATE_LOCATION',
|
|
110
|
+
}),
|
|
111
|
+
templateName: core_1.Flags.string({
|
|
112
|
+
dependsOn: ['programmatic'],
|
|
113
|
+
description: 'Name of the template',
|
|
114
|
+
env: 'TEMPLATE_NAME',
|
|
115
|
+
}),
|
|
116
|
+
};
|
|
49
117
|
exports.default = ExtractCommand;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SEPARATOR = exports.DIRECTUS_PINK = exports.DIRECTUS_PURPLE = void 0;
|
|
4
|
+
exports.DIRECTUS_PURPLE = '#6644ff';
|
|
5
|
+
exports.DIRECTUS_PINK = '#FF99DD';
|
|
6
|
+
exports.SEPARATOR = '------------------';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function extractAccess(dir: string): Promise<void>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const sdk_1 = require("../sdk");
|
|
6
|
+
const write_to_file_1 = tslib_1.__importDefault(require("../utils/write-to-file"));
|
|
7
|
+
async function extractAccess(dir) {
|
|
8
|
+
try {
|
|
9
|
+
const response = await sdk_1.api.client.request(() => ({
|
|
10
|
+
method: 'GET',
|
|
11
|
+
path: '/access?limit=-1',
|
|
12
|
+
}));
|
|
13
|
+
// Delete the id field from the permissions so we don't have to reset the autoincrement on the db
|
|
14
|
+
// for (const access of response) {
|
|
15
|
+
// delete access.id
|
|
16
|
+
// }
|
|
17
|
+
await (0, write_to_file_1.default)('access', response, dir);
|
|
18
|
+
core_1.ux.log('Extracted access');
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
core_1.ux.warn('Error extracting access:');
|
|
22
|
+
core_1.ux.warn(error.message);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.default = extractAccess;
|