Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

digital-ocean-dynamic-dns

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

digital-ocean-dynamic-dns - npm Package Compare versions

Comparing version 1.0.1 to 1.1.2

.dependabot/config.yml

21

index.js

@@ -0,3 +1,22 @@

const buildDomains = require('./lib/build-domains')
const updateDomain = require('./lib/update-domain')
module.exports = opts => updateDomain(opts).catch(e => process.exit(1))
module.exports = async args => {
let domains
try {
domains = await buildDomains(args)
} catch (e) {
process.exit(1)
}
const updater = updateDomain()
for (const domain of domains) {
try {
await updater(domain)
} catch (e) {
process.exit(1)
}
}
process.exit(0)
}

61

lib/do-api.js

@@ -1,23 +0,13 @@

const request = require('request-promise')
const fetch = require('node-fetch')
module.exports.listDomainRecords = ({ token, domainName }) =>
request({
method: 'GET',
url: `https://api.digitalocean.com/v2/domains/${domainName}/records`,
json: true,
headers: {
'content-type': 'application/json',
authorization: `Bearer ${token}`
}
jsonFetch(`https://api.digitalocean.com/v2/domains/${domainName}/records`, {
token,
method: 'GET'
}).then(res => res.domain_records)
module.exports.createDomainRecord = ({ token, domainName, domainRecord }) =>
request({
jsonFetch(`https://api.digitalocean.com/v2/domains/${domainName}/records`, {
token,
method: 'POST',
url: `https://api.digitalocean.com/v2/domains/${domainName}/records`,
json: true,
headers: {
'content-type': 'application/json',
authorization: `Bearer ${token}`
},
body: domainRecord

@@ -32,11 +22,36 @@ }).then(res => res.domain_record)

}) =>
request({
method: 'PUT',
url: `https://api.digitalocean.com/v2/domains/${domainName}/records/${domainRecordId}`,
json: true,
jsonFetch(
`https://api.digitalocean.com/v2/domains/${domainName}/records/${domainRecordId}`,
{
token,
method: 'PUT',
body: updateData
}
).then(res => res.domain_record)
async function jsonFetch (url, { method, token, body }) {
const fetchOpts = {
method,
headers: {
'content-type': 'application/json',
authorization: `Bearer ${token}`
},
body: updateData
}).then(res => res.domain_record)
}
}
if (body) {
fetchOpts.body = JSON.stringify(body)
}
const res = await fetch(url, fetchOpts)
if (!res.ok) {
const result = await res.text()
throw new Error(
'DigitalOcean API request failed with status ' +
res.status +
': ' +
result
)
}
return res.json()
}

@@ -1,3 +0,3 @@

const mockRequest = jest.fn(() => Promise.resolve())
jest.setMock('request-promise', mockRequest)
jest.mock('node-fetch')
const mockFetch = require('node-fetch')

@@ -7,4 +7,13 @@ const api = require('./do-api')

describe('lib/do-api', () => {
afterEach(() => jest.clearAllMocks())
afterEach(() => jest.resetAllMocks())
function createResponse (status, data) {
return {
status,
ok: status >= 200 && status < 300,
text: () => Promise.resolve(JSON.stringify(data)),
json: () => Promise.resolve(data)
}
}
describe('listDomainRecords()', () => {

@@ -15,10 +24,11 @@ it('fetches a list of domain records', () => {

const domainRecords = [{ record: 1 }, { record: 2 }]
mockRequest.mockReturnValueOnce(
Promise.resolve({ domain_records: domainRecords })
mockFetch.mockReturnValue(
Promise.resolve(createResponse(200, { domain_records: domainRecords }))
)
return api.listDomainRecords({ token, domainName }).then(result => {
const reqOpts = mockRequest.mock.calls[0][0]
const reqUrl = mockFetch.mock.calls[0][0]
const reqOpts = mockFetch.mock.calls[0][1]
expect(reqUrl).toContain(domainName)
expect(reqOpts.method).toBe('GET')
expect(reqOpts.url).toContain(domainName)
expect(reqOpts.headers['content-type']).toBe('application/json')

@@ -29,2 +39,15 @@ expect(reqOpts.headers.authorization).toBe(`Bearer ${token}`)

})
it('rejects if request fails', async () => {
const token = 'abc123'
const domainName = 'domain.test'
const domainRecords = [{ record: 1 }, { record: 2 }]
mockFetch.mockReturnValue(
Promise.resolve(createResponse(501, { some: 'error' }))
)
const result = await await expect(
api.listDomainRecords({ token, domainName })
).rejects.toThrow(/501/)
})
})

@@ -38,4 +61,4 @@

const domainRecord = { updatedRecord: 1 }
mockRequest.mockReturnValueOnce(
Promise.resolve({ domain_record: domainRecord })
mockFetch.mockReturnValue(
Promise.resolve(createResponse(200, { domain_record: domainRecord }))
)

@@ -46,8 +69,9 @@

.then(result => {
const reqOpts = mockRequest.mock.calls[0][0]
const reqUrl = mockFetch.mock.calls[0][0]
const reqOpts = mockFetch.mock.calls[0][1]
expect(reqUrl).toContain(domainName)
expect(reqOpts.method).toBe('POST')
expect(reqOpts.url).toContain(domainName)
expect(reqOpts.headers['content-type']).toBe('application/json')
expect(reqOpts.headers.authorization).toBe(`Bearer ${token}`)
expect(reqOpts.body).toEqual(domainRecord)
expect(reqOpts.body).toEqual(JSON.stringify(domainRecord))
expect(result).toEqual(domainRecord)

@@ -65,4 +89,4 @@ })

const domainRecord = { updatedRecord: 1 }
mockRequest.mockReturnValueOnce(
Promise.resolve({ domain_record: domainRecord })
mockFetch.mockReturnValue(
Promise.resolve(createResponse(200, { domain_record: domainRecord }))
)

@@ -73,9 +97,10 @@

.then(result => {
const reqOpts = mockRequest.mock.calls[0][0]
const reqUrl = mockFetch.mock.calls[0][0]
const reqOpts = mockFetch.mock.calls[0][1]
expect(reqUrl).toContain(domainName)
expect(reqUrl).toContain(domainRecordId)
expect(reqOpts.method).toBe('PUT')
expect(reqOpts.url).toContain(domainName)
expect(reqOpts.url).toContain(domainRecordId)
expect(reqOpts.headers['content-type']).toBe('application/json')
expect(reqOpts.headers.authorization).toBe(`Bearer ${token}`)
expect(reqOpts.body).toEqual(updateData)
expect(reqOpts.body).toEqual(JSON.stringify(updateData))
expect(result).toEqual(domainRecord)

@@ -82,0 +107,0 @@ })

const publicIp = require('public-ip')
const R = require('ramda')
const check = require('check')
const co = require('co')

@@ -9,42 +7,15 @@ const api = require('./do-api')

module.exports = function (opts) {
return validateOpts(opts)
.then(ensureAddress)
.then(updateOrCreateDomainRecord)
.catch(e => {
log.error(e)
throw e
})
}
module.exports = function build () {
let pendingIp
function validateOpts (opts = {}) {
const completeOpts = {
token: opts.token,
address: opts.address,
domainName: opts.domainName,
recordName: opts.recordName,
recordType: R.propOr('A', 'recordType', opts),
recordTtl: R.propOr(null, 'recordTtl', opts),
recordPriority: R.propOr(null, 'recordPriority', opts),
recordPort: R.propOr(null, 'recordPort', opts),
recordWeight: R.propOr(null, 'recordWeight', opts),
create: R.propOr(false, 'create', opts)
return domain => {
return ensureAddress(domain)
.then(updateOrCreateDomainRecord)
.catch(e => {
log.error(e)
throw e
})
}
const errors = check(completeOpts)
.hasString('token')
.hasString('domainName')
.hasString('recordName')
.errors()
if (errors.length) {
errors.forEach(log.error)
return Promise.reject('Missing or invalid arguments.')
}
return Promise.resolve(completeOpts)
}
function ensureAddress (opts) {
return co(function * () {
async function ensureAddress (opts) {
if (opts.address) {

@@ -55,13 +26,15 @@ log.info(`Using address: ${opts.address}`)

log.info('Obtain public IP address')
const ip = yield publicIp.v4()
log.info(`Public IP: ${ip}`)
if (!pendingIp) {
log.info('Obtaining public IP address')
pendingIp = publicIp.v4()
}
const ip = await pendingIp
log.info(`Using public IP address: ${ip}`)
return Object.assign({}, opts, { address: ip })
})
}
}
function updateOrCreateDomainRecord (opts) {
return co(function * () {
const domainRecord = yield getExistingDomainRecord(opts)
async function updateOrCreateDomainRecord (opts) {
const domainRecord = await getExistingDomainRecord(opts)

@@ -73,13 +46,12 @@ if (!domainRecord && opts.create) {

} else {
throw new Error(
log.error(
`Cannot find domain record "${opts.recordName}.${opts.domainName}". Set the --create flag if you want to create it.`
)
throw new Error('Domain record not found')
}
})
}
}
function getExistingDomainRecord (opts) {
return co(function * () {
async function getExistingDomainRecord (opts) {
log.info('Fetch existing Domain records')
const domainRecords = yield api.listDomainRecords({
const domainRecords = await api.listDomainRecords({
token: opts.token,

@@ -89,7 +61,5 @@ domainName: opts.domainName

return domainRecords.find(r => r.name === opts.recordName) || false
})
}
}
function createDomainRecord (opts) {
return co(function * () {
async function createDomainRecord (opts) {
const domainRecord = {

@@ -109,3 +79,3 @@ type: opts.recordType,

const newDomainRecord = yield api.createDomainRecord({
const newDomainRecord = await api.createDomainRecord({
token: opts.token,

@@ -119,7 +89,7 @@ domainName: opts.domainName,

)
})
}
function updateDomainRecord (opts, domainRecord) {
co(function * () {
return opts
}
async function updateDomainRecord (opts, domainRecord) {
if (opts.address === domainRecord.data) {

@@ -146,3 +116,3 @@ log.info(

const updatedRecord = yield api.updateDomainRecord({
const updatedRecord = await api.updateDomainRecord({
token: opts.token,

@@ -155,3 +125,5 @@ domainName: opts.domainName,

log.info(`Success updating domain record: ${JSON.stringify(updatedRecord)}`)
})
return opts
}
}

@@ -23,12 +23,4 @@ const mockPublicIp = {

describe('lib/update-domain', () => {
afterEach(() => jest.clearAllMocks())
afterEach(() => jest.resetAllMocks())
it('rejects if no opts are provided', () => {
return expect(updateDomain()).rejects.toBeDefined()
})
it('rejects if invalid opts are provided', () => {
return expect(updateDomain({ invalid: 'opts' })).rejects.toBeDefined()
})
it('uses a provided address', () => {

@@ -51,3 +43,3 @@ const ip = '123.456.789.123'

return updateDomain(opts).then(() => {
return updateDomain()(opts).then(() => {
const updateOpts = mockApi.updateDomainRecord.mock.calls[0][0]

@@ -75,3 +67,3 @@ expect(updateOpts.updateData.data).toEqual(ip)

return updateDomain(opts).then(() => {
return updateDomain()(opts).then(() => {
expect(mockPublicIp.v4).toHaveBeenCalledWith()

@@ -83,2 +75,39 @@ const updateOpts = mockApi.updateDomainRecord.mock.calls[0][0]

it('updates multiple domains with same public IP', async () => {
const domain1 = {
token: '123abc',
domainName: 'domain.test',
recordName: 'test1'
}
const domain2 = {
token: '123abc',
domainName: 'domain.test',
recordName: 'test2'
}
const ip = '123.456.789.123'
mockPublicIp.v4.mockReturnValue(Promise.resolve(ip))
mockApi.listDomainRecords.mockReturnValue(
Promise.resolve([
{
name: domain1.recordName
},
{
name: domain2.recordName
}
])
)
const updater = updateDomain()
await updater(domain1)
await updater(domain2)
const domain1Opts = mockApi.updateDomainRecord.mock.calls[0][0]
expect(domain1Opts.updateData.data).toEqual(ip)
const domain2Opts = mockApi.updateDomainRecord.mock.calls[0][0]
expect(domain2Opts.updateData.data).toEqual(ip)
})
it('updates an existing domain record with minimum opts', () => {

@@ -102,3 +131,3 @@ const ip = '123.456.789.123'

return updateDomain(opts).then(() => {
return updateDomain()(opts).then(() => {
const updateOpts = mockApi.updateDomainRecord.mock.calls[0][0]

@@ -108,4 +137,3 @@ expect(updateOpts.token).toEqual(opts.token)

expect(updateOpts.domainRecordId).toEqual(domainRecord.id)
expect(updateOpts.updateData).toEqual({
type: 'A',
expect(updateOpts.updateData).toMatchObject({
name: opts.recordName,

@@ -140,3 +168,3 @@ data: ip

return updateDomain(opts).then(() => {
return updateDomain()(opts).then(() => {
const updateOpts = mockApi.updateDomainRecord.mock.calls[0][0]

@@ -177,3 +205,3 @@ expect(updateOpts.token).toEqual(opts.token)

return updateDomain(opts).then(() => {
return updateDomain()(opts).then(() => {
expect(mockApi.updateDomainRecord).toHaveBeenCalledTimes(0)

@@ -194,3 +222,3 @@ expect(mockApi.createDomainRecord).toHaveBeenCalledTimes(0)

return expect(updateDomain(opts)).rejects.toBeDefined()
return expect(updateDomain()(opts)).rejects.toBeDefined()
})

@@ -221,3 +249,3 @@

return updateDomain(opts).then(() => {
return updateDomain()(opts).then(() => {
const createOpts = mockApi.createDomainRecord.mock.calls[0][0]

@@ -237,29 +265,2 @@ expect(createOpts.token).toEqual(opts.token)

})
it('creates a new domain record with default values', () => {
const opts = {
token: '123abc',
address: '123.456.789.123',
recordName: 'test',
domainName: 'domain.test',
create: true
}
mockApi.listDomainRecords.mockReturnValueOnce(Promise.resolve([]))
return updateDomain(opts).then(() => {
const createOpts = mockApi.createDomainRecord.mock.calls[0][0]
expect(createOpts.token).toEqual(opts.token)
expect(createOpts.domainName).toEqual(opts.domainName)
expect(createOpts.domainRecord).toEqual({
type: 'A',
name: opts.recordName,
data: opts.address,
priority: null,
port: null,
ttl: null,
weight: null
})
})
})
})
{
"name": "digital-ocean-dynamic-dns",
"version": "1.0.1",
"version": "1.1.2",
"description": "Dynamically update Domain Records on Digital Ocean",

@@ -29,3 +29,3 @@ "author": "Philip Klostermann <philip.klostermann@gmail.com>",

"test:watch": "jest --watch",
"prepublish": "npm run test"
"prepublishOnly": "npm run test"
},

@@ -35,12 +35,10 @@ "dependencies": {

"check": "^1.0.0",
"co": "^4.6.0",
"commander": "^2.9.0",
"fs-extra": "^9.0.1",
"node-fetch": "^2.6.1",
"public-ip": "^2.3.5",
"ramda": "^0.24.1",
"request": "^2.81.0",
"request-promise": "^4.2.1"
"ramda": "^0.24.1"
},
"devDependencies": {
"@types/jest": "^19.2.4",
"jest": "^20.0.4"
"jest": "^26.6.3"
},

@@ -47,0 +45,0 @@ "jest": {

# digital-ocean-dynamic-dns
Dynamically update Domain Records on Digital Ocean.

@@ -48,2 +49,26 @@

### Config file
Using the `-f` or `--file` option, all parameters can be set from a JSON file. It's possible to specify multiple domains when the JSON file contains an array of domains. Any arguments passed on the command line will also be applied but will be overridden by the file contents.
```
$ dodd -t abc123def456ghi789 -f /path/to/file.json
```
`/path/to/file.json`
```
[
{
"domainName": "domain.tld",
"recordName": "record"
},
{
"token": "xyz987uvw654qrs321",
"domainName": "other.tld",
"recordName": "test"
}
]
```
## Run automatically

@@ -93,2 +118,1 @@

```

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc