@mittwald/cli 1.0.0-alpha.27 → 1.0.0-alpha.28

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 (115) hide show
  1. package/README.md +814 -760
  2. package/dist/esm/ExtendedBaseCommand.d.ts +4 -0
  3. package/dist/esm/ExtendedBaseCommand.js +12 -0
  4. package/dist/esm/Formatter.js +13 -1
  5. package/dist/esm/GetBaseCommand.d.ts +2 -3
  6. package/dist/esm/GetBaseCommand.js +2 -4
  7. package/dist/esm/ListBaseCommand.d.ts +2 -3
  8. package/dist/esm/ListBaseCommand.js +2 -4
  9. package/dist/esm/commands/app/copy.d.ts +1 -1
  10. package/dist/esm/commands/app/copy.js +3 -3
  11. package/dist/esm/commands/app/dependency/list.d.ts +1 -1
  12. package/dist/esm/commands/app/dependency/update.d.ts +1 -1
  13. package/dist/esm/commands/app/dependency/update.js +3 -3
  14. package/dist/esm/commands/app/dependency/versions.d.ts +1 -1
  15. package/dist/esm/commands/app/get.d.ts +1 -1
  16. package/dist/esm/commands/app/get.js +4 -3
  17. package/dist/esm/commands/app/list.d.ts +1 -1
  18. package/dist/esm/commands/app/list.js +4 -0
  19. package/dist/esm/commands/app/ssh.d.ts +4 -3
  20. package/dist/esm/commands/app/ssh.js +18 -9
  21. package/dist/esm/commands/app/uninstall.d.ts +1 -1
  22. package/dist/esm/commands/app/uninstall.js +3 -6
  23. package/dist/esm/commands/backup/create.js +2 -2
  24. package/dist/esm/commands/backup/list.js +2 -2
  25. package/dist/esm/commands/context/get.js +27 -5
  26. package/dist/esm/commands/conversation/show.js +1 -2
  27. package/dist/esm/commands/cronjob/create.d.ts +22 -0
  28. package/dist/esm/commands/cronjob/create.js +96 -0
  29. package/dist/esm/commands/cronjob/delete.d.ts +13 -0
  30. package/dist/esm/commands/cronjob/delete.js +21 -0
  31. package/dist/esm/commands/cronjob/execute.d.ts +17 -0
  32. package/dist/esm/commands/cronjob/execute.js +42 -0
  33. package/dist/esm/commands/cronjob/execution/abort.d.ts +18 -0
  34. package/dist/esm/commands/cronjob/execution/abort.js +41 -0
  35. package/dist/esm/commands/cronjob/execution/list.js +4 -5
  36. package/dist/esm/commands/cronjob/get.d.ts +7 -9
  37. package/dist/esm/commands/cronjob/get.js +22 -12
  38. package/dist/esm/commands/cronjob/list.js +4 -6
  39. package/dist/esm/commands/database/mysql/charsets.d.ts +2 -4
  40. package/dist/esm/commands/database/mysql/charsets.js +2 -6
  41. package/dist/esm/commands/database/mysql/create.js +2 -2
  42. package/dist/esm/commands/database/mysql/delete.js +2 -2
  43. package/dist/esm/commands/database/mysql/list.d.ts +1 -2
  44. package/dist/esm/commands/database/mysql/list.js +2 -2
  45. package/dist/esm/commands/database/mysql/user/list.d.ts +1 -2
  46. package/dist/esm/commands/database/mysql/user/list.js +2 -3
  47. package/dist/esm/commands/database/mysql/versions.d.ts +1 -2
  48. package/dist/esm/commands/database/mysql/versions.js +1 -3
  49. package/dist/esm/commands/database/redis/create.js +2 -2
  50. package/dist/esm/commands/database/redis/get.d.ts +1 -3
  51. package/dist/esm/commands/database/redis/get.js +0 -3
  52. package/dist/esm/commands/database/redis/list.d.ts +1 -2
  53. package/dist/esm/commands/database/redis/list.js +3 -5
  54. package/dist/esm/commands/database/redis/versions.d.ts +1 -2
  55. package/dist/esm/commands/database/redis/versions.js +2 -3
  56. package/dist/esm/commands/domain/dnszone/list.d.ts +1 -2
  57. package/dist/esm/commands/domain/dnszone/list.js +2 -2
  58. package/dist/esm/commands/domain/list.d.ts +1 -2
  59. package/dist/esm/commands/domain/list.js +3 -4
  60. package/dist/esm/commands/domain/ownership/list.js +3 -7
  61. package/dist/esm/commands/domain/virtualhost/create.js +4 -4
  62. package/dist/esm/commands/domain/virtualhost/delete.js +1 -1
  63. package/dist/esm/commands/domain/virtualhost/get.js +1 -1
  64. package/dist/esm/commands/domain/virtualhost/list.d.ts +1 -3
  65. package/dist/esm/commands/domain/virtualhost/list.js +5 -8
  66. package/dist/esm/commands/mail/address/create.d.ts +4 -0
  67. package/dist/esm/commands/mail/address/create.js +59 -5
  68. package/dist/esm/commands/mail/address/list.d.ts +2 -2
  69. package/dist/esm/commands/mail/address/list.js +2 -4
  70. package/dist/esm/commands/mail/deliverybox/list.js +2 -2
  71. package/dist/esm/commands/project/create.js +2 -2
  72. package/dist/esm/commands/project/delete.d.ts +0 -1
  73. package/dist/esm/commands/project/delete.js +4 -8
  74. package/dist/esm/commands/project/filesystem/usage.d.ts +1 -1
  75. package/dist/esm/commands/project/filesystem/usage.js +3 -3
  76. package/dist/esm/commands/project/get.js +5 -5
  77. package/dist/esm/commands/project/invite/list.js +2 -2
  78. package/dist/esm/commands/project/membership/list.js +5 -5
  79. package/dist/esm/commands/project/ssh.d.ts +2 -2
  80. package/dist/esm/commands/project/ssh.js +4 -5
  81. package/dist/esm/commands/project/update.d.ts +2 -2
  82. package/dist/esm/commands/project/update.js +4 -5
  83. package/dist/esm/commands/server/get.d.ts +1 -3
  84. package/dist/esm/commands/server/get.js +2 -5
  85. package/dist/esm/commands/sftp-user/list.js +2 -2
  86. package/dist/esm/commands/ssh-user/list.js +2 -2
  87. package/dist/esm/lib/app/flags.d.ts +4 -2
  88. package/dist/esm/lib/app/flags.js +3 -7
  89. package/dist/esm/lib/assert_success.d.ts +3 -0
  90. package/dist/esm/lib/assert_success.js +7 -0
  91. package/dist/esm/lib/context.d.ts +24 -5
  92. package/dist/esm/lib/context.js +21 -16
  93. package/dist/esm/lib/context_flags.d.ts +10 -6
  94. package/dist/esm/lib/context_flags.js +8 -5
  95. package/dist/esm/lib/context_terraform.d.ts +6 -0
  96. package/dist/esm/lib/context_terraform.js +61 -0
  97. package/dist/esm/lib/context_user.d.ts +10 -0
  98. package/dist/esm/lib/context_user.js +34 -0
  99. package/dist/esm/lib/org/flags.d.ts +1 -9
  100. package/dist/esm/lib/org/flags.js +1 -1
  101. package/dist/esm/lib/project/flags.d.ts +1 -9
  102. package/dist/esm/lib/project/flags.js +1 -1
  103. package/dist/esm/lib/project/ingress.js +4 -2
  104. package/dist/esm/lib/server/flags.d.ts +1 -9
  105. package/dist/esm/lib/server/flags.js +1 -1
  106. package/dist/esm/rendering/react/RenderBaseCommand.d.ts +3 -0
  107. package/dist/esm/rendering/react/RenderBaseCommand.js +7 -0
  108. package/dist/esm/rendering/react/components/AppInstallation/AppInstallationDetails.js +8 -1
  109. package/dist/esm/rendering/react/components/CronJob/CronJobDetails.d.ts +8 -0
  110. package/dist/esm/rendering/react/components/CronJob/CronJobDetails.js +48 -0
  111. package/dist/esm/rendering/react/components/LocalFilename.d.ts +5 -0
  112. package/dist/esm/rendering/react/components/LocalFilename.js +14 -0
  113. package/dist/esm/rendering/react/components/Note.d.ts +1 -0
  114. package/dist/esm/rendering/react/components/Note.js +1 -1
  115. package/package.json +8 -6
@@ -1,16 +1,35 @@
1
1
  import { Config } from "@oclif/core";
2
+ export type ContextNames = "project" | "server" | "org" | "installation";
3
+ export type ContextKey<N extends ContextNames = ContextNames> = `${N}-id`;
4
+ export type ContextMap = Partial<Record<ContextKey, ContextValue>>;
5
+ export type ContextMapUpdate = Partial<Record<ContextKey, string>>;
6
+ export type ContextValueSource = {
7
+ type: string;
8
+ identifier: string;
9
+ };
10
+ export type ContextValue = {
11
+ value: string;
12
+ source: ContextValueSource;
13
+ };
14
+ export interface ContextProvider {
15
+ name: string;
16
+ getOverrides(): Promise<ContextMap>;
17
+ }
18
+ export interface WritableContextProvider extends ContextProvider {
19
+ update(data: ContextMapUpdate): Promise<void>;
20
+ }
2
21
  export declare class Context {
3
- private readonly config;
4
22
  private readonly contextData;
23
+ readonly providers: ContextProvider[];
5
24
  constructor(config: Config);
6
25
  private initializeContextData;
7
26
  private persist;
8
27
  private setContextValue;
9
- getContextValue(key: string): Promise<string | undefined>;
28
+ getContextValue(key: ContextKey): Promise<ContextValue | undefined>;
10
29
  setProjectId: (id: string) => Promise<void>;
11
30
  setServerId: (id: string) => Promise<void>;
12
31
  setOrgId: (id: string) => Promise<void>;
13
- projectId: () => Promise<string | undefined>;
14
- serverId: () => Promise<string | undefined>;
15
- orgId: () => Promise<string | undefined>;
32
+ projectId: () => Promise<ContextValue | undefined>;
33
+ serverId: () => Promise<ContextValue | undefined>;
34
+ orgId: () => Promise<ContextValue | undefined>;
16
35
  }
@@ -1,30 +1,35 @@
1
- import * as fs from "fs/promises";
2
- import * as path from "path";
1
+ import { TerraformContextProvider } from "./context_terraform.js";
2
+ import { UserContextProvider } from "./context_user.js";
3
+ function isWritable(p) {
4
+ return "update" in p;
5
+ }
3
6
  export class Context {
4
- config;
5
7
  contextData;
8
+ providers;
6
9
  constructor(config) {
7
- this.config = config;
10
+ this.providers = [
11
+ new UserContextProvider(config),
12
+ new TerraformContextProvider(),
13
+ ];
8
14
  this.contextData = this.initializeContextData();
9
15
  }
10
16
  async initializeContextData() {
11
- try {
12
- const contents = await fs.readFile(path.join(this.config.configDir, "context.json"), "utf-8");
13
- return JSON.parse(contents);
14
- }
15
- catch (e) {
16
- if (e instanceof Error && "code" in e && e.code === "ENOENT") {
17
- return {};
18
- }
19
- throw e;
17
+ const contextData = {};
18
+ for (const provider of this.providers) {
19
+ const overrides = await provider.getOverrides();
20
+ Object.assign(contextData, overrides);
20
21
  }
22
+ return contextData;
21
23
  }
22
24
  async persist(data) {
23
- await fs.writeFile(path.join(this.config.configDir, "context.json"), JSON.stringify(data), "utf-8");
25
+ for (const provider of this.providers) {
26
+ if (isWritable(provider)) {
27
+ await provider.update(data);
28
+ }
29
+ }
24
30
  }
25
31
  async setContextValue(key, value) {
26
- const data = await this.contextData;
27
- return await this.persist({ ...data, [key]: value });
32
+ return await this.persist({ [key]: value });
28
33
  }
29
34
  async getContextValue(key) {
30
35
  const data = await this.contextData;
@@ -2,14 +2,14 @@ import { Config } from "@oclif/core";
2
2
  import { Arg, ArgOutput, FlagOutput, OptionFlag } from "@oclif/core/lib/interfaces/parser.js";
3
3
  import { MittwaldAPIV2Client } from "@mittwald/api-client";
4
4
  import { AlphabetLowercase } from "@oclif/core/lib/interfaces/index.js";
5
- type FlagIDType<N extends string> = `${N}-id`;
6
- type ContextFlags<N extends string, TID extends string = FlagIDType<N>> = {
5
+ import { ContextKey, ContextNames } from "./context.js";
6
+ type ContextFlags<N extends ContextNames, TID extends string = ContextKey<N>> = {
7
7
  [k in TID]: OptionFlag<string>;
8
8
  };
9
- type ContextArgs<N extends string, TID extends string = FlagIDType<N>> = {
9
+ type ContextArgs<N extends ContextNames, TID extends string = ContextKey<N>> = {
10
10
  [k in TID]: Arg<string>;
11
11
  };
12
- type CommandType<N extends string, TID extends string = FlagIDType<N>> = {
12
+ export type CommandType<N extends ContextNames, TID extends string = ContextKey<N>> = {
13
13
  flags: {
14
14
  [k in TID]: OptionFlag<string>;
15
15
  };
@@ -18,12 +18,16 @@ type CommandType<N extends string, TID extends string = FlagIDType<N>> = {
18
18
  [k in TID]: Arg<string>;
19
19
  };
20
20
  };
21
- export type FlagSet<TName extends string> = {
21
+ export type FlagSet<TName extends ContextNames> = {
22
22
  name: TName;
23
23
  flags: ContextFlags<TName>;
24
24
  args: ContextArgs<TName>;
25
25
  withId: (apiClient: MittwaldAPIV2Client, command: CommandType<TName> | "flag" | "arg", flags: FlagOutput, args: ArgOutput, cfg: Config) => Promise<string>;
26
26
  };
27
+ export type FlagSetOptions = {
28
+ normalize: NormalizeFn;
29
+ displayName: string;
30
+ };
27
31
  export type NormalizeFn = (apiClient: MittwaldAPIV2Client, id: string) => string | Promise<string>;
28
- export declare function makeFlagSet<TName extends string>(name: TName, char: AlphabetLowercase, normalize?: NormalizeFn): FlagSet<TName>;
32
+ export declare function makeFlagSet<TName extends ContextNames>(name: TName, char: AlphabetLowercase, opts?: Partial<FlagSetOptions>): FlagSet<TName>;
29
33
  export {};
@@ -10,19 +10,22 @@ class MissingArgError extends Error {
10
10
  super(`No ${name} ID given. Please specify one as positional argument or set a default ${name} with 'mittwald context set --${flagName} <${flagName}>'`);
11
11
  }
12
12
  }
13
- export function makeFlagSet(name, char, normalize = (_, id) => id) {
13
+ export function makeFlagSet(name, char, opts = {}) {
14
+ const { displayName = name, normalize = (_, id) => id } = opts;
15
+ const article = displayName.match(/^[aeiou]/i) ? "an" : "a";
14
16
  const flagName = `${name}-id`;
15
17
  const flags = {
16
18
  [flagName]: Flags.string({
17
19
  char,
18
20
  required: false,
19
- summary: `ID or short ID of a ${name}; this flag is optional if a default ${name} is set in the context`,
20
- description: `May contain a short ID or a full ID of a ${name}; you can also use the "<%= config.bin %> context set --${name}-id=<VALUE>" command to persistently set a default ${name} for all commands that accept this flag.`,
21
+ summary: `ID or short ID of ${article} ${displayName}; this flag is optional if a default ${displayName} is set in the context`,
22
+ description: `May contain a short ID or a full ID of ${article} ${displayName}; you can also use the "<%= config.bin %> context set --${flagName}=<VALUE>" command to persistently set a default ${displayName} for all commands that accept this flag.`,
23
+ default: undefined,
21
24
  }),
22
25
  };
23
26
  const args = {
24
27
  [flagName]: Args.string({
25
- description: `ID or short ID of a ${name}; this argument is optional if a default ${name} is set in the context`,
28
+ description: `ID or short ID of ${article} ${displayName}; this argument is optional if a default ${displayName} is set in the context`,
26
29
  }),
27
30
  };
28
31
  const idFromArgsOrFlag = (flags, args) => {
@@ -41,7 +44,7 @@ export function makeFlagSet(name, char, normalize = (_, id) => id) {
41
44
  }
42
45
  const idFromContext = await new Context(cfg).getContextValue(flagName);
43
46
  if (idFromContext) {
44
- return idFromContext;
47
+ return idFromContext.value;
45
48
  }
46
49
  if (commandType === "flag") {
47
50
  throw new MissingFlagError(name, flagName);
@@ -0,0 +1,6 @@
1
+ import { ContextMap, ContextProvider } from "./context.js";
2
+ export declare class TerraformContextProvider implements ContextProvider {
3
+ readonly name = "terraform";
4
+ getOverrides(): Promise<ContextMap>;
5
+ private findTerraformStateFile;
6
+ }
@@ -0,0 +1,61 @@
1
+ import fs from "fs/promises";
2
+ import { cwd } from "process";
3
+ import path from "path";
4
+ function overrideIDFromState(state, type) {
5
+ const instances = state.resources?.find((r) => r.type === type)?.instances;
6
+ if (instances === undefined) {
7
+ return undefined;
8
+ }
9
+ // If the context cannot be determined unambiguously, don't use the Terraform
10
+ // state at all.
11
+ if (instances.length > 1) {
12
+ return undefined;
13
+ }
14
+ const id = instances[0]?.attributes?.id;
15
+ return typeof id === "string" ? id : undefined;
16
+ }
17
+ export class TerraformContextProvider {
18
+ name = "terraform";
19
+ async getOverrides() {
20
+ const file = await this.findTerraformStateFile();
21
+ if (!file) {
22
+ return {};
23
+ }
24
+ const contents = await fs.readFile(file, "utf-8");
25
+ const state = JSON.parse(contents);
26
+ const overrides = {};
27
+ const source = { type: "terraform", identifier: file };
28
+ const projectID = overrideIDFromState(state, "mittwald_project");
29
+ if (projectID) {
30
+ overrides["project-id"] = { value: projectID, source };
31
+ }
32
+ const serverID = overrideIDFromState(state, "mittwald_server");
33
+ if (serverID) {
34
+ overrides["server-id"] = { value: serverID, source };
35
+ }
36
+ const appID = overrideIDFromState(state, "mittwald_app");
37
+ if (appID) {
38
+ overrides["installation-id"] = { value: appID, source };
39
+ }
40
+ return overrides;
41
+ }
42
+ // Iterate through all parent directories and look for a terraform state file.
43
+ async findTerraformStateFile() {
44
+ let currentDir = cwd();
45
+ while (currentDir !== "/") {
46
+ const stateFile = path.join(currentDir, "terraform.tfstate");
47
+ try {
48
+ await fs.stat(stateFile);
49
+ return stateFile;
50
+ }
51
+ catch (e) {
52
+ if (e instanceof Error && "code" in e && e.code === "ENOENT") {
53
+ currentDir = path.dirname(currentDir);
54
+ continue;
55
+ }
56
+ throw e;
57
+ }
58
+ }
59
+ return undefined;
60
+ }
61
+ }
@@ -0,0 +1,10 @@
1
+ import { ContextKey, ContextMap, ContextProvider, WritableContextProvider } from "./context.js";
2
+ import { Config } from "@oclif/core";
3
+ export declare class UserContextProvider implements ContextProvider, WritableContextProvider {
4
+ private readonly config;
5
+ name: string;
6
+ constructor(config: Config);
7
+ private get contextFile();
8
+ getOverrides(): Promise<ContextMap>;
9
+ update(data: Record<ContextKey, string>): Promise<void>;
10
+ }
@@ -0,0 +1,34 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ export class UserContextProvider {
4
+ config;
5
+ name = "user";
6
+ constructor(config) {
7
+ this.config = config;
8
+ }
9
+ get contextFile() {
10
+ return path.join(this.config.configDir, "context.json");
11
+ }
12
+ async getOverrides() {
13
+ try {
14
+ const contents = await fs.readFile(this.contextFile, "utf-8");
15
+ const rawValues = JSON.parse(contents);
16
+ const source = { type: "user", identifier: this.contextFile };
17
+ return Object.fromEntries(Object.entries(rawValues).map(([k, v]) => [k, { value: v, source }]));
18
+ }
19
+ catch (e) {
20
+ if (e instanceof Error && "code" in e && e.code === "ENOENT") {
21
+ return {};
22
+ }
23
+ throw e;
24
+ }
25
+ }
26
+ async update(data) {
27
+ const baseData = await this.getOverrides();
28
+ const source = { type: "user", identifier: this.contextFile };
29
+ for (const k of Object.keys(data)) {
30
+ baseData[k] = { value: data[k], source };
31
+ }
32
+ await fs.writeFile(path.join(this.config.configDir, "context.json"), JSON.stringify(data), "utf-8");
33
+ }
34
+ }
@@ -2,12 +2,4 @@ export declare const orgFlags: {
2
2
  "org-id": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string>;
3
3
  }, orgArgs: {
4
4
  "org-id": import("@oclif/core/lib/interfaces/parser.js").Arg<string>;
5
- }, withOrgId: (apiClient: import("@mittwald/api-client").MittwaldAPIV2Client, command: "flag" | "arg" | ({
6
- flags: {
7
- "org-id": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string>;
8
- };
9
- } | {
10
- args: {
11
- "org-id": import("@oclif/core/lib/interfaces/parser.js").Arg<string>;
12
- };
13
- }), flags: import("@oclif/core/lib/interfaces/parser.js").FlagOutput, args: import("@oclif/core/lib/interfaces/parser.js").ArgOutput, cfg: import("@oclif/core").Config) => Promise<string>;
5
+ }, withOrgId: (apiClient: import("@mittwald/api-client").MittwaldAPIV2Client, command: "flag" | "arg" | import("../context_flags.js").CommandType<"org", "org-id">, flags: import("@oclif/core/lib/interfaces/parser.js").FlagOutput, args: import("@oclif/core/lib/interfaces/parser.js").ArgOutput, cfg: import("@oclif/core").Config) => Promise<string>;
@@ -1,3 +1,3 @@
1
1
  import { makeFlagSet } from "../context_flags.js";
2
2
  import { normalizeCustomerIdToUuid } from "../../Helpers.js";
3
- export const { flags: orgFlags, args: orgArgs, withId: withOrgId, } = makeFlagSet("org", "o", normalizeCustomerIdToUuid);
3
+ export const { flags: orgFlags, args: orgArgs, withId: withOrgId, } = makeFlagSet("org", "o", { normalize: normalizeCustomerIdToUuid });
@@ -2,12 +2,4 @@ export declare const projectFlags: {
2
2
  "project-id": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string>;
3
3
  }, projectArgs: {
4
4
  "project-id": import("@oclif/core/lib/interfaces/parser.js").Arg<string>;
5
- }, withProjectId: (apiClient: import("@mittwald/api-client").MittwaldAPIV2Client, command: "flag" | "arg" | ({
6
- flags: {
7
- "project-id": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string>;
8
- };
9
- } | {
10
- args: {
11
- "project-id": import("@oclif/core/lib/interfaces/parser.js").Arg<string>;
12
- };
13
- }), flags: import("@oclif/core/lib/interfaces/parser.js").FlagOutput, args: import("@oclif/core/lib/interfaces/parser.js").ArgOutput, cfg: import("@oclif/core").Config) => Promise<string>;
5
+ }, withProjectId: (apiClient: import("@mittwald/api-client").MittwaldAPIV2Client, command: "flag" | "arg" | import("../context_flags.js").CommandType<"project", "project-id">, flags: import("@oclif/core/lib/interfaces/parser.js").FlagOutput, args: import("@oclif/core/lib/interfaces/parser.js").ArgOutput, cfg: import("@oclif/core").Config) => Promise<string>;
@@ -1,3 +1,3 @@
1
1
  import { normalizeProjectIdToUuid } from "../../Helpers.js";
2
2
  import { makeFlagSet } from "../context_flags.js";
3
- export const { flags: projectFlags, args: projectArgs, withId: withProjectId, } = makeFlagSet("project", "p", normalizeProjectIdToUuid);
3
+ export const { flags: projectFlags, args: projectArgs, withId: withProjectId, } = makeFlagSet("project", "p", { normalize: normalizeProjectIdToUuid });
@@ -2,8 +2,10 @@ import { assertStatus } from "@mittwald/api-client-commons";
2
2
  import { normalizeProjectIdToUuid } from "../../Helpers.js";
3
3
  export async function getDefaultIngressForProject(apiClient, projectId) {
4
4
  const projectUuid = await normalizeProjectIdToUuid(apiClient, projectId);
5
- const projectIngresses = await apiClient.domain.ingressListForProject({
6
- projectId: projectUuid,
5
+ const projectIngresses = await apiClient.domain.ingressListIngresses({
6
+ queryParameters: {
7
+ projectId: projectUuid,
8
+ },
7
9
  });
8
10
  assertStatus(projectIngresses, 200);
9
11
  const foundIngress = projectIngresses.data.find((item) => {
@@ -2,12 +2,4 @@ export declare const serverFlags: {
2
2
  "server-id": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string>;
3
3
  }, serverArgs: {
4
4
  "server-id": import("@oclif/core/lib/interfaces/parser.js").Arg<string>;
5
- }, withServerId: (apiClient: import("@mittwald/api-client").MittwaldAPIV2Client, command: "flag" | "arg" | ({
6
- flags: {
7
- "server-id": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string>;
8
- };
9
- } | {
10
- args: {
11
- "server-id": import("@oclif/core/lib/interfaces/parser.js").Arg<string>;
12
- };
13
- }), flags: import("@oclif/core/lib/interfaces/parser.js").FlagOutput, args: import("@oclif/core/lib/interfaces/parser.js").ArgOutput, cfg: import("@oclif/core").Config) => Promise<string>;
5
+ }, withServerId: (apiClient: import("@mittwald/api-client").MittwaldAPIV2Client, command: "flag" | "arg" | import("../context_flags.js").CommandType<"server", "server-id">, flags: import("@oclif/core/lib/interfaces/parser.js").FlagOutput, args: import("@oclif/core/lib/interfaces/parser.js").ArgOutput, cfg: import("@oclif/core").Config) => Promise<string>;
@@ -1,3 +1,3 @@
1
1
  import { normalizeServerIdToUuid } from "../../Helpers.js";
2
2
  import { makeFlagSet } from "../context_flags.js";
3
- export const { flags: serverFlags, args: serverArgs, withId: withServerId, } = makeFlagSet("server", "s", normalizeServerIdToUuid);
3
+ export const { flags: serverFlags, args: serverArgs, withId: withServerId, } = makeFlagSet("server", "s", { normalize: normalizeServerIdToUuid });
@@ -3,6 +3,7 @@ import { ReactNode } from "react";
3
3
  import { ExtendedBaseCommand } from "../../ExtendedBaseCommand.js";
4
4
  import { Interfaces } from "@oclif/core";
5
5
  import { FlagInput } from "@oclif/core/lib/interfaces/parser.js";
6
+ import { CommandType } from "../../lib/context_flags.js";
6
7
  declare const renderFlags: {
7
8
  output: Interfaces.OptionFlag<string, Interfaces.CustomOptions>;
8
9
  };
@@ -15,5 +16,7 @@ export declare abstract class RenderBaseCommand<T extends typeof BaseCommand> ex
15
16
  init(): Promise<void>;
16
17
  run(): Promise<void>;
17
18
  protected abstract render(): ReactNode;
19
+ protected useAppInstallationId(command: CommandType<"installation"> | "flag" | "arg"): string;
20
+ protected useProjectId(command: CommandType<"project"> | "flag" | "arg"): string;
18
21
  }
19
22
  export {};
@@ -7,6 +7,7 @@ import { JsonCollectionProvider } from "./json/JsonCollectionProvider.js";
7
7
  import { Flags } from "@oclif/core";
8
8
  import { Render } from "./components/Render.js";
9
9
  import { useIncreaseInkStdoutColumns } from "./hooks/useIncreaseInkStdoutColumns.js";
10
+ import { usePromise } from "@mittwald/react-use-promise";
10
11
  const renderFlags = {
11
12
  output: Flags.string({
12
13
  description: "The output format to use; use 'txt' for a human readable text representation, and 'json' for a machine-readable JSON representation.",
@@ -46,4 +47,10 @@ export class RenderBaseCommand extends ExtendedBaseCommand {
46
47
  return this.render();
47
48
  } }) }) }) }));
48
49
  }
50
+ useAppInstallationId(command) {
51
+ return usePromise(() => this.withAppInstallationId(command), []);
52
+ }
53
+ useProjectId(command) {
54
+ return usePromise(() => this.withProjectId(command), []);
55
+ }
49
56
  }
@@ -9,6 +9,7 @@ import { phpInstaller } from "../../../../commands/app/create/php.js";
9
9
  import { nodeInstaller } from "../../../../commands/app/create/node.js";
10
10
  import { useProject } from "../../../../lib/project/hooks.js";
11
11
  import { IDAndShortID } from "../IDAndShortID.js";
12
+ import path from "path";
12
13
  export const AppInstallationDetails = ({ app, appInstallation }) => {
13
14
  const customInstallation = [phpInstaller.appId, nodeInstaller.appId].includes(app.id);
14
15
  const desiredAppVersion = useAppVersion(app.id, appInstallation.appVersion.desired);
@@ -18,6 +19,9 @@ export const AppInstallationDetails = ({ app, appInstallation }) => {
18
19
  const project = appInstallation.projectId
19
20
  ? useProject(appInstallation.projectId)
20
21
  : null;
22
+ const absoluteInstallPath = project
23
+ ? path.join(project.directories["Web"], appInstallation.installationPath)
24
+ : null;
21
25
  const rows = {
22
26
  "Installation ID": _jsx(Value, { children: appInstallation.id }),
23
27
  App: (_jsx(SingleResultTable, { rows: {
@@ -28,7 +32,7 @@ export const AppInstallationDetails = ({ app, appInstallation }) => {
28
32
  ID: _jsx(IDAndShortID, { object: project }),
29
33
  Description: _jsx(Value, { children: project.description }),
30
34
  } })) : (_jsx(Value, { notSet: true })),
31
- "Installation Path": _jsx(Value, { children: appInstallation.installationPath }),
35
+ "Installation Path": absoluteInstallPath ? (_jsx(Value, { children: absoluteInstallPath })) : (_jsx(Value, { notSet: true })),
32
36
  "Document root (in installation path)": (_jsx(Value, { children: appInstallation.customDocumentRoot ?? "/" })),
33
37
  Description: _jsx(Value, { children: appInstallation.description }),
34
38
  Status: customInstallation ? (_jsx(Text, { children: "custom application" })) : (_jsx(AppInstallationStatus, { appInstallation: appInstallation, desired: desiredAppVersion, current: currentAppVersion })),
@@ -36,6 +40,9 @@ export const AppInstallationDetails = ({ app, appInstallation }) => {
36
40
  const title = (_jsxs(_Fragment, { children: ["APP INSTALLATION: ", _jsx(Value, { children: app.name }), " in", " ", _jsx(Value, { children: appInstallation.installationPath })] }));
37
41
  const sections = [
38
42
  _jsx(SingleResult, { title: title, rows: rows }, "primary"),
43
+ _jsx(SingleResult, { title: "Access", rows: {
44
+ "SSH/SFTP Host": (_jsxs(Text, { children: [_jsxs(Value, { children: ["ssh.", project?.clusterID, ".", project?.clusterDomain] }), " ", _jsx(Text, { color: "gray", children: "(Use the \"app ssh\" command to connect directly using the CLI)" })] })),
45
+ } }, "access"),
39
46
  _jsx(AppSystemSoftware, { appInstallation: appInstallation }, "systemsoftware"),
40
47
  ];
41
48
  return (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: sections }));
@@ -0,0 +1,8 @@
1
+ import { FC } from "react";
2
+ import { MittwaldAPIV2 } from "@mittwald/api-client";
3
+ import CronjobCronjob = MittwaldAPIV2.Components.Schemas.CronjobCronjob;
4
+ type CronJobComponent = FC<{
5
+ cronjob: CronjobCronjob;
6
+ }>;
7
+ export declare const CronJobDetails: CronJobComponent;
8
+ export {};
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { SingleResult } from "../SingleResult.js";
3
+ import { Value } from "../Value.js";
4
+ import { Box, Text } from "ink";
5
+ import { IDAndShortID } from "../IDAndShortID.js";
6
+ import { CreatedAt } from "../CreatedAt.js";
7
+ import { useProject } from "../../../../lib/project/hooks.js";
8
+ import { useAppInstallation } from "../../../../lib/app/hooks.js";
9
+ import { FormattedDate } from "../FormattedDate.js";
10
+ const CronJobNextExecution = ({ cronjob }) => {
11
+ if (!cronjob.nextExecutionTime) {
12
+ return _jsx(Value, { notSet: true });
13
+ }
14
+ return (_jsx(Value, { children: _jsx(FormattedDate, { relative: true, date: cronjob.nextExecutionTime }) }));
15
+ };
16
+ const CronJobExecutionTargetURL = ({ dest, }) => {
17
+ return (_jsx(SingleResult, { title: "EXECUTION TARGET", rows: {
18
+ URL: _jsx(Value, { children: dest.url }),
19
+ } }));
20
+ };
21
+ const CronJobExecutionTargetCommand = ({ command, }) => {
22
+ return (_jsx(SingleResult, { title: "EXECUTION TARGET", rows: {
23
+ Interpreter: _jsx(Value, { children: command.interpreter }),
24
+ Script: _jsx(Value, { children: command.path }),
25
+ Parameters: command.parameters ? (_jsxs(Value, { children: [command.parameters, " "] })) : (_jsx(Value, { notSet: true })),
26
+ } }));
27
+ };
28
+ export const CronJobDetails = ({ cronjob }) => {
29
+ const project = cronjob.projectId ? useProject(cronjob.projectId) : null;
30
+ const app = useAppInstallation(cronjob.appId);
31
+ const rows = {
32
+ "Cron Job ID": _jsx(IDAndShortID, { object: cronjob }),
33
+ "Created At": _jsx(CreatedAt, { object: cronjob }),
34
+ Project: project ? _jsx(IDAndShortID, { object: project }) : _jsx(Value, { notSet: true }),
35
+ App: _jsx(IDAndShortID, { object: app }),
36
+ Schedule: (_jsxs(Text, { children: [_jsx(Value, { children: cronjob.interval }), " (next execution:", " ", _jsx(CronJobNextExecution, { cronjob: cronjob }), ")"] })),
37
+ };
38
+ const sections = [
39
+ _jsx(SingleResult, { title: _jsxs(_Fragment, { children: ["CRON JOB DETAILS: ", _jsx(Value, { children: cronjob.description })] }), rows: rows }, "primary"),
40
+ ];
41
+ if ("url" in cronjob.destination) {
42
+ sections.push(_jsx(CronJobExecutionTargetURL, { dest: cronjob.destination }, "destination"));
43
+ }
44
+ else {
45
+ sections.push(_jsx(CronJobExecutionTargetCommand, { command: cronjob.destination }, "destination"));
46
+ }
47
+ return (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: sections }));
48
+ };
@@ -0,0 +1,5 @@
1
+ import { FC } from "react";
2
+ export declare const LocalFilename: FC<{
3
+ filename: string;
4
+ relative?: boolean;
5
+ }>;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Filename } from "./Filename.js";
3
+ import { getHomeDir } from "@oclif/core/lib/util/os.js";
4
+ import path from "path";
5
+ export const LocalFilename = ({ filename, relative = false, }) => {
6
+ if (relative) {
7
+ filename = path.relative(process.cwd(), filename);
8
+ }
9
+ else {
10
+ filename = filename.replace(getHomeDir(), "~");
11
+ }
12
+ filename = filename.replace(process.cwd(), ".");
13
+ return _jsx(Filename, { filename: filename });
14
+ };
@@ -3,6 +3,7 @@ export type NoteProps = PropsWithChildren<{
3
3
  title?: string;
4
4
  color?: string;
5
5
  marginY?: number;
6
+ marginBottom?: number;
6
7
  raw?: boolean;
7
8
  }>;
8
9
  export declare const noteColor = "#407FF8";
@@ -4,5 +4,5 @@ export const noteColor = "#407FF8";
4
4
  export const Note = (props) => {
5
5
  const { title = "Note", color = noteColor, marginY = 0, raw = false } = props;
6
6
  const contents = raw ? (props.children) : (_jsx(Text, { wrap: "wrap", color: color, children: props.children }));
7
- return (_jsxs(Box, { width: 80, borderStyle: "round", borderColor: color, flexDirection: "column", paddingX: 2, marginY: marginY, children: [_jsx(Text, { bold: true, underline: true, color: color, children: title.toUpperCase() }), contents] }));
7
+ return (_jsxs(Box, { width: 80, borderStyle: "round", borderColor: color, flexDirection: "column", paddingX: 2, marginY: marginY, marginBottom: props.marginBottom, children: [_jsx(Text, { bold: true, underline: true, color: color, children: title.toUpperCase() }), contents] }));
8
8
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/cli",
3
- "version": "1.0.0-alpha.27",
3
+ "version": "1.0.0-alpha.28",
4
4
  "description": "Hand-crafted CLI for the mittwald API",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -44,10 +44,11 @@
44
44
  "bin"
45
45
  ],
46
46
  "dependencies": {
47
- "@mittwald/api-client": "^3.0.7",
48
- "@mittwald/api-client-commons": "^4.1.0",
49
- "@mittwald/react-use-promise": "^1.3.3",
47
+ "@mittwald/api-client": "^3.1.13",
48
+ "@mittwald/api-client-commons": "^4.1.1",
49
+ "@mittwald/react-use-promise": "^2.1.0",
50
50
  "@oclif/core": "^3.9.1",
51
+ "@oclif/plugin-autocomplete": "^3.0.3",
51
52
  "@oclif/plugin-help": "^6.0.5",
52
53
  "@oclif/plugin-update": "^4.1.3",
53
54
  "@oclif/plugin-warn-if-update-available": "^3.0.2",
@@ -59,7 +60,7 @@
59
60
  "ink-link": "^3.0.0",
60
61
  "ink-text-input": "^5.0.1",
61
62
  "js-yaml": "^4.1.0",
62
- "marked": "^9.1.5",
63
+ "marked": "^11.1.0",
63
64
  "marked-terminal": "^6.0.0",
64
65
  "node-notifier": "^10.0.1",
65
66
  "open": "^9.1.0",
@@ -198,7 +199,8 @@
198
199
  "plugins": [
199
200
  "@oclif/plugin-help",
200
201
  "@oclif/plugin-update",
201
- "@oclif/plugin-warn-if-update-available"
202
+ "@oclif/plugin-warn-if-update-available",
203
+ "@oclif/plugin-autocomplete"
202
204
  ],
203
205
  "update": {
204
206
  "node": {