@mittwald/cli 1.0.0-alpha.19 → 1.0.0-alpha.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.
Files changed (81) hide show
  1. package/README.md +889 -56
  2. package/dist/esm/commands/app/install/contao.d.ts +11 -0
  3. package/dist/esm/commands/app/install/contao.js +24 -0
  4. package/dist/esm/commands/app/install/joomla.d.ts +11 -0
  5. package/dist/esm/commands/app/install/joomla.js +23 -0
  6. package/dist/esm/commands/app/install/matomo.d.ts +11 -0
  7. package/dist/esm/commands/app/install/matomo.js +21 -0
  8. package/dist/esm/commands/app/install/node.d.ts +11 -0
  9. package/dist/esm/commands/app/install/node.js +13 -0
  10. package/dist/esm/commands/app/install/php.d.ts +11 -0
  11. package/dist/esm/commands/app/install/php.js +13 -0
  12. package/dist/esm/commands/app/install/shopware5.d.ts +11 -0
  13. package/dist/esm/commands/app/install/shopware5.js +26 -0
  14. package/dist/esm/commands/app/install/shopware6.d.ts +11 -0
  15. package/dist/esm/commands/app/install/shopware6.js +26 -0
  16. package/dist/esm/commands/app/install/typo3.d.ts +11 -0
  17. package/dist/esm/commands/app/install/typo3.js +22 -0
  18. package/dist/esm/commands/app/install/wordpress.d.ts +4 -16
  19. package/dist/esm/commands/app/install/wordpress.js +16 -107
  20. package/dist/esm/commands/app/versions.js +1 -1
  21. package/dist/esm/commands/org/invite.d.ts +1 -2
  22. package/dist/esm/commands/org/invite.js +3 -16
  23. package/dist/esm/commands/project/backup/create.d.ts +18 -0
  24. package/dist/esm/commands/project/backup/create.js +59 -0
  25. package/dist/esm/commands/project/backup/delete.d.ts +13 -0
  26. package/dist/esm/commands/project/backup/delete.js +21 -0
  27. package/dist/esm/commands/project/backup/download.d.ts +27 -0
  28. package/dist/esm/commands/project/backup/download.js +177 -0
  29. package/dist/esm/commands/project/backup/get.d.ts +11 -2
  30. package/dist/esm/commands/project/backup/get.js +26 -5
  31. package/dist/esm/commands/project/backup/list.d.ts +9 -9
  32. package/dist/esm/commands/project/backupschedule/list.d.ts +13 -7
  33. package/dist/esm/commands/project/backupschedule/list.js +15 -7
  34. package/dist/esm/commands/project/create.d.ts +2 -1
  35. package/dist/esm/commands/project/create.js +3 -6
  36. package/dist/esm/lib/app/Installer.d.ts +21 -0
  37. package/dist/esm/lib/app/Installer.js +49 -0
  38. package/dist/esm/lib/app/flags.d.ts +25 -0
  39. package/dist/esm/lib/app/flags.js +190 -1
  40. package/dist/esm/lib/app/install.d.ts +4 -0
  41. package/dist/esm/lib/app/install.js +20 -0
  42. package/dist/esm/lib/app/{appVersionHelpers.d.ts → versions.d.ts} +3 -0
  43. package/dist/esm/lib/app/{appVersionHelpers.js → versions.js} +18 -0
  44. package/dist/esm/lib/app/wait.d.ts +3 -0
  45. package/dist/esm/lib/app/wait.js +19 -0
  46. package/dist/esm/lib/expires.d.ts +11 -0
  47. package/dist/esm/lib/expires.js +31 -0
  48. package/dist/esm/lib/password.d.ts +2 -0
  49. package/dist/esm/lib/password.js +20 -0
  50. package/dist/esm/lib/project/hooks.d.ts +3 -0
  51. package/dist/esm/lib/project/hooks.js +11 -0
  52. package/dist/esm/lib/project/ingress.d.ts +2 -0
  53. package/dist/esm/lib/project/ingress.js +16 -0
  54. package/dist/esm/lib/project/shortId.d.ts +2 -0
  55. package/dist/esm/lib/project/shortId.js +11 -0
  56. package/dist/esm/lib/projectbackup/hooks.d.ts +5 -0
  57. package/dist/esm/lib/projectbackup/hooks.js +19 -0
  58. package/dist/esm/lib/viewhelpers/size.d.ts +2 -1
  59. package/dist/esm/lib/viewhelpers/size.js +2 -2
  60. package/dist/esm/lib/wait.d.ts +4 -0
  61. package/dist/esm/lib/wait.js +13 -2
  62. package/dist/esm/rendering/process/components/ProcessStateSummary.js +3 -0
  63. package/dist/esm/rendering/process/process.d.ts +2 -0
  64. package/dist/esm/rendering/process/process.js +4 -0
  65. package/dist/esm/rendering/react/components/ProjectBackup/ProjectBackupDetails.d.ts +6 -0
  66. package/dist/esm/rendering/react/components/ProjectBackup/ProjectBackupDetails.js +29 -0
  67. package/dist/esm/rendering/react/components/ProjectBackup/ProjectBackupStatus.d.ts +6 -0
  68. package/dist/esm/rendering/react/components/ProjectBackup/ProjectBackupStatus.js +8 -0
  69. package/package.json +1 -1
  70. package/dist/esm/commands/project/backupschedule/get.d.ts +0 -3
  71. package/dist/esm/commands/project/backupschedule/get.js +0 -6
  72. package/dist/esm/generated/backup/getProjectBackup.d.ts +0 -16
  73. package/dist/esm/generated/backup/getProjectBackup.js +0 -25
  74. package/dist/esm/generated/backup/getProjectBackupSchedule.d.ts +0 -16
  75. package/dist/esm/generated/backup/getProjectBackupSchedule.js +0 -25
  76. package/dist/esm/generated/backup/listProjectBackupSchedules.d.ts +0 -13
  77. package/dist/esm/generated/backup/listProjectBackupSchedules.js +0 -24
  78. package/dist/esm/generated/backup/listProjectBackups.d.ts +0 -13
  79. package/dist/esm/generated/backup/listProjectBackups.js +0 -24
  80. /package/dist/esm/lib/app/{appHelpers.d.ts → uuid.d.ts} +0 -0
  81. /package/dist/esm/lib/app/{appHelpers.js → uuid.js} +0 -0
@@ -1,7 +1,196 @@
1
- import { Args } from "@oclif/core";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { getDefaultIngressForProject } from "../project/ingress.js";
3
+ import { Value } from "../../rendering/react/components/Value.js";
4
+ import { getProjectShortIdFromUuid } from "../project/shortId.js";
5
+ import { Text } from "ink";
6
+ import { assertStatus } from "@mittwald/api-client-commons";
7
+ import { projectFlags } from "../project/flags.js";
8
+ import { processFlags, } from "../../rendering/process/process_flags.js";
9
+ import { Flags, Args } from "@oclif/core";
10
+ import { generatePasswordWithSpecialChars } from "../password.js";
2
11
  export const appInstallationFlags = {
3
12
  "installation-id": Args.string({
4
13
  description: "ID of the app installation to get",
5
14
  required: true,
6
15
  }),
7
16
  };
17
+ function buildFlagsWithDescription(appName) {
18
+ return {
19
+ version: Flags.string({
20
+ required: true,
21
+ summary: `Version of ${appName} to be installed.`,
22
+ description: `Specify the Version in which your ${appName} will be installed.
23
+ If none is given the ${appName} will be installed in the latest available version.`,
24
+ default: "latest",
25
+ }),
26
+ host: Flags.string({
27
+ required: false,
28
+ summary: `Host to initially configure your ${appName} installation with; needs to be created separately.`,
29
+ description: `Specify a host which will be used during the installation and as an initial host for the ${appName} configuration.
30
+ If not given the default host for the given Project will be used.
31
+ This does not change the target of the used Host and can be changed later by configuring the Host and your ${appName} installation.`,
32
+ }),
33
+ "admin-user": Flags.string({
34
+ required: false,
35
+ summary: "Username for your administrator-user.",
36
+ description: `Username of the first administrator-user which will be created during the ${appName} installation.
37
+ If not given an adequate username will be created from your mStudio Account Data.
38
+ After the installation is finished the Username can be changed and additional administrator-users can be created.`,
39
+ }),
40
+ "admin-email": Flags.string({
41
+ required: false,
42
+ summary: "E-Mail-Address of your administrator-user.",
43
+ description: `E-Mail-Address that will correlate to the first administrator-user which will be created during the ${appName} installation.
44
+ If not given your mStudio Account-E-Mail-Address will be used. This E-Mail-Address can be changed after the installation is finished.`,
45
+ }),
46
+ "admin-pass": Flags.string({
47
+ required: false,
48
+ summary: "Password of your administrator-user.",
49
+ description: `Password that will correlate to the first administrator-user which will be created during the ${appName} installation.
50
+ If not given a random secure Password will be generated and sent to stdout. This Password can be changed after the installation is finished`,
51
+ }),
52
+ "admin-firstname": Flags.string({
53
+ required: false,
54
+ summary: "Firstname of your administrator-user.",
55
+ description: `Firstname that will correlate to the first administrator-user which will be created during the ${appName} installation.
56
+ If none is given your mStudio Account-Firstname will be used. This Firstname can be changed after the installation is finished`,
57
+ }),
58
+ "admin-lastname": Flags.string({
59
+ required: false,
60
+ summary: "Lastname of your administrator-user.",
61
+ description: `Lastname that will correlate to the first administrator-user which will be created during the ${appName} installation.
62
+ If none is given your mStudio Account-Firstname will be used. This Lastname can be changed after the installation is finished`,
63
+ }),
64
+ "site-title": Flags.string({
65
+ required: false,
66
+ summary: `Site Title for your ${appName} installation.`,
67
+ description: `Site Title which will be displayed in the Tab and at the top of the Frontend of your ${appName} installation.
68
+ It is also the Title shown in the App-Overview in the mStudio.
69
+ If none is given the Software Name and the given Project will be used. The Title can be changed after the installation is finished`,
70
+ }),
71
+ "shop-email": Flags.string({
72
+ required: false,
73
+ summary: `E-Mail-Address your ${appName} will be working with.`,
74
+ description: `The E-Mail-Address your ${appName} shop will be using for correspondence..
75
+ If not given your mStudio Account-E-Mail-Address will be used. This E-Mail-Address can be changed after the installation is finished.`,
76
+ }),
77
+ "shop-lang": Flags.string({
78
+ required: false,
79
+ summary: `Language your ${appName} will be working with.`,
80
+ description: `The default Language your ${appName} shop will be using.
81
+ The Front- and Backend will be displayed using the given language.
82
+ If not given will default to German(de_DE). The language can be changed after the installation is finished.`,
83
+ }),
84
+ "shop-currency": Flags.string({
85
+ required: false,
86
+ summary: `Currency your ${appName} will be working with.`,
87
+ description: `The default Currency your ${appName} shop communicates prices and calculates transactions with.
88
+ If not given will default to EUR(€). The currency can be changed after the installation is finished.`,
89
+ }),
90
+ "install-mode": Flags.string({
91
+ required: true,
92
+ summary: `The installation variant your ${appName} will be installed with.`,
93
+ description: `${appName} can be installed in one of two different ways. your ${appName} shop communicates prices and calculates transactions with.
94
+ Either as a composer project or in a more manual fashion using the source directory and the ${appName} console install wizard.
95
+ If not given will default to composer installation. This can not be changed later.`,
96
+ options: ["composer", "symlink"],
97
+ default: "composer",
98
+ }),
99
+ wait: Flags.boolean({
100
+ char: "w",
101
+ description: `Wait for your ${appName} to be ready.`,
102
+ }),
103
+ };
104
+ }
105
+ export function provideSupportedFlags(requestedFlagNames, appName) {
106
+ const availableFlags = buildFlagsWithDescription(appName);
107
+ const supportedFlags = requestedFlagNames.reduce((collector, currentValue) => {
108
+ return {
109
+ ...collector,
110
+ [currentValue]: availableFlags[currentValue],
111
+ };
112
+ }, {});
113
+ const flagsToReturn = {
114
+ ...projectFlags,
115
+ ...processFlags,
116
+ ...supportedFlags,
117
+ json: Flags.boolean({}),
118
+ };
119
+ return flagsToReturn;
120
+ }
121
+ export async function autofillFlags(apiClient, process, necessaryFlags, flags, projectId, appName) {
122
+ const ownUser = await apiClient.user.getOwnAccount();
123
+ assertStatus(ownUser, 200);
124
+ // Version
125
+ if (necessaryFlags.includes("version") && !flags.version) {
126
+ flags.version = "latest";
127
+ }
128
+ // Host
129
+ if (necessaryFlags.includes("host") && !flags.host) {
130
+ flags.host =
131
+ "https://" + (await getDefaultIngressForProject(apiClient, projectId));
132
+ process.addInfo(_jsxs(Text, { children: ["Using default Host ", _jsx(Value, { children: flags["host"] })] }));
133
+ }
134
+ // Title
135
+ if (necessaryFlags.includes("site-title") && !flags["site-title"]) {
136
+ flags["site-title"] =
137
+ appName + " " + (await getProjectShortIdFromUuid(apiClient, projectId));
138
+ }
139
+ // Admin User
140
+ if (necessaryFlags.includes("admin-user") && !flags["admin-user"]) {
141
+ if (ownUser.data.person) {
142
+ flags["admin-user"] =
143
+ ownUser.data.person.firstName.charAt(0).toLowerCase() +
144
+ ownUser.data.person.lastName.toLowerCase();
145
+ }
146
+ else {
147
+ flags["admin-user"] = await getProjectShortIdFromUuid(apiClient, projectId);
148
+ }
149
+ process.addInfo(_jsxs(Text, { children: ["Using generated Admin User: ", _jsx(Value, { children: flags["admin-user"] })] }));
150
+ }
151
+ // Admin Pass
152
+ if (necessaryFlags.includes("admin-pass") && !flags["admin-pass"]) {
153
+ flags["admin-pass"] = generatePasswordWithSpecialChars();
154
+ process.addInfo(_jsxs(Text, { children: ["Using generated random Admin Pass: ", _jsx(Value, { children: flags["admin-pass"] })] }));
155
+ }
156
+ // Admin Firstname
157
+ if (necessaryFlags.includes("admin-firstname") && !flags["admin-firstname"]) {
158
+ if (ownUser.data.person) {
159
+ flags["admin-firstname"] = ownUser.data.person.firstName;
160
+ }
161
+ else {
162
+ flags["admin-firstname"] = "Max";
163
+ }
164
+ process.addInfo(_jsxs(Text, { children: ["Using mStudio firstname as Admin firstname (", _jsx(Value, { children: flags["admin-firstname"] }), ")"] }));
165
+ }
166
+ // Admin Lastname
167
+ if (necessaryFlags.includes("admin-lastname") && !flags["admin-lastname"]) {
168
+ if (ownUser.data.person) {
169
+ flags["admin-lastname"] = ownUser.data.person.lastName;
170
+ }
171
+ else {
172
+ flags["admin-lastname"] = "Mustermann";
173
+ }
174
+ process.addInfo(_jsxs(Text, { children: ["Using mStudio lastname as Admin lastname (", _jsx(Value, { children: flags["admin-lastname"] }), ")"] }));
175
+ }
176
+ // Admin E-Mail
177
+ if (necessaryFlags.includes("admin-email") && !flags["admin-email"]) {
178
+ flags["admin-email"] = ownUser.data.email;
179
+ process.addInfo(_jsxs(Text, { children: ["Using mStudio email as Admin email (", _jsx(Value, { children: flags["admin-email"] }), ")"] }));
180
+ }
181
+ // Shop E-Mail
182
+ if (necessaryFlags.includes("shop-email") && !flags["shop-email"]) {
183
+ flags["shop-email"] = ownUser.data.email;
184
+ process.addInfo(_jsxs(Text, { children: ["Using mStudio email as Shop email (", _jsx(Value, { children: flags["shop-email"] }), ")"] }));
185
+ }
186
+ // Shop Language Code
187
+ if (necessaryFlags.includes("shop-lang") && !flags["shop-lang"]) {
188
+ flags["shop-lang"] = "de-DE";
189
+ process.addInfo(_jsx(Text, { children: "Using default shop language 'de_DE'." }));
190
+ }
191
+ // Shop Currency
192
+ if (necessaryFlags.includes("shop-currency") && !flags["shop-currency"]) {
193
+ flags["shop-currency"] = "EUR";
194
+ process.addInfo(_jsx(Text, { children: "Using default shop currency '\u20AC'." }));
195
+ }
196
+ }
@@ -0,0 +1,4 @@
1
+ import { MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client";
2
+ import { ProcessRenderer } from "../../rendering/process/process.js";
3
+ import AppAppVersion = MittwaldAPIV2.Components.Schemas.AppAppVersion;
4
+ export declare function triggerAppInstallation(apiClient: MittwaldAPIV2Client, process: ProcessRenderer, projectId: string, flags: Record<string, string>, appVersion: AppAppVersion): Promise<string[]>;
@@ -0,0 +1,20 @@
1
+ import { assertStatus } from "@mittwald/api-client-commons";
2
+ export async function triggerAppInstallation(apiClient, process, projectId, flags, appVersion) {
3
+ const [appInstallationId, eventId] = await process.runStep("starting installation", async () => {
4
+ const result = await apiClient.app.requestAppinstallation({
5
+ pathParameters: { projectId },
6
+ data: {
7
+ appVersionId: appVersion.id,
8
+ description: flags["site-title"],
9
+ updatePolicy: "none",
10
+ userInputs: Object.keys(flags).map((k) => ({
11
+ name: k.replace("-", "_"),
12
+ value: flags[k],
13
+ })),
14
+ },
15
+ });
16
+ assertStatus(result, 201);
17
+ return [result.data.id, result.headers["etag"]];
18
+ });
19
+ return [appInstallationId, eventId];
20
+ }
@@ -1,5 +1,8 @@
1
1
  import { MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client";
2
+ import AppAppVersion = MittwaldAPIV2.Components.Schemas.AppAppVersion;
3
+ import { ProcessRenderer } from "../../rendering/process/process.js";
2
4
  type AppVersion = MittwaldAPIV2.Components.Schemas.AppAppVersion;
5
+ export declare function normalizeToAppVersionUuid(apiClient: MittwaldAPIV2Client, version: string, process: ProcessRenderer, appUuid: string): Promise<AppAppVersion>;
3
6
  export declare function getLatestAvailableAppVersionForApp(apiClient: MittwaldAPIV2Client, appId: string): Promise<AppVersion | undefined>;
4
7
  export declare function getAppVersionUuidFromAppVersion(apiClient: MittwaldAPIV2Client, appId: string, appVersion: string | undefined): Promise<AppVersion | undefined>;
5
8
  export {};
@@ -1,5 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
2
  import { assertStatus } from "@mittwald/api-client-commons";
2
3
  import { gt } from "semver";
4
+ import { Value } from "../../rendering/react/components/Value.js";
5
+ import { Text } from "ink";
6
+ import { getAppNameFromUuid } from "./uuid.js";
7
+ export async function normalizeToAppVersionUuid(apiClient, version, process, appUuid) {
8
+ let appVersion;
9
+ if (version && version !== "latest") {
10
+ appVersion = await getAppVersionUuidFromAppVersion(apiClient, appUuid, version);
11
+ }
12
+ else {
13
+ appVersion = await getLatestAvailableAppVersionForApp(apiClient, appUuid);
14
+ }
15
+ if (!appVersion) {
16
+ throw new Error(`${await getAppNameFromUuid(apiClient, appUuid)} version ${version} does not seem to exist for the mStudio.`);
17
+ }
18
+ process.addInfo(_jsxs(Text, { children: ["installing version: ", _jsx(Value, { children: appVersion.externalVersion })] }));
19
+ return appVersion;
20
+ }
3
21
  // Get latest available Internal App Version for App UUID
4
22
  export async function getLatestAvailableAppVersionForApp(apiClient, appId) {
5
23
  const versions = await apiClient.app.listAppversions({
@@ -0,0 +1,3 @@
1
+ import { MittwaldAPIV2Client } from "@mittwald/api-client";
2
+ import { ProcessRenderer } from "../../rendering/process/process.js";
3
+ export declare function waitUntilAppIsInstalled(apiClient: MittwaldAPIV2Client, process: ProcessRenderer, appInstallationId: string, eventId: string): Promise<void>;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { waitUntil } from "../wait.js";
3
+ import { Text } from "ink";
4
+ export async function waitUntilAppIsInstalled(apiClient, process, appInstallationId, eventId) {
5
+ const stepWaiting = process.addStep(_jsx(Text, { children: "waiting for app installation to be ready" }));
6
+ await waitUntil(async () => {
7
+ const installationResponse = await apiClient.app.getAppinstallation({
8
+ pathParameters: { appInstallationId },
9
+ // TODO: Remove once @mittwald/api-client supports this
10
+ headers: { "if-event-reached": eventId }, // eslint-disable-line
11
+ });
12
+ if (installationResponse.status === 200 &&
13
+ installationResponse.data.appVersion.current ==
14
+ installationResponse.data.appVersion.desired) {
15
+ return true;
16
+ }
17
+ });
18
+ stepWaiting.complete();
19
+ }
@@ -0,0 +1,11 @@
1
+ export type ExpireFlags = {
2
+ expires: string;
3
+ };
4
+ export declare function expireFlags(resourceName: string): {
5
+ expires: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
6
+ };
7
+ export declare function expireFlagsRequired(resourceName: string): {
8
+ expires: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ };
10
+ export declare function expirationDateFromFlagsOptional(flags: Partial<ExpireFlags>): Date | undefined;
11
+ export declare function expirationDateFromFlags(flags: ExpireFlags): Date;
@@ -0,0 +1,31 @@
1
+ import { Flags } from "@oclif/core";
2
+ import parseDuration from "parse-duration";
3
+ export function expireFlags(resourceName) {
4
+ return {
5
+ expires: Flags.string({
6
+ description: `An interval after which the ${resourceName} expires (examples: 30m, 30d, 1y).`,
7
+ }),
8
+ };
9
+ }
10
+ export function expireFlagsRequired(resourceName) {
11
+ return {
12
+ expires: Flags.string({
13
+ description: `An interval after which the ${resourceName} expires (examples: 30m, 30d, 1y).`,
14
+ required: true,
15
+ }),
16
+ };
17
+ }
18
+ export function expirationDateFromFlagsOptional(flags) {
19
+ if (!flags.expires) {
20
+ return undefined;
21
+ }
22
+ return expirationDateFromFlags(flags);
23
+ }
24
+ export function expirationDateFromFlags(flags) {
25
+ const d = new Date();
26
+ const i = parseDuration(flags.expires);
27
+ if (!i) {
28
+ throw new Error("could not parse duration: " + flags.expires);
29
+ }
30
+ return new Date(d.getTime() + i);
31
+ }
@@ -0,0 +1,2 @@
1
+ export declare function generatePassword(length?: number): string;
2
+ export declare function generatePasswordWithSpecialChars(length?: number, amountSpecialChars?: number): string;
@@ -0,0 +1,20 @@
1
+ import crypto from "crypto";
2
+ const passwordAllowedSpecialChars = ["%", "_", "-", "+", "&"];
3
+ const defaultPasswordLength = 32;
4
+ function randomInt(min, max) {
5
+ return Math.floor(Math.random() * (max - 1) + min);
6
+ }
7
+ function getRandomSpecialCharacter() {
8
+ return passwordAllowedSpecialChars[randomInt(0, passwordAllowedSpecialChars.length - 1)];
9
+ }
10
+ export function generatePassword(length = defaultPasswordLength) {
11
+ return crypto.randomBytes(length).toString("base64").substring(0, length);
12
+ }
13
+ export function generatePasswordWithSpecialChars(length = defaultPasswordLength, amountSpecialChars = Math.floor(length / 8)) {
14
+ const passwordCharacters = generatePassword(length).split("");
15
+ for (let i = 0; i < amountSpecialChars; i++) {
16
+ passwordCharacters[randomInt(1, passwordCharacters.length - 1)] =
17
+ getRandomSpecialCharacter();
18
+ }
19
+ return passwordCharacters.join("");
20
+ }
@@ -0,0 +1,3 @@
1
+ import { MittwaldAPIV2 } from "@mittwald/api-client";
2
+ import ProjectProject = MittwaldAPIV2.Components.Schemas.ProjectProject;
3
+ export declare function useProject(projectId: string): ProjectProject;
@@ -0,0 +1,11 @@
1
+ import { useRenderContext } from "../../rendering/react/context.js";
2
+ import { usePromise } from "@mittwald/react-use-promise";
3
+ import { assertStatus } from "@mittwald/api-client-commons";
4
+ export function useProject(projectId) {
5
+ const { apiClient } = useRenderContext();
6
+ const project = usePromise((id) => apiClient.project.getProject({
7
+ pathParameters: { id },
8
+ }), [projectId]);
9
+ assertStatus(project, 200);
10
+ return project.data;
11
+ }
@@ -0,0 +1,2 @@
1
+ import { MittwaldAPIV2Client } from "@mittwald/api-client";
2
+ export declare function getDefaultIngressForProject(apiClient: MittwaldAPIV2Client, projectId: string): Promise<string>;
@@ -0,0 +1,16 @@
1
+ import { assertStatus } from "@mittwald/api-client-commons";
2
+ import { normalizeProjectIdToUuid } from "../../Helpers.js";
3
+ export async function getDefaultIngressForProject(apiClient, projectId) {
4
+ const projectUuid = await normalizeProjectIdToUuid(apiClient, projectId);
5
+ const projectIngresses = await apiClient.domain.ingressListForProject({
6
+ pathParameters: { projectId: projectUuid },
7
+ });
8
+ assertStatus(projectIngresses, 200);
9
+ const foundIngress = projectIngresses.data.find((item) => {
10
+ return item.isDefault === true;
11
+ });
12
+ if (foundIngress) {
13
+ return foundIngress.hostname;
14
+ }
15
+ throw new Error(`Given ID ${projectUuid} does not seem to be valid`);
16
+ }
@@ -0,0 +1,2 @@
1
+ import { MittwaldAPIV2Client } from "@mittwald/api-client";
2
+ export declare function getProjectShortIdFromUuid(apiClient: MittwaldAPIV2Client, uuid: string): Promise<string>;
@@ -0,0 +1,11 @@
1
+ import { assertStatus } from "@mittwald/api-client-commons";
2
+ export async function getProjectShortIdFromUuid(apiClient, uuid) {
3
+ const project = await apiClient.project.getProject({
4
+ pathParameters: { id: uuid },
5
+ });
6
+ assertStatus(project, 200);
7
+ if (project.data.shortId) {
8
+ return project.data.shortId;
9
+ }
10
+ throw new Error(`Given ID ${uuid} does not seem to be valid`);
11
+ }
@@ -0,0 +1,5 @@
1
+ import { MittwaldAPIV2 } from "@mittwald/api-client";
2
+ import BackupProjectBackup = MittwaldAPIV2.Components.Schemas.BackupProjectBackup;
3
+ import BackupProjectBackupSchedule = MittwaldAPIV2.Components.Schemas.BackupProjectBackupSchedule;
4
+ export declare function useProjectBackup(projectBackupId: string): BackupProjectBackup;
5
+ export declare function useProjectBackupSchedule(projectBackupScheduleId: string): BackupProjectBackupSchedule;
@@ -0,0 +1,19 @@
1
+ import { useRenderContext } from "../../rendering/react/context.js";
2
+ import { usePromise } from "@mittwald/react-use-promise";
3
+ import { assertStatus } from "@mittwald/api-client-commons";
4
+ export function useProjectBackup(projectBackupId) {
5
+ const { apiClient } = useRenderContext();
6
+ const projectBackup = usePromise((id) => apiClient.backup.getProjectBackup({
7
+ pathParameters: { projectBackupId: id },
8
+ }), [projectBackupId]);
9
+ assertStatus(projectBackup, 200);
10
+ return projectBackup.data;
11
+ }
12
+ export function useProjectBackupSchedule(projectBackupScheduleId) {
13
+ const { apiClient } = useRenderContext();
14
+ const projectBackupSchedule = usePromise((id) => apiClient.backup.getProjectBackupSchedule({
15
+ pathParameters: { projectBackupScheduleId: id },
16
+ }), [projectBackupScheduleId]);
17
+ assertStatus(projectBackupSchedule, 200);
18
+ return projectBackupSchedule.data;
19
+ }
@@ -1 +1,2 @@
1
- export declare function formatBytes(bytes?: number): string;
1
+ import { Options } from "pretty-bytes";
2
+ export declare function formatBytes(bytes?: number, opts?: Options): string;
@@ -1,4 +1,4 @@
1
1
  import prettyBytes from "pretty-bytes";
2
- export function formatBytes(bytes = 0) {
3
- return prettyBytes(bytes, { binary: true });
2
+ export function formatBytes(bytes = 0, opts = {}) {
3
+ return prettyBytes(bytes, { binary: true, ...opts });
4
4
  }
@@ -1 +1,5 @@
1
+ export declare const waitFlags: {
2
+ wait: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
3
+ "wait-timeout": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
4
+ };
1
5
  export declare function waitUntil<T>(tester: () => Promise<T | null>, timeoutSeconds?: number): Promise<T>;
@@ -1,6 +1,17 @@
1
- export async function waitUntil(tester, timeoutSeconds = 120) {
1
+ import { Flags } from "@oclif/core";
2
+ export const waitFlags = {
3
+ wait: Flags.boolean({
4
+ char: "w",
5
+ description: "Wait for the resource to be ready.",
6
+ }),
7
+ "wait-timeout": Flags.integer({
8
+ description: "The number of seconds to wait for the resource to be ready.",
9
+ default: 600,
10
+ }),
11
+ };
12
+ export async function waitUntil(tester, timeoutSeconds = 600) {
2
13
  let waited = 0;
3
- while (waited < 120) {
14
+ while (waited < timeoutSeconds) {
4
15
  const result = await tester();
5
16
  if (result) {
6
17
  return result;
@@ -22,6 +22,9 @@ export const ProcessStateSummary = ({ step, }) => {
22
22
  return (_jsxs(_Fragment, { children: [_jsx(Text, { children: ". " }), _jsx(Text, { color: "red", children: "error" })] }));
23
23
  }
24
24
  else {
25
+ if (step.progress) {
26
+ return (_jsxs(_Fragment, { children: [_jsx(Text, { children: "... " }), _jsx(Text, { color: "blue", children: step.progress })] }));
27
+ }
25
28
  return _jsx(Text, { children: "..." });
26
29
  }
27
30
  };
@@ -8,6 +8,7 @@ export type ProcessStepRunnable = {
8
8
  title: ReactNode;
9
9
  phase: "running" | "completed" | "failed" | "aborted";
10
10
  error?: unknown;
11
+ progress?: string;
11
12
  };
12
13
  export type ProcessStepConfirm = {
13
14
  type: "confirm";
@@ -28,6 +29,7 @@ export declare class RunnableHandler {
28
29
  get done(): boolean;
29
30
  abort(): void;
30
31
  complete(): void;
32
+ progress(p: string): void;
31
33
  error(err: unknown): void;
32
34
  }
33
35
  export interface ProcessRenderer {
@@ -16,6 +16,10 @@ export class RunnableHandler {
16
16
  this.processStep.phase = "completed";
17
17
  this.listener();
18
18
  }
19
+ progress(p) {
20
+ this.processStep.progress = p;
21
+ this.listener();
22
+ }
19
23
  error(err) {
20
24
  this.processStep.phase = "failed";
21
25
  this.processStep.error = err;
@@ -0,0 +1,6 @@
1
+ import { FC } from "react";
2
+ import { MittwaldAPIV2 } from "@mittwald/api-client";
3
+ import BackupProjectBackup = MittwaldAPIV2.Components.Schemas.BackupProjectBackup;
4
+ export declare const ProjectBackupDetails: FC<{
5
+ projectBackup: BackupProjectBackup;
6
+ }>;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SingleResult, SingleResultTable } from "../SingleResult.js";
3
+ import { Text } from "ink";
4
+ import { Value } from "../Value.js";
5
+ import { useProjectBackupSchedule } from "../../../../lib/projectbackup/hooks.js";
6
+ import { FormattedDate } from "../FormattedDate.js";
7
+ import { ProjectBackupStatus } from "./ProjectBackupStatus.js";
8
+ import { useProject } from "../../../../lib/project/hooks.js";
9
+ import { IDAndShortID } from "../IDAndShortID.js";
10
+ export const ProjectBackupDetails = ({ projectBackup }) => {
11
+ const project = useProject(projectBackup.projectId);
12
+ const schedule = projectBackup.parentId
13
+ ? useProjectBackupSchedule(projectBackup.parentId)
14
+ : undefined;
15
+ return (_jsx(SingleResult, { title: _jsxs(Text, { children: ["PROJECT BACKUP: ", _jsx(Value, { children: projectBackup.id })] }), rows: {
16
+ ID: _jsx(Value, { children: projectBackup.id }),
17
+ "Created at": projectBackup.createdAt ? (_jsx(Value, { children: _jsx(FormattedDate, { date: projectBackup.createdAt, relative: true, absolute: true }) })) : (_jsx(Value, { notSet: true })),
18
+ "Expires in": projectBackup.expiresAt ? (_jsx(Value, { children: _jsx(FormattedDate, { date: projectBackup.expiresAt, relative: true, absolute: true }) })) : (_jsx(Value, { notSet: true })),
19
+ Status: _jsx(ProjectBackupStatus, { projectBackup: projectBackup }),
20
+ Project: (_jsx(SingleResultTable, { rows: {
21
+ ID: _jsx(IDAndShortID, { object: project }),
22
+ Description: _jsx(Value, { children: project.description }),
23
+ } })),
24
+ Schedule: schedule ? (_jsx(SingleResultTable, { rows: {
25
+ ID: _jsx(Value, { children: schedule.id }),
26
+ Schedule: _jsx(Value, { children: schedule.schedule }),
27
+ } })) : (_jsx(Value, { notSet: true })),
28
+ } }));
29
+ };
@@ -0,0 +1,6 @@
1
+ import { FC } from "react";
2
+ import { MittwaldAPIV2 } from "@mittwald/api-client";
3
+ import BackupProjectBackup = MittwaldAPIV2.Components.Schemas.BackupProjectBackup;
4
+ export declare const ProjectBackupStatus: FC<{
5
+ projectBackup: BackupProjectBackup;
6
+ }>;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Text } from "ink";
3
+ export const ProjectBackupStatus = ({ projectBackup }) => {
4
+ if (projectBackup.status === "Completed") {
5
+ return _jsx(Text, { color: "green", children: "\u2705 completed" });
6
+ }
7
+ return _jsx(Text, { color: "yellow", children: projectBackup.status });
8
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/cli",
3
- "version": "1.0.0-alpha.19",
3
+ "version": "1.0.0-alpha.20",
4
4
  "description": "Hand-crafted CLI for the mittwald API",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -1,3 +0,0 @@
1
- import { GeneratedBackupGetProjectBackupSchedule } from "../../../generated/backup/getProjectBackupSchedule.js";
2
- export default class Get extends GeneratedBackupGetProjectBackupSchedule {
3
- }
@@ -1,6 +0,0 @@
1
- /* eslint-disable */
2
- /* prettier-ignore */
3
- /* This file is auto-generated with acg (@mittwald/api-code-generator) */
4
- import { GeneratedBackupGetProjectBackupSchedule } from "../../../generated/backup/getProjectBackupSchedule.js";
5
- export default class Get extends GeneratedBackupGetProjectBackupSchedule {
6
- }
@@ -1,16 +0,0 @@
1
- import { MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client";
2
- import { GetBaseCommand } from "../../GetBaseCommand.js";
3
- export type PathParams = MittwaldAPIV2.Paths.V2ProjectBackupsProjectBackupId.Get.Parameters.Path;
4
- type APIResponse = Awaited<ReturnType<MittwaldAPIV2Client["backup"]["getProjectBackup"]>>;
5
- export declare abstract class GeneratedBackupGetProjectBackup extends GetBaseCommand<typeof GeneratedBackupGetProjectBackup, APIResponse> {
6
- static description: string;
7
- static flags: {
8
- [x: string]: import("@oclif/core/lib/interfaces/parser.js").CompletableFlag<any>;
9
- };
10
- static args: {
11
- projectBackupId: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
12
- };
13
- protected getData(): Promise<APIResponse>;
14
- protected mapParams(input: PathParams): Promise<PathParams> | PathParams;
15
- }
16
- export {};