directus-template-cli 0.4.3 → 0.5.0-beta.2

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