publish.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. const debug = require('debug')('joystream:discovery:publish')
  2. /**
  3. * The name of the key used for publishing. We use same key used by the ipfs node
  4. * for the network identitiy, to make it possible to identify the ipfs node of the storage
  5. * provider and use `ipfs ping` to check on the uptime of a particular node.
  6. */
  7. const PUBLISH_KEY = 'self'
  8. /**
  9. * Applies JSON serialization on the data object and converts the utf-8
  10. * string to a Buffer.
  11. * @param {object} data - json object
  12. * @returns {Buffer} returns buffer from UTF-8 json
  13. */
  14. function bufferFrom(data) {
  15. return Buffer.from(JSON.stringify(data), 'utf-8')
  16. }
  17. /**
  18. * Encodes the service info into a standard format see. /storage-node/docs/json-signing.md
  19. * To be able to add a signature over the json data. Signing is not currently implemented.
  20. * @param {object} info - json object
  21. * @returns {Buffer} return buffer.
  22. */
  23. function encodeServiceInfo(info) {
  24. return bufferFrom({
  25. serialized: JSON.stringify(info),
  26. })
  27. }
  28. /**
  29. * A PublisherClient is used to store a JSON serializable piece of "service information" in the ipfs network
  30. * using the `self` key of the ipfs node. This makes looking up that information available through IPNS.
  31. */
  32. class PublisherClient {
  33. /**
  34. * Create an instance of a PublisherClient, taking an optional ipfs client instance. If not provided
  35. * a default client using default localhost node will be used.
  36. * @param {IpfsClient} ipfs - optional instance of an ipfs-http-client.
  37. */
  38. constructor(ipfs) {
  39. this.ipfs = ipfs || require('ipfs-http-client')('localhost', '5001', { protocol: 'http' })
  40. }
  41. /**
  42. * Publishes the service information, encoded using the standard defined in encodeServiceInfo()
  43. * to ipfs, using the local ipfs node's PUBLISH_KEY, and returns the key id used to publish.
  44. * What we refer to as the ipns id.
  45. * @param {object} serviceInfo - the service information to publish
  46. * @return {string} - the ipns id
  47. */
  48. async publish(serviceInfo) {
  49. const keys = await this.ipfs.key.list()
  50. let servicesKey = keys.find((key) => key.name === PUBLISH_KEY)
  51. // An ipfs node will always have the self key.
  52. // If the publish key is specified as anything else and it doesn't exist
  53. // we create it.
  54. if (PUBLISH_KEY !== 'self' && !servicesKey) {
  55. debug('generating ipns services key')
  56. servicesKey = await this.ipfs.key.gen(PUBLISH_KEY, {
  57. type: 'rsa',
  58. size: 2048,
  59. })
  60. }
  61. if (!servicesKey) {
  62. throw new Error('No IPFS publishing key available!')
  63. }
  64. debug('adding service info file to node')
  65. const files = await this.ipfs.add(encodeServiceInfo(serviceInfo))
  66. debug('publishing...')
  67. const { name, value } = await this.ipfs.name.publish(files[0].hash, {
  68. key: PUBLISH_KEY,
  69. resolve: false,
  70. // lifetime: // string - Time duration of the record. Default: 24h
  71. // ttl: // string - Time duration this record should be cached
  72. })
  73. debug(`published ipns name: ${name} -> ${value}`)
  74. // Return the key id under which the content was published. Which is used
  75. // to lookup the actual ipfs content id of the published service information
  76. // Note: name === servicesKey.id
  77. return servicesKey.id
  78. }
  79. }
  80. module.exports = {
  81. PublisherClient,
  82. }