directus-template-cli 0.5.0-beta.2 → 0.5.0-beta.20
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/README.md +176 -15
- package/dist/commands/apply.d.ts +41 -18
- package/dist/commands/apply.js +113 -122
- package/dist/commands/extract.d.ts +29 -1
- package/dist/commands/extract.js +76 -51
- package/dist/flags/common.d.ts +7 -0
- package/dist/flags/common.js +41 -0
- package/dist/lib/constants.d.ts +3 -0
- package/dist/lib/constants.js +6 -0
- package/dist/lib/extract/extract-access.js +5 -3
- package/dist/lib/extract/extract-assets.d.ts +0 -414
- package/dist/lib/extract/extract-assets.js +29 -25
- package/dist/lib/extract/extract-collections.js +5 -4
- package/dist/lib/extract/extract-content.d.ts +0 -2
- package/dist/lib/extract/extract-content.js +16 -15
- package/dist/lib/extract/extract-dashboards.js +8 -6
- package/dist/lib/extract/extract-extensions.js +5 -3
- package/dist/lib/extract/extract-fields.js +5 -4
- package/dist/lib/extract/extract-files.js +5 -3
- package/dist/lib/extract/extract-flows.js +8 -6
- package/dist/lib/extract/extract-folders.js +5 -3
- package/dist/lib/extract/extract-permissions.js +5 -3
- package/dist/lib/extract/extract-policies.js +5 -3
- package/dist/lib/extract/extract-presets.js +5 -3
- package/dist/lib/extract/extract-relations.js +5 -3
- package/dist/lib/extract/extract-roles.js +5 -3
- package/dist/lib/extract/extract-schema.js +6 -8
- package/dist/lib/extract/extract-settings.js +5 -3
- package/dist/lib/extract/extract-translations.js +6 -6
- package/dist/lib/extract/extract-users.js +6 -6
- package/dist/lib/load/apply-flags.d.ts +22 -0
- package/dist/lib/load/apply-flags.js +67 -0
- package/dist/lib/load/index.js +9 -5
- package/dist/lib/load/load-access.js +47 -41
- package/dist/lib/load/load-collections.js +61 -17
- package/dist/lib/load/load-dashboards.js +30 -30
- package/dist/lib/load/load-data.js +47 -11
- package/dist/lib/load/load-extensions.js +49 -43
- package/dist/lib/load/load-files.js +44 -51
- package/dist/lib/load/load-flows.d.ts +1 -1
- package/dist/lib/load/load-flows.js +44 -38
- package/dist/lib/load/load-folders.js +34 -35
- package/dist/lib/load/load-permissions.js +15 -17
- package/dist/lib/load/load-policies.js +23 -21
- package/dist/lib/load/load-presets.js +27 -26
- package/dist/lib/load/load-relations.js +19 -18
- package/dist/lib/load/load-roles.js +45 -45
- package/dist/lib/load/load-settings.js +39 -2
- package/dist/lib/load/load-translations.js +24 -24
- package/dist/lib/load/load-users.js +44 -34
- package/dist/lib/load/update-required-fields.d.ts +1 -0
- package/dist/lib/load/update-required-fields.js +24 -0
- package/dist/lib/sdk.d.ts +20 -2
- package/dist/lib/sdk.js +124 -9
- package/dist/lib/utils/auth.d.ts +26 -0
- package/dist/lib/utils/auth.js +48 -4
- package/dist/lib/utils/catch-error.d.ts +15 -2
- package/dist/lib/utils/catch-error.js +31 -25
- package/dist/lib/utils/get-template.d.ts +1 -0
- package/dist/lib/utils/get-template.js +42 -1
- package/dist/lib/utils/read-file.js +2 -1
- package/dist/lib/utils/read-templates.js +4 -2
- package/oclif.manifest.json +74 -28
- package/package.json +2 -2
- package/dist/lib/interfaces.d.ts +0 -8
- package/dist/lib/interfaces.js +0 -2
package/README.md
CHANGED
|
@@ -4,21 +4,16 @@ A CLI tool to make applying or extracting Directus "templates" a little easier..
|
|
|
4
4
|
|
|
5
5
|
**Notes:**
|
|
6
6
|
|
|
7
|
-
- This is a
|
|
8
|
-
- ⚠️ Known issues with using MySQL currently
|
|
9
|
-
- Templates are applied / extracted on an all or nothing basis – meaning that all the schema, content, and system settings are extracted or applied. We'd love to support more granular operations in the future. (PRs welcome 🙏)
|
|
7
|
+
- This is a beta release. It is recommended for use on POC, demo, or greenfield projects only. When applying templates, you should always backup your project/database before applying a template.
|
|
8
|
+
- ⚠️ Known issues with using MySQL currently. We highly recommend using PostgreSQL or SQLite for your database provider. If you're using PostgreSQL in production, we recommend using PostgreSQL in local development as well.
|
|
10
9
|
- If you are extracting or applying from a remote source, the script can take quite a while depending on the "size" of your instance (how many collections, how many items in each collection, number and size of assets, etc). The script applies a strict rate limit of 10 requests per second using bottleneck.
|
|
11
|
-
|
|
12
|
-
## Breaking Changes in v0.4.0
|
|
13
|
-
|
|
14
|
-
- Templates are no longer being bundled with the CLI or included in this repository. Templates are stored in this repository - https://github.com/directus-community/directus-templates.
|
|
15
|
-
- When applying templates, the schema snapshot / schema diff is no longer used to create collections, fields, and relations. This allows support for loading multiple templates into a single instance.
|
|
16
|
-
|
|
17
|
-
## Usage
|
|
10
|
+
- As of v0.5.0, the CLI is compatible with Directus 11 and up. If you need to apply or extract to an instance of Directus 10, you can use v0.4.0 of the CLI. `npx directus-template-cli@0.4 extract` or `npx directus-template-cli@0.4 apply`.
|
|
18
11
|
|
|
19
12
|
Using the @latest tag ensures you're receiving the latest version of the packaged templates with the CLI. You can review [the specific versions on NPM](https://www.npmjs.com/package/directus-template-cli) and use @{version} syntax to apply the templates included with that version.
|
|
20
13
|
|
|
21
|
-
|
|
14
|
+
## Applying a Template
|
|
15
|
+
|
|
16
|
+
🚧 Make backups of your project/database before applying templates.
|
|
22
17
|
|
|
23
18
|
1. Create a Directus instance on [Directus Cloud](https://directus.cloud) or using self-hosted version.
|
|
24
19
|
2. Login and create a Static Access Token for the admin user.
|
|
@@ -26,24 +21,190 @@ Using the @latest tag ensures you're receiving the latest version of the package
|
|
|
26
21
|
4. Run the following command on the terminal and follow the prompts.
|
|
27
22
|
|
|
28
23
|
```
|
|
29
|
-
|
|
24
|
+
npx directus-template-cli@latest apply
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
You can choose from our community maintained templates or you can also choose a template from a local directory or a public GitHub repository.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Programmatic Mode
|
|
31
|
+
|
|
32
|
+
By default, the CLI will run in interactive mode. For CI/CD pipelines or automated scripts, you can use the programmatic mode:
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
Using a token:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
npx directus-template-cli@latest apply -p --directusUrl="http://localhost:8055" --directusToken="admin-token-here" --templateLocation="./my-template" --templateType="local"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Using email/password:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
npx directus-template-cli@latest apply -p --directusUrl="http://localhost:8055" --userEmail="admin@example.com" --userPassword="admin" --templateLocation="./my-template" --templateType="local"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Partial apply (apply only some of the parts of a template to the instance):
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
npx directus-template-cli@latest apply -p --directusUrl="http://localhost:8055" --userEmail="admin@example.com" --userPassword="your-password" --templateLocation="./my-template" --templateType="local" --partial --schema --permissions --no-content
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Available flags for programmatic mode:
|
|
55
|
+
|
|
56
|
+
- `--directusUrl`: URL of the Directus instance to apply the template to (required)
|
|
57
|
+
- `--directusToken`: Token to use for the Directus instance (required if not using email/password)
|
|
58
|
+
- `--userEmail`: Email for Directus authentication (required if not using token)
|
|
59
|
+
- `--userPassword`: Password for Directus authentication (required if not using token)
|
|
60
|
+
- `--templateLocation`: Location of the template to apply (required)
|
|
61
|
+
- `--templateType`: Type of template to apply. Options: community, local, github. Defaults to `local`.
|
|
62
|
+
- `--partial`: Enable partial template application
|
|
63
|
+
- `--content`: Load Content (data)
|
|
64
|
+
- `--dashboards`: Load Dashboards
|
|
65
|
+
- `--extensions`: Load Extensions
|
|
66
|
+
- `--files`: Load Files
|
|
67
|
+
- `--flows`: Load Flows
|
|
68
|
+
- `--permissions`: Load Permissions
|
|
69
|
+
- `--schema`: Load Schema
|
|
70
|
+
- `--settings`: Load Settings
|
|
71
|
+
- `--users`: Load Users
|
|
72
|
+
|
|
73
|
+
When using `--partial`, you can also use `--no` flags to exclude specific components from being applied. For example:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
npx directus-template-cli@latest apply -p --directusUrl="http://localhost:8055" --userEmail="admin@example.com" --userPassword="your-password" --templateLocation="./my-template" --templateType="local" --partial --no-content --no-users
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This command will apply the template but exclude content and users. Available `--no` flags include:
|
|
80
|
+
|
|
81
|
+
- `--no-content`: Skip loading Content (data)
|
|
82
|
+
- `--no-dashboards`: Skip loading Dashboards
|
|
83
|
+
- `--no-extensions`: Skip loading Extensions
|
|
84
|
+
- `--no-files`: Skip loading Files
|
|
85
|
+
- `--no-flows`: Skip loading Flows
|
|
86
|
+
- `--no-permissions`: Skip loading PermissionsI
|
|
87
|
+
- `--no-schema`: Skip loading Schema
|
|
88
|
+
- `--no-settings`: Skip loading Settings
|
|
89
|
+
- `--no-users`: Skip loading Users
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
#### Template Component Dependencies
|
|
93
|
+
|
|
94
|
+
When applying templates, certain components have dependencies on others. Here are the key relationships to be aware of:
|
|
95
|
+
|
|
96
|
+
- `--users`: Depends on `--permissions`. If you include users, permissions will automatically be included.
|
|
97
|
+
- `--permissions`: Depends on `--schema`. If you include permissions, the schema will automatically be included.
|
|
98
|
+
- `--content`: Depends on `--schema`. If you include content, the schema will automatically be included.
|
|
99
|
+
- `--files`: No direct dependencies, but often related to content. Consider including `--content` if you're including files.
|
|
100
|
+
- `--flows`: No direct dependencies, but may interact with other components. Consider your specific use case.
|
|
101
|
+
- `--dashboards`: No direct dependencies, but often rely on data from other components.
|
|
102
|
+
- `--extensions`: No direct dependencies, but may interact with other components.
|
|
103
|
+
- `--settings`: No direct dependencies, but affects the overall system configuration.
|
|
104
|
+
|
|
105
|
+
When using the `--partial` flag, keep these dependencies in mind. For example:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
npx directus-template-cli@latest apply -p --directusUrl="http://localhost:8055" --directusToken="admin-token-here" --templateLocation="./my-template" --templateType="local" --partial --users
|
|
30
109
|
```
|
|
31
110
|
|
|
32
|
-
|
|
111
|
+
This command will automatically include `--permissions` and `--schema` along with `--users`, even if not explicitly specified.
|
|
112
|
+
|
|
113
|
+
If you use `--no-` flags, be cautious about excluding dependencies. For instance, using `--no-schema` while including `--content` may lead to errors or incomplete application of the template.
|
|
114
|
+
|
|
115
|
+
#### Using Environment Variables
|
|
116
|
+
|
|
117
|
+
You can also pass flags as environment variables. This can be useful for CI/CD pipelines or when you want to avoid exposing sensitive information in command-line arguments. Here are the available environment variables:
|
|
118
|
+
|
|
119
|
+
- `TARGET_DIRECTUS_URL`: Equivalent to `--directusUrl`
|
|
120
|
+
- `TARGET_DIRECTUS_TOKEN`: Equivalent to `--directusToken`
|
|
121
|
+
- `TARGET_DIRECTUS_EMAIL`: Equivalent to `--userEmail`
|
|
122
|
+
- `TARGET_DIRECTUS_PASSWORD`: Equivalent to `--userPassword`
|
|
123
|
+
- `TEMPLATE_LOCATION`: Equivalent to `--templateLocation`
|
|
124
|
+
- `TEMPLATE_TYPE`: Equivalent to `--templateType`
|
|
125
|
+
-
|
|
126
|
+
|
|
127
|
+
### Existing Data
|
|
128
|
+
|
|
129
|
+
You can apply a template to an existing Directus instance. This is nice because you can have smaller templates that you can "compose" for various use cases. The CLI tries to be smart about existing items in the target Directus instance. But mileage may vary depending on the size and complexity of the template and the existing instance.
|
|
130
|
+
|
|
131
|
+
**System Collections**
|
|
132
|
+
|
|
133
|
+
In most of the system collections (collections,roles, permissions, etc.), if an item with the same identifier already exists, it will be typically be SKIPPED vs overwritten.
|
|
33
134
|
|
|
34
|
-
|
|
135
|
+
Exceptions:
|
|
136
|
+
|
|
137
|
+
- directus_settings: The CLI attempts to merge the template's project settings with the existing settings in the target instance. Using the existing settings as a base and updating them with the values from the template. This should prevent overwriting branding, themes, and other customizations.
|
|
138
|
+
|
|
139
|
+
**Your Collections:**
|
|
140
|
+
|
|
141
|
+
For data in your own user-created collections, if an item has the same primary key, the data will be overwritten with the incoming data from the template.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Extracting a Template
|
|
35
146
|
|
|
36
147
|
The CLI can also extract a template from a Directus instance so that it can be applied to other instances.
|
|
37
148
|
|
|
149
|
+
Note: We do not currently support partial extraction. The entire template will be extracted. We thought it better to have the data and not need it, than need it and not have it.
|
|
150
|
+
|
|
38
151
|
1. Make sure you remove any sensitive data from the Directus instance you don't want to include in the template.
|
|
39
152
|
2. Login and create a Static Access Token for the admin user.
|
|
40
153
|
3. Copy the static token and your Directus URL.
|
|
41
154
|
4. Run the following command on the terminal and follow the prompts.
|
|
42
155
|
|
|
43
156
|
```
|
|
44
|
-
|
|
157
|
+
npx directus-template-cli@latest extract
|
|
45
158
|
```
|
|
46
159
|
|
|
160
|
+
### Programmatic Mode
|
|
161
|
+
|
|
162
|
+
By default, the CLI will run in interactive mode. For CI/CD pipelines or automated scripts, you can use the programmatic mode:
|
|
163
|
+
|
|
164
|
+
Using a token:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
npx directus-template-cli@latest extract -p --templateName="My Template" --templateLocation="./my-template" --directusToken="admin-token-here" --directusUrl="http://localhost:8055"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Using email/password:
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
npx directus-template-cli@latest extract -p --templateName="My Template" --templateLocation="./my-template" --userEmail="admin@example.com" --userPassword="admin" --directusUrl="http://localhost:8055"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Available flags for programmatic mode:
|
|
177
|
+
|
|
178
|
+
- `--directusUrl`: URL of the Directus instance to extract the template from (required)
|
|
179
|
+
- `--directusToken`: Token to use for the Directus instance (required if not using email/password)
|
|
180
|
+
- `--userEmail`: Email for Directus authentication (required if not using token)
|
|
181
|
+
- `--userPassword`: Password for Directus authentication (required if not using token)
|
|
182
|
+
- `--templateLocation`: Directory to extract the template to (required)
|
|
183
|
+
- `--templateName`: Name of the template (required)
|
|
184
|
+
|
|
185
|
+
#### Using Environment Variables
|
|
186
|
+
|
|
187
|
+
Similar to the Apply command, you can use environment variables for the Extract command as well:
|
|
188
|
+
|
|
189
|
+
- `SOURCE_DIRECTUS_URL`: Equivalent to `--directusUrl`
|
|
190
|
+
- `SOURCE_DIRECTUS_TOKEN`: Equivalent to `--directusToken`
|
|
191
|
+
- `SOURCE_DIRECTUS_EMAIL`: Equivalent to `--userEmail`
|
|
192
|
+
- `SOURCE_DIRECTUS_PASSWORD`: Equivalent to `--userPassword`
|
|
193
|
+
- `TEMPLATE_LOCATION`: Equivalent to `--templateLocation`
|
|
194
|
+
|
|
195
|
+
## Logs
|
|
196
|
+
|
|
197
|
+
The Directus Template CLI logs information to a file in the `.directus-template-cli/logs` directory.
|
|
198
|
+
|
|
199
|
+
Logs are automatically generated for each run of the CLI. Here's how the logging system works:
|
|
200
|
+
- A new log file is created for each CLI run.
|
|
201
|
+
- Log files are stored in the `.directus-template-cli/logs` directory within your current working directory.
|
|
202
|
+
- Each log file is named `run-[timestamp].log`, where `[timestamp]` is the ISO timestamp of when the CLI was initiated.
|
|
203
|
+
|
|
204
|
+
The logger automatically sanitizes sensitive information such as passwords, tokens, and keys before writing to the log file. But it may not catch everything. Just be aware of this and make sure to remove the log files when they are no longer needed.
|
|
205
|
+
|
|
206
|
+
Note: If you encounter any issues with the CLI, providing these log files can greatly assist in diagnosing and resolving the problem.
|
|
207
|
+
|
|
47
208
|
## License
|
|
48
209
|
|
|
49
210
|
This tool is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
package/dist/commands/apply.d.ts
CHANGED
|
@@ -3,26 +3,49 @@ export default class ApplyCommand extends Command {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
-
content: import("@oclif/core/lib/interfaces
|
|
7
|
-
dashboards: import("@oclif/core/lib/interfaces
|
|
8
|
-
directusToken: import("@oclif/core/lib/interfaces
|
|
9
|
-
directusUrl: import("@oclif/core/lib/interfaces
|
|
10
|
-
extensions: import("@oclif/core/lib/interfaces
|
|
11
|
-
files: import("@oclif/core/lib/interfaces
|
|
12
|
-
flows: import("@oclif/core/lib/interfaces
|
|
13
|
-
partial: import("@oclif/core/lib/interfaces
|
|
14
|
-
permissions: import("@oclif/core/lib/interfaces
|
|
15
|
-
programmatic: import("@oclif/core/lib/interfaces
|
|
16
|
-
schema: import("@oclif/core/lib/interfaces
|
|
17
|
-
settings: import("@oclif/core/lib/interfaces
|
|
18
|
-
templateLocation: import("@oclif/core/lib/interfaces
|
|
19
|
-
templateType: import("@oclif/core/lib/interfaces
|
|
20
|
-
|
|
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
|
+
userEmail: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
21
|
+
userPassword: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
|
+
users: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
21
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* MAIN
|
|
26
|
+
* Run the command
|
|
27
|
+
* @returns {Promise<void>} - Returns nothing
|
|
28
|
+
*/
|
|
22
29
|
run(): Promise<void>;
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
/**
|
|
31
|
+
* INTERACTIVE
|
|
32
|
+
* Run the command in interactive mode
|
|
33
|
+
* @param flags - The ApplyFlags
|
|
34
|
+
* @returns {Promise<void>} - Returns nothing
|
|
35
|
+
*/
|
|
25
36
|
private runInteractive;
|
|
37
|
+
/**
|
|
38
|
+
* PROGRAMMATIC
|
|
39
|
+
* Run the command in programmatic mode
|
|
40
|
+
* @param flags - The ApplyFlags
|
|
41
|
+
* @returns {Promise<void>} - Returns nothing
|
|
42
|
+
*/
|
|
26
43
|
private runProgrammatic;
|
|
27
|
-
|
|
44
|
+
/**
|
|
45
|
+
* INTERACTIVE
|
|
46
|
+
* Select a local template from the given directory
|
|
47
|
+
* @param localTemplateDir - The local template directory path
|
|
48
|
+
* @returns {Promise<Template>} - Returns the selected template
|
|
49
|
+
*/
|
|
50
|
+
private selectLocalTemplate;
|
|
28
51
|
}
|
package/dist/commands/apply.js
CHANGED
|
@@ -1,40 +1,38 @@
|
|
|
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");
|
|
5
4
|
const core_1 = require("@oclif/core");
|
|
6
5
|
const inquirer = tslib_1.__importStar(require("inquirer"));
|
|
6
|
+
const path = tslib_1.__importStar(require("node:path"));
|
|
7
|
+
const customFlags = tslib_1.__importStar(require("../flags/common"));
|
|
8
|
+
const constants_1 = require("../lib/constants");
|
|
9
|
+
const apply_flags_1 = require("../lib/load/apply-flags");
|
|
7
10
|
const index_js_1 = tslib_1.__importDefault(require("../lib/load/index.js"));
|
|
8
|
-
const sdk_2 = require("../lib/sdk");
|
|
9
11
|
const auth_1 = require("../lib/utils/auth");
|
|
12
|
+
const catch_error_1 = tslib_1.__importDefault(require("../lib/utils/catch-error"));
|
|
10
13
|
const get_template_1 = require("../lib/utils/get-template");
|
|
14
|
+
const logger_1 = require("../lib/utils/logger");
|
|
11
15
|
const open_url_1 = tslib_1.__importDefault(require("../lib/utils/open-url"));
|
|
12
|
-
const separator = '------------------';
|
|
13
16
|
class ApplyCommand extends core_1.Command {
|
|
14
|
-
|
|
17
|
+
/**
|
|
18
|
+
* MAIN
|
|
19
|
+
* Run the command
|
|
20
|
+
* @returns {Promise<void>} - Returns nothing
|
|
21
|
+
*/
|
|
15
22
|
async run() {
|
|
16
23
|
const { flags } = await this.parse(ApplyCommand);
|
|
17
24
|
const typedFlags = flags;
|
|
18
25
|
await (typedFlags.programmatic ? this.runProgrammatic(typedFlags) : this.runInteractive(typedFlags));
|
|
19
26
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
throw new Error('Invalid Directus token. Please check your credentials.');
|
|
29
|
-
}
|
|
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
|
-
}
|
|
27
|
+
/**
|
|
28
|
+
* INTERACTIVE
|
|
29
|
+
* Run the command in interactive mode
|
|
30
|
+
* @param flags - The ApplyFlags
|
|
31
|
+
* @returns {Promise<void>} - Returns nothing
|
|
32
|
+
*/
|
|
36
33
|
async runInteractive(flags) {
|
|
37
|
-
const validatedFlags =
|
|
34
|
+
const validatedFlags = (0, apply_flags_1.validateInteractiveFlags)(flags);
|
|
35
|
+
core_1.ux.styledHeader(core_1.ux.colorize(constants_1.DIRECTUS_PURPLE, 'Directus Template CLI - Apply'));
|
|
38
36
|
const templateType = await inquirer.prompt([
|
|
39
37
|
{
|
|
40
38
|
choices: [
|
|
@@ -65,7 +63,7 @@ class ApplyCommand extends core_1.Command {
|
|
|
65
63
|
}
|
|
66
64
|
case 'local': {
|
|
67
65
|
const localTemplateDir = await core_1.ux.prompt('What is the local template directory?');
|
|
68
|
-
template = await
|
|
66
|
+
template = await this.selectLocalTemplate(localTemplateDir);
|
|
69
67
|
break;
|
|
70
68
|
}
|
|
71
69
|
case 'github': {
|
|
@@ -74,27 +72,57 @@ class ApplyCommand extends core_1.Command {
|
|
|
74
72
|
break;
|
|
75
73
|
}
|
|
76
74
|
case 'directus-plus': {
|
|
77
|
-
|
|
75
|
+
(0, open_url_1.default)('https://directus.io/plus?utm_source=directus-template-cli&utm_content=apply-command');
|
|
76
|
+
core_1.ux.log('Redirecting to Directus website.');
|
|
77
|
+
core_1.ux.exit(0);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
core_1.ux.log(`You selected ${template.templateName}`);
|
|
81
|
-
core_1.ux.log(
|
|
82
|
-
// Get Directus URL
|
|
80
|
+
core_1.ux.log(`You selected ${core_1.ux.colorize(constants_1.DIRECTUS_PINK, template.templateName)}`);
|
|
81
|
+
core_1.ux.log(constants_1.SEPARATOR);
|
|
82
|
+
// Get Directus URL
|
|
83
83
|
const directusUrl = await (0, auth_1.getDirectusUrl)();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
validatedFlags.directusUrl = directusUrl;
|
|
85
|
+
// Prompt for login method
|
|
86
|
+
const loginMethod = await inquirer.prompt([
|
|
87
|
+
{
|
|
88
|
+
choices: [
|
|
89
|
+
{ name: 'Directus Access Token', value: 'token' },
|
|
90
|
+
{ name: 'Email and Password', value: 'email' },
|
|
91
|
+
],
|
|
92
|
+
default: 'token',
|
|
93
|
+
message: 'How do you want to log in?',
|
|
94
|
+
name: 'loginMethod',
|
|
95
|
+
type: 'list',
|
|
96
|
+
},
|
|
97
|
+
]);
|
|
98
|
+
if (loginMethod.loginMethod === 'token') {
|
|
99
|
+
const directusToken = await (0, auth_1.getDirectusToken)(directusUrl);
|
|
100
|
+
validatedFlags.directusToken = directusToken;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const userEmail = await core_1.ux.prompt('What is your email?');
|
|
104
|
+
validatedFlags.userEmail = userEmail;
|
|
105
|
+
const userPassword = await core_1.ux.prompt('What is your password?', { type: 'hide' });
|
|
106
|
+
validatedFlags.userPassword = userPassword;
|
|
107
|
+
}
|
|
108
|
+
await (0, auth_1.initializeDirectusApi)(validatedFlags);
|
|
87
109
|
if (template) {
|
|
88
|
-
core_1.ux.
|
|
110
|
+
core_1.ux.styledHeader(core_1.ux.colorize(constants_1.DIRECTUS_PURPLE, `Applying template - ${template.templateName} to ${directusUrl}`));
|
|
89
111
|
await (0, index_js_1.default)(template.directoryPath, validatedFlags);
|
|
90
112
|
core_1.ux.action.stop();
|
|
91
|
-
core_1.ux.log(
|
|
92
|
-
core_1.ux.
|
|
113
|
+
core_1.ux.log(constants_1.SEPARATOR);
|
|
114
|
+
core_1.ux.info('Template applied successfully.');
|
|
93
115
|
core_1.ux.exit(0);
|
|
94
116
|
}
|
|
95
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* PROGRAMMATIC
|
|
120
|
+
* Run the command in programmatic mode
|
|
121
|
+
* @param flags - The ApplyFlags
|
|
122
|
+
* @returns {Promise<void>} - Returns nothing
|
|
123
|
+
*/
|
|
96
124
|
async runProgrammatic(flags) {
|
|
97
|
-
const validatedFlags =
|
|
125
|
+
const validatedFlags = (0, apply_flags_1.validateProgrammaticFlags)(flags);
|
|
98
126
|
let template;
|
|
99
127
|
switch (validatedFlags.templateType) {
|
|
100
128
|
case 'community': {
|
|
@@ -111,65 +139,54 @@ class ApplyCommand extends core_1.Command {
|
|
|
111
139
|
break;
|
|
112
140
|
}
|
|
113
141
|
default: {
|
|
114
|
-
|
|
142
|
+
(0, catch_error_1.default)('Invalid template type. Please check your template type.', {
|
|
143
|
+
fatal: true,
|
|
144
|
+
});
|
|
115
145
|
}
|
|
116
146
|
}
|
|
117
|
-
await
|
|
118
|
-
|
|
147
|
+
await (0, auth_1.initializeDirectusApi)(validatedFlags);
|
|
148
|
+
const logMessage = `Applying template - ${template.templateName} to ${validatedFlags.directusUrl}`;
|
|
149
|
+
core_1.ux.styledHeader(logMessage);
|
|
150
|
+
logger_1.logger.log('info', logMessage);
|
|
119
151
|
await (0, index_js_1.default)(template.directoryPath, validatedFlags);
|
|
120
152
|
core_1.ux.action.stop();
|
|
121
|
-
core_1.ux.log(
|
|
122
|
-
core_1.ux.
|
|
153
|
+
core_1.ux.log(constants_1.SEPARATOR);
|
|
154
|
+
core_1.ux.info('Template applied successfully.');
|
|
123
155
|
core_1.ux.exit(0);
|
|
124
156
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
157
|
+
/**
|
|
158
|
+
* INTERACTIVE
|
|
159
|
+
* Select a local template from the given directory
|
|
160
|
+
* @param localTemplateDir - The local template directory path
|
|
161
|
+
* @returns {Promise<Template>} - Returns the selected template
|
|
162
|
+
*/
|
|
163
|
+
async selectLocalTemplate(localTemplateDir) {
|
|
164
|
+
try {
|
|
165
|
+
const templates = await (0, get_template_1.getInteractiveLocalTemplate)(localTemplateDir);
|
|
166
|
+
if (templates.length === 1) {
|
|
167
|
+
return templates[0];
|
|
132
168
|
}
|
|
169
|
+
const { selectedTemplate } = await inquirer.prompt([
|
|
170
|
+
{
|
|
171
|
+
choices: templates.map(t => ({
|
|
172
|
+
name: `${t.templateName} (${path.basename(t.directoryPath)})`,
|
|
173
|
+
value: t,
|
|
174
|
+
})),
|
|
175
|
+
message: 'Multiple templates found. Please select one:',
|
|
176
|
+
name: 'selectedTemplate',
|
|
177
|
+
type: 'list',
|
|
178
|
+
},
|
|
179
|
+
]);
|
|
180
|
+
return selectedTemplate;
|
|
133
181
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
if (error instanceof Error) {
|
|
184
|
+
core_1.ux.error(error.message);
|
|
158
185
|
}
|
|
159
|
-
|
|
160
|
-
|
|
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;
|
|
186
|
+
else {
|
|
187
|
+
core_1.ux.error('An unknown error occurred while getting the local template.');
|
|
170
188
|
}
|
|
171
189
|
}
|
|
172
|
-
return validatedFlags;
|
|
173
190
|
}
|
|
174
191
|
}
|
|
175
192
|
ApplyCommand.description = 'Apply a template to a blank Directus instance.';
|
|
@@ -181,38 +198,29 @@ ApplyCommand.examples = [
|
|
|
181
198
|
ApplyCommand.flags = {
|
|
182
199
|
content: core_1.Flags.boolean({
|
|
183
200
|
allowNo: true,
|
|
184
|
-
default:
|
|
201
|
+
default: undefined,
|
|
185
202
|
description: 'Load Content (data)',
|
|
186
|
-
relationships: [
|
|
187
|
-
{ flags: ['schema', 'files'], type: 'all' },
|
|
188
|
-
],
|
|
189
203
|
}),
|
|
190
204
|
dashboards: core_1.Flags.boolean({
|
|
191
205
|
allowNo: true,
|
|
192
|
-
default:
|
|
206
|
+
default: undefined,
|
|
193
207
|
description: 'Load Dashboards (dashboards, panels)',
|
|
194
208
|
}),
|
|
195
|
-
directusToken:
|
|
196
|
-
|
|
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
|
-
}),
|
|
209
|
+
directusToken: customFlags.directusToken,
|
|
210
|
+
directusUrl: customFlags.directusUrl,
|
|
203
211
|
extensions: core_1.Flags.boolean({
|
|
204
212
|
allowNo: true,
|
|
205
|
-
default:
|
|
213
|
+
default: undefined,
|
|
206
214
|
description: 'Load Extensions',
|
|
207
215
|
}),
|
|
208
216
|
files: core_1.Flags.boolean({
|
|
209
217
|
allowNo: true,
|
|
210
|
-
default:
|
|
218
|
+
default: undefined,
|
|
211
219
|
description: 'Load Files (files, folders)',
|
|
212
220
|
}),
|
|
213
221
|
flows: core_1.Flags.boolean({
|
|
214
222
|
allowNo: true,
|
|
215
|
-
default:
|
|
223
|
+
default: undefined,
|
|
216
224
|
description: 'Load Flows (operations, flows)',
|
|
217
225
|
}),
|
|
218
226
|
partial: core_1.Flags.boolean({
|
|
@@ -222,31 +230,22 @@ ApplyCommand.flags = {
|
|
|
222
230
|
}),
|
|
223
231
|
permissions: core_1.Flags.boolean({
|
|
224
232
|
allowNo: true,
|
|
225
|
-
default:
|
|
233
|
+
default: undefined,
|
|
226
234
|
description: 'Loads permissions data. Collections include: directus_roles, directus_policies, directus_access, directus_permissions.',
|
|
227
235
|
summary: 'Load permissions (roles, policies, access, permissions)',
|
|
228
236
|
}),
|
|
229
|
-
programmatic:
|
|
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
|
-
}),
|
|
237
|
+
programmatic: customFlags.programmatic,
|
|
235
238
|
schema: core_1.Flags.boolean({
|
|
236
239
|
allowNo: true,
|
|
237
|
-
default:
|
|
240
|
+
default: undefined,
|
|
238
241
|
description: 'Load schema (collections, relations)',
|
|
239
242
|
}),
|
|
240
243
|
settings: core_1.Flags.boolean({
|
|
241
244
|
allowNo: true,
|
|
242
|
-
default:
|
|
245
|
+
default: undefined,
|
|
243
246
|
description: 'Load settings (project settings, translations, presets)',
|
|
244
247
|
}),
|
|
245
|
-
templateLocation:
|
|
246
|
-
dependsOn: ['programmatic', 'templateType'],
|
|
247
|
-
description: 'Location of the template to apply',
|
|
248
|
-
env: 'TEMPLATE_LOCATION',
|
|
249
|
-
}),
|
|
248
|
+
templateLocation: customFlags.templateLocation,
|
|
250
249
|
templateType: core_1.Flags.string({
|
|
251
250
|
default: 'local',
|
|
252
251
|
dependsOn: ['programmatic'],
|
|
@@ -255,20 +254,12 @@ ApplyCommand.flags = {
|
|
|
255
254
|
options: ['community', 'local', 'github'],
|
|
256
255
|
summary: 'Type of template to apply. Options: community, local, github.',
|
|
257
256
|
}),
|
|
257
|
+
userEmail: customFlags.userEmail,
|
|
258
|
+
userPassword: customFlags.userPassword,
|
|
258
259
|
users: core_1.Flags.boolean({
|
|
259
260
|
allowNo: true,
|
|
260
|
-
default:
|
|
261
|
+
default: undefined,
|
|
261
262
|
description: 'Load users',
|
|
262
|
-
relationships: [
|
|
263
|
-
{ flags: ['permissions'], type: 'all' },
|
|
264
|
-
],
|
|
265
263
|
}),
|
|
266
264
|
};
|
|
267
265
|
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
|
-
}
|