@liara/cli
Version:
The command line interface for Liara
403 lines (402 loc) • 13.4 kB
JavaScript
import inquirer from 'inquirer';
import Command from '../../../base.js';
import { Flags } from '@oclif/core';
import { createDebugLogger } from '../../../utils/output.js';
import { ux } from '@oclif/core';
import { RecordType, } from '../../../types/dns-records.js';
const promptRecordContent = {
A: async (flags) => {
// @ts-ignore
let result = [];
if (flags.ip) {
flags.ip.map((ip) => {
result.push({ ip: ip });
});
}
else {
let done = false;
let i = 1;
do {
const { ip } = (await inquirer.prompt({
name: 'ip',
type: 'input',
message: `Enter ip${i} (leave empty to finish):`,
validate: (input) => input.length >= 0 || done === true,
}));
if (ip.length > 0) {
result.push({ ip: ip });
}
else {
done = true;
}
i++;
} while (!done);
}
return result;
},
AAAA: async (flags) => {
// @ts-ignore
let result = [];
if (flags.ip) {
flags.ip.map((ip) => {
result.push({ ip: ip });
});
}
else {
let done = false;
let i = 1;
do {
const { ip } = (await inquirer.prompt({
name: 'ip',
type: 'input',
message: `Enter ip${i} (leave empty to finish):`,
validate: (input) => input.length >= 0 || done === true,
}));
if (ip.length > 0) {
result.push({ ip: ip });
}
else {
done = true;
}
i++;
} while (!done);
}
return result;
},
ALIAS: async (flags) => {
// @ts-ignore
let result = [];
if (flags.host) {
result.push({ host: flags.host });
}
else {
let done = true;
let i = 1;
do {
const { host } = (await inquirer.prompt({
name: 'host',
type: 'input',
message: `Enter host${i} (leave empty to finish):`,
validate: (input) => input.length >= 0 || done === true,
}));
if (host.length > 0) {
result.push({ host: host });
}
else {
done = true;
}
i++;
} while (!done);
}
return result;
},
CNAME: async (flags) => {
// @ts-ignore
let result = [];
if (flags.host) {
result.push({ host: flags.host });
}
else {
let done = true;
let i = 1;
do {
const { host } = (await inquirer.prompt({
name: 'host',
type: 'input',
message: `Enter host${i} (leave empty to finish):`,
validate: (input) => input.length >= 0 || done === true,
}));
if (host.length > 0) {
result.push({ host: host });
}
else {
done = true;
}
i++;
} while (!done);
}
return result;
},
MX: async (flags) => {
// @ts-ignore
let result = [];
if (flags.mx) {
flags.mx.map((combineInput) => {
const parsed = combineInput.split(',');
if (parsed.length != 2) {
throw Error('mx flag should be like this: <hostname>,<priority>');
}
result.push({ host: parsed[0], priority: parsed[1] });
});
}
else {
let done = false;
let i = 1;
do {
const { combineInput } = (await inquirer.prompt({
name: 'combineInput',
type: 'input',
message: `Enter hostname and priority ${i} (<hostname> <priority>. leave empty to finish):`,
validate: (input) => input.length == 0 || input.split(' ').length == 2 || done === true,
}));
if (combineInput.length > 0) {
const parsed = combineInput.split(' ');
if (parsed.length != 2) {
throw Error('mx inputs should be like this: <hostname> <priority>');
}
result.push({ host: parsed[0], priority: parsed[1] });
}
else {
done = true;
}
i++;
} while (!done);
}
return result;
},
SRV: async (flags) => {
// @ts-ignore
let result = [];
if (flags.srv) {
flags.srv.map((combineInput) => {
const parsed = combineInput.split(',');
if (parsed.length != 4) {
throw Error('srv flag should be like this: <hostname>,<port>,<priority>,<weight>');
}
result.push({
host: parsed[0],
port: parsed[1],
priority: parsed[2],
weight: parsed[3],
});
});
}
else {
let done = false;
let i = 1;
do {
const { combineInput } = (await inquirer.prompt({
name: 'combineInput',
type: 'input',
message: `Enter hostname, port, priority and weight ${i} (<hostname> <port> <priority> <weight>. leave empty to finish):`,
validate: (input) => input.length == 0 || input.split(' ').length == 4 || done === true,
}));
if (combineInput.length > 0) {
const parsed = combineInput.split(' ');
if (parsed.length != 4) {
throw Error('srv inputs should be like this: <hostname> <port> <priority> <weight>');
}
result.push({
host: parsed[0],
port: parsed[1],
priority: parsed[2],
weight: parsed[3],
});
}
else {
done = true;
}
i++;
} while (!done);
}
return result;
},
TXT: async (flags) => {
// @ts-ignore
let result = [];
if (flags.txt) {
flags.txt.map((txt) => {
result.push({ text: txt });
});
}
else {
let done = false;
let i = 1;
do {
const { text } = (await inquirer.prompt({
name: 'text',
type: 'input',
message: `Enter text${i} (leave empty to finish):`,
validate: (input) => input.length >= 0 || done === true,
}));
if (text.length > 0) {
result.push({ text: text });
}
else {
done = true;
}
i++;
} while (!done);
}
return result;
},
};
class Update extends Command {
async run() {
const { flags } = await this.parse(Update);
await this.setGotConfig(flags);
const debug = createDebugLogger(flags.debug);
await this.setGotConfig(flags);
const account = await this.getCurrentAccount();
((account && account.region === 'germany') || flags.region === 'germany') &&
this.error('We do not support germany any more.');
const zone = flags.zone || (await this.promptZone());
const name = flags.name || (await this.promptName());
const ttl = flags.ttl || (await this.promptTTL());
const record = await this.getRecordByName(zone, name);
if (record === undefined || record.id === undefined) {
this.error(`Record ${name} for zone ${zone} not found`);
}
const DNSRecord = {
name: name,
type: record.type,
ttl: ttl,
// @ts-ignore
contents: await promptRecordContent[record.type](flags),
};
try {
const { data } = await this.got
.put(Update.PATH.replace('{zone}', zone).replace('{id}', record.id), {
json: { ...DNSRecord },
})
.json();
// @ts-ignore
let contents = [];
switch (data.type) {
case RecordType.A:
case RecordType.AAAA:
data.contents.map((rec) => {
// @ts-ignore
contents.push(rec.ip);
});
break;
case RecordType.ALIAS:
case RecordType.CNAME:
case RecordType.MX:
case RecordType.SRV:
data.contents.map((rec) => {
// @ts-ignore
contents.push(rec.host);
});
break;
case RecordType.TXT:
data.contents.map((rec) => {
// @ts-ignore
contents.push(rec.text);
});
break;
default:
this.error('Unknown error in showing records');
}
const tableData = {
id: data.id,
name: data.name,
type: data.type,
ttl: data.ttl,
contents: contents.join('\n'),
};
const columnConfig = {
id: {},
name: {},
type: {},
ttl: {},
contents: {},
};
ux.table([tableData], columnConfig, flags);
}
catch (error) {
if (error.response && error.response.statusCode === 404) {
this.error(`Zone not found.`);
}
this.error(error.response.message);
}
}
async setGotConfig(config) {
await super.setGotConfig(config);
const new_got = this.got.extend({ prefixUrl: Update.baseURL });
this.got = new_got; // baseURL is different for zone api
}
async promptName() {
const { name } = (await inquirer.prompt({
name: 'name',
type: 'input',
message: 'Enter record name:',
validate: (input) => input.length > 0,
}));
return name;
}
async promptZone() {
const { zone } = (await inquirer.prompt({
name: 'zone',
type: 'input',
message: 'Enter domain:',
validate: (input) => input.length > 2,
}));
return zone;
}
async getRecordByName(zone, name) {
const { data } = await this.got('api/v1/zones/{zone}/dns-records'.replace('{zone}', zone)).json();
if (!data.length) {
this.error(`Not found any records.
Please open up https://console.liara.ir/zones.`);
}
const recordID = data.find((record) => record.name === name);
return recordID;
}
async promptTTL() {
const { ttl } = (await inquirer.prompt({
name: 'ttl',
type: 'input',
message: 'Enter record ttl:',
validate: (input) => {
const parsed = parseInt(input);
return !isNaN(parsed);
},
}));
return parseInt(ttl);
}
}
Update.description = 'update a DNS record';
Update.baseURL = 'https://dns-service.iran.liara.ir';
Update.PATH = 'api/v1/zones/{zone}/dns-records/{id}';
Update.flags = {
...Command.flags,
zone: Flags.string({
char: 'z',
description: 'name of the zone (domain)',
}),
name: Flags.string({
char: 'n',
description: 'record name',
}),
ttl: Flags.integer({
char: 'l',
description: 'time to live',
}),
ip: Flags.string({
char: 'i',
description: 'ip value for record A and AAAA',
multiple: true,
}),
host: Flags.string({
char: 's',
description: 'host value for record ALIAS and CNAME',
}),
mx: Flags.string({
char: 'm',
description: 'host and priority values for MX record. mx flag should be like this: --mx <hostname>,<priority>',
multiple: true,
}),
srv: Flags.string({
char: 'r',
description: 'hostname, port, priority and weight values for SRV record. srv flag should be like this: <hostname>,<port>,<priority>,<weight>',
multiple: true,
}),
txt: Flags.string({
char: 'x',
description: 'text value for record TXT',
multiple: true,
}),
...ux.table.flags(),
};
export default Update;