index.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. const Provider = require('../Provider')
  2. const request = require('request')
  3. const purest = require('purest')({ request })
  4. const utils = require('../../helpers/utils')
  5. const logger = require('../../logger')
  6. const adapter = require('./adapter')
  7. const { ProviderApiError, ProviderAuthError } = require('../error')
  8. /**
  9. * Adapter for API https://developers.facebook.com/docs/graph-api/using-graph-api/
  10. */
  11. class Facebook extends Provider {
  12. constructor (options) {
  13. super(options)
  14. this.authProvider = options.provider = Facebook.authProvider
  15. this.client = purest(options)
  16. }
  17. static get authProvider () {
  18. return 'facebook'
  19. }
  20. list ({ directory, token, query = {} }, done) {
  21. const qs = {
  22. fields: 'name,cover_photo,created_time,type'
  23. }
  24. if (query.cursor) {
  25. qs.after = query.cursor
  26. }
  27. let path = 'me/albums'
  28. if (directory) {
  29. path = `${directory}/photos`
  30. qs.fields = 'icon,images,name,width,height,created_time'
  31. }
  32. this.client
  33. .get(path)
  34. .qs(qs)
  35. .auth(token)
  36. .request((err, resp, body) => {
  37. if (err || resp.statusCode !== 200) {
  38. err = this._error(err, resp)
  39. logger.error(err, 'provider.facebook.list.error')
  40. return done(err)
  41. } else {
  42. this._getUsername(token, (err, username) => {
  43. err ? done(err) : done(null, this.adaptData(body, username, directory, query))
  44. })
  45. }
  46. })
  47. }
  48. _getUsername (token, done) {
  49. this.client
  50. .get('me')
  51. .qs({ fields: 'email' })
  52. .auth(token)
  53. .request((err, resp, body) => {
  54. if (err || resp.statusCode !== 200) {
  55. err = this._error(err, resp)
  56. logger.error(err, 'provider.facebook.user.error')
  57. return done(err)
  58. } else {
  59. done(null, body.email)
  60. }
  61. })
  62. }
  63. _getMediaUrl (body) {
  64. const sortedImages = adapter.sortImages(body.images)
  65. return sortedImages[sortedImages.length - 1].source
  66. }
  67. download ({ id, token }, onData) {
  68. return this.client
  69. .get(id)
  70. .qs({ fields: 'images' })
  71. .auth(token)
  72. .request((err, resp, body) => {
  73. if (err) return logger.error(err, 'provider.facebook.download.error')
  74. request(this._getMediaUrl(body))
  75. .on('data', onData)
  76. .on('end', () => onData(null))
  77. .on('error', (err) => {
  78. logger.error(err, 'provider.facebook.download.url.error')
  79. })
  80. })
  81. }
  82. thumbnail (_, done) {
  83. // not implementing this because a public thumbnail from facebook will be used instead
  84. const err = new Error('call to thumbnail is not implemented')
  85. logger.error(err, 'provider.facebook.thumbnail.error')
  86. return done(err)
  87. }
  88. size ({ id, token }, done) {
  89. return this.client
  90. .get(id)
  91. .qs({ fields: 'images' })
  92. .auth(token)
  93. .request((err, resp, body) => {
  94. if (err || resp.statusCode !== 200) {
  95. err = this._error(err, resp)
  96. logger.error(err, 'provider.facebook.size.error')
  97. return done(err)
  98. }
  99. utils.getURLMeta(this._getMediaUrl(body))
  100. .then(({ size }) => done(null, size))
  101. .catch((err) => {
  102. logger.error(err, 'provider.facebook.size.error')
  103. done()
  104. })
  105. })
  106. }
  107. logout ({ token }, done) {
  108. return this.client
  109. .delete('me/permissions')
  110. .auth(token)
  111. .request((err, resp) => {
  112. if (err || resp.statusCode !== 200) {
  113. logger.error(err, 'provider.facebook.logout.error')
  114. done(this._error(err, resp))
  115. return
  116. }
  117. done(null, { revoked: true })
  118. })
  119. }
  120. adaptData (res, username, directory, currentQuery) {
  121. const data = { username: username, items: [] }
  122. const items = adapter.getItemSubList(res)
  123. items.forEach((item) => {
  124. data.items.push({
  125. isFolder: adapter.isFolder(item),
  126. icon: adapter.getItemIcon(item),
  127. name: adapter.getItemName(item),
  128. mimeType: adapter.getMimeType(item),
  129. id: adapter.getItemId(item),
  130. thumbnail: adapter.getItemThumbnailUrl(item),
  131. requestPath: adapter.getItemRequestPath(item),
  132. modifiedDate: adapter.getItemModifiedDate(item)
  133. })
  134. })
  135. data.nextPagePath = adapter.getNextPagePath(res, currentQuery, directory)
  136. return data
  137. }
  138. _error (err, resp) {
  139. if (resp) {
  140. if (resp.body && resp.body.error.code === 190) {
  141. // Invalid OAuth 2.0 Access Token
  142. return new ProviderAuthError()
  143. }
  144. const fallbackMessage = `request to ${this.authProvider} returned ${resp.statusCode}`
  145. const msg = resp.body && resp.body.error ? resp.body.error.message : fallbackMessage
  146. return new ProviderApiError(msg, resp.statusCode)
  147. }
  148. return err
  149. }
  150. }
  151. module.exports = Facebook