From 82e3e8d0d0a6c119900f74b0db046064774ca421 Mon Sep 17 00:00:00 2001 From: Taylor Beseda Date: Wed, 26 Jul 2023 21:22:26 -0600 Subject: [PATCH] domains records list, add, remove --- package.json | 24 ++++---- src/commands/domains/help.js | 32 ++++++++++ src/commands/domains/index.js | 18 +++--- src/commands/domains/records.js | 101 ++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 22 deletions(-) create mode 100644 src/commands/domains/records.js diff --git a/package.json b/package.json index 38c9e1e..8b65f16 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,11 @@ "license": "Apache-2.0", "dependencies": { "@apidevtools/json-schema-ref-parser": "9.1.0", - "@architect/inventory": "3.5.6", + "@architect/inventory": "3.5.7", "@architect/parser": "6.0.2", - "@architect/sandbox": "5.7.1", + "@architect/sandbox": "5.7.4", "@architect/utils": "3.1.9", - "@begin/api": "1.8.0", + "@begin/api": "1.9.1", "@enhance/starter-project": "5.1.4", "adm-zip": "0.5.10", "enquirer": "2.3.6", @@ -48,23 +48,23 @@ "devDependencies": { "@architect/eslint-config": "^2.1.1", "@architect/plugin-node-prune": "2.1.0-RC.0", - "@aws-sdk/client-apigatewaymanagementapi": "3.338.0", - "@aws-sdk/client-dynamodb": "3.338.0", - "@aws-sdk/client-s3": "3.338.0", - "@aws-sdk/client-sns": "3.338.0", - "@aws-sdk/client-sqs": "3.338.0", - "@aws-sdk/client-ssm": "3.338.0", - "@aws-sdk/lib-dynamodb": "3.338.0", + "@aws-sdk/client-apigatewaymanagementapi": "3.378.0", + "@aws-sdk/client-dynamodb": "3.378.0", + "@aws-sdk/client-s3": "3.378.0", + "@aws-sdk/client-sns": "3.378.0", + "@aws-sdk/client-sqs": "3.378.0", + "@aws-sdk/client-ssm": "3.378.0", + "@aws-sdk/lib-dynamodb": "3.378.0", "aws-sdk": "2.1384.0", "cross-env": "^7.0.3", - "eslint": "8.43.0", + "eslint": "8.45.0", "fs-extra": "11.1.1", "nyc": "^15.1.0", "pkg": "5.8.1", "proxyquire": "~2.1.3", "string-argv": "~0.3.2", "tap-arc": "~0.3.5", - "tape": "~5.6.3" + "tape": "~5.6.6" }, "eslintConfig": { "extends": "@architect/eslint-config", diff --git a/src/commands/domains/help.js b/src/commands/domains/help.js index 8396ac6..90c8ba7 100644 --- a/src/commands/domains/help.js +++ b/src/commands/domains/help.js @@ -146,4 +146,36 @@ const HELP = { ], }, }, + records: { + en: { + usage: [ 'domains records ', '[options]' ], + description: 'Manage DNS records for a domain', + contents: { + header: 'Records parameters', + items: [ + { name: '--domain', description: 'Domain name to list records for' }, + { name: '-a, --add', description: 'Add a record' }, + { name: '-r, --remove', description: 'Remove a record' }, + { name: '-t, --type', description: 'Record type; currently only TXT is supported' }, + { name: '-n --name', description: 'Record name, ie. example.com' }, + { name: '--ttl', description: 'Record TTL; defaults to 300' }, + { name: '--value', description: 'Record value' }, + ], + }, + examples: [ + { + name: 'List all records for a domain', + example: 'begin domains records --domain example.com', + }, + { + name: 'Add a TXT record', + example: 'begin domains records --domain example.com --add --type TXT --name example.com --value foobarbaz', + }, + { + name: 'Remove a TXT record', + example: 'begin domains records --domain example.com -r -t TXT -n example.com --value foobarbaz"', + }, + ], + }, + } } diff --git a/src/commands/domains/index.js b/src/commands/domains/index.js index 2efcdf6..785bd75 100644 --- a/src/commands/domains/index.js +++ b/src/commands/domains/index.js @@ -1,5 +1,5 @@ let names = { en: [ 'domains' ] } -let subcommands = [ 'list', 'add', 'remove', 'link', 'unlink' ] +let subcommands = [ 'list', 'add', 'remove', 'link', 'unlink', 'records' ] let aliases = { ls: 'list', buy: 'add', @@ -17,10 +17,11 @@ let help = require('./help').bind({}) async function action (params) { let { args } = params - let { verbose } = args - let subcommand = args._[1] || defaultCommand + let { domain, env, verbose, _ } = args + let subcommand = _[1] || defaultCommand let alias = Object.keys(aliases).includes(subcommand) && aliases[subcommand] subcommand = alias || subcommand + env = env || args.e if (subcommands.includes(subcommand)) { let _inventory = require('@architect/inventory') @@ -31,19 +32,16 @@ async function action (params) { if (!config.access_token) return Error('You must be logged in, please run: begin login') - params.inventory = await _inventory() - + let inventory = await _inventory() let appID try { - appID = getAppID(params.inventory, args) + appID = getAppID(inventory, args) } - catch (error) { + catch (e) { appID = null } - let env = args.env || args.e - let domain = args.domain - return action({ config, appID, env, domain, verbose, ...params }) + return action({ config, appID, env, domain, verbose, inventory, ...params }) } else { let err = new Error('Please specify an domains subcommand') diff --git a/src/commands/domains/records.js b/src/commands/domains/records.js new file mode 100644 index 0000000..ea07ddf --- /dev/null +++ b/src/commands/domains/records.js @@ -0,0 +1,101 @@ +const visibleTypes = [ + 'TXT', + 'MX', + 'ALIAS', + 'NS', + 'SPF', +] + +async function action (params) { + let c = require('picocolors') + let client = require('@begin/api') + let { config, args, domain, verbose } = params + let { access_token: token, stagingAPI: _staging } = config + let { add, remove, type, name, value, ttl } = args + add = add || args.a + remove = remove || args.r + type = type || args.t + name = name || args.n + // value = value || args.v // -v is "verbose" + + if (!domain) + return Error('Please specify a domain with --domain') + + if (add || remove) { + if (!type || (typeof type === 'string' && type.length === 0)) + type = 'TXT' // defaults to TXT + if (!name || (typeof name === 'string' && name.length === 0)) + return Error('Please specify a record name with --name') + if (!value || (typeof value === 'string' && value.length === 0)) + return Error('Please specify record value with --value') + + ttl = Number(ttl) || 300 + value = `"${value}"` + } + + // @ts-ignore + let domains = await client.domains.list({ token, _staging }) + let theDomain = domains.find(d => d.domain === domain) + + if (!theDomain) + return Error([ + `You do not subscribe to the domain "${domain}".`, + `To subscribe, run: begin domains check ${domain}`, + ].join('\n')) + + if (add) { + let result = await client.domains.records.upsert({ + token, + // @ts-ignore + _staging, + domainID: theDomain.domainID, + changes: [ + { + type, + name, + value, + ttl, + } + ] + }) + + return `Added record ${c.bold(type)} ${c.cyan(name)} (${result.status})` + } + else if (remove) { + let result = await client.domains.records.delete({ + token, + // @ts-ignore + _staging, + domainID: theDomain.domainID, + record: { + type, + name, + value, + ttl, + } + }) + + return `Removed record ${c.bold(type)} ${c.cyan(name)} (${result.status})` + } + else { + // list records + let records = await client.domains.records.list({ token, _staging, domainID: theDomain.domainID }) + let outputRecords = verbose ? records : records.filter(r => visibleTypes.includes(r.type)) + outputRecords = outputRecords.sort((a, b) => a.type.localeCompare(b.type)) + + if (outputRecords.length === 0) { + return c.red(`No records found for ${c.underline(c.cyan(domain))}`) + } + else { + return outputRecords.map(r => + `${c.bold(r.type)} ${c.cyan(r.name)} ${c.italic(r.ttl || '')} ${r.values?.join(' ') || ''}` + ).join('\n') + } + } +} + +module.exports = { + name: 'records', + description: 'Manage DNS records for a domain', + action, +}