#!/usr/bin/env node
// Packages
const chalk = require ( "chalk" ) ;
const minimist = require ( "minimist" ) ;
const ms = require ( "ms" ) ;
const table = require ( "text-table" ) ;
// Ours
const cfg = require ( "../lib/cfg" ) ;
const DomainRecords = require ( "../lib/domain-records" ) ;
const indent = require ( "../lib/indent" ) ;
const login = require ( "../lib/login" ) ;
const strlen = require ( "../lib/strlen" ) ;
const { handleError , error } = require ( "../lib/error" ) ;
const exit = require ( "../lib/utils/exit" ) ;
const logo = require ( "../lib/utils/output/logo" ) ;
const argv = minimist ( process . argv . slice ( 2 ) , {
string : [ "config" ] ,
boolean : [ "help" , "debug" ] ,
alias : {
help : "h" ,
config : "c" ,
debug : "d" ,
token : "t"
}
} ) ;
const subcommand = argv . _ [ 0 ] ;
// options
const help = ( ) => {
console . log (
`
$ { chalk . bold ( ` ${ logo } now dns ls ` ) } [ domain ]
$ { chalk . bold ( ` ${ logo } now dns add ` ) } < domain > < name > < A | AAAA | ALIAS | CNAME | TXT > < value >
$ { chalk . bold ( ` ${ logo } now dns add ` ) } < domain > < name > MX < value > < mx_priority >
$ { chalk . bold ( ` ${ logo } now dns add ` ) } < domain > < name > SRV < priority > < weight > < port > < target >
$ { chalk . bold ( ` ${ logo } now dns rm ` ) } < id >
$ { chalk . dim ( "Options:" ) }
- h , -- help output usage information
- c $ { chalk . bold . underline ( "FILE" ) } , -- config = $ { chalk . bold . underline ( "FILE" ) } config file
- d , -- debug debug mode [ off ]
- t $ { chalk . bold . underline ( "TOKEN" ) } , -- token = $ { chalk . bold . underline ( "TOKEN" ) } login token
$ { chalk . dim ( "Examples:" ) }
$ { chalk . gray ( "–" ) } List all your DNS records
$ { chalk . cyan ( "$ now dns ls" ) }
$ { chalk . gray ( "–" ) } Add an A record for a subdomain
$ { chalk . cyan ( "$ now dns add <YOUR DOMAIN> <SUBDOMAIN NAME> A <RECORD VALUE>" ) }
$ { chalk . cyan ( "$ now dns add zeit.rocks api A 198.51.100.100" ) }
$ { chalk . gray ( "–" ) } Add an MX record ( @ as a name refers to the domain )
$ { chalk . cyan ( "$ now dns add <YOUR DOMAIN> @ MX <RECORD VALUE> <PRIORITY>" ) }
$ { chalk . cyan ( "$ now dns add zeit.rocks @ MX mail.zeit.rocks 10" ) }
`
) ;
} ;
// options
const debug = argv . debug ;
const apiUrl = argv . url || "https://api.zeit.co" ;
if ( argv . config ) {
cfg . setConfigFile ( argv . config ) ;
}
if ( argv . help || ! subcommand ) {
help ( ) ;
exit ( 0 ) ;
} else {
const config = cfg . read ( ) ;
Promise . resolve ( argv . token || config . token || login ( apiUrl ) )
. then ( async token => {
try {
await run ( token ) ;
} catch ( err ) {
handleError ( err ) ;
exit ( 1 ) ;
}
} )
. catch ( e => {
error ( ` Authentication error – ${ e . message } ` ) ;
exit ( 1 ) ;
} ) ;
}
async function run ( token ) {
const domainRecords = new DomainRecords ( apiUrl , token , { debug } ) ;
const args = argv . _ . slice ( 1 ) ;
const start = Date . now ( ) ;
if ( subcommand === "ls" || subcommand === "list" ) {
if ( args . length > 1 ) {
error (
` Invalid number of arguments. Usage: ${ chalk . cyan ( "`now dns ls [domain]`" ) } `
) ;
return exit ( 1 ) ;
}
const elapsed = ms ( new Date ( ) - start ) ;
const res = await domainRecords . ls ( args [ 0 ] ) ;
const text = [ ] ;
let count = 0 ;
res . forEach ( ( records , domain ) => {
count += records . length ;
if ( records . length > 0 ) {
const cur = Date . now ( ) ;
const header = [
[ "" , "id" , "name" , "type" , "value" , "aux" , "created" ] . map ( s =>
chalk . dim ( s ) )
] ;
const out = table (
header . concat (
records . map ( record => {
const time = chalk . gray (
ms ( cur - new Date ( Number ( record . created ) ) ) + " ago"
) ;
return [
"" ,
record . id ,
record . name ,
record . type ,
record . value ,
record . mxPriority || record . priority || "" ,
time
] ;
} )
) ,
{
align : [ "l" , "r" , "l" , "l" , "l" , "l" ] ,
hsep : " " . repeat ( 2 ) ,
stringLength : strlen
}
) ;
text . push ( ` \n \n ${ chalk . bold ( domain ) } \n ${ indent ( out , 2 ) } ` ) ;
}
} ) ;
console . log (
` > ${ count } record ${ count === 1 ? "" : "s" } found ${ chalk . gray ( ` [ ${ elapsed } ] ` ) } `
) ;
console . log ( text . join ( "" ) ) ;
} else if ( subcommand === "add" ) {
const param = parseAddArgs ( args ) ;
if ( ! param ) {
error (
` Invalid number of arguments. See: ${ chalk . cyan ( "`now dns --help`" ) } for usage. `
) ;
return exit ( 1 ) ;
}
const record = await domainRecords . create ( param . domain , param . data ) ;
const elapsed = ms ( new Date ( ) - start ) ;
console . log (
` ${ chalk . cyan ( "> Success!" ) } A new DNS record for domain ${ chalk . bold ( param . domain ) } ${ chalk . gray ( ` ( ${ record . uid } ) ` ) } created ${ chalk . gray ( ` [ ${ elapsed } ] ` ) } `
) ;
} else if ( subcommand === "rm" || subcommand === "remove" ) {
if ( args . length !== 1 ) {
error (
` Invalid number of arguments. Usage: ${ chalk . cyan ( "`now dns rm <id>`" ) } `
) ;
return exit ( 1 ) ;
}
const record = await domainRecords . getRecord ( args [ 0 ] ) ;
if ( ! record ) {
error ( "DNS record not found" ) ;
return exit ( 1 ) ;
}
const yes = await readConfirmation (
record ,
"The following record will be removed permanently\n"
) ;
if ( ! yes ) {
error ( "User abort" ) ;
return exit ( 0 ) ;
}
await domainRecords . delete ( record . domain , record . id ) ;
const elapsed = ms ( new Date ( ) - start ) ;
console . log (
` ${ chalk . cyan ( "> Success!" ) } Record ${ chalk . gray ( ` ${ record . id } ` ) } removed ${ chalk . gray ( ` [ ${ elapsed } ] ` ) } `
) ;
} else {
error ( "Please specify a valid subcommand: ls | add | rm" ) ;
help ( ) ;
exit ( 1 ) ;
}
return domainRecords . close ( ) ;
}
process . on ( "uncaughtException" , err => {
handleError ( err ) ;
exit ( 1 ) ;
} ) ;
function parseAddArgs ( args ) {
if ( ! args || args . length < 4 ) {
return null ;
}
const domain = args [ 0 ] ;
const name = args [ 1 ] === "@" ? "" : args [ 1 ] ;
const type = args [ 2 ] ;
const value = args [ 3 ] ;
if ( ! ( domain && typeof name === "string" && type ) ) {
return null ;
}
if ( type === "MX" ) {
if ( args . length !== 5 ) {
return null ;
}
return {
domain ,
data : {
name ,
type ,
value ,
mxPriority : args [ 4 ]
}
} ;
} else if ( type === "SRV" ) {
if ( args . length !== 7 ) {
return null ;
}
return {
domain ,
data : {
name ,
type ,
srv : {
priority : value ,
weight : args [ 4 ] ,
port : args [ 5 ] ,
target : args [ 6 ]
}
}
} ;
}
if ( args . length !== 4 ) {
return null ;
}
return {
domain ,
data : {
name ,
type ,
value
}
} ;
}
function readConfirmation ( record , msg ) {
return new Promise ( resolve => {
const time = chalk . gray (
ms ( new Date ( ) - new Date ( Number ( record . created ) ) ) + " ago"
) ;
const tbl = table (
[
[
record . id ,
chalk . bold (
` ${ record . name . length > 0 ? record . name + "." : "" } ${ record . domain } ${ record . type } ${ record . value } ${ record . mxPriority ? record . mxPriority : "" } `
) ,
time
]
] ,
{ align : [ "l" , "r" , "l" ] , hsep : " " . repeat ( 6 ) }
) ;
process . stdout . write ( ` > ${ msg } ` ) ;
process . stdout . write ( " " + tbl + "\n" ) ;
process . stdout . write (
` ${ chalk . bold . red ( "> Are you sure?" ) } ${ chalk . gray ( "[y/N] " ) } `
) ;
process . stdin
. on ( "data" , d => {
process . stdin . pause ( ) ;
resolve ( d . toString ( ) . trim ( ) . toLowerCase ( ) === "y" ) ;
} )
. resume ( ) ;
} ) ;
}