@mittwald/cli 1.0.0-alpha.10 → 1.0.0-alpha.11

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 (70) hide show
  1. package/dist/esm/Formatter.js +3 -0
  2. package/dist/esm/ListBaseCommand.js +6 -1
  3. package/dist/esm/commands/domain/ownership/list.js +1 -0
  4. package/dist/esm/commands/domain/virtualhost/create.d.ts +26 -0
  5. package/dist/esm/commands/domain/virtualhost/create.js +122 -0
  6. package/dist/esm/commands/domain/virtualhost/delete.d.ts +13 -0
  7. package/dist/esm/commands/domain/virtualhost/delete.js +21 -0
  8. package/dist/esm/commands/domain/virtualhost/get.d.ts +13 -2
  9. package/dist/esm/commands/domain/virtualhost/get.js +85 -5
  10. package/dist/esm/commands/domain/virtualhost/list.d.ts +1 -1
  11. package/dist/esm/commands/domain/virtualhost/list.js +29 -13
  12. package/dist/esm/commands/mail/address/create.d.ts +16 -8
  13. package/dist/esm/commands/mail/address/create.js +51 -9
  14. package/dist/esm/commands/org/delete.d.ts +13 -0
  15. package/dist/esm/commands/org/delete.js +17 -0
  16. package/dist/esm/commands/org/invite/list-own.d.ts +46 -5
  17. package/dist/esm/commands/org/invite/list-own.js +38 -3
  18. package/dist/esm/commands/org/invite/list.d.ts +13 -3
  19. package/dist/esm/commands/org/invite/list.js +31 -2
  20. package/dist/esm/commands/org/invite/revoke.d.ts +17 -0
  21. package/dist/esm/commands/org/invite/revoke.js +39 -0
  22. package/dist/esm/commands/org/invite.d.ts +21 -0
  23. package/dist/esm/commands/org/invite.js +66 -0
  24. package/dist/esm/commands/org/list.d.ts +13 -4
  25. package/dist/esm/commands/org/list.js +17 -3
  26. package/dist/esm/commands/org/membership/list-own.d.ts +45 -5
  27. package/dist/esm/commands/org/membership/list-own.js +41 -3
  28. package/dist/esm/commands/org/membership/list.d.ts +33 -5
  29. package/dist/esm/commands/org/membership/list.js +40 -3
  30. package/dist/esm/commands/org/membership/revoke.d.ts +17 -0
  31. package/dist/esm/commands/org/membership/revoke.js +43 -0
  32. package/dist/esm/generated/domain/listDomainOwnerships.d.ts +1 -1
  33. package/dist/esm/generated/domain/listDomainOwnerships.js +3 -6
  34. package/dist/esm/lib/context_flags.js +2 -1
  35. package/dist/esm/lib/handleError.js +11 -0
  36. package/dist/esm/rendering/react/components/Ingress/DnsValidationErrors.d.ts +8 -0
  37. package/dist/esm/rendering/react/components/Ingress/DnsValidationErrors.js +17 -0
  38. package/dist/esm/rendering/react/components/Ingress/DomainOwnership.d.ts +8 -0
  39. package/dist/esm/rendering/react/components/Ingress/DomainOwnership.js +7 -0
  40. package/dist/esm/rendering/react/components/Note.d.ts +1 -2
  41. package/dist/esm/rendering/react/components/Warning.d.ts +4 -0
  42. package/dist/esm/rendering/react/components/Warning.js +7 -0
  43. package/dist/esm/rendering/react/error.d.ts +1 -0
  44. package/dist/esm/rendering/react/error.js +43 -0
  45. package/dist/esm/rendering/react/process_flags.js +2 -1
  46. package/package.json +4 -4
  47. package/dist/esm/commands/org/invite/get.d.ts +0 -3
  48. package/dist/esm/commands/org/invite/get.js +0 -6
  49. package/dist/esm/commands/org/membership/get.d.ts +0 -3
  50. package/dist/esm/commands/org/membership/get.js +0 -6
  51. package/dist/esm/generated/customer/getCustomerCategory.d.ts +0 -16
  52. package/dist/esm/generated/customer/getCustomerCategory.js +0 -25
  53. package/dist/esm/generated/customer/getCustomerInvite.d.ts +0 -16
  54. package/dist/esm/generated/customer/getCustomerInvite.js +0 -25
  55. package/dist/esm/generated/customer/getCustomerMembership.d.ts +0 -16
  56. package/dist/esm/generated/customer/getCustomerMembership.js +0 -25
  57. package/dist/esm/generated/customer/listCustomerInvites.d.ts +0 -13
  58. package/dist/esm/generated/customer/listCustomerInvites.js +0 -17
  59. package/dist/esm/generated/customer/listCustomerMemberships.d.ts +0 -13
  60. package/dist/esm/generated/customer/listCustomerMemberships.js +0 -17
  61. package/dist/esm/generated/customer/listCustomers.d.ts +0 -13
  62. package/dist/esm/generated/customer/listCustomers.js +0 -17
  63. package/dist/esm/generated/customer/listInvitesForCustomer.d.ts +0 -13
  64. package/dist/esm/generated/customer/listInvitesForCustomer.js +0 -24
  65. package/dist/esm/generated/customer/listMembershipsForCustomer.d.ts +0 -13
  66. package/dist/esm/generated/customer/listMembershipsForCustomer.js +0 -24
  67. package/dist/esm/generated/customer/listOfCustomerCategories.d.ts +0 -13
  68. package/dist/esm/generated/customer/listOfCustomerCategories.js +0 -17
  69. package/dist/esm/generated/domain/ingressGetSpecific.d.ts +0 -16
  70. package/dist/esm/generated/domain/ingressGetSpecific.js +0 -25
@@ -25,6 +25,9 @@ export class ListFormatter {
25
25
  };
26
26
  }
27
27
  log(output, columns, opts) {
28
+ if (output.length === 0) {
29
+ return;
30
+ }
28
31
  ux.table(output, columns, {
29
32
  printLine: console.log,
30
33
  ...opts,
@@ -28,7 +28,12 @@ export class ListBaseCommand extends BaseCommand {
28
28
  }
29
29
  getColumns(data) {
30
30
  if (data.length === 0) {
31
- return {};
31
+ return {
32
+ id: {
33
+ header: "ID",
34
+ minWidth: 36,
35
+ },
36
+ };
32
37
  }
33
38
  // define some default columns, can be overwritten by child class
34
39
  let columns = {
@@ -1,6 +1,7 @@
1
1
  import { GeneratedDomainListDomainOwnerships, } from "../../../generated/domain/listDomainOwnerships.js";
2
2
  export default class List extends GeneratedDomainListDomainOwnerships {
3
3
  mapData(data) {
4
+ console.log(data);
4
5
  return data;
5
6
  }
6
7
  }
@@ -0,0 +1,26 @@
1
+ import { ExecRenderBaseCommand } from "../../../rendering/react/ExecRenderBaseCommand.js";
2
+ import { ReactNode } from "react";
3
+ import { MittwaldAPIV2 } from "@mittwald/api-client";
4
+ import IngressIngress = MittwaldAPIV2.Components.Schemas.IngressIngress;
5
+ import DomainDomainOwnership = MittwaldAPIV2.Components.Schemas.DomainDomainOwnership;
6
+ type CreateResult = {
7
+ ingress: IngressIngress;
8
+ ownership: DomainDomainOwnership | null;
9
+ };
10
+ export default class Create extends ExecRenderBaseCommand<typeof Create, CreateResult> {
11
+ static description: string;
12
+ static examples: {
13
+ description: string;
14
+ command: string;
15
+ }[];
16
+ static flags: {
17
+ hostname: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
18
+ "path-to-dir": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
19
+ "path-to-app": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
20
+ "path-to-url": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
21
+ quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
22
+ };
23
+ protected exec(): Promise<CreateResult>;
24
+ protected render({ ingress, ownership }: CreateResult): ReactNode;
25
+ }
26
+ export {};
@@ -0,0 +1,122 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ExecRenderBaseCommand } from "../../../rendering/react/ExecRenderBaseCommand.js";
3
+ import { makeProcessRenderer, processFlags, } from "../../../rendering/react/process_flags.js";
4
+ import { projectFlags, withProjectId } from "../../../lib/project/flags.js";
5
+ import { assertStatus } from "@mittwald/api-client-commons";
6
+ import { Flags } from "@oclif/core";
7
+ import { Success } from "../../../rendering/react/components/Success.js";
8
+ import { Value } from "../../../rendering/react/components/Value.js";
9
+ import { waitUntil } from "../../../lib/wait.js";
10
+ import { Box } from "ink";
11
+ import { DnsValidationErrors } from "../../../rendering/react/components/Ingress/DnsValidationErrors.js";
12
+ import { DomainOwnership } from "../../../rendering/react/components/Ingress/DomainOwnership.js";
13
+ export default class Create extends ExecRenderBaseCommand {
14
+ static description = "Create a new ingress";
15
+ static examples = [
16
+ {
17
+ description: "Create a new ingress, with the root path mapping to your project's root directory",
18
+ command: "<%= config.bin %> <%= command.id %> --hostname mw.example --path-to-dir /:/",
19
+ },
20
+ {
21
+ description: "Create a new ingress, with the root path mapping to an app",
22
+ command: "<%= config.bin %> <%= command.id %> --hostname mw.example --path-to-app /:3ecaf1a9-6eb4-4869-b811-8a13c3a2e745",
23
+ },
24
+ {
25
+ description: "Create a new ingress, with the root path mapping to a URL",
26
+ command: "<%= config.bin %> <%= command.id %> --hostname mw.example --path-to-url /:https://redirect.example",
27
+ },
28
+ ];
29
+ static flags = {
30
+ ...processFlags,
31
+ ...projectFlags,
32
+ hostname: Flags.string({
33
+ description: "the hostname of the ingress",
34
+ required: true,
35
+ }),
36
+ "path-to-dir": Flags.string({
37
+ summary: "add a path mapping to a directory",
38
+ multiple: true,
39
+ description: "This flag can be used to map a specific URL path to a directory in your project's file system; the value for this flag should be the URL path and the filesystem path, separated by a colon, e.g. /:/ or /:/some/sub/path. You can specify this flag multiple times to map multiple paths to different directories, and also combine it with the other --path-to-* flags.",
40
+ }),
41
+ "path-to-app": Flags.string({
42
+ summary: "add a path mapping to an app",
43
+ description: "This flag can be used to map a specific URL path to an app; the value for this flag should be the URL path and the app ID, separated by a colon, e.g. /:3ecaf1a9-6eb4-4869-b811-8a13c3a2e745. You can specify this flag multiple times to map multiple paths to different apps, and also combine it with the other --path-to-* flags.",
44
+ multiple: true,
45
+ }),
46
+ "path-to-url": Flags.string({
47
+ summary: "add a path mapping to an external url",
48
+ multiple: true,
49
+ description: "This flag can be used to map a specific URL path to an external URL; the value for this flag should be the URL path and the external URL, separated by a colon, e.g. /:https://redirect.example. You can specify this flag multiple times to map multiple paths to different external URLs, and also combine it with the other --path-to-* flags.",
50
+ }),
51
+ };
52
+ async exec() {
53
+ const projectId = await withProjectId(this.apiClient, this.flags, this.args, this.config);
54
+ const process = makeProcessRenderer(this.flags, "Creating a new ingress");
55
+ const { hostname } = this.flags;
56
+ const paths = [];
57
+ for (const pathToDir of this.flags["path-to-dir"] ?? []) {
58
+ const [path, directory] = pathToDir.split(":");
59
+ paths.push({ path, target: { directory } });
60
+ }
61
+ for (const pathToApp of this.flags["path-to-app"] ?? []) {
62
+ const [path, installationId] = pathToApp.split(":");
63
+ paths.push({ path, target: { installationId } });
64
+ }
65
+ for (const pathToUrl of this.flags["path-to-url"] ?? []) {
66
+ const [path, url] = pathToUrl.split(":");
67
+ paths.push({ path, target: { url } });
68
+ }
69
+ const { id: ingressId } = await process.runStep("creating ingress", async () => {
70
+ const response = await this.apiClient.domain.ingressCreate({
71
+ data: {
72
+ projectId,
73
+ hostname,
74
+ paths,
75
+ },
76
+ });
77
+ assertStatus(response, 201);
78
+ return response.data;
79
+ });
80
+ const [ingress, ownership] = await process.runStep("waiting for ingress to be ready", async () => {
81
+ return await waitUntil(async () => {
82
+ const response = await this.apiClient.domain.ingressGetSpecific({
83
+ pathParameters: { ingressId },
84
+ });
85
+ if (response.status !== 200) {
86
+ return null;
87
+ }
88
+ const ownershipResponse = await this.apiClient.domain.listDomainOwnerships({
89
+ pathParameters: { projectId },
90
+ });
91
+ if (ownershipResponse.status === 200) {
92
+ const ownership = ownershipResponse.data.find((o) => o.domain === hostname);
93
+ if (ownership) {
94
+ return [response.data, ownership];
95
+ }
96
+ }
97
+ if (response.data.ips.v4.length === 0) {
98
+ return null;
99
+ }
100
+ return [response.data, null];
101
+ });
102
+ });
103
+ process.complete(_jsxs(Success, { children: ["You virtual host for ", _jsx(Value, { children: hostname }), " was successfully created! \uD83D\uDE80", " ", ingress.ips.v4.length > 0
104
+ ? "Please point your DNS records to the following IP addresses: " +
105
+ ingress.ips.v4.join(", ")
106
+ : "Please follow the instructions below to verify your domain ownership."] }));
107
+ return { ingress, ownership };
108
+ }
109
+ render({ ingress, ownership }) {
110
+ if (this.flags.quiet) {
111
+ this.log(ingress.id);
112
+ }
113
+ const output = [];
114
+ if (ingress.dnsValidationErrors.length > 0) {
115
+ output.push(_jsx(Box, { marginLeft: 2, children: _jsx(DnsValidationErrors, { ingress: ingress }) }, "dns"));
116
+ }
117
+ if (ownership) {
118
+ output.push(_jsx(Box, { marginLeft: 2, children: _jsx(DomainOwnership, { ownership: ownership }) }, "ownership"));
119
+ }
120
+ return output;
121
+ }
122
+ }
@@ -0,0 +1,13 @@
1
+ import { DeleteBaseCommand } from "../../../DeleteBaseCommand.js";
2
+ export default class Delete extends DeleteBaseCommand<typeof Delete> {
3
+ static description: string;
4
+ static resourceName: string;
5
+ static flags: {
6
+ force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
7
+ quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
8
+ };
9
+ static args: {
10
+ "virtual-host-id": import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
11
+ };
12
+ protected deleteResource(): Promise<void>;
13
+ }
@@ -0,0 +1,21 @@
1
+ import { assertStatus } from "@mittwald/api-client-commons";
2
+ import { DeleteBaseCommand } from "../../../DeleteBaseCommand.js";
3
+ import { Args } from "@oclif/core";
4
+ export default class Delete extends DeleteBaseCommand {
5
+ static description = "Delete a virtual host";
6
+ static resourceName = "virtual host";
7
+ static flags = { ...DeleteBaseCommand.baseFlags };
8
+ static args = {
9
+ "virtual-host-id": Args.string({
10
+ description: "ID of the virtual host to delete",
11
+ required: true,
12
+ }),
13
+ };
14
+ async deleteResource() {
15
+ const ingressId = this.args["virtual-host-id"];
16
+ const response = await this.apiClient.domain.ingressDelete({
17
+ pathParameters: { ingressId },
18
+ });
19
+ assertStatus(response, 204);
20
+ }
21
+ }
@@ -1,3 +1,14 @@
1
- import { GeneratedIngressGetSpecific } from "../../../generated/domain/ingressGetSpecific.js";
2
- export default class Get extends GeneratedIngressGetSpecific {
1
+ import { MittwaldAPIV2 } from "@mittwald/api-client";
2
+ import { RenderBaseCommand } from "../../../rendering/react/RenderBaseCommand.js";
3
+ import { ReactNode } from "react";
4
+ export type PathParams = MittwaldAPIV2.Paths.V2IngressesIngressId.Get.Parameters.Path;
5
+ export declare class Get extends RenderBaseCommand<typeof Get> {
6
+ static description: string;
7
+ static flags: {
8
+ [x: string]: import("@oclif/core/lib/interfaces/parser.js").CompletableFlag<any>;
9
+ };
10
+ static args: {
11
+ "ingress-id": import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
12
+ };
13
+ protected render(): ReactNode;
3
14
  }
@@ -1,6 +1,86 @@
1
- /* eslint-disable */
2
- /* prettier-ignore */
3
- /* This file is auto-generated with acg (@mittwald/api-code-generator) */
4
- import { GeneratedIngressGetSpecific } from "../../../generated/domain/ingressGetSpecific.js";
5
- export default class Get extends GeneratedIngressGetSpecific {
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { GetBaseCommand } from "../../../GetBaseCommand.js";
3
+ import { Args } from "@oclif/core";
4
+ import { RenderBaseCommand } from "../../../rendering/react/RenderBaseCommand.js";
5
+ import { usePromise } from "@mittwald/react-use-promise";
6
+ import { assertStatus } from "@mittwald/api-client-commons";
7
+ import { RenderJson } from "../../../rendering/react/json/RenderJson.js";
8
+ import { useRenderContext } from "../../../rendering/react/context.js";
9
+ import { SingleResult } from "../../../rendering/react/components/SingleResult.js";
10
+ import { Value } from "../../../rendering/react/components/Value.js";
11
+ import { Box, Text } from "ink";
12
+ import { Header } from "../../../rendering/react/components/Header.js";
13
+ import { DomainOwnership } from "../../../rendering/react/components/Ingress/DomainOwnership.js";
14
+ import { DnsValidationErrors } from "../../../rendering/react/components/Ingress/DnsValidationErrors.js";
15
+ const IngressOwnershipVerificationSummary = ({ ownership }) => {
16
+ return ownership ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: "yellow", children: "still required" }), _jsx(Text, { color: "gray", children: " (see instructions below)" })] })) : (_jsx(Text, { color: "green", children: "verified" }));
17
+ };
18
+ const IngressPath = ({ path }) => {
19
+ if ("directory" in path.target) {
20
+ return (_jsxs(Text, { children: ["->", " Local directory: ", _jsx(Value, { children: path.target.directory })] }));
21
+ }
22
+ if ("url" in path.target) {
23
+ return (_jsxs(Text, { children: ["->", " Redirect: ", _jsx(Value, { children: path.target.url })] }));
24
+ }
25
+ if ("installationId" in path.target) {
26
+ const { apiClient } = useRenderContext();
27
+ const installation = usePromise((id) => apiClient.app.getAppinstallation({
28
+ pathParameters: { appInstallationId: id },
29
+ }), [path.target.installationId]);
30
+ assertStatus(installation, 200);
31
+ const app = usePromise((id) => apiClient.app.getApp({ pathParameters: { appId: id } }), [installation.data.appId]);
32
+ assertStatus(app, 200);
33
+ return (_jsxs(Text, { children: ["->", " App: ", _jsx(Value, { children: app.data.name }), ", installed at", " ", _jsx(Value, { children: installation.data.installationPath })] }));
34
+ }
35
+ };
36
+ const IngressPaths = ({ ingress }) => {
37
+ const paths = {};
38
+ for (const path of ingress.paths) {
39
+ paths[path.path] = _jsx(IngressPath, { path: path });
40
+ }
41
+ return _jsx(SingleResult, { title: "Paths", rows: paths });
42
+ };
43
+ const GetIngress = ({ ingress }) => {
44
+ const { apiClient } = useRenderContext();
45
+ const ownerships = usePromise((projectId) => apiClient.domain.listDomainOwnerships({ pathParameters: { projectId } }), [ingress.projectId]);
46
+ assertStatus(ownerships, 200);
47
+ const ownership = ownerships.data.find((o) => o.domain === ingress.hostname);
48
+ const rows = {
49
+ "Virtual Host ID": _jsx(Value, { children: ingress.id }),
50
+ Hostname: _jsx(Value, { children: ingress.hostname }),
51
+ "Verification of ownership": (_jsx(IngressOwnershipVerificationSummary, { ownership: ownership })),
52
+ "IP addresses": _jsx(Value, { children: ingress.ips.v4.join("\n") }),
53
+ };
54
+ const sections = [
55
+ _jsx(SingleResult, { title: _jsxs(_Fragment, { children: ["VIRTUAL HOST DETAILS: ", _jsx(Value, { children: ingress.hostname })] }), rows: rows }, "primary"),
56
+ _jsx(IngressPaths, { ingress: ingress }, "paths"),
57
+ ];
58
+ if (ownership) {
59
+ sections.push(_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginY: 1, children: _jsx(Header, { title: "Pending ownership verification" }) }), _jsx(DomainOwnership, { ownership: ownership })] }, "ownership"));
60
+ }
61
+ if (ingress.dnsValidationErrors.length > 0) {
62
+ sections.push(_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginY: 1, children: _jsx(Header, { title: "DNS validation errors" }) }), _jsx(DnsValidationErrors, { ingress: ingress })] }, "dns-validation-errors"));
63
+ }
64
+ return (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: sections }));
65
+ };
66
+ export class Get extends RenderBaseCommand {
67
+ static description = "Get a virtual host.";
68
+ static flags = { ...GetBaseCommand.baseFlags };
69
+ static args = {
70
+ "ingress-id": Args.string({
71
+ summary: "The ID of the ingress to get.",
72
+ required: true,
73
+ }),
74
+ };
75
+ render() {
76
+ const ingressId = this.args["ingress-id"];
77
+ const ingressResponse = usePromise((id) => this.apiClient.domain.ingressGetSpecific({
78
+ pathParameters: { ingressId: id },
79
+ }), [ingressId]);
80
+ assertStatus(ingressResponse, 200);
81
+ if (this.flags.output === "json") {
82
+ return _jsx(RenderJson, { name: "ingress", data: ingressResponse.data });
83
+ }
84
+ return _jsx(GetIngress, { ingress: ingressResponse.data });
85
+ }
6
86
  }
@@ -10,7 +10,7 @@ export declare class List extends ListBaseCommand<typeof List, ResponseItem, Res
10
10
  static description: string;
11
11
  static args: {};
12
12
  static flags: {
13
- "project-id": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
+ all: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
14
14
  };
15
15
  getData(): Promise<Response>;
16
16
  protected mapParams(input: PathParams): Promise<PathParams> | PathParams;
@@ -1,22 +1,25 @@
1
1
  import { ListBaseCommand } from "../../../ListBaseCommand.js";
2
2
  import { Flags } from "@oclif/core";
3
+ import { projectFlags, withProjectId } from "../../../lib/project/flags.js";
3
4
  export class List extends ListBaseCommand {
4
- static description = "List Ingresses the user has access to.";
5
+ static description = "List virtualhosts for a project.";
5
6
  static args = {};
6
7
  static flags = {
7
8
  ...ListBaseCommand.baseFlags,
8
- "project-id": Flags.string({
9
- description: "Project ID to filter by; if omitted this will list virtual hosts in all projects you have access to.",
10
- required: false,
9
+ ...projectFlags,
10
+ all: Flags.boolean({
11
+ char: "a",
12
+ description: "List all virtual hosts that you have access to, regardless of project",
11
13
  }),
12
14
  };
13
15
  async getData() {
14
- if (this.flags["project-id"]) {
15
- return await this.apiClient.domain.ingressListForProject({
16
- pathParameters: { projectId: this.flags["project-id"] },
17
- });
16
+ if (this.flags.all) {
17
+ return await this.apiClient.domain.ingressListAccessible({});
18
18
  }
19
- return await this.apiClient.domain.ingressListAccessible({});
19
+ const projectId = await withProjectId(this.apiClient, this.flags, this.args, this.config);
20
+ return await this.apiClient.domain.ingressListForProject({
21
+ pathParameters: { projectId },
22
+ });
20
23
  }
21
24
  mapParams(input) {
22
25
  return input;
@@ -26,7 +29,7 @@ export class List extends ListBaseCommand {
26
29
  }
27
30
  getColumns(data) {
28
31
  const baseColumns = super.getColumns(data);
29
- return {
32
+ const columns = {
30
33
  id: baseColumns.id,
31
34
  projectId: { header: "Project ID" },
32
35
  hostname: {},
@@ -34,19 +37,32 @@ export class List extends ListBaseCommand {
34
37
  get: (r) => r.paths
35
38
  .map((p) => {
36
39
  if ("directory" in p.target) {
37
- return `${p.path} -> directory (${p.target.directory})`;
40
+ return `${p.path} directory (${p.target.directory})`;
38
41
  }
39
42
  if ("url" in p.target) {
40
- return `${p.path} -> url (${p.target.url})`;
43
+ return `${p.path} url (${p.target.url})`;
41
44
  }
42
- return `${p.path} -> app (${p.target.installationId})`;
45
+ return `${p.path} app (${p.target.installationId})`;
43
46
  })
44
47
  .join("\n"),
45
48
  },
49
+ status: {
50
+ header: "Status",
51
+ get: (r) => {
52
+ if (r.dnsValidationErrors.length === 0) {
53
+ return "ready";
54
+ }
55
+ return `${r.dnsValidationErrors.length} issues`;
56
+ },
57
+ },
46
58
  ips: {
47
59
  header: "IP addresses",
48
60
  get: (r) => r.ips.v4.join(", "),
49
61
  },
50
62
  };
63
+ if (!this.flags.all) {
64
+ delete columns.projectId;
65
+ }
66
+ return columns;
51
67
  }
52
68
  }
@@ -1,20 +1,28 @@
1
1
  import { ExecRenderBaseCommand } from "../../../rendering/react/ExecRenderBaseCommand.js";
2
2
  import { ReactNode } from "react";
3
- export default class Create extends ExecRenderBaseCommand<typeof Create, {
3
+ import { ProcessRenderer } from "../../../rendering/react/process.js";
4
+ type CreateResult = {
4
5
  addressId: string;
5
- }> {
6
+ generatedPassword: string | null;
7
+ };
8
+ export default class Create extends ExecRenderBaseCommand<typeof Create, CreateResult> {
9
+ static summary: string;
6
10
  static description: string;
7
11
  static flags: {
8
12
  address: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
13
  "catch-all": import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
14
  "enable-spam-protection": import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
15
  quota: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
16
+ password: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
17
+ "random-password": import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
12
18
  quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
13
19
  };
14
- protected exec(): Promise<{
15
- addressId: string;
16
- }>;
17
- protected render(executionResult: {
18
- addressId: string;
19
- }): ReactNode;
20
+ static examples: {
21
+ description: string;
22
+ command: string;
23
+ }[];
24
+ protected getPassword(process: ProcessRenderer): Promise<[string, boolean]>;
25
+ protected exec(): Promise<CreateResult>;
26
+ protected render(executionResult: CreateResult): ReactNode;
20
27
  }
28
+ export {};
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Flags } from "@oclif/core";
3
3
  import { assertStatus } from "@mittwald/api-client-commons";
4
4
  import { projectFlags, withProjectId } from "../../../lib/project/flags.js";
@@ -6,34 +6,70 @@ import { ExecRenderBaseCommand } from "../../../rendering/react/ExecRenderBaseCo
6
6
  import { makeProcessRenderer, processFlags, } from "../../../rendering/react/process_flags.js";
7
7
  import { Text } from "ink";
8
8
  import { Success } from "../../../rendering/react/components/Success.js";
9
+ import * as crypto from "crypto";
10
+ import { Value } from "../../../rendering/react/components/Value.js";
9
11
  export default class Create extends ExecRenderBaseCommand {
10
- static description = "Create a new mail address";
12
+ static summary = "Create a new mail address";
13
+ static description = `\
14
+ When running this command with the --quiet flag, the output will contain the ID of the newly created address.
15
+ In addition, when run with --generated-password the output will be the ID of the newly created address, followed by a tab character and the generated password.`;
11
16
  static flags = {
12
17
  ...projectFlags,
13
18
  ...processFlags,
14
19
  address: Flags.string({
15
20
  char: "a",
16
- description: "Mail address",
21
+ summary: "mail address",
17
22
  required: true,
18
23
  }),
19
24
  "catch-all": Flags.boolean({
20
- description: "Make this a catch-all mail address",
25
+ description: "make this a catch-all mail address",
21
26
  }),
22
27
  "enable-spam-protection": Flags.boolean({
23
- description: "Enable spam protection for this mailbox",
28
+ description: "enable spam protection for this mailbox",
24
29
  default: true,
25
30
  allowNo: true,
26
31
  }),
27
32
  quota: Flags.integer({
28
- description: "Mailbox quota in mebibytes",
33
+ description: "mailbox quota in mebibytes",
29
34
  default: 1024,
30
35
  }),
36
+ password: Flags.string({
37
+ summary: "mailbox password",
38
+ description: "This is the password that should be used for the mailbox; if omitted, the command will prompt interactively for a password.\n\nCAUTION: providing this flag may log your password in your shell history!",
39
+ }),
40
+ "random-password": Flags.boolean({
41
+ summary: "generate a random password",
42
+ description: "This flag will cause the command to generate a random 32-character password for the mailbox; when running with --quiet, the address ID and the password will be printed to stdout, separated by a tab character.",
43
+ }),
31
44
  };
45
+ static examples = [
46
+ {
47
+ description: "Create non-interactively with password",
48
+ command: "$ read -s PASSWORD &&\n <%= config.bin %> <%= command.id %> --password $PASSWORD --address foo@bar.example",
49
+ },
50
+ {
51
+ description: "Create non-interactively with random password",
52
+ command: "<%= config.bin %> <%= command.id %> --random-password --address foo@bar.example",
53
+ },
54
+ ];
55
+ async getPassword(process) {
56
+ if (this.flags.password) {
57
+ return [this.flags.password, false];
58
+ }
59
+ if (this.flags["random-password"]) {
60
+ const generated = await process.runStep("generating random password", async () => {
61
+ return crypto.randomBytes(32).toString("base64").substring(0, 32);
62
+ });
63
+ process.addInfo(_jsxs(Text, { children: ["generated password: ", _jsx(Value, { children: generated })] }));
64
+ return [generated, true];
65
+ }
66
+ return [await process.addInput(_jsx(Text, { children: "Mailbox password" }), true), false];
67
+ }
32
68
  async exec() {
33
69
  const { flags } = await this.parse(Create);
34
70
  const projectId = await withProjectId(this.apiClient, flags, {}, this.config);
35
71
  const process = makeProcessRenderer(flags, "Creating a new mail address");
36
- const password = await process.addInput(_jsx(Text, { children: "Mailbox password" }), true);
72
+ const [password, passwordGenerated] = await this.getPassword(process);
37
73
  const response = await process.runStep("creating mail address", async () => {
38
74
  const response = await this.apiClient.mail.mailaddressCreate({
39
75
  pathParameters: { projectId },
@@ -51,11 +87,17 @@ export default class Create extends ExecRenderBaseCommand {
51
87
  return response;
52
88
  });
53
89
  process.complete(_jsx(Success, { children: "Your mail address was successfully created." }));
54
- return { addressId: response.data.id };
90
+ return {
91
+ addressId: response.data.id,
92
+ generatedPassword: passwordGenerated ? password : null,
93
+ };
55
94
  }
56
95
  render(executionResult) {
57
96
  if (this.flags.quiet) {
58
- return executionResult.addressId;
97
+ if (executionResult.generatedPassword) {
98
+ return (_jsxs(Text, { children: [executionResult.addressId, "\t", executionResult.generatedPassword] }));
99
+ }
100
+ return _jsx(Text, { children: executionResult.addressId });
59
101
  }
60
102
  }
61
103
  }
@@ -0,0 +1,13 @@
1
+ import { DeleteBaseCommand } from "../../DeleteBaseCommand.js";
2
+ export default class Delete extends DeleteBaseCommand<typeof Delete> {
3
+ static description: string;
4
+ static resourceName: string;
5
+ static flags: {
6
+ force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
7
+ quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
8
+ };
9
+ static args: {
10
+ [x: string]: import("@oclif/core/lib/interfaces/parser.js").Arg<unknown>;
11
+ };
12
+ protected deleteResource(): Promise<void>;
13
+ }
@@ -0,0 +1,17 @@
1
+ import { assertStatus } from "@mittwald/api-client-commons";
2
+ import { DeleteBaseCommand } from "../../DeleteBaseCommand.js";
3
+ import { projectArgs } from "../../lib/project/flags.js";
4
+ import { withOrgId } from "../../lib/org/flags.js";
5
+ export default class Delete extends DeleteBaseCommand {
6
+ static description = "Delete an organization";
7
+ static resourceName = "organization";
8
+ static flags = { ...DeleteBaseCommand.baseFlags };
9
+ static args = { ...projectArgs };
10
+ async deleteResource() {
11
+ const customerId = await withOrgId(this.apiClient, {}, this.args, this.config);
12
+ const response = await this.apiClient.customer.deleteCustomer({
13
+ pathParameters: { customerId },
14
+ });
15
+ assertStatus(response, 200);
16
+ }
17
+ }