'use strict'; var request = require('@root/request'); request = require('util').promisify(request); var crypto = require('crypto'); var querystring = require('querystring'); var defaults = { region:'ovh-eu', basePath:'/1.0' }; var recordsStore = {}; module.exports.create = function(config) { var applicationKey = config.applicationKey||null; var applicationSecret = config.applicationSecret||null; var consumerKey = config.consumerKey||null; // one of ovh-eu, ovh-ca, kimsufi-eu, kimsufi-ca, soyoustart-eu, soyoustart-ca, runabove-ca var region = config.region || defaults.region; var apiTimeDiff = config.apiTimeDiff || null; var basePath = config.basePath || defaults.basePath; if (typeof(applicationKey) !== 'string' || typeof(applicationSecret) !== 'string') { throw new Error('[OVH] You should precise an application key / secret'); } var endpoints = { 'ovh-eu': 'eu.api.ovh.com', 'ovh-ca': 'ca.api.ovh.com', 'kimsufi-eu': 'eu.api.kimsufi.com', 'kimsufi-ca': 'ca.api.kimsufi.com', 'soyoustart-eu': 'eu.api.soyoustart.com', 'soyoustart-ca': 'ca.api.soyoustart.com', 'runabove-ca': 'api.runabove.com' }; var baseUrl = 'https://' + endpoints[region] + basePath; /** * Signs an API request * * @param {String} httpMethod * @param {String} url * @param {String} body * @param {Number|String} timestamp * @return {String} The signature */ function signRequest(httpMethod, url, body, timestamp) { var s = [ applicationSecret, consumerKey, httpMethod, url, body || '', timestamp ]; return '$1$' + crypto.createHash('sha1').update(s.join('+')).digest('hex'); } function api(httpMethod, path, params) { // Time drift if (apiTimeDiff === null && path !== '/auth/time') { return api('GET', '/auth/time', {}) .then(function(res) { apiTimeDiff = res.body - Math.round(Date.now() / 1000); return api(httpMethod, path, params); }).catch(function(err) { throw new Error('[OVH] Unable to fetch OVH API time'); }); } var headers = { 'X-Ovh-Application': applicationKey }; if (httpMethod === 'GET') { path += '?' + querystring.stringify(params); } var url = baseUrl + path; if (path.indexOf('/auth') < 0) { headers['X-Ovh-Timestamp'] = Math.round(Date.now() / 1000) + apiTimeDiff; // Sign request if (typeof(consumerKey) === 'string') { headers['X-Ovh-Consumer'] = consumerKey; headers['X-Ovh-Signature'] = signRequest( httpMethod, url, params, headers['X-Ovh-Timestamp'] ); } } console.debug( '[OVH] API call:', httpMethod, path, params || '' ); return request({ method: httpMethod, url: url, headers: headers, json: true, form: params }); } return { init: function(deps) { request = deps.request; return null; }, zones: function(data) { return api('GET', '/domain/zone') .then(function(resp) { if (200 !== resp.statusCode) { console.error(resp.statusCode); console.error(resp.body); throw new Error('Could not get list of zones.'); } // list of zones return resp.body; }).catch(function(err) { }); }, set: function(data) { var ch = data.challenge; var txt = ch.dnsAuthorization; // record name received as argument : www.qwerty.sampledomain.com // received zone id : sampledomain.com required record name by OVH : www.qwerty //console.debug('adding txt', data); return api('POST', '/domain/zone/' + ch.dnsZone + '/record', { fieldType: 'TXT', subDomain: ch.dnsPrefix, target: txt, ttl: 1 }).then(function(resp) { if (200 !== resp.statusCode) { console.error(resp.statusCode); console.error(resp.body); throw new Error('record did not set.'); } // save id for remove recordsStore[ch.dnsPrefix] = resp.body['id'] }).then(function() { // Apply zone modification on DNS servers return api('POST', '/domain/zone/' + ch.dnsZone + '/record', {}) .then(function() { return true; }); }); }, remove: function(data) { var ch = data.challenge; return api('DELETE', '/domain/zone/' + ch.dnsZone + '/record/'+recordsStore[ch.dnsPrefix]) .then(function(resp) { if (200 !== resp.statusCode) { throw new Error('record did not remove.'); } }) .then(function() { return api('POST', '/domain/zone/' + ch.dnsZone + '/record', {}) .then(function(resp) { if (200 !== resp.statusCode) { console.error(resp.statusCode); console.error(resp.body); throw new Error('record did not remove.'); } return true; }); }); }, get: function(data) { var ch = data.challenge; return api('GET', '/domain/zone/' + ch.dnsZone + '/record/'+recordsStore[ch.dnsPrefix]) .then(function(resp) { if (200 !== resp.statusCode) { throw new Error('record did not remove.'); } return { dnsAuthorization:resp.body.target }; }); } }; };