cli.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #!/usr/bin/env node
  2. /*
  3. * This file is part of the storage node for the Joystream project.
  4. * Copyright (C) 2019 Joystream Contributors
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. 'use strict'
  20. const path = require('path')
  21. const fs = require('fs')
  22. const assert = require('assert')
  23. const { RuntimeApi } = require('@joystream/runtime-api')
  24. const meow = require('meow')
  25. const chalk = require('chalk')
  26. const _ = require('lodash')
  27. const debug = require('debug')('joystream:cli')
  28. // Project root
  29. const project_root = path.resolve(__dirname, '..')
  30. // Configuration (default)
  31. const pkg = require(path.resolve(project_root, 'package.json'))
  32. // Parse CLI
  33. const FLAG_DEFINITIONS = {
  34. // TODO
  35. }
  36. const cli = meow(`
  37. Usage:
  38. $ joystream key_file command [options]
  39. All commands require a key file holding the identity for interacting with the
  40. runtime API.
  41. Commands:
  42. upload Upload a file to a Colossus storage node. Requires a
  43. storage node URL, and a local file name to upload. As
  44. an optional third parameter, you can provide a Data
  45. Object Type ID - this defaults to "1" if not provided.
  46. download Retrieve a file. Requires a storage node URL and a content
  47. ID, as well as an output filename.
  48. head Send a HEAD request for a file, and print headers.
  49. Requires a storage node URL and a content ID.
  50. Dev Commands: Commands to run on a development chain.
  51. dev-init Setup chain with Alice as lead and provider.
  52. dev-check Check the chain is setup with Alice as lead and provider.
  53. `,
  54. { flags: FLAG_DEFINITIONS })
  55. function assert_file (name, filename) {
  56. assert(filename, `Need a ${name} parameter to proceed!`)
  57. assert(fs.statSync(filename).isFile(), `Path "${filename}" is not a file, aborting!`)
  58. }
  59. const commands = {
  60. // add Alice well known account as storage provider
  61. 'dev-init': async (runtime_api) => {
  62. let dev = require('./dev')
  63. return dev.init(runtime_api)
  64. },
  65. // Checks that the setup done by dev-init command was successful.
  66. 'dev-check': async (runtime_api) => {
  67. let dev = require('./dev')
  68. return dev.check(runtime_api)
  69. // await runtime_api.assets.checkLiaisonForDataObject(providerId, '')
  70. },
  71. 'upload': async (runtime_api, url, filename, do_type_id) => {
  72. // Check parameters
  73. assert_file('file', filename)
  74. const size = fs.statSync(filename).size
  75. console.log(`File "${filename}" is ` + chalk.green(size) + ' Bytes.')
  76. if (!do_type_id) {
  77. do_type_id = 1
  78. }
  79. console.log('Data Object Type ID is: ' + chalk.green(do_type_id))
  80. // Generate content ID
  81. // FIXME this require path is like this because of
  82. // https://github.com/Joystream/apps/issues/207
  83. const { ContentId } = require('@joystream/types/lib/media')
  84. var cid = ContentId.generate()
  85. cid = cid.encode().toString()
  86. console.log('Generated content ID: ' + chalk.green(cid))
  87. // Create Data Object
  88. const data_object = await runtime_api.assets.createDataObject(
  89. runtime_api.identities.key.address, cid, do_type_id, size)
  90. console.log('Data object created.')
  91. // TODO in future, optionally contact liaison here?
  92. const request = require('request')
  93. url = `${url}asset/v0/${cid}`
  94. console.log('Uploading to URL', chalk.green(url))
  95. const f = fs.createReadStream(filename)
  96. const opts = {
  97. url: url,
  98. headers: {
  99. 'content-type': '',
  100. 'content-length': `${size}`
  101. },
  102. json: true
  103. }
  104. return new Promise((resolve, reject) => {
  105. const r = request.put(opts, (error, response, body) => {
  106. if (error) {
  107. reject(error)
  108. return
  109. }
  110. if (response.statusCode / 100 !== 2) {
  111. reject(new Error(`${response.statusCode}: ${body.message || 'unknown reason'}`))
  112. return
  113. }
  114. console.log('Upload successful:', body.message)
  115. resolve()
  116. })
  117. f.pipe(r)
  118. })
  119. },
  120. 'download': async (runtime_api, url, content_id, filename) => {
  121. const request = require('request')
  122. url = `${url}asset/v0/${content_id}`
  123. console.log('Downloading URL', chalk.green(url), 'to', chalk.green(filename))
  124. const f = fs.createWriteStream(filename)
  125. const opts = {
  126. url: url,
  127. json: true
  128. }
  129. return new Promise((resolve, reject) => {
  130. const r = request.get(opts, (error, response, body) => {
  131. if (error) {
  132. reject(error)
  133. return
  134. }
  135. console.log('Downloading', chalk.green(response.headers['content-type']), 'of size', chalk.green(response.headers['content-length']), '...')
  136. f.on('error', (err) => {
  137. reject(err)
  138. })
  139. f.on('finish', () => {
  140. if (response.statusCode / 100 !== 2) {
  141. reject(new Error(`${response.statusCode}: ${body.message || 'unknown reason'}`))
  142. return
  143. }
  144. console.log('Download completed.')
  145. resolve()
  146. })
  147. })
  148. r.pipe(f)
  149. })
  150. },
  151. 'head': async (runtime_api, url, content_id) => {
  152. const request = require('request')
  153. url = `${url}asset/v0/${content_id}`
  154. console.log('Checking URL', chalk.green(url), '...')
  155. const opts = {
  156. url: url,
  157. json: true
  158. }
  159. return new Promise((resolve, reject) => {
  160. const r = request.head(opts, (error, response, body) => {
  161. if (error) {
  162. reject(error)
  163. return
  164. }
  165. if (response.statusCode / 100 !== 2) {
  166. reject(new Error(`${response.statusCode}: ${body.message || 'unknown reason'}`))
  167. return
  168. }
  169. for (var propname in response.headers) {
  170. console.log(` ${chalk.yellow(propname)}: ${response.headers[propname]}`)
  171. }
  172. resolve()
  173. })
  174. })
  175. }
  176. }
  177. async function main () {
  178. // Key file is at the first instance.
  179. const key_file = cli.input[0]
  180. assert_file('key file', key_file)
  181. // Create runtime API.
  182. const runtime_api = await RuntimeApi.create({ account_file: key_file })
  183. // Simple CLI commands
  184. const command = cli.input[1]
  185. if (!command) {
  186. throw new Error('Need a command to run!')
  187. }
  188. if (commands.hasOwnProperty(command)) {
  189. // Command recognized
  190. const args = _.clone(cli.input).slice(2)
  191. await commands[command](runtime_api, ...args)
  192. } else {
  193. throw new Error(`Command "${command}" not recognized, aborting!`)
  194. }
  195. }
  196. main()
  197. .then(() => {
  198. console.log('Process exiting gracefully.')
  199. process.exit(0)
  200. })
  201. .catch((err) => {
  202. console.error(chalk.red(err.stack))
  203. process.exit(-1)
  204. })