@chabokan.net/cli 0.8.10 → 0.9.0

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 (55) hide show
  1. package/README.md +520 -66
  2. package/dist/base.d.ts +34 -9
  3. package/dist/base.js +172 -20
  4. package/dist/commands/account/info.d.ts +12 -0
  5. package/dist/commands/account/info.js +40 -0
  6. package/dist/commands/account/list.d.ts +5 -1
  7. package/dist/commands/account/list.js +35 -16
  8. package/dist/commands/account/remove.d.ts +5 -1
  9. package/dist/commands/account/remove.js +30 -15
  10. package/dist/commands/account/use.d.ts +6 -2
  11. package/dist/commands/account/use.js +26 -15
  12. package/dist/commands/cloudserver/create.d.ts +40 -0
  13. package/dist/commands/cloudserver/create.js +242 -0
  14. package/dist/commands/cloudserver/delete.d.ts +14 -0
  15. package/dist/commands/cloudserver/delete.js +65 -0
  16. package/dist/commands/cloudserver/list.d.ts +12 -0
  17. package/dist/commands/cloudserver/list.js +46 -0
  18. package/dist/commands/cloudserver/restart.d.ts +13 -0
  19. package/dist/commands/cloudserver/restart.js +51 -0
  20. package/dist/commands/cloudserver/start.d.ts +13 -0
  21. package/dist/commands/cloudserver/start.js +51 -0
  22. package/dist/commands/cloudserver/stop.d.ts +13 -0
  23. package/dist/commands/cloudserver/stop.js +52 -0
  24. package/dist/commands/deploy.d.ts +16 -5
  25. package/dist/commands/deploy.js +182 -101
  26. package/dist/commands/login.d.ts +8 -4
  27. package/dist/commands/login.js +97 -49
  28. package/dist/commands/service/domain/add.d.ts +15 -0
  29. package/dist/commands/service/domain/add.js +74 -0
  30. package/dist/commands/service/domain/remove.d.ts +15 -0
  31. package/dist/commands/service/domain/remove.js +72 -0
  32. package/dist/commands/service/list.d.ts +5 -1
  33. package/dist/commands/service/list.js +41 -20
  34. package/dist/commands/service/logs.d.ts +7 -3
  35. package/dist/commands/service/logs.js +46 -34
  36. package/dist/commands/service/resize.d.ts +10 -6
  37. package/dist/commands/service/resize.js +94 -72
  38. package/dist/commands/service/restart.d.ts +7 -3
  39. package/dist/commands/service/restart.js +40 -31
  40. package/dist/commands/service/start.d.ts +7 -3
  41. package/dist/commands/service/start.js +41 -30
  42. package/dist/commands/service/stop.d.ts +7 -3
  43. package/dist/commands/service/stop.js +41 -30
  44. package/dist/commands/wallet/list.d.ts +12 -0
  45. package/dist/commands/wallet/list.js +41 -0
  46. package/dist/constants.d.ts +2 -0
  47. package/dist/constants.js +7 -2
  48. package/dist/helper.d.ts +32 -9
  49. package/dist/helper.js +426 -49
  50. package/dist/types.d.ts +77 -0
  51. package/dist/types.js +2 -0
  52. package/dist/ui.d.ts +23 -0
  53. package/dist/ui.js +70 -0
  54. package/oclif.manifest.json +635 -25
  55. package/package.json +39 -35
@@ -0,0 +1,15 @@
1
+ import Command from "../../../base.js";
2
+ export default class ServiceDomainRemove extends Command {
3
+ static description: string;
4
+ static examples: {
5
+ description: string;
6
+ command: string;
7
+ }[];
8
+ static flags: {
9
+ service: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ help: import("@oclif/core/interfaces").BooleanFlag<void>;
12
+ };
13
+ run(): Promise<void>;
14
+ send_request(cli: Command, selected_service: string, domain_name: string): Promise<void>;
15
+ }
@@ -0,0 +1,72 @@
1
+ import { Flags } from '@oclif/core';
2
+ import Command from "../../../base.js";
3
+ import { handleApiError, logErrorDetails } from "../../../helper.js";
4
+ import * as ui from "../../../ui.js";
5
+ import inquirer from 'inquirer';
6
+ import axios from "axios";
7
+ export default class ServiceDomainRemove extends Command {
8
+ static description = 'disconnect a custom domain from a service';
9
+ static examples = [
10
+ {
11
+ description: 'Pick the service and enter the domain interactively',
12
+ command: '<%= config.bin %> service domain remove',
13
+ },
14
+ {
15
+ description: 'Disconnect a domain in one non-interactive command',
16
+ command: '<%= config.bin %> service domain remove -s my-app -d www.example.com',
17
+ },
18
+ ];
19
+ static flags = {
20
+ ...Command.flags,
21
+ service: Flags.string({ char: 's', description: 'name of the service the domain is connected to' }),
22
+ domain: Flags.string({ char: 'd', description: 'domain name to disconnect' }),
23
+ };
24
+ async run() {
25
+ const { flags } = await this.parse(ServiceDomainRemove);
26
+ const cli = this;
27
+ await this.init_run();
28
+ if (!(await this.require_login())) {
29
+ return;
30
+ }
31
+ const selected_service = await this.select_service(flags.service, {}, 'Disconnect Domain');
32
+ if (!selected_service) {
33
+ return;
34
+ }
35
+ let domain_name = flags.domain;
36
+ if (!domain_name) {
37
+ ({ domain_name } = await inquirer.prompt({
38
+ type: 'input',
39
+ message: 'Domain name:',
40
+ name: 'domain_name',
41
+ validate(input) {
42
+ return input.trim().length > 0 || 'Domain name cannot be empty';
43
+ },
44
+ }));
45
+ }
46
+ await this.send_request(cli, selected_service, domain_name.trim());
47
+ }
48
+ async send_request(cli, selected_service, domain_name) {
49
+ try {
50
+ const { data } = await axios.delete(`services/${selected_service}/domain/${domain_name}/`, this.axiosConfig);
51
+ if (data?.success === false) {
52
+ cli.log(ui.error(`Could not disconnect ${ui.value(domain_name)}. ${data.message || 'Check the domain name and try again.'}`));
53
+ return;
54
+ }
55
+ cli.log(ui.success(`Disconnected ${ui.value(domain_name)} from ${ui.value(selected_service)}.`));
56
+ }
57
+ catch (error) {
58
+ const errorInfo = handleApiError(error, 'Failed to disconnect domain.', {
59
+ endpoint: `services/${selected_service}/domain/${domain_name}/`,
60
+ serviceName: selected_service,
61
+ operation: 'Disconnect Domain'
62
+ });
63
+ if (errorInfo.status === 404) {
64
+ cli.log(ui.error(`Domain ${ui.value(domain_name)} was not found on ${ui.value(selected_service)}.`));
65
+ }
66
+ else {
67
+ cli.log(ui.error(errorInfo.message));
68
+ }
69
+ logErrorDetails(errorInfo, cli);
70
+ }
71
+ }
72
+ }
@@ -1,8 +1,12 @@
1
1
  import Command from "../../base.js";
2
2
  export default class ServiceList extends Command {
3
3
  static description: string;
4
+ static examples: {
5
+ description: string;
6
+ command: string;
7
+ }[];
4
8
  static flags: {
5
- help: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<void>;
9
+ help: import("@oclif/core/interfaces").BooleanFlag<void>;
6
10
  };
7
11
  run(): Promise<void>;
8
12
  }
@@ -1,32 +1,53 @@
1
1
  import Command from "../../base.js";
2
- import { ux } from "@oclif/core";
2
+ import { printTable } from "@oclif/table";
3
+ import * as ui from "../../ui.js";
4
+ import { handleApiError, logErrorDetails } from "../../helper.js";
3
5
  export default class ServiceList extends Command {
4
- static description = "show account services list";
6
+ static description = "list the services on your account";
7
+ static examples = [
8
+ {
9
+ description: 'List every service on the active account',
10
+ command: '<%= config.bin %> service list',
11
+ },
12
+ {
13
+ description: 'Switch account first, then list that account’s services',
14
+ command: '<%= config.bin %> account use -u me@example.com && <%= config.bin %> service list',
15
+ },
16
+ ];
5
17
  static flags = {
6
18
  ...Command.flags,
7
19
  };
8
20
  async run() {
9
- const { args, flags } = await this.parse(ServiceList);
21
+ await this.parse(ServiceList);
10
22
  const cli = this;
11
- await this.init_run();
12
- const config_json = await this.read_config();
13
- let all_services = await this.get_services();
14
- if (all_services.length > 0) {
15
- const services_data = all_services.map((service) => {
16
- // const shamshiate = shamsi.gregorianToJalali(
17
- // new Date(project.created)
18
- // );
19
- return {
20
- Name: service.name,
21
- };
23
+ try {
24
+ await this.init_run();
25
+ if (!(await this.require_login())) {
26
+ return;
27
+ }
28
+ const all_services = await this.get_services();
29
+ if (all_services.length === 0) {
30
+ cli.log(ui.info('You have no services yet.'));
31
+ cli.log(ui.hint('Create one at https://hub.chabokan.net, then come back here'));
32
+ return;
33
+ }
34
+ const services_data = all_services.map((service) => ({
35
+ Service: service.value,
36
+ Platform: service.platform,
37
+ }));
38
+ printTable({
39
+ data: services_data,
40
+ columns: ["Service", "Platform"],
22
41
  });
23
- ux.table(services_data, {
24
- Name: {},
25
- }, flags);
42
+ cli.log(ui.hint(`Act on one with ${ui.cmd('chabok service logs -s <name>')}, ${ui.cmd('restart')}, ${ui.cmd('stop')} …`));
26
43
  }
27
- else {
28
- cli.log("first you should create service");
29
- return;
44
+ catch (error) {
45
+ const errorInfo = handleApiError(error, 'Failed to fetch services list.', {
46
+ endpoint: 'services/',
47
+ operation: 'List Services'
48
+ });
49
+ cli.log(ui.error(errorInfo.message));
50
+ logErrorDetails(errorInfo, cli);
30
51
  }
31
52
  }
32
53
  }
@@ -1,10 +1,14 @@
1
1
  import Command from "../../base.js";
2
2
  export default class ServiceLogs extends Command {
3
3
  static description: string;
4
+ static examples: {
5
+ description: string;
6
+ command: string;
7
+ }[];
4
8
  static flags: {
5
- service: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
6
- help: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<void>;
9
+ service: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ help: import("@oclif/core/interfaces").BooleanFlag<void>;
7
11
  };
8
12
  run(): Promise<void>;
9
- send_request(cli: any, selected_service: any): Promise<void>;
13
+ send_request(cli: Command, selected_service: string): Promise<void>;
10
14
  }
@@ -1,59 +1,71 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import Command from "../../base.js";
3
- import { isEmptyObject } from "../../helper.js";
4
- import chalk from "chalk";
5
- import inquirer from 'inquirer';
3
+ import { handleApiError, logErrorDetails } from "../../helper.js";
4
+ import * as ui from "../../ui.js";
6
5
  import axios from "axios";
7
6
  export default class ServiceLogs extends Command {
8
- static description = 'read latest logs from service';
7
+ static description = 'read the latest logs from a service';
8
+ static examples = [
9
+ {
10
+ description: 'Pick a service from an interactive list',
11
+ command: '<%= config.bin %> service logs',
12
+ },
13
+ {
14
+ description: 'Read logs for a specific service',
15
+ command: '<%= config.bin %> service logs --service my-app',
16
+ },
17
+ {
18
+ description: 'Search the logs for errors',
19
+ command: '<%= config.bin %> service logs -s my-app | grep -i error',
20
+ },
21
+ {
22
+ description: 'Save the logs to a file',
23
+ command: '<%= config.bin %> service logs -s my-app > my-app.log',
24
+ },
25
+ ];
9
26
  static flags = {
10
27
  ...Command.flags,
11
- service: Flags.string({ char: 's', description: 'service name' }),
28
+ service: Flags.string({ char: 's', description: 'name of the service to read logs from' }),
12
29
  };
13
30
  async run() {
14
- const { args, flags } = await this.parse(ServiceLogs);
31
+ const { flags } = await this.parse(ServiceLogs);
15
32
  const cli = this;
16
33
  await this.init_run();
17
- const config_json = await this.read_config();
18
- let selected_service = flags.service;
19
- if (isEmptyObject(config_json.users)) {
20
- cli.log(`${chalk.red('[Error]')} first you should login!`);
34
+ if (!(await this.require_login())) {
21
35
  return;
22
36
  }
23
- if (!flags.service) {
24
- let all_services = await this.get_services();
25
- if (all_services.length > 0) {
26
- let { service } = await inquirer.prompt({
27
- type: 'list',
28
- message: 'Please select a service:',
29
- name: 'service',
30
- choices: all_services
31
- });
32
- selected_service = service;
33
- }
34
- else {
35
- cli.log("first you should create service");
36
- return;
37
- }
37
+ const selected_service = await this.select_service(flags.service, {}, 'Fetch Logs');
38
+ if (!selected_service) {
39
+ return;
38
40
  }
39
41
  await this.send_request(cli, selected_service);
40
42
  }
41
43
  async send_request(cli, selected_service) {
42
44
  try {
43
- if (selected_service) {
44
- const { data } = await axios.get("services/" + selected_service + "/logs/", this.axiosConfig);
45
- if (data.success) {
46
- cli.log(data.logs);
47
- }
45
+ const { data } = await axios.get(`services/${selected_service}/logs/`, this.axiosConfig);
46
+ if (data.success && data.logs) {
47
+ // Logs go out verbatim so they stay pipeable into grep, tee and friends.
48
+ cli.log(data.logs);
49
+ }
50
+ else {
51
+ cli.log(ui.info(`No logs yet for ${ui.value(selected_service)}.`));
52
+ cli.log(ui.hint('A service that has just started may take a moment to produce output'));
48
53
  }
49
54
  }
50
- catch (e) {
51
- if (e.response.status == 404) {
52
- cli.log(chalk.blue("Selected Service Not Founded."));
55
+ catch (error) {
56
+ const errorInfo = handleApiError(error, 'Failed to fetch service logs.', {
57
+ endpoint: `services/${selected_service}/logs/`,
58
+ serviceName: selected_service,
59
+ operation: 'Fetch Logs'
60
+ });
61
+ if (errorInfo.status === 404) {
62
+ cli.log(ui.error(`Service ${ui.value(selected_service)} was not found.`));
63
+ cli.log(ui.hint(`Check the available names with ${ui.cmd('chabok service list')}`));
53
64
  }
54
65
  else {
55
- console.log(e.data);
66
+ cli.log(ui.error(errorInfo.message));
56
67
  }
68
+ logErrorDetails(errorInfo, cli);
57
69
  }
58
70
  }
59
71
  }
@@ -1,13 +1,17 @@
1
1
  import Command from "../../base.js";
2
2
  export default class ServiceResize extends Command {
3
3
  static description: string;
4
+ static examples: {
5
+ description: string;
6
+ command: string;
7
+ }[];
4
8
  static flags: {
5
- service: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
6
- ram: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
- cpu: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
- disk: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
- help: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<void>;
9
+ service: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ ram: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ cpu: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ disk: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ help: import("@oclif/core/interfaces").BooleanFlag<void>;
10
14
  };
11
15
  run(): Promise<void>;
12
- send_request(cli: any, selected_service: any, ram: any, cpu: any, disk: any): Promise<void>;
16
+ send_request(cli: Command, selected_service: string, ram: string, cpu: string, disk: string): Promise<void>;
13
17
  }
@@ -1,130 +1,152 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import Command from "../../base.js";
3
- import { isEmptyObject } from "../../helper.js";
4
- import chalk from "chalk";
3
+ import { handleApiError, logErrorDetails } from "../../helper.js";
4
+ import * as ui from "../../ui.js";
5
5
  import inquirer from 'inquirer';
6
6
  import axios from "axios";
7
7
  export default class ServiceResize extends Command {
8
- static description = 'resize a service';
8
+ static description = 'change the RAM, CPU and disk allocated to a service';
9
+ static examples = [
10
+ {
11
+ description: 'Choose the service and every resource interactively',
12
+ command: '<%= config.bin %> service resize',
13
+ },
14
+ {
15
+ description: 'Resize a service in one non-interactive command',
16
+ command: '<%= config.bin %> service resize -s my-app --ram 2 --cpu 1 --disk 10',
17
+ },
18
+ {
19
+ description: 'Set only the RAM and pick the rest from the prompts',
20
+ command: '<%= config.bin %> service resize -s my-app --ram 4',
21
+ },
22
+ ];
9
23
  static flags = {
10
24
  ...Command.flags,
11
- service: Flags.string({ char: 's', description: 'service name' }),
12
- ram: Flags.string({ char: 'r', description: 'RAM' }),
13
- cpu: Flags.string({ char: 'c', description: 'CPU' }),
14
- disk: Flags.string({ char: 'd', description: 'DISK' }),
25
+ service: Flags.string({ char: 's', description: 'name of the service to resize' }),
26
+ ram: Flags.string({ char: 'r', description: 'RAM in GB, in steps of 0.5 (e.g. 0.5, 1, 2)' }),
27
+ cpu: Flags.string({ char: 'c', description: 'CPU cores, in steps of 0.5 (e.g. 0.5, 1, 2)' }),
28
+ disk: Flags.string({ char: 'd', description: 'disk in GB, in steps of 5 (e.g. 5, 10, 30)' }),
15
29
  };
16
30
  async run() {
17
- const { args, flags } = await this.parse(ServiceResize);
31
+ const { flags } = await this.parse(ServiceResize);
18
32
  const cli = this;
19
33
  await this.init_run();
20
- const config_json = await this.read_config();
21
- let selected_service = flags.service;
22
34
  let selected_ram = flags.ram;
23
35
  let selected_cpu = flags.cpu;
24
36
  let selected_disk = flags.disk;
25
- if (isEmptyObject(config_json.users)) {
26
- cli.log(`${chalk.red('[Error]')} first you should login!`);
37
+ if (!(await this.require_login())) {
27
38
  return;
28
39
  }
40
+ const selected_service = await this.select_service(flags.service, { 'not_status': 'pending', 'has_custom_plan': 'true' }, 'Resize Service');
29
41
  if (!selected_service) {
30
- let all_services = await this.get_services({ 'not_status': 'pending', 'has_custom_plan': 'true' });
31
- if (all_services) {
32
- let { service } = await inquirer.prompt({
33
- type: 'list',
34
- message: 'Please select a service:',
35
- name: 'service',
36
- choices: all_services
37
- });
38
- selected_service = service;
39
- }
42
+ return;
40
43
  }
41
44
  if (!selected_ram) {
42
- let { ram } = await inquirer.prompt({
43
- type: 'list',
44
- message: 'Please select service ram:',
45
+ const { ram } = await inquirer.prompt({
46
+ type: 'select',
47
+ message: 'RAM:',
45
48
  name: 'ram',
46
49
  choices: [
47
- { value: "0.5", name: "0.5G" },
48
- { value: "1", name: "1G" },
49
- { value: "2", name: "2G" },
50
- { value: "4", name: "4G" },
51
- { value: "6", name: "6G" },
52
- { value: "8", name: "8G" },
50
+ { value: "0.5", name: "0.5 GB" },
51
+ { value: "1", name: "1 GB" },
52
+ { value: "2", name: "2 GB" },
53
+ { value: "4", name: "4 GB" },
54
+ { value: "6", name: "6 GB" },
55
+ { value: "8", name: "8 GB" },
53
56
  ]
54
57
  });
55
58
  selected_ram = ram;
56
59
  }
57
60
  if (!selected_cpu) {
58
- let { cpu } = await inquirer.prompt({
59
- type: 'list',
60
- message: 'Please select service cpu:',
61
+ const { cpu } = await inquirer.prompt({
62
+ type: 'select',
63
+ message: 'CPU:',
61
64
  name: 'cpu',
62
65
  choices: [
63
- { value: "0.5", name: "0.5 Core" },
64
- { value: "1", name: "1 Core" },
65
- { value: "2", name: "2 Core" },
66
- { value: "3", name: "3 Core" },
67
- { value: "4", name: "4 Core" },
66
+ { value: "0.5", name: "0.5 core" },
67
+ { value: "1", name: "1 core" },
68
+ { value: "2", name: "2 cores" },
69
+ { value: "3", name: "3 cores" },
70
+ { value: "4", name: "4 cores" },
68
71
  ]
69
72
  });
70
73
  selected_cpu = cpu;
71
74
  }
72
75
  if (!selected_disk) {
73
- let { disk } = await inquirer.prompt({
74
- type: 'list',
75
- message: 'Please select service disk:',
76
+ const { disk } = await inquirer.prompt({
77
+ type: 'select',
78
+ message: 'Disk:',
76
79
  name: 'disk',
77
80
  choices: [
78
- { value: "5", name: "5G" },
79
- { value: "10", name: "10G" },
80
- { value: "15", name: "15G" },
81
- { value: "30", name: "30G" },
82
- { value: "40", name: "40G" },
83
- { value: "50", name: "50G" },
81
+ { value: "5", name: "5 GB" },
82
+ { value: "10", name: "10 GB" },
83
+ { value: "15", name: "15 GB" },
84
+ { value: "30", name: "30 GB" },
85
+ { value: "40", name: "40 GB" },
86
+ { value: "50", name: "50 GB" },
84
87
  ]
85
88
  });
86
89
  selected_disk = disk;
87
90
  }
91
+ if (!selected_ram || !selected_cpu || !selected_disk) {
92
+ cli.log(ui.error('RAM, CPU and disk are all required.'));
93
+ return;
94
+ }
88
95
  let wrong_value = false;
89
- if (selected_ram % 0.5 != 0) {
90
- cli.log(`${chalk.red('[Error]')} ram amount should coefficient of 0.5`);
96
+ const ramNum = Number.parseFloat(selected_ram);
97
+ const cpuNum = Number.parseFloat(selected_cpu);
98
+ const diskNum = Number.parseFloat(selected_disk);
99
+ if (Number.isNaN(ramNum) || ramNum % 0.5 !== 0) {
100
+ cli.log(ui.error(`RAM must be a multiple of 0.5 GB, got ${ui.value(selected_ram)}.`));
91
101
  wrong_value = true;
92
102
  }
93
- if (selected_cpu % 0.5 != 0) {
94
- cli.log(`${chalk.red('[Error]')} cpu amount should coefficient of 0.5`);
103
+ if (Number.isNaN(cpuNum) || cpuNum % 0.5 !== 0) {
104
+ cli.log(ui.error(`CPU must be a multiple of 0.5 cores, got ${ui.value(selected_cpu)}.`));
95
105
  wrong_value = true;
96
106
  }
97
- if (selected_disk % 5 != 0) {
98
- cli.log(`${chalk.red('[Error]')} disk amount should coefficient of 5`);
107
+ if (Number.isNaN(diskNum) || diskNum % 5 !== 0) {
108
+ cli.log(ui.error(`Disk must be a multiple of 5 GB, got ${ui.value(selected_disk)}.`));
99
109
  wrong_value = true;
100
110
  }
101
- if (selected_service && selected_ram && selected_cpu && selected_disk && !wrong_value) {
102
- await this.send_request(cli, selected_service, selected_ram, selected_cpu, selected_disk);
111
+ if (wrong_value) {
112
+ cli.log(ui.hint(`Example: ${ui.cmd(`chabok service resize -s ${selected_service} --ram 2 --cpu 1 --disk 10`)}`));
113
+ return;
103
114
  }
115
+ await this.send_request(cli, selected_service, selected_ram, selected_cpu, selected_disk);
104
116
  }
105
117
  async send_request(cli, selected_service, ram, cpu, disk) {
106
118
  try {
107
- if (selected_service) {
108
- const { data } = await axios.post("services/" + selected_service + "/resize/", {
109
- "ram": ram,
110
- "cpu": cpu,
111
- "disk": disk,
112
- }, this.axiosConfig);
113
- if (data.success) {
114
- cli.log(`${chalk.green('[Success]')} service resized successfully.`);
115
- }
116
- else {
117
- cli.log(`${chalk.red('[Error]')} some problem in resize.`);
118
- }
119
+ const { data } = await axios.post(`services/${selected_service}/resize/`, {
120
+ ram,
121
+ cpu,
122
+ disk,
123
+ }, this.axiosConfig);
124
+ if (data.success) {
125
+ cli.log(ui.success(`Service ${ui.value(selected_service)} resized to ${ui.value(`${ram} GB RAM`)}, ${ui.value(`${cpu} CPU`)}, ${ui.value(`${disk} GB disk`)}.`));
126
+ cli.log(ui.hint(`Restart it to apply the new limits: ${ui.cmd(`chabok service restart -s ${selected_service}`)}`));
127
+ }
128
+ else {
129
+ cli.log(ui.error(`Could not resize ${ui.value(selected_service)}. ${data.message || 'Check the resource values and try again.'}`));
119
130
  }
120
131
  }
121
- catch (e) {
122
- if (e.response.status == 404) {
123
- cli.log(chalk.blue("Selected Service Not Founded."));
132
+ catch (error) {
133
+ const errorInfo = handleApiError(error, 'Failed to resize service.', {
134
+ endpoint: `services/${selected_service}/resize/`,
135
+ serviceName: selected_service,
136
+ operation: 'Resize Service'
137
+ });
138
+ if (errorInfo.status === 404) {
139
+ cli.log(ui.error(`Service ${ui.value(selected_service)} was not found.`));
140
+ cli.log(ui.hint(`Check the available names with ${ui.cmd('chabok service list')}`));
141
+ }
142
+ else if (errorInfo.status === 400 || errorInfo.status === 422) {
143
+ cli.log(ui.error(errorInfo.message));
144
+ cli.log(ui.hint('RAM and CPU go in steps of 0.5; disk goes in steps of 5'));
124
145
  }
125
146
  else {
126
- console.log(e.data);
147
+ cli.log(ui.error(errorInfo.message));
127
148
  }
149
+ logErrorDetails(errorInfo, cli);
128
150
  }
129
151
  }
130
152
  }
@@ -1,10 +1,14 @@
1
1
  import Command from "../../base.js";
2
2
  export default class ServiceRestart extends Command {
3
3
  static description: string;
4
+ static examples: {
5
+ description: string;
6
+ command: string;
7
+ }[];
4
8
  static flags: {
5
- service: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
6
- help: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<void>;
9
+ service: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ help: import("@oclif/core/interfaces").BooleanFlag<void>;
7
11
  };
8
12
  run(): Promise<void>;
9
- send_request(cli: any, selected_service: any): Promise<void>;
13
+ send_request(cli: Command, selected_service: string): Promise<void>;
10
14
  }